一、简介
代理模式指的是客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。使用代理模式有两个目的:保护目标对象;增强目标对象。
代理模式的结构:
- 真实类:客户端要调用的对象的类型;
- 接口:真实要执行的对象所实现的接口;
- 代理类:实现了和真实所实现的相同接口,客户端通过调用代理类的实例间接调用真实类的对象。
二、静态代理
用一个例子来表示代理模式:有个人renter要租房子,但是对当地不熟,需要一个当地的朋友friend来帮他找房子。
1、真实类的接口
Person:
public interface Person {
void findHouse();
}
2、真实类
Renter:
public class Renter implements Person {
@Override
public void findHouse() {
System.out.println("找房子");
}
}
3、代理类
Friend:
public class Friend {
private Renter renter;
public Friend(Renter renter) {
this.renter = renter;
}
public void findHouse() {
System.out.println("朋友代找房子");
renter.findHouse();
System.out.println("朋友找到房子");
}
}
4、客户端应用
public class StaticProxyMain {
public static void main(String[] args) {
// 传入的参数renter,表示这个代理类只帮这个需要找房子的朋友找房子
Friend friend = new Friend(new Renter());
friend.findHouse();
}
}
三、动态代理
动态代理和静态代理的基本思想是一致的,只不过动态代理的功能更加强大,随着业务的扩展适应性更强。
还是以上面的找房子为例,动态代理相当于找房子这个业务发展成了房屋中介,可以满足多样化的租房需求。
1、JDK动态代理
1.1 动态代理类
动态代理的关键,就是实现JDK的InvocationHandler接口,并复写invoke方法,实现在invoke方法中调用传入的Object的相应方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Agency implements InvocationHandler {
/**
* 被代理的对象
*/
private Object target;
public Object getInstance(Object target) {
this.target = target;
Class<?> type = target.getClass();
return Proxy.newProxyInstance(type.getClassLoader(), type.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object object = method.invoke(this.target, args);
after();
return object;
}
private void before() {
System.out.println("中介开始找房");
}
private void after() {
System.out.println("中介找房结束");
}
}
1.2 应用
public class JdkProxyMain {
public static void main(String[] args) {
Person person = (Person) new Agency().getInstance(new Renter());
person.findHouse();
}
}
1.3 JDK实现动态代理的过程
JDK动态代理生成代理对象的过程,叫做字节码重组:
- 通过反射,获取被代理对象的引用,并且获取它的所有接口;
- JDK动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口;
- 动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用;
- 编译新生成的Java代码.class文件;
- 重新加载到JVM中运行。
总的来说,原理分成两部分:
- 代理类的生成:直接编写Class字节码;
- 代理方法的调用:反射
2、CGLib代理
2.1 代理类
public class CglibAgency implements MethodInterceptor {
public Object getInstance(Class<?> type) {
Enhancer enhancer = new Enhancer();
// 设置新类的父类
enhancer.setSuperclass(type);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
private void before() {
System.out.println("代理公司开始找房");
}
private void after() {
System.out.println("代理公司找房结束");
}
}
2.2 应用
public class CglibProxyMain {
public static void main(String[] args) {
Renter renter = (Renter) new CglibAgency().getInstance(Renter.class);
renter.findHouse();
}
}
2.3 原理
- 代理类的编写:使用ASM框架编写Class字节码;
- 代理方法的调用:FastClass机制,为代理类或被代理类各生成一个类,这个类会为代理类或被代理类的方法分配一个int类型的index。FastClass可以直接使用这个index定位要调用的方法,并直接进行调用,省去了反射的过程。
2.4 CGLib和JDK动态代理对比
- JDK动态代理实现了被代理对象的接口,CGLib代理继承了被代理对象;
- JDK动态代理直接写Class字节码,CGLib代理使用ASM框架写Class字节码,CGLib代理实现更复杂、效率更低;
- JDK动态代理调用代理方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用的,CGLib代理的执行效率更高。