【计划模式】代理模式

打印 上一主题 下一主题

主题 872|帖子 872|积分 2616

一、什么是代理模式

概念:



  • 代理模式是一种布局型计划模式,它允许通过创建一个代理对象来控制对另一个对象的访问。
分类:


  • 静态代理:在编译时就已经确定了代理对象和真实对象的关系,代理对象和真实对象实现相同的接口或继续相同的父类。在代理对象中,对真实对象的方法举行封装,在方法调用前后可以添加额外的逻辑。
  • 动态代理:在运行时生成代理对象,不需要事先确定代理对象和真实对象的关系。常用的动态代理技术有JDK动态代理和CGLIB动态代理。

    • JDK动态代理:通过使用Java的反射机制,动态生成代理对象。代理对象必须实现至少一个接口,通过Proxy类和InvocationHandler接口实现代理对象的创建和方法调用的处理。
    • CGLIB动态代理:通过继续的方式,动态生成代理对象。代理对象无需实现接口,通过Enhancer类和MethodInterceptor接口实现代理对象的创建和方法调用的处理。

代理模式的脚色构成:


  • 抽象主题(Subject):抽象主题定义了真实主题和代理共同的接口,它是客户端和代理对象之间的束缚。
  • 真实主题(Real Subject):真实主题是现实执行业务逻辑的对象,它实现了抽象主题的接口。
  • 代理(Proxy):代理是对真实主题的访问控制和管理对象。代理实现了抽象主题的接口,并在其内部持有一个真实主题的引用。代理对象可以在访问真实主题之前举行一些额外的处理,比方权限验证、缓存等。
代理模式中的客户端通过与代理对象举行交互,而代理对象负责将哀求转发给真实主题对象。客户端无需直接与真实主题对象打交道,而是通过代理对象来举行间接访问。这样做的好处是可以在代理对象中添加一些额外的功能,同时也可以对真实主题对象举行访问控制,提高了体系的灵活性和安全性。
二、代理模式的Java实现示例

1、静态代理的实现

起首定义一个接口 Subject,包罗一个方法 request():
  1. public interface Subject {
  2.     void request();
  3. }
复制代码
然后创建一个真实的对象 RealSubject,实现 Subject 接口:
  1. public class RealSubject implements Subject {
  2.     @Override
  3.     public void request() {
  4.         System.out.println("真实的对象处理请求");
  5.     }
  6. }
复制代码
接着创建一个代理对象 ProxySubject,同样实现 Subject 接口,并持有一个真实对象的引用:
  1. public class ProxySubject implements Subject {
  2.     private RealSubject realSubject;
  3.     public ProxySubject(RealSubject realSubject) {
  4.         this.realSubject = realSubject;
  5.     }
  6.     @Override
  7.     public void request() {
  8.         System.out.println("代理对象处理请求之前的操作");
  9.         realSubject.request();
  10.         System.out.println("代理对象处理请求之后的操作");
  11.     }
  12. }
复制代码
最后,可以通过以下方式使用代理对象:
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         RealSubject realSubject = new RealSubject();
  4.         ProxySubject proxySubject = new ProxySubject(realSubject);
  5.         proxySubject.request();
  6.     }
  7. }
复制代码
运行上述代码可以得到以下输出:
  1. 代理对象处理请求之前的操作
  2. 真实的对象处理请求
  3. 代理对象处理请求之后的操作
复制代码
在这个示例中,ProxySubject 是代理对象,通过持有一个 RealSubject 对象的引用,来代理真实对象的方法调用。在代理对象的 request() 方法中,可以在真实对象的方法调用前后添加一些额外的操纵,从而实现了静态代理。
2、jDK动态代理的实现

在Java中,动态代理可以使用 java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口来实现。下面是一个简单的动态代理的示例代码:
起首定义一个接口 Subject,包罗一个方法 request():
  1. public interface Subject {
  2.     void request();
  3. }
复制代码
然后创建一个真实的对象 RealSubject,实现 Subject 接口:
  1. public class RealSubject implements Subject {
  2.     @Override
  3.     public void request() {
  4.         System.out.println("真实的对象处理请求");
  5.     }
  6. }
复制代码
接着创建一个实现 InvocationHandler 接口的代理处理器 ProxyHandler:
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. public class ProxyHandler implements InvocationHandler {
  4.     private Object target;
  5.     public ProxyHandler(Object target) {
  6.         this.target = target;
  7.     }
  8.     @Override
  9.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  10.         System.out.println("代理对象处理请求之前的操作");
  11.         Object result = method.invoke(target, args);
  12.         System.out.println("代理对象处理请求之后的操作");
  13.         return result;
  14.     }
  15. }
复制代码
最后,可以通过以下方式使用动态代理对象:
  1. import java.lang.reflect.Proxy;
  2. public class Main {
  3.     public static void main(String[] args) {
  4.         RealSubject realSubject = new RealSubject();
  5.         ProxyHandler proxyHandler = new ProxyHandler(realSubject);
  6.         Subject proxySubject = (Subject) Proxy.newProxyInstance(
  7.                 RealSubject.class.getClassLoader(),
  8.                 RealSubject.class.getInterfaces(),
  9.                 proxyHandler);
  10.         proxySubject.request();
  11.     }
  12. }
复制代码
运行上述代码可以得到以下输出:
  1. 代理对象处理请求之前的操作
  2. 真实的对象处理请求
  3. 代理对象处理请求之后的操作
复制代码
在这个示例中,ProxyHandler 是代理处理器,通过实现 InvocationHandler 接口,可以在代理对象的方法调用前后添加一些额外的操纵。在 main() 方法中,使用 Proxy 类的 newProxyInstance() 方法动态创建一个代理对象,并将代理处理器传入。然后通过代理对象调用 request() 方法,现实上是调用了代理处理器的 invoke() 方法,在该方法中可以添加额外的操纵,并通过反射调用真实对象的现实方法。
留意,动态代理要求目的类必须实现一个接口,由于动态代理是基于接口来创建代理对象的。
3、CGLIB动态代理的实现

CGLIB(Code Generation Library)是一个用于生成Java类字节码的开源库,它可以在运行时动态生成一个指定类的子类作为代理类。CGLIB动态代理主要用于代理那些没有实现接口的类。
下面是CGLIB动态代理的一个简单示例:
起首,我们需要引入cglib库的依赖,比方使用Maven:
  1. <dependency>
  2.     <groupId>cglib</groupId>
  3.     <artifactId>cglib</artifactId>
  4.     <version>3.3.0</version>
  5. </dependency>
复制代码
然后,创建一个目的类 UserDao:
  1. public class UserDao {
  2.     public void save() {
  3.         System.out.println("保存用户信息");
  4.     }
  5. }
复制代码
接下来,创建一个代理类 CglibProxy:
  1. import net.sf.cglib.proxy.Enhancer;
  2. import net.sf.cglib.proxy.MethodInterceptor;
  3. import net.sf.cglib.proxy.MethodProxy;
  4. import java.lang.reflect.Method;
  5. public class CglibProxy implements MethodInterceptor {
  6.     private Object target;
  7.     public CglibProxy(Object target) {
  8.         this.target = target;
  9.     }
  10.     public Object getProxy() {
  11.         Enhancer enhancer = new Enhancer();
  12.         enhancer.setSuperclass(target.getClass());
  13.         enhancer.setCallback(this);
  14.         return enhancer.create();
  15.     }
  16.     @Override
  17.     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  18.         System.out.println("代理前的操作");
  19.         Object result = proxy.invoke(target, args);
  20.         System.out.println("代理后的操作");
  21.         return result;
  22.     }
  23. }
复制代码
最后,我们可以使用代理类来生成代理对象并调用目的类的方法:
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         UserDao userDao = new UserDao();
  4.         CglibProxy cglibProxy = new CglibProxy(userDao);
  5.         UserDao proxy = (UserDao) cglibProxy.getProxy();
  6.         proxy.save();
  7.     }
  8. }
复制代码
运行程序,输出效果为:
  1. 代理前的操作
  2. 保存用户信息
  3. 代理后的操作
复制代码
可以看到,CGLIB动态代理通过创建目的类的子类,实现了对目的类方法的代理,同时在代理前后举行了一些额外的操纵。
三、动态带来和静态代理的区别

动态代理和静态代理都是实现代理模式的方式,它们之间的主要区别在于代理对象的生成时机和方式。

  • 代理对象的生成时机:

    • 静态代理需要在编译时期就创建代理对象,即在程序运行之前就已经存在代理类的字节码文件。代理类一般通过手工编写代码来创建。
    • 动态代理是在运行时期动态生成代理对象。代理类是根据目的类和代理处理器的信息,在程序运行时动态生成的。

  • 代理对象的生成方式:

    • 静态代理需要为每个目的类手动编写一个代理类,将目的类的方法委托给代理类来举行处理。
    • 动态代理通过使用 Proxy 类和 InvocationHandler 接口,在运行时动态生成代理对象。代理对象在运行时动态地实现了目的接口,并将方法的调用委托给代理处理器。

  • 灵活性和扩展性:

    • 动态代理相比静态代理更加灵活,可以代理任意实现了接口的目的类,无需为每个目的类都编写一个代理类。
    • 静态代理相比动态代理的扩展性较差,每次新增或修改目的类时,都需要手动修改代理类。

总体来说,动态代理相比静态代理具有更高的灵活性和扩展性,但代理对象的生成过程相对复杂一些。静态代理则相对简单,代理对象的生成在编译时期就已经完成。选择使用哪种代理方式取决于具体的需求和场景。
四、代理模式的应用场景


  • 远程代理:用于在差别的地址空间中访问远程对象。
    比方,调用远程服务器上的WebService接口时,可以使用代理模式来隐藏网络通信的细节。
  • 假造代理:用于延迟加载对象,提高体系性能。
    比方,当需要加载大量图片时,可以使用假造代理来在表现图片时才真正加载和表现,而不是一次性加载所有图片。
  • 安全代理:用于控制对对象的访问权限。
    比方,对于某些敏感操纵或数据,可以使用安全代理来举行权限验证,确保只有授权用户才气访问。
  • 缓存代理:用于缓存对象的效果,避免重复执行开销较大的操纵。
    比方,对于频繁访问数据库的操纵,可以使用缓存代理来缓存查询效果,减少对数据库的访问。
  • 日记记载代理:用于在调用对象方法前后举行日记记载,以便举行调试或性能分析。
    比方,可以使用日记记载代理来记载每个方法的调用时间和参数。
  • 计数代理:用于对对象的方法调用举行计数,可以用于统计方法的调用次数等。
    比方,可以使用计数代理来统计某个方法被调用的次数,以便举行性能分析或优化。
总的来说,代理模式实用于需要在不改变客户端代码的情况下增加额外功能或控制对对象的访问的场景。它可以提供更加灵活、安全和高效的访问方式。
五、SpringBoot中代理模式的实现

在Spring Boot中,代理模式的实现主要通过Spring框架提供的AOP(面向切面编程)功能实现。Spring AOP基于动态代理技术,可以在运行时生成代理对象,并在代理对象的方法执行前后插入额外的逻辑。
以下是使用Spring Boot实现代理模式的示例代码:

  • 定义一个接口(被代理对象):
  1. public interface UserService {
  2.     void addUser(String username);
  3.     void deleteUser(String username);
  4. }
复制代码

  • 创建一个实现该接口的目的对象:
  1. @Service
  2. public class UserServiceImpl implements UserService {
  3.     @Override
  4.     public void addUser(String username) {
  5.         System.out.println("添加用户:" + username);
  6.     }
  7.    
  8.     @Override
  9.     public void deleteUser(String username) {
  10.         System.out.println("删除用户:" + username);
  11.     }
  12. }
复制代码

  • 创建一个切面类,实现对目的对象方法的增强逻辑:
  1. @Component
  2. @Aspect
  3. public class LogAspect {
  4.     @Before("execution(* com.example.UserService.*(..))")
  5.     public void beforeMethod(JoinPoint joinPoint){
  6.         String methodName = joinPoint.getSignature().getName();
  7.         System.out.println("调用方法:" + methodName);
  8.     }
  9.   
  10.     @AfterReturning("execution(* com.example.UserService.*(..))")
  11.     public void afterMethod(JoinPoint joinPoint){
  12.         String methodName = joinPoint.getSignature().getName();
  13.         System.out.println("方法调用结束:" + methodName);
  14.     }
  15. }
复制代码

  • 在Spring Boot的设置类中启用AOP功能:
  1. @Configuration
  2. @EnableAspectJAutoProxy
  3. public class AppConfig {
  4.     // 其他配置...
  5. }
复制代码

  • 通过Spring的依赖注入,使用代理对象调用方法:
  1. @RestController
  2. public class UserController {
  3.     @Autowired
  4.     private UserService userService;
  5.    
  6.     @PostMapping("/users")
  7.     public void addUser(@RequestParam String username) {
  8.         userService.addUser(username);
  9.     }
  10.    
  11.     @DeleteMapping("/users/{username}")
  12.     public void deleteUser(@PathVariable String username) {
  13.         userService.deleteUser(username);
  14.     }
  15. }
复制代码
通过以上步骤,可以实现在调用UserService接口的方法前后自动插入日记记载的功能,而不需要显式在每个方法中添加日记代码。这就是使用Spring Boot实现代理模式的方式之一。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美丽的神话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表