静态代理模式
通过创建一个代理角色来包装真实角色,从而实现对真实角色的访问控制和增强功能。在静态代理模式中,代理角色和真实角色实现相同的接口,代理角色持有一个对真实角色的引用,并在其方法调用前后执行额外的逻辑。
角色分析
- 抽象角色(Subject):使用接口或者抽象类来解决,定义了代理类和原始类共同实现的方法,它是代理角色和真实角色之间的约定接口。比如说租房是中介和房东共同的需求。
- 真实角色(Real Subject):被代理的角色。
- 代理角色(Proxy):代理角色也实现了抽象角色的接口,并持有对原始类的引用。代理角色也可以提供一些附属的操作。
- 客户(Client):客户端是使用代理角色和真实角色的调用方。
案例
在正常情况下,我们应该是真实角色租客和去找真实角色房东进行房屋出租这个行为。
抽象角色:租房
//租房
public interface Rent {
public void rent();
}
真实角色:房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东出租了房子");
}
}
真实角色:租客
public class User {
public static void main(String[] args) {
Host host = new Host();
host.rent();
}
}
结果:
但是实际情况是租客一般不会与中介进行直接的沟通,而是通过代理角色中介来进行租房这个行为的实现。
代理角色:中介
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
}
}
真实角色:租客
public class User {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
结果:
房东只需要出租房子,租客也只需要租房子,但是代理角色中介可以实现一些附属操作,包括替租客看房,收取中介费,为房东租客提供租赁合同。
@Override
public void rent() {
host.rent();
seeHose();
getMoney();
ht();
}
public void seeHose(){
System.out.println("中介看房");
}
public void getMoney(){
System.out.println("中介收费");
}
public void ht(){
System.out.println("签订合同");
}
在这个过程中,租客直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式。
优缺点
优点:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情
- 公共的业务由代理来完成 . 实现了业务的分工
- 公共业务发生扩展时变得更加集中和方便
缺点:
- 类多了 , 多了代理类 , 工作量变大了,开发效率降低
动态代理
动态代理是一种在运行时创建代理对象的技术。与静态代理不同,静态代理需要提前编写代理类,而动态代理可以在运行时生成代理类,无需提前编写。动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理。
我们主要了解以西JDK的动态代理,了解JDK的动态代理首先需要了解两个类,一个是InvocationHandler(调用处理器),另一个是Proxy(代理)。
InvocationHandler(调用处理器):
Java编程语言中的一个接口,它是反射API的一部分。它与Proxy类一起使用,用于创建动态代理对象,可以代表底层目标对象拦截方法调用。
InvocationHandler接口有一个名为invoke()
的方法,当在代理对象上调用方法时,该方法被调用。在invoke()
方法中,可以编写自定义的逻辑来处理方法调用。
Proxy(代理):
Java中的一个类,它用于创建代理对象。代理对象实现了一个或多个接口,并使用InvocationHandler来处理方法调用。当代理对象的方法被调用时,InvocationHandler的invoke()
方法会被触发,并在该方法中进行相应的处理。
实现步骤:
-
定义一个接口:首先,需要定义一个接口,其中包含被代理对象的方法声明。
-
实现InvocationHandler接口:创建一个实现InvocationHandler接口的类,该类负责处理代理对象的方法调用。在
invoke()
方法中编写自定义逻辑,例如在方法调用前后执行额外的操作。 -
创建代理对象:使用Proxy类的
newProxyInstance()
方法创建代理对象。该方法接受类加载器、需要实现的接口以及InvocationHandler对象作为参数,并返回一个代理对象。 -
调用代理对象的方法:通过代理对象调用方法时,实际上会触发InvocationHandler的
invoke()
方法。
代码实现
InvocationHandler接口实现类:
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的角色
public Rent rent;
public void setRent(Rent rent){
this.rent=rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHose();
getMoney();
Object result = method.invoke(rent,args);
ht();
return result;
}
//额外进行的自定义操作
public void seeHose(){
System.out.println("中介看房");
}
public void getMoney(){
System.out.println("中介收费");
}
public void ht(){
System.out.println("签订合同");
}
}
getProxy()参数解释:
this.getClass().getClassLoader()
: 这是类加载器对象,用于加载代理类。通常使用当前类的类加载器来加载代理类。rent.getClass().getInterfaces()
: 这是一个接口数组,它包含了被代理对象所实现的接口。代理对象将实现这些接口,并将代理方法的调用委托给InvocationHandler处理。this
: 这是实现了InvocationHandler接口的对象,用于处理代理方法的调用。
invoke()参数解释:
Object proxy
: 这是代理对象,即通过Proxy类生成的代理对象。可以使用该对象来调用被代理对象的方法。Method method
: 这是被调用的方法的实例。通过该对象,可以获取方法的相关信息,例如方法名、参数等。Object[] args
: 这是方法调用时传递的参数数组。它包含了方法调用时传递给被调用方法的参数值。
真实角色,租客:
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//通过调用程序来处理角色来处理我们要调用的接口对象!
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
结果:
优点:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
- 一个动态代理 , 一般代理某一类业务
- 一个动态代理可以代理多个类,代理的是接口!