在springBoot中使用AOP需要引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
主要注解
切面表达式
- *:匹配任意数量的字符
- ..:一般用于匹配任意数量的子包或通配符
- +:匹配指定类及其子类
designators里面有很多方式,下面图片基本罗列了:
下面一一详细介绍:
匹配包/类型 within
如:
//匹配ProductService类里的所有方法
@Pointcut("within(com.example.demo.MyAOp.service.ProductService)")
public void adminonly2(){}
//匹配com.example.demo.MyAOp.service包及子包下所有方法
@Pointcut("within(com.example.demo.MyAOp.service..*)")
public void adminonly3(){}
匹配对象
- 注:
参数匹配
注:图中标红的不可使用(会报错,不知道原因),需要缩小范围,如
//匹配ProductService中只有一个Long参数的方法
@Pointcut("args(Long)&&within(com.example.demo.MyAOp.service.ProductService)")
public void adminonly4(){}
匹配注解
- -
//匹配标有MyAnnotation的类下的方法(不包括子孙类)/*注:缩小范围加上其它限制条件,不然可能报错,不知道为什么*/
@Pointcut("@target(com.example.demo.MyAOp.myAnnocation.MyAnnotation)&&within(com.example.demo.MyAOp.service..*)")
public void adminonly(){}
//匹配标有MyAnnotation的类下的方法(包括子孙类)
@Pointcut("@within(com.example.demo.MyAOp.myAnnocation.MyAnnotation)")
public void adminonly2(){}
//匹配标有MyAnnotation的方法
@Pointcut("@annotation(com.example.demo.MyAOp.myAnnocation.MyAnnotation)")
public void adminonly3(){}
//匹配传入参数有MyAnnotation注解的方法
@Pointcut("@args(com.example.demo.MyAOp.myAnnocation.MyAnnotation)")
public void adminonly4(){}
execution表达式
- -
//匹配方法修饰符是public 返回值是*(任意返回值),com.example.demo.MyAOp.service包下
//以Service结尾的类里的参数为任意的并且抛出Exception异常的delete方法
@Pointcut("execution(public * com.example.demo.MyAOp.service.*Service.delete(..) throws Exception)")
public void adminonly88(){}
//匹配方法修饰符是public 返回值是*(任意返回值),com.example.demo.MyAOp.service包下
//以Service结尾的类里的参数为任意的delete方法
@Pointcut("execution(public * com.example.demo.MyAOp.service.*Service.delete(..))")
public void adminonly88(){}
advice注解
- @Before
@Before("adminonly88()&&args(aa)")
public void ceheck(Object aa){
System.out.println("进入了AOP-------------------执行Before,接收参数= "+ aa);
}
输出结果:进入了AOP-------------------执行Before,接收参数= 1
这是一个删除的方法
- @AfterReturning
@AfterReturning(value = "adminonly88()",returning = "aa")
public void ceheck1(Object aa){
System.out.println("进入了AOP-------------------执行After,返回结果="+ aa);
}
输出结果:这是一个插入的方法
进入了AOP-------------------执行After,返回结果=返回值
- @Around
@Around("adminonly88()&&args(aa)")
public void ceheck2(ProceedingJoinPoint joinPoint,Object aa){
Object result = null;
System.out.println("进入了AOP-------------------执行AfterReturning,接收参数= :"+ aa );
try {
result =joinPoint.proceed(joinPoint.getArgs());
System.out.println("进入了AOP-------------------执行AfterReturning222 :" );
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("进入了AOP-------------------执行AfterReturning333,返回参数= "+result );
}
输出结果:进入了AOP-------------------执行AfterReturning,接收参数= :1
是一个删除的方法
进入了AOP-------------------执行AfterReturning222 :
进入了AOP-------------------执行AfterReturning333,返回参数= 返回值
实现原理
SpringAOP的实现是通过代理模式实现,通过jdk和Cglib的动态代理实现。
如何强制使用cglib代理呢?
注意事项
如何理解无法拦截内部方法调用???看下面例子
如果直接调用getMenuList()方法,@Cacheable有效;若通过getRecommends()去调用的话,则@Cacheable无效。这是为什么呢?
- this是没有经过AOP代理的bean, 而AOP的实现就是一个代理的增强。
- 解决办法:改为走代理bean的方法。下面是具体操作:
ApplicationContextHolder类的代码:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Created by king on 2017-09-01
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext ctx;
public static ApplicationContext getContext() {
return ctx;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
}