AOP的简介
Aop 是面向切面编程,AOP的的核心是切面。AOP在不修改源代码本身的前提下使用运行时的动态代理技术对已有的代码逻辑增强。AOP可以实现组件化,可拔插式的功能扩展,通过简单配置即可将功能增强到指定的切入点。可用于权限认证、日志、事务处理。
AOP的底层原理
Aop的底层是由运行时动态代理支撑,在bean初始化流程中,借助BeanPostProcessor(后置处理器)将原始目标对象织入通知,生成代理对象,AOP设计原理是对原有的业务逻辑进行横切增强,使用不同的通知织入方式,她有不同的底层原理支撑(编译期,类加载器,对象创建期)
AOP的使用
创建完成spring 应用后,是无法直接使用Spring的AOP的,需要添加两个依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
其中aspectj
中为我们提供了@Pointcut
,After
, @Aspect
,@Before
,@Around
等注解
而 aspectjweaver
提供了AOP切片执行的主逻辑
AOP的术语
Target
目标对象:被代理的原始对象
proxy
代理对象:目标对象被织入通知后的产物
JoinPoint
连接点:目标对象的所属类中定义的所有方法均为连接点
PointCut
切入点:被切面拦截增强的连接点(切入点一定是连接点,连接点不一定是切入点)
Advice
通知:增强的逻辑代码,即拦截到目标对象的连接点之后要做的事情
在目标方法前执行@Before
在目标方法后执行@After
在目标方法前后执行@Around
在目标方法返回执行 @AfterReturning
在目标方法异常执行 @AfterThrowing
Aspect
切面:切入点+通知
introduction引介:特殊的通知类型,可以在不修改原有类的代码的前提下,在运行期为原始类动态添加新的属性/方法
Aop切面的使用步骤
- 首先在启动类上添加
@EnableAspectJAutoProxy
注解开启AspectJ
- 需要使用
@Component
将当前类注册成spring组件 - 使用
@Aspect
注解将此类声明成为AOP的切面 - 设置增强方法 -->方法上添加通知(advice)例如
@Around()
- 添加切入点@Around()中表达式
@Around("execution(* cn.ton.controller.*.*(..))")
为Pointcut(切入点) - 增强方法的参数传入
ProceedingJoinPoint(连接点)
作为入参,这里Spring会自动将增强方法的上下文封装成该格式传给我们,我们可以通过该参数获取到所想要的目标方法的信息
在启动类上添加@EnableAspectJAutoProxy
注解开启AspectJ
package cn.ton;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@MapperScan("cn.ton.mapper")
@EnableAspectJAutoProxy
public class FeignDemo1Application {
public static void main(String[] args) {
SpringApplication.run(FeignDemo1Application.class, args);
}
}
设置切面
package cn.ton.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class DemoLogger {
@Around("execution(* cn.ton.controller.*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
final long start = System.currentTimeMillis();
final Object proceed = joinPoint.proceed();
log.info("{}()has cost {} ms",joinPoint.getSignature().getName(),System.currentTimeMillis()-start);
return proceed;
}
}
切入点(Pointcut)表达式
AspectJ描述符 | 描述 |
---|---|
args() | 限制连接点匹配参数为指定类型的执行方法 |
@args() | 限制连接点匹配参数由指定注解标注的执行方法 |
execution() | 用于匹配是连接点的执行方法 |
this() | 限制连接点匹配的AOP代理的bean引用为指定类型的类 |
target | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解所标注的类型 |
@annotationn | 限定匹配带有指定注解的连接点 |
以 @Around("execution(* cn.ton.controller.*.*(..))")
为例,当使用了execution来描述切入点,这里表示执行在cn.ton.controller
包(不包含自包)中任何Spring Compoent
对应的方法都会作为切入点实现增强
execution(* cn.ton.controller..*.*(..)) //.. 还可以表示任意包,controller 包及其子包下的任意类就都会被扫到