• Spring相框:AOP详细说明


    AOP中国的名字叫做面向方面编程。这个名字是很形象。因为你真的可以把像面包切系统。并直接增加面包的修改。科而异,对整个系统,小到一定的方法。


    AOP它有什么用?有关示例,各组分可以含有安全、事务、,AOP就是把每一个组件中的安全作为一个方面进行集中处理。事务作为一个方面,数据库作为一个方面等等。这样才干做到高内聚、低耦合。

    AOP中有三个重要的术语:通知、切点、连接点。

    他们之间的关系例如以下图。



    AOP实现的原理是改动目标类中的代码。

    至于怎么改动,有多种方式:编译时、类载入时、执行时。编译时改动须要特殊的编译器。类载入时改动须要特殊的类载入器。执行时。就是应用在执行的时候AOP框架会为目标对象生成一个动态代理类。Spring AOP採用的就是执行时代理。

    Spring容器通过ObjectFactory创建全部的Bean实例。而且实例之外添加一层动态代理。

    SpringAOP具体实现主要涉及到反射机制中的Proxy.newProxyInstance和InvocationHandler。在兴许的JVM文章中还会具体介绍。


    除了Spring AOP眼下流行的AOP框架还有AspectJ、JBoss AOP。


    以下是AOP的Hello World程序。目标是,在某个类的createApple方法调用之前做一些事情,可是又不能直接改变这种方法的代码。以下这段代码就是在createApple方法运行之前,额外运行beforeCreateApple,有点类似于Hook。

    代码例如以下:

    <bean id="appleListener" class="xxx"/>
    
    <aop:aspect ref="appleListener">
      <aop:pointcut id="apple" expression="execution(* *.createApple(..))" />
      
      <aop:before pointcut-ref="apple" method="beforeCreateApple" />
    </aop:aspect>
    

    上面这段代码的意思是。当程序中不论什么一个类的createApple方法被调用之前,都先调用appleListener中的beforeCreateApple方法。


    切点表达式语言。上面样例中的execution(* *.createApple(..))就是表达式语言。第一个星号表示返回值的类型。第二个星号表示被调用的类名。

    支持例如以下语法:

    • args() 将參数传递给切面
    • @args() 匹配注解才传递參数
    • execution() 匹配详细的方法
    • this() 匹配当前bean
    • target() 匹配目标对象
    • @target() 匹配目标对象的注解
    • within() 匹配实例的类型
    • @within() 匹配实例的注解
    • @annotation() 匹配注解
    • bean() 匹配bean id

    以下举例说明切点表达式语言。

    // 切点为运行com.example.Apple.eat方法,返回值随意。參数随意。
    execution(* com.example.Apple.eat(..))
    
    // within表示仅仅匹配com.example.*下的随意方法。用了and连接符号。
    execution(* com.example.Apple.eat(..) and within(com.example.*))
    
    // bean表示匹配对应的bean
    execution(* com.example.Apple.eat(..) and bean(apple))
    


    以下的样例演示了切点的各种修饰方式。

    <aop:config>
      <!--定义切面,test是事先定义好的一个bean-->
      <aop:aspect ref="test">
        <!--定义切点-->
        <aop:pointcup id="apple-eat" expression="execution(* com.example.Apple.eat(..))"/>
        
        <!--在切点之前调用test.beforeEat-->
        <aop:before pointcut-ref="apple-eat" method="beforeEat"/>
        
        <!--在切点运行成功之后调用-->
        <aop:after-return pointcut-ref="apple-eat" method="eatSuccess"/>
        
        <!--在切点运行失败之后调用-->
        <aop:after-throwing pointcut-ref="apple-eat" method="eatFailed"/>
        
        <!--在切点之后调用。无论成功失败-->
        <aop:after pointcut-ref="apple-eat" method="afterEat"/>
        
        <!--围绕通知。以下有具体说明-->
        <aop:around pointcut-ref="apple-eat" method="eatApple"/>
        
        <!--动态添加接口,以下有具体说明-->
        <aop:declare-parents types-matching="com.example.Apple+" implement-interface="com.example.Fruit" default-impl="com.example.FruitImpl"/>
      </aop:aspect>
    </aop:config>
    


    现代化的Spring支持注解方式的切面。以下请看样例。

    // 定义切面
    @Aspect
    public class Test {
        // 定义切点。方法中不须要写不论什么代码。
        @Pointcut("execution(* com.example.Apple.eat(..))")
        public void appleEat() { }
        
        // 切面之前
        @Before("appleEat()")
        public void beforeEat() { }
        
        // 切面运行成功之后
        @AfterReturning("appleEat()")
        public void eatSuccess() { }
        
        // 切面运行失败之后
        @AfterThrowing("appleEat()")
        public void eatFailed() { }
        
        // 切面之后。无论成功失败
        @After("appleEat()")
        public void afterEat() { }
        
        // 围绕切面,以下有具体说明
        @Around("appleEat")
        public void eatApple(ProceedingJoinPoint joinpoint) { }
        
        // 定义传递參数的切点
        @Pointcut("execution(* com.example.Apple.eat(..)) and args(size)")
        public void appleEat2() { }
        
        // 接收切点的參数
        @Before("appleEat2")
        public void beforeEat2(int size) {
            // 可以得到切点的size參数
        }
    }
    


    围绕通知。它的目的是为了解决切点前后无法通信的问题。本质是四种切点的结合体。比方我想记录一个切点的运行时间。就须要用到围绕通知。以下是围绕通知的代码。

    public void eatApple(ProceedingJoinPoint joinPoint) {
        // 在切点之前
        System.out.println("before pointcut");
        
        // 手动运行切点
        joinPoint.proceed();
        
        // 在切点之后
        System.out.println("after pointcut");
    }
    


    Introduction引入,也就是动态添加新接口。它的作用就是在程序执行的过程中动态地为一个实例添加接口。请看以下的样例。

    // Introduction引入。
    @DeclareParents(value="com.example.Phone+", defaultImpl="com.example.AppleWatchImpl")
    public static AppleWatch appleWatch;
    

    上面的样例中给appleWatch字段添加了一个注解。意思是让appleWatch字段能够转换使Phone种类。最初appleWatch它不能被转换成Phone的。

    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    单词 统计
    第十周学习记录
    梦断代码阅读笔记03
    梦断代码阅读笔记02
    梦断代码阅读笔记01
    用户模板和用户场景
    第九周学习记录
    分享好友-分享朋友圈
    生命周期函数-页面刷新
    底部导航的设置
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4873440.html
Copyright © 2020-2023  润新知