zoukankan      html  css  js  c++  java
  • Spring(十七):Spring AOP(一):简介

    背景:

    需求:

    给一个计算器计算函数执行前后添加日志。

    实现:

    1)直接在函数中修改代码;

    IArithmeticCalculator.java接口类

    package com.dx.spring.beans.aop;
    
    public interface IArithmeticCalculator {
        // Addition, subtraction, multiplication, and division
        int add(int i, int j);
    
        int sub(int i, int j);
    
        int multi(int i, int j);
    
        int div(int i, int j);
    }

    实现类中添加日志:

    package com.dx.spring.beans.aop;
    
    public class ArithmeticCalculatorImpl implements IArithmeticCalculator {
        @Override
        public int add(int i, int j) {
            System.out.println("method add before");
            int result = i + j;
            System.out.println("method add after");
            return result;
        }
    
        @Override
        public int sub(int i, int j) {
            System.out.println("method sub before");
            int result = i - j;
            System.out.println("method sub after");
            return result;
        }
    
        @Override
        public int multi(int i, int j) {
            System.out.println("method multi before");
            int result = i * j;
            System.out.println("method multi after");
            return result;
        }
    
        @Override
        public int div(int i, int j) {
            System.out.println("method div before");
            int result = i / j;
            System.out.println("method div after");
            return result;
        }
    }
    View Code

    2)Java动态代理,实现参考《Java中动态代理方式:》;

    ArithmeticCalculatorImpl.Java实现:

    package com.dx.spring.beans.aop;
    
    public class ArithmeticCalculatorImpl implements IArithmeticCalculator {
        @Override
        public int add(int i, int j) {
            int result = i + j;
            return result;
        }
    
        @Override
        public int sub(int i, int j) {
            int result = i - j;
            return result;
        }
    
        @Override
        public int multi(int i, int j) {
            int result = i * j;
            return result;
        }
    
        @Override
        public int div(int i, int j) {
            int result = i / j;
            return result;
        }
    }
    View Code

    ProxyFactory.java动态代理工厂:

    package com.dx.spring.beans.aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyFactory {
        private Object target=null;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
        
         /**
         * 给目标对象生成代理对象
         */
        public Object getProxyInstance() {
            ClassLoader classLoader = target.getClass().getClassLoader();
            Class<?>[] interfaces = target.getClass().getInterfaces();
    
            InvocationHandler invocationHandler = new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    before(method);
                    // 执行目标对象方法
                    Object returnValue = method.invoke(target, args);
                    after(method);
    
                    return returnValue;
                }
            };
    
            return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        }
    
        private void before(Method method) {
            System.out.println("before " + method);
        }
    
        private void after(Method method) {
            System.out.println("after " + method);
        }
        
    }
    View Code

    调用测试Client.java:

    package com.dx.spring.beans.aop;
    
    public class Client {
        public static void main(String[] args) {
            IArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
            // int result=arithmeticCalculator.add(1, 2);
            // System.out.println(result);
            IArithmeticCalculator arithmeticCalculatorProxyFactory = (IArithmeticCalculator) new ProxyFactory(arithmeticCalculator).getProxyInstance();
            int result = arithmeticCalculatorProxyFactory.add(1, 2);
            System.out.println(result);
        }
    }

    3)Spring AOP实现。

    下边会举例,这里暂时不举例。

    AOP:

    AOP简介:

    1)AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统OOP(Object-Oriented Programming,面向对象编程)的补充。

    2)AOP的主要编程对象是切面(aspect),而切面模块化横切关注点。

    3)在应用AOP编程时,任然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。

    4)AOP的好处:

    ---- 每个事物逻辑位于一个位置,代码不分散,便于维护和升级;

    ---- 业务模块更简洁,只包含核心业务代码。

    AOP术语:

    1)切面(aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象;

    2)通知(advice):切面必须要完成的工作;

    3)目标(target):被通知的对象;

    4)连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。

    连接点由两个信息确定:方法表示的程序执行点、相对点表示的方位。

    例如:ArithmethicCalculator#add()方法执行前的连接点执行点为ArithmethicCalculator#add();方位为该方法执行前的位置。

    5)切点(pointcut):每个类都拥有多个连接点:例如ArithmethicCalculator的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。

    AOP通过切点位置到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。

    Spring AOP

    1)AspectJ:java社区里最完整最流行的AOP框架。在Spring2.0以上版本中,可以使用AspectJ注解基于XML配置的AOP。

    备注:先学习基于AspectJ注解的方式,接着再学习基于XML配置。

    2) 在Spring中启动AspectJ注解支持:

    1. 要在Spring应用中使用AspectJ注解,必须在ClassPath下包含AspectJ类库:aopalliance.jar、aspectj.wearver.jar和spring-aspects.jar;
    2. 将aop schema添加到<beans>根元素中;
    3. 要在Spring IOC容器中启用AspectJ注解支持,只要在Bean配置文件中定义一个空的xml元素<aop:aspectj-auotproxy>;
    4. 当Spring IOC容器侦测到Bean配置文件中的<aop:aspectj-autoproxy>元素是,会自动为与aspectj切面匹配的bean创建代理。

    Spring AOP实现需求:

    第一步:导入spring aop依赖包&额外依赖包(aopalliance.jar、asm.jar、aspectjrt.jar、aspectjweaver.jar);


    Aop额外依赖包:aopalliance.jar、asm.jar、aspectjrt.jar、aspectjweaver.jar下载:链接:https://pan.baidu.com/s/1SjYtrfrUwPIZXikr4guXkg 密码:lowr

    第二步:添加spring-aop.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
        <!-- 配置自动扫描的包 -->
        <context:component-scan base-package="com.dx.spring.beans.aop"></context:component-scan>
        <!-- 配置是AspectJ注解起作用 :自动为匹配的类生成代理对象 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

    第三步:添加Spring组件

    IArithmeticCalculator.java组件接口

    package com.dx.spring.beans.aop;
    
    /**
     * Description:Addition, subtraction, multiplication, and division
     */
    public interface IArithmeticCalculator {
        int add(int i, int j);
    
        int sub(int i, int j);
    
        int multi(int i, int j);
    
        int div(int i, int j);
    }
    View Code

    ArithmeticCalculatorImpl.java组件

    package com.dx.spring.beans.aop;
    
    import org.springframework.stereotype.Component;
    
    @Component("arithmeticCalculator")
    public class ArithmeticCalculatorImpl implements IArithmeticCalculator {
        @Override
        public int add(int i, int j) {
            int result = i + j;
            return result;
        }
    
        @Override
        public int sub(int i, int j) {
            int result = i - j;
            return result;
        }
    
        @Override
        public int multi(int i, int j) {
            int result = i * j;
            return result;
        }
    
        @Override
        public int div(int i, int j) {
            int result = i / j;
            return result;
        }
    }
    View Code

    第四步:给Spring组件添加LoggingAspect切面类:

    LoggingAspect.java切面类

    package com.dx.spring.beans.aop;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    //把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。
    @Aspect
    @Component
    public class LoggingAspect {    
        // 声明该方法为一个前置通知:在目标方法开始之前执行
        @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.add(int, int))")
        public void beforeMethod() {
            System.out.println("before ");
        }
    }

    第五步:添加测试类Client.java&测试

    package com.dx.spring.beans.aop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Client {
        public static void main(String[] args) {
            // 1:创建Spring的IOC容器;
            ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");
            // 2.从IOC容器中获取Bean的实例
            IArithmeticCalculator arithmeticCalculator = (IArithmeticCalculator) ctx.getBean("arithmeticCalculator");
            // 3.使用Bean
            int result = arithmeticCalculator.add(1, 3);
            System.out.println(result);
        }
    }

    测试结果:

    before
    4

    Spring AOP合并切入点表达式:

    示例:

    上边的LoggingAspect.java中的配置仅仅适用于接口IArithm*eticCalculator.java的add方法,如果想通过一个配置适用IArithmeticCalculator.java接口的四个方法,可以使用“*”占位符混匹方式。

    第一步:修改LoggingAspect.java接口

    package com.dx.spring.beans.aop;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    //把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。
    @Aspect
    @Component
    public class LoggingAspect {    
        // 声明该方法为一个前置通知:在目标方法开始之前执行
        @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")
        public void beforeMethod() {
            System.out.println("before ");
        }
    }

    第二步:修改Client.java&测试

            // 1:创建Spring的IOC容器;
            ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");
            // 2.从IOC容器中获取Bean的实例
            IArithmeticCalculator arithmeticCalculator = (IArithmeticCalculator) ctx.getBean("arithmeticCalculator");
            // 3.使用Bean
            int result = arithmeticCalculator.add(1, 3);
            System.out.println(result);
            
            result = arithmeticCalculator.add(4, 2);
            System.out.println(result);

    测试结果:

    before
    4
    before
    6

    利用方法签名编写AspectJ切入点表达式:

    最经典的切入点表达式是根据方法的签名来匹配各种方法:

    • --- execution(* com.dx.spring.beans.aop.IArithmeticCalculator.*.(..)):匹配IArithmeticCalculator中声明的所有方。第一个"*"代表任意修饰符及任意返回值;第二个"*"代表任意方法;其中".."匹配任意数量的参数。若目标类与接口与该切面在同一个包中,可以省略报名。
    • --- execution(public * IArithmeticCalculator.*.(..)):匹配IArithmeticCalculator接口的所有公有方法。
    • --- execution(public double IArithmeticCalculator.*.(..)):匹配IArithmeticCalculator接口中返回double类型数据的方法。
    • --- execution(public double IArithmeticCalculator.*.(double,..)):匹配IArithmeticCalculator接口中返回double类型数据且第一个参数参数为double类型的(".."匹配任意数量任意类型的参数)方法。
    • --- execution(public double IArithmeticCalculator.*.(double,..)):匹配IArithmeticCalculator接口中返回double类型数据且有两个类型为double类型的参数的方法。

    Spring AOP让通知访问当前连接点的细节:

    通知方法支持接收参数,其中就允许接收JoinPoint,通过JointPoint参数对象,可以获取函数名称,和参数。

    第一步:修改LoggingAspect.java类:

    package com.dx.spring.beans.aop;
    
    import java.util.List;
    import java.util.Arrays;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    //把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。
    @Aspect
    @Component
    public class LoggingAspect {
        // 声明该方法为一个前置通知:在目标方法开始之前执行
        @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")
        public void beforeMethod(JoinPoint joinpoint) {
            String methodName = joinpoint.getSignature().getName();
            List<Object> args = Arrays.asList(joinpoint.getArgs());
            System.out.println("before method " + methodName + " with " + args);
        }
    }

    第二步:测试打印结果

    before method add with [1, 3]
    4
    before method add with [4, 2]
    6

  • 相关阅读:
    求给定数组中最大值和其在数组中的索引并输出
    多线程与多进程
    logging模块
    QWidget上下文菜单处理函数
    python中的yield关键字
    菜单栏(QMenuBar)与菜单(QMenu)
    PyQt5布局管理(1)
    QMainFrame类
    QTP11使用DOM XPath以及CSS识别元素对象
    C# 跨线程访问控件
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/9127119.html
Copyright © 2011-2022 走看看