Spring 源码分析(九)--AOP

我们知道,使用面向对象编程(OOP)有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共行为时,例如日志,安全检测等,我们只有在每个对象里引用公共行为,这样程序中就产生了大量的重复代码,程序就不便于维护了,所以就有了一个对面向对象编程的补充,即面向方面编程(AOP),AOP所关注的方向是横向的,不同于OOP的纵向。

Spring 2.0 采用@AspectJ注解对POJO进行标注,从而定义一个包含切点信息和增强横切逻辑的切面。Spring2.0可以将这个切面织入到匹配的目标Bean中。@AspectJ注解使用AspectJ切点表达式语法进行切点定义,可以通过切点函数,运算符,通配符等高级功能进行切点定义,拥有强大的连接点描述能力。

一:动态AOP使用示例

(1)创建用于拦截的bean

在实际工作中,此bean可能是满足业务需要的核心逻辑,例如test方法可能会封装这某个核心业务,但是,如果我们想在test前后加入日志来跟踪调试,如果直接修改源码并不符合面向对象的设计方法,而且随意修改原有代码也会造成一定的风险,还好接下来的Spring帮我们做到了这一点。

public class TestBean {
    private String testStr = "testStr";

    public String getTestStr() {
        return testStr;
    }

    public void setTestStr(String testStr) {
        this.testStr = testStr;
    }

    public void test(){
        System.out.println("test");
    }
}

(2)创建Advisor。

Spring中摒弃了最原始的繁杂配置方式而采用@AspectJ注解对POJO进行标注,使AOP的工作大大简化,例如,在AspectJTest类中,我们要做的就是在所有类的test方法执行前在控制台中打印beforeTest,而在所有类的test方法执行后打印afterTest,同时又使用环绕的方式在所有类的方法执行前后再次分别打印before1和after1。

@Aspect
public class AspectJTest {

    @Pointcut("execution(* *.test(..))")
    public void test(){

    }

    @Before("test()")
    public void beforeTest(){
        System.out.println("beforeTest");
    }

    @After("test()")
    public void afterTest(){
        System.out.println("afterTest");
    }

    public Object arountTest(ProceedingJoinPoint p){
        System.out.println("before1");
        Object obj = null;
        try{
            obj = p.proceed();
        }catch (Throwable e){
            e.printStackTrace();
        }
        System.out.println("after1");
        return obj;
    }
}

(3)创建配置文件

尽管注解可以取代XML配置,但是本文还是通过XML配置来开启AOP功能,做更详细的讲解,这样有助于理解。

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

    <aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
    <bean id = "test" class="test.TestBean" ></bean>
    <bean class="test.AspectJTest" ></bean>
</beans>

(4)测试

 public static void main(String[] args){
        ApplicationContext bf = new ClassPathXmlApplicationContext("aspectTest.xml");
        TestBean bean = (TestBean) bf.getBean("test");
        bean.test();
    }

(5)结果

beforeTest

before1

test

afterTest

after1

Spring实现了对所有类的test方法进行增加,使辅助功能可以独立于核心业务之外,方便与程序的扩展和解耦。

那么,Spring究竟是如何实现AOP的呢?首先我们知道,Spring是否支持注解的AOP是由一个配置文件控制的,也就是<aop:aspectj--autoproxy />,当在配置文件中声明了这句配置的时候,Spring就会支持注解的AOP,那么我们的分析就从这句注解开始。

二:动态AOP自定义标签

之前讲过Spring中的自定义注解,如果声明了自定义的注解,那么就一定会在程序中的某个地方注册了对应的解析器。通过搜索发现在AopNamespaceHander中对应着这样一段函数:

public class AopNamespaceHandler extends NamespaceHandlerSupport {

    /**
     * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
     * ‘{@code config}‘, ‘{@code spring-configured}‘, ‘{@code aspectj-autoproxy}‘
     * and ‘{@code scoped-proxy}‘ tags.
     */
    @Override
    public void init() {
        // In 2.0 XSD as well as in 2.1 XSD.
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

        // Only in 2.0 XSD: moved to context namespace as of 2.1
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }

}

我们可以得知,在解析配置文件的时候,一旦遇到aspectj-autoproxy注解时就会使用解析器AspectJAutoProxyBeanDefinitionParser进行解析,下面看看AspectJAutoProxyBeanDefinitionParser的内部实行。

(2.1)注册AnnotationAwareAspectJAutoProxyCreator

所有解析器,因为是对BeanDefinitionParser接口的统一实现,入口都是从parse函数开始的,AspectJAutoProxyBeanDefinitionParser的parse函数如下:

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

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

    private void extendBeanDefinition(Element element, ParserContext parserContext) {
        BeanDefinition beanDef =
                parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
        if (element.hasChildNodes()) {
            addIncludePatterns(element, parserContext, beanDef);
        }
    }

    private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
        ManagedList<TypedStringValue> includePatterns = new ManagedList<TypedStringValue>();
        NodeList childNodes = element.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node instanceof Element) {
                Element includeElement = (Element) node;
                TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
                valueHolder.setSource(parserContext.extractSource(includeElement));
                includePatterns.add(valueHolder);
            }
        }
        if (!includePatterns.isEmpty()) {
            includePatterns.setSource(parserContext.extractSource(element));
            beanDef.getPropertyValues().add("includePatterns", includePatterns);
        }
    }

}

其中registerAspectJAnnotationAutoProxyCreatorIfNecessary 函数是我们比较关心的,也是关键逻辑的实现。

/**
* @author Rob Harrop
 * @author Juergen Hoeller
 * @author Mark Fisher
 * @since 2.0
 * @see AopConfigUtils
 */
public abstract class AopNamespaceUtils {

    /**
     * The {@code proxy-target-class} attribute as found on AOP-related XML tags.
     */
    public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";

    /**
     * The {@code expose-proxy} attribute as found on AOP-related XML tags.
     */
    private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";

    public static void registerAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {

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

    public static void registerAspectJAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {

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

    public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {

        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));        //对于proxy-target-class以及expose--proxy属性的处理
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);        //注册组件并通知,便于监听器做进一步的处理        //其中beanDefinition的className为AnnotationAwareAspectJAutoProxyCreator
        registerComponentIfNecessary(beanDefinition, parserContext);
    }

    private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
        if (sourceElement != null) {
            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);
            }
        }
    }

    private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {
        if (beanDefinition != null) {
            BeanComponentDefinition componentDefinition =
                    new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
            parserContext.registerComponent(componentDefinition);
        }
    }

}

在registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中主要完成了3件事情,基本上每行代码就是一个完整的逻辑。

1. 注册或升级AnnotationAwareAspectJAutoProxyCreator

对于AOP的实现,基本上都是靠AnnotationAwareAspectJAutoProxyCreator去完成,它可以根据@Point注解定义的切点来自动代理相匹配的bean。

原文地址:https://www.cnblogs.com/fdzfd/p/8452783.html

时间: 2024-08-08 03:00:58

Spring 源码分析(九)--AOP的相关文章

【Spring源码分析】AOP源码解析(上篇)

前言 前面写了六篇文章详细地分析了Spring Bean加载流程,这部分完了之后就要进入一个比较困难的部分了,就是AOP的实现原理分析.为了探究AOP实现原理,首先定义几个类,一个Dao接口: 1 public interface Dao { 2 3 public void select(); 4 5 public void insert(); 6 7 } Dao接口的实现类DaoImpl: 1 public class DaoImpl implements Dao { 2 3 @Overrid

【Spring源码分析】AOP源码解析(下篇)

AspectJAwareAdvisorAutoProxyCreator及为Bean生成代理时机分析 上篇文章说了,org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator这个类是Spring提供给开发者的AOP的核心类,就是AspectJAwareAdvisorAutoProxyCreator完成了[类/接口-->代理]的转换过程,首先我们看一下AspectJAwareAdvisorAutoProx

【spring源码分析】IOC容器初始化(总结)

前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正的bean对象. 总结 [spring源码分析]IOC容器初始化(一):主要分析了Spring是如何解析占位符以及BeanFactory的最终实现类DefaultListableBeanFactory. [spring源码分析]IOC容器初始化(二):以loadBeanDefinitions函数为切

【Spring源码分析】配置文件读取流程

前言 Spring配置文件读取流程本来是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一起的,这两天在看Spring自定义标签的时候,感觉对Spring配置文件读取流程还是研究得不够,因此将Spring配置文件读取流程部分从之前的文章拆出来单独成为一文. 为了看一下Spring配置文件加载流程,先定义一个bean.xml: 1 <?xml version="1.0" encoding="UTF-8"?>

Spring源码分析专题 —— 阅读指引

阅读源码的意义 更深入理解框架原理,印象更深刻 学习优秀的编程风格.编程技巧.设计思想 解决实际问题,如修复框架中的bug,或是参考框架源码,结合实际业务需求编写一个独有的框架 阅读源码的方法 首先是要有一定的编程经验.如果连业务代码都写得不流畅那是不建议阅读源码的,因为基础不好的情况下一是阅读困难,二是无法静下心理解,每看两行就会纠结花大量时间在源码上是否值得,感觉不如写多两行业务代码来得有价值. 要有耐心.一篇关于源码的文章可能需要阅读两三小时以上才能读完,如果没有这个觉悟,可能看到一半就转

【Spring源码分析】非懒加载的Bean实例化过程(下篇)

doCreateBean方法 上文[Spring源码分析]非懒加载的Bean实例化过程(上篇),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下AbstractAutowireCapableBeanFactory的doCreateBean方法代码: 1 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[]

Spring源码分析——BeanFactory体系之抽象类、类分析(二)

上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之抽象类.类分析(一),今天继续分析. 一.工厂Bean注册支持——FactoryBeanRegistrySupport 废话不多说,直接看我注释的源码: /* * Copyright 2002-2012 the original author or authors. * * Licensed und

Spring源码分析——BeanFactory体系之抽象类、类分析(一)

上一篇介绍了BeanFactory体系的所有接口——Spring源码分析——BeanFactory体系之接口详细分析,本篇就接着介绍BeanFactory体系的抽象类和接口. 一.BeanFactory的基本类体系结构(类为主): 上图可与 Spring源码分析——BeanFactory体系之接口详细分析 的图结合分析,一个以接口为主,一个以类为主(PS:Spring的体系结构要分析清楚,不得不曲线救国啊!不然27寸屏幕给我画估计都装不下.). 具体: 1.7层的类体系继承. 2.Abstrac

【Spring源码分析】Bean加载流程概览

代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已,Spring的加载过程相对是不太透明的,不太好去找加载的代码入口. 下面有很简单的一段代码可以作为Spring代码加载的入口: 1 ApplicationContext ac = new Clas

spring源码分析构建

命令如下: ant ant install-maven ant jar package E:\download\spring-framework-3.1.3.RELEASE\build-spring-framework\readme.txt readme.txt文档很重要,里面有非常重要的命令,如果导入eclipse之后,发现少了jar包,执行这些命令,就可以自动下载缺少的jar包. https://github.com/spring-projects/spring-framework这个页面首