1、概述
Spring AOP的核心是基于代理实现的,代理有jdk基于接口的动态代理和基于asm实现的允许类没有接口的cglib代理,上一小结,已经分析过Spring封装了使用了@Aspect注解的类,并将切面方法封装成实现了AbstractAspectJAdvice的类放入缓存中,等待创建代理对象时使用,AbstractAspectJAdvice有三个参数:
//切面方法 诸如使用了@Before这类的方法
Method aspectJAdviceMethod
//封装了expression表达式,给定一个类和方法名,能返回是否匹配
AspectJExpressionPointcut pointcut
//可以创建一个切面类,然后通过反射执行切面方法
AspectInstanceFactory aspectInstanceFactory
通过这个对象就可以拦截所有的类的创建找出符合条件的bean创建代理执行增强操作,这也是spring的实现原理。
2、jdk代理
jdk代理是基于接口的代理,所以被代理的对象必须是有接口实现的类,代理创建时通过Proxy.newProxyInstance实现的,这个方法有三个参数:
//指定要使用的类加载器
ClassLoader loader,
//被代理的类所实现的接口,增强接口的方法
Class<?>[] interfaces,
//方法处理器,会拦截所有方法,然后执行增强参数。
InvocationHandler inoker
简单实例
订单操作接口
public interface OrderUpdateService {
/**
* 订单付款
* @param orderAmt
*/
void payOrder(String orderAmt);
}
订单操作实现类
@Slf4j
public class OrderUpdateServiceImpl implements OrderUpdateService {
public void payOrder(String orderAmt) {
log.info("订单付款中.....");
}
}
订单代理类
@Slf4j
public class OrderUpdateServiceImplProxy {
//目标类 要增强的类
private OrderUpdateService updateService;
public OrderUpdateServiceImplProxy(OrderUpdateService updateService) {
this.updateService = updateService;
}
public OrderUpdateService getProxy() {
return (OrderUpdateService) Proxy.newProxyInstance(OrderUpdateServiceImplProxy.class.getClassLoader(), new Class[]{OrderUpdateService.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("payOrder".equals(method.getName())) {
log.info("付款前处理");
method.invoke(updateService, args);
log.info("付款后处理");
}
return proxy;
}
});
}
}
demo验证
public static void main(String[] args) {
OrderUpdateService orderUpdateService = new OrderUpdateServiceImpl();
OrderUpdateServiceImplProxy proxy = new OrderUpdateServiceImplProxy(orderUpdateService);
orderUpdateService = proxy.getProxy();
orderUpdateService.payOrder("100");
}
验证结果
11:18:07.859 [main] INFO s.a.e.p.OrderUpdateServiceImplProxy - 付款前处理
11:18:07.890 [main] INFO s.a.e.p.s.OrderUpdateServiceImpl - 订单付款中.....
11:18:07.890 [main] INFO s.a.e.p.OrderUpdateServiceImplProxy - 付款后处理
payOrder方法的确时被增强了。
3、cglib代理
Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。如下图所示Cglib与Spring等应用的关系。
- 最底层的是字节码
Bytecode,字节码是Java为了保证“一次编译、到处运行”而产生的一种虚拟指令格式,例如iload_0、iconst_1、if_icmpne、dup等 - 位于字节码之上的是
ASM,这是一种直接操作字节码的框架,应用ASM需要对Java字节码、Class结构比较熟悉 - 位于
ASM之上的是CGLIB、Groovy、BeanShell,后两种并不是Java体系中的内容而是脚本语言,它们通过ASM框架生成字节码变相执行Java代码,这说明在JVM中执行程序并不一定非要写Java代码----只要你能生成Java字节码,JVM并不关心字节码的来源,当然通过Java代码生成的JVM字节码是通过编译器直接生成的,算是最“正统”的JVM字节码 - 位于
CGLIB、Groovy、BeanShell之上的就是Hibernate、Spring AOP这些框架了,这一层大家都比较熟悉 - 最上层的是Applications,即具体应用,一般都是一个Web项目或者本地跑一个程序
所以,Cglib的实现是在字节码的基础上的,并且使用了开源的ASM读取字节码,对类实现增强功能的。
Cglib可以通过Callback回调函数完成对方法的增强,通过CallbackFilter函数过滤符合条件的函数,Spring也是基于这两个接口完成bean的增强功能的,所以可以猜测前面Advice参数的pointCut方法匹配器就是在CallbackFilter中起作用的。下来看看cglib的简单使用:
下面的验证是拦截GameService的playGames方法,在playGames方法之前,执行TransactionManager类的start和commit方法。
@Test
public void testProxyFilter() throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(GameService.class);
// NoOp.INSTANCE 表示没有被匹配的方法不执行任何操作 如果CallbackFilter返回0
//代表使用callbacks集合中的第一个元素执行方法拦截策略 1 则使用第二个。
Callback[] callbacks = {new TransactionInterceptor(), NoOp.INSTANCE};
//设置回调函数集合
enhancer.setCallbacks(callbacks);
//根据回调过滤器返回指定回调函数索引
enhancer.setCallbackFilter(new TransactionFilter());
GameService gameService = (GameService) enhancer.create();
Person person = new Person("lijinpeng", 26, false, new UserDao());
gameService.setPerson(person);
gameService.playGames();
}
执行结果:playGames得到了增强。
[2019-06-03 15:13:59] [INFO ][s.r.beans.models.aop.TransactionManager][15][start][]start tx
[2019-06-03 15:13:59] [INFO ][s.road.beans.models.scan.GameService][29][playGames][]person-name:lijinpeng play games
[2019-06-03 15:13:59] [INFO ][s.r.beans.models.aop.TransactionManager][20][commit][]commit tx
方法增强器
@Slf4j
public class TransactionManager {
public void start() {
log.info("start tx");
MessageTracerUtils.addMessage("start tx");
}
public void commit() {
log.info("commit tx");
MessageTracerUtils.addMessage("commit tx");
}
public void rollback() {
log.info("rollback tx");
MessageTracerUtils.addMessage("rollback tx");
}
public Object getAspectInstance() {
return new TransactionManager();
}
}
方法拦截器
//方法拦截器 会拦截 所有方法 ,所以需要加判断 cglib 还提供了filter过滤器 可以用于过滤指定方法
public class TransactionInterceptor implements MethodInterceptor {
TransactionManager tx = new TransactionManager();
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
tx.start();
Object value = methodProxy.invokeSuper(o, objects);
tx.commit();
return value;
}
}
通过CallbackFilter接口,可以自定义拦截增强策略,返回的是前面设置的Callbacks集合中的回调函数索引,这里的含义是,如果方法名是playGames则使用Callbacks集合中索引为0的回调函数即TransactionInterceptor,该回调函数就是要在playGames前执行TransactionManager的start()方法,playGames方法后执行commit(),否则使用NoOp.INSTANCE表示什么也不做。
public class TransactionFilter implements CallbackFilter {
//返回回调函数在集合中的索引
public int accept(Method method) {
if ("playGames".equals(method.getName())) {
return 0;
} else {
return 1;
}
}
}
cglib代理通过Callback方法控制目标方法的增强逻辑,通过CallbackFilter用来指定适配的方法使用不通的回调函数完成不通的功能增强,cglib还可以为类动态得创建新得方法,不过知道cglib如何实现代理对Spring AOP的源码学习来说已经足够了。
4、代理对象的校验
在Spring中,并不是所有的bean都需要创建代理,在Spring Aop中,只有被我们配置的Aspect切面类的PointCut表达式匹配的类才会创建代理,所以在创建代理之前需要对要创建bean进行校验以判断该类是否需要被创建为代理对象。上一篇的结尾处分析过,当解析完@Aspect注解的切面类后,就会调用BeanPostProcessor的postProcessAfterInitialization的方法去创建一个代理对象,这个方法会调用wrapIfNecessary方法,这个方法会完成bean是否需要被创建为代理的校验,在这个方法里面,有一个核心的方法getAdvicesAndAdvisorsForBean,这个方法会返回一个Advisor数组集合,这个数组集合的Advisor即表示要创建的bean是否能被Advisor的Advice中的PointCut匹配到,如果可以匹配到,则为bean创建代理。AbstractAdvisorAutoProxyCreator完成这个方法的功能实现:
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
//查找所有需要作用于beanClass的Advisor
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
//直接返回null
return DO_NOT_PROXY;
}
return advisors.toArray();
}
核心方法是 findEligibleAdvisors(beanClass, beanName):
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//核心方法1:查找容器中所有已经生成了的Advisor,这是第二次执行
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//核心方法2:从容器中所有的Advisro找出能够作用于beanClass的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
//为Advisor提供钩子方法 方便扩展Advisor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
//根据Order排序Advisor
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
这个方法首先又再次调用了findCandidateAdvisors方法,这个方法在解析切面类的时候也调用了一次,目的是为切面类的切面方法生成Advisor,并根据注解为每个Advisor再生成一个Advice对象。这里第二次调用的目的是获取容器中所有生成过的Advisor,然后调用findAdvisorsThatCanApply方法找出所有能够作用于目标bean的Advisor,extendAdvisors方法是为了方便扩展Advisor,sortAdvisors是基于Order接口排序Advisor的。接下来主要来看下前两个方法:
AnnotationAwareAspectJAutoProxyCreator重写了findCandidateAdvisors方法,在重写了的方法里面,先调用了父类的,然后调用了基于注解的获取Advisor的方法。
@Override
protected List<Advisor> findCandidateAdvisors() {
//查找构建所有基于XML配置的切面类的切面方法
List<Advisor> advisors = super.findCandidateAdvisors();
//查找构建所有基于注解的切面类的切面方法
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
这次我们来看看基于xml的切面类是如何获取容器中的Advisor的。
public List<Advisor> findAdvisorBeans() {
String[] advisorNames = null;
synchronized (this) {
//这个阶段如果xml配置了切面类 cachedAdvisorBeanNames 应该已经包含了切面方法生成的切面类的 //beanName了
advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
//Aspect 可能是基于FactroyBean创建的,这里不实例化Aspect,还是应该交给代理实现
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
}
if (advisorNames.length == 0) {
return new LinkedList<Advisor>();
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
//直接通过切面方法生成的BeanDefinition 获取bean实例
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
//异常处理
......
}
return advisors;
}
从上面的代码看获取xml配置的切面方法生成的Advisor很简单,其实不然,真正复杂的逻辑是读取xml生成BeanDefinition和getBean()的过程,这里就不展开分析了。 这个方法过程就是找出由XMl配置的切面类生成的所有切面方法的Advisor,然后遍历beanName,生成实例对象,然会返回生成后的Advisor实例对象。
4.1 获取所有Advisor
接下来再来看看基于注解的Advisor是如何获取的。
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
//此时的aspectNames已经不为空了,省略之前生成Advisor的部分
.......
return advisors;
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String aspectName : aspectNames) {
//获取所有基于注解生成Advisor
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
//如果当时生成的Advisor是由工厂生成的,这个时候从工厂获取
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
这个方法有两部分,第一部分省略了,内容是上一篇分析过的生成基于注解的Advisor的过程,第二部分是获取已经生成过的Advisor的过程,由于基于注解生成的Advisor已经在创建的同时完成了bean的初始化,所以这里直接从缓存中获取即可,如果Advisor是由工厂类生成,则此时需要通过工厂类获取Advisor。
4.2 获取beanClass的Advisor
上面分析过程已经从xml和注解的方式获取到了容器中的所有Advisor,接下来会执行第二步操作,校验容器中的Advisor是否能够作用与要创建的bean上,回头看核心方法2:findAdvisorsThatCanApply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
for (Advisor candidate : candidateAdvisors) {
//核心点1:通过Introduction实现的advice
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
//核心点2:匹配能够作用于clazz的Advice
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
这个方法的核心点1做了一次匹配,这个匹配是通过IntroductionAdvisor实现的,首先简单了解下这个问题的背景:
如果需要对一些组件新增一些功能,比如在付款接口完成后,需要记录这笔交易耗时时间,但是此时根据业务不同,可能会有很多个付款实现类,这个时候的做法就是新增一个统计耗时的接口,这样做虽然可以解决问题,但是污染了业务代码,而且每个类中都要实现一遍,这就违反了面向对象的开放封闭原则,这个问题通过装饰者模式解决,Spring为了能够完成这个功能,使用Introduction+advice实现的,Spring通过一个特殊的拦截器IntroductionInterceptor实现。与PointCut作用于接口层次上不同,这种方式作用于类的层次,但是很不建议在生产环境使用这种粗粒度的实现,只需要知道Spring中有这种实现的方式就可以了,这里也不作为重点分析。
我们来看核心点2,canApply(candidate, clazz, hasIntroductions)从方法参数可以看出参数candidate是一个Advisor,claszz是目标类对象,由于Advisor实现类里面有PointCut,所以就可以匹配clazz类的方法是否属于切入方法,需要被增强。
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
//因为作用于类 直接匹配类就行了。
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
//转换成成PointCut接口
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
//如果advisor没有一个pointcut,默认它对所有的bean都是生效的
return true;
}
}
上面的方法首先校验了advisor是否是IntroductionAdvisor,上面分析过,IntroductionAdvisor是基于类型的,所以这里直接针对这种bean直接调用了ClassFilter接口去匹配,然后如果advisor是一个PointcutAdvisor,则转换成PointcutAdvisor再调用canApply方法,最后如果Advisor并没有配置成一个PointcutAdvisor,就默认对所有的bean都是生效的,进入canApply方法继续看
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
//前面说过 PointCut接口getMethodMatcher的返回一个方法匹配器
MethodMatcher methodMatcher = pc.getMethodMatcher();
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
//获取目标类得所有接口接口
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
//获取目标类及其接口的所有方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
这个方法其实很明确了,就是通过PointCut获取到一个方法匹配器MethodMatcher,这个匹配器通过match方法可以判断当前的方法是否能够被匹配到,PointCut的封装其实就是aspectj的,但我们关注点并不在这,就不进入里面分析了, 然后获取目标类的所有接口,循环所有的接口然后获取各个接口的所有方法,调用MethodMatcher的match方法用来判断当前方法是否能够被匹配,只要有一个方法被匹配到,则但会true,表明当前的advisor能够作用于当前bean。
Spring在bean的创建过程中,就是通过上面的过程查找能够作用于bean的Advisor的,如果能够找到,就为bean创建代理,否则就当普通的bean创建,经过上面的判断,我们案例中的bean肯定是有符合条件的Advisor,所以接下来看看Spring是如何创建代理的。
5、代理模式的查找
再来回忆下这个方法:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//如果bean是通过TargetSource接口获取 则直接返回
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//如果bean是切面类 直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//如果bean是Aspect 而且允许跳过创建代理, 加入advise缓存 返回
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//如果前面生成的advisor缓存中存在能够匹配到目标类方法的Advisor 则创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
经过前面的分析,通过getAdvicesAndAdvisorsForBean方法可以获取到容器中所有能被作用于当前bean的Advisor,此时的specificInterceptors肯定是不为null,所以就会执行createProxy创建代理的方法了。
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
//如果是基于IntroductionAdvisor,直接指定代理的目标类即可
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
//创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
//校验当前代理工厂是否可以直接代理目标类和目标接口
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
//将beanClass的接口设置到代理工厂上
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//构建能够作用于beanClass类的Advisor集合
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
由于IntroductionAdvisor这样特殊的拦截器已经指定了增强的类对象,也就是说该类实现了IntroductionInterceptor拦截器,定义过了类的方法需要如何增强,所以上面的方法第一步就是给他指定了增强类,然后创建ProxyFactory代理工厂,先校验代理工厂是否能够直接代理目标类及其接口,如果不能则evaluateProxyInterfaces方法会将beanClass的接口设置到代理工厂上,接下来通过buildAdvisors方法构建一个能够作用于beanClass类的Advisor集合放入到代理工厂中,以便后续创建代理时使用,最后调用proxyFactory.getProxy方法一个代理对象,在这里buildAdvisors方法没可看的,就是将Advisor封装到一个集合里等待备用:
protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
Advisor[] commonInterceptors = resolveInterceptorNames();
List<Object> allInterceptors = new ArrayList<Object>();
if (specificInterceptors != null) {
//将所有Advisor放入到allInterceptors集合中
allInterceptors.addAll(Arrays.asList(specificInterceptors));
if (commonInterceptors.length > 0) {
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
}
else {
allInterceptors.addAll(Arrays.asList(commonInterceptors));
}
}
}
if (logger.isDebugEnabled()) {
int nrOfCommonInterceptors = commonInterceptors.length;
int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
Advisor[] advisors = new Advisor[allInterceptors.size()];
for (int i = 0; i < allInterceptors.size(); i++) {
//根据Advice类型转换成不用的Advisor实现
advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
}
return advisors;
}
下面看看Spring时如何创建代理的
public Object getProxy(ClassLoader classLoader) {
//createAopProxy方法创建一个代理对象
return createAopProxy().getProxy(classLoader);
}
createAopProxy方法创建一个代理对象,这个方法由DefaultAopProxyFactory类实现:
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
这个方法决定了是使用jdk代理还是cglib代理,上面的判断由三个校验组成,只要有任何一个结果为true都会进入else使用jdk代理,来看下这个三个判断条件:
//自定义指定配置
config.isOptimize()
//是否使用了proxy-target-class="true"
config.isProxyTargetClass()
//目标类是否实现了接口 而且接口不能与SpringProxy类相同
hasNoUserSuppliedProxyInterfaces(config))
这三个方法中的config.isOptimize(),config.isProxyTargetClass()默认都会返回false,针对最后一个方法是判断目标类是否没有接口,当目标类有接口实现的时候就会走到最后一个else,使用JdkDynamicAopProxy创建代理,也就是jdk代理。
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
//如果目标类是实现了接口 会直接返回false
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
然后再来看config.isOptimize(),这个属性的初始值是false,我并没有在源码中发现有哪里可以修改这个值,但是如果我们自定义一个实现了AbstractAdvisorAutoProxyCreator类的处理器,像下面这样配置,就可以设置这个值为true了。这个应该是基于Sping的扩展的,但这里对普通注解bean都是false的,所以不需要太纠结。
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="optimize" value="true"/>
</bean>
然后再看一个比较关键的判断config.isProxyTargetClass(),这个配置实际上就是aop基于自动注解配置里面的一个属性
<aop:aspectj-autoproxy proxy-target-class="true">
这个值就是proxy-target-class的值,这个值的注入是注册AnnotationAwareAspectJAutoProxyCreator时候设置的,来看下面的代码,这部分是注册AnnotationAwareAspectJAutoProxyCreator时候,设置proxyTargetClass属性的
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
if (sourceElement != null) {
// PROXY_TARGET_CLASS_ATTRIBUTE就是proxy-target-class属性
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
//主要执行这个方法
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//如果proxy-target-class属性配置的是true 直接设置这个值的类型为true
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
所以说如果我们通过<aop:aspectj-autoproxy proxy-target-class="true">方式如果配置了proxy-target-class为true,则就会直接使用cglib代理了,当然下面还会有一些校验,不过最终还是会走到cglib代理,这次先来看看cglib代理吧
6、Cglib模式的代理
6.1 Enhancer的创建
cglib代理就是由ObjenesisCglibAopProxy这个类实现的,获取代理对象直接调用其父类CglibAopProxy的getProxy方法获取。
@Override
public Object getProxy(ClassLoader classLoader) {
try {
Class<?> rootClass = this.advised.getTargetClass();
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
//校验类
validateClassIfNecessary(proxySuperClass, classLoader);
//开始创建cglib代理
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
//核心点1:获取回调集合
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
//核心点2:设置过滤器
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// 创建代理对象
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (Exception ex) {
//异常捕获抛出
.........
}
}
cglib代理的使用方式和我们之前做的简单案例的使用是一样的:
首先创建一个Enhancer,setSuperclass设置要代理的对象,
setInterfaces可以不设置,因为是基于cglib代理的,不需要接口实现,这里的含义是指定创建的代理继承AopProxy的一些接口。
setNamingPolicy设置cglib的命名策略,这里是以BySpringCGLIB为前缀:
***
getCallbacks***方法用于获取增强器,会详细解析。
***
enhancer.setCallbackFilter()***用于过滤器用于过滤不需要增强的方法的,也会详细解析
最后通过***
createProxyClassAndInstance***创建一个代理实例对象。
从上面单独对cglib单例的使用讲解可以知道,cglib代理是基于asm在字节码上的实现,可以动态的创建一个继承于目标类的代理对象,然后执行增强操作,此外cglib还提供了Callback接口用于执行对方法的拦截,由MethodInterceptor接口的intercept方法拦截,然后cglib还可以通过CallbackFilter类来指定对不同的类或者方法执行不同的增强操作,accept方法会返回一个索引,这个索引是设置的CallbackFilter集合中集合的索引,通过accpet方法如果返回0就会使用Callback集合中第一个增强拦截器,下面会针对Spring设置的Callback集合和CallbackFilter分别分析。
6.2 Callback拦截器集合
首先来看下Spring是如何获取CallbackFilter集合。
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimisation choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
Callback targetInterceptor;
if (exposeProxy) {
//这个方法是表示处理嵌套的,也就是在代理方法中调用本次的其他方法 那么针对该方法的增强效果将会失效
........
}
else {
targetInterceptor = isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
}
Callback targetDispatcher = isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
//这个事核心点,从这里可以看出Spring里面总共设置了6个方法增强器
//其中第一个就是我们我们配置的增强器
Callback[] mainCallbacks = new Callback[] {
aopInterceptor, // for normal advice
targetInterceptor, // invoke target without considering advice, if optimized
new SerializableNoOp(), // no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
if (isStatic && isFrozen) {
.........
}
else {
callbacks = mainCallbacks;
}
return callbacks;
}
首先Spring将创建的的ProxyFactroy对象封装到DynamicAdvisedInterceptor,这个代理类里面有之前匹配到的Advisor集合,然后判断exposeProxy是否是true,这个属性也是在xml配置文件中配置的,背景就是如果在事务A中使用了代理,事务A调用了目标类的的方法a,在方法a中又调用目标类的方法b,方法a,b同时都是要被增强的方法,如果不配置exposeProxy属性,方法b的增强将会失效,如果配置exposeProxy,方法b在方法a的执行中也会被增强了。接下来再看mainCallbacks这个集合,这个集合里面封装了6个是实现了Callback或MethodInterceptor接口的类,其中第一个aopInterceptor就是封装了目标类的要增强的逻辑也就是Advisor集合,第二个是针对之前配置的optimize属性使用的,后面的四个基本上不会做太多业务逻辑上的拦截,Spring最终将这6个增强器集合返回作为cglib的拦截器链,之后通过CallbackFilter的accpet方法返回的索引从这个集合中返回对应的拦截增强器执行增强操作。
6.3 CallbackFilter 过滤器
Spring实现的是通过ProxyCallbackFilter实现CallbackFilter接口,然后赋值给代理对象,上面分析过,这个接口主要是通过accpet接口实现的,来看看是如何匹配的:
@Override
public int accept(Method method) {
if (AopUtils.isFinalizeMethod(method)) {
logger.debug("Found finalize() method - using NO_OVERRIDE");
//不使用代理
return NO_OVERRIDE;
}
if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
if (logger.isDebugEnabled()) {
logger.debug("Method is declared on Advised interface: " + method);
}
return DISPATCH_ADVISED;
}
// We must always proxy equals, to direct calls to this.
if (AopUtils.isEqualsMethod(method)) {
logger.debug("Found 'equals' method: " + method);
return INVOKE_EQUALS;
}
// We must always calculate hashCode based on the proxy.
if (AopUtils.isHashCodeMethod(method)) {
logger.debug("Found 'hashCode' method: " + method);
return INVOKE_HASHCODE;
}
Class<?> targetClass = this.advised.getTargetClass();
// Proxy is not yet available, but that shouldn't matter.
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
boolean haveAdvice = !chain.isEmpty();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
if (haveAdvice || !isFrozen) {
// If exposing the proxy, then AOP_PROXY must be used.
if (exposeProxy) {
if (logger.isDebugEnabled()) {
logger.debug("Must expose proxy on advised method: " + method);
}
//使用封装了Advisor的拦截增强器
return AOP_PROXY;
}
String key = method.toString();
// Check to see if we have fixed interceptor to serve this method.
// Else use the AOP_PROXY.
if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
// We know that we are optimising so we can use the FixedStaticChainInterceptors.
int index = this.fixedInterceptorMap.get(key);
return (index + this.fixedInterceptorOffset);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Unable to apply any optimisations to advised method: " + method);
}
//使用封装了Advisor的拦截增强器
return AOP_PROXY;
}
}
else {
if (exposeProxy || !isStatic) {
return INVOKE_TARGET;
}
Class<?> returnType = method.getReturnType();
if (targetClass == returnType) {
return INVOKE_TARGET;
}
else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
return DISPATCH_TARGET;
}
else {
return INVOKE_TARGET;
}
}
}
这个方法里面,我们只需要关心AOP_PROXY这个值就可以了,这个值是0,也就是对应上面生成的Callback集合中的第一个拦截增强器,也就是aopInterceptor,这个拦截器里面封装了所有能够作用于目标类的Advisor增强类,所以就会调用到切面类定义的切面方法来进行增强操作了。
6.4 Advisor调用链
上面分析过,Spring使用的是aopInterceptor作为拦截增强器,这个增强器被封装进了DynamicAdvisedInterceptor类中,这个类实现了MethodInterceptor方法,所以被拦截到的方法会进入intercept方法中,由于这个类里面封装了前面我们匹配到的所有能够作用于这个类的Advisor集合,但是可能针对这些Advisor可能执行顺序有些疑问,虽然Spring允许通过继承Order接口实现排序,但是比如@Before,@After,@Round等是需要在某个方法的不同时机执行的,所以这需要构造一个调用链,废话不多,还是先来看aopInterceptor的intercept方法是如何做的:
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
try {
if()
{
//省略不重要代码
........
}
else {
//创建一个方法执行器 主要逻辑在这里
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
在这个方法里面,我们只需要关心CglibMethodInvocation的proceed方法,调用链就是在这里构建的
@Override
public Object proceed() throws Throwable {
//首先获取到所有的Advisor 然后去除一个下标-1
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//获取当前Advisor
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
//如果是动态方法匹配器
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
//匹配失败直接跳过,再次执行proceed方法
return proceed();
}
}
else {
//调用拦截器增强集合
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
这个方法是构造调用链的实现原理,首先获取到Advisor集合,然后用currentInterceptorIndex表示当前集合剩余未执行的Advisor,currentInterceptorIndex=0的时候,集合中所有的Advisor已经执行完毕了,这种方法还是很巧妙的,我们从整体上看下是如何形成调用链的,该方法最终调用最后一行代码,也就是MethodInterceptor类的invoke方法,把当前对象作为参数传进去,当前对象也就是aopInterceptor,这里面封装了所有符合条件的Advisor(每次都要强调这个类里面的数据就是让我们始终能保持在主线上),该类实现了ProxyMethodInvocation接口间接实现了MethodInvocation接口,最后一行的代码调用invoke方法,也就是MethodInterceptor接口的方法,来看下这个方法的定义:
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
invoke的参数是MethodInvocation,由于aopInterceptor是MethodInvocation的实现类,所以可以将该类作为参数往下传递,前面分析过了切面的注解方法注解对应的类为:
| @Before | MethodBeforeAdviceInterceptor |
|---|---|
| @After | AspectJAfterReturningAdvice |
| @Around | AspectJAroundAdvice |
| @AfterThrowing | AspectJAfterThrowingAdvice |
这些生成的Advice切面类毫无疑问都实现了MethodInterceptor方法,而MethodInterceptor的invoke方法需要接收一个类型为MethodInvocation的对象,正好aopInterceptor实现了MethodInvocation,所以可以作为参数在多个Advisor间流转,每执行完一个Advisor就进入aopInterceptor的process方法再次校验,直至执行完所有的Advisor`。
假设此时的aopInterceptor里面包含了两个切面方法MethodBeforeAdviceInterceptor和AspectJAfterReturningAdvice,此时先取出AspectJAfterReturningAdvice,然后执行invoke方法,看下AspectJAfterReturningAdvice的invoke方法:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
//此时的mi对象就是 aopInterceptor ,又回到了proceed方法
return mi.proceed();
}
finally {
//自身的方法最后执行
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
AspectJAfterReturningAdvice的invoke方法会首先调用参数 mi 的proceed方法,此时mi就是aopInterceptor对象(这里面封装了所有适用于目标类对象的切面方法类),所以会再次返回到拦截器集合里,取出MethodBeforeAdviceInterceptor对象,调用它的invoke方法,最后自调用AspectJAfterReturningAdvice封装的切面方法也就是通过@After注解修饰的方法。再来看看AspectJAfterReturningAdvice的invoke方法
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
这里会先调用通过@Before注解修饰的方法,然后再次调用aopInterceptor拦截器链,虽然这两个注解是最简单的,但是确时逻辑最清晰的,也是最容易理解的,其他的切面注解方法的调用过程也是这样,最终完成这个拦截器链的调用。
经过上面的步骤,就可以创建一个代理对象了, 然后Spring将代理对象放入缓存中,我们使用时得到的是一个代理对象,调用目标方法,就会执行到增强器方法,进而找到所有的Advisor,构造一个调用链,这样就完成业务的增强了。我相信如果对照着源码看,多调试几遍代码,应该会大致了解Spring实现Cglib的代理的一个流程。关于Spring时如何实现cglib代理的暂时就分析到这里。
7、JDK模式的代理
jdk模式的代理是通过JdkDynamicAopProxy这个类实现的,由于jdk的代理的实现比cglib代理稍微简单一点,我这里暂时就先不分析了,关于jdk代理模式的解析估计有很多资料,以后有时间我补上这部分内容
8、总结
Spring源码很复杂,如果想把每个细节都搞明白,几乎是不可能的,而且无疑会浪费很多时间,我们只需要搞懂常用的功能的实现方式,遇到问题可以从源码的角度解决问题就行了,说的更高一点,如果我们能学到作者的一些架构思路以及对代码的功能抽象,以后可以对Spring进行扩展,那就再好不过了,废话不多了,总结下一些这部分内容究竟在说些什么吧。
这一部分内容是Spring AOP的第二部分内容,是基于上一步解析@Aspect注解,将注入@Before等注解方法生成Advice切面方法类的过程,这一部分则是使用这些切面方法类,然后找出能够作用于目标类的Advisor,然后创建代理对象,具体流程是:
1、从Advisor缓存中获取第一步生成的所有Advisor(包括通过xml配置和注解配置的)
2、遍历Advisor集合,从Advisor里面获取PointCut,获取方法匹配器MethodMatcher,通过match方法匹配一个当前Advisor是否能够作用于目标类,如果能,则放入集合中
3、如果第二部获取的集合为空,则直接返回,不需要创建代理,如果不为空,也就是Advisor缓存中有能够作用于目标类的Advisor,则根据配置选择不同的代理的方法(cglib 还是 jdk 代理)
4、假如是cglib代理,需要构建Callback拦截增强器集合,然后创建一个CallbackFilter过滤器,选择合适的拦截增强器对bean实现增强处理
5、返回创建的代理对象。
写完这一篇,我终于把这两个月看Spring的源码对其的理解记录下来了, 即便如此,我感觉我对Spring还是知之甚少,还需要巩固,我认为如果能通过Spring封装自己写的框架,就像Sping-redis,Spring-kafka 那样直接通过注解或者配置就可以拿来使用了,肯定对Spring有一个更高层次的认识,为了加深对Spring的理解,通过刘欣老师的讲解,我也跟着写了一遍稍微简单的Spring实现,那段代码花了我将近一个月的时间,但是写完再看Spring的源码就会容易理解的多,源码地址是:
感兴趣的可以拉下来看看。