AOP小结

一. AOP 底层原理

AOP 底层使用动态代理,分为两种情况:

  • 有接口,使用 JDK 动态代理
  • 无接口,使用 CGLIB 动态代理

二. AOP 术语

  1. 连接点:类中可以被增强的方法
  2. 切入点:实际被增强的方法
  3. 通知(增强):实际增强的逻辑部分。分为前置通知、后置通知、环绕通知、异常通知、最终通知
  4. 切面:把通知应用到切入点的过程

三. AOP 操作

Spring 一般基于 AspectJ 实现 AOP 操作,可基于 xml 实现,也可基于注解实现(常用)

1. 切入点表达式

语法结构:execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))

// 例一:对 Book 类的 add 方法进行增强,*表示任意修饰符(public,private...),返回类型可以省略
execution(* com.dao.Book.add(..))
// 例二:对 Book 类所有方法进行增强
execution(* com.dao.Book.*(..))
// 例三:对 dao 包里所有的所有方法进行增强
execution(* com.dao.*.*(..))

2. 基于注解(重点)

基本步骤

(1)在spring 配置文件中开启注解扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启注解扫描-->
    <context:component-scan base-package="com.aop.anno"/>

</beans>

(2)使用注解加载类的 bean,并在增强类上添加 @Aspect 注解

package com.aop.anno;

import org.springframework.stereotype.Component;

// 被增强的类
@Component
public class User {

    void add() {
        System.out.println("增加了一个用户");
    }
}
package com.aop.anno;

import org.springframework.stereotype.Component;

// 增强类
@Component
@Aspect  // 生成代理对象
public class UserProxy {

    @Before("execution(* com.aop.anno.User.add(..))")
    public void before() {
        System.out.println("before...");
    }
    
    // 最终通知,发生异常也执行
    @After("execution(* com.aop.anno.User.add(..))")
    public void after() {
        System.out.println("after...");
    }
    
    // 后置(返回)通知,触发 afterThrowing 不执行
    @AfterReturning("execution(* com.aop.anno.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning...");
    }
    
    @Around("execution(* com.aop.anno.User.add(..))")
    public void around(ProceedingJoinPoint pj) throw Throwable {
        System.out.println("环绕前");
        pj.process();
        // try {
        //    pj.proceed();
        // } catch (Throwable throwable) {
        //    throwable.printStackTrace();
        // }
        System.out.println("环绕后");
        
    }
    
    // 有异常才执行
    @AfterThrowing("execution(* com.aop.anno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing...");
    }
    
}

(3) 在 spring 配置文件中开启生成代理对象

<aop:aspectj-autoproxy/>

(4) 测试

public void text() {
	ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    User user = context.getBean("user", User.class);
    user.add();	  // 先输出 before...,再输出 hhh 
}

执行结果

环绕前
before......
增加了一个用户
环绕后
after........
afterReturning

如果在 add 方法中引发了异常,则执行结果为:

环绕前
before......
after........
afterThrowing......

注意如果在 around 方法中异常没有被抛出而是在方法体内被处理,则不会执行 afterThrowing 方法,此时执行结果为:

环绕前
before......
环绕后
after........
afterReturning

如果将上面的 around 方法注释掉,则引发异常后结果为:

before......
after........
afterThrowing......

相同切入点抽取

@Pointcut("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void pointdemo() {}

@Before("pointdemo()")
public void before() {
    System.out.println("before......");
}

@After("pointdemo()")
public void after() {
    System.out.println("after........");
}

多个增强类的优先级

在增强类上添加 Order(int v) 注解,v 越小优先级越高

完全注解开发

创建配置类,不需要 xml 配置文件

@Configuration
@Component(basePackages = {"com.pojo"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop{}

使用和 IOC 的完全注解开发一样

3. 基于配置文件

<!--增强类-->
<bean id="diy" class="com.kuang.diy.DiyPointCut"/>

<aop:config>
    <!--   切入点   -->
    <aop:pointcut id="point" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
        
    <!--  自定义切面,ref 为增强类   -->
    <aop:aspect ref="diy">
        <!--  通知  -->
        <aop:before method="before111" pointcut-ref="point"/>
        <aop:after method="after111" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>
你只有十分努力,才能看上去毫不费力。
原文地址:https://www.cnblogs.com/214txdy/p/15024961.html