• SpringAop


    为什么使用AOP?

    1:代码混乱:越来越多的非业务需求(比如日志和验证等)加入后,原有的业务方法急剧膨胀,每个方法在处理自己的业务逻辑的同时还要兼顾其它多个关注点。
    2:代码分散:假如就单单的满足加入日志需求,就不得不在多个模块中重复相同的日志代码,如果日志需求发送了改变,还要去修改所有的模块。

    解决方案:

    现在有一个业务类:

    package com.shiro.springbootshiro.aop;
    
    /**
     * 作用:spring  aop 中的业务类
     */
    public interface ArithmeticCalculator {
    
        int add(int i,int j);
    }

    实现这个业务类:

    package com.shiro.springbootshiro.aop;
    
    /**
     * 作用:实现这个业务类
     */
    public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
        @Override
        public int add(int i, int j) {
            return i+j;
        }
    }

    给这个业务加上日志信息:使用动态代理

    package com.shiro.springbootshiro.aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Arrays;
    
    /**
     * 作用:给spring aop中业务添加日志需求,使用动态代理
     */
    public class ArithmeticCalculatorLoggingProxy {
        //要代理的对象
        private ArithmeticCalculator target;
    
        public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
            this.target = target;
        }
        public ArithmeticCalculator getLoggingProxy(){
            ArithmeticCalculator proxy = null;
            //代理对象由哪一个类加载器负责加载。
            ClassLoader loader = target.getClass().getClassLoader();
            //代理对象的类型,即其中有哪些方法。
            Class[] interfaces = new Class[]{ArithmeticCalculator.class};
            //当调用代理对象中的方法的时候,要先执行什么代码。
            InvocationHandler h = new InvocationHandler() {
    
                /**
                 *
                 * @param proxy : 正在返回的那个代理对象,一般情况下,在invoke都不使用该对象。
                 * @param method : 正在调用的那个方法
                 * @param args :调用方法时,传入的参数
                 * @return
                 * @throws Throwable
                 */
    
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String name = method.getName();
                    //在执行方法前,先打印出方法的名称
                    System.out.println("先打印出方法的名字:"+name+"开始:"+ Arrays.asList(args));
                    //执行方法
                    Object result = method.invoke(target, args);
                    //执行方法后,打印日志,输出结果
                    System.out.println("打印"+name+"后的结果:"+result);
                    return result;
                }
            };
            proxy= (ArithmeticCalculator) Proxy.newProxyInstance(loader,interfaces,h);
            return proxy;
        }
    }

    测试:

    package com.shiro.springbootshiro.aop;
    
    /**
     * 作用:实现spring aop中的业务代码
     */
    public class Main {
        public static void main(String[] args) {
            //目标方法
            ArithmeticCalculator target = new ArithmeticCalculatorImpl();
            //代理类
            ArithmeticCalculator proxy= new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
            int i = proxy.add(3, 4);
            System.out.println(i);
        }
    }

    输出:

    使用AOP

    aop是面向切面编程。切面模块化横切关注点就是要添加的日志功能。

    好处:每个业务的逻辑不用修改。便于维护和升级。业务模块更加的简洁。

    验证是一个切面,日志是一个切面。

    概念:

    切面(Aspect):横切关注点。就是要添加的功能验证,日志。

    通知(Advice):切面必须要完成的工作。

    目标(Target):被通知的对象,就是原有的业务。

    代理(Proxy):向目标对象应用通知之后创建的对象。

    连接点(Joinpoint):程序执行的某个特点位置。比较类的某个方法执行前,或者执行后。比如上面的add方法,就是连接点。

    切点(pointcut):每个类拥有多个连接点。AOP通过特点的切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点就是查询条件。一个切点可以匹配多个连接点。使用的是类和方法作为连接点的查询条件。

    在springboot中使用AOP

    1:加入依赖:

    <!--使用spring的AOP-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>

    2:写切面类

    package com.example.aop.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    /**
     * 作用: 测试spring 的AOP
     */
    @Aspect     //说明这是一个切面类
    @Component  // 添加到IOC容器中
    public class UserServiceAspect {
    
        private static final Logger log = LoggerFactory.getLogger(UserServiceAspect.class);
    
        /*@PointCut注解表示表示横切点,哪些方法需要被横切*/
        /*切点表达式*/
        @Pointcut("execution(public * com.example.aop.service.*.*(..))")
        /*切点签名*/
        public void print() {
        }
    
        @Before("print()")
        public void before(JoinPoint joinPoint){
            log.info("前置切面before……");
            Object[] args = joinPoint.getArgs();
            String methodName = joinPoint.getSignature().getName();
            System.out.println("参数:"+args);
            System.out.println("方法名:"+methodName);
        }
    }
  • 相关阅读:
    拉格朗日插值模板题 luoguP4871
    FFT P3803 [模板]多项式乘法
    codeforces #629 F
    codeforces #629 E-Tree Queries
    数学—线性基
    codeforces #629 D.Carousel
    luogu P1447_能量采集 (莫比乌斯反演)
    luogu P2257- YY的GCD (莫比乌斯反演)
    luogu P2522-Problem b (莫比乌斯反演)
    luogu P3455 (莫比乌斯反演)
  • 原文地址:https://www.cnblogs.com/bulrush/p/10700987.html
Copyright © 2020-2023  润新知