spring实现AOP可以利用两种技术——JDK的动态代理和CGLIB。今天上课时老师详细讲了下JDK的动态代理。由于之前从来没接触过这个概念,听的我云里雾里的。回家之后好好整理了下思路,在这里就做个简单的学习笔记。
首先要讲一下动态代理的概念。所谓动态指的是代理类是由JDK帮我们生成的,我们不需要写代理类的代码。那何谓代理呢?先看一下下面这张类图:
Target是需要被代理的类。Proxy就是代理类。他们必须实现相同的接口。并且代理类中包含被代理的实例。工作时,客户端获得的是代理类的实例。调用接口中定义的方法时会去调用Proxy的具体方法。Proxy再通过自己内部的Target实例调用被代理类的方法。也就是说实际工作的还是Target类的实例。当然在Target实例方法调用的前后可以加一些用户定制的行为(例如可以输出一下日志)。这就是简单的AOP了。
这么说可能有点抽象,下面来看个例子。先把代码贴出:
Hello接口
public interface Hello {
void
sayHello();
}
HelloImpl类
public class HelloImpl implements Hello
{
public void sayHello() {
System.out.println("HelloImpl.sayHello()");
}
}
ProxyFactory
public class ProxyFactory {
public
static Hello createHello(String className) {
Hello
hello=getHello(className);
return new
LogHander().bind(hello);
}
private static Hello getHello(String
className) {
Hello hello=null;
try {
Class
cl=Class.forName(className);
hello=(Hello) cl.newInstance();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
} catch
(InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO
Auto-generated catch block
e.printStackTrace();
}
return
hello;
}
}
LogerHandler
public class LogHander implements
InvocationHandler {
private Hello hello;
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
Object returnObject=null;
String
methodName=method.getName();
System.out.println("-- begin
"+methodName+"()");
returnObject=method.invoke(hello, args);
System.out.println("-- end "+methodName+"()");
return
returnObject;
}
public Hello bind(Hello hello)
{
this.hello=hello;
return (Hello)
Proxy.newProxyInstance(hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(), this);
}
}
MainAction测试类
public class MainAction {
public static
void main(String[] args) {
String
className=HelloImpl.class.getName();
Hello
hello=ProxyFactory.createHello(className);
hello.sayHello();
}
}
简单的代码就不分析了。ProxyFactory的createHello方法返回的是一个实现了Hello接口的代理类。具体创建这个接口的是LogerBandler里的Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), this);这句代码。最后调用业务方法sayHello时,其实是去调用了LogerHandler的invoke方法(具体怎么实现的那时虚拟机做的事,我们不需要关心)。所以要作AOP或者说是方法拦截(这里是输出一个日志)应该在invoke方法里做。那具体调用业务类(这里是HelloImpl)的哪个方法呢?invoke里的method参数就告诉invoke具体调用的方法了。
Ok,动态代理的概念和实现确实有点难理解。不过相信大家只要愿意花时间还是可以弄清楚的。