Spring_AOP源码分析


title: Spring_AOP源码分析
date: 2016-11-03 01:15:11
categories: Spring
tags: [Spring,AOP]


Spring AOP 源码分析

零.Spring aop的使用

想要分析aop源码。总要先知道spring aop怎么使用吧。要不然,分析个orz...

使用Spring AOP可以基于两种方式,一种是比较方便和强大的注解方式,另一种则是中规中矩的xml配置方式。

0.1 基于注解的使用

第一步xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <!-- 激活组件扫描功能,在包cn.ysh.studio.spring.aop及其子包下面自动扫描通过注解配置的组件 -->
    <context:component-scan base-package="cn.ysh.studio.spring.aop"/>
    <!-- 激活自动代理功能 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    
    <!-- 用户服务对象 -->
    <bean id="userService" class="cn.ysh.studio.spring.aop.service.UserService" />
    
</beans>

第二步是为Aspect切面类添加注解:

@Component
//声明这是一个切面Bean
@Aspect
public class ServiceAspect {

    private final static Log log = LogFactory.getLog(ServiceAspect.class);
    
    //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
    @Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
    public void aspect(){    }
    
    /*
     * 配置前置通知,使用在方法aspect()上注册的切入点
     * 同时接受JoinPoint切入点对象,可以没有该参数
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("before " + joinPoint);
        }
    }
    
    //配置后置通知,使用在方法aspect()上注册的切入点
    @After("aspect()")
    public void after(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("after " + joinPoint);
        }
    }
    
    //配置环绕通知,使用在方法aspect()上注册的切入点
    @Around("aspect()")
    public void around(JoinPoint joinPoint){
        long start = System.currentTimeMillis();
        try {
            ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            if(log.isInfoEnabled()){
                log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
            }
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            if(log.isInfoEnabled()){
                log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
            }
        }
    }
    
    //配置后置返回通知,使用在方法aspect()上注册的切入点
    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("afterReturn " + joinPoint);
        }
    }
    
    //配置抛出异常后通知,使用在方法aspect()上注册的切入点
    @AfterThrowing(pointcut="aspect()", throwing="ex")
    public void afterThrow(JoinPoint joinPoint, Exception ex){
        if(log.isInfoEnabled()){
            log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
        }
    }
    
}

第三步测试:

  public class Tester {

     private final static Log log = LogFactory.getLog(Tester.class);
    
     public static void main(String[] args) {
         //启动Spring容器
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         //获取service组件
          UserService service = (UserService) context.getBean("userService");
        //以普通的方式调用UserService对象的三个方法
         User user = service.get(1L);
         service.save(user);
         try {
              service.delete(1L);
        } catch (Exception e) {
            if(log.isWarnEnabled()){
                log.warn("Delete user : " + e.getMessage());
            }
        }
    }
}

0.2 xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">


    <!-- 系统服务组件的切面Bean -->
    <bean id="serviceAspect" class="cn.ysh.studio.spring.aop.aspect.ServiceAspect"/>
    <!-- AOP配置 -->
    <aop:config>
        <!-- 声明一个切面,并注入切面Bean,相当于@Aspect -->
        <aop:aspect id="simpleAspect" ref="serviceAspect">
            <!-- 配置一个切入点,相当于@Pointcut -->
            <aop:pointcut expression="execution(* cn.ysh.studio.spring.aop.service..*(..))" id="simplePointcut"/>
            <!-- 配置通知,相当于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
            <aop:before pointcut-ref="simplePointcut" method="before"/>
            <aop:after pointcut-ref="simplePointcut" method="after"/>
            <aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
            <aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
        </aop:aspect>
    </aop:config>

</beans>

AOP用起来还是很简单的。就把xml配置好就算完工了。有Advisor和aspect两种方式来完成。如果是用Advisor的话需要实现AfterReturningAdvice,MethodBeforeAdvice,ThrowsAdvice等接口。而如果用aspect的话则不用继承或者实现其他的类,一个普通的类即可。

一.AOP介绍

软件开发经历了从汇编语言到高级语言和从过程化编程到面向对象编程;前者是为了提高开发效率,而后者则使用了归纳法,把具有共性的东西进行归类并使之模块化,达到便于维护和扩展的目的;如果说面向对象编程可以对业务需求进行很好的分解使之模块化;那么面向切面编程AOP(Aspect-Oriented Programming)则可以对系统需求进行很好的模软件开发经历了从汇编语言到高级语言和从过程化编程到面向对象编程;前者是为了提高开发效率,而后者则使用了归纳法,把具有共性的东西进行归类并使之模块化,达到便于维护和扩展的目的;如果说面向对象编程可以对业务需求进行很好的分解使之模块化;那么面向切面编程AOP(Aspect-Oriented Programming)则可以对系统需求进行很好的模块组织,简化系统需求和实现之间的对比关系,是对OOP思想的一种补充;块组织,简化系统需求和实现之间的对比关系,是对OOP思想的一种补充。

举个例子来说明一下吧!现在系统中有很多的业务方法,如上传产品信息、修改产品信息、发布公司库等;现在需要对这些方法的执行做性能监控,看每个业务方法的执行时间;在不改变原业务代码的基础上,也许我们会这么做。

Offer接口:

package edu.zju.cs.lyp.Spring_aop;

public interface IOffer {
    public void postOffer();
    public void modifyOffer();
}

Offer实现:

package edu.zju.cs.lyp.Spring_aop;

public class OfferImpl implements IOffer {
    public void postOffer() {
        System.out.println("post offer");
    }

    public void modifyOffer() {
        System.out.println("modify offer");
    }
}

工具类:

package edu.zju.cs.lyp.Spring_aop;

public class PerformanceUtil {
    public static long start=0;
    public static long end=0;
    public static void startPerformance(){
        start=System.currentTimeMillis();
    }
    public static void endPerformance(){
        end=System.currentTimeMillis();
        System.out.println("method use:"+(end-start));
    }
}

Offer代理:

package edu.zju.cs.lyp.Spring_aop;

public class OfferProxy implements IOffer {

    private IOffer delegate;
    
    public OfferProxy(IOffer delegate){
        this.delegate=delegate;
    }
    public void postOffer() {
        PerformanceUtil.startPerformance();
        delegate.postOffer();
        PerformanceUtil.endPerformance();
    }

    public void modifyOffer() {
        PerformanceUtil.startPerformance();
        delegate.modifyOffer();
        PerformanceUtil.endPerformance();
    }

}

Offer测试:

package edu.zju.cs.lyp.Spring_aop;

public class TestProxy {
    public static void main(String[] args) {
        IOffer offer= new OfferProxy(new OfferImpl());
        offer.postOffer();
        offer.modifyOffer();
    }
}

输出:

post offer
method use:1
modify offer
method use:0

上面的例子中,OfferProxy实现了IOffer,而所有的业务实现均委托给其成员offer;可以想像,这应该就是最简单的AOP的实现了;但这种方式会存在一个问题:如果有非常多的这种业务对象需要性能监控,我们就需要写同样多的XyzProxy来满足需求,这也是非常巨大的工作量。

二. 代理模式

代理模式中,存在一个称为ProxyObject的代理对象和RealObject的真实对象,它们都实现了相同的接口;在调用的地方持有ProxyObject的实例,当调用request()方法时,ProxyObject可以在执行RealObject.request()前后做一些特定的业务,甚至不调用RealObject.request()方法。

目前实现代理模式的方式有两种:基于JDK的动态代理和基于CGLIB字节码的代理。

2.1 JDK动态代理

JDK动态代理,顾名思义,是基于JDK的反射(reflect)机制;在JDK中,提供了InvocationHandler这个接口。

注释如下:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

意思是说:该接口由被代理对象的handler所实现;当调用代理对象的方法时,该方法调用将被编码,然后交给代理对象的invoke方法去执行。
因此上面的代码可以改写成如下所示:

实现:

package edu.zju.cs.lyp.Spring_aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory implements InvocationHandler {
    private Object delegate;
    
    public Object bind(Object delegate){
        this.delegate= delegate;
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), 
                delegate.getClass().getInterfaces(), this);
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        PerformanceUtil.startPerformance();
        Object result=null;
        try {
            result=method.invoke(delegate, args);
        } catch (Exception e) {
            // TODO: handle exceptions
        }
        PerformanceUtil.endPerformance();
        return result;
    }
    
}

测试:

package edu.zju.cs.lyp.Spring_aop;

public class TestJDKProxy {
    public static void main(String[] args) {
        IOffer offer = (IOffer) new ProxyFactory().bind(new OfferImpl());
        offer.postOffer();
        offer.modifyOffer();
    }
}

结果:

post offer
method use:1
modify offer
method use:0

通过这种方式,你不需要为针对每一个业务写一个代理对象,就可以很轻松地完成你的需求;但也许你已经注意到了,JDK的动态代理,在创建代理对象(上面红色代码部分)时,被代理的对象需要实现接口(即面向接口编程);

2.2CGLIB代理方式

如果目标对象没有实现任何接口,那怎么办呢?不用担心,你可以用CGLIB来实现代理。

实现:

package edu.zju.cs.lyp.Spring_aop;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;


public class CglibProxyFactory  implements MethodInterceptor{
    
    private Object delegate;
    
    public Object bind(Object delegate){
        this.delegate=delegate;
        Enhancer enhancer= new Enhancer();
        enhancer.setSuperclass(delegate.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        PerformanceUtil.startPerformance();
        Object o =proxy.invoke(this.delegate, args);
        PerformanceUtil.endPerformance();
        return o;
    }
    
}

测试类:

package edu.zju.cs.lyp.Spring_aop;

public class TestCglibProxy {
    public static void main(String[] args) {
        DefaultOffer defaultOffer= (DefaultOffer) new CglibProxyFactory().bind(new DefaultOffer());
        defaultOffer.postOffer();
        defaultOffer.modifyOffer();
    }
}

测试结果:

post offer
method use:20
modify offer
method use:0

使用CGLIB创建的代理对象,其实就是继承了要代理的目标类,然后对目标类中所有非final方法进行覆盖,但在覆盖方法时会添加一些拦截代码(上面CglibProxyFactory类中的intercept方法)。

三. Spring AOP 实现

3.1 Spring AOP 几个基本概念

Spring AOP jar包:spring-aop-4.2.5.release.jar

Spring AOP中的几个基本概念,每次学习AOP都被这几个概念折腾的很不爽,我们在这里再把这几个概念描述一遍,力争把这几个概念搞清,在每次review这块内容的时候可以很快上手。

  1. 切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
  2. 连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
  3. 通知(Advice):通知就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;
  4. 切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;
  5. 目标对象(Target):就是被AOP动态代理的目标对象;

用一张图来形象地表达AOP的概念及其关系如下:

3.2 Spring AOP 中切入点、通知、切面的实现

理解了上面的几个概念后,我们分别来看看Spring AOP是如何实现这些概念的;

3.2.1.切入点(Pointcut)

它定义了哪些连接点需要被织入横切逻辑;在Java中,连接点对应哪些类(接口)的方法。因此,我们都能猜到,所谓的切入点,就是定义了匹配哪些娄的哪些方法的一些规则,可以是静态的基于类(方法)名的值匹配,也可以是基于正则表达式的模式匹配。

来看看Spring AOP Pointcut相关的类图:

在Pointcut接口的定义中,也许你已经想到了,ClassFilter是类过滤器,它定义了哪些类名需要拦截;典型的两个实现类为TypePatternClassFilter和TrueClassFilter(所有类均匹配);而MethodMatcher为方法匹配器,定义哪些方法需要拦截。

在上面的类图中:

  • StaticMethodMatch与DynamicMethodMatch的区别是后者在运行时会依据方法的参数值进行匹配。
  • NameMatchMethodPointCut根据指定的mappedNames来匹配方法。
  • AbstractRegexpMethodPointCut根据正则表达式来匹配方法

类图中部分代码实现:

MethodMatcher.class:

package org.springframework.aop;

import java.lang.reflect.Method;

public abstract interface MethodMatcher {
    public static final MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

    public abstract boolean matches(Method paramMethod, Class<?> paramClass);

    public abstract boolean isRuntime();

    public abstract boolean matches(Method paramMethod, Class<?> paramClass, Object[] paramArrayOfObject);
}

StaticMethodMatcher.class:

package org.springframework.aop.support;

import java.lang.reflect.Method;
import org.springframework.aop.MethodMatcher;

public abstract class StaticMethodMatcher implements MethodMatcher {
    public final boolean isRuntime() {
        return false;
    }

    public final boolean matches(Method method, Class<?> targetClass, Object[] args) {
        throw new UnsupportedOperationException("Illegal MethodMatcher usage");
    }
}

DynamicMethodMatcher.class:

package org.springframework.aop.support;

import java.lang.reflect.Method;
import org.springframework.aop.MethodMatcher;

public abstract class DynamicMethodMatcher implements MethodMatcher {
    public final boolean isRuntime() {
        return true;
    }

    public boolean matches(Method method, Class<?> targetClass) {
        return true;
    }
}

Pointcut.class:

package org.springframework.aop;

public abstract interface Pointcut {
    public static final Pointcut TRUE = TruePointcut.INSTANCE;

    public abstract ClassFilter getClassFilter();

    public abstract MethodMatcher getMethodMatcher();
}

3.2.2.通知(Advice)

通知定义了具体的横切逻辑。在Spring中,存在两种类型的Advice,即per-class和per-instance的Advice。

所谓per-class,即该类型的Advice只提供方法拦截,不会为目标对象保存任何状态或者添加新的特性,它也是我们最常见的Advice。下面是per-class的类图:

  • BeforeAdvice:在连接点前执行的横切逻辑。
  • AfterReturningAdvice:在连接点执行后,再执行横切逻辑。
  • AfterAdvice:一般由程序自己实现,当抛出异常后,执行横切逻辑。
  • AroundAdvice:Spring AOP中并没有提供这个接口,而是采用了AOP Alliance的MethodInteceptor接口;通过看AfterReturningAdvice的源码我们知道,它是不能更改连接点所在方法的返回值的(更改引用);但使用的MethodInteceptor,所有的事情,都不在话下。

部分源码介绍:

AfterAdvice,Advice两个接口是空的

AfterReturningAdvice.class

package org.springframework.aop;

import java.lang.reflect.Method;

public abstract interface AfterReturningAdvice extends AfterAdvice {
    public abstract void afterReturning(Object paramObject1, Method paramMethod, Object[] paramArrayOfObject,
            Object paramObject2) throws Throwable;
}

MethodBeforeAdvice.class

import java.lang.reflect.Method;

public abstract interface MethodBeforeAdvice extends BeforeAdvice {
    public abstract void before(Method paramMethod, Object[] paramArrayOfObject, Object paramObject) throws Throwable;
}

MethodInterceptor.class

package org.aopalliance.intercept;

public abstract interface MethodInterceptor extends Interceptor {
    public abstract Object invoke(MethodInvocation paramMethodInvocation) throws Throwable;
}

在上面的类图中,还有两种类没有介绍,那就是 ***AdviceAdapter 和 ***AdviceInteceptor.结构如图所示


我们以AfterReturningAdviceInterceptor为例来说明:

package org.springframework.aop.framework.adapter;

import java.io.Serializable;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.util.Assert;

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
    private final AfterReturningAdvice advice;

    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        Object retVal = mi.proceed();
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }
}

该类实现了MethodInterceptor和AfterAdvice接口,同时构造函数中还有一个AfterReturningAdvice实例的参数;这个类存在的作用是为了什么呢?Spring AOP把所有的Advice都适配成了MethodInterceptor,统一的好处是方便后面横切逻辑的执行(参看下一节),适配的工作即由***AdviceAdapter完成;

Spring AOP所谓的AfterReturningAdvice,通过适配成MethodInterceptor后,其实就是在invoke方法中,先执行目标对象的方法,再执行的AfterReturningAdvice所定义的横切逻辑。

对于per-instance的Advice,目前只有一种实现,就是Introduction,使用的场景比较少。

3.2.3.切面(Aspect)

在Spring中,Advisor就是切面;但与通常的Aspect不同的是,Advisor通常只有一个Pointcut和一个Advice,而Aspect则可以包含多个Pointcut和多个Advice,因此Advisor是一种特殊的Aspect。

接下来看下per-class Advisor的类图:

继承关系如下:

Advisor包含一个Pointcut和一个Advisor;在AbstractGenericPointcutAdvisor中,持有一个Advice的引用;下面的几个实现,均是针对前面提到的几种不同的Pointcut的实现。

3.3 Spring AOP实现基本线索

我们选择ProxyFactoryBean作为入口点和分析的开始。ProxyFactoryBean是在Spring IoC环境中,创建AOP应用的最底层方法,从中,可以看到一条实现AOP的基本线索。

所有的逻辑从以下的方法开始,我们主要针对单例的代理对象的生成:

public Object getObject() throws BeansException {
    //这里初始化通知器链
    initializeAdvisorChain();
    if (isSingleton()) {
        //根据定义需要生成单例的proxy
        return getSingletonInstance();
    }

    if (this.targetName == null) {
        this.logger.warn(
                "Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");
    }
    //这里根据定义需要生成prototype类型的proxy
    return newPrototypeInstance();
}

下面我们深入到SpringAOP核心代码的内部,看看代理对象的生成机制,拦截器横切逻辑以及织入的实现。

private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            //返回具体的目标对象,就是被代理的对象
            this.targetSource = freshTargetSource();
            if ((this.autodetectInterfaces) && (getProxiedInterfaces().length == 0) && (!(isProxyTargetClass()))) {
                //从targetsource中获取目标对象的class
                Class targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }
                //这里设置代理对象的借口
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            //这里使用proxyfactory来生成我们需要的proxy。
            super.setFrozen(this.freezeProxy);
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }

ProxyFactoryBean是AdvisedSupport的子类,Spring使用AopProxy接口把AOP代理的实现与框架的其他部分分离开来。在AdvisedSupport中通过这样的方式来得到AopProxy,当然这里需要得到AopProxyFactory的帮助 ,从JDK或者cglib中得到想要的代理对象:

protected final synchronized AopProxy createAopProxy() {
        if (!(this.active)) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

getAopProxyFactory()获取ProxyCreatorSupport的属性aopProxyFactory

public AopProxyFactory getAopProxyFactory() {
        return this.aopProxyFactory;
    }

该属性被默认初始化为DefaultAopProxyFactory对象。

public ProxyCreatorSupport() {
    this.aopProxyFactory = new DefaultAopProxyFactory();
}
public ProxyCreatorSupport(AopProxyFactory aopProxyFactory) {
        Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");
        this.aopProxyFactory = aopProxyFactory;
    }

这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方,它包含JDK和Cglib两种实现方式。让我接着往里面看:

/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/
package org.springframework.aop.framework;

import java.io.Serializable;
import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        //做一些判断操作。isoptimize()是指是否采取进一步的优化,true采用cglib来生成代理。
        //isproxytargetclass决定是否采用基于接口的代理。
        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.");
            }
            //如果目标对象实现的接口,则采用jdk动态代理来生成proxy
            if ((targetClass.isInterface()) || (Proxy.isProxyClass(targetClass))) {
                return new JdkDynamicAopProxy(config);
            }
            //如果target不是接口的实现的话,返回cglib类型的aopproxy
            return new ObjenesisCglibAopProxy(config);
        }
        //不满足最开始的判断 直接使用jdk动态代理
        return new JdkDynamicAopProxy(config);
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class[] ifcs = config.getProxiedInterfaces();
        return ((ifcs.length == 0) || ((ifcs.length == 1) && (SpringProxy.class.isAssignableFrom(ifcs[0]))));
    }
}

可以看到其中的代理对象可以由JDK或者Cglib来生成,JdkDynamicAopProxy类和Cglib2AopProxy都实现的是AopProxy的接口,我们进入JdkDynamicAopProxy实现中看看Proxy是怎样生成的:

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        if ((config.getAdvisors().length == 0) && (config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE)) {
            throw new AopConfigException("No advisors and no TargetSource specified");
        }
        this.advised = config;
    }

    public Object getProxy() {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }

    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        //根据advised中的配置信息,获取proxy需要代理的接口、放入proxiedInterfaces中。
        Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        //这里我们调用jdk proxy 来生成需要的proxy实例
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

再来看cglib代理

class ObjenesisCglibAopProxy extends CglibAopProxy {
    private static final Log logger = LogFactory.getLog(ObjenesisCglibAopProxy.class);

    private static final SpringObjenesis objenesis = new SpringObjenesis();

    public ObjenesisCglibAopProxy(AdvisedSupport config) {
        super(config);
    }

    protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
        Class proxyClass = enhancer.createClass();
        Object proxyInstance = null;

        if (objenesis.isWorthTrying()) {
            try {
                proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
            } catch (Throwable ex) {
                logger.debug("Unable to instantiate proxy using Objenesis, falling back to regular proxy construction",
                        ex);
            }

        }

        if (proxyInstance == null) {
            try {
                proxyInstance = (this.constructorArgs != null)
                        ? proxyClass.getConstructor(this.constructorArgTypes).newInstance(this.constructorArgs)
                        : proxyClass.newInstance();
            } catch (Throwable ex) {
                throw new AopConfigException(
                        "Unable to instantiate proxy using Objenesis, and regular proxy instantiation via default constructor fails as well",
                        ex);
            }

        }

        ((Factory) proxyInstance).setCallbacks(callbacks);
        return proxyInstance;
    }
}

继承了CglibAopProxy。下面的和最开始的例子比较,是不是很熟悉。

public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }
        try {
            Class rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

            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);

            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));

            Callback[] callbacks = getCallbacks(rootClass);
            Class[] types = new Class[callbacks.length];
            int k;
            for (int k = 0; k < types.length; ++k) {
                types[k] = callbacks[k].getClass();
            }

            enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(),
                    this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);

            return createProxyClassAndInstance(enhancer, callbacks);
        } catch (CodeGenerationException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass()
                    + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex);
        } catch (IllegalArgumentException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass()
                    + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex);
        } catch (Exception ex) {
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }

用Proxy包装target之后,通过ProxyFactoryBean得到对其方法的调用就被Proxy拦截了, ProxyFactoryBean的getObject()方法得到的实际上是一个Proxy了,target对象已经被封装了。对 ProxyFactoryBean这个工厂bean而言,其生产出来的对象是封装了目标对象的代理对象。

3.4拦截器的作用

前面分析了SpringAOP实现中得到Proxy对象的过程,接下来我们去探寻Spring AOP中拦截器链是怎样被调用的,也就是Proxy模式是怎样起作用的。
还记得在JdkDynamicAopProxy中生成Proxy对象的时候,有一句这样的代码吗?

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

这里我们的JdkDynamicAopProxy实现了InvocationHandler这个接口,final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable.

this参数对应的是InvocationHandler对象,也就是说当 Proxy对象的函数被调用的时候,InvocationHandler的invoke方法会被作为回调函数调用.

我们来看一下动态代理中invoke函数的实现:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Class targetClass = null;
        Object target = null;
        try {
            Object localObject1;
            //目标对象未实现equals方法
            if ((!(this.equalsDefined)) && (AopUtils.isEqualsMethod(method))) {
                localObject1 = Boolean.valueOf(equals(args[0]));
                return localObject1;
            }
            //目标对象未实现hashcode方法
            if ((!(this.hashCodeDefined)) && (AopUtils.isHashCodeMethod(method))) {
                localObject1 = Integer.valueOf(hashCode());
                return localObject1;
            }
            //opaque顺序控制生成代理对象是否可以强制转换类型为advised,默认为false。
            //z这里针对opaque为true且代理的为借口自身,并且代理类为advised借口的子接口,不进行代理操作。
            if ((!(this.advised.opaque)) && (method.getDeclaringClass().isInterface())
                    && (method.getDeclaringClass().isAssignableFrom(Advised.class))) {
            //这里就是目标对象的调用
                localObject1 = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
                return localObject1;
            }
            //设置exposeproxy为true,让springaop框架将生成的当前代理对象绑定到threadlocal
            if (this.advised.exposeProxy) {
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }
            //这里是得到目标对象,目标对象可能来自一个示例池或者一个简单的java对象。
            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }
            //重要!!!:::这里获得定义好的拦截器链
            List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            Object retVal;
            Object retVal;
            //如果没有拦截器,直接调用目标的对象方法,不创建methodinvocation
            if (chain.isEmpty()) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            } else {
                //如果有拦截器的设定,那么需要调用拦截器之后才能调用目标对象的相应的方法。
                //通过构造一个ReflectiveMethodInvocation来实现
                MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass,
                        chain);
                //通过ReflectiveMethodInvocation来调用拦截器连和相应的目标方法。
                //在proceed方法内部实现了自身的递归调用来便利整个拦截器链。

                retVal = invocation.proceed();
            }

            Class returnType = method.getReturnType();
            if ((retVal != null) && (retVal == target) && (returnType.isInstance(proxy))
                    && (!(RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())))) {
                retVal = proxy;
            } else if ((retVal == null) && (returnType != Void.TYPE) && (returnType.isPrimitive())) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }

            Object localObject2 = retVal;

            return localObject2;
        } finally {
            if ((target != null) && (!(targetSource.isStatic()))) {
                //释放gettarget方法获取的target对象,和targetsource实现有关
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

上面所说的目标对象方法的调用,是通过AopUtils的方法调用,使用反射机制来对目标对象的方法进行的;

public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args) throws Throwable {
    try {
        ReflectionUtils.makeAccessible(method);
        return method.invoke(target, args);
    } catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    } catch (IllegalArgumentException ex) {
        throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" + method
                + "] on target [" + target + "]", ex);
    } catch (IllegalAccessException ex) {
        throw new AopInvocationException("Could not access method [" + method + "]", ex);
    }
}

接下来,我们来看具体的ReflectiveMethodInvocation中proceed()方法的实现,也就是拦截器链的实现机制:

public Object proceed() throws Throwable {
        //重点!!!currentInterceptorIndex初始化值为-1,首先判断长度是否为0,为0直接调用目标对象的方法。
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers
                .get(++this.currentInterceptorIndex);

        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            //匹配逻辑,只要方法匹配就调用拦截器,不匹配,跳过这个拦截器,调用下一个。
            InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            //需要注意一点,我们这里虽然反悔了,但是匹配到的拦截器自身的invoke方法还是会调用的,
            //继续遍历拦截器链
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            //没匹配, 调用下一个拦截器,重复上面逻辑
            return proceed();
        }
        //如果是MethodInterceptor,我们调用invoke方法,主要为了兼容原始aop联盟的东西,
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }

从上面的分析我们看到了Spring AOP拦截机制的基本实现,比如Spring怎样得到Proxy,怎样利用JAVA Proxy以及反射机制对用户定义的拦截器链进行处理。

3.5织入的实现

在上面调用拦截器的时候,经过一系列的注册,适配的过程以后,拦截器在拦截的时候,会调用到预置好的一个通知适配器,设置通知拦截器,这是一系列Spring设计好为通知服务的类的一个,是最终完成通知拦截和实现的地方,例如对 MethodBeforeAdviceInterceptor的实现是这样的:

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        //这个invoke方法是拦截器的回调方法,会在代理对象的方法被调用的时候出发回调
        return mi.proceed();
    }
}

可以看到通知适配器将advice适配成Interceptor以后,会调用advice的before方法去执行横切逻辑。这样就成功的完成了before通知的织入。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容