代理模式详解
什么是代理模式
代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
通过静态代理更容易了解代理模式的意思
静态代理
首先,代理类和被代理类都实现了同一个接口
public interface MyInterface {
void show(String str);
}
被代理类实现了接口如下
public class Impl implements MyInterface{
@Override
public void show(String str) {
System.out.println(str);
}
}
代理类实现了接口且拥有被代理类的引用,这样可以用代理类在被代理原本业务前后进行扩展且不需要改变被代理类
public class ImplProxy implements MyInterface{
private Impl target;
public ImplProxy(Impl target){
this.target=target;
}
@Override
public void show(String str) {
System.out.println("前-静态代理");
target.show(str);
System.out.println("后-静态代理");
}
}
使用代理类
public static void main(String[] args) {
Impl impl=new Impl();
ImplProxy implProxy = new ImplProxy(impl);
implProxy.show("show执行");
//前-静态代理
//show执行
//后-静态代理
}
简单说,代理就是:代理类实现了被代理类的接口且拥有被代理类的引用,在调用被代理类方法时进行扩展
动态代理
静态代理需要手动编写代理类,在程序运行前代理类已经编译成字节码了,运行时再加载进jvm内存。
但是动态代理则是在程序运行时动态地生成加载代理类的字节码,根据InvocationHandler的实现方法不同而生成不同的代理类。
相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
动态代理有不同的实现方式,我们介绍JDK的动态代理(基于接口)和CGLIB的动态代理(基于继承)
JDK动态代理
首先我们还是需要一个公共的接口
public interface MyInterface {
void show(String str);
}
实现我们的被代理类
public class Impl implements MyInterface{
@Override
public void show(String str) {
System.out.println(str);
}
}
实现我们的InvocationHandler
InvocationHandler拥有被代理类的实例引用,invoke()方法实际调用了被代理类并进行扩展
public class myInvocationHandler implements InvocationHandler {
private Object target;
public myInvocationHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前-动态代理");
//调用被代理类
Object res = method.invoke(target, args);
System.out.println("后-动态代理");
return res;
}
}
使用实例,关键在Proxy.newProxyInstance(),
三个参数:代理类的类加载器,代理的接口,InvocationHandler实例
public static void main(String[] args) {
myInvocationHandler myInvocationHandler = new myInvocationHandler(new Impl());
MyInterface proxy=(MyInterface) Proxy
.newProxyInstance(Impl.class.getClassLoader(),
Impl.class.getInterfaces(),
myInvocationHandler);
proxy.show("动态代理show()");
//前-动态代理
//动态代理show()
//后-动态代理
}
newProxyInstance()生成并实例化了代理类,对代理类的class文件进行反编译类似如下:
public final class $Proxy0 extends Proxy implements MyInterface {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
*super(paramInvocationHandler),是调用父类Proxy的构造方法。
*父类持有:protected InvocationHandler h;
*Proxy构造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*/
public $Proxy0(InvocationHandler paramInvocationHandler){
super(paramInvocationHandler);
}
//这个静态块本来是在最后的,我把它拿到前面来,方便描述
static{
try{
//....
m3 = Class.forName("proxy.MyInterface").getMethod("show", new Class[0]);
//....
}catch (NoSuchMethodException localNoSuchMethodException){
//...
}
}
/**
*调用持有的InvocationHandler的invoke方法
*/
public final void show(){
try{
this.h.invoke(this, m3, null);
return;
}
catch (){
//.....
}
}
}
总的来说,就是Proxy.newProxyInstance动态生成了代理类,这个代理类实现了公共接口并持有我们传入的InvocationHandler,代理类调用接口中的方法时间接调用了InvocationHandler的invoke方法,间接调用了被代理类并做了扩展。
CGLIB动态代理
和基于JDK的动态代理不同的是:
-
JDK争对接口进行动态代理,代理类和委托类共同实现同一个接口,只代理接口中声明的方法;
-
CGLIB动态代理依赖于CGLIB的类库需要配置依赖,实现原理上,CGLIB动态代理争对继承进行代理,代理类继承于委托类;
CGLIB动态代理简单演示
//委托类
public class CglibTest {
public void show1(){
System.out.println("show1.....");
}
}
//代理类的拦截器
public class CglibTestProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用了intercept()1");
Object obj=methodProxy.invokeSuper(o,objects);
System.out.println("调用了intercept()2");
return obj;
}
public Object getProxyObj(Class<?> clazz){
Enhancer enhancer=new Enhancer();
//设置代理类的父类即委托类
enhancer.setSuperclass(clazz);
//设置代理类的拦截器即本类
enhancer.setCallback(this);
//创建代理类
return enhancer.create();
}
}
//测试类
public class ProxyTest {
public static void main(String[] args) {
CglibTestProxy cglibTestProxy = new CglibTestProxy();
CglibTest proxyObj = (CglibTest) cglibTestProxy.getProxyObj(CglibTest.class);
proxyObj.show1();
}
}