zoukankan      html  css  js  c++  java
  • Spring 核心技术 AOP 实例

    AOP 简介

    Aop(Aspect Oriented Programming),面向切面编程,这是对面向对象思想的一种补充。

    面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法的功能,常见的使用场景非常多:

    1. 日志
    2. 事务
    3. 数据库操作
    4. ....

    这些操作中,无一例外,都有很多模板化的代码,而解决模板化代码,消除臃肿就是 Aop 的强项。在 Aop 中,有几个常见的概念:

    概念 说明
    切点 要添加代码的地方,称作切点
    通知(增强) 通知就是向切点动态添加的代码
    切面 切点 + 通知
    连接点 切点的定义

    AOP 的实现

    在 Aop 实际上集基于 Java 动态代理来实现的。Java 中的动态代理有两种实现方式:

    • cglib
    • jdk

    动态代理

    基于 JDK 的动态代理实例。

    1. 定义一个计算器接口

    public interface MyCalculator {
        
        int add(int a, int b);
    }
    

    2. 定义计算机接口的实现

    public class MyCalculatorImpl implements MyCalculator {
        
        public int add(int a, int b) {
            return a + b;
        }
    }
    

    3. 定义代理类

    public class CalculatorProxy {
        
        public static Object getInstance(final MyCalculatorImpl myCalculator) {
            return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() {
                
                /**
                 * @param proxy 代理对象
                 * @param method 代理的方法
                 * @param args 方法的参数
                 * @return
                 * @throws Throwable
                 */
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println(method.getName()+"方法开始执行啦...");
                    Object invoke = method.invoke(myCalculator, args);
                    System.out.println(method.getName()+"方法执行结束啦...");
                    return invoke;
                }
            });
        }
    }
    

    Proxy.newProxyInstance 方法接收三个参数,第一个是一个 classloader,第二个是代理多项实现的接口,第三个是代理对象方法的处理器,所有要额外添加的行为都在 invoke 方法中实现。

    XML 配置 AOP

    1. 在 pom.xml 中引入 Spring 和 AOP 相关依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    
    <!-- AOP Begin -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.9.5</version>
    </dependency>
    <!-- AOP Over -->
    

    2. 接下来,定义通知/增强,但是单纯定义自己的行为即可,不再需要注解

    public class LogAspect {
    
        public void before(JoinPoint joinPoint) {
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            System.out.println(name + "方法开始执行了...");
        }
        
        public void after(JoinPoint joinPoint) {
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            System.out.println(name + "方法执行结束了...");
        }
    
        public void returing(JoinPoint joinPoint,Integer r) {
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            System.out.println(name + "方法返回:"+r);
        }
        
        public void afterThrowing(JoinPoint joinPoint,Exception e) {
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            System.out.println(name + "方法抛异常了:"+e.getMessage());
        }
        
        public Object around(ProceedingJoinPoint pjp) {
            Object proceed = null;
            try {
                //这个相当于 method.invoke 方法,我们可以在这个方法的前后分别添加日志,就相当于是前置/后置通知
                proceed = pjp.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            return proceed;
        }
    }
    

    3. 接下来在 Spring 中配置 AOP

    <bean class="com.antoniopeng.hello.spring.aop.LogAspect" id="logAspect"/>
    <aop:config>
        <aop:pointcut id="pc1" expression="execution(* com.antoniopeng.hello.spring.aop.commons.*.*(..))"/>
        
        <aop:aspect ref="logAspect">
            <aop:before method="before" pointcut-ref="pc1"/>
            <aop:after method="after" pointcut-ref="pc1"/>
            <aop:after-returning method="returing" pointcut-ref="pc1" returning="r"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pc1" throwing="e"/>
            <aop:around method="around" pointcut-ref="pc1"/>
        </aop:aspect>
    </aop:config>
    

    4. 最后,在 Main 方法中加载配置文件

    public class Main {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            MyCalculatorImpl myCalculator = ctx.getBean(MyCalculatorImpl.class);
            myCalculator.add(3, 4);
            myCalculator.min(5, 6);
        }
    }
    

    本文发于:https://antoniopeng.com

  • 相关阅读:
    【洛谷P6178】【模板】Matrix-Tree 定理
    【洛谷P4336】黑暗前的幻想乡
    【洛谷P3332】K大数查询
    分布式存储系统大数据同步方面的两个问题
    Vector和ArrayList的比较
    HDU 3172 Virtual Friends (并查集)
    uva 12260
    uva-1427 Parade (单调队列优化dp)
    英语学习20130816 -> 这些很重要的英语句子
    新浪微博SDK授权后无法返回应用解决方案
  • 原文地址:https://www.cnblogs.com/antoniopeng/p/14274842.html
Copyright © 2011-2022 走看看