zoukankan      html  css  js  c++  java
  • 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框架的使用
  • 相关阅读:
    JS-记住用户名【cookie封装引申】
    JS-cookie封装
    JS-比较函数中嵌套函数,可以排序【对象数组】
    JS-随机div颜色
    JS-过滤敏感词【RegExp】
    JS-提取字符串—>>普通方法VS正则表达式
    CSS- ie6,ie7,ie8 兼容性写法,CSS hack写法
    JS-【同页面多次调用】轮播特效封装-json传多个参数
    JS-【同页面多次调用】tab选项卡封装
    Redis主从同步
  • 原文地址:https://www.cnblogs.com/chentang/p/12444285.html
Copyright © 2011-2022 走看看