猿蜕变14——别再聊AOP了,看这个就好了

本文介绍了面向切面编程(AOP)的概念,通过一个实例展示了如何在不修改源代码的情况下,利用JDK动态代理在调用方法前后添加额外功能。文章解释了AOP中的切面、连接点、切点、增强等核心概念,并简要提到了Spring中的两种代理方式:JDK动态代理和CGLIB代理。

看过之前的蜕变系列文章,相信你对mybatis有了应用方面的认识。但是这些要完成你的蜕变还不够,考虑到大家的基础知识,我们继续回到spring的话题上来,我们一起聊一聊AOP。

每月底工厂君会根据后台记录筛选转发文章到朋友圈的前三位的朋友,给与奖励,第一名100元,第二名50元,第三名30元的现金奖励。

猿蜕变同样是一个原创系列文章,帮助你从一个普通的小白,开始掌握一些行业内通用的框架技术知识以及锻炼你对系统设计能力的提升,完成属于你的蜕变,更多精彩内容,敬请大家关注公主号猿人工厂,点击猿人养成获取

AOP是Aspect OrientedProgramming的缩写,中文译名,面向切面编程。我们曾经为了解决字符编码问题,编写了Filter对所有请求的字符编码进行了统一的字符处理。我们配置了Filter之后,请求要先经过Filter之后,才会交给servlet进行处理。Filter中实现的代码,直接强行插入到了Servlet的代码之前。相对于Servlet来说,Filter像一把砍刀一样,把原本正常执行的请求流程横切了,只不过,这个切入点,是固定在了在执行Servlet代码之前。这种切面是静态的。AOP其实是一种编程思想:在不修改源代码的情况下,将实现了某方面功能的代码切入到原有程序的指定位置的一种思想。

当然,我这样讲可能太抽象了,一般情况下,很多其它的教材一般是AOP的从目的出发的:在实际开发中,我们需要添加一些和业务无关的代码,比如打日志,提交数据库事务等等。这些操作和业务无关,代码写法又比较固定,我们可以抽取出来,放到一个统一的地方去处理…诚然,是可以将和业务无关的代码抽取出来,实现非业务功能和业务逻辑的分离,让大家更加专注于业务逻辑,但是不要忘了,AOP可不仅仅能干这个噢。至于能干点儿啥新鲜的咱们后面再说。

下面这张图很形象的说明了AOP

 

 

提到AOP,那么就不得不搞懂几个概念。

1) Aspect :切面,既然是面向切面编程,没有切面还面向啥呢?什么是切面,切面是一个方面,代表需要解决的一个方面的问题,比如日志打印、比如事务管理、比如异常处理等等。

2)Join point :连接点,也就是横向切入的位置,程序执行的某个位置;

3) Pointcut :切点,符合切点规则的连接点集合(一个切点包含一个或多个连接点),也就代码真正需要被切入执行的地方;

4) Advice :增强,也就是一段代码,面向切面不是要解决某个方面的问题吗?解决问题是需要编写代码的,Advice就是代码的地方,也就是某个连接点,需要执行的具体操作(在Spring中分为: Before advice , After returning advice , After throwing advice ,After (finally) advice , Around advice );

5)Introduction:引介,一种特殊的增强方式,给一个类增加属性和方法,让原有的类具备增加新的功能和特性。

6)Weaving:织入,织入是指将增强设置到连接点的具体过程。

7)aop代理(AOP proxy)

spring中的aop代理有两种:jdk自带的动态代理和CGLIB代理。

上面说了一大堆,我们看点简单的原理性质的东西,——如何才能在不修改源代码的基础上实现新的功能呢?我们带着例子一步一步来看,我们有一个HelloService还有一个实现类HelloServiceImpl:

 

package com.pz.study.frame.aop.service; public interface HelloService {             public void sayHello();}

 

package com.pz.study.frame.aop.service.impl; import com.pz.study.frame.aop.service.HelloService; public class HelloServiceImpl implements HelloService {        @Override       public void sayHello() {              System.out.println("sayHello");        } }

 

还有一个测试用例

 

package com.pz.study.frame.aop.test; import org.junit.Test; import com.pz.study.frame.aop.service.HelloService;import com.pz.study.frame.aop.service.impl.HelloServiceImpl; public class TestDemo {             @Test       public voidtestSayHello(){                           HelloService helloService = new HelloServiceImpl();                           helloService.sayHello();                                 } }

 

我们知道,HelloServiceImpl的sayHello方法会输出”sayHello”。我们有没有什么办法在不修改HelloServiceImpl代码的情况下,让sayHello的方法做一点别的事情呢?别想了,直接看例子:

编写代码:

package com.pz.study.frame.aop.invocation; import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method; public class HelloInvocationHandler implements InvocationHandler {          private Object target;       public HelloInvocationHandler(Object target) {        this.target = target;    }        @Override       public Object invoke(Object proxy, Method method,Object[] args)                     throws Throwable {              method.invoke(target, args);              System.out.println("hello world");                           return null;       } }

 

 

 

编写测试用例

 

 

@Test       public void testSayHello2(){                           HelloInvocationHandler handler = new HelloInvocationHandler( new HelloServiceImpl() );                           HelloService helloService =(HelloService)Proxy.newProxyInstance(HelloService.class.getClassLoader(),  newClass<?>[]{HelloService.class},handler);                           helloService.sayHello();       }

 

运行测试用例控制台输出:

sayHello

hello world

这是什么鬼?没有修改HelloServiceImpl的情况下,调用了helloService.sayHello()方法,结果多输出了一行hello world!

这就是JDK的动态代理,动态代理?那什么是动态代理?动态代理是一种机制:在代码的运行时期,使用Proxy给某个接口创建一个代理对象。接口将方法委托给代理对象执行,JDK提供的动态代理是针对接口的,只能做接口增强的事情,而不能给某个类做增强的事情。要实现动态代理,需要以下步骤:

1.定义一个类实现java.lang.relect.invocation接口重写invoke方法,实现调用接口的具体逻辑。

这里要特别说一下,method.invoke方法会调用目标对象的方法(接口实现类),这个方法要不要调用根据具体的需求来看,不是一定要调用的。把一件自己要做的事情交给别人来完成,那个别人就是你的代理人,怎么做是代理人的事  情,结果怎么样也是他的事情。

2.使用Proxy.newProxyInstance来创建代理对象需要3个参数,ClassLoader,接口数组,需要处理接口方法调用的InvocationHandler对象。

3.将Proxy.newProxyInstance返回的参数强制转换为接口类型。

 

 

     已经忘记猿思考系列5——一文明白java和微商那点儿事儿,的同学,好好看看,懂了这些再看下一章节spring 的AOP的用法,喝水一样简单。

我建了一个技术群,群里有很多高手,加小编微信,备注:学习。带你见识更多的高手,帮你快速成长。


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值