• Java动态编程---动态代理


    java中动态编程用到的技术有:反射(动态代理),javassist和ASM,这几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象。在实现成本上Javassist和反射都很低,而ASM由于直接操作字节码,相比Javassist源码级别的api实现成本高很多。几个方法有自己的应用场景,比如Kryo使用的是ASM,追求性能的最大化。而NBeanCopyUtil采用的是Javassist,在对象拷贝的性能上也已经明显高于其他的库,并保持高易用性。实际项目中推荐先用Javassist实现原型,若在性能测试中发现Javassist成为了性能瓶颈,再考虑使用其他字节码操作方法做优化。

    下面以代码的方式实现一个动态代理。

    目标:

           在调用持久层UserDAO将业务数据写入数据库的前后加入日志的功能。代码结构如下图所示:

    User:

    package dynamicproxy.model;
    
    public class User {
        public User(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
        private String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }

    IUserDAO:

    package dynamicproxy.dao;
    
    import dynamicproxy.model.User;
    
    public interface IUserDAO {
    
        String addUser(User user);
    
        String updateUser(User user);
    }

    UserDAO:

    package dynamicproxy.dao;
    
    import dynamicproxy.model.User;
    
    public class UserDAO implements IUserDAO {
        public String addUser(User user) {
            System.out.println("开始向数据库中写入数据...");
            return String.format("添加用户[%s]成功", user.getName());
        }
    
        public String updateUser(User user) {
            System.out.println("开始向数据库中写入数据...");
            return String.format("修改用户[%s]成功", user.getName());
        }
    }

    关键的InvocationHandler类LogInterceptor:

    package dynamicproxy.interceptor;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class LogInterceptor implements InvocationHandler {
        private Object target;
    
        /*
         * 在执行指定方法之前调用
         */
        private void beforeMethod(Method method) {
            System.out.println(String.format("日志:用户开始执行[%s]方法...", method.getName()));
        }
    
        /*
         * 在执行指定方法之后调用
         */
        private void afterMethod(Method method) {
            System.out.println(String.format("日志:用户执行[%s]方法完成.", method.getName()));
        }
    
        public LogInterceptor(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            beforeMethod(method);// 加入日志处理逻辑
            Object result = method.invoke(target, args);// 调用被代理对象的指定method方法
            afterMethod(method);// 加入日志处理逻辑
            return result;
        }
    
    }

    测试代码:

    package dynamicproxy;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    import org.junit.Test;
    import dynamicproxy.dao.IUserDAO;
    import dynamicproxy.dao.UserDAO;
    import dynamicproxy.interceptor.LogInterceptor;
    import dynamicproxy.model.User;
    
    public class UserDAOTest {
    
        @Test
        public void test() {
            IUserDAO dao = new UserDAO();
            InvocationHandler logInterceptor = new LogInterceptor(dao);// InvocationHandler
            dao = (IUserDAO) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(),
                    logInterceptor);
            System.out.println(dao.addUser(new User("franson", 21)));
            System.out.println(System.lineSeparator());
            System.out.println(dao.updateUser(new User("franson", 21)));
        }
    
    }

    运行JUnit测试结果如下:

    日志:用户开始执行[addUser]方法...
    开始向数据库中写入数据...
    日志:用户执行[addUser]方法完成.
    添加用户[franson]成功
    
    
    日志:用户开始执行[updateUser]方法...
    开始向数据库中写入数据...
    日志:用户执行[updateUser]方法完成.
    修改用户[franson]成功

    这样就达到了我们在不修改原有业务逻辑代码的情况下实现添加日志的功能。

  • 相关阅读:
    Spring Boot 入门之持久层篇(三)
    Spring Boot 入门之 Web 篇(二)
    Spring Boot 入门之基础篇(一)
    Java 设计模式之建造者模式(四)
    Java 设计模式之抽象工厂模式(三)
    Java 设计模式之工厂模式(二)
    c++ type_info and typeid
    opengl& 颜色
    OpenGl And 视图
    如何写一个c++插件化系统
  • 原文地址:https://www.cnblogs.com/franson-2016/p/5517320.html
Copyright © 2020-2023  润新知