静态代理与动态代理
代理,顾名思义就是对象的行为被别人代为执行,举个例子:租房
房东想要出售房屋,但是不想管理这里面复杂的步骤,就会有一个中间商来代理房东执行,当用户想要租房的时候,就会去找中间商来租房,由中间商来沟通房东
对代理模式进行分析:
- 抽象角色:一般是接口或者抽象类,比如这里的租房,具有相同的功能
- 真实角色:被代理的角色,比如这里的房东
- 代理角色:代理真实角色,在代理真实角色后可以做一些附加操作,比如这里的中介
- 客户:访问代理对象
静态代理
就以房东出租房屋为例看一下静态代理的代码:
租房
public interface Rent {
void rent();
}
房东
public class HouseHost implements Rent {
@Override
public void rent() {
System.out.println("房东往出租房!");
}
}
中介
public class ProxyHost implements Rent {
private HouseHost host;
public ProxyHost() {
}
public ProxyHost(HouseHost host) {
this.host = host;
}
//在有共同的行为上,中介可以做一些房东没有的操作
@Override
public void rent() {
lookHouse();
//房东的行为
host.rent();
money();
}
//中介的一些附加操作
public void lookHouse() {
System.out.println("中介带看房!");
}
public void money() {
System.out.println("中介收钱!");
}
}
客户
public class User {
public static void main(String[] args) {
HouseHost houseHost = new HouseHost();
ProxyHost proxyHost = new ProxyHost(houseHost);
proxyHost.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用关心别的业务
- 公共的业务交给代理角色进行处理
- 业务发生扩展,方便集中管理
缺点:
- 如果真实角色有多个,代码量会翻倍
在实际开发中,想要对功能进行扩展,可以通过创建一个代理类来对原有的方法进行扩展,而不是在原来的代码上进行扩展,就比如上面代理类对房东要做的事进行了扩展
总结:静态代理就是通过对类的扩展而不是对类的修改
动态代理
动态代理和静态代理的区别就是动态代理类是动态生成的,不是我们直接写好的
动态代理分为两大类:
- 基于接口的动态代理 ---- JDK动态代理
关键类:
基于JDK的动态代理主要与InvocationHandler接口和Proxy类有联系。
InvocationHandler主要用来对需要代理的对象的方法进行扩展,而Proxy主要作用是提供了创建动态代理类和实例的静态方法
关键方法
InvocationHandler:
Object invoke(Object proxy, //调用该方法的代理实例,也就是被代理的对象
Method method, //要执行的方法
Object[] args) //方法执行的参数
结果:从代理实例上的方法调用返回的值。如果接口方法声明返回类型是基本类型,则此方法的返回值必须是对应的基本包装类的实例;否则,它必须是可声明返回类型的类型。
Proxy:
这是JDK文档中的写法,我们就以简单的方法来创建代理:
public static Object newProxyInstance(ClassLoader loader, //类加载器来定义代理类
Class<?>[] interfaces, //代理实例实现的接口列表
InvocationHandler h) //这个就是我们写的InvocationHandler的实现类
结果:具有由指定的类加载器定义并实现指定接口的代理类的指定调用处理程序的代理实例
Rent接口
public interface Rent {
void rent();
}
HouseHost
public class HouseHost implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房屋");
}
}
ProxyInvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//要代理的角色
//根据你传入的什么角色进行代理,动态生成
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//得到代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行方法要传递方法的调用者(也就是被代理的角色)和方法的参数
Object result = method.invoke(target, args);
//添加功能,而且这个添加功能不仅仅是对一个方法进行添加的,可以是多个方法
log(method.getName());
return result;
}
//根据执行不同的方法,输出不同的日志
public void log(String msg) {
System.out.println("执行了" + msg + "方法");
}
}
客户端
public class Client {
public static void main(String[] args) {
//多态
Rent host = new HouseHost();
ProxyInvocationHandler pi = new ProxyInvocationHandler();
pi.setTarget(host);
//注意:1.获取到的Object类型的代理对象可以强制转换成目标对象实现过的接口类型,但是不能转换成目标类型
// 2.代理对象调用代理方法,会调用InvocationHandler中的invoke方法
Rent proxy = (Rent) pi.getProxy();
proxy.rent();
}
}
- 基于类的动态代理 ---- cglib
什么是cglib?
CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。相比于JDK动态代理,cglib可以对一个普通类进行代理,而JDK的动态代理只能对接口进行代理,如果代理的类是一个普通类,那么java的动态代理就没法使用了。
总结:
动态代理相较于静态代理,一个动态代理代理的就是一类服务,不再是一个类。就比如房东租房这个例子,即使有很多的角色需要被代理,也可以通过这一个类来进行扩展。静态代理代理的是一个房东,动态代理代理的是租房这个业务
总结
- 代理分为静态代理和动态代理 两种
- 静态代理,代理类需要自己编写代码完成
- 动态代理,代理类通过Proxy.newInstance()方法完成
- 不论是动态代理和静态代理,代理者和被代理者都要实现共同的接口
- 代理模式本质上的目的是为了增强代码的现有功能