美文网首页
java超快速入门(五):面向切面编程(AOP)

java超快速入门(五):面向切面编程(AOP)

作者: 自我De救赎 | 来源:发表于2021-09-29 08:40 被阅读0次

    AOP原理

    AOP是使用代理模式,在指定方法执行前、执行后等各个阶段进行自定义处理
    下面所讲到的代理模式中,一共有两种类型,一种是基于接口的代理,一种是基于继承的代理

    • 基于接口的代理模式,需要指定目标类的接口,java类加载器将根据给定的接口生成一个动态代理类,并且在执行目标方法时,执行指定方法(如invoke方法)。因为动态代理类是由接口生成,因此如果通过代理类执行接口不存在但目标类实现了的方法,将会编译不通过,报错
    • 基于继承的代理模式,目标类不需要有接口,类加载器将动态生成继承自目标类的代理类,因此代理类拥有目标类的所有方法,cglib就是基于这种模式

    AOP术语

    • target目标类:需要被代理的类。例如:UserService
    • Joinpoint连接点:所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
    • PointCut切入点:已经被增强的连接点。例如:addUser()
    • advice通知/增强,增强代码。例如:after、before
    • Weaving织入:是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
    • proxy代理类
    • Aspect切面:是切入点pointcut和通知advice的结合

    实现AOP模式

    手动代理模式

    使用Proxy.newProxyInstance()方法进行代理

    Target target = new Target()
    TargetInterface proxy = (TargetInterface)Proxy.newProxyInstance(Target.class.getClassLoader(), new Class[]{TargetInterface.class}, new InvocationHandler() {
        public Object before(Object proxy, Method method, Object[] args){
            System.out.println("执行" + method.getName() + "\r\n");
            return proxy;
        }
        public Object after(Object proxy, Method method, Object[] args, Object result){
            System.out.println("执行结果" + result.toString() + "\r\n");
            return result;
        }
        public Object exceptionHandler(Object proxy, Method method, Object[] args){
            return null;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try{
                proxy = this.before(proxy, method, args);
                Object result = method.invoke(target, args);
                result = this.after(proxy, method, args, result);
                return result;
            }catch (Throwable throwable){
                return this.exceptionHandler(proxy, method, args)
            }
        }
    });
    

    cglib

    cglib通过动态创建目标类的子类作为代理类来实现代理模式,cglib代理的好处是,目标类不需要实现任何接口,因为代理类是继承自目标类,因此有目标类的所有方法

    先定义一个切面类

    /** MyAspect.java **/
    package com.sinbxeunha.josechan.aspect;
    
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class MyAspect {
        public Object before(Object proxy, Method method, Object[] args, MethodProxy methodProxy){
            System.out.println("执行" + method.getName() + "\r\n");
            return null;
        }
    
        public Object after(Object proxy, Method method, Object[] args, MethodProxy methodProxy, Object result){
            assert result != null && result.toString() != null;
            System.out.println("执行结果:" + result.toString() + "\r\n");
            return result;
        }
    
        public Object exceptionHandler(Object proxy, Method method, Object[] args, MethodProxy methodProxy, Throwable throwable){
            System.out.println("结果异常:" + throwable.getMessage() + "\r\n");
            return null;
        }
    }
    
    

    使用cglib进行代理

    //cglib代理
    Target target = new Target();
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(target.getClass());
    MyAspect aspect = new MyAspect();
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            try{
                //前方法
                proxy = aspect.before(proxy, method, args, methodProxy);
                //下面两句是一样的
                Object result = method.invoke(target, args);
    //                    Object result = Proxy.invokeSuper(target, args);
                //后方法
                return result = aspect.after(proxy, method, args, methodProxy, result);
            }catch (Throwable throwable){
                return aspect.exceptionHandler(proxy, method, args, methodProxy, throwable);
            }
        }
    });
    

    aopalliance

    aop联盟定义了一系列规范,实现这些接口就能实现切面编程
    同样的我们实现切面类

    /*** MyAspect.java ***/
    package com.sinbxeunha.josechan.aspect;
    
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyAspect {
        public Object before(MethodInvocation invocation){
            System.out.println("执行" + invocation.getMethod().getName() + "\r\n");
            return null;
        }
    
        public Object after(Object result){
            System.out.println("执行结果:" + result.toString() + "\r\n");
            return result;
        }
    
        public Object exceptionHandler(Throwable throwable){
            System.out.println("结果异常:" + throwable.getMessage() + "\r\n");
            return null;
        }
    }
    

    定义通知,实现Advice接口,这里我们实现MethodInterceptor接口,该接口底层继承自Advice接口

    /**MyInterceptor.java**/
    package com.sinbxeunha.josechan.aspect;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.stereotype.Component;
    
    public class MyInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            MyAspect aspect = new MyAspect();
    
            try{
                aspect.before(invocation);
                Object result = invocation.proceed();
                assert result != null;
                return aspect.after(result);
            }catch (Throwable throwable){
                return aspect.exceptionHandler(throwable);
            }
        }
    }
    

    创建代理对象

    //工厂bean
    ProxyFactoryBean bean = new ProxyFactoryBean();
    //配置
    AdvisedSupport config = new AdvisedSupport();
    //目标类
    Target target = new Target();
    //通知
    MyInterceptor interception = new MyInterceptor();
    //设置目标类的接口
    config.setInterfaces(new Class[]{TargetInterface.class});
    //设置目标
    config.setTarget(target);
    //设置通知
    config.addAdvice(interception);
    //获得代理对象,链式调用 bean.获得工厂.配置工厂.获得代理对象
    TargetInterface proxy = (TargetInterface) bean.getAopProxyFactory().createAopProxy(config).getProxy();
    

    AspectJ

    • 切入点表达式
      execution(<方法的返回类型> <完整类名>.<方法名>(...<参数类型>))

    • aspectJ定义了一系列通知,并通过注解的方式指定通知的类型

    • 通知类型如下:

      • Before 前置通知,在目标方法执行前执行
      • AfterReturning 后置通知,在目标方法执行后执行
      • AfterThrowing 抛出异常通知,在目标方法抛出异常时执行
      • After 最终通知,所有流程走完后执行该通知
      • Around 环绕通知,类似于前面MyInterceptor类的invoke方法,需要手动写整个增强过程
    • 当指定了目标类的接口时,aspectj会通过接口实现代理类,否则将采用cglib的方式,继承目标类实现代理,当然,你也可以通过配置,让aspectj强制使用cglib的模式

    首先先定义一个切面类

    /** AspectJAspect.java **/
    package com.sinbxeunha.josechan.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.aspectj.weaver.tools.JoinPointMatch;
    import org.springframework.cglib.proxy.MethodProxy;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    //@Component
    //@EnableAspectJAutoProxy
    @Aspect
    public class AspectJAspect {
    
        @Pointcut("execution(* com.sinbxeunha.josechan.service.*.*(..))")
        private void pointCut() {
        }
    
        //注解前置通知
        @Before("pointCut()")
        public Object before(JoinPoint joinPoint) {
            System.out.println("执行" + joinPoint.getSignature().toString() + "\r\n");
            return null;
        }
    
        //注解后置通知
        @AfterReturning(value = "pointCut()", returning = "result")
        public Object after(JoinPoint joinPoint, Object result) {
            assert result != null && result.toString() != null;
            System.out.println("执行结果:" + result.toString() + "\r\n");
            return result;
        }
    
        //注解抛出异常通知
        @AfterThrowing(value = "pointCut()", throwing = "throwable")
        public Object exceptionHandler(JoinPoint joinPoint, Throwable throwable) {
            System.out.println("结果异常:" + throwable.getMessage() + "\r\n");
            return null;
        }
    
        //注解最终通知
        @After("pointCut()")
        public Object afterAll(JoinPoint joinPoint) {
            return null;
        }
    
        //注解环绕通知,这个通知的增强内容相当于上面所有通知的并集
    //    @Around("pointCut()")
        public Object around(ProceedingJoinPoint joinPoint) {
            try {
                before(joinPoint);
                Object result = joinPoint.proceed();
                assert result != null;
                return after(joinPoint, result);
            } catch (Throwable throwable) {
                return exceptionHandler(joinPoint, throwable);
            } finally {
                afterAll(joinPoint);
            }
        }
    }
    

    接下来通过工厂构造代理

    // AspectJ
    //目标类
    Target target = new Target();
    //切面类
    AspectJAspect aspect = new AspectJAspect();
    //代理工厂
    AspectJProxyFactory factory = new AspectJProxyFactory();
    factory.setTarget(target);
    factory.setInterfaces(new Class[]{TargetInterface.class});
    factory.addAspect(aspect);
    //        //强制使用cglib
    //        factory.setProxyTargetClass(true);
    TargetInterface proxy = factory.getProxy();
    

    通过spring容器实现自动AspectJ代理

    要通过spring容器实现自动代理,需要将切面类解析道容器中,并且开启自动代理选项
    只需要在切面类上增加两行注解:

    /** AspectJAspect.java **/
    //...省略
    
    @Component
    @EnableAspectJAutoProxy
    ////强制使用cglib
    //@EnableAspectJAutoProxy(proxyTargetClass = true)
    @Aspect
    public class AspectJAspect {
    //...省略
    }
    

    并且通过容器去获取目标类

    //将目标类解析到容器中
    //...省略
    @Service
    public class Target implements TargetInterface {
    //...省略
    
    //通过容器获取目标类
    /** @var ApplicationContext applicationContext 容器实例 **/
    Target proxy = applicationContext.getBean(Target.class);
    

    相关文章

      网友评论

          本文标题:java超快速入门(五):面向切面编程(AOP)

          本文链接:https://www.haomeiwen.com/subject/gzyqcltx.html