这篇讲代理模式
一、代理模式定义,
代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式组成:
抽象角色: 通过接口或抽象类来声明真实角色实现的业务方法
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法 并可以附件上自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
代理模式优点:
(1) 职责清晰:真实角色就是实现实际的业务逻辑,不必关系其他非本职能的事务,通过后期的代理来完成一件完整事务,附带的结果就是编程简洁清晰。
(2) 代理对象可以在客户端和真实对象之间起到中介作用和保护真实对象的作用,开-闭原则。
(3) 高扩展性
模式结构图:
二、代理模式分类
1、静态代理:静态代理指的是我们自己创建代理类,代理类在代码编译前就已经创建。
2、动态代理:动态代理指的是由程序动态生成代理类,常见的动态代理方式有JDK的反射方式生成、CGLib反射方式生成,通过javaassist字节码生成、ASM方式生成等。
三、代理模式代码实现
关于实现,首先是静态代理:
1、静态代理(顾客在网上购物):
客户类:Customer .java
package com.interview.design.proxy;
public class Customer {
public static void main(String[] args) {
Shopping proxy = new Proxy(new GoodsFactory());
System.out.println("我要买双个包");
proxy.buyBag();
System.out.println("我要买件衣服");
proxy.buyClothes();
System.out.println("我要买双鞋子");
proxy.buyShoes();
}
}
抽象角色类:Shopping.java
package com.interview.design.proxy;
public interface Shopping {
//买包
public void buyBag();
//买衣服
public void buyClothes();
//买鞋子
public void buyShoes();
}
代理角色类:Proxy.java
package com.interview.design.proxy;
public class Proxy implements Shopping {
private Shopping goodsFactory;
public Proxy(Shopping goodsFactory) {
this.goodsFactory = goodsFactory;
}
@Override
public void buyBag() {
goodsFactory.buyBag();
}
@Override
public void buyClothes() {
goodsFactory.buyClothes();
}
@Override
public void buyShoes() {
goodsFactory.buyShoes();
}
}
真实角色类:goodsFactory.java
package com.interview.design.proxy;
/**
* @Author: gtd
* @Description:
* @Date: Create in 17:37 2019/1/10
*/
public class GoodsFactory implements Shopping {
@Override
public void buyBag() {
System.out.println("生产了一个包");
System.out.println("包包已经寄出");
}
@Override
public void buyClothes() {
System.out.println("生产了一件衣服");
System.out.println("衣服已经寄出");
}
@Override
public void buyShoes() {
System.out.println("生产了一双鞋子");
System.out.println("鞋子已经寄出");
}
}
2、动态代理方式:
①、基于JDK的动态代理方式(租客去租房):
客户类:Tenant.java
package com.interview.design.proxy.dynamicProxy;
import com.interview.collection.ArrayListRemove;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
/**
* @Author: gtd
* @Description:
* @Date: Create in 0:19 2019/1/11
*/
public class Tenant {
public static void main(String[] args) {
LandLord landLord = new LandLord();
//由JDK通过反射去创建代理角色
//获取类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//获取被代理类实现的接口数组
Class[] interfaces = landLord.getClass().getInterfaces();
//获取代理类对象
Renting proxy = (Renting) Proxy.newProxyInstance(classLoader,interfaces,
(Object target, Method method, Object[] params)->{ //此处由于实现比较简单,所以使用Lambda表达式
System.out.println("我是代理,我去找房东");
Object obj = method.invoke(landLord,params);
System.out.println("代理任务完成");
return obj;
});
System.out.println("我想租房,并联系代理");
proxy.rentOut();
}
}
抽象角色接口Renting.java:
package com.interview.design.proxy.dynamicProxy;
/**
* @Author: gtd
* @Description:
* @Date: Create in 0:21 2019/1/11
*/
public interface Renting {
//出租
public void rentOut();
}
真实角色类LandLord.java
package com.interview.design.proxy.dynamicProxy;
/**
* @Author: gtd
* @Description:
* @Date: Create in 0:20 2019/1/11
*/
public class LandLord implements Renting{
public void rentOut(){
System.out.println("我是房东,我有房出租");
}
}
②、基于CGLib的动态代理方式(劝孩子学习):
客户类 Parent.java
package com.interview.design.proxy.cglib;
/**
* @Author: gtd
* @Description:
* @Date: Create in 11:11 2019/1/11
*/
public class Parent {
public static void main(String[] args) {
System.out.println("我们是小明的父母,请劝一下孩子,让他好好学习吧");
Child child = new Child();
//获取代理工厂
StudyProxyFactory studyProxyFactory = new StudyProxyFactory();
//获取代理类
Child proxy = (Child) studyProxyFactory.getInstance(child);
proxy.study();
}
}
创建代理类的工具类 StudyProxyFactory.java
package com.interview.design.proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Author: gtd
* @Description:
* @Date: Create in 11:13 2019/1/11
*/
public class StudyProxyFactory implements MethodInterceptor {
private Object target;
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("小明呀,快高考了,你爸妈让我劝你好好学习");
Object obj = method.invoke(target,objects);
System.out.println("中介的任务完成了");
return obj;
}
}
真实业务角色类 Child.java
package com.interview.design.proxy.cglib;
/**
* @Author: gtd
* @Description:
* @Date: Create in 11:36 2019/1/11
*/
public class Child {
public void study(){
System.out.println("我是书中的小明,我醒悟了,要好好学习");
}
}
四、代理模式使用场景
1、静态代理:实现简单,使用前提是我们的真实业务角色必须实现某个接口,我们才能创建一个实现了同样接口的代理角色,并且当不同的业务角色(实现不同接口的类)都需要代理角色的时候要为每一个业务角色都创建一个代理类。静态代理适用于比较简单的业务逻辑,如对真实业务角色进行隐藏,进行封装,进行扩展。
优点:比较简单,易于实现,降低使用者与业务角色之间的耦合,起到保护业务角色的作用,可以在后期不修改业务角色的前提下做简单的扩展。
缺点:需要一个个手动创建代理类;真实角色类必须实现某个接口;当代理类较多时代码会变得冗余;由于给真实业务角色做了一次代理封装,当代理角色的需要做的功能比较复杂或被频繁调用时会降低系统性能(所有代理角色都是如此)。
2、动态代理:动态代理不需要我们手动的创建代理角色,只需要对外提供一个代理角色的创建工具类,我们可以选择使用JDK的动态代理或CGLib的动态代理,当然两者的区别是基于JDK的动态代理需要真实业务角色需要实现接口,而CGLib则不需要,CGLib不能对final修饰的类创建代理类(CGLib其实是通过创建子类的方式创建代理类)。CGLib创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。动态代理适用于需要大量代理角色并且代理角色业务一致的场景。比如我们常用的Spring AOP,RPC框架等。
优点:动态创建,不需要我们手动一个个的创建代理类,代理角色通过反射调用真实角色的方法,不需要知道具体的方法名称和参数信息。
缺点:JDK动态代理和CGLib代理配合使用就能满足我们的需求了,当然如果两者能合二为一自动适应就更好了。
五、引用和借鉴
科学百科词条 https://baike.baidu.com/item/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/8374046?fr=aladdin
http://www.cnblogs.com/meet/p/5116464.html
如果在阅读时发现有错误或不足的地方清指正,大家一起学习。
源码上传地址:https://github.com/tiedungao/interviewDesignModelProject