1. spring 中的面向切面编程

AOP (aspect切面 oriented 面向 programming 编程)

切面 aspect = 通知 adivce + 切点 pointcut

通知:是一个方法,其中包含了重复的逻辑(计时,事务)
切点:是一种匹配条件, 与条件相符合的目标方法,才会应用通知方法

代理:proxy
目标:target

2. 使用步骤

  1. 添加 maven 依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.22.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.13</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>
  1. 编写切面类
@Aspect
public class 切面类 {

    @Around("切点表达式") // 切点表达式用来匹配目标和通知
    public Object 通知方法(ProceedingJoinPoint pjp) {
        // 写一些重复的逻辑, 计时, 事务控制,权限控制
        // 调用目标 , 其中 result 是目标方法返回的结果
        Object result = pjp.proceed();
        return result;
    }

}
  1. 把切面类和目标类都交给 spring 容器管理
    可以用 <context:component-scan> 配合 @Component @Service 等注解扫描
    也可以使用 <bean>

  2. 使用切面
    从容器 getBean 根据接口获取,容器返回代理对象,代理对象中进行切点匹配,匹配到了执行通知,通知内部间接调用目标

// 1. 偷梁换柱, spring 容器返回的是一个代理对象
UserService userService = context.getBean(UserService.class);

System.out.println(userService.getClass());

// 2. 调用的是代理对象的 a 方法
userService.a();

3. 切点表达式

3.1 within 匹配类中的所有方法(粒度粗)

语法:

within(包名.类名)

UserServiceTarget
insert
update
OrderServiceTarget
a
b
c
within(service.*ServiceTarget) // 其中 * 表示所有

3.2 execution 表达式 可以精确到类中的每个方法 (粒度细)

语法:

execution(访问修饰符 返回值 包名.类名.方法名(参数信息))

返回值的位置写 * 表示返回值得类型是任意的
类名 写 * 代表任意的类
方法名中出现 *, 例如下面的表达式会匹配所有以 find 开头的方法

@Around("execution(public * service.UserService.*a())")

但是 * 写在参数位置,只能匹配一个任意类型的参数

@Around("execution(public * service.UserService.a(*))")

a(int) 匹配
a(long) 匹配

a(int, int) 不匹配
a() 不匹配

… 写在参数位置, 匹配任意个数和任意类型的参数

@Around("execution(public * service.UserService.a(..))")

a(int) 匹配
a(long) 匹配
a(int, int) 匹配
a() 匹配

3.3 @annotation

语句:

@annotation(包名.注解名)

看执行方法上有没有这个注解,有的话就算匹配

4. 通知类型

@Around - 环绕通知 (功能最强大, 可以自由定义通知代码的位置)
@Before - 前置通知
@After - 后置通知
@AfterReturning - 正常返回通知
@AfterThrowing - 异常返回通知

这几种通知实际上对应的就是通知代码相对于目标代码的位置
@Before - 前置通知
try {
目标方法
@AfterReturning - 正常返回通知
} catch(Exception e) {
@AfterThrowing - 异常返回通知
} finally {
@After - 后置通知
}

5. 面向切面的应用

  1. spring 中的事务管理
@Transactional
public void deleteByIds(List<Integer> ids) {

}
// 代理对象.deleteByIds, 代理对象中与@Transactional进行匹配,发现方法上有这个注解,就调用事务通知, 继续调用目标方法
// TransactionInterceptor(跟事务相关的通知代码)
  1. 统一的权限控制
  2. 缓存的控制
  3. 统一的日志记录

6. spring 中如果目标类没有实现接口

  • 如果目标对象实现了接口:底层 spring 会调用 jdk的动态代理技术 来生成代理, 要求代理对象和目标对象实现共同的接口
  • 如果目标对象没有实现接口:底层 spring 会调用 cglib 代理技术 来生成代理,生成了一个子类作为代理对象

7. spring 中的单元测试