原创内容,转载请注明出处
首先在写这篇文章前我给自己提了以下几个问题。Spring Aop是什么?为什么要有它的存在?它能解决什么实际问题吗?
先举一个编程的例子。比如一个功能,实现的是对数据库的一些增删改查的功能(方法名分别是add、delete、update、find),现在由于业务的变更,需要在对数据库操作完成后增加日志记录功能,记录当前人对数据库进行了什么样的操作,操作是否成功。这时首先想到的实现方式应该是在add、delete、update、find的方法内部最后面增加记录日志的实现方式。如果又因为业务的变更,还需要判断这个人是否有该操作的权限,如果没有,则不让该用户进行操作,这时我们又得修改所有的数据库操作的方法,在每个方法执行前校验用户的权限。
从上面这个例子可以看出实际的业务功能和权限功能、日志功能耦合在一起,当哪天需要修改或者剔除权限功能和日志功能的时候,这时就需要注释或修改代码了,这样因为一个需求的变更导致工作量成倍的增加显然不是程序员想要的结果。这时,我们可能会想,是否可以将这种通用的功能和业务功能分离开呢?答案是可以的,使用Jdk的动态代理就可以实现在不改变原来的业务功能基础上增加日志功能,如下面这个例子。
1、Jdk动态代理实现
用过Jdk动态代理的同学应该都知道,Jdk动态代理主要的类有Proxy和InvocationHandler。先看个例子
新建一个Action接口类,代码如下
package com.test.jdkproxy; public interface Action { void action(); }
新建Action的实现类ActionImp类,代码如下
package com.test.jdkproxy; public class ActionImp implements Action { public void action() { // TODO Auto-generated method stub System.out.println("-------执行操作--------"); } }
新建HandlerImp类,实现了InvocationHandler接口,代码如下
package com.test.jdkproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class HandlerImp implements InvocationHandler { private Object action; public HandlerImp(Action action){ this.action = action; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-------------记录开始日志-----------------"); Object obj = method.invoke(this.action, args); System.out.println("-------------记录成功日志-----------------"); return obj; } }
新建Junit测试代码
/** * 测试jdk动态代理 */ @Test public void testJdkProxy(){ Action obj = (Action) Proxy.newProxyInstance(ActionImp.class.getClassLoader(), ActionImp.class.getInterfaces(), new HandlerImp(new ActionImp())); obj.action(); }
如上,Proxy生成了一个Action的动态代理对象,并为Action的方法前后都增加了记录日志功能。
2、Spring 动态代理实现
上述的例子是一个Jdk动态代理实现的简单案例,也是一个简单的Aop实现。Spring Aop中的实现有两种,一种是Cglib实现,一种是Jdk动态代理。当目标类是接口的时候,Spring使用Jdk动态代理实现,否则Spring使用Cglib实现。下面来看看Spring Aop的实现例子。
创建业务接口类SqlManager,代码如下
package com.test.proxy; public interface SqlManager { public abstract void insert(String sql); }
创建业务实现类SqlManagerImp,代码如下
package com.test.proxy; public class SqlManagerImp implements SqlManager { public void insert(String sql){ System.out.println("--------执行插入!----------"); } }
创建日志通知类LogAdvice,代码如下
package com.test.proxy; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class LogAdvise implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("---------记录日志-------"); } }
创建bean.xml文件,如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="sqlManager" class="com.test.proxy.SqlManagerImp" lazy-init="true"/> <bean id="logAdvice" class="com.test.proxy.LogAdvise" lazy-init="true"/> <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="sqlManager"></property> <property name="proxyInterfaces"> <value>com.test.proxy.SqlManager</value> </property> <property name="interceptorNames"> <list> <value>logAdvice</value> </list> </property> </bean> </beans>
创建Junit测试代码如下
@Test public void testProxyFactoryBean(){ ApplicationContext bf = new FileSystemXmlApplicationContext("H:\\workspaceST\\cygoattest\\src\\test\\resources\\bean.xml"); SqlManager manager = (SqlManager) bf.getBean("factoryBean"); manager.insert(""); }
执行测试,结果如下
到此,Spring动态代理完成了添加日志功能,在这个例子中使用了ProxyFactoryBean,它实现了FactoryBean接口,在BeanFactory篇中有提到过FactoryBean。FactoryBean相当于工厂模式的Factory,是用来生产Bean的,如果在上例中是调用getBean(“&factoryBean”),则会返回一个FactoryBean实例;如果是调用getBean(“factoryBean”),则会先返回ProxyFactoryBean实例,然后通过ProxyFactoryBean的getObject()方法去返回一个SqlManager实例。
3、源码分析
在BeanFactory篇中依赖注入里面有讲到AbstractBeanFactory的getObjectForBeanInstace方法,在该方法会判断当前获取的bean是否是FactoryBean,如果是的话则会调用getObject方法去生产实际的bean,上例中是SqlManager。下面开始分析ProxyFactoryBean的getObject方法源码。
1.首先调用initializeAdvisorChain方法,该方法是将Advice转换对应的Advisor,然后保存到一个LinkedList集合中。
2.如果对象是单例的话调用getSingletonInstance方法,去获得单例动态代理对象,否则调用newPrototyInstance方法去获取动态代理对象。
3.织入通知Advice。Jdk的织入方式入口是InvocationHandler的invoke方法,Cglib方式暂不研究。
3.1、初始化Advisor链
1.在初始化Advisor链时,首先会判断初始化Advisor链标识是否为true,为true代表已经初始化过,直接返回,不需要重复初始化Advisor链。
2.遍历所有Advice名称。然后通过Advice名称让BeanFactory工厂去获取对应的Advice对象。(注:由于ProxyFactoryBean实现了BeanFactoryAware,故而在BeanFactory生成ProxyFactoryBean实例时,会往ProxyFactoryBean实例注入BeanFactory工厂)
3.通过DefaultAdvisorAdapterRegistry对象的wrap包装方法,将获取Advice对象包装成Advisor对象,这里包装成DefaultPointcutAdvisor。
4.将包装好的Advisor对象装入LinkedList集合中。
3.2、获取代理对象
1.调用DefaultAopProxyFactory的createAopProxy方法去获得AopProxy实例,如果目标类是接口则返回JdkDynamicAopProxy实例,否则返回CglibAopProxy实例。
2.调用对应AopProxy实例的getProxy方法去返回动态代理对象,其中JdkDynamicAopProxy是调用Proxy.newProxyInstance返回动态代理对象,也就是说JdkDynamibiancAopProxy的实现就是Jdk动态代理。
3.3、织入通知Advice(Jdk方式)
JdkDynamicAopProxy本身实现了InvocationHandler接口,所以在调用Proxy.newProxyInstance(classLoader,proxiedInterfaces,this)使用的是this,下面来看JdkDynamicAopProxyProxy是怎么实现InvocationHandler的invoke方法。
1.通过AdvisedSupport类的getInterceptorAndDynamicInterceptorAdvice去获取方法对应的方法拦截器MethodInterceptor链。
2.如果方法拦截器链是空,则直接通过Java的反射调用方法的执行;否则遍历执行所有拦截器,然后再通过Java反射调用方法的执行。
3.4、拦截器链获取
1.AdvisedSupport类首先去缓存中获取拦截器,如果缓存中不存在拦截器,则调用DefaultAdvisorChainFactory类的getInterceptorsAndDynamicInterceptorAdvice去获取拦截器。
2.在DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptorAdvice获取拦截器的方法中,实际上调用了DefaultAdvisorAdaterRegistryAdvisor类的getInterceptors方法去将Advisor适配成对应方法拦截器MethodInterceptor(使用适配器模式,通过对应的适配器,将Advisor适配成对应的MethodInterceptor。比如MethodBeforeAdviceAdapter适配器将MethodBeforeAdvice适配成MethodBeforeAdviceInterceptor)。
2.1.在DefaultAdvisorAdaterRegistryAdvisor类的构造函数中,首先注册了一系列的默认适配器(MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter)。
2.2.在getInterceptors方法中,循环所有适配器去适配Advisor,并将Advisor适配成对应的MethodInterceptor,并返回。
2.3.如果advisor实例是PointcutAdvisor,则获取该实例的MethodMatcher,调用matches方法去判断目标方法是否匹配,如果匹配,则返回适配后的MethodInterceptor。
3.JdkDynamicAopProxy将相关代理信息(代理对象,目标对象,方法和参数,目标类,拦截器)封装成一个ReflectiveMethodInvocation实例,并调用该实例的proceed方法处理。
3.1该方法循环遍历List集合interceptorsAndDynamicMethodMatchers拦截器链中的方法拦截器(MethodInterceptor或InterceptorAndDynamicMethodMatcher(该对象内有MethodInterceptor实例成员)对象)。
3.2调用MethodInterceptor的invoke方法去完成Advice通知的执行。
源代码如附件