• 10 - Spring AOP介绍与使用


    1、AOP的概念

    先引入一个例子:计算器

    接口:Calculator

    1. package com.zhoulz.service;
    2. public interface Calculator {
    3. public Integer add(Integer i,Integer j) throws NoSuchMethodException;
    4. public Integer sub(Integer i,Integer j) throws NoSuchMethodException;
    5. public Integer mul(Integer i,Integer j) throws NoSuchMethodException;
    6. public Integer div(Integer i,Integer j) throws NoSuchMethodException;
    7. //运行时,会报“NoSuchMethodException”异常,怎么办?
    8. //:将上面的int类型全部改为 Integer
    9. }

    实现子类1:MyCalculator

    1. package com.zhoulz.service;
    2. import com.zhoulz.util.LogUtil;
    3. import java.lang.reflect.Method;
    4. public class MyCalculator implements Calculator {
    5. public Integer add(Integer i, Integer j) throws NoSuchMethodException {
    6. //现在,想添加日志的输出功能,怎么做?
    7. //最简单的做法:在每行代码里面都做一个最基本的输出 —— 但是这样做效率太低了
    8. //System.out.prIntegerln("add方法开始执行,参数是:" + i + "----" + j );
    9. //改进: —— 定义一个LogUtil类(包含start()方法和stop()方法)
    10. //通过反射
    11. /*Method add = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
    12. //然后add传入到下面的方法中 —— 下面的都同理
    13. LogUtil.start(add,i,j);*/
    14. Integer result = i + j;
    15. //System.out.prIntegerln("add方法执行结束,结果是:" + result);
    16. //LogUtil.stop(add,result);
    17. return result;
    18. }
    19. public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
    20. //System.out.prIntegerln("sub方法开始执行,参数是:" + i + "----" + j );
    21. /*Method sub = MyCalculator.class.getMethod("sub", Integer.class, Integer.class);
    22. LogUtil.start(sub,i,j);*/
    23. Integer result = i - j;
    24. //System.out.prIntegerln("sub方法执行结束,结果是:" + result);
    25. //LogUtil.stop(sub,result);
    26. return result;
    27. }
    28. public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
    29. //System.out.prIntegerln("mul方法开始执行,参数是:" + i + "----" + j );
    30. //Method mul = MyCalculator.class.getMethod("mul", Integer.class, Integer.class);
    31. //LogUtil.start(mul,i,j);
    32. Integer result = i * j;
    33. //System.out.prIntegerln("mul方法执行结束,结果是:" + result);
    34. //LogUtil.stop(mul,result);
    35. return result;
    36. }
    37. public Integer div(Integer i, Integer j) throws NoSuchMethodException {
    38. //System.out.prIntegerln("div方法开始执行,参数是:" + i + "----" + j );
    39. //这个地方有个异常,直接抛出去,见Calculator类中
    40. //Method div = MyCalculator.class.getMethod("div", Integer.class, Integer.class);
    41. //LogUtil.start(div,i,j);
    42. Integer result = i / j;
    43. //System.out.prIntegerln("div方法执行结束,结果是:" + result);
    44. //LogUtil.stop(div,result);
    45. return result;
    46. }
    47. /**
    48. * 上面,为了解决代码重复的问题,采取了一些措施(往上抽象),如:创建LogUtil类、引入了Method方法(获取具体的方法名称,这个必须要通过反射来做) 。。。
    49. * 但是发现,此时又多了一些重复的代码,如:Method add = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
    50. * 怎么办?继续往上抽象?但是可能又会引入一些重复的代码?又怎么办?
    51. * 即,不管怎么写,都(可能)变得很麻烦,怎么办? : 动态代理
    52. * 具体操作(以测试类为例):可以换一个更加简单的写法
    53. * :在MyTest类中,给MyCalculator()加一个对应的代理类/对象,由这个代理类来完成具体的日志的添加功能。
    54. * (直接在测试类中写不方便,需要先写一个单独的(抽象的)类作为代理类,见包proxy)
    55. */
    56. }

    实现子类2:SecondCalculator —— 只是定义了,用来说明,没做具体实现。

    1. package com.zhoulz.service;
    2. //Calculator的第二个实现子类:SecondCalculator (MyCalculator是定义的第一个实现子类)
    3. public class SecondCalculator implements Calculator {
    4. @Override
    5. public Integer add(Integer i, Integer j) throws NoSuchMethodException {
    6. return null;
    7. }
    8. @Override
    9. public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
    10. return null;
    11. }
    12. @Override
    13. public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
    14. return null;
    15. }
    16. @Override
    17. public Integer div(Integer i, Integer j) throws NoSuchMethodException {
    18. return null;
    19. }
    20. }

    为了简化代码,进行了封装,创建了 LogUtil 类:

    1. package com.zhoulz.util;
    2. import java.lang.reflect.Method;
    3. import java.util.Arrays;
    4. public class LogUtil {
    5. //public void start(int i, int j){ }//需要传参,但是这样就只能传两个参数了,改进见下
    6. //再引入参数:Method method —— 可以获取到方法的名字 ,要结合反射,见MyCalculator类
    7. public static void start(Method method,Object ... objects){ // "..." 代表可变参数
    8. //System.out.println("方法开始执行,参数是:" + i + "----" + j );
    9. System.out.println(method.getName()+"方法开始执行,参数是:" + Arrays.asList(objects));
    10. }
    11. //为什么加static?? 通过“LogUtil.”可直接调用
    12. public static void stop(Method method,Object ... objects){
    13. //System.out.println("sub方法执行结束,结果是:" + Arrays.asList(objects));
    14. System.out.println(method.getName()+"方法执行结束,结果是:" + Arrays.asList(objects));
    15. }
    16. public static void logException(Method method,Exception e){
    17. System.out.println(method.getName()+"方法抛出异常:"+e.getMessage());
    18. }
    19. public static void logFinally(Method method){
    20. System.out.println(method.getName()+"方法执行结束。。。。over");
    21. }
    22. }

    再次进行简化,使用(动态)代理的方式,创建了 CalculatorProxy 类:

    1. package com.zhoulz.proxy;
    2. import com.zhoulz.service.Calculator;
    3. import com.zhoulz.util.LogUtil;
    4. import java.lang.reflect.InvocationHandler;
    5. import java.lang.reflect.Method;
    6. import java.lang.reflect.Proxy;
    7. import java.util.Arrays;
    8. /**
    9. * 动态代理:
    10. * 必须要有接口,如果没有接口,不能使用,这种方式是用jdk提供的reflect包下的类
    11. * 但是在生产环境中,我们不能保证每个类都有实现的接口,所以有第二种方式:cglib
    12. *
    13. * cglib在实现的时候,有没有接口都无所谓
    14. *
    15. * 在spring中使用了两种动态代理的方式,一种是jdk提供的(刚刚完成的),另外一种就是cglib
    16. *
    17. * 其实下面写死了,也可以不写死,如:
    18. * public static Object getCalculator(final Object object){} —— 这样就可以用不同的接口和实现类了(不然只能换实现子类)
    19. * */
    20. public class CalculatorProxy {
    21. //为的是获取Calculator对象
    22. public static Calculator getCalculator(final Calculator calculator){ //但是要告诉它生成一个什么样的代理对象
    23. //所以,此时里面一定要写好/传入一个父类
    24. //ClassLoader是一个类加载器
    25. //因为是要生成上面 Calculator calculator 的代理类,所以此时你在传递值的时候应该用当前这个类的加载器,写法见下 (获取的时候还是通过反射)
    26. //即第1点:先获取被代理对象的 类加载器
    27. ClassLoader loader = calculator.getClass().getClassLoader();
    28. //第2点:获取被代理对象的 所有接口(因为要求传入的是class的一个数组)
    29. Class[] interfaces = calculator.getClass().getInterfaces();
    30. //第3点:InvocationHandler也是一个接口,里面就提供了一个invoke(Object var1, Method var2, Object[] var3)方法
    31. //对于这样的接口,只有一个对应的方法,那么直接写一个匿名的内部类就行了(为什么???)
    32. //(InvocationHandler:用来执行被代理类需要执行的方法!!!)
    33. InvocationHandler handler = new InvocationHandler() { //然后里面的方法会自动实现,
    34. @Override
    35. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    36. //System.out.println("invoke方法被执行了");//验证该方法是否被执行?执行了
    37. Object result = null; //因为result最后要做返回,所以这里给提出来
    38. try {
    39. //验证成功后,加入对日志的处理(输出),参考原先的LogUtil:
    40. //System.out.println(method.getName()+"方法开始执行,参数列表是:"+ Arrays.asList(args));
    41. //或者在代理中,把之前写的LogUtil用过来 —— 用于日志的输出,下面同理
    42. LogUtil.start(method,args);
    43. //开始调用被代理类的方法 : method.invoke(),注意,invoke()里面是有参数的
    44. /* Object*/ result = method.invoke(calculator,args);//会报错,必须把上面的Calculator calculator用final修饰,
    45. //因为此时,你是在一个匿名内部类里面需要去调用外部方法的参数,所以此时必须要把当前这个参数修饰成final。
    46. //不加final,就没办法调用(为什么???匿名内部类的特性)
    47. //System.out.println(method.getName()+"方法执行完成,结果是:"+result);
    48. LogUtil.stop(method,args);
    49. }catch (Exception e){
    50. //System.out.println(method.getName()+"方法抛出异常:"+e.getMessage());
    51. LogUtil.logException(method,e);
    52. e.printStackTrace();
    53. }finally {
    54. //System.out.println(method.getName()+"方法执行结束。。。。over");
    55. LogUtil.logFinally(method);
    56. }
    57. //另外,上面 Object result = 还有可能出现异常,比如运算中,出现1除以0,所以要进行try-catch处理
    58. return result;
    59. }
    60. };
    61. //然后就可以进行下面生成代理的的操作了
    62. //然后要生成一个代理类,用Proxy。要传入的参数如下:
    63. //Proxy.newProxyInstance(loader,interfaces,InvocationHandler(起名叫handler即可))这些参数都还没有,先创建实例对象,见上面
    64. Object o = Proxy.newProxyInstance(loader, interfaces, handler);
    65. return (Calculator)o; //该方法getCalculator()的返回值是Calculator
    66. //最后,就可以用了,见测试类
    67. }
    68. }

    测试类:

    1. import com.zhoulz.proxy.CalculatorProxy;
    2. import com.zhoulz.service.Calculator;
    3. import com.zhoulz.service.MyCalculator;
    4. import com.zhoulz.service.SecondCalculator;
    5. import org.junit.Test;
    6. public class MyTest {
    7. @Test //使用junit单元测试,需要添加junit依赖
    8. public void test01() throws NoSuchMethodException {
    9. /*MyCalculator myCalculator = new MyCalculator();
    10. System.out.println(myCalculator.add(10, 20));
    11. System.out.println(myCalculator.div(10,2));*/
    12. //注释掉上面,换由代理对象来完成:
    13. //代理 -测试 :新建一个测试test02()
    14. }
    15. @Test
    16. public void test02() throws NoSuchMethodException {
    17. //使用代理之前,记得在MyCalculator中注销之前LogUtil的设计内容
    18. //使用代理:传入参数为 Calculator对象,但是 Calculator为接口,不能new,所以要用它的实现类MyCalculator
    19. //CalculatorProxy.getCalculator(new Calculator())
    20. Calculator calculator = CalculatorProxy.getCalculator(new MyCalculator());
    21. //Calculator calculator = CalculatorProxy.getCalculator(new SecondCalculator());
    22. //哪里体现动态了?
    23. //:MyCalculator是Calculator的一个实现子类,如果有其他的实现子类(如SecondCalculator),
    24. // 这里直接换成SecondCalculator即可,其他的都不用动
    25. calculator.add(1,1);
    26. calculator.sub(1,1);
    27. calculator.mul(1,1);
    28. calculator.div(1,0);
    29. System.out.println(calculator.getClass());//class com.sun.proxy.$Proxy7
    30. // 结果表明:calculator的类并不是 MyCalculator,
    31. // $Proxy7 是实际的代理对象,是在运行过程中动态生成的
    32. //CalculatorProxy 并不是代理对象
    33. }
    34. }

    附 pom 依赖: —— junit 的依赖

    1. <dependencies>
    2. <dependency>
    3. <groupId>junitgroupId>
    4. <artifactId>junitartifactId>
    5. <version>4.12version>
    6. <scope>testscope>
    7. dependency>
    8. dependencies>

     

     

  • 相关阅读:
    什么是学习能力?如何提高学习能力?
    Imitation Learning学习记录(理论&例程)
    H5全栈实习day04:微信小程序
    王颖奇:ONES.ai 上线,以及我的一些思考
    java计算机毕业设计家电售后管理系统源码+mysql数据库+系统+lw文档+部署
    【计算机毕业设计】企业员工岗前培训管理系统
    iPhone 15分辨率,屏幕尺寸,PPI 详细数据对比 iPhone 15 Plus、iPhone 15 Pro、iPhone 15 Pro Max
    在Vue3中使用Element Plus Icon图标的几种方式
    神经网络如何避免过拟合,人工神经网络过拟合
    Thinkphp漏洞远程代码执行漏洞事件分析报告
  • 原文地址:https://blog.csdn.net/zhoulizhengZ/article/details/127647918