简介
AOP(面向切面编程),作为OOP(面向对象编程)的补充,用于处理哪些业务无关的,例如鉴权,日志等公共逻辑,将之抽取封装成一个可重用的模块(切面),减少代码重复,降低耦合,提高系统可维护性。
方式
静态代理
在编译阶段将AspectJ(切面)织入到Java字节码生成AOP代理类
动态代理
在运行阶段在内存中临时生产一个AOP对象且在特定的切点做了增强处理
- JDK动态代理(基于接口)
- CGLIB动态代理(基于继承)
术语
- Advice(通知/增强):切面需要做的具体工作
- Join point(连接点):允许使用通知的地方
- Poincut(切点):执行的位置
- Aspect(切面):通知和切点的结合
- Introduction(引入):切面定义的属性方法应用到目标类
- target(目标):被通知的对象
- Weaving(织入):把切面加入程序代码的过程
常用的增强类型
- 前置通知(before):在某连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非抛出异常)
- 返回后通知(afterReturning):在某连接点正常完成后执行的通知
- 抛出异常后通知(afterThrowing):在方法抛出异常退出时执行的通知
- 后通知(after):当某连接点退出的时候执行的通知
- 环绕通知(around):包围一个连接点通知
执行顺序
- @Order(1):越小越先执行
- around->before->around->after->afterReturning
- 橙色:@Order(1),绿色:@Order(2)
Springboot引入AOP
- 加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 写注解
无
- 写配置
无
Introduction
package com.virgo.user.auto;
import com.virgo.user.service.TestService;
import com.virgo.user.service.TestServiceImpl;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;
/**
* @author zhaozha
* @date 2019/10/24 下午1:00
*/
@Aspect
@Component
public class IntroductionAop {
@DeclareParents(value = "com.virgo.user..service..*", defaultImpl = TestServiceImpl.class)
public TestService testService;
}
package com.virgo.user.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author zhaozha
* @date 2019/10/24 下午1:02
*/
@Service
@Slf4j
public class TestServiceImpl implements TestService{
@Override
public void test() {
log.info("all can use");
}
}
...
// CommonService使用TestService
TestService testService = (TestService)commonServiceImpl;
testService.test();
...
顺序
- 代码
package com.virgo.user.auto;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author zhaozha
* @date 2019/10/24 下午1:29
*/
@Slf4j
@Aspect
@Component
@Order(1)
public class TestAopOrder1 {
@Pointcut("execution(* com.virgo.user.service.*.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void begin() {
log.info("2:{}","before");
}
@After("pointcut()")
public void commit() {
log.info("9:{}","after");
}
@AfterReturning("pointcut()")
public void afterReturning(JoinPoint joinPoint) {
log.info("10:{}","afterReturning");
}
@AfterThrowing("pointcut()")
public void afterThrowing() {
log.info("afterThrowing");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
log.info("1:{}","around");
return joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
throw e;
} finally {
log.info("8:{}","around");
}
}
}
package com.virgo.user.auto;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author zhaozha
* @date 2019/10/24 下午1:11
*/
@Slf4j
@Aspect
@Component
@Order(2)
public class TestAopOrder2 {
@Pointcut("execution(* com.virgo.user.service.*.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void begin() {
log.info("4:{}","before");
}
@After("pointcut()")
public void commit() {
log.info("6:{}","after");
}
@AfterReturning("pointcut()")
public void afterReturning(JoinPoint joinPoint) {
log.info("7:{}","afterReturning");
}
@AfterThrowing("pointcut()")
public void afterThrowing() {
log.info("afterThrowing");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
log.info("3:{}","around");
return joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
throw e;
} finally {
log.info("5:{}","around");
}
}
}
- 效果
注解生效AOP
注解
- @Target:注解的作用目标
- ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
- ElementType.FIELD:允许作用在属性字段上
- ElementType.METHOD:允许作用在方法上
- ElementType.PARAMETER:允许作用在方法参数上
- ElementType.CONSTRUCTOR:允许作用在构造器上
- ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
- ElementType.ANNOTATION_TYPE:允许作用在注解上
- ElementType.PACKAGE:允许作用在包上
- @Retention:注解的生命周期
- RetentionPolicy.SOURCE:Annotations are to be discarded by the compiler.(编译期可见,不会写入 class 文件)
- RetentionPolicy.CLASS:Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time. This is the default behavior.(写入 class 文件,类加载丢弃)
- RetentionPolicy.RUNTIME:Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.(永久保存,可以反射获取)
- @Documented:注解是否应当被包含在 JavaDoc 文档中
- @Inherited:是否允许子类继承该注解
AOP
- 代码
package com.virgo.user.auto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author zhaozha
* @date 2019/10/24 下午1:39
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAop {
String value() default "";
}
package com.virgo.user.auto;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author zhaozha
* @date 2019/10/24 下午1:53
*/
@Slf4j
@Aspect
@Component
@Order(1)
public class TestAnnotationAop {
@Pointcut(value = "@annotation(logAop)", argNames = "logAop")
public void pointcut(LogAop logAop) {
}
@Around(value = "pointcut(logAop)", argNames = "joinPoint,logAop")
public Object around(ProceedingJoinPoint joinPoint,LogAop logAop) throws Throwable {
try {
log.info(logAop.value());
return joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
throw e;
} finally {
log.info("");
}
}
}
- 效果(方法加注释@LogAop("测试Annotation"))
execution
execution(* com.virgo.user.service...(..))
- 第一个*:任意的返回值
- com.virgo.user.service:包名
- .. :当前包及其子包
- *:所有类
- .*(..) 表示任何方法,括号代表参数 .. 表示任意参数