设计模式-代理模式
什么是代理模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
在这里给一个举例说明一下:
租客向要去找一个房间租用,虽然它可以自己去找房源源,但是这样太慢且太费时,所以大多数租客都会选择找中介或者是租房公司去帮忙找满足自己要求的房源。这个过程就是代理模式。本应该租客们处理的shiq交予租房公司解决。
优点
- 职责清晰。
- 高扩展性。
- 智能化。
缺点
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
代理模式分类
静态代理模式
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
代码逻辑如下:
- 创建公共的父类。
- 创建需要找房的租客人群类型(学生,码农),且实现父类方法。
- 创建代理类,且实现父类方法,并且在逻辑中判别人群类型进行相应处理。
- 创建测试类,演示如何使用。
所创建类如下:
代码如下:
- 创建父类
/**
* @description: 人-父类
* @author: lichunkai
* @create: 2019-11-21 00:02
**/
public interface Person {
//所有人都需要住所
void needHouse();
}
- 创建需要找房的租客人群类型(学生,码农),且实现父类方法。
/**
* @description: 学生
* @author: lichunkai
* @create: 2019-11-20 23:55
**/
public class Student implements Person {
@Override
public void needHouse() {
//要求
System.out.println("我是学生:我需要一个学区房,离学校要近");
}
}
**
* @description: 码农
* @author: lichunkai
* @create: 2019-11-21 00:18
**/
public class Coder implements Person{
@Override
public void needHouse() {
System.out.println("我是码农:我的要求是房租便宜,我没有钱");
}
}
- 创建代理类,且实现父类方法,并且在逻辑中判别人群类型进行相应处理。
/**
* @description: 中介
* @author: lichunkai
* @create: 2019-11-20 23:58
**/
public class Agent implements Person{
private Person person ;
public Agent(Person person){
this.person = person;
}
@Override
public void needHouse() {
//中介了解客户的要求
System.out.println("你的要求是什么");
person.needHouse();
//根据客户要求找房子
if(person.getClass() == Coder.class){
System.out.println("根据你的要求我会为你找便宜的房子");
}else if(person.getClass() == Student.class){
System.out.println("根据你的要求我会为你找学区房");
}
}
}
- 创建测试类,演示如何使用。
/**
* @description: 测试类
* @author: lichunkai
* @create: 2019-11-21 00:23
**/
public class Test {
public static void main(String[] args) {
System.out.println("--------------码农找房----------");
//程序员找房
Agent agent1 = new Agent(new Coder());
agent1.needHouse();
System.out.println("--------------学生找房----------");
//学生找房
Agent agent2 = new Agent(new Student());
agent2.needHouse();
}
}
运行结果
动态代理模式
代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。【其实就是通过反射调用,这样可很方便的对代理类的函数进行统一的处理。】
jdk动态代理
该种代理模式的实现需要实现jdk接口InvocationHandler ,并且通过Proxy类产生的代理对象
代码逻辑:
- 新建一个接口
- 为接口创建的实现类
- 创建代理类实现java.lang.reflect.InvocationHandler接口
- 测试
所需要创建类如下:
代码如下:
- 新建一个接口
/**
* @program: prepare02
* @description: 人-父类
* @author: lichunkai
* @create: 2019-11-21 00:02
**/
public interface JDKPerson {
//所有人都需要住所
void needHouse();
}
- 为接口创建的实现类
/**
* @program: prepare02
* @description: 实体-学生
* @author: lichunkai
* @create: 2019-11-20 23:55
**/
public class JDKStudent implements JDKPerson {
@Override
public void needHouse() {
//要求
System.out.println("我是学生:我需要一个学区房,离学校要近");
}
}
/**
* @program: prepare02
* @description: 码农
* @author: lichunkai
* @create: 2019-11-21 00:18
**/
public class JDKCoder implements JDKPerson{
@Override
public void needHouse() {
System.out.println("我是码农:我的要求是房租便宜,我没有钱");
}
}
- 创建代理类实现 java.lang.reflect.InvocationHandler接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @program: prepare02
* @description: 中介
* @author: lichunkai
* @create: 2019-11-20 23:58
**/
public class AgentJDKProxy implements InvocationHandler {
// 目标类
private JDKPerson target;
public Object getInstance(JDKPerson target){
this.target = target;
Class<?> clazz = target.getClass();
// 通过反射生产目标对象(字节码重组来实现 新的对象 Proxy)
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
/**
* 执行目标类目标方法
* @param proxy 代理对象
* @param method 目标方法(代表正在执行的方法)
* @param args 目标方法的参数(代表正在执行的方法的实参)
* @return 表示当前执行方法的返回值
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 可以进行定制开发
System.out.println("调用的目标方法:" + method.getName());
System.out.println("我是jdk中的中介: 我要给你找房子");
// 执行目标方法 反射
method.invoke(this.target, args);
// 可以进行定制开发
System.out.println("正在根据你的要求找房子中。。。");
return null;
}
}
- 测试
/**
* @program: prepare02
* @description: 测试Jdk动态代理
* @author: lichunkai
* @create: 2019-11-21 01:02
**/
public class JDKTest {
public static void main(String[] args) {
System.out.println("----------码农找房-------");
JDKPerson obj1 = (JDKPerson) new AgentJDKProxy().getInstance(new JDKCoder());
System.out.println(obj1.getClass());
obj1.needHouse();//调用该方方法时,会先调用AgentJDKProxy.invoke() 通过反射获取方法
System.out.println("----------学生找房-------");
JDKPerson obj2 = (JDKPerson) new AgentJDKProxy().getInstance(new JDKStudent());
System.out.println(obj2.getClass());
obj2.needHouse();//调用该方方法时,会先调用AgentJDKProxy.invoke() 通过反射获取方法
}
}
cglib动态代理模式
cglib代理模式是第三方提供的一种方式,所以需要导入相应的包。当然因为第三方的原因,他的功能会比jdk代理模式更加强大。【性能上jdk和cglib两者是一样的】
代码逻辑
- 创建需要被代理的类,可以不需要任何父类
- 创建代理类并且实现MethodInterceptor,且在里面根据需求实现intercept()且统一修改返回值。
- 测试
包准备
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
代码如下
- 创建需要被代理的类
/**
* @description: 男人
* @author: lichunkai
* @create: 2019-11-21 13:00
**/
public class Man {
//说明情况 并且返回需求
public String getLove() {
System.out.println("我是男人,我要找个女朋友");
return "我要找个女朋友";
}
}
/**
* @description: 女人
* @author: lichunkai
* @create: 2019-11-21 13:00
**/
public class Woman {
//说明情况 并且返回需求
public String getLove() {
System.out.println("我是女人,我要找个男朋友");
return "我要找个男朋友";
}
}
- 创建代理类并且实现MethodInterceptor,且在里面根据需求实现intercept()且统一修改返回值。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @description: cgllib代理类-这里表示为媒婆
* @author: lichunkai
* @create: 2019-11-21 13:12
**/
public class CglibProxy implements MethodInterceptor {
// 目标类
public Object getInstance(Class<?> clazz){
// 代理类创建
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("我是媒婆获取到了你的要求了");
//invokeSuper不仅会运行一遍还会返回原有的返回值
//这是我们就可以统一更改返回值了
Object repose = methodProxy.invokeSuper(o, objects);
System.out.println("我会处理的");
repose = "我进入了返回值:"+repose.toString();
return repose;
}
}
- 测试
/**
* @description: 测试
* @author: lichunkai
* @create: 2019-11-21 13:22
**/
public class PoxyTest {
public static void main(String[] args) {
Man obj = (Man) new CglibProxy().getInstance(Man.class);
System.out.println(obj.getClass());
String reponse = obj.getLove();
System.out.println("----返回值已经被更改: "+reponse);
System.out.println("----------Woman------------");
Woman obj2 = (Woman) new CglibProxy().getInstance(Woman.class);
System.out.println(obj2.getClass());
String reponse2 = obj2.getLove();
System.out.println("----返回值已经被更改: "+reponse2);
}
}
- 结果显示
两种动态代理模式比较
- 性能:jdk1.8版本之前,jdk代理模式比cglib速度慢,消耗大,但是1.8版本之后,jdk进行了优化从而使性能和速度和cglib没有什么区别。
其他: - cglib属于三方实现的,所以需要去导包。jdk动态代理不需要引包。
- cglib具有高级功能:回调过滤器(CallbackFilter)等,jdk没有。
- jdk动态代理需要实现接口,cglib不需要。
注意:cglib的被代理类不需要任何父类也可以,jdk的代理方式需要父类。