Spring AOP入门基础-继承、装饰者,代理的选择

关于Spring AOP,底层是基于动态代理实现的,下面简单的学习下为什么选择动态代理,而不选择继承实现,装饰者模式实现,下面参考如下业务场景下理解。

业务场景

业务层如果有业务需求,需要在注册用户,升级用户,和删除用户方法前都进行一次权限验证,最原始的方法就是在业务层每个方法前都添加代码验证。这是最原始的方式,在实际业务中有很多的方法,那都需要重写修改,很显然这是不合理的,因此衍生如下几个解决方案:

(1)使用继承类,在继承类中对继承的方法进行修改,参考DogDemo01
(2)使用装饰者模式,在装饰类中对原有方法进行装饰修改,参考DogDemo02
(3)使用代理模式,创建出一个代理类,在代理类中修改方法,添加权限,代理模式分为以下两种:
a 使用静态代理,参考DogDemo03
b 使用动态代理,动态代理又分为两种,有默认Java JDK动态代理,参考DogDemo04,又有CGlib动态代理,参考DogDemo05
下面使用狗的案例来简单体验一下几个选择的是否可行。

代码准备,先准备了Animal接口,还创建了Dog类,实现Animal接口,下面就是基于此展开。

Animal接口

 1 package com.boe.proxy.service;
 2
 3 /**
 4  * 动物的接口
 5  */
 6 public interface Animal {
 7     //吃的动作
 8     public void eat();
 9     //工作职责
10     public void work();
11 }

Dog类,实现Animal接口,里面还添加自定义方法一个,后面用来测试动态代理。

 1 package com.boe.proxy.service;
 2
 3 public class Dog implements Animal{
 4     @Override
 5     public void eat() {
 6         System.out.println("我喜欢吃骨头");
 7     }
 8
 9     @Override
10     public void work() {
11         System.out.println("我要守护主人");
12     }
13
14     //添加一个接口外的方法,测试JDK静态代理类的不足
15     public void see(){
16         System.out.println("我能在黑夜中看到一切");
17     }
18 }

使用继承类

使用继承者测试代码如下,就是在子类中修改父类的方法,考虑到父类中如果方法很多,这种方法不可取。并且这种只对子类新创建的对象修改方法有效,父类还是原来的方法。

 1 package com.boe.proxy.service;
 2
 3 /**
 4  * 使用继承父类,修改方法,但是只对新创建的对象有效
 5  */
 6 public class DogDemo01 {
 7     public static void main(String[] args) {
 8         //修改方法只对新对象有效,对以前的对象无效
 9         System.out.println("-------这是一条继承狗-------");
10         DogExtend dog=new DogExtend();
11         dog.eat();
12         dog.work();
13
14         //原来对象无法修改
15         System.out.println("-------这是一条老狗-------");
16         Dog originDog=new Dog();
17         originDog.eat();
18         originDog.work();
19     }
20 }
21
22 //继承狗,修改狗的方法
23 class DogExtend extends Dog{
24     //修改eat
25     @Override
26     public void work() {
27         System.out.println("我要看贼");
28     }
29 }

测试结果,可以完成对方法的修改,work方法被修改了,打印结果由"我要守护主人"变成"我要看贼"。

使用装饰者模式

使用装饰者模式测试代码如下,这个方法也有个弊端,必须实现被装饰类所有的接口方法,如果接口中有很多方法将会重写很多方法,也不是最好的选择。

 1 package com.boe.proxy.service;
 2
 3 /**
 4  * 使用装饰者模式,将需要被装饰的狗注入装饰狗,但是这个有个弊端,需要实现接口Animal中所有方法
 5  * 如果共同的Animal接口有100个方法,则需要重写100个方法,也不是好的解决方案
 6  */
 7 public class DogDemo02 {
 8     public static void main(String[] args) {
 9         System.out.println("-------这是一条老狗-------");
10         Dog originDog=new Dog();
11         originDog.eat();
12         originDog.work();
13         System.out.println("-------这是一条装饰狗-------");
14         DecorateDog dog=new DecorateDog(originDog);
15         dog.eat();
16         dog.work();
17     }
18 }
19
20 /**
21  * 装饰狗,装饰狗和被装饰狗,都需实现同样的接口
22  */
23 class DecorateDog implements Animal{
24     //属性是装饰狗
25     private Dog dog=null;
26     //被装饰狗注入
27     public DecorateDog(Dog dog) {
28         this.dog = dog;
29     }
30     //如果不修改,也需要实现接口中所有的方法
31     @Override
32     public void eat() {
33         //吃不修改
34         dog.eat();
35     }
36
37     @Override
38     public void work() {
39         //工作修改
40         System.out.println("我是装饰狗,我爱工作");
41     }
42 }

测试结果,可以完成对方法work的修改,打印出"我是装饰狗,我爱工作"。

使用代理模式

代理模式分为两种,即静态代理和动态代理,静态代理和装饰模式感觉比较类似,动态代理分为JDK原生动态代理和Cglib动态代理,后者是对前者的完善,改善前者只能实现接口方法的局限。

静态代理

以下为静态代理代码实现,存在和装饰模式同样的弊端,即可能需要重写很多方法。

 1 package com.boe.proxy.service;
 2
 3 /**
 4  * 静态代理狗,弊端与装饰狗类似
 5  */
 6 public class DogDemo03 {
 7     public static void main(String[] args) {
 8         System.out.println("-------这是一条老狗-------");
 9         Dog originDog=new Dog();
10         originDog.eat();
11         originDog.work();
12         System.out.println("-------这是一条代理狗-------");
13         ProxyDog proxyDog=new ProxyDog();
14         proxyDog.eat();
15         proxyDog.work();
16     }
17 }
18
19 /**
20  * 代理狗
21  */
22 class ProxyDog implements Animal{
23     //创建老狗对象
24     Dog dog=new Dog();
25     //也需要重写所有的接口方法
26     @Override
27     public void eat() {
28         //吃方法不重写
29         dog.eat();
30     }
31
32     @Override
33     public void work() {
34         //工作方法修改
35         System.out.println("我是代理狗,我也爱工作");
36     }
37 }

测试结果,可以完成对work方法的修改,打印出"我是代理狗,我也爱工作"。

动态代理

动态代理即上面说的两种,接下来分别使用来完成代理生成,两者均有固定套路写法。

(1)JDK动态代理,使用JDK Proxy接口的静态方法newProxyInstance()来完成,具体参考代码。

 1 package com.boe.proxy.service;
 2
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6
 7 /**
 8  * 动态代理,使用JDK自带代理方法
 9  */
10 public class DogDemo04 {
11     public static void main(String[] args) {
12         System.out.println("-------这是一条老狗-------");
13         Dog originDog=new Dog();
14         originDog.eat();
15         originDog.work();
16         //下面使用JDK动态代理,需要使用JDK提供的Proxy
17         /**
18          * 参考API解释
19          * 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
20          */
21         Animal proxyDog = (Animal) Proxy.newProxyInstance(
22                 originDog.getClass().getClassLoader(),
23                 originDog.getClass().getInterfaces(),
24                 //InvocationHandler是接口,下面是匿名内部类
25                 new InvocationHandler() {
26                     /**
27                      * 当代理对象执行代理对象的方法时,会执行以下方法
28                      * @param proxy 被代理对象
29                      * @param method 被代理对象身上的方法
30                      * @param args 被代理对象身上方法对应的参数
31                      * @return
32                      * @throws Throwable
33                      */
34                     @Override
35                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
36                         //如果是work方法,修改
37                         if ("work".equals(method.getName())) {
38                             System.out.println("我是JDK动态代理狗,我非常爱工作");
39                             return null;
40                         }
41                         //如果是其他方法不修改
42                         else {
43                             return method.invoke(originDog, args);
44                         }
45                     }
46                 }
47         );
48         //使用动态代理狗方法
49         System.out.println("-------这是一条动态JDK代理狗-------");
50         proxyDog.eat();
51         proxyDog.work();
52         //JDK动态代理类只能调用接口中的方法,不能调用Dog类中自定义方法
53         //proxyDog.see();
54
55     }
56 }

测试结果,可以正常生成一条JDK代理狗,并且需要修改的代码只需要写少量,调用代理对象的方法后,都需要执行InvocationHandler接口里定义的invoke()方法,需要修改的方法单独处理,调用代理对象的方法是修改后的方法,其他不需要修改的使用method.invoke(obj,args)方法返回,即依然调用的是代理对象以前的方法。

JDK动态代理可以比较完美的解决业务问题,但是它有个明显的问题,即只能实现接口中的方法,Dog类中自定义的see()方法无法通过代理类调用,编译期就会报错,这样就引出了Cglib动态代理。

(2)Cglib动态代理

它是对JDK动态代理的完善,代理对象不仅仅实现了接口中的方法,还可以实现父类中的所有方法,Spring-core核心包中就集成了cglib包,案例中导入的Spring-core包完成测试。

 1 package com.boe.proxy.service;
 2
 3 import org.springframework.cglib.proxy.Enhancer;
 4 import org.springframework.cglib.proxy.MethodInterceptor;
 5 import org.springframework.cglib.proxy.MethodProxy;
 6
 7 import java.lang.reflect.Method;
 8
 9 /**
10  * 使用cglib动态代理,它不仅仅可以实现接口中的方法,还可以实现父类中的非final定义的方法
11  */
12 public class DogDemo05 {
13     public static void main(String[] args) {
14         System.out.println("-------这是一条老狗-------");
15         Dog originDog=new Dog();
16         originDog.eat();
17         originDog.work();
18         //下面使用cglib动态代理,需要额外导包,spring-core包集成了cglib包
19         //1 创建增强器
20         Enhancer enhancer=new Enhancer();
21         //2 指定要实现的接口,这句可以不写
22         enhancer.setInterfaces(originDog.getClass().getInterfaces());
23         //3 指定要继承的父类
24         enhancer.setSuperclass(originDog.getClass());
25         //4 设定回调函数
26         enhancer.setCallback(new MethodInterceptor() {
27             /**
28              * 指定动态代理对象中的方法,会进入这里执行
29              * @param proxy 被代理对象
30              * @param method 被代理对象的方法
31              * @param args 被代理对象方法上的参数
32              * @param methodProxy 方法代理对象
33              * @return
34              * @throws Throwable
35              */
36             @Override
37             public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
38                 //如果是work方法,重写
39                 if("work".equals(method.getName())){
40                     System.out.println("我是cglib动态代理狗,我也非常爱工作");
41                     return null;
42                 }else{
43                     return method.invoke(originDog,args);
44                 }
45             }
46         });
47         //生成动态代理狗
48         Dog proxyDog = (Dog) enhancer.create();
49         //使用动态代理狗方法
50         System.out.println("-------这是一条动态cglib代理狗-------");
51         proxyDog.eat();
52         proxyDog.work();
53         //可以执行父类上的非接口定义方法,因此推荐使用cglib代理方法
54         proxyDog.see();
55     }
56 }

测试结果,发现不仅仅可以实现接口方法的修改,还可以实现父类Dog自定义方法see()的修改,实现打印"我能在黑夜中看到一切"。这样Cglib就是目前的最优选择。

总结

(1)继承类和装饰者模式均可以实现方法的重写,但存在重写大量方法的可能,不可取。

(2)JDK动态代理也可以解决业务问题,并可以解决书写大量方法代码的问题,但是只能调用被代理对象中接口方法。

(3)Cglib动态代理弥补了JDK动态代理的不足,可以让代理对象调用被代理类中所有的方法(注意final修饰的方法除外)。

原文地址:https://www.cnblogs.com/youngchaolin/p/11594869.html

时间: 2024-10-26 00:30:27

Spring AOP入门基础-继承、装饰者,代理的选择的相关文章

Spring -- AOP入门基础

动态代理 我们在日常开发过程中是否会遇到下图中的这种状况 红框中的是我们要输出的日志,你是否发现,日志中大部分信息都是相同的,并且如果我们要修改一个地方,所有的地方都需要改,而且代码看起来还比较冗余 下面我们就可以通过动态代理的方式解决这个问题 看下代码 public interface Calculation { public int add(int x, int y); public int sub(int x, int y); public int mul(int x, int y); p

Spring AOP入门——概念及注意点

AOP是什么? AOP从功能上来说就是在执行某些业务逻辑的前后,可以允许你动态地添加一些操作(比如记录日志.或者是判断是否有权限等),这些操作的添加,完全不耦合于原来的业务逻辑,从而对原有业务逻辑完全是透明. 也就是说,这段操作和业务逻辑是完全分开的,它可能在项目中需要横切多个模块,且其自身也是一个独立的模块,贯穿了整个项目.我们完全可以根据需要启用或者停用这个功能. AOP的典型应用就是事务管理和日志. AOP中的概念 下面这些术语并不是Spring定义的.由于AOP中的术语不是那么形象,所以

Spring AOP详解 、 JDK动态代理、CGLib动态代理

AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中. 一.AOP术语 1.连接点(Joinpoint) 程序执行的某个特定位置:如类开始初始化之前.类初始化之后.类某个方法调用前.调用后等:一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就成为“连接点”,Spring仅支持方法的连接点,即

Spring AOP 入门

引言 AOP是软件开发思想发展到一定阶段的产物,AOP的出现并不是为了代替OOP,仅作为OOP的有益补充,在下面的例子中这个概念将会得到印证.AOP的应用场合是受限制的,一般适用于那些具有横切逻辑的应用场合,例如性能监测,访问控制,事务管理,日志记录.在平常的应用开发中AOP很难被使用到,但是AOP是Spring的亮点之一,有必要一看. 一 AOP以及术语 AOP是Aspect Oriented Programing的简称,被译为面向切面编程.AOP希望将散落在业务逻辑函数中的相同代码抽取到一个

【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中. 一.AOP术语 1.连接点(Joinpoint) 程序执行的某个特定位置:如类开始初始化之前.类初始化之后.类某个方法调用前.调用后等:一个类

spring aop入门

一.何为AOP? spring 的两大核心思想无非是 IOC和AOP.那么Spring 的 aop 是神马意思呢?AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现. 二.有何用途? 主要用于将日志记录,性能统计,安全控制

python入门基础-函数装饰器的理解

1.装饰器 # 知乎某大神是这么比喻装饰器的:内裤可以用来遮羞,但是到了冬天他就没有办法为我们御寒,聪明的人于是发明了长裤,有了长裤后宝宝再也不冷了, # 装饰器就像我们这里说的长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效. # # 大神是将程序中原本的函数比喻成内裤,而装饰器比喻成了长裤,这样在寒冬里它们结合一起使用就给所有人带来了温暖. # # 装饰器本质上是一个python函数,它可以让其它函数在不改动代码的情况下增加额外的功能,并且装饰器的返回值也是一个函数对象. # 在

Spring batch 入门基础

Spring Batch是一个轻量级的,完全面向Spring的批处理框架,可以应用于企业级大量的数据处理系统.Spring Batch以POJO和大家熟知的Spring框架为基础,使开发者更容易的访问和利用企业级服务.Spring Batch可以提供大量的,可重复的数据处理功能,包括日志记录/跟踪,事务管理,作业处理统计工作重新启动.跳过,和资源管理等重要功能. 业务方案: 批处理定期提交. 并行批处理:并行处理工作. 企业消息驱动处理 大规模的并行处理 手动或是有计划的重启 局部处理:跳过记录

maven入门基础:创建nexus代理仓库(九)

阿里云服务端代理地址:http://maven.aliyun.com/nexus/content/groups/public/ 其他参照:https://www.cnblogs.com/my_captain/p/12243521.html 原文地址:https://www.cnblogs.com/my_captain/p/12243586.html