对tiny-spring项目的学习,转载自黄亿华大佬的<<1000行代码读懂Spring(一)- 实现一个基本的IoC容器>>。
原网址:
http://my.oschina.net/flashsword/blog/192551
https://my.oschina.net/flashsword/blog/194481
概念的讲解转自 https://blog.csdn.net/Tritoy/article/details/81010595
IOC 什么是IOC? 来自知乎:https://www.zhihu.com/question/23277575/answer/24259844
Inversion of Control,即控制反转,是一种设计思想。
传统程序设计中,创建对象的主动权是由自己把控的;现在由Spring的IoC容器根据配置文件来创建及注入依赖对象,而不是由对象主动去找。
什么是DI? Dependency Injection,即依赖注入,和IOC是同一个概念不同角度的描述。组件之间的依赖关系由容器在运行期决定,由容器动态的将依赖关系注入到组件之中。DI的目的是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。
举个栗子 IOC 找女朋友一般情况就是到处去看哪里有符合自己审美的,然后打听她们的兴趣爱好、电话等等。传统程序开发也是如此,在一个对象中,如果要使用其他的对象,我们需要手动new一个。
Ioc相当于一个婚恋平台,你只需要告诉它们你的要求,然后它们会根据要求给你提供一个女朋友,整个过程都是由平台来控制。Spring的开发方式也是如此。所有的类都会在容器中登记,告诉spring它是什么,需要什么,然后spring会在运行到适当的时候把你要的东西给你,同时也把你给其他需要你的东西。所有类的创建、销毁都有spring控制。以前是对象控制其他对象,现在是spring控制所有对象,所以叫控制反转。
DI IoC的一个重点就是在系统运行中,动态的向某个对象提供它所需要的其他对象,这就是通过DI实现的。
比如对象A需要操作数据库,以前我们是自己编码获取一个Connction对象,现在我们只需要告诉spring,A中需要一个Connection,至于Connection怎么构造、何时构造我们都不需要知道。spring会在适当的时候向A注射一个Connection,这就是依赖注入。
tiny-spring分析 克隆项目到本地
我在原来项目的基础上添加了一些中文注释,github项目地址:https://github.com/zcsherrydc/zcs-spring.git
每一步的命令需要更改为对应的序号,如第一步就是step1,第二步就是step2
1 git checkout origin/step1
step1-最基本的容器 1 git checkout step-1 -container-register-and-get
IoC最基本的角色有两个:容器(BeanFactory)和Bean本身。BeanDefinition封装了Bean对象。
step2-在工厂中创建bean 1 git checkout step-2 -abstract-beanfactory-and-do -bean-initilizing-in -it
step1中的bean是初始化好之后再set进去的,实际使用中我们希望容器来管理bean的创建。为了方便扩展,抽象出BeanFactory接口和AbstractBeanFactory类。
AbstractBeanFactory里面最重要的是抽象方法doCreateBean()。通过反射获取bean的实例,放入BeanDefinition中,并注册到BeanFactory。
1 2 3 4 5 6 public void registerBeanDefinition (String name, BeanDefinition beanDefinition) { Object bean = doCreateBean(beanDefinition); beanDefinition.setBean(bean); beanDefinitionMap.put(name, beanDefinition); }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void test () { BeanFactory beanFactory = new AutowireCapable(); BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setBeanClassName("zcs.ioc.HelloWorldService" ); beanFactory.registerBeanDefinition("helloWorldService" ,beanDefinition); HelloWorldService helloWorldService= (HelloWorldService) beanFactory.getBean("helloWorldService" ); helloWorldService.helloWorld(); }
我们需要给BeanFactory传入完整类名和实例名称。这些都可以通过xml文件获取,在step4中完成。还有一个问题就是现在是通过无参构造函数创建的对象,内部成员变量都是null,所以下一步是对成员变量赋值。
step3-为bean注入属性 1 git checkout step-3 -inject-bean-with-property
创建两个类,PropertyValue和PropertyValues,前者保存一个字段的名称和对应值,后者有一个集合保存了所有的PropertyValue。每个BeanDefinition有一个PropertyValues。
AutowireCapable中新增属性注入方法。Spring使用setter进行注入,这里为了简单使用Field。
1 2 3 4 5 6 7 protected void applyPropertyValues (Object bean, BeanDefinition mbd) throws Exception { for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); declaredField.setAccessible(true ); declaredField.set(bean, propertyValue.getValue()); } }
HelloWorldService新增text字段。
step4-读取xml来初始化bean 1 git checkout step-4-config-beanfactory-with-xml
看一下文件结构:
Resource
类名
说明
Resource
接口,通过getInputStream()获取资源的输入流
UrlResource
实现Resource接口,通过URL获取资源
ResourceLoader
资源加载类,通过getResource()获取一个Resource对象
类名
说明
BeanDefinition
Bean的包装类,包括Bean的名字、类型、属性键值对
BeanDefinitionReader
解析 BeanDefinition 的接口。通过 loadBeanDefinitions(String) 从一个地址来加载类定义。
AbstractBeanDefinitionReader
实现 BeanDefinitionReader 接口的抽象类
XmlBeanDefinitionReader
具体实现了 loadBeanDefinitions() 方法,从 XML 文件中读取类定义
XmlBeanDefinitionReader负责读取xml,把标签封装成BeanDefinition注册到registry,之后把registry都注册到AutowireCapableBeanFactory中
整个读取流程:
new一个ResourceLoader放入XmlBeanDefinition中,通过loadBeanDefinition传入xml文件地址
获取xml文件的输入流
通过DocumentBuilder解析出Document。先获取所有的bean,然后根据bean名称和类名构建空实例,对当前的bean继续解析,根据property实现对bean属性的赋值
将解析好的bean注册到BeanFactory
step5-为bean注入bean 1 git checkout step-5-inject-bean-to-bean
目前为止,我们只是处理了bean之间没有依赖的情况,还无法处理bean之间的依赖。
我们定义一个BeanReference来表示这个属性是对另一个Bean的引用。在XMLBeanDefinition的processProperty方法中加入判断。如果是ref,就new一个BeanReference封装到PropertyValue.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private void processProperty (Element ele, BeanDefinition beanDefinition) { NodeList propertyNode = ele.getElementsByTagName("property" ); for (int i = 0 ; i < propertyNode.getLength(); i++) { Node node = propertyNode.item(i); if (node instanceof Element) { Element propertyEle = (Element) node; String name = propertyEle.getAttribute("name" ); String value = propertyEle.getAttribute("value" ); if (value!=null && value.length()>0 ) { beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value)); }else { String ref=propertyEle.getAttribute("ref" ); if (ref == null || ref.length() == 0 ) { throw new IllegalArgumentException("Configuration problem: <property> element for property '" + name + "' must specify a ref or value" ); } BeanReference beanReference = new BeanReference(ref); beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference)); } } } }
解析完成之后,BeanFactory中只有<String, BeanDefinition>信息,并没有bean实例,即bean为null。
同时为了解决循环依赖的问题,我们使用懒加载的方式,即在需要使用的时候才创建。如果属性对应的bean找不到,那么就先创建。因为总是先创建后注入,所以不会存在循环依赖的问题。
1 2 3 HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService" ); helloWorldService.helloWorld();
xml中的配置
1 2 3 4 5 6 7 8 <bean name ="helloWorldService" class ="zcs.ioc.HelloWorldService" > <property name ="text" value ="Hello World!" > </property > <property name ="outputService" ref ="outputService" > </property > </bean > <bean name ="outputService" class ="zcs.ioc.OutputService" > <property name ="helloWorldService" ref ="helloWorldService" > </property > </bean >
step6-ApplicationContext登场 1 git checkout step-6-invite-application-context
现在功能差不多齐全了,但是使用起来比较麻烦,于是引入ApplicationContext接口,并在refresh()方法下进行bean的初始化。
1 2 3 4 5 public ClassPathXmlApplicationContext (String configLocation, AbstractBeanFactory beanFactory) throws Exception { super (beanFactory); this .configLocation = configLocation; refresh(); }
所以使用的时候只需要传入xml地址就可以了
1 2 3 4 5 public void test () throws Exception { ApplicationContext applicationContext=new ClassPathXmlApplicationContext("tinyioc.xml" ); HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService" ); helloWorldService.helloWorld(); }
至此为止,tiny-spring的IoC部分基本结束。
AOP 什么是AOP 即面向切面编程,将分散在各个业务逻辑代码中相同的部分通过横向切割的方式提取到一个独立的模块中
比如日志记录、安全控制、事务处理、异常处理等
Spring Aop主要是通过动态代理实现的,先看一下代理的相关内容
什么是代理 给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。
使用代理的最主要原因就是在不改变目标对象的情况下对方法进行增强。比如,增加日志等。
举个栗子 有一个接口,只有hello的方法
1 2 3 public interface IHello { void hello () ; }
有一个实现类
1 2 3 4 5 6 public class HelloImpl implements IHello { @Override public void hello () { System.out.println("hello" ); } }
现在想在方法被调用的时候添加日志。如果直接在实现类上改,那么所有的实现类都需要改,显而易见,这是非常繁琐的一件事。当新增实现类时,也需要做这种繁琐重复的事情,代码可维护性极低。
那么该怎么才能一次性完成这项工作呢?这就需要代理了。
静态代理 举个栗子理解一下
有一排瓶子放在那不动,任务是给每个瓶子贴上标签。
方法一:每次需要改标签你要自己一个个去贴。
方法二:现在有了流水线,你把之前贴标签的任务交给了流水线上的机器来做,只要往机器里输入标签内容,把需要改的瓶子放入流水线,出来的就是改过标签的瓶子了,这样就方便多了。
这里的瓶子就相当于实现类,静态代理就相当于方法二,代理类相当于流水线的机器,在代理类中进行方法的修改(比如添加日志)。只要将需要修改的实现类(委托类)传给代理类即可。
target是需要修改的实现类,通过构造函数传入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class HelloProxy implements IHello { private IHello target; public HelloProxy (IHello target) { this .target = target; } @Override public void hello () { System.out.println("start" ); target.hello(); System.out.println("end" ); } }
1 2 3 4 5 6 @Test public void test () { IHello hello=new HelloImpl(); HelloProxy helloProxy=new HelloProxy(hello); helloProxy.hello(); }
输出结果如下:
优点
客户端不需要知道实现类是什么,只需知道代理类即可
缺点
代理类和实现类(委托类)实现了相同的接口,接口类每增加一个方法,代理类和实现类都需要实现,增加了维护的复杂度。例如想在每个方法都添加日志,那么代理类的每个实现方法也都要添加
一个代理类只对应一种接口,如果想对两个接口都添加日志,那么就需要两个代理类。静态代理只能为特定的接口服务
动态代理 在程序开发中,静态代理会产生许多代理类,所以就需要动态代理了。
动态代理是通过反射机制实现的,能够代理各种类型的对象。缺点:委托类一定要有接口
实现动态代理需要两个类:
java.lang.reflect.InvocationHandler接口
1 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable ;
该接口只有一个方法,代理类需要实现这个方法来实现方法的调用
java.lang.reflect.Proxy类
1 2 3 4 public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
通过该方法得到一个代理类对象
代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class DynamicProxy { private Object target; public DynamicProxy (Object target) { this .target = target; } public Object getProxy () { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler(){ @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start" ); Object ret = method.invoke(target, args); System.out.println("end" ); return ret; } }); } }
测试
1 2 3 4 5 6 7 @Test public void test () { DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl()); IHello hello1 = (IHello) dynamicProxy.getProxy(); hello1.hello(); }
细说一下底层实现原理,主要实现就是newProxyInstance方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @CallerSensitive public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null ) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } Class<?> cl = getProxyClass0(loader, intfs); try { ``` final Constructor<?> cons = cl.getConstructor(constructorParams); ``` return cons.newInstance(new Object[]{h}); } catch ··· }
主要看一下getProxyClass0方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535 ) { throw new IllegalArgumentException("interface limit exceeded" ); } return proxyClassCache.get(loader, interfaces); } private final BiFunction<K, P, ?> subKeyFactory;Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>{ @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { ``` byte [] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0 , proxyClassFile.length); } catch ``` } }
CGLIB 肯定不可能所有的类都实现了接口,所以动态代理并不适用所有类
这时候就需要CGLIB登场,原理是在通过ASM指令动态生成被代理类的子类,并重写所有的非private、非final的方法。
写一个国际通用入门类
1 2 3 4 5 public class HelloWorld { public void hello () { System.out.println("hello world" ); } }
写CGLIB代理类,代理类需要实现MethodInterceptor接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CGLibTest implements MethodInterceptor { public Object getInstance (Class<?> target) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target); enhancer.setCallback(this ); return enhancer.create(); } @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("开始" ); Object invoke = methodProxy.invokeSuper(o, objects); System.out.println("结束" ); return invoke; } }
1 2 3 4 5 @Test public void test () throws Exception { HelloWorld helloWorld= (HelloWorld) new CGLibTest().getInstance(HelloWorld.class ) ; helloWorld.hello(); }
注意事项:
委托类不能是final
方法如果是final/static,不会被拦截
原理 底层原理是动态代理,使用了两种:
JDK动态代理,默认,实现接口的类,创建速度快
CGLib动态代理,适用没有实现接口的类,运行速度快
如果是单例,最好使用CGLib代理
相关概念 Join point(连接点) 能够被拦截的地方,每个成员方法都可以是连接点
Pointcut(切点) 具体定位的连接点(方法)就是切点
Advice(通知/增强) 表示在切点上具体需要执行的操作 ,提供了前置、后置、环绕、异常、返回这5种操作
Aspect(切面) 类似Java中的类声明,由切点和相应的Advice结合而成
Weaving(织入) 把Advice应用到目标对象来创建新的代理对象的过程
Spring对AOP的支持
基于代理:现在基本不使用 首先看一下增强接口的继承关系图
分为5类:
tiny-spring分析 继续之前的代码分析
step7-使用JDK动态代理实现AOP织入 我们知道,想要成功代理的话需要委托实例和委托实例的接口
这是一个完整的动态代理结构图,我们一点一点分析
我们把实例和Class(其实是Interface)封装进TargetSource,把TargetSource和MethodInterceptor封装进AdvisedSupport,那么MethodInterceptor是个什么东西呢?
AOP中有两个重要的角色:MethodInterceptor和MethodInvocation,它们分别对应上文提到的Advice和Join Point两个概念,我们知道AOP的作用就是在哪干,干什么,Join Point表示在哪个方法上执行,而Advice就是要干什么
1 2 3 4 public interface MethodInterceptor extends Interceptor { Object invoke (MethodInvocation var1) throws Throwable ; }
1 2 3 4 public interface MethodInvocation extends Invocation { Method getMethod () ; }
所以我们需要做的事情都要写在MethodInterceptor中,而且在上一部分中我们了解到它代表环绕增强,表示在目标方法前后实施增强
让我们来写一个小代码练练手,还是用之前的HelloWorldService接口
1、先写一个MethodInterceptor实现类,表示我们需要做什么 这个接口只有一个方法,重写即可,invocation.proceed()代表执行原方法,前后的代码是我们新增的非业务逻辑代码,这样就写好了要做什么
1 2 3 4 5 6 7 8 9 10 11 12 public class TimerInterceptor implements MethodInterceptor { @Override public Object invoke (MethodInvocation invocation) throws Throwable { long time = System.nanoTime(); System.out.println("Invocation of Method " + invocation.getMethod().getName() + " start!" ); Object proceed = invocation.proceed(); System.out.println("Invocation of Method " + invocation.getMethod().getName() + " end! takes " + (System.nanoTime() - time) + " nanoseconds." ); return proceed; } }
2、接下来写要在哪做,通过前面的分析我们知道,需要实现MethodInvocation接口 这是MethodInvocation的继承关系图,可以看到,顶层接口是Joinpoint,也就是连接点
新建一个实现类,需要重写上述接口的所有方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class ReflectiveMethodInvocation implements MethodInvocation { private Object target; private Method method; private Object[] args; public ReflectiveMethodInvocation (Object target, Method method, Object[] args) { this .target = target; this .method = method; this .args = args; } @Override public Method getMethod () { return method; } @Override public Object[] getArguments() { return args; } @Override public Object proceed () throws Throwable { return method.invoke(target, args); } @Override public Object getThis () { return target; } @Override public AccessibleObject getStaticPart () { return method; } }
3、新建一个动态代理类,通过getProxy反射获取一个实例对象,然后调用invoke方法实现动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class JdkDynamicAopProxy implements AopProxy , InvocationHandler { private AdvisedSupport advised; public JdkDynamicAopProxy (AdvisedSupport advised) { this .advised = advised; } @Override public Object getProxy () { return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { advised.getTargetSource().getTargetClass() }, this ); } @Override public Object invoke (final Object proxy, final Method method, final Object[] args) throws Throwable { MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)); } }
4、测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml" ); HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService" ); helloWorldService.helloWorld(); AdvisedSupport advisedSupport = new AdvisedSupport(); TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldService.class ) ; advisedSupport.setTargetSource(targetSource); TimerInterceptor timerInterceptor = new TimerInterceptor(); advisedSupport.setMethodInterceptor(timerInterceptor); JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport); HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy(); helloWorldServiceProxy.helloWorld();
step8-使用AspectJ管理切面 在完成了织入之后,需要考虑的问题就是对什么类以及什么方法进行织入,也就是切点。Spring中ClassFilter和MethodMatcher分别表示类和方法做匹配,怎么匹配呢?
AspectJ是对java的aop增强的一门语言,有单独的编译器,Spring借鉴了AspectJ风格的切点表达式,新建AspectJExpressionPointcut实现上述两个接口,通过切点解析器将AspectJ风格的表达式解析成切点表达式
pointcutParser就是切点解析器,supportedPrimitives是一个set集合,表示AspectJ语言的关键字
pointcutParser将String类型的expression解析包装成PointcutExpression,然后可以就可以匹配类和方法了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public AspectJExpressionPointcut (Set<PointcutPrimitive> supportedPrimitives) { pointcutParser = PointcutParser .getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives); } protected void checkReadyToMatch () { if (pointcutExpression == null ) { pointcutExpression = buildPointcutExpression(); } } private PointcutExpression buildPointcutExpression () { return pointcutParser.parsePointcutExpression(expression); } @Override public boolean matches (Class<?> targetClass) { checkReadyToMatch(); return pointcutExpression.couldMatchJoinPointsInType(targetClass); } @Override public boolean matches (Method method, Class<?> targetClass) { checkReadyToMatch(); ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method); if (shadowMatch.alwaysMatches()) { return true ; } else if (shadowMatch.neverMatches()) { return false ; } return false ; }
AspectJ包帮我们做好了匹配的事情,只不过我们使用动态代理而不是它的编译器实现了织入
step9-将AOP融入Bean的创建过程中 现在我们已经解决了在哪做,做什么的问题,接下来就要把AOP融入IOC容器的Bean中,我们首先要解决在IOC容器的哪里织入AOP,然后是要为哪些对象织入
Spring提供了BeanPostProcessor,只要Bean实现了这个接口,那么在初始化的时候会优先找到它们,并且在初始化的过程中调用这个接口,从而实现对BeanFactory无侵入的扩展
在 postProcessorAfterInitialization 方法中,使用动态代理的方式,返回一个对象的代理对象。解决了 在 IOC 容器的何处植入 AOP 的问题。
1 2 3 4 5 6 public interface BeanPostProcessor { Object postProcessBeforeInitialization (Object bean, String beanName) throws Exception ; Object postProcessAfterInitialization (Object bean, String beanName) throws Exception ; }
AspectJAwareAdvisorAutoProxyCreator是实现AOP织入的关键类,它实现了BeanPostProcessor接口,它会扫描所有的切点(AspectJExpressionPointcutAdvisor包装了Pointcut和AspectJExpressionPointcut),并对bean做织入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public Object postProcessAfterInitialization (Object bean, String beanName) throws Exception { if (bean instanceof AspectJExpressionPointcutAdvisor) return bean; if (bean instanceof MethodInterceptor) return bean; List<AspectJExpressionPointcutAdvisor> advisors = beanFactory .getBeansForType(AspectJExpressionPointcutAdvisor.class ) ; for (AspectJExpressionPointcutAdvisor advisor : advisors) { if (advisor.getPointcut().getClassFilter().matches(bean.getClass())) { AdvisedSupport advisedSupport = new AdvisedSupport(); advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); TargetSource targetSource = new TargetSource(bean, bean.getClass().getInterfaces()); advisedSupport.setTargetSource(targetSource); return new JdkDynamicAopProxy(advisedSupport).getProxy(); } } return bean; }
动态代理类的invoke
1 2 3 4 5 6 7 8 9 10 11 public Object invoke (final Object proxy, final Method method, final Object[] args) throws Throwable { MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); if (advised.getMethodMatcher() != null && advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)); } else { return method.invoke(advised.getTargetSource().getTarget(), args); } }
xml
1 2 3 4 5 6 7 8 9 10 <bean id ="autoProxyCreator" class ="zcs.aop.AspectJAwareAdvisorAutoProxyCreator" > </bean > <bean id ="timeInterceptor" class ="zcs.aop.TimerInterceptor" > </bean > <bean id ="aspectjAspect" class ="zcs.aop.AspectJExpressionPointcutAdvisor" > <property name ="advice" ref ="timeInterceptor" > </property > <property name ="expression" value ="execution(* zcs.*.*(..))" > </property > </bean >
整个流程如下
AutoProxyCreator(实现了BeanPostProcessor接口)最先被实例化
其他Bean开始实例化,AutoProxyCreator加载BeanFactory中所有的PointcutAdvisor(这保证了PointcutAdvisor优先于普通Bean),然后依次使用PointcutAdvisor内置的ClassFilter判断当前对象需不需要拦截
如果是,生成AdvisedSupport交给AopProxy生成代理对象
AopProxy实现了InvocationHandler接口,在invoke函数中,首先使用advised.getMethodMatcher()判断是不是要拦截的方法,如果是则交给Advice(自定义的MethodInterceptor)执行,如果不是,则直接执行。
step10-使用CGLib进行类的织入 前面说到,JDK动态代理只能对接口实现代理,如果类没有实现接口,那么必须要用CGLib来实现。
我们需要新建一个ProxyFactory根据TargetSource类型来自动创建代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class ProxyFactory extends AdvisedSupport implements AopProxy { @Override public Object getProxy () { return createAopProxy().getProxy(); } protected final AopProxy createAopProxy () { return new Cglib2AopProxy(this ); } protected final AopProxy createAopProxy (TargetSource targetSource) { return new JdkDynamicAopProxy(this ); } }
TargetSource修改变量
1 2 3 4 5 6 7 8 public class TargetSource { private Class<?> targetClass; private Class<?>[] interfaces; private Object target; }
整体流程: