文档章节

Spring源码(二)---AOP

robin-yao
 robin-yao
发布于 2016/03/29 21:27
字数 2366
阅读 381
收藏 11

AOP 基本概念


    PointCut

        切入点简单的理解就是定义了我们要切入的位置(在某些类 某些方法上切入)。因此Spring Aop的PointCut实现应该满足过滤得到目标类目标方法的需求。

        从PointCut接口定义我们可以看到ClassFilter和MethodMatcher,ClassFilter判断那些类是要织入增强的,MethodMatcher是判断哪些方法是满足织入增强的。

public interface Pointcut {
   ClassFilter getClassFilter();
   MethodMatcher getMethodMatcher();
   Pointcut TRUE = TruePointcut.INSTANCE;
}
public interface ClassFilter {

   /**
    * 判断给定的clazz是否满足织入条件,满足则返回true
    */
   boolean matches(Class<?> clazz);
    /**
    * Canonical instance of a ClassFilter that matches all classes.
    * 这个默认的Filter是说所有的类都满足增强条件,都会返回true
    */
   ClassFilter TRUE = TrueClassFilter.INSTANCE;

}
public interface MethodMatcher {

   /**
    * Perform static checking whether the given method matches. 
      仅仅对方法的静态匹配,不会检验方法具体参数值。
    */
   boolean matches(Method method, Class<?> targetClass);

   /**
    *是否是进行运行时方法匹配
    */
   boolean isRuntime();
    /**
    运行时 方法和方法参数同时都要进行匹配,满足则返回true
    */
   boolean matches(Method method, Class<?> targetClass, Object... args);
    /**
    * Canonical instance that matches all methods.
    * 默认所有的方法都匹配
    */
   MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

        默认的pointcut实现有:NameMatchMethodPointcut,JdkRegexpMethodPointcut,AspectJExpressionPointcut等。这里对NameMatchMethodPointcut与AspectJExpressionPointcut做下简单介绍。

        NameMatchMethodPointcut:仅仅通过对方法名字进行匹配织入位置。

      其中从其父类StaticMethodMatcherPointcut源码可以看到如下代码,说明该pointcut会默认所有的类都满足要求,即不对类做过滤。

private ClassFilter classFilter = ClassFilter.TRUE;
public void setClassFilter(ClassFilter classFilter) {
   this.classFilter = classFilter;
}
@Override
public ClassFilter getClassFilter() {
   return this.classFilter;
}

        AspectJExpressionPointcut:本身实现了MethodMatcher和Pointcut接口。该pointcut主要特点是支持AspectJ Expression。用户可以使用该语法定义poincut ,满足一些通配需求。

    

    Advice

        Advice就是在上面我们通过Pointcut定义好的位置织入的具体逻辑。Advice翻译过来叫做通知或者增强,这里我们统一叫做增强。增强 按照与业务代码执行顺序的先后位置主要分为三类:

        BeforeAdvice:在业务代码执行之前执行我们织入的增强代码。这里BeforeAdvice接口去源码中看其实没有定义任何方法,是个空接口,我们在写具体的方法增强时是用的其子接口MethodBeforeAdvice。

        AopAroundAdvice:在业务代码执行的前后织入增强代码。这种环绕增强spring借助aop alliance的MethodInterceptor实现。我们一般直接实现MethodInterceptor接口,实现我们增强代码即可。

        AfterAdvice:在业务代码执行之后织入增强代码。和BeforeAdvice一样,它也是一个空接口。它具体又分为AfterReturningAdvice,ThrowsAdvice。一般我们实现直接实现AfterReturningAdvice接口即可。


    Aspect/Advisor

        spring 中的Aspect意思代表了一组poincut和advice,我们经常利用aspect注解定义一个类,然后通过

<aop:aspectj-autoproxy/>自动去发现该aspect。advisor是指一个pointcut和对应的advice。从范围上讲aspect包含了advisor。

        这里介绍下默认的advisor  DefaultPointcutAdvisor。

        从类图接口继承上看 该默认的Advisor继承了Advisor 和PoincutAdvisor。Advisor是为了组装advice(增强),PointcutAdvisor在Advisor 接口基础上添加了Pointcut,定义了getPointcut接口。DefaultPointcutAdvisor可以看出一个对advisor和pointcut实现了getter/setter接口的类。


ProxyFactory/ProxyFactoryBean/AutoProxyCreator

    AopProxy 

        这里叫做AopProxy不是很好,因为AopProxy是产生proxy的工具类,后面没有加Factory,也许Spring是为了避免与ProxyFactory冲突。AopProxy定义了getProxy方法。具体实现类有JdkDynamicAopProxy,ObjenesisCglibAopProxy(CglibAopProxy的扩展类)

    这里在介绍AopProxy产生proxy的原理之前先提下AdvisedSupport。AdvisedSupport类图如下,它是AOP proxy配置管理的基类。

        它里面维护了AOP proxy代理生产所需的配置,比如Advisors,TargetSource,AdvisorChainFactory。它的子类ProxyCreatorSupport实现了对AopProxy(JdkDynamicAopProxy,ObjenesisCglibAopProxy)具体类里的注入,实现代理创建功能。ProxyCreatorSupport是我们下面要介绍的ProxyFactory与ProxyFactoryBean的父类。

    JdkDynamicAopProxy:这里我们介绍下Jdk动态生产代理的原理。JdkDynamicAopProxy实现了JDK InvocationHandler接口。我们先看下invoke方法具体代码

//invocationHandler接口实现
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   MethodInvocation invocation;
   Object oldProxy = null;
   boolean setProxyContext = false;
   //获取被代理的目标类实例
   TargetSource targetSource = this.advised.targetSource;
   Class<?> targetClass = null;
   Object target = null;

   try {
      //对Object上的基本方法不处理。
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
         // The target does not implement the equals(Object) method itself.
         return equals(args[0]);
      }
      if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
         // The target does not implement the hashCode() method itself.
         return hashCode();
      }
      if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         // Service invocations on ProxyConfig with the proxy config...
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;
      //判断是否把代理放到利用ThreadLocal实现的AOP上下文中
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }

      // 目标实例及目标类
      target = targetSource.getTarget();
      if (target != null) {
         targetClass = target.getClass();
      }
      // 得到我们注册的advice链,这一步很关键,它会根据我们的注册的pointcut来进行对目标类目标方法进行过滤,
      // 判断方法是否满足定义的pointcut。下段代码我们具体分析
      //如果chain为空说明不需要任何增强,
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      if (chain.isEmpty()) {
         //没有任何advice 直接对方法进行调用
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // We need to create a method invocation...
         //其中包含了代理类 目标对象 advice链 待调用方法。
         invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // Proceed to the joinpoint through the interceptor chain.
         //对advice进行调用。
         retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      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);
      }
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}
//同Proxy生成代理
@Override
public Object getProxy(ClassLoader classLoader) {
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
//类DefaultAdvisorChainFactory

@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
      Advised config, Method method, Class<?> targetClass) {

   List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
   Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
   boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
   AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

   for (Advisor advisor : config.getAdvisors()) {
      if (advisor instanceof PointcutAdvisor) {
         // Add it conditionally.
         PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
         //先根据ClassFilter来判断目标类是不是满足Pointcut要求
         if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
            //取得方法拦截器
            MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
            //获得pointcut上的MethodMatcher
            MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
            //通过MethodMatcher来判断方法是否符合poincut定义的需求。
            if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
               //如果是运行时MethodMatcher则需要对方法参数值进行匹配。
               if (mm.isRuntime()) {
                  // Creating a new object instance in the getInterceptors() method
                  // isn't a problem as we normally cache created chains.
                  for (MethodInterceptor interceptor : interceptors) {
                     interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                  }
               }
               else {
                  interceptorList.addAll(Arrays.asList(interceptors));
               }
            }
         }
      }
      else if (advisor instanceof IntroductionAdvisor) {
         IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
         if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
            Interceptor[] interceptors = registry.getInterceptors(advisor);
            interceptorList.addAll(Arrays.asList(interceptors));
         }
      }
      else {
         Interceptor[] interceptors = registry.getInterceptors(advisor);
         interceptorList.addAll(Arrays.asList(interceptors));
      }
   }

   return interceptorList;
}

    

因为JdkDynamicAopProxy采用JDK动态代理原理 它只能对实现相应接口的类进行代理,不能对没有接口的实现类进行代理。

    ObjenesisCglibAopProxy:CglibAOpProxy采用Cglib库通过Enhancer来对类字节码修改,实现AOP增强。CligbAopProxy可以对没有接口类进行代理,但不能对final类或方法继续代理。

    ProxyFactory

        AOP proxies 代理工厂,主要是为了编程( programmatic use)方式获取proxy,而不是通过一个Bean Factory去获取。ProxyFactory为aop proxy提供了简单的配置和获取方法。

         下面我看一个简单的例子

//定义一个advice
public class AopMethodBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("before "+method.getName()+"-------");
    }
}
// action
public interface HelloAction {

    public String sayHello(String name);

    public String sayHelloA(String name);
}
//impl
public class HelloActionImpl implements HelloAction {

    @Override
    public String sayHello(String name) {
        System.out.println("hello "+name);
        return "hello "+name;
    }

    @Override
    public String sayHelloA(String name) {
        System.out.println("helloA "+name);
        return "helloA "+name;
    }
}
// ProxyFactoryTest
public class ProxyFactoryTest {


    @Test
    public void testGetProxy(){

        ProxyFactory proxyFactory=new ProxyFactory();

        //pointcut 匹配sayHello方法 
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();

        pointcut.setMappedNames(new String[] {"sayHello"});

        //define advisor (pointcut + advice)
        DefaultPointcutAdvisor beforeAdvisor=new DefaultPointcutAdvisor(pointcut,new AopMethodBeforeAdvice());
    
        HelloAction helloActionTarget=new HelloActionImpl();
        //这样就可以采用JDK proxy生成方法
        proxyFactory.setInterfaces(HelloAction.class);
        proxyFactory.setTarget(helloActionTarget);

        proxyFactory.addAdvisor(beforeAdvisor);

        HelloAction proxy=(HelloAction)proxyFactory.getProxy();

        proxy.sayHello("yao");
        proxy.sayHelloA("yaoA");

    }

}

        我们debug进去看看一个aop 代理是如何运行的

        首先构造父类ProxyCreatorSupport,前面我们介绍过,主要是增加属性AopProxyFactory,生成一个AopPorxy(用来生产代理)

    DefaultAopProxyFactory主要是根据有无接口来生产JDK or Cglib AopProxy

    然后通过生成的AopProxy getProxy即获取到代理对象。代理对象织入了advisor,具体执行就调用了我们上面分析的JDK invoke 方法。(这里假设采用JDK动态代理)

    ProxyFactoryBean

     ProxyFactoryBean和ProxyFactory功能一样,只不过ProxyFactoryBean实现了FactoryBean,与Spring IOC容器结合起来了。下面是其类图,它并实现了BeanFactoryAware感知接口,实现BeanFactory的自动注入。与ProxyFactory一样都是ProxyCreatorSupport子类。

    我们重点看下其getObject接口,

先判断是否是产生单例代理还是原型代理

产生单例代理就存下来,下次直接利用。具体过程都是先判处产生 createAopProxy(JDK or Cglib)然后getProxy即可。

原型的是每次连ProxyCreatorSupport都是新生成的,然后在createAopProxy,最后getProxy即可。

getProxy

    总之 ProxyFactoryBean的主要目的就是把Aop 嵌入到IOC 容器中。


    AutoProxyCreator

    上面我们讲了这么多都是针对一个目标一个目标配置的,如果目标很多怎么办呢?这里Spring IOC容器通过BeanPostProcessor 来实现Aop 代理自动创建,BeanPostProcessor。常见的自动代理创建有BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator。我们看看默认的DefaultAdvisorAutoProxyCreator的类图

AbstractAutoProxyCreator实现了BeanPostProcessor的postProcessBeforeInstantiation

AbstractAutoProxyCreator的createProxy方法。采用ProxyFactory实现。

    这里测试用例就不写了,文章还有很多介绍不全,慢慢修正补充。

本文链接:http://my.oschina.net/robinyao/blog/649518

© 著作权归作者所有

robin-yao
粉丝 166
博文 54
码字总数 61436
作品 0
杭州
私信 提问
Spring 源码解读 推荐流程

Spring源代码解析(一):IOC容器:http://www.javaeye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.javaeye.com/topic/86594 Spring源代码解析(三):Sprin...

2k10
2015/04/02
408
0
Spring系列教程六:AOP详细讲解

AOP 概述 什么是 AOP AOP:全称是 Aspect Oriented Programming 即:面向切面编程。 AOP技术是对OOP技术的一种延伸,AOP是面向纵向,OOP是面向横向。简单的说它就是把我们程序重复的代码抽取...

我叫小糖主
05/19
38
0
深入聊一聊 Spring AOP 实现机制!

作者 | 张书康 责编 | 郭 芮 AOP(Aspect-Oriented Programming,即面向切面编程。Spring Aop 在 Spring框架中的地位举足轻重,主要用于实现事务、缓存、安全等功能。本篇主要是对源码进行深...

CSDN资讯
01/26
0
0
那些年,我们一起追的Spring

学无止境,但仍需及时总结。 自去年开始写作以来,写了一些关于Spring的文章,今天将它们汇总起来,一方面方便大家阅读,另一方面,也是一次小的复盘总结。 IOC 首先是Spring的IOC,也就是控...

SexyCode
2018/08/14
0
0
Spring AOP 创建代理的源码解析

相关文章 Spring AOP 注解方式源码解析 Spring AOP 功能使用详解 Spring 的 getBean 方法源码解析 Spring bean 创建过程源码解 Spring 中 bean 注册的源码解析 前言 在上篇文章 Spring AOP 注...

TSMYK
01/01
135
0

没有更多内容

加载失败,请刷新页面

加载更多

DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
10分钟前
0
0
数据库中间件MyCat

什么是MyCat? 查看官网的介绍是这样说的 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务、ACID、可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵...

沉浮_
今天
4
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
今天
6
0
常用物流快递单号查询接口种类及对接方法

目前快递查询接口有两种方式可以对接,一是和顺丰、圆通、中通、天天、韵达、德邦这些快递公司一一对接接口,二是和快递鸟这样第三方集成接口一次性对接多家常用快递。第一种耗费时间长,但是...

程序的小猿
今天
4
0
Python机器学习之数据探索可视化库yellowbrick

背景介绍 从学sklearn时,除了算法的坎要过,还得学习matplotlib可视化,对我的实践应用而言,可视化更重要一些,然而matplotlib的易用性和美观性确实不敢恭维。陆续使用过plotly、seaborn,...

yeayee
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部