问题1:AOP是什么?java
Aspect Oriented Programming 面向切面编程,在不改变类的代码的状况下,对类方法进行功能加强。git
问题2:咱们须要作什么?github
在咱们的框架中要向使用用户提供AOP功能,让他们能够经过AOP技术实现对类方法进行功能加强。正则表达式
从"Aspect Oriented Programming 面向切面编程,在不改变类的代码的状况下,对类方法进行功能加强"这句话咱们能获得下面的这些信息:spring
咱们先来看一下下面的这张图express
说明:apache
程序运行时会调用不少方法,调用的不少方法就叫作Join points(链接点,能够被选择来进行加强的方法点),在方法的前或者后选择一个地方来切入,切入的的地方就叫作Pointcut(切入点,选择加强的方法),而后把要加强的功能(Advice)加入到切入点所在的位置。Advice和Pointcut组成一个切面(Aspect)编程
AOP的几个概念:缓存
Advice、Pointcut、Weaving的特色:app
Advice(功能加强):
1)用户性:由用户提供加强功能的逻辑代码
2)变化的:不一样的加强需求,会有不一样的逻辑
3)可选时机:可选择在方法前、后、异常时进行功能加强
4)多重的:同一个切入点上能够有多重加强
Pointcut(切入点):
1)用户性:由用户来指定
2)变化的:用户可灵活指定
3)多点性:用户能够选择在多个点上进行功能加强
Weaving(织入):
1)无侵入性,由于不改变原类的代码
2)咱们在框架中实现
咱们将要分析Advice、Pointcut、Aspect这三个东西
Advice是由用户来提供,咱们来使用,它是多变得。
问题1:咱们如何能识别用户提供的东西?用户在咱们写好框架之后使用咱们的框架。
问题2:如何让咱们的代码隔绝用户提供的多变?
解决方法:
咱们定义一套标准接口,用户经过实现接口来提供它们不一样的逻辑。
为了应对变化,这里使用到了设计原则:面向接口编程
Advice的特色:可选时机,可选择在方法前、后、异常时进行功能加强
1)有的Advice是在方法执行前进行加强——前置加强
2)有的Advice是在方法执行后进行加强——后置加强
3)有的Advice会在方执行先后都进行加强——环绕加强
4)有的Advice则只是在方法执行抛出异常时进行加强——异常处理加强
问题1:咱们须要作什么?
定义标准接口方法,让用户能够实现它,提供各类加强。
问题2:这四种加强所需的参数同样吗?
下面咱们来一个一个的分析
前置加强:在方法执行前进行加强
问题1:它可能须要什么参数?
目的是对方法进行加强,应该须要的是方法相关的信息。
问题2:运行时,方法有哪些信息?
方法自己 Method
方法属于哪一个类 Object
方法的参数 Object [ ]
方法的返回值
...........
问题3:前置加强可能须要什么参数?
方法自己 Method
方法属于哪一个类 Object
方法的参数 Object [ ]
问题3:前置加强的返回值是什么?
在方法执行前进行加强,不须要返回值
后置加强:在方法执行后进行加强
问题1:后置加强可能须要什么参数?
方法自己 Method
方法属于哪一个类 Object
方法的参数 Object [ ]
方法的返回值
问题2:后置加强的返回值是什么?
在方法执行后进行加强,不须要返回值
环绕加强:方法执行先后进行加强(包裹方法进行加强)
问题1:它可能须要什么参数?
方法自己 Method
方法属于哪一个类 Object
方法的参数 Object [ ]
问题2:环绕加强的返回值是什么?
方法被它包裹,也即方法将由它来执行,它须要返回方法的返回值。Object
异常处理加强:捕获方法执行时的异常,进行加强处理。
问题1:它可能须要什么参数?
异常信息
问题2:进行异常处理加强须要包裹方法吗?
须要,把执行代码用try包起来,捕获到哪一个异常就在哪里进行加强
问题3:那它能否在环绕中实现?
能够
问题1:是把这三个方法定义在一个接口中,仍是分别定义在三个接口中?
分三个接口,这样能够经过类型来区分不一样的加强(Advice)
类图以下:
对应的代码实现:
前置加强:
package com.study.spring.aop.advice; import java.lang.reflect.Method; /** * * @Description: 前置加强接口 * @author leeSmall * @date 2018年12月1日 * */ public interface MethodBeforeAdvice extends Advice { /** * 实现该方法进行前置加强 * * @param method 被加强的方法 * * @param args 被加强的方法的参数 * * @param target 被加强的目标对象(被加强的方法所在的类) * * @throws Throwable 异常 */ void before(Method method, Object[] args, Object target) throws Throwable; }
后置加强:
package com.study.spring.aop.advice; import java.lang.reflect.Method; /** * * @Description: 后置加强接口 * @author leeSmall * @date 2018年12月1日 * */ public interface AfterReturningAdvice extends Advice { /** * 实现该方法,提供后置加强 * * @param returnValue 被加强的方法的返回值 * * @param method 被加强的方法 * * @param args 被加强的方法的参数 * * @param target 被加强的目标对象(被加强的方法所在的类) * * @throws Throwable 异常 */ void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable; }
方法进行环绕(前置、后置)加强、异常处理加强:
package com.study.spring.aop.advice; import java.lang.reflect.Method; /** * * @Description: 对方法进行环绕(前置、后置)加强、异常处理加强接口 * @author leeSmall * @date 2018年12月1日 * */ public interface MethodInterceptor extends Advice { /** * 对方法进行环绕(前置、后置)加强、异常处理加强,方法实现中需调用目标方法。 * * @param method 被加强的方法 * * @param args 被加强的方法的参数 * * @param target 被加强的目标对象(被加强的方法所在的类) * * @return Object 被加强的方法的返回值 * @throws Throwable */ Object invoke(Method method, Object[] args, Object target) throws Throwable; }
Pointcut的特色:
1)用户性:由用户来指定
2)变化的:用户可灵活指定
3)多点性:用户能够选择在多个点上进行加强
咱们须要作什么?
为用户提供一个东西,让他们能够灵活的指定多个方法点,并且咱们又能懂
切入点是由用户来指定在哪些方法点上进行加强,那么这个哪些方法如何来表示,能知足上面的点?
分析:
1)指定哪些方法,是否是一个描述信息
2)如何来指定一个方法——某类的某个方法
3)方法重载怎么办——加上参数类型
4)有没有感受其实就是一个完整的方法签名
com.study.design.mode.samples.proxy.Girl.dating(float length)
com.study.design.mode.samples.proxy.Girl.dating(long time)
5)如何作到多点性,灵活性?在一个描述中指定一类类的某些方法?
a)某个包下的某个类的某个方法
b)某个包下的全部类中的全部方法
c)某个包下的全部类中的do开头的方法
d)某个包下的以service结尾的类中的do开头的方法
e)某个包下的及其子包下的以service结尾的类中的do开头的方法
总结:咱们须要一个能描述上面a-e这些信息的表达式
6)要表达哪些信息?
包名、类名、方法名(参数类型)
7)每部分的要求是怎样的?
包名:有父子特色,要能模糊匹配
类名:要能模糊匹配
方法:要能模糊匹配
参数类型:参数能够有多个
8)这个表达式将被咱们用来决定是否须要对某个类的某个方法进行功能加强,这个决定过程应该是怎样的?
匹配类、匹配方法
9)一个表达式很差实现,分红多个表达式进行组合是否更容易些?
能够这么考虑
10)咱们掌握的表达式有哪些?
正则表达式
Ant Path表达式
AspectJ的Pointcut表达式——execution(* com.study.design.mode.samples.proxy.Girl.*(..))
总结:正则表达式是能够的,AspectJ本就是切面编程的组件,也是能够的
AspectJ是什么?
AspectJ是java里面切面编程的库,可以帮助咱们完成预编译时的代码加强,和eclispe配合使用能够生成相关的字节码。咱们在AOP里面只使用了他的表达式解析匹配相关的API
AspectJ的Pointcut表达式是用来表示应该在哪一个类的哪一个方法进行切入进行方法加强的。
语法以下:
示例:
* com.study.design.mode.samples.proxy.Girl.*(..)
下面开始对Pointcut进行接口、类设计了
问题1:切点应有什么属性?
切点定义表达式
问题2:切点应对外提供什么行为(方法)?
问题3:切点将被咱们用来作什么?
对类、方法进行匹配
切点应该提供匹配类、匹配方法的行为
问题4:若是在咱们设计的框架中要能灵活扩展的切点的实现方式,咱们该如何设计?
这又是一个支持可多变的问题,像通知同样,咱们来定义一套标准接口,定义好基本行为,面向接口编程,屏蔽掉具体的实现
不管哪一种实现,都实现匹配类、匹配方法的接口
Pointcut标准接口的类图:
Pointcut代码
package com.study.spring.aop.pointcut; import java.lang.reflect.Method; /** * * @Description: Pointcut标准接口 * @author leeSmall * @date 2018年12月2日 * */ public interface Pointcut { //匹配类 boolean matchsClass(Class<?> targetClass); //匹配方法 boolean matchsMethod(Method method, Class<?> targetClass); }
下面咱们来实现AspectJ表达式的Pointcut
AspectJExpressionPointcut的实现
可使用AspectJ的API把这两个match方法实现
实现步骤:
1)引入AspectJ的jar
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.1</version> </dependency>
2)掌握AspectJ的API的使用,咱们只使用它的切点表达式解析匹配部分
a)入口:org.aspectj.weaver.tools.PointcutParser 得到切点解析器
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
b)解析表达式获得org.aspectj.weaver.tools.PointcutExpression
PointcutExpression pe = pp.parsePointcutExpression("execution(* com.study.spring.samples.*.set*(..))");
c)用PointcutExpression匹配类,匹配有时候不许,后面能够经过匹配方法来精确匹配
pe.couldMatchJoinPointsInType(ABean.class)
d)用PointcutExpression匹配方法,能够实现精确匹配
//拿到匹配类的Class Class<?> cl = ABean.class; //经过方法名拿到Method Method aMethod = cl.getMethod("doSomthing", null); //使用PointcutExpression匹配方法拿到匹配对象ShadowMatch ShadowMatch sm = pe.matchesMethodExecution(aMethod); //从匹配对象ShadowMatch里面查看是否匹配 System.out.println(sm.alwaysMatches());
AspectJExpressionPointcut的具体代码:
package com.study.spring.aop.pointcut; import java.lang.reflect.Method; import org.aspectj.weaver.tools.PointcutExpression; import org.aspectj.weaver.tools.PointcutParser; import org.aspectj.weaver.tools.ShadowMatch; /** * * @Description: AspectJ表达式的Pointcut * @author leeSmall * @date 2018年12月2日 * */ public class AspectJExpressionPointcut implements Pointcut { //得到切点解析器 private static PointcutParser pp = PointcutParser .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); //表达式 private String expression; //Pointcut表达式对象 private PointcutExpression pe; public AspectJExpressionPointcut(String expression) { super(); this.expression = expression; //解析表达式获得org.aspectj.weaver.tools.PointcutExpression pe = pp.parsePointcutExpression(expression); } //匹配类 用PointcutExpression匹配类,匹配有时候不许,后面能够经过匹配方法来精确匹配 @Override public boolean matchsClass(Class<?> targetClass) { return pe.couldMatchJoinPointsInType(targetClass); } //匹配方法 用PointcutExpression匹配方法,能够实现精确匹配 @Override public boolean matchsMethod(Method method, Class<?> targetClass) { ShadowMatch sm = pe.matchesMethodExecution(method); return sm.alwaysMatches(); } public String getExpression() { return expression; } }
到这里,功能加强(Advice)和Pointcut咱们都实现了,下面来看看用户如何使用咱们提供的东西了
说明:
从上面的Advice和Pointcut的类图咱们能够知道,用户要使用咱们提供的Advice和Pointcut,只须要实现本身的一个Advice,如MyBeforeAdvice,并把实现的Advice配置成bean,而后传入一个表达式到AspectJExpressionPointcut里面就能够了。
配置的实现的Advice的bean的名字(adviceBeanName)和表达式(expression)组成一个切面
上面的使用仍是不太好,这个时候咱们须要为上面的使用抽象出一个接口,使得用户的使用更加简单!!!请继续看下面的内容
为用户提供更简单的外观,Advisor(通知者)组合Advice和Pointcut
Advisor(通知者)代码实现:
package com.study.spring.aop.advisor; /** * * @Description: Advisor(通知者)接口 * @author leeSmall * @date 2018年12月2日 * */ public interface Advisor { String getAdviceBeanName(); String getExpression(); }
基于切入点的通知者实现代码:
package com.study.spring.aop.advisor; import com.dn.spring.aop.pointcut.Pointcut; /** * * @Description:基于切入点的通知者实现 * @author leeSmall * @date 2018年12月2日 * */ public interface PointcutAdvisor extends Advisor { Pointcut getPointcut(); }
AspectJPointcutAdvisor(基于AspectJ切入点的通知者实现)代码实现:
package com.study.spring.aop.advisor; import com.dn.spring.aop.pointcut.AspectJExpressionPointcut; import com.dn.spring.aop.pointcut.Pointcut; /** * * @Description: 基于AspectJ切入点的通知者实现 用户配的一个切面,包含Advice(功能加强)和Pointcut(切入点) * @author leeSmall * @date 2018年12月2日 * */ public class AspectJPointcutAdvisor implements PointcutAdvisor { //用户配置的advice的bean的名字 private String adviceBeanName; //切入点表达式 private String expression; //AspectJ表达式切入点对象 private AspectJExpressionPointcut pointcut; public AspectJPointcutAdvisor(String adviceBeanName, String expression) { super(); this.adviceBeanName = adviceBeanName; this.expression = expression; this.pointcut = new AspectJExpressionPointcut(this.expression); } @Override public Pointcut getPointcut() { return this.pointcut; } @Override public String getAdviceBeanName() { return this.adviceBeanName; } @Override public String getExpression() { return this.expression; } }
扩展不一样的Advisor实现:
还可把AspectJPointcutAdvisor和RegExpressionPointcutAdvisor的公共部分提取出来减小冗余代码:
织入要完成什么?
将用户提供的加强功能加到指定的方法上。这一部分是咱们要实现的
思考如下问题:
问题1:在何时作织入?
建立bean实例的时候,在bean初始化完成后,再对其进行加强
问题2:如何肯定bean要加强?
对bean类及其方法挨个匹配用户指定的切面,若是有切面匹配就是要加强的
问题3:如何织入
代理
整理一下AOP的使用流程,帮助咱们更好地去设计织入
问题1:用户到哪里去注册切面?
BeanFactory?
问题2:判断匹配、织入的逻辑写在哪里?
写在BeanFactory中?
咱们如今是否是要在Bean建立的过程当中加入一项处理?后续可能在Bean建立过程当中还会加入更多别的处理,若是直接在BeanFactory中实现会有什么很差?
BeanFactory的代码会愈来愈多
不易扩展
那么该怎么来设计呢?
回顾一下Bean产生的过程当中都经历了什么
在Bean产生的过程当中,会有不少的处理逻辑加入到过程的不一样阶段,好比bean初始化前、bean初始化后等等
咱们如何来设计能让咱们的BeanFactory一次写好后,后面就不改代码,就能够灵活扩展呢?
在各个节点加入扩展点、加入注册机制
什么是扩展点,什么是注册机制?
这里就须要用到前面学习的观察者模式(监听模式)了,BeanFactory就是主题(保证写好一次后就不在改变),6个扩展点就是观察者,主题面向观察者编程,BeanFactory(主题)里面能够添加、删除、通知6个扩展点(观察者)
观察者模式类图:
说明:
主题Subject面向观察者接口Observer编程,主题里面能够添加、删除和通知观察者Observer;
注意每一个观察者都有一个回调方法update,若是有变化就会在主题的notifyObservers()方法里面调用update方法,把最新的变化给到观察者
变化之处:观察者会变,观察者的数量会变。
不变:主题的代码要不受观察者变化的影响。
观察者模式定义:
定义了对象之间一对多的依赖关系,当一端对象改变状态时,它的全部依赖者都会收到通知并自动更新(被调用更新方法)。也称为:监听模式、发布订阅模式。提供一种对象之间松耦合的设计方式。
代码实现:
AOP加强处理接口:
package com.study.spring.beans; /** * * @Description: AOP加强处理接口 * @author leeSamll * @date 2018年12月2日 * */ public interface BeanPostProcessor { //bean初始化前加强 default Object postProcessBeforeInitialization(Object bean, String beanName) throws Throwable { return bean; } //bean初始化后加强 default Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable { return bean; } }
Advisor注册接口:
package com.study.spring.aop.advisor; import java.util.List; /** * * @Description: Advisor注册接口 * @author leeSamll * @date 2018年12月2日 * */ public interface AdvisorRegistry { //注册Advisor public void registAdvisor(Advisor ad); //获取Advisor public List<Advisor> getAdvisors(); }
AOP加强处理的观察者实现:
package com.study.spring.aop; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.aop.advisor.AdvisorRegistry; import com.dn.spring.aop.advisor.PointcutAdvisor; import com.dn.spring.aop.pointcut.Pointcut; import com.dn.spring.beans.BeanFactory; import com.dn.spring.beans.BeanFactoryAware; import com.dn.spring.beans.BeanPostProcessor; /** * * @Description: AOP加强处理的观察者实现 * @author leeSamll * @date 2018年12月2日 * */ public class AdvisorAutoProxyCreator implements AdvisorRegistry, BeanPostProcessor, BeanFactoryAware { private List<Advisor> advisors; private BeanFactory beanFactory; public AdvisorAutoProxyCreator() { this.advisors = new ArrayList<>(); } public void registAdvisor(Advisor ad) { this.advisors.add(ad); } public List<Advisor> getAdvisors() { return advisors; } @Override public void setBeanFactory(BeanFactory bf) { this.beanFactory = bf; } //后置加强 public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable { // 在此判断bean是否须要进行切面加强 List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName); // 如须要就进行加强,再返回加强的对象。 if (CollectionUtils.isNotEmpty(matchAdvisors)) { bean = this.createProxy(bean, beanName, matchAdvisors); } return bean; } //在此判断bean是否须要进行切面加强 private List<Advisor> getMatchedAdvisors(Object bean, String beanName) { if (CollectionUtils.isEmpty(advisors)) { return null; } // 获得类、类的全部方法 Class<?> beanClass = bean.getClass(); List<Method> allMethods = this.getAllMethodForClass(beanClass); // 存放匹配的Advisor的list List<Advisor> matchAdvisors = new ArrayList<>(); // 遍历Advisor来找匹配的 for (Advisor ad : this.advisors) { if (ad instanceof PointcutAdvisor) { if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) { matchAdvisors.add(ad); } } } return matchAdvisors; } //获取类的全部方法,包括继承的父类和实现的接口里面的方法 private List<Method> getAllMethodForClass(Class<?> beanClass) { List<Method> allMethods = new LinkedList<>(); //获取beanClass的全部接口 Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass)); classes.add(beanClass); //遍历全部的类和接口反射获取到全部的方法 for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method m : methods) { allMethods.add(m); } } return allMethods; } //判断类及类的方法是否和切面匹配 private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) { Pointcut p = pa.getPointcut(); // 首先判断类是否匹配 if (!p.matchsClass(beanClass)) { return false; } // 再判断是否有方法匹配 for (Method method : methods) { if (p.matchsMethod(method, beanClass)) { return true; } } return false; } //建立代理对象加强 private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Throwable { // 经过AopProxyFactory工厂去完成选择、和建立代理对象的工做。 return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean, beanName, matchAdvisors, beanFactory) .getProxy(); } }
Advisor(通知者)接口:
package com.study.spring.aop.advisor; /** * * @Description: Advisor(通知者)接口 * @author leeSmall * @date 2018年12月2日 * */ public interface Advisor { //获取配置的Advice的bean的名字 String getAdviceBeanName(); //获取切入点表达式 String getExpression(); }
在IOC容器(bean工厂)接口里面增长注册AOP织入(注册AOP加强处理的观察者实现)的方法:
package com.study.spring.beans; /** * * @Description: IOC容器(bean工厂)接口:负责建立bean实例 * @author leeSmall * @date 2018年11月29日 * */ public interface BeanFactory { /** * 获取bean * * @param name bean的名字 * * @return bean 实例 * @throws Exception */ Object getBean(String name) throws Throwable; //注册AOP织入(注册AOP加强处理的观察者实现) void registerBeanPostProcessor(BeanPostProcessor bpp); }
问题1:如何判断bean实例是否要加强?
1)经过反射获取bean类及全部方法
java.lang.Class.getMethods() : Method[ ] 获取全部修饰符为public的方法,包括实现的接口和继承的父类里面的全部public修饰的方法
java.lang.Class.getMethod(String, Class<?>...) : Method 获取一个指定的public修饰符修饰的方法,包括实现的接口里面的public修饰的方法
java.lang.Class.getDeclaredMethods() : Method[ ] 获取类里面的全部方法,包括public, protected, default (package) access, and private 这些修饰符修饰的方法,可是不能获取从父类继承来的方法
总结:上面的三种方式都不能保证获取到全部的方法,若是要获取全部的方法就得递归调用,找到全部的类再去获取对应的方法,这一点Spring已经有实现好了的了,咱们直接拿来用就好了。
Spring获取类的全部方法的API以下:
//获取类的全部方法,包括继承的父类和实现的接口里面的方法 private List<Method> getAllMethodForClass(Class<?> beanClass) { List<Method> allMethods = new LinkedList<>(); //获取beanClass的全部接口 Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass)); classes.add(beanClass); //遍历全部的类和接口反射获取到全部的方法 for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method m : methods) { allMethods.add(m); } } return allMethods; }
说明:
获取类的全部接口:org.springframework.util.ClassUtils.getAllInterfacesForClassAsSet(Class<?>)
获取类的全部方法:org.springframework.util.ReflectionUtils.getAllDeclaredMethods(Class<?>)
2)遍历Advisor(通知者),取Advisor中的Pointcut(切入点)来匹配类、匹配方法
判断bean实例是否要加强的代码实现:
//在此判断bean是否须要进行切面加强 private List<Advisor> getMatchedAdvisors(Object bean, String beanName) { if (CollectionUtils.isEmpty(advisors)) { return null; } // 获得类、类的全部方法 Class<?> beanClass = bean.getClass(); List<Method> allMethods = this.getAllMethodForClass(beanClass); // 存放匹配的Advisor的list List<Advisor> matchAdvisors = new ArrayList<>(); // 遍历Advisor来找匹配的 for (Advisor ad : this.advisors) { if (ad instanceof PointcutAdvisor) { if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) { matchAdvisors.add(ad); } } } return matchAdvisors; } //获取类的全部方法,包括继承的父类和实现的接口里面的方法 private List<Method> getAllMethodForClass(Class<?> beanClass) { List<Method> allMethods = new LinkedList<>(); //获取beanClass的全部接口 Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass)); classes.add(beanClass); //遍历全部的类和接口反射获取到全部的方法 for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method m : methods) { allMethods.add(m); } } return allMethods; } //判断类及类的方法是否和切面匹配 private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) { Pointcut p = pa.getPointcut(); // 首先判断类是否匹配 if (!p.matchsClass(beanClass)) { return false; } // 再判断是否有方法匹配 for (Method method : methods) { if (p.matchsMethod(method, beanClass)) { return true; } } return false; }
问题2:代理加强的逻辑是怎么样的?
回忆一下JDK动态代理:
在运行时,对接口建立代理对象
生成代理类$Proxy0的方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:须要被代理的目标对象实现的接口,能够传入多个
InvocationHandler h:功能加强的接口
功能加强的接口:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
参数说明:
Object proxy:被代理的目标对象(接口)
Method method:要调用的目标对象的方法
Object[] args:要调用的目标对象的方法的参数
回忆一下cglib动态代理:
在运行期为类、接口生成动态代理对象。 以达到不改动原类代码而实现功能加强的目的
说明:
实现思想和前面的JDK动态代理同样,只是使用了不一样的API。
代理类由Enhancer生成,代理类实现被代理的类或者接口,特定的功能加强的实现MyMethodInterceptor实现MethodInterceptor接口,特定的功能加强实现MyMethodInterceptor里面持有被代理的类或者接口target
下面就把生成代理对象的部分和功能加强实现的部分分别实现
不管是JDK动态代理仍是cglib动态代理都是生成代理对象,所以能够对这两种代理进行抽象,先看下面的类图
要生成代理对象,完成织入加强,JDK动态代理这里须要一些什么数据?
要实现的接口——要实现抽象生成代理对象的接口AopProxy和JDK动态代理生成代理对象的接口InvocationHandler
目标对象——须要加强的Bean
匹配的Advisors——Advice和Pointcut组成的切面去匹配被加强的Bean及Bean里面的方法
BeanFactory ?
要生成代理对象,完成织入加强,cglib动态代理这里须要一些什么数据?
要继承的类 ?
要实现的接口——要实现抽象生成代理对象的接口AopProxy和cglib动态代理生成代理对象的接口MethodInterceptor
目标对象——须要加强的Bean
匹配的Advisors——Advice和Pointcut组成的切面去匹配被加强的Bean及Bean里面的方法
BeanFactory ?
构造参数类型 ?
构造参数 ?
下面看具体的代码实现:
JDK动态代理和cglib动态代理抽象出公共部分的接口去获取代理对象:
package com.study.spring.aop; /** * * @Description: JDK动态代理和cglib动态代理抽象出公共部分的接口去获取代理对象 * @author leeSmall * @date 2018年12月2日 * */ public interface AopProxy { //获取代理对象 Object getProxy(); //经过类加载器获取代理对象 Object getProxy(ClassLoader classLoader); }
JDK动态代理实现:
package com.study.spring.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.beans.BeanFactory; /** * * @Description: JDK动态AOP代理实现 * @author leeSmall * @date 2018年12月2日 * */ public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class); private String beanName; private Object target; private List<Advisor> matchAdvisors; private BeanFactory beanFactory; public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) { super(); this.beanName = beanName; this.target = target; this.matchAdvisors = matchAdvisors; this.beanFactory = beanFactory; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); } @Override public Object getProxy() { return this.getProxy(target.getClass().getClassLoader()); } @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("为" + target + "建立代理。"); } return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this); } }
cglib动态代理实现:
package com.study.spring.aop; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.beans.BeanDefinition; import com.dn.spring.beans.BeanFactory; import com.dn.spring.beans.DefaultBeanFactory; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * * @Description: cglib动态AOP代理实现 * @author leeSmall * @date 2018年12月2日 * */ public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor { private static final Log logger = LogFactory.getLog(CglibDynamicAopProxy.class); private static Enhancer enhancer = new Enhancer(); private String beanName; private Object target; private List<Advisor> matchAdvisors; private BeanFactory beanFactory; public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) { super(); this.beanName = beanName; this.target = target; this.matchAdvisors = matchAdvisors; this.beanFactory = beanFactory; } @Override public Object getProxy() { return this.getProxy(target.getClass().getClassLoader()); } @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("为" + target + "建立cglib代理。"); } Class<?> superClass = this.target.getClass(); enhancer.setSuperclass(superClass); enhancer.setInterfaces(this.getClass().getInterfaces()); enhancer.setCallback(this); Constructor<?> constructor = null; try { constructor = superClass.getConstructor(new Class<?>[] {}); } catch (NoSuchMethodException | SecurityException e) { } if (constructor != null) { return enhancer.create(); } //没有无参构造函数时,从BeanDefinition里面获取构造参数的类型和值进行加强 else { BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName); return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues()); } } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); } }
逻辑以下:
加强逻辑代码应该写在JDK动态代理的invoke方法和cglib动态代理的intercept方法里面
因为都是加强的代理,逻辑是同样的就提取成一个公共的方法com.study.spring.aop.AopProxyUtils.applyAdvices(Object, Method, Object[], List<Advisor>, Object, BeanFactory)
JdkDynamicAopProxy代码:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); }
CglibDynamicAopProxy代码:
@Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); }
加强逻辑实现工具类AopProxyUtils代码:
package com.dn.spring.aop; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.springframework.util.CollectionUtils; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.aop.advisor.PointcutAdvisor; import com.dn.spring.beans.BeanFactory; /** * * @Description: 加强逻辑实现工具类 * @author leeSmall * @date 2018年12月3日 * */ public class AopProxyUtils { /** * 对方法应用advices加强 * * @param target * @param method * @param args * @param matchAdvisors * @param proxy * @param beanFactory * @return * @throws Throwable */ public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> matchAdvisors, Object proxy, BeanFactory beanFactory) throws Throwable { // 这里要作什么? // 一、获取要对当前方法进行加强的advice List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors, beanFactory); // 二、若有加强的advice,就责任链式加强执行 if (CollectionUtils.isEmpty(advices)) { //没有Advice就直接调用invoke方法 return method.invoke(target, args); } else { // 有Advice就责任链式执行加强 AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, advices); return chain.invoke(); } } /** * 获取与方法匹配的切面的advices * * @param beanClass * @param method * @param matchAdvisors * @param beanFactory * @return * @throws Exception */ public static List<Object> getShouldApplyAdvices(Class<?> beanClass, Method method, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable { if (CollectionUtils.isEmpty(matchAdvisors)) { return null; } List<Object> advices = new ArrayList<>(); for (Advisor ad : matchAdvisors) { if (ad instanceof PointcutAdvisor) { //若是当前方法和切入点匹配就是要加入加强功能的方法 if (((PointcutAdvisor) ad).getPointcut().matchsMethod(method, beanClass)) { advices.add(beanFactory.getBean(ad.getAdviceBeanName())); } } } return advices; } }
先实现JDK动态代理对象的方式的:
须要的参数:
要实现的接口 ——AopProxy和InvocationHandler
//建立代理对象 @Override public Object getProxy() { return this.getProxy(target.getClass().getClassLoader()); } //建立代理对象 @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("为" + target + "建立代理。"); } return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this); }
实现cglib动态代理方式的:
须要的参数:
继承的参数
实现的接口
Callback
构造参数类型
构造参数
问题:构造参数类型、构造参数从哪来?
建立对象实例时会有
传递构造参数类型和构造参数:
如何来传递建立bean实例时得到的数据到初始化后的AOP中呢?
在BeanDefinition中用ThreadLocal保存建立bean实例时得到的数据(构造参数类型、构造参数)
GenericBeanDefinition代码:
//没有无参构造函数时,传递构造参数的类型和值到cglib动态代理里面去获取有参构造函数进行加强 private ThreadLocal<Object[]> realConstructorArgumentValues = new ThreadLocal<>(); @Override public Object[] getConstructorArgumentRealValues() { return realConstructorArgumentValues.get(); } @Override public void setConstructorArgumentRealValues(Object[] values) { realConstructorArgumentValues.set(values); }
DefaultBeanFactory代码:
// 构造方法来构造对象 private Object createInstanceByConstructor(BeanDefinition bd) throws Throwable { try { Object[] args = this.getConstructorArgumentValues(bd); if (args == null) { return bd.getBeanClass().newInstance(); } else { bd.setConstructorArgumentRealValues(args); // 决定构造方法 Constructor<?> constructor = this.determineConstructor(bd, args); // 缓存构造函数由determineConstructor 中移到了这里,不管原型否都缓存,由于后面AOP须要用 bd.setConstructor(constructor); return constructor.newInstance(args); } } catch (SecurityException e1) { logger.error("建立bean的实例异常,beanDefinition:" + bd, e1); throw e1; } }
CglibDynamicAopProxy代码:
//建立代理对象 @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("为" + target + "建立cglib代理。"); } Class<?> superClass = this.target.getClass(); enhancer.setSuperclass(superClass); enhancer.setInterfaces(this.getClass().getInterfaces()); enhancer.setCallback(this); Constructor<?> constructor = null; try { constructor = superClass.getConstructor(new Class<?>[] {}); } catch (NoSuchMethodException | SecurityException e) { } if (constructor != null) { return enhancer.create(); } //没有无参构造函数时,从BeanDefinition里面获取构造参数的类型和值进行加强 else { BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName); return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues()); } }
把选择使用哪一个动态代理的逻辑交给工厂去判断
AopProxyFactory代码:
package com.study.spring.aop; import java.util.List; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.beans.BeanFactory; /** * * @Description: 工厂AopProxyFactory负责选择使用哪一个动态代理 * @author leeSamll * @date 2018年12月3日 * */ public interface AopProxyFactory { AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable; /** * 得到默认的AopProxyFactory实例 * * @return AopProxyFactory */ static AopProxyFactory getDefaultAopProxyFactory() { return new DefaultAopProxyFactory(); } }
DefaultAopProxyFactory代码:
package com.study.spring.aop; import java.util.List; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.beans.BeanFactory; /** * * @Description: 工厂AopProxyFactory的默认实现 负责选择使用哪一个动态代理 * @author leeSamll * @date 2018年12月3日 * */ public class DefaultAopProxyFactory implements AopProxyFactory { @Override public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable { // 是该用jdk动态代理仍是cglib? if (shouldUseJDKDynamicProxy(bean, beanName)) { return new JdkDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory); } else { return new CglibDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory); } } //默认使用cglib private boolean shouldUseJDKDynamicProxy(Object bean, String beanName) { // 如何判断? // 这样能够吗:有实现接口就用JDK,没有就用cglib? return false; } }
AdvisorAutoProxyCreator代码:
//建立代理对象加强 private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Throwable { // 经过AopProxyFactory工厂去完成选择、和建立代理对象的工做。 return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean, beanName, matchAdvisors, beanFactory) .getProxy(); }
完整源码获取地址:
https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v3