什么叫代理模式
代理模式(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()));
}
}
1. JDK动态代理
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可以观察到这个流程。
2. CGLIB动态代理
由于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动态代理采用的是继承方式,所以不要求目标类实现特定的接口。