代理Proxy:
-
Proxy代理模式是一种结构型设计模式,主要解决的是直接访问对象带来的问题
-
代理模式也就是:当两个类需要通信的时候,引入了第三方的代理类,将两个类的关系解耦,我们只需要了解代理类即可
-
我们举一个通俗的例子:“客户租房子”, 客户需要租房子,房东需要把房子出租,房东要把房子租出去需要出租方(也就是房东)到房产管理部门办理备案,另外,还需要向税务部门申报,缴纳房产税、所得税 ,这个过程是十分麻烦的,于是房东可以把出租的信息交给中介去做,这个中介就是代理,房东只需要向中介提供租房的信息,此时房东不在需要办理之前那么多麻烦的手续了,客户找房子也只需要找到中介签租赁合同即可
Sping AOP的底层就是动态代理
- 而在不改变原有代码功能的基础上,可以新增新的功能,就是AOP横向切割的核心思想
静态代理
静态代理角色分析
-
抽象角色 : 一般使用接口或者抽象类来实现 (按以上通俗例子就是出租房子)
-
真实角色 : 被代理的角色, (房东)
-
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .(中介)
-
客户 : 使用代理角色来进行一些操作 .(客户租房)
代码实现
抽象角色
//租房的接口
public interface Rent {
public void rent();
}
真是角色
//房东
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房子");
}
}
代理角色
//中介 代理
@NoArgsConstructor
@AllArgsConstructor
public class Proxy implements Rent{
private Host host;
public void rent() {
host.rent();
seeHouse();
hetong();
fare();
}
//看房
public void seeHouse(){
System.out.println("中介带顾客看房子");
}
//签租赁合同
public void hetong(){
System.out.println("签租赁合同");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
客户
public class Client {
public static void main(String[] args) {
//房东要出租房子
Host host = new Host();
//代理,中介帮房东出租房子,代理一般会有一些附属操作
Proxy proxy = new Proxy(host);
//客户不在面对房东,直接找中介租房即可
proxy.rent();
}
}
静态代理的优点:
- 客户端不需要知道真实角色(实现类)是什么,客户端只需要直到代理即可,很大程度上对代码进行了解耦,
静态代理的缺点
- 真实角色类和代理角色类实现了相同的接口,如果接口中新增添了一个方法,真实角色类和代理角色类都要实现这个方法,代码的维护程度的复杂性增加
- 代理角色类只服务于一种真实角色类对象,如果真实角色类增加了,就需要更多的代理类,我们程序员的工作量日益递增,此时静态代理就无法胜任了,于是就衍生出了动态代理
动态代理
根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理
如上的示例中,一个代理只能代理一个类型,在编译阶段已经确定为被代理对象,而动态代理在运行的是,采用反射原理,能够代理各种类型的对象
在java中实现动态代理,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持
动态代理的步骤:
1.创建被代理的类以及接口
2.创建一个实现 InvocationHandler接口的实现类,该类必须实现invoke方法
3.通过Proxy类的静态方法newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
4.通过代理调用方法
【InvocationHandler:调用处理程序】InvocationHandler接口的定义如下
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
第一个参数是代理实例,第二个参数对应于调用接口代理实例上的接口的方法,第三个参数是:方法调用时所需要参数
【Proxy : 代理】
我们通过创建的
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
第一个参数是类加载,第二个参数是代理类实现的接口列表,第三个参数是得到InvocationHandler接口的子类的实例
代码实现
第一步:创建被代理的类以及接口
public interface Rent {
public void rent();
}
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
第二步以及第三步 创建InvocationHandler接口实现类 并创建接口的代理类的实例
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 {
return method.invoke(target,args);
}
}
可以看到Proxy.newProxyInstance()方法通过类加载器以及接口模板反射方式生成代理类,InvocationHandler 接口的实现类即可通过invoke()转发实现接口的方法,这个过程是在运行期间动态生成的,与静态代理相比,接口中所有声明的方法都被转移到调用处理器invoke()方法中集中处理,不像静态代理的抽象接口中的每一方法都要在代理对象中进行中转,
第四步.通过代理调用方法
public static void main(String[] args) {
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(new Host());
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
总之静态代理的优点动态代理他都有,静态代理的缺点他也有
当抽象接口增加新的业务时,InvocationHandler实现类将不再进行中转,使得业务更加集中,也更加方便 当有新的抽象接口加入时,只需要重新动态注入即可