`

【转】1000行代码读懂Spring(二)- 在Spring中实现AOP

 
阅读更多

http://my.oschina.net/flashsword/blog/194481

 

 

 

关于AOP

AOP是Spring核心功能之一。今天就用tiny-spring来实现一个AOP。具体功能会包括:

  1. 读取AspectJ格式的Pointcut描述。
  2. 使用JDK动态代理以及CGLib两种方式进行AOP织入。

AOP分为配置(Pointcut,Advice),织入(Weave)两部分工作,当然还有一部分是将AOP整合到整个容器的生命周期中。

AOP相关概念较多,我不会一一列举,但是会在每一步对概念做一点解释。

7.step7-使用JDK动态代理实现AOP织入

git checkout step-7-method-interceptor-by-jdk-dynamic-proxy

织入(weave)相对简单,我们先从它开始。Spring AOP的织入点是AopProxy,它包含一个方法Object getProxy()来获取代理后的对象。

在Spring AOP中,我觉得最重要的两个角色,就是我们熟悉的MethodInterceptorMethodInvocation(这两个角色都是AOP联盟的标准),它们分别对应AOP中两个基本角色:AdviceJoinpoint。Advice定义了在切点指定的逻辑,而Joinpoint则代表切点。

public interface MethodInterceptor extends Interceptor {

    Object invoke(MethodInvocation invocation) throws Throwable;
}

Spring的AOP只支持方法级别的调用,所以其实在AopProxy里,我们只需要将MethodInterceptor放入对象的方法调用即可。

我们称被代理对象为TargetSource,而AdvisedSupport就是保存TargetSource和MethodInterceptor的元数据对象。这一步我们先实现一个基于JDK动态代理的JdkDynamicAopProxy,它可以对接口进行代理。于是我们就有了基本的织入功能。

    @Test
    public void testInterceptor() throws Exception {
        // --------- helloWorldService without AOP
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
        HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
        helloWorldService.helloWorld();

        // --------- helloWorldService with AOP
        // 1. 设置被代理对象(Joinpoint)
        AdvisedSupport advisedSupport = new AdvisedSupport();
        TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldServiceImpl.class,
                HelloWorldService.class);
        advisedSupport.setTargetSource(targetSource);

        // 2. 设置拦截器(Advice)
        TimerInterceptor timerInterceptor = new TimerInterceptor();
        advisedSupport.setMethodInterceptor(timerInterceptor);

        // 3. 创建代理(Proxy)
        JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport);
        HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy();

        // 4. 基于AOP的调用
        helloWorldServiceProxy.helloWorld();

    }

8.step8-使用AspectJ管理切面

git checkout step-8-invite-pointcut-and-aspectj

完成了织入之后,我们要考虑另外一个问题:对什么类以及什么方法进行AOP?对于“在哪切”这一问题的定义,我们又叫做“Pointcut”。Spring中关于Pointcut包含两个角色:ClassFilterMethodMatcher,分别是对类和方法做匹配。Pointcut有很多种定义方法,例如类名匹配、正则匹配等,但是应用比较广泛的应该是和AspectJ表达式的方式。

AspectJ是一个“对Java的AOP增强”。它最早是其实是一门语言,我们跟写Java代码一样写它,然后静态编译之后,就有了AOP的功能。下面是一段AspectJ代码:

aspect PointObserving {
    private Vector Point.observers = new Vector();

    public static void addObserver(Point p, Screen s) {
        p.observers.add(s);
    }
    public static void removeObserver(Point p, Screen s) {
        p.observers.remove(s);
    }
    ...
}

这种方式无疑太重了,为了AOP,还要适应一种语言?所以现在使用也不多,但是它的Pointcut表达式被Spring借鉴了过来。于是我们实现了一个AspectJExpressionPointcut

    @Test
    public void testMethodInterceptor() throws Exception {
        String expression = "execution(* us.codecraft.tinyioc.*.*(..))";
        AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
        aspectJExpressionPointcut.setExpression(expression);
        boolean matches = aspectJExpressionPointcut.getMethodMatcher().matches(HelloWorldServiceImpl.class.getDeclaredMethod("helloWorld"),HelloWorldServiceImpl.class);
        Assert.assertTrue(matches);
    }

9.step9-将AOP融入Bean的创建过程

git checkout step-9-auto-create-aop-proxy

万事俱备,只欠东风!现在我们有了Pointcut和Weave技术,一个AOP已经算是完成了,但是它还没有结合到Spring中去。怎么进行结合呢?Spring给了一个巧妙的答案:使用BeanPostProcessor

BeanPostProcessor是BeanFactory提供的,在Bean初始化过程中进行扩展的接口。只要你的Bean实现了BeanPostProcessor接口,那么Spring在初始化时,会优先找到它们,并且在Bean的初始化过程中,调用这个接口,从而实现对BeanFactory核心无侵入的扩展。

那么我们的AOP是怎么实现的呢?我们知道,在AOP的xml配置中,我们会写这样一句话:

<aop:aspectj-autoproxy/>

它其实相当于:

<bean id="autoProxyCreator" class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator"></bean>

AspectJAwareAdvisorAutoProxyCreator就是AspectJ方式实现织入的核心。它其实是一个BeanPostProcessor。在这里它会扫描所有Pointcut,并对bean做织入。

为了简化xml配置,我在tiny-spring中直接使用Bean的方式,而不是用aop前缀进行配置:

    <bean id="autoProxyCreator" class="us.codecraft.tinyioc.aop.AspectJAwareAdvisorAutoProxyCreator"></bean>

    <bean id="timeInterceptor" class="us.codecraft.tinyioc.aop.TimerInterceptor"></bean>

    <bean id="aspectjAspect" class="us.codecraft.tinyioc.aop.AspectJExpressionPointcutAdvisor">
        <property name="advice" ref="timeInterceptor"></property>
        <property name="expression" value="execution(* us.codecraft.tinyioc.*.*(..))"></property>
    </bean>

TimerInterceptor实现了MethodInterceptor(实际上Spring中还有Advice这样一个角色,为了简单,就直接用MethodInterceptor了)。

至此,一个AOP基本完工。

10.step10-使用CGLib进行类的织入

git checkout step-10-invite-cglib-and-aopproxy-factory

前面的JDK动态代理只能对接口进行代理,对于类则无能为力。这里我们需要一些字节码操作技术。这方面大概有几种选择:ASMCGLibjavassist,后两者是对ASM的封装。Spring中使用了CGLib。

在这一步,我们还要定义一个工厂类ProxyFactory,用于根据TargetSource类型自动创建代理,这样就需要在调用者代码中去进行判断。

另外我们实现了Cglib2AopProxy,使用方式和JdkDynamicAopProxy是完全相同的。

有一个细节是CGLib创建的代理是没有注入属性的,
Spring的解决方式是:CGLib仅作代理,任何属性都保存在TargetSource中,使用MethodInterceptor=>TargetSource的方式进行调用。

至此,AOP部分完工,Spring的核心也基本成型。除去import语句,main下面一共是1026行。下篇博文会对Spring进行一个整体的分析。

分享到:
评论

相关推荐

    1000行代码读懂Spring核心

    《1000行代码读懂Spring核心》这篇文章以作者黄亿华的个人经验,通过学习一个简化的Spring框架的实现——tiny-spring项目,来深入理解Spring核心的概念和技术实现。tiny-spring是一个开源项目,旨在通过逐步构建一个...

    1000行代码读懂spring核心.pdf

    文档中提到的“1000行代码读懂Spring核心”,指的是通过一个名为tiny-spring的开源项目,来逐步讲解Spring框架的实现原理。这个项目是对Spring框架核心功能的一个简化版本,通过逐行代码的分析,来帮助开发者理解...

    spring aop生成日志文件

    利用自定义注解和spring aop和java反射机制生成用户能够读懂的日志记录。如:用户张三在2013年9月27日17:00执行了用户管理模块的用户删除功能参数为(编号:123456)各位可根据需要写入数据库或者保存到文件。

    spring学习

    首先,我们来看《1000行代码读懂Spring核心.pdf》。这本书籍以实践为主导,通过1000行左右的代码示例,清晰地展示了Spring框架的核心功能。它涵盖了IoC(Inversion of Control,控制反转)和DI(Dependency ...

    教你阅读Spring源码资源.zip

    Spring框架是Java开发中不可或缺的一部分,它以其强大的依赖注入、AOP(面向切面编程)以及模块化...在阅读源码的过程中,不仅要看懂代码,更要理解设计模式和架构思想,这样才能真正将Spring的精髓运用到日常开发中。

    如何快速读懂项目源码javaWeb.doc

    【如何快速读懂项目源码JavaWeb】 阅读JavaWeb项目源代码是一个系统性的过程,涉及到多个层面的技术理解。以下是一步步解析源码的关键步骤: 1. **理解表结构**: - 开始之前,首先要了解项目的数据库设计,包括...

    彻底读懂Spring(一)读源码,我们可以从第一行读起

    在Spring框架中,深入理解源码能够帮助我们更好地掌握其实现原理和工作流程。本文将从Spring的第一行代码开始,逐步解析`AnnotationConfigApplicationContext`的构造过程,特别是关注`AnnotatedBeanDefinitionReader...

    mytinyspring

    《黄亿华1000行代码读懂Spring(一):实现一个基本的IoC容器》 Spring框架作为Java世界中的重要组件,以其强大的依赖注入(Dependency Injection,简称DI)和面向切面编程(Aspect-Oriented Programming,简称AOP...

    spring+springmvc+mybatis

    3. 集成MyBatis:在Spring配置文件中配置SqlSessionFactory和MapperScannerConfigurer,以便Spring能够自动扫描并管理Mapper接口。 4. 编写业务逻辑:创建Service层,使用Spring的@Autowired注解注入需要的依赖,...

    leedcode.zip

    1. 读懂题目:仔细阅读题目描述,明确输入输出格式和约束条件。 2. 分析问题:分析问题的本质,确定算法思路,选择合适的数据结构。 3. 编码实现:用Java编写代码,注意代码的可读性和效率。 4. 单元测试:编写测试...

    《码农翻身》第二章 Java帝国.emmx

    Spring本质系列(2) -- AOP 三层架构和MVC那点事儿 Java帝国之拨云见日识回调 小张的Duck Typing JDBC的诞生 JDBC后传 一个不安分的JDBC驱动 Java帝国之 Java Bean(上) Java帝国之 Java Bean(下) Java帝国...

    达内cloud_note源代码

    学员需要理解这些基础知识,才能读懂代码逻辑。 2. **MVC架构**:cloud_note项目可能采用了Model-View-Controller(MVC)设计模式,这是一种常用的应用程序设计架构,用于分离业务逻辑、数据模型和用户界面。在Java...

    30天学通Java项目案例例开发+源代码

    《30天学通Java项目案例开发》是一个旨在帮助初学者快速掌握Java编程语言和实践应用的教程。...记住,理论知识与实践相结合是提升技能的关键,所以不仅要读懂代码,还要动手编写,才能真正掌握Java的魅力。

    MyPersonalBlog.zip

    在个人博客系统中,Spring可以用来管理数据库连接、事务处理、服务层对象等,通过DI设计模式使得代码更松耦合,易于测试和维护。 2. **Spring MVC**:作为Spring的一部分,Spring MVC用于处理HTTP请求和响应。它...

    Learn Java File By Hello World Example

    ”接着给出了答案:在阅读完这个文档后,作为一个Java开发者,你将会熟悉Java类文件的结构,并且能读懂类文件内容。你将理解字符串"[Ljava/lang/String;"中'['和'L'的含义,以及类文件中的调试信息。此外,你还将...

    [信息办公]云网OA 2.2_cloundoa.rar

    8. **文档阅读与编写**:源码中可能包含一些设计文档或API文档,能够读懂并理解这些文档对于项目理解很有帮助。同时,良好的文档习惯也有助于他人理解和维护你的代码。 通过深入研究和实践【信息办公】云网OA 2.2_...

    SillyGooseServer:javaEE Server(SSM框架)

    5. 熟悉Maven或Gradle构建工具,能读懂和修改构建配置。 6. 对于Web应用,了解Servlet和JSP的基础知识也是必要的。 总的来说,SillyGooseServer项目是一个典型的JavaEE服务器应用实例,它展示了如何结合SSM框架构建...

    JAVA自学之路

    当你读懂了一个问题之后,要好好的思考这个问题可能会在哪些环节上出错。 一辆汽车从总成线上下来,车门子关不上! 哪错了?你怎么查? 当然是顺着生产线一站一站的查下来。 程序也是一样的,也是一系列...

    TP09_2020-21

    【标题】"TP09_2020-21" 指的可能是一个项目或者课程的编号,其中“TP”...如果你打算深入学习Java或评估该项目,你需要熟悉上述提到的Java知识点,并能读懂和分析代码,甚至可能需要运行和调试项目以理解其工作原理。

Global site tag (gtag.js) - Google Analytics