代理模式(Proxy Patrern)就是将对象的直接访问变为访问这个对象的代理对象。即通过代理对象间接地访问原本的对象。
代理是为了扩展类而存在的,可以控制对目标类的服务的访问。
为了进行接下来的实验,首先创建一个接口InterfaceTest
和一个实现了这个接口的类InterfaceTestImpl
。
//接口 public interface InterfaceTest { public void test(int x); } //实现类 public class InterfaceTestImpl implements InterfaceTest{ @Override public void test(int x) { System.out.println("test:".concat(this.getClass().getSimpleName())); } }
JDK动态代理是通过JDK自带的Proxy
类中的newProxyInstance()
方法来动态生成代理对象的。我们需要实现InvocationHandler
接口,在其invoke()
方法中编写调用目标对象的代码。
下面编写代码来实现JDK动态代理:
public class Solution implements InvocationHandler{ //目标对象jdkTest private final InterfaceTest jdkTest = new InterfaceTestImpl(); //代理对象的test()方法被调用时,此invoke方法将被调用 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke ".concat(method.getName())); //去调目标对象jdkTest的test()方法 method.invoke(jdkTest, args); System.out.println("after invoke ".concat(method.getName())); return null; } public static void main(String[] args) { //利用反射,获取类加载器 Class<?> clazz = InterfaceTestImpl.class; //创建代理对象proxy InterfaceTest proxy = (InterfaceTest) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] {InterfaceTest.class},new Solution()); //调代理对象的test()方法 proxy.test(1); } }
这里用到的重要的方法,newProxyInstance,靠它动态生成代理对象。来看它的三个参数:
newProxyInstance public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException Returns an instance of a proxy class for the specified interfacesthat dispatches method invocations to the specified invocationhandler. Proxy.newProxyInstance throws IllegalArgumentException for the same reasons that Proxy.getProxyClass does.
ClassLoader loader
,也就是目标类的类加载器,我们利用反射,获取目标类之后通过getClassLoader()
方法得到类加载器。Class<?>[] interfaces
,指定要实现的接口。InvocationHandler h
,InvocationHandler
对象。输出结果为:
总结实现JDK动态代理的步骤:
InvocationHandler
接口,在其invoke()
方法中编写调用目标对象的代码;Proxy.newProxyInstance()
方法创建代理对象。这一切做完之后,调用代理对象的test()
方法实际上最后会去执行目标对象的test()
方法。通过debug可以观察到这个流程。
由于JDK动态代理是面向接口的,也就是说如果目标类没有相应的接口,JDK动态代理就无法为其创建代理。这时可以选择用CGLIB动态代理来实现。
下面编写代码来实现CGLIB动态代理:
public class Solution implements MethodInterceptor{ @Override public Object intercept(Object caller, Method method, Object[] args, MethodProxy proxy) throws Throwable { // TODO 自动生成的方法存根 System.out.println("before invokeSuper ".concat(method.getName())); proxy.invokeSuper(caller, args); //method.invoke(caller, args); System.out.println("after invokSuper ".concat(method.getName())); return null; } public static void main(String[] args) { //创建类加强器,用来创建动态代理类 Enhancer eh = new Enhancer(); //指定父类,也就是目标类 eh.setSuperclass(InterfaceTestImpl.class); //指定回调方法,当调用代理对象的某个方法时,此回调方法将被调用 eh.setCallback(new Solution()); //利用类加强器en创建代理对象 InterfaceTestImpl ns = (InterfaceTestImpl) eh.create(); ns.test(1); ns.toString(); } }
输出结果为:
总结实现CGLIB动态代理的步骤:
MethodIntercepto
接口,并在intercept
方法中编写调用目标对象的代码;从setSuperclass()
方法可以看出,实际上目标类是代理类的父类。也就是说,CGLIB动态代理采用的是继承方式,所以不要求目标类实现特定的接口。