• (III)AOP:第三节:AOP配置与使用


    一、AOP 配置

      1、导入 jar 包

        ① 导入 Spring 基础包

               <dependency>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                    <version>1.1.3</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-beans</artifactId>
                    <version>${spring-version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                    <version>${spring-version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</artifactId>
                    <version>${spring-version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-expression</artifactId>
                    <version>${spring-version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-beans</artifactId>
                    <version>${spring-version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-test</artifactId>
                    <version>${spring-version}</version>
                </dependency>

        ② 导入基础的 AOP 包

                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aspects</artifactId>
                    <version>5.2.3.RELEASE</version>
                </dependency>

        ③ 导入加强版的面向切面编程(即使目标对象没有实现任何接口也能创建动态代理

                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>com.springsource.org.aspectj.weaver</artifactId>
                    <version>1.6.4.RELEASE</version>
                </dependency>
                <dependency>
                    <groupId>org.aopalliance</groupId>
                    <artifactId>com.springsource.org.aopalliance</artifactId>
                    <version>1.0.0</version>
                </dependency>
                <dependency>
                    <groupId>net.sourceforge.cglib</groupId>
                    <artifactId>com.springsource.net.sf.cglib</artifactId>
                    <version>2.1.3</version>
                </dependency>

      2、写配置

    ①将目标类和切面类(封装通知方法(在目标方法执行前后的方法))加入到IOC容器中
    
    ②告诉Spring哪个是切面类@Aspect
    
    ③告诉Spring,切面类里面的每一个方法,都是何时何地运行
    

       将业务类添加到容器中:

    @Service
    public class MyMathCalculator implements Calculator{}

       将切面类(日志类)加到容器中

    /**
     * 如果将这个类(切面类)中的这些方法(通知方法)动态的在目标方法运行的各个位置切入
     */
    @Aspect
    @Component
    public class LogUtils {
    
        /**
         * 告诉 Spring 每个方法都什么时候运行
         *
         * try {
         *     @Before
         *     method.invoke(obj, args);
         *     @AfterReturning
         * }catch(e) {
         *     @AfterThrowing
         * }finally {
         *     @After
         * }
         *
         *
         * @Before:在目标方法运行之前              前置通知
         * @After:在目标方法运行结束之后           后置通知
         * @AfterReturning:在目标方法正常放回之后  返回通知
         * @AfterThrowing:在目标方法抛异常之后     异常通知
         * @Around:环绕                           环绕通知
         */
    
        //想在执行目标方法之前
        //execution(访问权限符 返回值类 方法签名)
        @Before("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
        public static void logStart(){
            System.out.println("【XXX】方法执行了,参数为【XXX】");
        }
    
        //想在目标方法正执行完毕之后
        @AfterReturning("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
        public static void logReturn(){
            System.out.println("【XXX】方法执行完成,他的结果为是:");
        }
    
        @AfterThrowing("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
        //想在目标方法出现异常时执行
        public static void logException(){
            System.out.println("【XXX】方法出现了异常,异常为: ");
        }
    
        //想在目标方法结束时执行
        @After("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
        public static void logEnd(){
            System.out.println("【XXX】方法执行最终完成");
        }
    }

        开启包扫描:

        <context:component-scan base-package="com.njf.aop"></context:component-scan>

      3、在配置文件中 开启基于注解的AOP模式

        <!--  开启基于注解的AOP功能:aop 名称空间  -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

      4、测试

        ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
    
        @Test
        public void test1() {
            //1、从ioc容器中拿到目标对象
            //注意:如果想要用类型获取,一定要用他的接口类型,不要用他的本类
            Calculator calculator = ioc.getBean(Calculator.class);
    
            calculator.add(1, 2);
        }

    二、AspectJ

      1、简介

        AspectJ:Java社区里最完整最流行的AOP框架。

        在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。

      2、在 Spring 中启用 AspectJ 注解支持

        ① 导入jar包依赖

    aopalliance.jar
    
    aspectj.weaver.jar
    
    spring-aspects.jar
    

      

        ② 开启基于注解的 AOP

          (1)引入AOP名称空间

          

           (2)配置文件

            在配置文件中开启自动生成代理。

    <!-- 开启 aspectJ 的自动代理功能 -->
    <aop:aspectj-autoproxy />
    作用:当 Spring IOC 容器侦测到 bean 配置文件中的 <aop:aspectj-autoproxy> 元素时,会自动为与 AspectJ 切面匹配的 bean 创建代理。

           (3)开启 Spring IOC 的扫描组件

    <!-- 让 IOC 容器扫描添加了注解的组件 -->
    <context:component-scan  base-package="com.spring.aop"></context:component-scan>

      3、用AspectJ注解声明切面

      (1)要在  Spring 中声明 AspectJ 切面,只需要在 IOC 容器中将切面声明为 bean 实例;
      (2)当在 Spring IOC 容器中初始化 AspectJ 切面之后,Spring IOC 容器就会为那些与 AspectJ 切面想匹配的 bean 创建代理;
      (3)在 AspectJ 注解中,切面只是一个带有 @Aspect 注解的 Java 类,它往往包含很多通知;
      (4)通知是标注有某种注解的简单的 Java 方法;
    try{
        @Before
        method.invoke(obj,args);
        @AfterReturning
    }catch(e){
        @AfterThrowing:在目标方法抛异常之后
    }finally{
        @after:在目标方法运行结束之后
    }

      4、切点表达式:

    execution(访问权限符 返回值类型 方法签名)

      5、通知注解

    AspectJ 支持 5 种类型的通知注解:
    告诉Spring每个方法,都什么时候运行
    
    ① @Before:前置通知,在方法执行之前执行;
    ② @After:后置通知,在方法执行之后执行(finally里面,永远都会执行);
    ③ @AfterRunning:返回通知,在方法返回结果之后执行;
    ④ @AfterThrowing:异常通知,在方法抛出异常之后执行;
    ⑤ @Around:环绕通知,围绕这方法执行;

    三、完整代码

      计算器核心业务:

    // 计算器接口
    public interface Calculator {
    
        public int add(int i, int j);
        public int sub(int i, int j);
        public int mul(int i, int j);
        public int div(int i, int j);
    }
    
    //计算器接口实现类
    @Service
    public class MyMathCalculator implements Calculator{
    
        @Override
        public int add(int i, int j) {
            int result = i + j;
            return result;
        }
    
        @Override
        public int sub(int i, int j) {
            int result = i - j;
            return result;
        }
    
        @Override
        public int mul(int i, int j) {
            int result = i * j;
            return result;
        }
    
        @Override
        public int div(int i, int j) {
            int result = i / j;
            return result;
        }
    
    }

      切面类:

    /**
     * 如果将这个类(切面类)中的这些方法(通知方法)动态的在目标方法运行的各个位置切入
     */
    @Aspect
    @Component
    public class LogUtils {
    
        /**
         * 告诉 Spring 每个方法都什么时候运行
         *
         * try {
         *     @Before
         *     method.invoke(obj, args);
         *     @AfterReturning
         * }catch(e) {
         *     @AfterThrowing
         * }finally {
         *     @After
         * }
         *
         *
         * @Before:在目标方法运行之前              前置通知
         * @After:在目标方法运行结束之后           后置通知
         * @AfterReturning:在目标方法正常放回之后  返回通知
         * @AfterThrowing:在目标方法抛异常之后     异常通知
         * @Around:环绕                           环绕通知
         */
    
        //想在执行目标方法之前
        //execution(访问权限符 返回值类 方法签名)
        @Before("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
        public static void logStart(){
            System.out.println("【XXX】方法执行了,参数为【XXX】");
        }
    
        //想在目标方法正执行完毕之后
        @AfterReturning("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
        public static void logReturn(){
            System.out.println("【XXX】方法执行完成,他的结果为是:");
        }
    
        @AfterThrowing("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
        //想在目标方法出现异常时执行
        public static void logException(){
            System.out.println("【XXX】方法出现了异常,异常为: ");
        }
    
        //想在目标方法结束时执行
        @After("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
        public static void logEnd(){
            System.out.println("【XXX】方法执行最终完成");
        }
    }

      配置文件:

        <!-- 扫描让 IOC 容器管理的 bean -->
        <context:component-scan base-package="com.njf.aop"></context:component-scan>
         
         <!-- 当Spring IOC容器侦测到bean配置文件中的该元素时,会自动为与AspectJ切面匹配的bean创建代理 -->
         <!-- 开启 aspectJ 的自动代理功能 -->
         <aop:aspectj-autoproxy />

    四、细节

      1、IOC容器中保存的是组件的代理对象

        @Test
        public void test1() {
            //1、从ioc容器中拿到目标对象
            //注意:如果想要用类型获取,一定要用他的接口类型,不要用他的本类
            /**
             * 细节一:
             *  com.njf.aop.calc.MyMathCalculator@368f2016
             *  class com.sun.proxy.$Proxy13
             *  AOP 的底层就是动态代理,容器中保存是组件是它的代理对象:$Proxy13, 当然不是本类的类型。
             *
             */
    
            Calculator calculator = ioc.getBean(Calculator.class);
    
            calculator.add(1, 2);
            System.out.println(calculator);
            System.out.println(calculator.getClass());
    
        }

      2、没有接口就是本类类型

        CGLIB会帮我们创建好代理对象,就算没有接口

        @Test
        public void test2() {
    
            //没有接口就是本类类型,
            //com.njf.aop.calc.MyMathCalculator@3098cf3b
            //class com.njf.aop.calc.MyMathCalculator$$EnhancerByCGLIB$$13c10013
            //cglib 帮我们创建好的代理对象
            MyMathCalculator calculator = ioc.getBean(MyMathCalculator.class);
    
            calculator.add(1, 2);
            System.out.println(calculator);
            System.out.println(calculator.getClass());
    
        }

      有接口就转成接口类型(Calculator.class),没有接口就转成本类类型(MyMathCalculator.class)

  • 相关阅读:
    Java根据百度API获得经纬度,然后根据经纬度在获得城市信息
    获取鼠标位置的几个通用的JS函数
    java 定时备份数据库
    基于commons-net实现ftp创建文件夹、上传、下载功能
    java自动识别用户上传的文本文件编码
    CSS3实现10种Loading效果
    Java实现拖拽上传
    JAVA 比较两张图片的相似度的代码
    Java jsp页面中jstl标签详解
    mysql去除重复数据
  • 原文地址:https://www.cnblogs.com/niujifei/p/15442766.html
Copyright © 2020-2023  润新知