设计模式之cglib动态代理

cnblogs 2024-08-20 15:09:00 阅读 91

什么是动态代理呢?

动态代理就是在java进程运行时,通过字节码技术,动态的生成某个类的代理类。在这个代理类中,我们可以做一些额外的操作,一方面仍然保持原有的方法的能力,另外一方面还增强了这些能力。听着是不是AOP有点像,没错,动态代理就是AOP的技术基石。

在这之前我曾经写过两篇相关的文章:

https://www.cnblogs.com/jilodream/p/10611593.html 设计模式之代理模式

https://www.cnblogs.com/jilodream/p/10624940.html 设计模式之Jdk动态代理

而在实现动态代理时,一般会有两种办法:

jdk动态代理(可以点上文链接查看),以及cglib动态代理,

话不多说,我们直接来看如何使用cglib来动态代理:

例子我们还是使用和jdk动态代理相同的明星/明星代理类这个场景

pom依赖

1 <dependency>

2 <groupId>cglib</groupId>

3 <artifactId>cglib</artifactId>

4 <version>3.1</version>

5 </dependency>

明星类

1 package com.example.demo.learncglib;

2

3 import lombok.Data;

4 import lombok.NoArgsConstructor;

5

6 /**

7 * @discription

8 */

9

10 @Data

11 @NoArgsConstructor

12 public class SuperStar implements ISuperStar {

13 String starName;

14

15 public SuperStar(String starName) {

16 this.starName = starName;

17 }

18

19 @Override

20 public void signContract() {

21 System.out.println(starName + " 签名");

22 // to do sth

23 return;

24 }

25

26 @Override

27 public void negotiate() {

28 System.out.println(starName + " 谈判");

29 // to do sth

30 return;

31 }

32

33 @Override

34 public String getStarImage() {

35 System.out.println(starName + " start image");

36 // to do sth

37 return "One " + starName + " Image";

38 }

39 }

明星类接口

1 package com.example.demo.learncglib;

2

3 public interface ISuperStar

4 {

5 /**

6 * 签约

7 */

8 void signContract();

9

10 void negotiate();

11

12 String getStarImage();

13 }

代理工厂

1 package com.example.demo.learncglib;

2

3

4 import net.sf.cglib.core.DebuggingClassWriter;

5 import net.sf.cglib.proxy.Enhancer;

6 import net.sf.cglib.proxy.MethodInterceptor;

7 import net.sf.cglib.proxy.MethodProxy;

8

9 import java.lang.reflect.Method;

10

11 /**

12 * @discription

13 */

14 public class ProxyFactory implements MethodInterceptor {

15

16 private String starName;

17

18 public SuperStar create(String starName) {

19 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code\\common\\learn-design" +

20 "-pattern\\target\\cglib");

21

22 this.starName = starName;

23 Enhancer enhancer = new Enhancer();

24 enhancer.setSuperclass(SuperStar.class);

25 enhancer.setCallback(this);

26 SuperStar proxy = (SuperStar) enhancer.create();

27 proxy.starName = starName;

28 return proxy;

29 }

30

31

32 @Override

33 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

34 System.out.println(starName + "的代理人开始组织活动");

35 Object obj = methodProxy.invokeSuper(o, objects);

36 System.out.println(starName + "的代理人结束组织活动");

37 return obj;

38 }

39 }

主类

1 package com.example.demo.learncglib;

2

3 /**

4 * @discription

5 */

6 public class CglibMain {

7 public static void main(String[] args) {

8 ProxyFactory factory = new ProxyFactory();

9 SuperStar superStar = factory.create("messi");

10 superStar.signContract();

11 superStar.negotiate();

12 String image=superStar.getStarImage();

13 System.out.println("we get a image: "+image);

14 }

15

16

17 }

运行效果

messi的代理人开始组织活动

messi 签名

messi的代理人结束组织活动

messi的代理人开始组织活动

messi 谈判

messi的代理人结束组织活动

messi的代理人开始组织活动

messi start image

messi的代理人结束组织活动

we get a image: One messi Image

Disconnected from the target VM, address: '127.0.0.1:64165', transport: 'socket'

生成代理类的核心逻辑在com.example.demo.learncglib.ProxyFactory#create方法中:

我们首先声明一个增强器:Enhancer enhancer

接着设置代理类父类:SuperStar.class

接着设置回调类(包含增强方法的类):? implements MethodInterceptor

最后调用增强类的创建方法就生成好了:enhancer.create()

整体的流程和jdk动态代理很像,

不同点是jdk动态代理是根据接口,动态的生成实现类,代理类和被代理类均为接口实现类,是“兄弟关系”,被代理的方法就是接口中公开的方法。

而cglib动态代理是将被代理类作为父类,派生出子类,代理类和被代理类均为继承关系,是“父子关系”,被代理的方法就是父类中公开的方法。

如下图:

其实细细想想也很正常,我们想要间接的动态的获取到某个类中公开的方法,有两种途径,第一种是继承自它,那么它所有的公开方法我们都能继续持有(cglib的思路)。第二种就是和他实现相同的约定(接口),那么它多有开发的协议,我们也就能动态的获取到了(jdk动态代理的思路)。

说了这么多来讲讲cglib方式的局限性,主要还是和继承有关系:

1、无法动态代理final方法,因为子类无法复写。

2、无法动态代理static,因为子类无法复写。

jdk动态代理和cglib可以说是各有优劣,很多人说经过jdk动态代理的速度优化,spring 目前已经默认采用jdk动态代理了,所以jdk动态代理更好。

这里我要说两点,

1、设计和实现是两回事,未来cglib的实现思路经过优化,又胜出了,也并不能说明cglib的设计方案更好;

2、目前的Springboot最新版本又采用了cglib作为默认的aop实现方式,这也并不能说明cglib就比jdk动态代理方式要强了。

ps:如果想要将运行时动态生成的class文件保存到磁盘中,可以在执行的代码出添加,如上文代码示例的红色字体处:

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code\\common\\learn-design-pattern\\target\\cglib");



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。