from:https://my.oschina.net/kaywu123/blog/632486
Spring AOP 架构
先是生成代理对象,然后是拦截器的作用,最后是编织的具体实现。这是AOP实现的三个步骤,当然Spring AOP也是一样。
而从Spring AOP整体架构上看,其核心都是建立在代理上的。当我们建立增强实例时,我们必须先使用 ProxyFactory 类加入我们需要织入该类的所有增强,然后为该类创建代理。一般而言,AOP实现代理的方法有三种,而 Spring 内部用到了其中的两种方法:动态代理和静态代理,而动态代理又分为JDK动态代理和CGLIB代理。而前面提到的 ProxyFactory 类控制着 Spring AOP 中的织入和创建代理的过程。在真正创建代理之前,我们必须指定被增强对象或者目标对象。我们是通过 setTarget() 方法来完成这个步骤的。而 ProxyFactory 内部将生成代理的过程全部转给 DefaultAopProxyFactory 对象来完成,然后根据设置转给其他的类来完成。下面是 Spring AOP 生成代理的原理图:
而特别需要注意的是,在 Spring AOP 中,一个 Advisor(通知者,也翻作 通知器或者增强器)就是一个切面,他的主要作用是整合切面增强设计(Advice)和切入点设计(Pointcut)。Advisor有两个子接口:IntroductionAdvisor 和 PointcutAdvisor,基本所有切入点控制的 Advisor 都是由 PointcutAdvisor 实现的。而下面的是Spring AOP的整体架构图
乍一看,非常的复杂,但如果结合上面的Spring AOP生成代理的原理图一起看,也就那么回事,只是丰富的许多属性了,我们会慢慢介绍的。
Spring AOP 使用范例
项目结构
分析 Spring AOP 源码的话,直接从最简单业务代码入手,一步步的对源码进行解析。希望借此能看清楚 Spring AOP 纵向和横向代码之间的联系,而且思维也不会太跳跃。下面就是 Spring AOP 测试程序 Test 项目的结构图:
注:Spring 源码版本是 Spring-4.1.6,我的源码分析也是基于这一版本的,JDK 版本是 1.8,aspect 是用的 aspectjrt-1.7.4 和 aspectjweaver-1.7.4。
创建用于拦截的Bean
在实际项目中,Bean应该可以算是核心逻辑了,其中的 printTest 方法中可能会封装整个的核心业务逻辑,如此重要的地方我们想在printTest方法前后加入日志来进行跟踪或者调试也是很自然地想法,但是如果直接修改源码是不符合面向对象的设计原则的,它并不符合面向对象的设计方式,而且随意的改动原有的代码也会造成一定的风险,这里就体现 AOP 的优越性了。
package test;
/**
* 测试Bean
*
* @Auther kay
* @Date 2016-03-08
*/
public class TestBean {
private String testStr = "testStr";
public String getTestStr(){
return testStr;
}
public void setTestStr(String testStr){
this.testStr = testStr;
}
public void printTest(){
System.out.println("test Bean");
}
}
创建Advisor
Spring AOP 实现的核心,这里采用了@AspectJ注释对POJO进行标注,使AOP的工作大大简化。
package test;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
/**
* Aspect切面
*
* @Auther kay
* @Date 2016-03-08
*/
@Aspect
public class AspectTest {
/**
* 配置切入点,主要为方便同类中其他方法使用此处配置的切入点
*/
private final String POINT_CUT = "execution(* test.TestBean.*(..))";
/**
* 配置前置通知,使用在方法aspect()上注册的切入点
* 同时接受JoinPoint切入点对象,可以没有该参数
*/
@Before(POINT_CUT)
public void beforeTest(){
System.out.println("before Test");
}
/**
* 配置后置通知,使用在方法aspect()上注册的切入点
*/
@After(POINT_CUT)
public void afterTest(){
System.out.println("after Test");
}
/**
* 配置环绕通知,使用在方法aspect()上注册的切入点
*
* @param point JoinPoint的支持接口
* @return Object
*/
@Around(POINT_CUT)
public Object arountTest(ProceedingJoinPoint point){
System.out.println("before1");
Object object = null;
try{
object = point.proceed();
}
catch (Throwable e){
e.printStackTrace();
}
System.out.println("after1");
return object;
}
}
配置文件
XML是Spring的基础,尽管Spring一再简化配置,并且大有使用注解取代XML配置之势,但无论如何XML还是Spring的基石。
<?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-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy />
<!-- 业务逻辑切面配置 -->
<bean id="test" class = "test.TestBean" />
<bean class="test.AspectTest" />
</beans>
测试
测试程序,验证效果。
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试程序
*
* @Auther kay
* @Date 2016-03-08
*/
public class Test {
public static void main(String[] arge){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
TestBean bean = context.getBean("test", TestBean.class);
bean.printTest();
}
}
打印结果
控制台打印如下代码:
Spring AOP是如何实现AOP的?首先我们知道,Spring是否支持注释的AOP是由一个配置文件来控制的,也就是<aop:aspectj-autoproxy />,下面我们分析就从这句配置开始。
Spring AOP 的入口
BeanDefinition 的解析
首先spring-config.xml配置文件里的<aop:aspectj-autoproxy>进行解析,发现其如果不是bean标签,则会用不同的类来解析。解析AOP的是:http\://www.springframework.org/schema/aop=org.springframeworl.aop.config.AopNamespaceHandler。也就是 AopNamespaceHandler 类来解析,我们对 AopNamespaceHandler 类进行分析
发现 AopNamespaceHandler 的一段代码是 <aop:aspectj-autoproxy> 解析的入口
AopNamespaceHandler.java
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
BeanDefinition 的载入
由此,我们知道只要配置文件中出现“aspectj-autoproxy”的注解时就会使用解析器对 AspectJAutoProxyBeanDefinitionParser 进行解析
所有解析器,都是由 BeanDefinitionParser 接口的统一实现,入口都是从 parse函数开始的,AspectJAutoProxyBeanDefinitionParser 的 parse 函数如下:
AspectJAutoProxyBeanDefinitionParser .java
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册 AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 对于注释中子类进行处理
extendBeanDefinition(element, parserContext);
return null;
}
其中的 registerAspectJAnnotationAutoProxyCreatorIfNecessary 函数是 AnnotationAwareAspectJAutoProxyCreator 注册的地方,也是注册的主要逻辑实现的地方。
AopNamespaceUtils.java
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 注册或升级 AutoProxyCreator 定义 beanName 为 org.Springframework.aop.config.internalAutoProxyCreator的
// BeanDefinition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 对于 proxy-target-class 以及 expose-proxy 属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 注册组件并通知,便于监听器作进一步处理
// 其中 beanDefinition 的 className 为 AnnotationAwareAspectJAutoProxyCreator
registerComponentIfNecessary(beanDefinition, parserContext);
}
BeanDefinition 的注册
在对于AOP实现上,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成的,它可以根据@Point 注解定义的切点来自动代理相匹配的 bean。但是为了配置简便,Spring 使用了自定义配置来帮我们自动注册 AnnotationAwareAspectJAutoProxyCreator。具体实现如下
AopNamespaceUtils.java
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
AopConfigUtils.java
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 如果已经存在自动代理创建器且存在的自动代理创建器与现在的不一致那么需要根据优先级来判断到底需要任何使用
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// AUTO_PROXY_CREATOR_BEAN_NAME="org.springframework.aop.config.internalAutoProxyCreator"
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
// 改变bean 最重要的就是改变bean 所对应的 className 属性
apcDefinition.setBeanClassName(cls.getName());
}
}
// 如果已经存在自动代理创建器并且与将要创建的一致,那么无需再此创建
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// AUTO_PROXY_CREATOR_BEAN_NAME="org.springframework.aop.config.internalAutoProxyCreator"
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
以上代码中实现了自动注册 AnnotationAwareAspectJAutoProxyCreator 类的功能,同时这里还涉及了一个优先级的问题,如果已经存在了自动代理创建器,而且存在的自动代理创建器与现在的不一致,那么就需要根据优先级来判断到底需要使用哪一个?
属性处理
一般而言,Spring AOP 内部使用 JDK 动态代理或者 CGLIB 来为目标对象创建代理。如果被代理的目标对象实现了至少一个接口,则会使用 JDK 动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个 CGLIB 代理。一般情况下,使用 CGLIB 需要考虑增强(advise)Final 方法不能被复写以及需要指定 CGLIB 包的位置,尽管 CGLIB 效率更高,但还是推荐使用 JDK 动态代理。
而 AOP 配置中的 prioxy-target-class 和 expose-proxy 属性在代理生成中起到了至关重要的。prioxy-target-class 主要负责上面两种代理的实现,而 expose-proxy 则负责自我调用切面中的增强。
AopNamespaceUtils.java
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
if (sourceElement != null) {
// 对于 proxy-target-class 属性的处理
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 对于 expose-proxy 属性的处理
boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
AopConfigUtils.java
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
// 强制使用,其实也是一个属性设置
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
这些,基本就是Spring AOP部分的实现框架了,下节就要对AOP的主要实现类 AnnotationAwareAspectJAutoProxyCreator 进行分析。