软件编程
位置:首页>> 软件编程>> java编程>> java面试常见模式问题---代理模式

java面试常见模式问题---代理模式

作者:兴趣使然の草帽路飞  发布时间:2022-02-14 13:07:31 

标签:代理模式,java
  • 本篇总结的是 代理设计模式,后续会经常更新~

  • 代理模式最直观的解释就是,通过代理,将被代理对象 “增强”!(即,扩展被代理对象的功能)

  • 代理模式分为静态代理,和 * : * 的代理类是动态生成的 , 静态代理的代理类是我们提前写好的逻辑。

  • Java 中实现 * 的方式有 2 种:

  • JDK *

  • CGLIB *

1、静态代理

静态代理角色分析

  • 抽象角色 :一般使用接口或者抽象类来实现。

  • 真实角色 :被代理的角色。

  • 代理角色: 代理真实角色 , 代理真实角色后 ,一般会做一些附属的操作。

  • 调用方:使用代理角色来进行一些操作。

我们以租客租客租房子为例,涉及到的对象有:租客、中介、房东。(房东即为被代理对象,中介即为代理对象)

租客通过中介之手租住房东的房子,代理对象中介需要寻找租客租房,并从中获取中介费用。

代码实现

Rent.java 即抽象角色


// 抽象角色:租房
public interface Rent {
  public void rent();
}

Host.java 即真实角色


// 真实角色: 房东,房东要出租房子
public class Host implements Rent{
  public void rent() {
      System.out.println("房屋出租");
 }
}

Proxy.java 即代理角色


//代理角色:中介
public class Proxy implements Rent {
  private Host host;
  public Proxy() { }
  public Proxy(Host host) {
      this.host = host;
 }
  // 租房
  public void rent(){
      seeHouse();
      host.rent();
      fare();
 }
  // 看房
  public void seeHouse(){
      System.out.println("带房客看房");
 }
  // 收中介费
  public void fare(){
      System.out.println("收中介费");
 }
}

Client.java 调用方,即客户


// 客户类,一般客户都会去找代理!
public class Client {
  public static void main(String[] args) {
      // 房东要租房
      Host host = new Host();
      // 中介帮助房东
      Proxy proxy = new Proxy(host);
      // 你去找中介!
      proxy.rent();
 }
}

静态代理的缺点

需要手动创建代理类,如果需要代理的对象多了,那么代理类也越来越多。

为了解决,这个问题,就有了 * !

2、 *

说到 * ,面试的时候肯定会问 * 的两种实现方式:

先来看公共的 UserService 接口,和 UserServiceImpl 实现类:


/**
* @author csp
* @date 2021-06-03
*/
public interface UserService {
   /**
    * 登录
    */
   void login();
   /**
    * 登出
    */
   void logout();
}

/**
* @author csp
* @date 2021-06-03
*/
public class UserServiceImpl implements UserService{
   @Override
   public void login() {
       System.out.println("用户登录...");
   }
   @Override
   public void logout() {
       System.out.println("用户推出登录...");
   }
}

JDK *

代码如下


/**
* @author csp
* @date 2021-06-03
*/
public class JDKProxyFactory implements InvocationHandler {
   // 目标对象(被代理对象)
   private Object target;
   public JDKProxyFactory(Object target) {
       super();
       this.target = target;
   }
   /**
    * 创建代理对象
    *
    * @return
    */
   public Object createProxy() {
       // 1.得到目标对象的类加载器
       ClassLoader classLoader = target.getClass().getClassLoader();
       // 2.得到目标对象的实现接口
       Class<?>[] interfaces = target.getClass().getInterfaces();
       // 3.第三个参数需要一个实现invocationHandler接口的对象
       Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, this);
       return newProxyInstance;
   }
   /**
    * 真正执行代理增强的方法
    *
    * @param proxy  代理对象.一般不使用
    * @param method 需要增强的方法
    * @param args   方法中的参数
    * @return
    */
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("JDK * :登录/登出前逻辑校验......");
       Object invoke = method.invoke(target, args);
       System.out.println("JDK * :登录/登出后日志打印......");
       return invoke;
   }
   public static void main(String[] args) {
       // 1.创建对象
       UserServiceImpl userService = new UserServiceImpl();
       // 2.创建代理对象
       JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(userService);
       // 3.调用代理对象的增强方法,得到增强后的对象
       UserService userServiceProxy = (UserService) jdkProxyFactory.createProxy();
       userServiceProxy.login();
       System.out.println("==================================");
       userServiceProxy.logout();
   }
}

输出结果如下

JDK * :登录/登出前逻辑校验......
用户登录...
JDK * :登录/登出后日志打印......
==================================
JDK * :登录/登出前逻辑校验......
用户推出登录...
JDK * :登录/登出后日志打印......

CGLIB *

代码如下:


/**
* @author csp
* @date 2021-06-03
*/
public class CglibProxyFactory implements MethodInterceptor {
   // 目标对象(被代理对象)
   private Object target;
   // 使用构造方法传递目标对象
   public CglibProxyFactory(Object target) {
       super();
       this.target = target;
   }
   /**
    * 创建代理对象
    *
    * @return
    */
   public Object createProxy() {
       // 1.创建Enhancer
       Enhancer enhancer = new Enhancer();
       // 2.传递目标对象的class
       enhancer.setSuperclass(target.getClass());
       // 3.设置回调操作
       enhancer.setCallback(this);
       return enhancer.create();
   }
   /**
    * 真正执行代理增强的方法
    * @param o 代理对象
    * @param method 要增强的方法
    * @param objects 要增强方法的参数
    * @param methodProxy 要增强的方法的代理
    * @return
    * @throws Throwable
    */
   @Override
   public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
       System.out.println("cglib * :登录/登出前逻辑校验......");
       Object invoke = method.invoke(target, objects);
       System.out.println("cglib * :登录/登出后日志打印......");
       return invoke;
   }
   public static void main(String[] args) {
       // 1.创建对象
       UserServiceImpl userService = new UserServiceImpl();
       // 2.创建代理对象
       CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(userService);
       // 3.调用代理对象的增强方法,得到增强后的对象
       UserService userServiceProxy = (UserService) cglibProxyFactory.createProxy();
       userServiceProxy.login();
       System.out.println("==================================");
       userServiceProxy.logout();
   }
}

测试结果如下

cglib * :登录/登出前逻辑校验......
用户登录...
cglib * :登录/登出后日志打印......
==================================
cglib * :登录/登出前逻辑校验......
用户推出登录...
cglib * :登录/登出后日志打印......

面试题一:JDK * 和CGLIB * 区别?

① JDK * 本质上是实现了被代理对象的接口,而 CGLib 本质上是继承了被代理对象,覆盖其中的方法。

② JDK * 只能对实现了接口的类生成代理,CGLib 则没有这个限制。但是 CGLib 因为使用继承实现,所以 CGLib 所以无法对 final private 方法static方法进行代理。

③ JDK * 是 JDK 里自带的,CGLib * 需要引入第三方的 jar 包。

④ 在调用代理方法上,JDK * 是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用。(看过一篇文章,介绍说 FastClass 简单的理解,就是使用一个 index 下标作为入参,可以直接定位到要调用的方法直接,并进行调用)

在性能上,JDK1.7 之前,由于使用了 FastClass 机制,CGLib 在执行效率上比 JDK 快,但是随着 JDK * 的不断优化,从 JDK 1.7 开始,JDK * 已经明显比 CGLib 更快了。

面试题二:JDK * 为什么只能对实现了接口的类生成代理?

根本原因是通过 JDK * 生成的类已经继承了 Proxy 类,所以无法再使用继承的方式去对类实现代理。

来源:https://csp1999.blog.csdn.net/article/details/117672843#comments_16904981

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com