AOP
1.开启aop的步骤演示:
1.开启AOP配置,使用注解 @EnableAspectJAutoProxy
@Configuration
@ComponentScan(basePackages = {"com.yyq"})
@EnableAspectJAutoProxy
public class SpringConfig {
}
2.被增强类不需要做任何操作(因为AOP的优点之一就是不侵犯原代码)
3.增强方法:
1.创建一个类,类上使用注解@Aspect,说明是增强类
2.类中创建方法,方法上使用注解@Before,@After…(如下),说明是增强方法
3.在Before的value参数中写明切点表达式,切点表达式写法:
规定: execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
例如: execution(* com.yyq.service.impl.UserserviceImpl.say(…))
说明: * 表示任意的权限修饰符,返回类型给个空格即可. 类全路径,大概类比一下,下面有更详细举例.
切点表达式写法:
由于Aspect本身并不属于spring,他是一个独立的开源项目, 所以他的语法稍显怪异, 但咱们只管应用于实际,根据框架规则去走即可.举如下几个常用例子可供参考:
任意公共方法的执行:execution(public * *(..))
任何一个名字以“set”开始的方法的执行:execution(* set*(..))
UserService接口定义的任意方法的执行:execution(* com.yyq.service.UserService.*(..))
在service包中定义的任意方法的执行:execution(* com.yyq.service.*.*(..))
在service包或其子包中定义的任意方法的执行:execution(* com.yyq.service..*.*(..))
@Component
@Aspect()
public class Log {
@Before(value = "execution(* com.yyq.service.impl.UserserviceImpl.say(..))")
public void strongBefore(){
System.out.println("事前加强");
}
}
五个注解:
- @Before 前置通知,在被增强方法之前执行
- @After 后置(最终)通知,在被增强方法执行完毕后,return之前执行(类似finally,抛异常也会执行)
- @AfterReturning 返回通知,在被增强方法返回值之后,return之后执行.(抛异常就不会继续执行)
- @Around 环绕通知,可自行配置, 在方法参数出引入point***
- @AfterThrowing 异常通知, 在方法抛异常之后执行
其中较特殊的是@Around环绕通知,因为他需要在方法的前后分别进行增强,还得执行原方法逻辑,下面举例说明
@Component
@Aspect()
public class Log {
@Around(value = "execution(* com.yyq.service.impl..*(..))")
public void strongAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前置通知............");
MethodSignature signature = (MethodSignature)joinPoint.getSignature();//拓展:代表切面方法,强转后功能更多
Method method = signature.getMethod();//拓展:可获取切面的方法Method,反射里的知识.通过方法可以获取注解,参数,返回值类型等等信息
Object target = joinPoint.getTarget();//拓展:可获取切面对应的类的对象实例
//拓展:以上三行,获取到了Method以及对象实例,根据反射里的相关知识,此处可以做其他的一些拓展业务,自行脑补
joinPoint.proceed(); // 此方法是核心!!! 代表原方法执行,必须要写,否则原方法不会执行
System.out.println("环绕后置通知..................");
}
}
补充小知识:
1.增强方法增加参数,可通过反射获取更多原方法信息,便于处理其他业务逻辑(如自定义注解相关)
如上Around的写法,可看到方法增加了一个参数ProceedingJoinPoint,但是此参数只能在@Around里使用,那其他类型的增强方法呢?可以使用JoinPoint,此接口不支持proceed()方法,即不可自执行原方法,但强转后也可获取反射对象Method以及原方法所在的实例,具体用处自行脑补
@AfterReturning(value = "execution(* com.yyq.service.impl..*(..))")
public void stringAfter(JoinPoint joinPoint) {
System.out.println(joinPoint);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//拓展:代表切面方法,强转后功能更多
Method method = signature.getMethod();//拓展:可获取切面的方法Method,反射里的知识.通过方法可以获取注解,参数,返回值类型等等信息
Object target = joinPoint.getTarget();//拓展:可获取切面对应的类的对象实例
//拓展:以上三行,获取到了Method以及对象实例,根据反射里的相关知识,此处可以做其他的一些拓展业务,自行脑补
//此接口无proceed()方法.
System.out.println("后置通知");
}
2.相同切入点表达式抽取(简化书写)
同一个切面在每个注解里都写一遍比较混乱,不利于代码统一管理,此处有一个简化写法 @PointCut,示例如下:
@Component
@Aspect()
public class Log {
@Pointcut(value = "execution(* com.yyq.service.impl.UserserviceImpl.say())")
public void save(){
}
@Before(value = "save()")
public void strongBefore(){
System.out.println("事前加强");
}
@After(value = "save()")
public void strongAfter(){
System.out.println("事后加强");
}
}
3.多个增强类增强同一个方法, 优先级写法
在增强类上增加@Order(),写上数字,值越小, 优先级越高
@Component
@Aspect()
@Order(1)
public class Log {
@Pointcut(value = "execution(* com.yyq.service.impl.UserserviceImpl.say())")
public void save(){
}
@Before(value = "save()")
public void strongBefore(){
System.out.println("事前加强");
}
}