zoukankan      html  css  js  c++  java
  • Spring AOP

    AOP简介
      AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统OOP的补充
      AOP的主要编程对象时切面(aspect),而切面模块化横切关注点
      在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。
      AOP的好处
        每个事务逻辑位于一个位置,代码不分散,便于维护和升级
        业务模块更简洁,只包含核心业务代码

    AOP术语
      切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
      通知(Advice):切面必须要完成的工作
      目标(Target):被通知的对象
      代理(Proxy):向目标对象应用通知之后创建的对象
      连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点有两个信息确定:方法表示的程序执行点,相对点表示的方位。例如ArithmeticCalculator#add()方法执行前的连接点,执行点为ArithmeticCalculator#add();方位为该方法执行前的位置。
      切点(pointcut):每个类都拥有多个连接点。例如ArithmeticCalculator的所有方法其实都是连接点,即连接点是程序类中客观存在的事务。AOP通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    下面实现一个简单的计算器来解释AOP的产生背景。

    接口:ArithmeticCalculator.java,定义加减乘除方法

     1 package com.yl.spring.aop.helloworld;
     2 
     3 public interface ArithmeticCalculator {
     4     
     5     int add(int i, int j);
     6     int sub(int i, int j);
     7     
     8     int mul(int i, int j);
     9     int div(int i, int j);
    10 }

    实现类:ArithmeticCalculatorImpl.java 实现基本的加减乘除

     1 package com.yl.spring.aop.helloworld;
     2 
     3 public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
     4 
     5     @Override
     6     public int add(int i, int j) {
     7         int result = i + j;
     8         return result;
     9         
    10     }
    11 
    12     @Override
    13     public int sub(int i, int j) {
    14         int result = i - j;
    15         return result;
    16     }
    17 
    18     @Override
    19     public int mul(int i, int j) {
    20         int result = i * j;
    21         return result;
    22     }
    23 
    24     @Override
    25     public int div(int i, int j) {
    26         int result = i / j;
    27         return result;
    28     }
    29 
    30 }

    至此,上面两段代码实现了基本的计算器。新的需求时在计算前后增加日志。

    最简单的实现就是在每个方法的计算语句前后增加日志代码,即输出语句。具体实现如下:

    ArithmeticCalculatorLoggingImpl.java

     1 package com.yl.spring.aop.helloworld;
     2 
     3 public class ArithmeticCalculatorLoggingImpl implements ArithmeticCalculator {
     4 
     5     @Override
     6     public int add(int i, int j) {
     7         System.out.println("the method add begin with[" + i + ", " + j + "]");
     8         int result = i + j;
     9         System.out.println("the method add end with " +result);
    10         return result;
    11         
    12     }
    13 
    14     @Override
    15     public int sub(int i, int j) {
    16         System.out.println("the method sub begin with[" + i + ", " + j + "]");
    17         int result = i - j;
    18         System.out.println("the method sub end with " +result);
    19         return result;
    20     }
    21 
    22     @Override
    23     public int mul(int i, int j) {
    24         System.out.println("the method mul begin with[" + i + ", " + j + "]");
    25         int result = i * j;
    26         System.out.println("the method mul end with " +result);
    27         return result;
    28     }
    29 
    30     @Override
    31     public int div(int i, int j) {
    32         System.out.println("the method div begin with[" + i + ", " + j + "]");
    33         int result = i / j;
    34         System.out.println("the method div end with " +result);
    35         return result;
    36     }
    37 
    38 }

     但是上述的实现方法增加了业务模块的复杂度,而且修改日志代码时也不方便。

    下面在介绍一个新的方法,即动态代理。具体实现如下:

    ArithmeticCalculatorLoggingProxy.java

     1 package com.yl.spring.aop.helloworld;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Method;
     5 import java.lang.reflect.Proxy;
     6 import java.util.Arrays;
     7 
     8 import org.springframework.beans.propertyeditors.ClassArrayEditor;
     9 
    10 public class ArithmeticCalculatorLoggingProxy {
    11     //要代理的对象
    12     private ArithmeticCalculator target;
    13     
    14     public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
    15         this.target = target;
    16     }
    17     
    18     public ArithmeticCalculator getLoggingProxy() {
    19         ArithmeticCalculator proxy = null;
    20         
    21         //代理对象由哪一个类加载器负责加载
    22         ClassLoader loader = target.getClass().getClassLoader();
    23         //代理对象的类型,即其中有哪些方法
    24         Class [] interfaces = new Class[]{ArithmeticCalculator.class};
    25         //当调用代理对象其中的方法时,该执行的代码
    26         InvocationHandler h =  new InvocationHandler() {
    27             /**
    28              * proxy:正在返回的那个代理对象,一般情况下,在invoke方法中都不使用该对象
    29              * method:正在调用的方法
    30              * args:调用方法时,传入的参数
    31              */
    32             @Override
    33             public Object invoke(Object proxy, Method method, Object[] args)
    34                     throws Throwable {
    35                 //此句话或造成循环调用,最终内存溢出
    36                 //System.out.println(proxy.toString());
    37                 
    38                 
    39                 String methodName = method.getName();
    40                 //日志
    41                 System.out.println("the method " + methodName + " begin with " + Arrays.asList(args));
    42                 //执行该方法
    43                 Object result = method.invoke(target, args);
    44                 //日志
    45                 System.out.println("the method " + methodName + " end with " + result);
    46                 return result;
    47             }
    48         };
    49         
    50         proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
    51         
    52         return proxy;
    53     }
    54 
    55 }

    至此,动态代理的实现完毕。

    测试类:

     1 package com.yl.spring.aop.helloworld;
     2 
     3 public class Main {
     4     public static void main(String[] args) {
     5         
     6         /*//测试日志
     7         ArithmeticCalculator arithmeticCalculator = null;
     8         arithmeticCalculator = new ArithmeticCalculatorLoggingImpl();
     9         
    10         int result = arithmeticCalculator.add(1, 2);
    11         System.out.println("-->" + result);
    12         
    13         result = arithmeticCalculator.div(4, 2);
    14         System.out.println("-->" + result);*/
    15         
    16         //动态代理测试
    17         ArithmeticCalculator target = new ArithmeticCalculatorImpl();
    18         ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
    19         
    20         System.out.println(proxy.getClass().getName());
    21         
    22         int result = proxy.add(1, 2);
    23         System.out.println("-->" + result);
    24         
    25         result = proxy.div(4, 2);
    26         System.out.println("-->" + result);
    27     }
    28 }

    AOP的具体用法还请参考本系列的后续文章......

  • 相关阅读:
    codevs 3305 水果姐逛水果街Ⅱ&&codevs3006
    开发webrtc通过Js调用Go接口发送数据报错500(Internal Sever Error)排查分析
    TSINGSEE青犀视频开发单通道 Go WebRTC服务端拉流接口无响应导致程序堵塞,如何处理?
    H265网页视频播放器项目EasyPlayer.JS版本不支持PCM/711音频格式如何转换?
    网络穿透/视频拉转推服务系统EasyNTS通过gomod管理工具编译提示缺少依赖库解决方法
    WebRTC播放器通过js Video标签拉流播放有时无法刷新视频画面的原因排查
    开源框架WebRTC终极指南:3大 API 详解
    开源框架WebRTC 10年发展,现已成为官方Web标准
    IE8 下 iframe 滚动条的问题
    父窗口jquery触发iframe按钮事件(转载)
  • 原文地址:https://www.cnblogs.com/dreamfree/p/4093768.html
Copyright © 2011-2022 走看看