最近在开发中遇到了一个刚好可以用AOP实现的例子,就顺便研究了AOP的实现原理,把学习到的东西进行一个总结。文章中用到的编程语言为kotlin,需要的可以在IDEA中直接转为java。
这篇文章将会按照如下目录展开:
- AOP简介
- 代码中实现举例
- AOP实现原理
- 部分源码解析
1. AOP简介
相信大家或多或少的了解过AOP,都知道它是面向切面编程,在网上搜索可以找到很多的解释。这里我用一句话来总结:AOP是能够让我们在不影响原有功能的前提下,为软件横向扩展功能。
那么横向扩展怎么理解呢,我们在WEB项目开发中,通常都遵守三层原则,包括控制层(Controller)->业务层(Service)->数据层(dao),那么从这个结构下来的为纵向,它具体的某一层就是我们所说的横向。我们的AOP就是可以作用于这某一个横向模块当中的所有方法。
我们在来看一下AOP和OOP的区别:AOP是OOP的补充,当我们需要为多个对象引入一个公共行为,比如日志,操作记录等,就需要在每个对象中引用公共行为,这样程序就产生了大量的重复代码,使用AOP可以完美解决这个问题。
接下来介绍一下提到AOP就必须要了解的知识点:
切面:拦截器类,其中会定义切点以及通知
切点:具体拦截的某个业务点。
通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:
- 前置通知:@Before 在目标业务方法执行之前执行
- 后置通知:@After 在目标业务方法执行之后执行
- 返回通知:@AfterReturning 在目标业务方法返回结果之后执行
- 异常通知:@AfterThrowing 在目标业务方法抛出异常之后
- 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行
2. 代码中实现举例
上面已经大概的介绍了AOP中需要了解的基本知识,也知道了AOP的好处,那怎么在代码中实现呢?给大家举个例子:我们现在有个学校管理系统,已经实现了对老师和学生的增删改,又新来个需求,说是对老师和学生的每次增删改做一个记录,到时候校长可以查看记录的列表。
那么问题来了,怎么样处理是最好的解决办法呢?这里我罗列了三种解决办法,我们来看下他的优缺点。
最简单的就是第一种方法,我们直接在每次的增删改的函数当中直接实现这个记录的方法,这样代码的重复度太高,耦合性太强,不建议使用。
其次就是我们最长使用的,将记录这个方法抽离出来,其他的增删改调用这个记录函数即可,显然代码重复度降低,但是这样的调用还是没有降低耦合性。
这个时候我们想一下AOP的定义,再想想我们的场景,其实我们就是要在不改变原来增删改的方法,给这个系统增加记录的方法,而且作用的也是一个层面的方法。这个时候我们就可以采用AOP来实现了。
我们来看下代码的具体实现:
1,首先我定义了一个自定义注解作为切点
1@Target(AnnotationTarget.FUNCTION)
2//注解作用的范围,这里声明为函数
3@Order(Ordered.HIGHEST_PRECEDENCE)
4//声明注解的优先级为最高,假设有多个注解,先执行这个
5annotation class Hanler(val handler: HandlerType)
6//自定义注解类,HandlerType是一个枚举类型,里面定义的就是学生和老师的增删改操作,在这里就不展示具体内容了
1@Aspect //该注解声明这个类为一个切面类
2@Component
3class HandlerAspect{
4
5 @Autowired
6 private lateinit var handlerService: HandlerService
7
8@AfterReturning("@annotation(handler)") //当有函数注释了注解,将会在函数正常返回后在执行我们定义的方法
9fun hanler(hanler: Hanler) {
10 handlerService.add(handler.operate.value) //这里是真正执行记录的方法
11}
12}
1/**
2* 删除学生方法
3*/
4@Handler(operate= Handler.STUDENT_DELETE) //当执行到删除学生方法时,切面类就会起作用了,当学生正常删除后就会执行记录方法,我们就可以看到记录方法生成的数据
5fun delete(id:String) {
3. AOP实现原理
我们现在了解了代码中如何实现,那么AOP实现的原理是什么呢?之前看了一个博客说到,提到AOP大家都知道他的实现原理是动态代理,显然我之前就是不知道的,哈哈,但是相信阅读文章的你们一定是知道的。
讲到动态代理就不得不说代理模式了,代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
代理模式包含如下角色:
- subject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口;
- RealSubject:真实主题角色,是实现抽象主题接口的类;
- Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。
代理对象提供与真实对象相同的接口,以便代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。如下图所示:
那么代理又分为静态代理和动态代理,这里写两个小的demo,动态代理采用的就是JDK代理。举个例子就是现在一个班上的学生需要交作业,现在由班长代理交作业,那么班长就是代理,学生就是被代理的对象。
3.1 静态代理
首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有交作业的行为。这样,学生交作业就可以让班长来代理执行。
1/**
2 * 创建person接口
3 */
4public interface Person {
5 //交作业
6 void giveTask();
7}
1public class Student implements Person {
2 private String name;
3 public Student(String name) {
4 this.name = name;
5 }
6
7 public void giveTask() {
8 System.out.println(name + "交语文作业");
9 }
10}
1/**
2 * 学生代理类,也实现了Person接口,保存一个学生实体,这样就可以代理学生产生行为
3 */
4public class StudentsProxy implements Person{
5 //被代理的学生
6 Student stu;
7
8 public StudentsProxy(Person stu) {
9 // 只代理学生对象
10 if(stu.getClass() == Student.class) {
11 this.stu = (Student)stu;
12 }
13 }
14
15 //代理交作业,调用被代理学生的交作业的行为
16 public void giveTask() {
17 stu.giveTask();
18 }
19}
1public class StaticProxyTest {
2 public static void main(String[] args) {
3 //被代理的学生林浅,他的作业上交有代理对象monitor完成
4 Person linqian = new Student("林浅");
5
6 //生成代理对象,并将林浅传给代理对象
7 Person monitor = new StudentsProxy(linqian);
8
9 //班长代理交作业
10 monitor.giveTask();
11 }
12}
这里并没有直接通过林浅(被代理对象)来执行交作业的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。
代理模式就是在访问实际对象时引入一定程度的间接性,这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。
比如班长在帮林浅交作业的时候想告诉老师最近林浅的进步很大,就可以轻松的通过代理模式办到。在代理类的交作业之前加入方法即可。这个优点就可以运用在spring中的AOP,我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。
3.2 动态代理
动态代理和静态代理的区别是,静态代理的的代理类是我们自己定义好的,在程序运行之前就已经变异完成,但是动态代理的代理类是在程序运行时创建的。
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。比如我们想在每个代理方法之前都加一个处理方法,我们上面的例子中只有一个代理方法,如果还有很多的代理方法,就太麻烦了,我们来看下动态代理是怎么去实现的。
首先还是定义一个Person接口:
1/**
2 * 创建person接口
3 */
4public interface Person {
5 //交作业
6 void giveTask();
7}
1public class Student implements Person {
2 private String name;
3 public Student(String name) {
4 this.name = name;
5 }
6
7 public void giveTask() {
8 System.out.println(name + "交语文作业");
9 }
10}
1public class StuInvocationHandler<T> implements InvocationHandler {
2 //invocationHandler持有的被代理对象
3 T target;
4
5 public StuInvocationHandler(T target) {
6 this.target = target;
7 }
8
9 /**
10 * proxy:代表动态代理对象
11 * method:代表正在执行的方法
12 * args:代表调用目标方法时传入的实参
13 */
14 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
15 System.out.println("代理执行" +method.getName() + "方法");
16 Object result = method.invoke(target, args);
17 return result;
18 }
1/**
2 * 代理类
3 */
4public class ProxyTest {
5 public static void main(String[] args) {
6
7 //创建一个实例对象,这个对象是被代理的对象
8 Person linqian = new Student("林浅");
9
10 //创建一个与代理对象相关联的InvocationHandler
11 InvocationHandler stuHandler = new StuInvocationHandler<Person>(linqian);
12
13 //创建一个代理对象stuProxy来代理linqian,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
14 Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
15
16 //代理执行交作业的方法
17 stuProxy.giveTask();
18 }
19}
那么到这里问题就来了,为什么代理对象执行的方法都会通过InvocationHandler中的invoke方法来执行,带着这个问题,我们需要看一下动态代理的源码,对他进行简单的分析。
上面我们使用Proxy类的newProxyInstance方法创建了一个动态代理对象,看一下他的源码:
1public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
2 throws IllegalArgumentException
3 {
4 Objects.requireNonNull(h);
5
6 final Class<?>[] intfs = interfaces.clone();
7 final SecurityManager sm = System.getSecurityManager();
8 if (sm != null) {
9 checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
10 }
11
12 /*
13 * Look up or generate the designated proxy class.
14 */
15 Class<?> cl = getProxyClass0(loader, intfs);
16
17 /*
18 * Invoke its constructor with the designated invocation handler.
19 */
20 try {
21 if (sm != null) {
22 checkNewProxyPermission(Reflection.getCallerClass(), cl);
23 }
24
25 final Constructor<?> cons = cl.getConstructor(constructorParams);
26 final InvocationHandler ih = h;
27 if (!Modifier.isPublic(cl.getModifiers())) {
28 AccessController.doPrivileged(new PrivilegedAction<Void>() {
29 public Void run() {
30 cons.setAccessible(true);
31 return null;
32 }
33 });
34 }
35 return cons.newInstance(new Object[]{h});
36 } catch (IllegalAccessException|InstantiationException e) {
37 throw new InternalError(e.toString(), e);
38 } catch (InvocationTargetException e) {
39 Throwable t = e.getCause();
40 if (t instanceof RuntimeException) {
41 throw (RuntimeException) t;
42 } else {
43 throw new InternalError(t.toString(), t);
44 }
45 } catch (NoSuchMethodException e) {
46 throw new InternalError(e.toString(), e);
47 }
48 }
1byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
2 String path = "/Users/mapei/Desktop/okay/65707.class";
3
4 try{
5 FileOutputStream fos = new FileOutputStream(path);
6 fos.write(classFile);
7 fos.flush();
8 System.out.println("代理类class文件写入成功");
9 }catch (Exception e) {
10 System.out.println("写文件错误");
11 }
1import java.lang.reflect.InvocationHandler;
2import java.lang.reflect.Method;
3import java.lang.reflect.Proxy;
4import java.lang.reflect.UndeclaredThrowableException;
5import proxy.Person;
6
7public final class $Proxy0 extends Proxy implements Person
8{
9 private static Method m1;
10 private static Method m2;
11 private static Method m3;
12 private static Method m0;
13
14 /**
15 *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
16 *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
17 *被代理对象的实例,就可以去调用真正的对象实例。
18 */
19 public $Proxy0(InvocationHandler paramInvocationHandler)
20 throws
21 {
22 super(paramInvocationHandler);
23 }
24
25 //这个静态块本来是在最后的,我把它拿到前面来,方便描述
26 static
27 {
28 try
29 {
30 //看看这儿静态块儿里面的住giveTask通过反射得到的名字m3,其他的先不管
31 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
32 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
33 m3 = Class.forName("proxy.Person").getMethod("giveTask", new Class[0]);
34 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
35 return;
36 }
37 catch (NoSuchMethodException localNoSuchMethodException)
38 {
39 throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
40 }
41 catch (ClassNotFoundException localClassNotFoundException)
42 {
43 throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
44 }
45 }
46
47 /**
48 *
49 *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
50 *this.h.invoke(this, m3, null);我们可以对将InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。
51 */
52 public final void giveTask()
53 throws
54 {
55 try
56 {
57 this.h.invoke(this, m3, null);
58 return;
59 }
60 catch (Error|RuntimeException localError)
61 {
62 throw localError;
63 }
64 catch (Throwable localThrowable)
65 {
66 throw new UndeclaredThrowableException(localThrowable);
67 }
68 }
69
70}
4. 部分源码解析
aop创建代理的源码分析
1,看一下bean如何被包装为proxy
1protected Object createProxy(
2 Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
3
4 if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
5 AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
6 }
7
8 // 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的
9 ProxyFactory proxyFactory = new ProxyFactory();
10 proxyFactory.copyFrom(this);
11
12 if (!proxyFactory.isProxyTargetClass()) {
13 if (shouldProxyTargetClass(beanClass, beanName)) {
14 proxyFactory.setProxyTargetClass(true);
15 }
16 else {
17 evaluateProxyInterfaces(beanClass, proxyFactory);
18 }
19 }
20
21 // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中
22 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
23 for (Advisor advisor : advisors) {
24 proxyFactory.addAdvisor(advisor);
25 }
26
27 proxyFactory.setTargetSource(targetSource);
28 customizeProxyFactory(proxyFactory);
29
30 proxyFactory.setFrozen(this.freezeProxy);
31 if (advisorsPreFiltered()) {
32 proxyFactory.setPreFiltered(true);
33 }
34
35 // 3.调用getProxy获取bean对应的proxy
36 return proxyFactory.getProxy(getProxyClassLoader());
37 }
2,创建何种类型的Proxy?JDKProxy还是CGLIBProxy?
1protected Object createProxy(
2 Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
3
4 if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
5 AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
6 }
7
8 // 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的
9 ProxyFactory proxyFactory = new ProxyFactory();
10 proxyFactory.copyFrom(this);
11
12 if (!proxyFactory.isProxyTargetClass()) {
13 if (shouldProxyTargetClass(beanClass, beanName)) {
14 proxyFactory.setProxyTargetClass(true);
15 }
16 else {
17 evaluateProxyInterfaces(beanClass, proxyFactory);
18 }
19 }
20
21 // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中
22 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
23 for (Advisor advisor : advisors) {
24 proxyFactory.addAdvisor(advisor);
25 }
26
27 proxyFactory.setTargetSource(targetSource);
28 customizeProxyFactory(proxyFactory);
29
30 proxyFactory.setFrozen(this.freezeProxy);
31 if (advisorsPreFiltered()) {
32 proxyFactory.setPreFiltered(true);
33 }
34
35 // 3.调用getProxy获取bean对应的proxy
36 return proxyFactory.getProxy(getProxyClassLoader());
37 }
382,创建何种类型的Proxy?JDKProxy还是CGLIBProxy?
39public Object getProxy(ClassLoader classLoader) {
40 return createAopProxy().getProxy(classLoader);
41 }
42 // createAopProxy()方法就是决定究竟创建何种类型的proxy
43 protected final synchronized AopProxy createAopProxy() {
44 if (!this.active) {
45 activate();
46 }
47 // 关键方法createAopProxy()
48 return getAopProxyFactory().createAopProxy(this);
49 }
50
51 // createAopProxy()
52 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
53 // 1.config.isOptimize()是否使用优化的代理策略,目前使用与CGLIB
54 // config.isProxyTargetClass() 是否目标类本身被代理而不是目标类的接口
55 // hasNoUserSuppliedProxyInterfaces()是否存在代理接口
56 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
57 Class<?> targetClass = config.getTargetClass();
58 if (targetClass == null) {
59 throw new AopConfigException("TargetSource cannot determine target class: " +
60 "Either an interface or a target is required for proxy creation.");
61 }
62
63 // 2.如果目标类是接口类(目标对象实现了接口),则直接使用JDKproxy
64 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
65 return new JdkDynamicAopProxy(config);
66 }
67
68 // 3.其他情况则使用CGLIBproxy
69 return new ObjenesisCglibAopProxy(config);
70 }
71 else {
72 return new JdkDynamicAopProxy(config);
73 }
74 }
3,getProxy()方法
1final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable// JdkDynamicAopProxy类结构,由此可知,其实现了InvocationHandler,则必定有invoke方法,来被调用,也就是用户调用bean相关方法时,此invoke()被真正调用
2 // getProxy()
3 public Object getProxy(ClassLoader classLoader) {
4 if (logger.isDebugEnabled()) {
5 logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
6 }
7 Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
8 findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
9
10 // JDK proxy 动态代理的标准用法
11 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
12 }
4,invoke()方法法
1//使用了JDK动态代理模式,真正的方法执行在invoke()方法里,看到这里在想一下上面动态代理的例子,是不是就完全明白Spring源码实现动态代理的原理了。
2 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
3 MethodInvocation invocation;
4 Object oldProxy = null;
5 boolean setProxyContext = false;
6
7 TargetSource targetSource = this.advised.targetSource;
8 Class<?> targetClass = null;
9 Object target = null;
10
11 try {
12 // 1.以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法
13 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
14 // The target does not implement the equals(Object) method itself.
15 return equals(args[0]);
16 }
17 else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
18 // The target does not implement the hashCode() method itself.
19 return hashCode();
20 }
21 else if (method.getDeclaringClass() == DecoratingProxy.class) {
22 // There is only getDecoratedClass() declared -> dispatch to proxy config.
23 return AopProxyUtils.ultimateTargetClass(this.advised);
24 }
25 else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
26 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
27 // Service invocations on ProxyConfig with the proxy config...
28 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
29 }
30
31 Object retVal;
32
33 if (this.advised.exposeProxy) {
34 // Make invocation available if necessary.
35 oldProxy = AopContext.setCurrentProxy(proxy);
36 setProxyContext = true;
37 }
38
39 // May be null. Get as late as possible to minimize the time we "own" the target,
40 // in case it comes from a pool.
41 target = targetSource.getTarget();
42 if (target != null) {
43 targetClass = target.getClass();
44 }
45 // 2.获取当前bean被拦截方法链表
46 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
47
48 // 3.如果为空,则直接调用target的method
49 if (chain.isEmpty()) {
50 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
51 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
52 }
53 // 4.不为空,则逐一调用chain中的每一个拦截方法的proceed,这里的一系列执行的原因以及proceed执行的内容,我 在这里就不详细讲了,大家感兴趣可以自己去研读哈
54 else {
55 // We need to create a method invocation...
56 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
57 // Proceed to the joinpoint through the interceptor chain.
58 retVal = invocation.proceed();
59 }
60
61