假设有这样一个类,实现了几个简单的方法:
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框架是大有裨益的。