zoukankan      html  css  js  c++  java
  • Java动态代理简单应用

    概念

      代理模式是基本的设计模式之一,它是开发者为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。

      Java动态代理比代理的思想更进一步,因为它可以动态地创建代理并动态地处理对代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器(InvocationHandler)上,调用处理器的工作是揭示调用的类型并确定相应的策略。

      Java动态代理实现机制采用了反射的思想,有关于反射的基础知识,可以参见博客Java发射机制浅析

    原理

      Spring核心AOP实现技术之一就是采用Java动态代理机制,权限认证、日志以及事务管理都可以采用动态代理,使用动态代理机制可以减少代码重复,降低系统耦合度。Java动态代理API位于java.lang.reflect包下,实现动态代理主要涉及以下两个关键步骤:

    1.实现InvocationHandler接口

    InvocationHandler接口仅定义了一个方法。

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    

    proxy:代理对象

    method:被代理类方法对象

    args:被代理方法参数数组

    proxy参数表示代理对象,以防开发者需要区分请求的来源,但是在许多情况下,开发者并不关心这一点。InvocationHandler通常需要持有被代理对象的引用,在动态代理上所做的所有调用都会被重定向到InvocationHandler接口的invoke方法上。在invoke方法内部调用被代理对象的method方法,开发者可以在方法调用前后加上需要的逻辑。如果只是想对某些方法进行代理,开发者可以在invoke方法内添加相应的过滤条件,通过method参数可以获取方法本身的很多信息。

    2.通过Proxy创建动态代理类实例

    Proxy是所有动态代理类的父类,并提供一个静态方法newProxyInstance负责创建动态代理类的实例。  

     public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
          throws IllegalArgumentException

    loader:类加载器(通常可以从已经被加载的对象中获取其类加载器)

    interfaces:代理类需要实现的接口列表(通常使用被代理类实现的接口列表)

    hInvocationHandler接口的一个实现

    newProxyInstance方法会根据传入的接口列表interfaces在虚拟机中动态的生成一个Proxy的子类,并实现了传入的接口列表interfaces的所有方法,然后返回该类的一个实例。因此返回的对象能够转型到传入的任意一个接口,通过该对象对接口内所有方法的调用都会重定向到第三个参数hinvoke方法。

    Java动态代理底层原理实现比较复杂。总而言之,是根据newProxyInstance方法传入的类加载器和接口,生成代理类的二进制字节码,再利用反射机制创建代理类的实例,在代理类的内部对被代理类方法的调用都会重定向到InvocationHandler中的invoke方法,在invoke方法内再对被代理方法的调用进行封装预处理,加上开发者自己的逻辑。

    应用

    下面演示两个简单的示例:方法性能监测和日志管理

    实体类 

    public class User {
        
        private String name;
    
        public User(String name) {
            super();
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        
    }

     接口

    public interface UserDao {
        
        void addUser(User user);
        
        void deleteUser(String name);
        
        void updateUser(String name);
    
    }

     实现类

    public class UserDaoImpl implements UserDao {
    
        @Override
        public void addUser(User user) {
            System.out.println("add user named " + user.getName() + "...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void deleteUser(String name) {
            System.out.println("delete user named " + name + "...");
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void updateUser(String name) {
            System.out.println("update user named " + name + "...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }

    方法性能监测

    监测方法运行性能效率,此处简单以方法运行时间作为评价标准。传统的做法是在每个方法执行前后各自记录时间,然后计算运行时间。采用动态代理机制,可以将以上操作统一到InvocationHandler中,并创建代理类监测所有的方法。

    InvocationHandler实现类 

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class PerformanceInterceptor implements InvocationHandler {
        
        private Object proxied;
        
        public PerformanceInterceptor(Object proxied) {
            this.proxied = proxied;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long startTime = System.currentTimeMillis();
            Object obj = method.invoke(proxied, args);
            long endTime = System.currentTimeMillis();
            System.out.println("Method " + method.getName() + " execution time: " + (endTime - startTime) * 1.0 / 1000 + "s");
            return obj;
        }
    
    }

    测试类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    import org.junit.Test;
    
    public class PerformanceInterceptorTest {
        
        @Test
        public void testInvoke() {
            UserDao userDao = new UserDaoImpl(); 
            Class<?> cls = userDao.getClass();
            InvocationHandler handler = new PerformanceInterceptor(userDao);
            UserDao proxy = (UserDao) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);
            proxy.addUser(new User("tom"));
            proxy.deleteUser("tom");
            proxy.updateUser("tom");
        }
        
    }

    测试结果 

    add user named Tom...
    Method addUser execution time: 1.0s
    delete user named tom...
    Method deleteUser execution time: 1.5s
    update user named tom...
    Method updateUser execution time: 2.0s

    日志管理

    有时候开发者需要在方法开始执行和执行结束时打印一些信息,使用动态代理就可以对所有方法或者某些方法进行简单日志管理,并写入到指定文件。

    InvocationHandler实现类 

    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    
    public class LogInterceptor implements InvocationHandler {
        
        private Object proxied;
        
        public static final String path = "run.log";
        
        public LogInterceptor(Object proxied) {
            this.proxied = proxied;
        }
        
        public String beforeMethod(Method method) {
            return getFormatedTime() + " Method:" + method.getName() + " start running
    ";
        }
        
        public String afterMethod(Method method) {
            return getFormatedTime() + " Method:" + method.getName() + " end running
    ";
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            write(path, beforeMethod(method));
            Object object = method.invoke(proxied, args);
            write(path, afterMethod(method));
            return object;
        }
        
        public String getFormatedTime() {
            DateFormat formater = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            return formater.format(System.currentTimeMillis());
        }
        
        public void write(String path, String content) {
            FileWriter writer = null;
            try {
                writer = new FileWriter(new File(path), true);
                writer.write(content);
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(null != writer) {
                    try {
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }

    测试类 

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    import org.junit.Test;
    
    public class LogInterceptorTest {
        
        @Test
        public void testInvoke() {
            UserDao userDao = new UserDaoImpl();
            Class<?> cls = userDao.getClass();
            InvocationHandler handler = new LogInterceptor(userDao);
            UserDao proxy = (UserDao) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);
            proxy.addUser(new User("tom"));
            proxy.deleteUser("tom");
            proxy.updateUser("tom");
        }
        
    }

    测试结果

    在工程根目录下生成run.log文件,内容如下: 

    2015-10-07 05:41:02 Method:addUser start running
    2015-10-07 05:41:03 Method:addUser end running
    2015-10-07 05:41:03 Method:deleteUser start running
    2015-10-07 05:41:05 Method:deleteUser end running
    2015-10-07 05:41:05 Method:updateUser start running
    2015-10-07 05:41:07 Method:updateUser end running

    总结

      尽管Java动态代理机制设计的已经非常出色,美中不足之处的是,Java动态代理只能代理实现接口的类。若想要对没有实现任何接口的类进行代理,这种情况确实罕见,就需要使用cdlibJava动态代理机制是实现AOP编程的重要技术之一,动态代理并非开发者日常使用的工具,但是在某些特殊的场景,使用动态代理可以非常好地解决某些类型的问题。


    作者:VictorWong
    出处:http://www.cnblogs.com/jwongo
    github:https://github.com/jwongo
    本文版权归作者和博客园共有,欢迎转载。水平有限,恳请读者批评指正。如果觉得对您有点用,就点个赞支持一下吧。

  • 相关阅读:
    php : Warning: strftime(): It is not safe to rely on the system's timezone settings.
    php : DOM 操作 XML
    php : 基础(3)
    php : 基础(2)
    阿里云物联网平台体验(NetGadgeteer+C#篇)
    阿里云物联网平台体验(树莓派+Nodejs篇)
    阿里云物联网平台体验(树莓派+Python篇)
    【MVP时间】5节课助你破解物联网硬件接入难点
    破解物联网落地困境-阿里云硬件接入最佳实践
    从端到云——工业物联网项目全栈快速开发实践
  • 原文地址:https://www.cnblogs.com/jwongo/p/java-dynamic-proxy.html
Copyright © 2011-2022 走看看