代理模式
代理模式概念
代理模式(Proxy Pattern)是指为其他对象提供一种代理,控制对这个对象的访问,属于结构型模式。
代理模式一般包含三种角色
- 抽象主题角色(Subject):抽象主题类的主要职责是声明真实主题与代理的共同接口方法,该类是以接口也可以是抽象类;
- 真实主题角色(RealSubject):该类被称为代理类,该类定义了代理所表示的真实对象,是负责执行系统真正的逻辑业务对象;
- 代理主题角色(Proxy):被称为代理类,内部持有RealSubject的引用,具备完全的对RealSubject的代理权,客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对象前后增加一些处理代码;
代理一般就是被理解为代码的增强,就像是Spring中的AoP对代码的增强。可以分为静态代理和动态代理。
可以看一个代理模式的通用写法:
- 定义一个接口,创建代理主题角色类
package zh.stu.proxy;
/**
1. @Author:KoVaVo
2. @Version:1.0.0
3. @Description:
*/
public interface ICar {
void run();
}
- 新建一个类并实现这个接口。真实角色主题类
package zh.stu.proxy;
/**
1. @Author:KoVaVo
2. @Version:1.0.0
3. @Description:
*/
public class ProxyCar implements ICar {
private ICar iCar;
public ProxyCar(ICar iCar) {
this.iCar = iCar;
}
public void run() {
before();
iCar.run();
after();
}
public void before()
{
System.out.println("before......");
}
public void after(){
System.out.println("after......");
}
}
- 新建代理主题角色类
package zh.stu.proxy;
/**
* @Author:KoVaVo
* @Version:1.0.0
* @Description:
*/
public class ProxyCar implements ICar {
private ICar iCar;
public ProxyCar(ICar iCar) {
this.iCar = iCar;
}
public void run() {
before();
iCar.run();
after();
}
public void before()
{
System.out.println("before......");
}
public void after(){
System.out.println("after......");
}
}
- List item测试调用代码
package zh.stu.proxy;
/**
* @Author:KoVaVo
* @Version:1.0.0
* @Description:
*/
public class Main {
public static void main(String[] args) {
ProxyCar proxyCar = new ProxyCar(new HongQ());
proxyCar.run();
}
}
从静态代理到动态代理
静态代理的成本太高,采用动态代理的底层一般不需要我们直接去实现。升级操作,在以上原有的代码添加一个类如下:
package zh.stu.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Author:KoVaVo
* @Version:1.0.0
* @Description:
*/
public class JdkProxy implements InvocationHandler {
private ICar target;
public ICar getInstance(ICar target){
this.target=target;
Class<?> clazz = target.getClass();
ClassLoader classLoader = clazz.getClassLoader();
return (ICar) Proxy.newProxyInstance(classLoader,clazz.getInterfaces(),this);
// this.target.getClass().
}
//重写实现接口的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object invoke = method.invoke(this.target, args);
after();
return invoke;
}
public void before(){
System.out.println("签订买车合同");
}
public void after(){
System.out.println("恭喜提车");
}
}
测试:
package zh.stu.proxy;
/**
1. @Author:KoVaVo
2. @Version:1.0.0
3. @Description:
*/
public class ProxyTest {
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();
ICar instance = jdkProxy.getInstance(new HongQ());
instance.run();
}
}
CGLIB和JDK的比较
-
CGLiB执行代理方法对的效率之所以比JDK高,是因为CGlib采用了FastClass机制,为代理类和被代理类生成一个类,这个类会为代理类或被代理类的方法分配一个index(int)类型;这个index当作一个入参,这个时候FastClass就可以直接定位到被调用的方法直接调用,省去了反射调用,所以比jdk效率高。
-
JDK 动态代理实现了被代理对象的接口,CLGlib代理继承了被代理的对象
-
JDK动态代理和CGLIB代理都在运行时期生成字节码,JDK动态代理直接写class字节码,CGLib代理使用ASM框架课程Class字节码,CGLib代理实现比较复杂,生成代理类比JDK动态代理效率低。
-
JDK动态代理调用代理方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用方法的,CGLib代理的执行效率高。