什么是代理模式?
给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。 通俗的来讲代理模式就是我们生活中常见的中介。
当我们想要去租房的时候,可能会通过某中介公司去代理商(中介),通过中介去找到我们心仪的房子。此场景就涉及了3个角色: 真实角色-房主(房屋的真正拥有者)、代理角色(房屋中介等代理商)、接口或抽象类(抽象角色-包含了真实角色和代理角色共同维护的方法- 租房)。
代理商是给房屋的主人做代理的,因此在编写代理模式代码的时候要注意:代理角色要包含对原对象的引用。
Java中的代理分为几种?
可以分为静态代理与动态代理两种。
静态代理
静态代理是指给每个真实的角色都指定一个代理角色,之后就可以通过这个代理角色来操作真实角色的。之所以称为静态代理,是因为所有的代理关系都是固定写死的。就像一个房东只对应一个租房代理中介,一个车主,对应一个租车代理商。
下面以静态代理“租房“”为例,演示静态代理的具体实现。
租房接口:(抽象角色)
/**
* @PackageName: com.proxy.demo
* @author: youjp
* @create: 2020-10-17 16:06
* @description: 租房接口(包含了真实角色和代理角色共同维护的方法)
* @Version: 1.0
*/
public interface Subject {
/**
* 租房
* @param money
* @return
*/
public abstract boolean rent(int money);
}
房主(真实角色)
/**
* @PackageName: com.proxy.demo
* @author: youjp
* @create: 2020-10-17 16:09
* @description: 房主(房屋真实拥有者)
* @Version: 1.0
*/
public class RealSubject implements Subject {
@Override
public boolean rent(int money) {
System.out.println("租房"+money+"钱");
return true;
}
}
中介(代理角色)
/**
* @PackageName: com.proxy.demo
* @author: youjp
* @create: 2020-10-17 16:11
* @description: 中介(代理角色)
* @Version: 1.0
*/
public class StaticProxy implements Subject {
//含有对象的真实引用
private RealSubject subject;
public void before(){
System.out.println("----before租房前,需要找中介,-----");
}
public void after(){
System.out.println("---after房子满意以后,付了款-------");
}
public StaticProxy(){
subject=new RealSubject();
}
@Override
public boolean rent(int money) {
//
this.before();
System.out.println("---中介替替房东出租房");
//调用真实角色的租房方法
subject.rent(money);
this.after();
return true;
}
}
测试类
/**
* @PackageName: com.proxy.demo
* @author: youjp
* @create: 2020-10-17 16:53
* @description: 测试静态代理
* @Version: 1.0
*/
public class TestStaticProxy {
public static void main(String[] args) {
StaticProxy staticProxy= new StaticProxy();
//调用代理角色的方法
staticProxy.rent(2000);
}
}
运行结果:
使用静态代理的好处:
可以发现静态代理的实现方式比较简单,使得真实角色处理的业务更加纯粹,不再去关注一些公共的事情。公共的业务由代理来完成—实现业务的分工。公共业务发生扩展时变得更加集中和方便。
使用静态代理的缺点
如果有多个真实角色,比如:租车、相亲等。就得编写多个代理商对象与之关联。就会增加程序代码量,也不会便于后期的维护。
动态代理
静态代理中,真实角色和代理角色是“一对一”的关系,而在动态代理中,真实角色和代理角色是“多对一”的关系。动态代理可以使用一个“万能”的代理者来代理任何类型的“真实角色”,这也是动态代理相比静态代理的优势所在。
思考一个问题,静态代理需要实现和真实角色相同的接口。列如:房屋中介和真正的房主都需要“出租房”,因此出租房就是二者共同要实现的接口。但是,动态代理既然是万能代理,那么应该实现什么样的接口呢?例如,房屋的主人和汽车的主人,都需要动态代理,那么这个动态代理是应该实现房屋的接口还是出租车的接口呢~~
为此,JDK中提供了一个万能的动态接口“InvocationHandler”.即使用动态代理时,实现InvocationHandler接口就行。该接口中的 invoke() 方法就是万能的代理方法。
下面,还是以出租房为例,来实现动态代理出租。
租房接口:(抽象角色)
/**
* @PackageName: com.proxy.demo
* @author: youjp
* @create: 2020-10-20 16:06
* @description: 租房接口(包含了真实角色和代理角色共同维护的方法)
* @Version: 1.0
*/
public interface Subject {
/**
* 租房
* @param money
* @return
*/
public abstract boolean rent(int money);
}
房主(真实角色)
/**
* @PackageName: com.proxy.demo
* @author: youjp
* @create: 2020-10-20 16:09
* @description: 房主(房屋真实拥有者)
* @Version: 1.0
*/
public class RealSubject implements Subject {
@Override
public boolean rent(int money) {
System.out.println("租房"+money+"钱");
return true;
}
}
代理角色
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @PackageName: com.proxy.demo
* @author: youjp
* @create: 2020-10-17 17:14
* @description: TODO 动态代理(中介)可以代理任意类型的角色
* @Version: 1.0
*/
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object obj){
this.object=obj;
}
public void before(){
System.out.println("----before---------");
}
public void after(){
System.out.println("---after-----------");
}
/**
*
* @param proxy 代理的角色
* @param method 被代理的方法
* @param args 方法的参数
* @return 真正被代理的方法rent()的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.before();
/**
* 以出租房为例,动态代理想要执行的rent()方法,必须调用真实的角色的rent();
* 动态代理使用 了反射技术,通过method.invoke(object,args),实现了对真实角色的rent()调用。
* 从代码上看,未发现rent()任何相关的代码,所以实现了万能代理
*/
Object result=method.invoke(object,args);
this.after();
return result;
}
}
测试类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @PackageName: com.proxy.demo
* @author: youjp
* @create: 2020-10-17 17:28
* @description:
* @Version: 1.0
*/
public class TestDynamicProxy {
public static void main(String[] args) {
//真实对象:房东
Subject realSubject=new RealSubject();
//初步的代理对象:handle就是realSubject的初步代理对象
InvocationHandler handler=new DynamicProxy(realSubject);
/**
* newProxyInstance(a,b,c)
* a:初步代理对象的类加载器(固定写法)
* b:接口类型的数组。要代理的方法是在哪些接口中定义的,即realSubject的接口
* c:要将哪个初步的代理对象转换为最终的代理对象
* subjectProxy :最终的代理对象
*/
Subject subjectProxy=(Subject) Proxy.newProxyInstance(
handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler
);
//获取到
boolean result= subjectProxy.rent(2000);
System.out.println(result?"ok":"error");
System.out.println(subjectProxy.getClass().getName());
}
}
运行结果;
可以发现,动态代理就是通过Proxy.newProxyInstance(),创建一个万能的代理对象,因为该万能对象中拥有真实对象的 接口,因此就知道需要代理的对象类型,知道要代理的方法是什么。
有兴趣的老爷,可以关注我的公众号【一起收破烂】,回复【006】获取2021最新java面试资料以及简历模型120套哦~