美文网首页
Java之动态代理的学习

Java之动态代理的学习

作者: 有财君 | 来源:发表于2019-04-24 00:10 被阅读0次

    假设有这样一个类,实现了几个简单的方法:

    public class Person {
        public void sayHello(String name) {
            System.out.println("Hello, " + name + "!");
        }
        public void walk() {
            System.out.println("Walking alone!")
        }
    }
    

    这个类完美的运行了一段时间以后,领导发现这里竟然没有记录日志,成何体统?加日志!

    那么这个时候类就会变成这样:

    public class Person {
        public void sayHello(String name) {
            System.out.println("Hello, " + name + "!");
        }
        public void walk() {
            Logger.info("开始行走了");
            System.out.println("Walking alone!")
            Logger.info("结束了");
        }
    }
    

    其实修改起来也不难,但是考虑一个问题,需求总是不断增加的,今天让加上日志,我们需要修改一次代码,明天让加一点别的,再修改一次,如此是没有穷尽的,最终我们的业务代码可能会被诸如日志,安全,性能,事务等等代码淹没。

    如果这个类,能够动态生成,并在执行真的业务方法之前,搞点事情,把这些非业务需求加进来,不就完美了吗?

    下面写一下实现,首先是一个Person的接口:

    package com.example.demo;
    
    public interface IPerson {
        void walk();
        void sayHello(String name);
    }
    

    接下来是这个接口的实现:

    package com.example.demo;
    
    public class Person implements IPerson {
        public void walk() {
            System.out.println("Walking alone");
        }
    
        public void sayHello(String name) {
            System.out.println("Hello, " + name);
        }
    }
    

    业务类这个时候已经写好了,接下来我们写一个Logger类,什么都不干,就是打印一些日志:

    package com.example.demo;
    
    public class Logger {
        public static void log(String content) {
            System.out.println(content);
        }
    }
    

    如果让这个类入侵到业务类里,最终的结果就是我说的,非业务代码淹没了业务代码。

    那么这个时候写一个类,实现了InvocationHandler接口,也就是动态代理:

    package com.example.demo;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class LogProxy implements InvocationHandler {
        private Object target;
    
        public LogProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Logger.log("开始打印日志");
            Object result = method.invoke(target, args);
            Logger.log("日志完毕");
            return result;
        }
    }
    
    

    这里最主要的是invoke方法,这里就是写扩展代码的地方了,一切业务无关的方法其实都可以写在这里。为了简单,我这里只写了Logger方法。

    下面写一个测试类:

    package com.example.demo;
    
    import java.lang.reflect.Proxy;
    
    public class PersonTest {
        public static void main(String[] args) {
            IPerson p = new Person();
            LogProxy logProxy = new LogProxy(p);
            IPerson person = (IPerson) Proxy.newProxyInstance(
                    Thread.currentThread().getContextClassLoader(),
                    p.getClass().getInterfaces(),
                    logProxy
            );
            person.sayHello("quan");
            person.walk();
    
            System.out.println(person.toString());
        }
    }
    
    

    这个类最后的输出是这样的:

    开始打印日志
    Hello, quan
    日志完毕
    开始打印日志
    Walking alone
    日志完毕
    

    纵观整个工程,我没有对Person类进行任何改动,但是也是实现了打印日志的功能的。这就是动态代理的工作了,虽然看起来没有变化,实际上也是发生了变化的,其实这是一个小小的骗局,系统实际上生成了一个代理类,看起来没什么变化,实际上底层是完全不一样的。

    而且,这个代理类还是可以复用的,如果有一个Dog类也需要打印日志,直接使用就可以了。

    动态代理也是实现AOP的关键技术,了解了动态代理,对于学习Spring框架是大有裨益的。

    相关文章

      网友评论

          本文标题:Java之动态代理的学习

          本文链接:https://www.haomeiwen.com/subject/ecpigqtx.html