java的代理详解

从以下几个方面浅谈一下java的代理机制。如有不足,欢迎留言交流。

为什么使用代理

类比一:

假如你刚毕业,要租一个房子,有两种方式。一种就是自己去找房源,找房东,这样无疑时间成本是很高的。第二种方式就是找一个房产中介,你只需要提供你的需求和租房的规格和条件。中介就会推荐你心仪的房子。这里的中介就是代理人,房源就是目标对象,你可以直接找房源,也可以通过中介找房源。

类比二:

记者要采访一个儿童。首先需要经过他父母的同意,他的父母就记者提出的问题能进行甄选,那些可以回答,那些问题拒绝回答。这里父母就是代理人,儿童就是目标对象。通过代理可以增强或者控制儿童这个目标对象。

总结: 生活中这样代理的例子还有很多,通过以上的两个简单的例子可以看到,在生活中使用代理往往可以节省时间和成本,使资源能有效的利用。而在程序中使用代理也不例外。以Java的动态代理为例,它的优势就是实现无侵入式的代码扩展,也就是在不修改源码的情况下实现方法的增强,在方法的前后你可以自定义你的实现。在后面的篇幅中会用代码作以说明。

java中代理类型:

Java的代理类主要有静态代理和动态代理

静态代理:

由程序员创建或者由第三方工具生成,再进行编译;在程序运行之前代理类的.class文件已经存在,这种代理的方式需要代理的对象和目标对象实现一样的接口。

优点: 可以在不修改目标对象的前提下扩展目标对象的功能

缺点:

  1. 冗余, 由于代理对象实现与目标对象一致的接口,会产生过多的代理理
  2. 不易维护,耦合度太高,一旦接口增加方法,目标对象和代理对象都要进行修改

已更新用户的信息的为例:

1.创建接口

     package cn.chen.proxy;

     public interface UserDao {
     void update();
    }

2.目标对象,实现接口

     public class UserDaoImpl implements UserDao {

     @Override
     public void update() {
     System.out.println("更新信息");
    }
  }

3.创建的代理对象,必须实现接口

  public class ProxyUserDao implements UserDao{

   private UserDao userDao;

  public ProxyUserDao(UserDao userDao){
    this.userDao = userDao;
  }

   /**
   * 对原有的方法进行增强
   */
   @Override
   public void update() {
    System.out.println("核对你的信息");
    userDao.update();
    System.out.println("信息更新成功");
    }
 }

4.测试

    public class ProxyUserTest {
   public static void main(String[] args) {
    // 目标对象
    UserDao userDao = new UserDaoImpl();

    // 代理对象
    ProxyUserDao proxyUserDao = new ProxyUserDao(userDao);
    
     
    System.out.println(proxyUserDao.getClass().getName());
    proxyUserDao.update();
  }
 }

运行结果:

     cn.chen.proxy.ProxyUserDao
     核对你的信息
     更新用户
     信息更新成功 

动态代理:

在程序运行时通过反射机制动态生成

优点:动态代理对象不需要实现与目标对象一致的接口,但要求目标对象必须实现接口,否则不能使用动态代理。

缺点: 使用动态代理的对象必须实现一个或多个接口。在代理类没有接口的情况下,可以使用cglib实现动态代理,达到代理类的无侵入。

静态代理和动态代理的主要区别:

  1. 静态代理在编译时就已经实现,编译完成后的代理类是一个实际的class文件。
  2. 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,是在运行时动态生成类字节码,并加载到jvm中。
  3. 静态的代理类需要实现与目标对象一致的接口,耦合度较高,而动态的代理类则不用实现与目标对象一致的接口。

动态代理的实现:

java的动态代理利用了JDK Apl,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。

在jdk中生成代理对象主要涉及的类有:

  • java.lang.reflect.Proxy 主要方法为:

     public static Object newProxyInstance(ClassLoader loader,
                                        Class<?>[] interfaces,
                                        InvocationHandler h)
      throws IllegalArgumentException
      
      这个方法会给与我们来生成一个代理对象,含有三个参数
      
       clasLoader: 类加载器
       interface绑定的接口,也就是把代理对象绑定到那些接口下,可以是多个接口
       invocationHander   绑定对象的逻辑实现
    
  • java.lang.reflect InvocationHander,主要方法为:

     Object invoke(Object proxy, Method method, Object[] args)
      这个方法也有三个参数
      proxy  代理的对象
      method 当前的方法
      args 运行参数
    

还是以用户的信息更新为例:

1.创建接口

     package cn.chen.proxy;

     public interface UserDao {
     void update();
    }

2.目标对象,实现接口

    public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
    System.out.println("更新信息");
   }
 }

3.生成代理对象

public class DynamicProxyUser  implements InvocationHandler {

// 被代理的对象
private Object object;

 public DynamicProxyUser(){}

public DynamicProxyUser(Object object) {
    this.object = object;
}

/**
 * 处理代理对象的逻辑,所有被代理对象的方法都会在invoke中执行
 * @param proxy 代理的对象
 * @param method  当前方法
 * @param args  方法运行参数
 * @return  方法调用结果
 * @throws Throwable
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("核对你的信息");
    // 执行目标方法
     Object target = method.invoke(object,args);
    System.out.println("更新成功");
    return target;
}
}

4.测试:

    public class DynamicProxyTest {
    public static void main(String[] args) {

    // 目标对象
     UserDao userDao = new UserDaoImpl();

     DynamicProxyUser dynamicProxyUser = new DynamicProxyUser(userDao);

     ClassLoader loader = userDao.getClass().getClassLoader();

     // 调用Proxy的newProxyInstance()方法生成最终的代理对象
     UserDao proxy = (UserDao) Proxy.newProxyInstance(loader,new Class[]{UserDao.class},dynamicProxyUser);
     
     System.out.println(proxy.getClass().getName());
     proxy.update();
}
}

 运行结果:
  com.sun.proxy.$Proxy0
  核对你的信息
  更新信息
  更新成功       

当然,除了java的代理类型外,比较流行的还有CGLB Javassit,ASM 等。

java代理的应用场景:

  1. 切面编程
  2. 加事物,加权限
  3. 加日志
  4. Rpc框架的使用
原文地址:https://www.cnblogs.com/chentang/p/12444285.html