AOP(Aspect Orient Programming),即面向切面编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。
1.通知(有的地方叫增强)(Advice)
需要完成的工作叫做通知,就是你写的业务逻辑中需要比如事务、日志等
2.连接点(Join point)
就是spring中允许使用通知的地方
3.切点(Poincut)
筛选出的连接点,一个类中的所有方法都是连接点,但又不全需要,会筛选出某些作为连接点做为切点。
4.切面(Aspect)
通知和切点的结合,通知和切点共同定义了切面的全部内容
5.目标(target)
需要加入额外代码的对象
6.织入(Weaving)
把切面加入程序代码的过程。切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入
编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器
类加载期:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
运行期:切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的
新建spring boot 项目
1.pom.xml 引入如下依赖:
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-aop
2.AopService.java
package com.vincent;
import org.springframework.stereotype.Service;
@Service
public class AopService {
public void test() {
System.out.println("AopService test----------");
}
}
3.AopAspect.java
package com.vincent;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AopAspect {
@Pointcut(value = "execution(* com.vincent.*.*(..))")
public void pointcut() {
}
@Before(value = "pointcut()")
public void before(JoinPoint jp) {
System.out.println("before--------------");
}
@AfterReturning(value = "pointcut()")
public void afterReturn(JoinPoint jp) {
System.out.println("afterReturn--------------");
}
@AfterThrowing(value = "pointcut()",throwing = "ex")
public void afterThrowing(JoinPoint jp,Throwable ex) {
System.out.println("afterThrowing--------------" + ex);
}
@After(value = "pointcut()")
public void after(JoinPoint jp) {
System.out.println("after--------------");
}
@Around(value = "pointcut()")
public Object around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("around--------------");
Object rst = null;
rst = jp.proceed();
return rst;
}
}
4.Ch4Application.java
package com.vincent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Ch4Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Ch4Application.class, args);
ctx.getBean(AopService.class).test();
}
}
1.运行Ch4Application.java
2.AopService.java test 方法抛出异常
public void test() {
int x = 1/ 0;
System.out.println("AopService test----------");
}
1.@Aspect 注解表明这是一个切面类
2.@Pointcut 注解表明是一个切点定义
3.@Before 表明该方法是一个前置通知,该方法在目标方法执行之前执行,通过JoinPoint 参数可以获取目标方法的方法名、修饰符等信息
4.@After 注解使一个后置通知,该方法在目标方法执行之后执行
5.@AfterReturning 注解表示返回通知,该方法可以获取目标方法的返回值,@AfterReturning 注解的returning 参数是指返回值的变量名,对应方法的参数
6.@AfterThrowing 注解表示异常通知,当目标方法发送异常时该方法会被调用,异常类型为Exception 表示所有异常都会被该方法处理
7.@Around 注解表示环绕通知,可实现前置通知、后置通知、异常通知、返回通知,通过调用方法参数ProceedingJoinPoing 对象的proceed 方法是目标方法继续执行
8.@Pointcut也可以使用注解定义切点,在需要被代理的方法上添加注解即可
注解定义:
package com.vincent.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintLog {
}
切面定义:
package com.vincent.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
@Pointcut("@annotation(com.vincent.aspect.PrintLog)")
public void logPointCut(){}
@Around("logPointCut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable{
Object target = joinPoint.getTarget();
Object args = joinPoint.getArgs();
Object rst = joinPoint.proceed();
return rst;
}
}
注解方式将带来更大的灵活性、细粒度,在需要被代理的方法上添加注解即可。springboot 中很多都使用注解实现AOP如:@Cacheable等