代理模式的作用:在访问对象的时候添加一个中间层进行代理访问,可以对访问过程加以控制,例如检查访问者信息、进行日志记录等操作。
我们最容易接触到代理模式的地方就是Spring中的AOP,他就使用了动态代理的模式进行操作。既然有动态代理,那么肯定有静态代理,接下来我们就来学习一下这些内容吧。
静态代理
我们使用招聘进行举例,我们去招聘是给老板办公,按理说应该由老板进行面试,但是老板说主管比他更了解业务,由他来进行面试,本来老板不懂技术,可能直接问问我每个月要多少工资,可是主管面试的时候会问我一些专业问题,比如Spring的Bean创建一共分了多少步,虽然我很苦逼,但是这里的主管起到了代理作用,他在老板面试的基础上又新增加了一些内容。
我们将这个流程图用代码实现一下
1.创建抽象对象进行行为统一,确定主管要实现的方法有哪些(代理的内容)
package com.test.proxy;
/**
* 招聘接口
* @author ME
* @date 2022/2/20 21:28
*/
public interface Recruit {
// 面试的方法
public void recruitPerson();
}
2.创建被代理对象,实现招聘方法,添加老板面试的方法内容
package com.test.proxy;
/**
* 老板类
* @author ME
* @date 2022/2/20 21:29
*/
public class Boss implements Recruit{
@Override
public void recruitPerson() {
System.out.println("你期望的薪资待遇是多少呀?小伙子,6000够不够啊");
}
}
3.创建代理对象,实现招聘方法,注入老板对象作为属性,在调用老板的招聘方法前后进行自己的逻辑添加
package com.test.proxy;
/**
* 主管类
* @author ME
* @date 2022/2/20 21:32
*/
public class Secretary implements Recruit{
// 注入Boss对象
private Boss boss;
// 显式创建无参构造方法
public Secretary() {
}
// 创建有参构造方法
public Secretary(Boss boss) {
this.boss = boss;
}
// 重写招聘方法,在老板招聘问题前后添加自己的问题与面试结果
@Override
public void recruitPerson() {
myRecruitQuestion();
boss.recruitPerson();
myRecruitResult();
}
// 自己的业务逻辑
private void myRecruitQuestion() {
System.out.println("Spring创建bean的过程分了多少步啊,都干了什么呀?小伙子");
}
private void myRecruitResult() {
System.out.println("感谢面试,回家等消息吧,下一位");
}
}
4.创建访问者,对代理对象(主管类)进行调用
package com.test.proxy;
/**
* @author ME
* @date 2022/2/20 21:31
*/
public class Me {
public static void main(String[] args) {
// 调用代理对象,并不直接调用Boss对象
Secretary secretary = new Secretary(new Boss());
// 执行代理对象的方法
secretary.recruitPerson();
}
}
我们来看一下方法执行的结果
在打印结果中我们可以看出代理模式的优点:
1.职责清晰,老板只关心他要多少薪资,具体他的技术水平由主管来评测。
2.高扩展性,假如过了一段时间主管说面试不能只问技术了,还给考察一下人品,那么再添加一个考察人品的方法就可以了。
3.低耦合,可以在不改变原有逻辑的情况下进行逻辑增加
4.当多个方法需要添加统一的新逻辑的时候,方便集中管理,可以添加代理类统一进行实现
缺点:
1.添加了代理模式,某些情况下可能会造成请求较慢
2.每一个真实对象都添加一个或多个代理角色的话代码量会翻倍,降低开发效率
动态代理
动态代理与静态代理的区别就在于动态代理的代理类(主管类)是动态生成的,不是我们编写的。
动态代理分为两大类:1.基于接口的动态代理 2.基于类的动态代理
基于接口的动态代理典型为—JDK动态代理
基于类的动态代理典型为—CGlib动态代理
JDK动态代理
上面的静态代理我们有四个角色,分别为抽象对象,被代理对象,代理对象,访问对象。动态代理就是在编码的时候不指定代理的方法,被代理的方法在运行时被确定。所以我们主要改动的是代理对象(改为动态代理配置)和访问对象(更改访问方式)。
1.动态代理配置类
package com.test.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author ME
* @date 2022/2/20 22:26
*/
// 实现InvocationHandler接口重写invoke方法
public class JdkProxy implements InvocationHandler {
// 注入接口对象用于进行代理类创建
private Recruit recruit;
public Recruit getRecruit() {
return recruit;
}
public void setRecruit(Recruit recruit) {
this.recruit = recruit;
}
// 手动创建的生成代理类的方法
public Object getProxy() {
// 参数一:代理类的类加载器,参数二:抽象对象的接口对象,参数三:InvocationHandler实现类
return Proxy.newProxyInstance(this.getClass().getClassLoader(), recruit.getClass().getInterfaces(), this);
}
// 处理代理的实例,并返回结果(代理类执行的所有方法都是通过此方法进行调用)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 自己的业务逻辑
myRecruitQuestion();
// 执行真实对象的方法
Object invoke = method.invoke(recruit, args);
// 自己的业务逻辑
myRecruitResult();
return invoke;
}
// 自己的业务逻辑
private void myRecruitQuestion() {
System.out.println("Spring创建bean的过程分了多少步啊,都干了什么呀?小伙子");
}
private void myRecruitResult() {
System.out.println("感谢面试,回家等消息吧,下一位");
}
}
2.访问对象进行访问
package com.test.jdkproxy;
/**
* @author ME
* @date 2022/2/20 21:31
*/
public class Me {
public static void main(String[] args) {
// 创建JDK动态代理配置类对象
JdkProxy jdkProxy = new JdkProxy();
// 将真实对象注入配置类对象中
jdkProxy.setRecruit(new Boss());
// 通过方法进行代理类的创建
Recruit proxy = (Recruit) jdkProxy.getProxy();
// 执行方法
proxy.recruitPerson();
}
}
如上方式就可以使用动态代理的方式进行对象访问了,这边提出一个公共方法,有需要的同学可以直接使用:
package com.test.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author ME
* @date 2022/2/20 22:26
*/
// 实现InvocationHandler接口重写invoke方法
public class JdkProxy implements InvocationHandler {
// 注入接口对象用于进行代理类创建
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
// 手动创建的生成代理类的方法
public Object getProxy() {
// 参数一:代理类的类加载器,参数二:抽象对象的接口对象,参数三:InvocationHandler实现类
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 处理代理的实例,并返回结果(代理类执行的所有方法都是通过此方法进行调用)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行真实对象的方法
Object invoke = method.invoke(target, args);
return invoke;
}
}
CGlib动态代理
由于CGlib是第三方组件,所以需要手动导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
与JDK动态代理一样,我们同样需要修改两个地方,代理对象与访问对象。
1.进行代理类的配置类创建
package com.test.cglibproxy;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author ME
* @date 2022/2/21 0:07
*/
public class CgLibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 自己的业务逻辑
myRecruitQuestion();
// 执行真实对象的方法
Object invoke = methodProxy.invokeSuper(o, objects);
// 自己的业务逻辑
myRecruitResult();
return invoke;
}
// 自己的业务逻辑
private void myRecruitQuestion() {
System.out.println("Spring创建bean的过程分了多少步啊,都干了什么呀?小伙子");
}
private void myRecruitResult() {
System.out.println("感谢面试,回家等消息吧,下一位");
}
}
2.创建访问对象进行方法调用
package com.test.cglibproxy;
import net.sf.cglib.proxy.Enhancer;
/**
* @author ME
* @date 2022/2/20 21:31
*/
public class Me {
public static void main(String[] args) {
// 创建代理类增强器,帮助进行代理类的创建
Enhancer enhancer = new Enhancer();
// 设置增强器的父类
enhancer.setSuperclass(Boss.class);
// 设置增强其的回调对象
enhancer.setCallback(new CgLibProxy());
// 进行代理对象的创建
Boss boss = (Boss) enhancer.create();
// 通过代理对象执行方法
boss.recruitPerson();
}
}
好,到这里代理模式我们就说的差不多了哈,整理一下JDK动态代理与CGlib动态代理的区别:
JDK只可以代理接口,而CGlib既可以代理接口也可以代理类,我个人总结下来仅有这一点区别,网上还有同学说CGlib是基于字节码的,性能要优于基于反射的JDK,但是也有人说JDK1.8以后两者区别不大,甚至JDK的动态代理要优于CGlib动态代理,这个就仁者见仁智者见智啦,我这里没做测试就不予评价了,其实举了这两个动态代理的例子,主要是想让各位小伙伴们了解一下,因为Spring中的AOP就是基于动态代理实现的,使用的就是JDK动态代理与CGlib动态代理,所以大家还是好好了解一下吧。
其他的内容在菜鸟教程中有比较详细的过程我就不在此赘述了,这篇文章的意义就是希望能够通过自己的理解尽可能形象的解释一下设计模式,使得抽象的内容理解起来变得简单些。