面向切面编程AOP
AOP的理念
AOP(Aspect Orientd Programming)面向切面编程,是指将分散在各业务逻辑代码中相同代码块抽取出来,在业务流程执行需要时,再将这段代码块注入到业务流程的指定位置。AOP的实现关键是AOP代理,AOP代理分为静态代理和动态代理。
AOP静态代理
AOP静态代理的代表是Aspect,与Spring AOP的主要区别是其基于字节码操作。Aspect静态代理在编译阶段对Java的字节码进行操作,将需要的Advice编制进去形成新的字节码,从而形成AOP代理。
AOP动态代理
AOP动态代理的代表是Spring AOP,动态代理不会修改字节码,而是在内存中生成临时的AOP对象增强原对象,并回调原对象方法。动态代理分为JDK动态代理和CGLIB代理。JDK动态代理通过反射来接收被代理的类,且被代理的类必须实现至少一个接口。如果被代理的类没有实现接口,则使用CGLIB代理。
Spring对AOP的支持
- Spring中的Spring AOP是由Spring的IOC容器负责生成和管理的,其依赖关系也是由IOC容器负责管理的。Spring AOP代理以容器中的Bean实例为目标对象,跟据IoC容器提供的依赖关系注入对象。其规则是:默认使用java动态代理创建AOP代理,这样可以为任何接口实例创建代理,假如代理的是类不是接口,Spring会使用CGLIB代理。
- Spring也支持注解@Aspect的静态代理
关于AOP代理的@Aspect实现
- 连接点(join point):具体被拦截的方法
- 切点(point cut):定义适配连接点的条件
- 通知(advice):前置通知(before advice,连接点方法执行前执行的方法)、后置通知(after advice,连接点方法执行后执行的方法)、环绕通知(around advice,在环绕通知中执行连接点方法)、事后返回通知(afterReturning advice,使用环绕通知才有,在环绕方法正常执行后,在后置通知之后执行)、异常通知(afterThrowing advice,使用环绕通知才有,在环绕方法中出现异常后直接执行)
- 目标对象(target):被代理的真实对象
- 织入(weaving):动态代理技术中,按约定讲各类通知织入约定流程中
- 切面(aspect):定义切点、各类通知的内容的类
详细解释一下通知的执行顺序:
- 假如没有声明环绕通知,默认环绕通知直接执行连接点方法
- 假如有声明环绕通知,正常执行的顺序是:首先执行的是环绕通知方法中执行连接点方法proceedingJoinPoint.proceed()这一行前的代码块,然后执行的是前置通知方法,然后执行连接点方法,然后在环绕方法返回后执行后置通知方法,然后执行后置返回通知方法。
- 假如有声明环绕通知,抛出异常的执行顺序是:首先执行的是环绕通知方法中执行连接点方法proceedingJoinPoint.proceed()这一行前的代码块,然后执行的是前置通知方法,然后执行连接点方法,有异常,直接跳去执行后置抛出通知方法。
注:
- 只要是在环绕通知方法中捕捉到异常并抛出异常,就会跳去执行后置抛出通知方法
- 下面代码中@AfterThrowing中throwing指定的异常名称必须和方法参数中的同名异常参数的java类型相同或其子类,才会执行方法。@AfterReturning中returning指定的返回值名称必须和方法参数中的同名返回值参数的java类型相同或其子类,才会执行方法,这个返回值是环绕通知方法的返回值。
/**
* 切面类
*/
@Aspect //注解代表这是切面类
@Component
public class SellerAuthorizeAspect {
//executing表明在执行的时候,拦截正则表达式适配的方法
//* 表示任意返回类型的方法
//路径是指定方法的相对路径
//(..)表示任意参数
//也可以精确匹配@Pointcut("execution(public String org.baeldung.dao.FooDao.findById(Long))")
@Pointcut("execution(public * com.example.testspringboot2.demo20190808.controller.Seller*.*(..))"+
"&& !execution(public * com.example.testspringboot2.demo20190808.controller.SellerUserController.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint){
//获取代理类对象
Object proxyObj = joinPoint.getThis();
//获取代理目标对象
Object targetObj = joinPoint.getTarget();
//获取通知签名
Signature signature = joinPoint.getSignature();
//获取代理类名字
String signatureName = signature.getDeclaringTypeName();
//获取代理类类型对象
Class proxyClass = signature.getDeclaringType();
//获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
//如果要获取Session信息的话,可以这样写:
HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
System.out.println("doBefore()..");
}
@After("pointCut()")
public void doAfter(JoinPoint joinPoint){
System.out.println("doAfter()..");
}
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("before doJoinPoint()..");
//获取连接点方法的方法名
String methodName = proceedingJoinPoint.getSignature().getName();
try{
//执行连接点方法
proceedingJoinPoint.proceed();
System.out.println("doJoinPoint()..");
}catch (Throwable e){
//throw e;
e.printStackTrace();
}
System.out.println("after doJoinPoint()..");
System.out.println("doAround()..");
return "myKey";
}
@AfterThrowing(value = "pointCut()",throwing = "exception")
public void doAfterThrowing(JoinPoint joinPoint, Exception exception){
System.out.println("doAfterThrowing()..");
}
@AfterReturning(value = "pointCut()",returning = "keys")
public void doAfterReturning(JoinPoint joinPoint,Object keys){
System.out.println("doAfterReturning()..keys"+keys);
System.out.println("doAfterReturning()..");
}
}