Java之动态代理的学习

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

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之动态代理的学习)