(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)

原文地址:https://www.cnblogs.com/niujifei/p/15442766.html