目录
一、什么是代理模式?
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
举个栗子: 我们要买房子,虽然可以自己一个小区一个小区去找,但是太浪费时间了,于是我们直接找一个中介,中介帮我们买。这个中介就是我们说的代理。
二、为什么要用代理模式?
优点
- 隔离,代理模式将代理对象与真实被调用对象分离。
- 降低系统的耦合性,拓展性好
- 可以保护目标对象
- 可以增强目标对象的功能
缺点
- 增加了系统的复杂度
- 增加了请求对象,降低了系统的处理效率
三、静态代理
废话不多说,代码说明一切
1、创建接口
public interface BuyHouse {
void buyHouse();
}
2、实现接口
public class BuyHouseImpl implements BuyHouse {
@Override
public void buyHouse() {
System.out.println("我要买房");
}
}
3、创建代理类
public class Proxy implements BuyHouse {
private BuyHouse subject;
public Proxy(BuyHouse subject) {
this.subject = subject;
}
private void after() {
System.out.println("调用之前");
}
private void before() {
System.out.println("调用之后");
}
@Override
public void buyHouse() {
before();
subject.buyHouse();
after();
}
}
4、测试
public class Test {
public static void main(String[] args) {
Proxy proxy = new Proxy(new BuyHouseImpl());
proxy.buyHouse();
}
}
5、总结
- 静态代理类有代理模式的优点
- 但每一个对象创建一个代理对象,成本太高。
四、动态代理
实现动态代理,目前介绍两种方式。
- JDK自带的代理
- CGLIB提供的类库
1、JDK代理模式
1.1 创建代理类
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public Object getInstance(Object o) {
this.object = o;
Class<?> clazz = o.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
注意Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader: 指定当前目标对象使用的类加载器,
Class<?>[] interfaces: 指定目标对象实现的接口的类型
InvocationHandler: 指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
1.2 测试
DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler();
BuyHouse proxyBuyHouse = (BuyHouse) dynamicProxyHandler.getInstance(new BuyHouseImpl());
proxyBuyHouse.buyHouse();
1.3 探究原理
在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。我们很好奇,运行时的代理类究竟什么样子。我们将运行时生成的代理类输出出来。
// 生成代理类
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{BuyHouse.class});
try {
FileOutputStream os = new FileOutputStream("E://$Proxy.class");
os.write(bytes);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
反编译 $Proxy.class
文件
public final class $Proxy0 extends Proxy implements BuyHouse {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void buyHouse() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.machuxin.course.patterns.proxy.jdklib.BuyHouse").getMethod("buyHouse");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
分析代码可知,生成的代理对象继承了Proxy
类,实现了BuyHouse
接口。重写了buyHouse
。调用链路为 $Proxy.bugHouse
-> DynamicProxyHandler.invoke
- > BuyHouseImpl.bugHouse
2、CGLIB
2.1 maven 项目需要进入
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2.2 创建对象
public class BuyHouseCG {
public void buyHouse() {
System.out.println("我要买房");
}
}
2.3 创建代理类
public class CglibProxy implements MethodInterceptor {
public Object getInstance(Class<?> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("买房前准备");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("买房后装修");
return result;
}
}
2.4 测试类
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
BuyHouseCG buyHouseCglibProxy = (BuyHouseCG) cglibProxy.getInstance(BuyHouseCG.class);
buyHouseCglibProxy.buyHouse();
}
2.5 CGLIB总结
- CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高
- CGLIB创建代理对象时所花费的时间却比JDK多得多
- CGLIB 采用继承的方式实现代理
关于为什么性能高:CGLIB采用了fastClass机制,为代理类和被代理类各生成一个类,为类中每个方法分配一个index,这个index当做入参,FastClass可以直接定位到要调用的方法并且直接调用。JDK则需要通过反射去调用。
3 JDK与CGLIB 对比
- CGLIB是通过继承,JDK是通过实现接口
- 都在运行时生成字节码,CGLIB实现比较复杂,所以比JDK时间长
- JDK通过的反射,CGLIB通过FastClass直接调用,CGLIB效率高
- 对于单例对象,CGLIB 不用频繁创建,效率更高
Spring中的代理
- 当Bean实现接口时,用JDK
- 当Bean没有实现接口时,用CGLIB
- 可以通过配置,强制使用CGLIB