Java 代理模式

1、代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。

 

 

 

代理(Proxy)模式结构图如下:

 

 

JAVA的代理有如下3种:

  • 静态代理
  • 动态代理
  • cglib代理

 

2、静态代理

接口类AdminService.java接口

1 package com.lance.proxy.demo.service;
2 
3 public interface AdminService {
4     void update();
5     Object find();
6 }

实现类AdminServiceImpl.java

 1 package com.lance.proxy.demo.service;
 2 
 3 public class AdminServiceImpl implements AdminService{
 4     public void update() {
 5         System.out.println("修改数据");
 6     }
 7 
 8     public Object find() {
 9         System.out.println("查找数据");
10         return new Object();
11     }
12 }

代理类AdminServiceProxy.java

 1 public class AdminServiceProxy implements AdminService{
 2     private AdminService adminService;
 3 
 4     public AdminServiceProxy(AdminService adminService) {
 5         this.adminService = adminService;
 6     }
 7     @Override
 8     public void update() {
 9         System.out.println("查询权限判断");
10         adminService.update();
11         System.out.println("更新结束");
12     }
13 
14     @Override
15     public Object find() {
16         System.out.println("判断查找权限");
17         Object o = adminService.find();
18         System.out.println("查找结束");
19         return o;
20     }
21 }

测试类StaticProxyTest.java

1 public class StaticProxyTest {
2     public static void main(String[] args) {
3         AdminService adminService = new AdminServiceImpl();
4         AdminServiceProxy proxy = new AdminServiceProxy(adminService);
5         proxy.update();
6         System.out.println("===========================");
7         proxy.find();
8     }
9 }

结果如下:

 

 

 

总结:

静态代理模式在不改变目标对象的前提下,实现了对目标对象的功能扩展。

不足:

静态代理实现了目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。

 

3、JDK动态代理

为解决静态代理对象必须实现接口的所有方法的问题,Java给出了动态代理,动态代理具有如下特点:
1、Proxy对象不需要implements接口;
2、Proxy对象的生成利用JDK的Api,在JVM内存中动态的构建Proxy对象。需要使用java.lang.reflect.Proxy类的

 

方法,方法参数说明:

ClassLoader loader:指定当前target对象使用类加载器,获取加载器的方法是固定的;

Class<?>[] interfaces:target对象实现的接口的类型,使用泛型方式确认类型

nvocationHandler invocationHandler:事件处理,执行target对象的方法时,会触发事件处理器的方法,会把当前执行target对象的方法作为参数传入。

AdminServiceImpl.java和AdminService.java和原来一样。


AdminServiceInvocation.java

 1 public class AdminServiceInvocation implements InvocationHandler {
 2     private Object target;
 3 
 4     public AdminServiceInvocation(Object target){
 5         this.target = target;
 6     }
 7 
 8     @Override
 9     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
10         System.out.println("判断权限");
11         Object invoke = method.invoke(target);
12         System.out.println("操作结束");
13         return invoke;
14     }
15 }

AdminServiceDynamicProxy.java

 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Proxy;
 3 
 4 public class AdminServiceDynamicProxy {
 5     private Object target;
 6     private InvocationHandler invocationHandler;
 7     
 8     public AdminServiceDynamicProxy(Object target,InvocationHandler invocationHandler){
 9         this.target = target;
10         this.invocationHandler = invocationHandler;
11     }
12 
13     public Object getPersonProxy() {
14         Object obj = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocationHandler);
15         return obj;
16     }
17 }

DynamicProxyTest.java

 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Method;
 3 import java.lang.reflect.Proxy;
 4 
 5 public class DynamicProxyTest {
 6     public static void main(String[] args) {
 7 
 8         // 方法一
 9         System.out.println("============ 方法一 ==============");
10         AdminService adminService = new AdminServiceImpl();
11         System.out.println("代理的目标对象:" + adminService.getClass());
12         AdminServiceInvocation adminServiceInvocation = new AdminServiceInvocation(adminService);
13         AdminService proxy = (AdminService) new AdminServiceDynamicProxy(adminService, adminServiceInvocation).getPersonProxy();
14         System.out.println("代理对象:" + proxy.getClass());
15         
16         Object obj = proxy.find();
17         System.out.println("find 返回对象:" + obj.getClass());
18         System.out.println("----------------------------------");
19         proxy.update();
20 
21         //方法二
22         System.out.println("============ 方法二 ==============");
23         AdminService target = new AdminServiceImpl();
24         AdminServiceInvocation invocation = new AdminServiceInvocation(adminService);
25         AdminService proxy2 = (AdminService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocation);
26 
27         Object obj2 = proxy2.find();
28         System.out.println("find 返回对象:" + obj2.getClass());
29         System.out.println("----------------------------------");
30         proxy2.update();
31 
32         //方法三
33         System.out.println("============ 方法三 ==============");
34         final AdminService target3 = new AdminServiceImpl();
35         AdminService proxy3 = (AdminService) Proxy.newProxyInstance(target3.getClass().getClassLoader(), target3.getClass().getInterfaces(), new InvocationHandler() {
36             @Override
37             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
38                 System.out.println("判断用户是否有权限进行操作");
39                 Object obj = method.invoke(target3, args);
40                 System.out.println("记录用户执行操作的用户信息、更改内容和时间等");
41                 return obj;
42             }
43         });
44 
45         Object obj3 = proxy3.find();
46         System.out.println("find 返回对象:" + obj3.getClass());
47         System.out.println("----------------------------------");
48         proxy3.update();
49     }
50 }

结果如下:

 1 ============ 方法一 ==============
 2 代理的目标对象:class com.proxy.java.service.AdminServiceImpl
 3 代理对象:class com.sun.proxy.$Proxy0
 4 判断权限
 5 查找数据
 6 操作结束
 7 find 返回对象:class java.lang.Object
 8 ----------------------------------
 9 判断权限
10 更新数据
11 操作结束
12 ============ 方法二 ==============
13 判断权限
14 查找数据
15 操作结束
16 find 返回对象:class java.lang.Object
17 ----------------------------------
18 判断权限
19 更新数据
20 操作结束
21 ============ 方法三 ==============
22 判断用户是否有权限进行操作
23 查找数据
24 记录用户执行操作的用户信息、更改内容和时间等
25 find 返回对象:class java.lang.Object
26 ----------------------------------
27 判断用户是否有权限进行操作
28 更新数据
29 记录用户执行操作的用户信息、更改内容和时间等

 

 

4、Cglib代理

JDK动态代理要求target对象是一个接口的实现对象,假如target对象只是一个单独的对象,并没有实现任何接口,这时候就会用到Cglib代理(Code Generation Library),即通过构建一个子类对象,从而实现对target对象的代理,因此目标对象不能是final类(报错),且目标对象的方法不能是final或static(不执行代理功能)。

Cglib依赖的jar包

  <dependency>
       <groupId>cglib</groupId>
       <artifactId>cglib</artifactId>
       <version>3.2.10</version>
  </dependency>

 

目标对象类AdminCglibService.java

 1 public class AdminCglibService {
 2     public void update(){
 3         System.out.println("修改数据");
 4     }
 5 
 6     public Object find() {
 7         System.out.println("查看管理系统数据");
 8         return new Object();
 9     }
10 }

代理类AdminServiceCglibProxy.java

 1 import org.springframework.cglib.proxy.Enhancer;
 2 import org.springframework.cglib.proxy.MethodInterceptor;
 3 import org.springframework.cglib.proxy.MethodProxy;
 4 
 5 import java.lang.reflect.Method;
 6 
 7 
 8 public class AdminServiceCglibProxy implements MethodInterceptor {
 9     private Object target;
10 
11     public AdminServiceCglibProxy(Object target){
12         this.target = target;
13     }
14 
15     public Object getProxyInstance(){
16         Enhancer en = new Enhancer();
17         en.setSuperclass(target.getClass());
18         en.setCallback(this);
19         return en.create();
20     }
21 
22     @Override
23     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
24         System.out.println("判断用户是否有权限进行操作");
25         Object obj = method.invoke(target);
26         System.out.println("记录用户执行操作的用户信息、更改内容和时间等");
27         return obj;
28     }
29 }

Cglib代理测试类CglibProxyTest.java

 1 public class CglibProxyTest {
 2     public static void main(String[] args) {
 3 
 4         AdminCglibService target = new AdminCglibService();
 5         AdminServiceCglibProxy proxyFactory = new AdminServiceCglibProxy(target);
 6         AdminCglibService proxy = (AdminCglibService)proxyFactory.getProxyInstance();
 7 
 8         System.out.println("代理对象:" + proxy.getClass());
 9 
10         Object obj = proxy.find();
11         System.out.println("find 返回对象:" + obj.getClass());
12         System.out.println("----------------------------------");
13         proxy.update();
14     }
15 }

 

结果为:

1 代理对象:class com.study.javamultithread.service.AdminCglibService$$EnhancerByCGLIB$$48403b74
2 判断用户是否有权限进行操作
3 查看管理系统数据
4 记录用户执行操作的用户信息、更改内容和时间等
5 find 返回对象:class java.lang.Object
6 ----------------------------------
7 判断用户是否有权限进行操作
8 修改数据
9 记录用户执行操作的用户信息、更改内容和时间等

 

总结

理解上述Java代理后,也就明白Spring AOP的代理实现模式,即加入Spring中的target是接口的实现时,就使用JDK动态代理,否是就使用Cglib代理。

Spring也可以通过<aop:config proxy-target-class="true">强制使用Cglib代理,使用Java字节码编辑类库ASM操作字节码来实现,直接以二进制形式动态地生成 stub 类或其他代理类,性能比JDK更强。

posted @ 2022-03-23 17:10  r1-12king  阅读(83)  评论(0编辑  收藏  举报