一、什么是代理模式?
这里不做过多的理论解释,相关资料一大堆,只简单说下我理解的代理模式,代理模式的意思就是你想完成的工作不用自己完成,交给代理去帮你去完成。代理模式的案例生活中很常见,比如:毕业了在城市里工作,需要租房,大部分人都会接触到中介,通过中介租房,这里的中介在代理模式中就充当了代理的角色,中介代理真实房东带你去看房,有些权利大的中介,甚至会代理真实房东和你签合同。
1.代理模式有什么好处?
1).中介隔离
还以上面租房为例:我是一位真实房东,家里有很多房产,打工是不可能打工的,平时就靠收收房租维持生活这样子。但是最近正好赶上毕业季,天天有租客联系我租房、看房,楼上楼下的跑是真累啊,而且还租不出去几套房子,这样的生活不能再持续下去了。于是我拨通了呗柯公寓的电话,希望呗柯公寓能够代理我的房子,帮我租出去,这样每天我就可以边收房租边好好呆在王者峡谷,不用每天去跟租客接触了。
2).在遵循开闭原则的情况下,增加功能
和呗柯公寓谈好价格后,我就等着房子租出去后,收租就行了,某一间房1500元每月,这是我跟呗柯公寓说好的价格。于是呗柯公寓的中介开始工作了,为了让租客对房子有一个很好的第一印象,负责的中介认真地打扫了整个房间,毕竟自己付出了劳动,也得给自己点回报,于是中介将房租价格加价200,打算以1700的价格出租出去,很快哈,房子就被租出去了,在我和租客签完合同后,中介又热心地帮助租客提运行李。
在上面租房的整个过程中,如果是我本人跟租客进行交涉,那么在签合同前,我不会打扫房间,也不会加价,也不会在合同签好之后,帮助租客提运行李,这些都是中介的附加行为,而我跟中介的唯一要求就是房子租出去签好合同,并且价格1500就行,中介的这些附加行为就相当于在不影响我的要求下(遵循开闭原则),增加的功能。
二、静态代理
静态代理是代理模式的一种,之所以会被称为静态代理,是因为还有个动态代理与之相对,下面直接以代码为例描述静态代理。
两个单词
Landlord:房东
Agency:中介
/**
* 房东接口,不管每个月要1500还是每个月3000、5000的房东,都要签合同
*/
public interface LandlordInterface {
void sign();
}
/**
* 每月要1500元的一个真实房东
*/
public class Landlord implements LandlordInterface{
@Override
public void sign() {
System.out.println("和租客签合同,1500元每月...");
}
}
/**
* 中介
*/
public class Agency implements LandlordInterface{
private Landlord landlord;
public Agency(Landlord landlord) {
this.landlord = landlord;
}
@Override
public void sign() {
// 在和租客签合同前,中介的"附加行为"
System.out.println("打扫房间...");
System.out.println("将房间价格加价200...");
// 房东和租客签合同
landlord.sign();
// 在和租客签合同后,中介的"附加行为"
System.out.println("帮租客提运行李...");
}
}
/**
* 模拟租房过程的一个测试类
*/
public class Test {
public static void main(String[] args) {
Landlord landlord = new Landlord();
// 中介要完成房东的要求,要完成哪个房东要求?所以需要在构造方法中传入这个租金为1500元的房东
Agency agency = new Agency(landlord);
// 签合同
agency.sign();
}
}
运行测试类的结果:
打扫房间...
将房间价格加价200...
真实房东和租客签合同,1500元每月...
帮租客提运行李...
以上代码描述的就是一个静态代理的过程,简单并且很容易理解。但是,如果这位房东不想自己收租了,于是把收租的权利也交给了中介,那么中介就要给自己添加一条收租行为;如果房东不想收水电费了,中介还要给自己添加一条收水电费的行为;如果房东...... 只要房东一有要求,那么中介类就要修改自己的行为(修改代码)。很显然,这种静态代理模式不是我们经常会用到的。
三、动态代理
动态代理分为JDK动态代理和Cglib动态代理,与静态代理相比较,从代码的角度看,代理类不再需要我们手动去创建了,而是根据被代理对象的需求动态生成一个代理对象。
1.JDK动态代理
JDK动态代理是Java提供的一种动态代理Api,要使用JDK动态代理,被代理对象需要实现一个接口,下面用代码演示一下动态代理的过程。
/**
* 房东接口
*/
public interface LandlordInterface {
void sign();
}
/**
* 每月要1500元的一个真实房东
*/
public class Landlord implements LandlordInterface{
@Override
public void sign() {
System.out.println("和租客签合同,1500元每月...");
}
}
/**
* 使用JDK动态代理,需要编写一个类实现InvocationHandler接口
*/
public class MyInvocationHandler implements InvocationHandler {
/**
* 被代理的对象(比如房东)
*/
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 中介每次在执行房东的任务前,都会加收租客费用
System.out.println("加价收取租客费用...");
// 执行房东的任务(即执行房东对象对应的方法)
Object result = method.invoke(target, args);
return result;
}
}
/**
* 模拟租房过程的一个测试类
*/
public class Test {
public static void main(String[] args) {
Landlord landlord = new Landlord();
MyInvocationHandler invocationHandler = new MyInvocationHandler(landlord);
// 动态生成一个代理对象
LandlordInterface proxy = (LandlordInterface) Proxy.newProxyInstance(
landlord.getClass().getClassLoader(),
landlord.getClass().getInterfaces(),
invocationHandler);
// 执行签合同方法
proxy.sign();
}
}
如果房东只有一个签合同行为,动态代理和静态代理相比,虽然不用去写代理类了,但是需要编写一个InvocationHandler的实现类,这里并没有体现动态代理的动态性、便利性,但是,如果房东还将收水费、收电费、收房租也代理给中介,那么就能看到动态代理的便利性了。
/**
* 房东接口
*/
public interface LandlordInterface {
void sign();
void waterCharge();
}
/**
* 每月要1500元的一个真实房东
*/
public class Landlord implements LandlordInterface{
@Override
public void sign() {
System.out.println("和租客签合同,1500元每月...");
}
@Override
public void waterCharge() {
System.out.println("收水费,每月10元...");
}
}
这里,又给房东添加了一个收水费的行为,此时中介想要代理收水费行为并从中牟利,不需要修改任何代码,直接在测试类中使用即可。
/**
* 模拟租房过程的一个测试类
*/
public class Test {
public static void main(String[] args) {
Landlord landlord = new Landlord();
MyInvocationHandler invocationHandler = new MyInvocationHandler(landlord);
// 动态生成一个代理对象
LandlordInterface proxy = (LandlordInterface) Proxy.newProxyInstance(
landlord.getClass().getClassLoader(),
landlord.getClass().getInterfaces(),
invocationHandler);
// 执行签合同方法
proxy.sign();
// 执行收水费方法
proxy.waterCharge();
}
}
如果使用静态代理的方式,还需要在中介类中添加对应的waterCharge()方法。
2.Cglib动态代理
通过上面JDK动态代理的实例可以看到,JDK动态代理依赖于被代理类对象(房东)需要实现一个接口(房东接口),如果被代理对象没有实现接口,那么就无法使用JDK动态代理了,这也是JDK动态代理和Cglib动态代理的区别之处。Cglib(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以动态地扩展Java类。Cglib动态代理示例:
<!-- 使用cglib动态代理需要引入cglib依赖或者加入cglib相关jar包 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
/**
* 每月要1500元的一个真实房东 需要注意:本类没有实现接口
*/
public class Landlord {
public void sign() {
System.out.println("和租客签合同,1500元每月...");
}
}
/**
* 使用Cglib动态代理,需要编写一个类实现MethodInterceptor接口
*/
public class MyMethodInterceptor implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
/**
* clazz是被代理对象的类类型
*/
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 中介每次在执行房东的任务前,都会加收租客费用
System.out.println("加价收取租客费用...");
// 执行房东的任务(即执行房东对象对应的方法)
Object result = methodProxy.invokeSuper(obj, args);
return result;
}
}
/**
* 模拟租房过程的一个测试类
*/
public class Test {
public static void main(String[] args) {
MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
// 动态生成一个代理对象
Landlord proxy = (Landlord) myMethodInterceptor.getProxy(Landlord.class);
// 执行签合同方法
proxy.sign();
}
}
题外话
本文拿中介租房作为一个代理模式的例子举例,对于中介加价的行为可能冒犯了中介从事者,我在此表示歉意。中介和我一样都是打工人,也很不容易,我第一次租房就是通过中介,接触到不少中介,并没有觉得租房过程有什么不舒服的地方,反倒他们的热情会让我愧疚我的租金给少了。。。还望看到这里的同学不要对中介有敌意。