• Spring的AOP详解


    AOP(Aspect-Oriented Programming)学前知识点

    定义AOP术语

    Spring对AOP的支持

    使用注解创建切面代码

    使用XML创建切面代码


    学前知识点:

    • 什么是横切关注点:横切关注点可以描述为影响多个业务模块的功能。
    • 切面来源:横切关注点可以被模块为切面(aspect)。
    • 将横切关注点模块化为切面的好处:1.使横切关注点的代码集中在一个地方。2.使每个业务模块只包含它们需要实现的主要功能,次要功能被转移到切面中。
    • AOP功能:AOP有助于横切关注点与它们所影响的对象之间的解耦。
    • AOP用法过程:横切关注点从多个类中抽取出来模块化成一个切面后,再将它的方法影响到这多个类执行的过程中。
    • AOP使用到的设计模式:代理模式。
    • 因为Spring基于动态代理,所以Spring只支持方法级别的连接点。
    • Spring的通知是Java编写的
    • 运行期,在ApplicationContext下的BeanFactory中加载所有bean,为有@Aspect注解的bean创建一个代理,这个代理会围绕着所有该切面的切点所匹配的bean,当有方法调用时,代理类拦截方法调用,先执行切面逻辑,再调用目标bean的方法。

    定义AOP术语:

    • 通知(Adivce):就是在目标对象方法的调用前后调用通知,为对象增加新的行为。

      Spring切面的5种类型的通知:

      1. 前置通知(Before):在目标方法被调用之前调用通知功能。
      2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么。
      3. 返回通知(After-returning):在目标方法成功执行之后调用通知。
      4. 异常通知(After-throwing):在目标方法抛出异常后调用通知。
      5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
    • 连接点(Join point):应用执行时能插入切面的点(Spring中类的各种方法都是连接点)。
    • 切点(Point cut):缩小切面通知的连接点范围。用切点表达式匹配到切面要插入的1个或多个连接点。

        

    • 切面(Aspect):切面是通知和切点的结合。是横切关注点模块化出来的一个类。
    • 引入(Introduction):引入允许我们向现有的类添加新方法或属性。
    • 织入(Weaving):切面在指定连接点被织入到目标对象中。织入是把切面应用到目标对象并创建新的代理的过程。  

        切面织入到类的三个时期:

      1. 编译期:在目标编译时织入切面。需要如AspectJ一样的特殊编译器。
      2. 类加载期:在目标类加载到JVM时被织入。这种方式需要特殊的类加载器,对字节码添加新的行为。
      3. 运行期:切面在应用运行的某个时刻被织入。在织入时,Spring AOP容器会为目标对象动态的创建一个代理对象。

     


    Spring对AOP的支持:

    • 基于代理的经典Spring AOP:编程模型很复杂很笨重。
    • 纯POJO切面:运用的XML配置将POJO转化为切面。
    • @AspectJ注解驱动的切面:Spring借鉴了AspectJ的切面,用注解将POJO转化为切面。
    • 注入式AspectJ切面:需要拦截构造器或属性的时候。

    使用注解创建切面代码:

      需要加入的依赖:

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.8.13</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>4.3.14.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.3.14.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.10</version>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>RELEASE</version>
        </dependency>

      表演接口:

    public interface Performance { // 表演接口
        void perform();
    }

      音乐剧类实现表演接口:

    public class Musical implements Performance {// 音乐剧类
        @Override
        public void perform() {
            System.out.println("开始演奏——啦啦啦啦——演奏完毕");
        }
    }

      观众切面:

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    
    // 定义一个切面
    @Aspect
    public class Audience {// 观众类
    
        @Pointcut("execution(* com.qsh.concert.Musical.perform(..))")// 将perform()方法定义为切点,并依附于perform()这个空方法上
        public void perform() {// 用作切点依附的空方法
        }
    
        @Before("perform()")// 前置通知
        public void closePhone() {// 关闭手机
            System.out.println("Before—Turn off the phone");
        }
    
        @Before("perform()")// 前置通知
        public void takeSeat() {// 请坐
            System.out.println("Before—Take a seat");
        }
    
        @After("perform()")// 后置通知
        public void standUp() {// 站起来
            System.out.println("After—stand up");
        }
    
        @AfterReturning("perform()")// 返回通知
        public void handclap() {// 鼓掌
            System.out.println("AfterReturning—CLAP~CLAP~CLAP!");
        }
    
        @AfterThrowing("perform()")// 异常通知
        public void demandRefund() {// 要求退款
            System.out.println("AfterThrowing—Demand a refund");
        }
    
        @Around("perform()")// 环绕通知
        public void doEverything(ProceedingJoinPoint jp) {// 实现上面所有通知
            try {
                System.out.println("Around—Turn off the phone");
                System.out.println("Around—Take a seat");
                jp.proceed();
                System.out.println("Around—stand up");
                System.out.println("Around—CLAP~CLAP~CLAP!");
            } catch (Throwable e) {
                System.out.println("Around—Demand a refund");
            }
        }
    }

      配置类:

    import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configuration
    @EnableAspectJAutoProxy //AspectJ自动代理会为使用了@Aspect注解的bean创建一个代理
    public class ConcertConfig {
        @Bean
        public Audience audience() {
            return new Audience();
        }
    
        @Bean
        public Musical musical() {
            return new Musical();
        }
    }

      测试方法:

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = ConcertConfig.class)
    public class test {
        @Autowired
        private Performance performance;
    
        @Test
        public void test() {
            performance.perform();
        }
    }

      测试结果:

    注:可以看出环绕通知先开始先结束,后置通知在返回通知前先调用。


    使用XML创建切面代码:

       表演接口:

    public interface Performance { // 表演接口
        void perform();
    }

      音乐剧类实现表演接口:

    public class Musical implements Performance {// 音乐剧类
        @Override
        public void perform() {
            System.out.println("开始演奏——啦啦啦啦——演奏完毕");
        }
    }

      观众类(无注解,一会用XML将这个类声明为切面):

    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class Audience {// 观众类
    
        public void closePhone() {// 关闭手机
            System.out.println("Before—Turn off the phone");
        }
    
        public void takeSeat() {// 请坐
            System.out.println("Before—Take a seat");
        }
    
        public void standUp() {// 站起来
            System.out.println("After—stand up");
        }
    
        public void handclap() {// 鼓掌
            System.out.println("AfterReturning—CLAP~CLAP~CLAP!");
        }
    
        public void demandRefund() {// 要求退款
            System.out.println("AfterThrowing—Demand a refund");
        }
    
        public void doEverything(ProceedingJoinPoint jp) {// 实现上面所有通知
            try {
                System.out.println("Around—Turn off the phone");
                System.out.println("Around—Take a seat");
                jp.proceed();// 调用被通知的方法
                System.out.println("Around—stand up");
                System.out.println("Around—CLAP~CLAP~CLAP!");
            } catch (Throwable e) {
                System.out.println("Around—Demand a refund");
            }
        }
    }

      XML配置:

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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-3.2.xsd">
    
        <!--将观众类装配为bean-->
        <bean id="audience" class="com.qsh.concert.Audience"/>
        <!--将音乐会类装配为bean-->
        <bean id="musical" class="com.qsh.concert.Musical"/>
        <!--将观众类声明为切面-->
        <aop:config>
            <aop:aspect ref="audience">
                <aop:pointcut id="perform" expression="execution(* com.qsh.concert.Performance.perform(..))"/>
                <aop:before pointcut-ref="perform" method="closePhone"/>
                <aop:before pointcut-ref="perform" method="takeSeat"/>
                <aop:after pointcut-ref="perform" method="standUp"/>
                <aop:after-returning pointcut-ref="perform" method="handclap"/>
                <aop:after-throwing pointcut-ref="perform" method="demandRefund"/>
                <aop:around pointcut-ref="perform" method="doEverything"/>
            </aop:aspect>
        </aop:config>
    </beans>

      测试类:

    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class test {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("concertconfig.xml");
            Performance performance = context.getBean(Performance.class);
            performance.perform();
        }
    }
  • 相关阅读:
    类的有参方法
    WPF 中的设备无关单位
    Skelta BPM.NET 2006 初探
    Reporting Services Handscript
    C语言I博客作业03
    C语言I博客作业02
    C++类的运算符重载和转换函数结合的问题
    C++ 函数返回类成员的问题
    kaggle 利用linear regression 进行房价预测
    android Could not open: c:\。。。。\.android/avd/XXXX.ini 问题和解决方法
  • 原文地址:https://www.cnblogs.com/JimKing/p/8847439.html
Copyright © 2020-2023  润新知