我们可以很轻松的实现一个简单的代理。
静态代理
实现静态代理是个很简单的事情,最基础的代理只需要定义一个接口(虽然不是必要,但这显然才是标准的设计)、一个被代理类和一个代理类,例如:
定义一个接口:
public interface HelloWorldInterface{
public void hello();
}
一个实现类:
public interface HelloWorld extends HelloWorldInterface{
public void hello(){
System.out.println("Do something here!");
}
}
和一个代理类:
public interface HelloWorldProxy extends HelloWorldInterface{
HelloWorld helloWorld;
public HelloWorldProxy (HelloWorld helloWorld) {
super();
this.helloWorld= helloWorld;
}
public void hello(){
System.out.println("Before hello!");
helloWorld.hello();
System.out.println("After hello!");
}
}
实际使用时,我们是去调用HelloWorldProxy的方法,其将作为HelloWorld的代理实现。
此种方式直接实现的代理太过于死板,因为每一种代理行为都要制定一个代理类,我们熟知的很多基于代理的实现(譬如AOP、事务)显然不可能用静态代理的方式针对每一处类切点都覆写一个代理类,这种时候就需要动态代理。
动态代理
我们所熟知的相当多的框架均基于动态代理开发,JDK本身基于反射(java.lang.reflect)提供了动态代理,我们只需定义代理的行为,而对于代理类的范围并不是固定值。核心接口是InvocationHandler,我们通过对其的实现来定义实际的代理行为,调用被代理类只需要invoke方法。并且可以选择调用0~n个被代理的方法。依旧以上面的代理为例,我们假设已经定义了相应的接口和实现类(实现类可以没有也可以是多个),对于代理行为的定义时这样的。
定义代理行为:
public class HelloWorldProxy extends InvocationHandler{
HelloWorld helloWorld;
public HelloWorldProxy (HelloWorld helloWorld) {
super();
this.helloWorld= helloWorld;
}
@Override
public void invoke(Object proxy, Method method, Object[] args){
System.out.println("Before "+ method.getName());
method.invoke(helloWorld,args);
System.out.println("After "+ method.getName());
return null; //非void直接按method.invoke的调用结果返回
}
}
由于InvocationHandler其实是函数式接口,可以用lamdba表达式实现。
当我们想要调用代理时,需要指定其
HelloWorld helloWorld= new HelloWorld ();
HelloWorldProxy helloWorldProxy = new HelloProxy(helloWorld);
Class[] clazzList = {HelloWorldInterface.class};
//没有自定义的 类加载器的话直接使用线程上下文的就好,总之都是一个系统 类加载器
HelloWorldInterface proxyHello = (HelloWorldInterface)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , helloWorldProxy);
//调用代理方法,从执行结果看等同于调用了HelloWorldProxy的invoke
proxyHello.hello();
如果你懒于定义InvocationHandler的实现,直接使用lamdba即可,当然这种时候我们限定住了可传的被代理实例:
HelloWorldInterface proxyHello = (HelloWorldInterface)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList ,
(Object proxy, Method method, Object[] args)->{
System.out.println("Before "+ method.getName());
//这一行可以没有,也可以是继承于clazzList的任一实现,本质上 只要能反射执行,就不会报错
method.invoke(helloWorld,args);
System.out.println("After "+ method.getName());
return null; //非void直接按method.invoke的调用结果返回
});
proxyHello.hello();
可以预见到基于此,AOP的思路大概是这样的:
Class[] clazza = new Class[0];
try {
clazza = new Class[]{Class.forName("com.xhl.autosite.common.Hello")};
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Xxx bean = (Xxx)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList ,
(Object proxy, Method method, Object[] args)->{
System.out.println("Before "+ method.getName());
//method.invoke(xxx); 这一行可能是没有的,有的话是从bean中获取的
System.out.println("After "+ method.getName());
return null;
});
//实际执行的是动态代理类的方法了
bean.xxx();

