面向切面编程AOP

面向切面编程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):定义切点、各类通知的内容的类

详细解释一下通知的执行顺序:

  1. 假如没有声明环绕通知,默认环绕通知直接执行连接点方法
  2. 假如有声明环绕通知,正常执行的顺序是:首先执行的是环绕通知方法中执行连接点方法proceedingJoinPoint.proceed()这一行前的代码块,然后执行的是前置通知方法,然后执行连接点方法,然后在环绕方法返回后执行后置通知方法,然后执行后置返回通知方法。
  3. 假如有声明环绕通知,抛出异常的执行顺序是:首先执行的是环绕通知方法中执行连接点方法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()..");
    } 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值