PPXu

AOP

2019-04-22

整合总结,备忘…

What’s AOP

Aspect Oriented Programming,面向切面编程。以 OOP 来对比理解:

纵向关系 OOP,横向角度 AOP

以 日志记录 为例,在没有AOP之前,如果需要在多个方法中进行日志记录,需要在每个方法中都重复编写同一段日志操作代码,哪怕日志操作记录的方法被封装到工具类(LogUtils),仅需要一行调用即可,这样的操作还是对业务代码有侵入性,而类似日志统计、性能分析等这一类就被称为侵入性业务,使原本的业务逻辑代码跟日志操作这类代码有耦合,并且往往这类代码横跨并嵌入众多模块里边,在各个模块里分散得很厉害,到处都能见到,造成代码维护困难。

> > 从对象组织角度来讲,我们一般采用的分类方法都是使用类似生物学分类的方法,以「继承」关系为主线,我们称之为纵向,也就是OOP。设计时只使用 OOP思想可能会带来两个问题: > * 对象设计的时候一般都是纵向思维,如果这个时候考虑这些不同类对象的共性,不仅会增加设计的难度和复杂性,还会造成类的接口过多而难以维护(共性越多,意味着接口契约越多)。 > * 需要对现有的对象 动态增加 某种行为或责任时非常困难。 > 而AOP就可以很好地解决以上的问题,怎么做到的?除了这种纵向分类之外,我们从横向的角度去观察这些对象,无需再去到处调用 LogUtils 了,声明哪些地方需要打印日志,这个地方就是一个切面,AOP 会在适当的时机为你把打印语句插进切面。

AOP用处

参数校验和判空

系统之间在进行接口调用时,往往是有入参传递的,入参是接口业务逻辑实现的先决条件,有时入参的缺失或错误会导致业务逻辑的异常,大量的异常捕获无疑增加了接口实现的复杂度,也让代码显得雍肿冗长,因此提前对入参进行验证是有必要的,可以提前处理入参数据的异常,并封装好异常转化成结果对象返回给调用方,也让业务逻辑解耦变得独立。

权限控制

避免到处都是申请权限和处理权限的代码。

无痕埋点

围绕方法调用前后进行接口调度次数统计的埋掉操作。

安全控制

比如全局的登录状态流程控制。

日志记录

常用于方法进入前、执行结果后的日志记录。

性能统计

检测方法耗时其实已经有一些现成的工具。痛点是这些工具使用起来都比较麻烦,效率低下,而且无法针对某一个块代码或者某个指定的sdk进行查看方法耗时。可以采用 AOP 思想对每个方法做一个切点,在执行之后打印方法耗时。

事务处理

声明方法,为特定方法加上事务,指定情况下(比如抛出异常)回滚事务。

异常处理

替代防御性的 try-Catch。

缓存

缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。留意一些常用的缓存框架的使用方式,即可发现AOP的应用,譬如guava cache/caffeine,可以在真实的DAO方法前冠上@Cacheable便可以指定方法返回值来被缓存。

设计模式

代理模式

代理模式又分静态代理动态代理。

静态代理

代理模式上,基本上有Subject角色,RealSubject角色,Proxy角色。其中:Subject角色负责定义RealSubject和Proxy角色应该实现的接口;RealSubject角色用来真正完成业务服务功能;Proxy角色负责将自身的Request请求,调用realsubject 对应的request功能来实现业务功能,自己不真正做业务。

动态代理

通常,我们会使用代理模式来实现 AOP,这就意味着代理模式可以优雅的解决侵入性业务问题。之所以优雅,其中一个点就在于,「动态」二字。较之静态,动态就体现出更加灵活,在运行时动态地对某些东西代理,代理它去做了一些其他的事情。这种动态依赖的是反射机制。因为静态代理需要预先定义好代理类的代码实现,而当大量使用静态代理时,就可能产生大量的代理类,随着类的数量及规模增大,代码维护成本也随之增大,为了解决这个问题,就有了动态地创建代理类的想法。


在运行期的代码中生成二进制字节码

由于JVM通过字节码的二进制信息加载类的,那么,如果我们在运行期系统中,遵循Java编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类,这样,就完成了在代码中,动态创建一个类的能力了。

在运行时期可以按照Java虚拟机规范对class文件的组织规则生成对应的二进制字节码。当前有很多开源框架可以完成这些功能,如ASM,Javassist。

Proxy角色在执行代理业务的时候,无非是在调用真正业务之前或者之后做一些“额外”业务。

上图可以看出,代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。换一种思路就是:在触发(invoke)真实角色的方法之前或者之后做一些额外的业务。那么,为了构造出具有通用性和简单性的代理类,可以将所有的触发真实角色动作交给一个触发的管理器,让这个管理器统一地管理触发。这种管理器就是Invocation Handler。

动态代理模式的结构跟上面的静态代理模式有所不同的地方,就在于多引入了一个InvocationHandler角色。

在静态代理中,代理Proxy中的方法,都指定了调用了特定的realSubject中的对应的方法:

在上面的静态代理模式下,Proxy所做的事情,无非是调用在不同的request时,调用触发realSubject对应的方法;更抽象点看,Proxy所作的事情;在Java中 方法(Method)也是作为一个对象来看待了,

动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:

在这种模式之中:代理Proxy 和RealSubject 应该实现相同的功能,这一点相当重要。(这里说的功能,可以理解为某个类的public方法)

在面向对象的编程之中,如果想要约定Proxy 和RealSubject可以实现相同的功能,有两种方式:
a. 一个比较直观的方式,就是定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。
b. 还有比较隐晦的方式,就是通过继承。因为如果Proxy继承自RealSubject,这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。

其中JDK中提供的创建动态代理的机制,是以a这种思路设计的(基于接口),而cglib 则是以b思路设计的(基于类继承)。

资源出自动态代理的神总结文章:Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

装饰者模式

装饰者模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰者来包裹真实的对象。
装饰者模式的实现上,与静态代理极为相似,都是透过增强被代理者的功能来做到扩展对象。关键的不同点在于:代理强调的是为其他对象提供一种代理以控制对这个对象的访问,而装饰者仅仅强调扩展,并不强调访问控制。而动态代理比装饰者模式更加灵活,被增强的对象和增强的内容都是可以更换的,动态化的。

AOP术语

AOP具体的通知包含

  • @Before,前置通知,执行方法前执行
  • @AfterReturn,返回通知,正常返回方法后执行
  • @After,后置通知,方法最终结束后执行,相当于finaly
  • @Around,环绕通知,围绕整个方法
  • @AfterThrowing,异常通知,抛出异常后执行
    开发者在命中连接点时,可以通过以上不同的通知,执行对应方法。这就是AOP中的Advisor。

一图胜千言:

辅助记忆各种通知类型:

具体术语包括

  • Aspect,切面,一个关注点的模块。
    例子中,LogAspect就是切面。
  • JoinPoint, 连接点,程序执行中的某个点,某个位置。
    例子中,testBean.getName()是连接点。
  • PointCut,切点,切面匹配连接点的点,一般与切点表达式相关,就是切面如何切点。
    例子中,@PointCut注解就是切点表达式,匹配对应的连接点
  • Advice,通知,指在切面的某个特定的连接点上执行的动作,也叫增强行为。
    例子中,before()与after()方法中的代码。
  • TargetObject,目标对象,指被切入的对象,也就是被代理的对象。
    例子中,从ctx中取出的testBean则是目标对象。
  • Weave,织入,将Advice作用在JoinPoint的过程。

一图胜千言(图片源自网络):

Spring AOP过程

1、Spring加载自动代理器AnnotationAwareAspectJAutoProxyCreator,当作一个系统组件。

2、当一个bean加载到Spring中时,会触发自动代理器中的bean后置处理

3、bean后置处理,会先扫描bean中所有的Advisor

4、然后用这些Adviosr和其他参数构建ProxyFactory

5、ProxyFactory会根据配置和目标对象的类型寻找代理的方式(JDK动态代理或CGLIG代理)

6、然后代理出来的对象放回context中,完成Spring AOP代理配置,等待被代理类的被调用

7、响应被代理类被调度,设置拦截器回调(JDK Proxy透过InvocationHandler、CGLIB透过DynamicAdvisedInterceptor完成回调)。

源码分析

以AspectJ实现为例:

  • Maven POM依赖配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.3.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.3.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.3.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.3.3.RELEASE</version>
    </dependency>
  • 定义一个待代理的业务Bean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class TestBean {

    private String name;

    public String getName() {
    return name;
    }

    public void setName( String name ) {
    this.name = name;
    }

    }
  • 定义一个切面Bean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    @Aspect
    public class LogAspect {

    @Pointcut( "execution(* com.xu.test.aop.TestBean.getName())" )
    public void getName() {

    }

    // 指向上面的getName()切点,透过getName()的PointCut注解指向真实被代理的方法,也就是execution里的表达式所指向的方法
    @Before( "getName()" )
    public void before( JoinPoint jp ) {
    String clazzName = jp.getTarget().getClass().getName();
    String methodName = jp.getSignature().getName();
    System.out.println( "before " + clazzName + "." + methodName + " executing" );
    }

    @After( "getName()" )
    public void after( JoinPoint jp ) {
    String clazzName = jp.getTarget().getClass().getName();
    String methodName = jp.getSignature().getName();
    System.out.println( "after " + clazzName + "." + methodName + " executing" );
    }

    // org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
    // @AfterThrowing( throwing = "ex", pointcut = "getName()" )
    // public void afterThrowing( JoinPoint jp, Throwable ex ) {
    // String clazzName = jp.getTarget().getClass().getName();
    // String methodName = jp.getSignature().getName();
    // System.out.println( "throw exception when " + clazzName + "." + methodName + " executing" );
    // }

    // org.springframework.aop.aspectj.AspectJAroundAdvice
    // @Around( value = "getName()" )
    // // 除了around通知,其余类型的通知都不能用ProceedingJoinPoint,只能用普通的JoinPoint
    // public void around( ProceedingJoinPoint pjp ) throws Throwable {
    // String clazzName = pjp.getTarget().getClass().getName();
    // String methodName = pjp.getSignature().getName();
    // System.out.println( "before " + clazzName + "." + methodName + " executing" );
    // Object result = pjp.proceed();
    // System.out.println( "after " + clazzName + "." + methodName + " executing, result = " + result );
    // }
    }
  • 配置业务Bean与切面Bean(aop-test.xml)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:aspectj-autoproxy/>

    <bean id="testBean" class="com.xu.test.aop.TestBean"/>
    <bean class="com.xu.test.aop.LogAspect"/>
    </beans>
  • 测试代理类执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class TestAOP {

    public static void main( String[] args ) {

    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("aop-test.xml");

    TestBean tb = ctx.getBean( "testBean", TestBean.class );

    tb.setName( "xxx" );

    String name = tb.getName();

    System.out.println( name );
    }
    }
  • 运行结果

    before com.xu.test.aop.TestBean.getName executing
    after com.xu.test.aop.TestBean.getName executing
    xxx

上面的例子之所以能完成AOP的代理,只因为Spring的xml配置里面加了这一句

< aop : aspectj-autoproxy / >

加上了这一个配置,使得整个Spring项目拥有了AOP的功能。全局搜索下aspectj-autoproxy这个字段,可以发现,是这个类AspectJAutoProxyBeanDefinitionParser解析了这个元素。

其中的parse方法调用的是AopNamespaceUtils类中的registerAspectJAnnotationAutoProxyCreatorIfNecessary。这个方法作用是初始化一个AOP专用的Bean,并且注册到Spring容器中。

1
2
3
4
5
6
7
8
9
10
11
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}

...
}
1
2
3
4
5
6
7
8
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {

BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}

解析这三个操作,

1、第一句,注册一个AnnotationAwareAspectJAutoProxyCreator(称它为自动代理器),这个Creator是AOP的操作核心,也是扫描Bean,代理Bean的操作所在。

2、第二句,解析配置元素,决定代理的模式。其中有JDK动态代理,还有CGLIB代理。

3、第三句,作为系统组件,把Creator这个Bean,放到Spring容器中。让Spring实例化,启动这个Creator。

自动代理器

自动代理器 AnnotationAwareAspectJAutoProxyCreator 继承自 AbstractAutoProxyCreator,AbstractAutoProxyCreator里边实现了BeanPostProceesor接口的postProcessAfterInitialization方法,这个方法是在一个Bean被加载并注册到Spring IOC容器后,由BeanFactory回调执行的,也就是说,切面是在目标对象被实例化的时候织入目标对象的,更准确的说,切面是在目标对象的bean在完成初始实例化之后,由bean工厂回调bean的后置处理器将切面织入到目标对象中的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

里边的wrapIfNecessary方法会生成一个新的代理对象,返回context(容器上下文)中加载。

AOP最核心的逻辑就在这个 wrapIfNecessary方法里边,里边主要是获取通知(advice/advisor)放到一个名为specificInterceptors的数组里,然后作为参数去调用createProxy方法,创建对应的代理对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

// Create proxy if we have advice.
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(获取通知),还有createProxy(创建代理)

获取通知

getAdvicesAndAdvisorsForBean,顾名思义,就是获取被代理的Bean所关联的advice及advisor,自然地,这里有个疑问,advice与advisor是什么关系,为什么获取通知不是只需要获取advice即可?Spring使用org.springframework.aop.Advisor接口表示切面的概念,当完成对目标对象方法的增强行为操作(也就是通知,Advice)和切入点(Point)的设计开发之后,需要一个对象将目标对象、增强行为和切入点三者结合起来,而Advisor(通知器)就是一个实现这个功能的对象,即通过Advisor通知器,可以定义哪些目标对象的哪些方法在什么地方使用这些增强的行为。简单来讲,Advisor=Advice+Point。

1
2
3
4
5
6
7
public interface Advisor {    
//获取切面的通知Advice
Advice getAdvice();

//判断这个通知是否和某个特定的实例对象相关
boolean isPerInstance();
}

事实上,debug进去AbstractAdvisorAutoProxyCreator对getAdvicesAndAdvisorsForBean的实现代码,可以看到,其实这里确实只是获取Advisor通知器而已,如上文说的,每个Advisor对象持有一个Advice通知,一步步debug,进入到AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors(),找到BeanFactoryAspectJAdvisorsBuilder中的buildAspectJAdvisors方法,这个方法里边就是寻找AspectBean,然后返回AspectBean中的所有Advisor的过程实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* Look for AspectJ-annotated aspect beans in the current bean factory,
* and return to a list of Spring AOP Advisors representing them.
* <p>Creates a Spring Advisor for each AspectJ advice method.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;

synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<Advisor>();
aspectNames = new LinkedList<String>();
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this
// case they would be cached by the Spring container but would not
// have been weaved
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}

if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}

譬如,例子中的LogAspect就是AspectBean,它定义了Before与After两个通知(或者说增强行为),那么最终便返回LogAspect中的Before与After对应的Advisor通知器。

创建代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* Create an AOP proxy for the given bean.
* @param beanClass the class of the bean
* @param beanName the name of the bean
* @param specificInterceptors the set of interceptors that is
* specific to this bean (may be empty, but not null)
* @param targetSource the TargetSource for the proxy,
* already pre-configured to access the bean
* @return the AOP proxy for the bean
* @see #buildAdvisors
*/
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
     
/**
*这里是与后面的代理拦截器链调用的逻辑有关
* ProxyFactory本质上就是一个ProxyConfig,它透过多层继承,最终继承到了ProxyConfig
* 继承链路是:ProxyFactory -> ProxyCreatorSupport -> AdvisedSupport -> ProxyConfig
* 所以这里new一个ProxyFactory其实就是创建一个ProxyConfig对象,
* 这个ProxyConfig对象将会存储与目标对象相匹配的advisor,这就是所谓的织入。
* 等到调用目标对象的时候再将advisor取出来包装(转换)成拦截器。最后组成拦截器链。
*/
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {

          proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

/** 这里是指定拦截器(specificInterceptors)包装成advisor。
* 这个specificInterceptors是作为这个方法的参数传进来的。
* 也就是说在这之前就已经将与目标对象相匹配的拦截器构建好了。
*/
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
//这里就是织入,将与目标对象相匹配的advisor存储到ProxyConfig对象中。
proxyFactory.addAdvisor(advisor);
}

proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}

return proxyFactory.getProxy(getProxyClassLoader());
}

把Advisor丢到proxyFactory(ProxyConfig)之后,最后要从proxyFactory里获取一个代理对象。也就是,ProxyFactory的getProxy方法,一路debug进去方法,最终可以在DefaultAopProxyFactory中createAopProxy的实现中,看到代理对象是怎么被生成的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

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

...
}

ProxyFactory会根据配置与目标对象的类型,选择用JDK动态代理,还是CGLIB的代理,代理后的对象会放回context中,然后等到程序执行时,会直接调用这个代理类。
留意到这里的JDK动态代理 or CGLIB动态代理的选择逻辑,体现到了目标为接口时使用JDK动态代理,目标为类时使用CGLIB动态代理的意思。


至此,整个代理的织入、连接过程就已完成。接下来的问题是,调用时怎么给目标类(被代理类)作访问拦截的。

AOP代理拦截

上面的代码分析中,已经知道,在Spring AOP通过JDK的Proxy方式或者CGLIB方式生成代理对象的时候,相关的拦截器已经生成并配置到代理对象中去了。
那么,拦截器的回调,是怎么设置的呢?
有两种方式:

  • JDK的Proxy方式生成代理对象:JdkDynamicAopProxy会通过连接点(ReflectiveMethodInvocation)来调用拦截器链中的拦截器(也就是调用通知方法)
  • CGLIB方式生成代理对象:根据CGLIB使用要求,通过DynamicAdvisedInterceptor来完成回调。

在《Spring技术内幕(第2版)》中有截取到两种方式的拦截器在Spring代码中的实现。

JdkDynamicAopProxy的invoke拦截

具体实现看org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[])源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
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 {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}

Object retVal;

if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}

// 得到目标对象的地方
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}

// 这里获取到定义好的拦截器链
// 这里的this.advised是个AdvisedSupport类型(继承ProxyConfig),就是一个ProxyConfig
// 进去getInterceptorsAndDynamicInterceptionAdvice方法,便会发现上文提到的Advisor转换成拦截器的过程
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// 如果没有设定拦截器(拦截器链为空),那么就直接调用target的对应方法
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 如果有拦截器的设定,那么需要调用拦截器之后才调用目标对象的相应方法
// 通过构造一个ReflectiveMethodInvocation对象来实现
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 沿着拦截器链继续前进(执行)
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);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
* for the given method, based on this configuration.
* @param method the proxied method
* @param targetClass the target class
* @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
// 这里作了缓存,把已有的interceptor链存起来,除了首次要生成,后续获取都是走的缓存,节省时间
// 这个interceptor链的生成是由advisorChainFactory完成的,在这里使用的是DefaultAdvisorChainFactory
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {

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

// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
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;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
// Advisor入参,获取到已经注册好的方法拦截器
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
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;
}

...
}

Cglib2AopProxy的intercept拦截

具体实现看org.springframework.aop.framework.Cglib2AopProxy.DynamicAdvisedInterceptor.intercept(Object, Method, Object[], MethodProxy)源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* General purpose AOP callback. Used when the target is dynamic or when the
* proxy is not frozen.
*/
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

private AdvisedSupport advised;

public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}

target = getTarget();
if (target != null) {
targetClass = target.getClass();
}

// 从advised(AdvisedSupport类型,继承自ProxyConfig)中取得配置好的通知
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;

// 如果没有配置的AOP通知,那么就直接调用target的对应方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
retVal = methodProxy.invoke(target, args);
}
else {
// 通过CglibMethodInvocation来启动advice通知
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}

...
}

AOP拦截器的调用

两种方式对拦截器的调用都是在ReflectiveMethodInvocation中通过proceed方法实现。在proceed方法中逐个实现拦截器的拦截方法。每个拦截器在执行之前,需要对代理方法完成一个匹配判断(即Pointcut切点中需要进行matches匹配过程)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
public Object proceed() throws Throwable {
// 从索引为-1的拦截器开始调用,并按序递增
// 如果拦截器链中的拦截器迭代调用完毕,这里开始调用target的函数,这个函数时通过反射机制完成的
// 这个函数具体实现在AopUtils.invokeJoinpointUsingReflection方法中
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}

Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 这里对拦截器进行动态匹配的判断
//这里是触发进行匹配的地方,如果和定义好的PointCut匹配,那么这个advice将会得到执行
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}

至此,整个AOP实现的来龙去脉,便走完了个大致流程。

JVM级别的AOP

基于Java Instrument的Agent实现

Reference

扫描二维码,分享此文章