细说代理模式
代理模式,大家应该都不陌生,很多框架底层都用了代理模式,像spring、mybatis等。虽然大家都听说过代理模式,但是可能也并不是那么地了解,本文将说一下常用的代理模式。
一、代理模式介绍
代理模式其实就是找替身,要去办一件事儿,自己不去,找人代替你去,这就是代理模式。在程序中就是,为对象提供一个替身,控制替身去访问目标对象,这样做的好处是,除了目标对象能提供的功能外,还可以让替身多做一些活,即可以扩展目标对象的功能。被代理的可以是远程对象、创建时开销很大的对象或者需要安全控制的对象。
代理模式主要分为以下三种:
-
静态代理 -
动态代理(又叫JDK代理、接口代理) -
cglib代理(也属于动态代理的范畴)
二、静态代理
「1、静态代理介绍:」
使用静态代理的时候,需要定义接口或者父类,被代理的对象和代理对象需要一起实现相同的接口或者继承相同的父类。
「2、应用实例:」
-
定义一个接口: TeacherDao
-
定义被代理的对象: TeacherDaoImpl
,需要实现TeacherDao
-
定义代理对象: TeacherDaoProxy
,也需要实现TeacherDao
-
要调用 TeacherDaoImpl
方法时,需要先创建TeacherDaoProxy
对象,然后创建TeacherDaoImpl
对象,将TeacherDaoImpl
对象交给TeacherDaoProxy
对象,再调相关方法
代码实现:
-
TeacherDao.java:
public interface TeacherDao {
void teach();
}
-
TeacherDaoImpl.java:
public class TeacherDaoImpl implements TeacherDao {
@Override
public void teach() {
System.out.println("今天又是没妹子的一天(ノへ ̄、)");
}
}
-
TeacherDaoProxy.java:
public class TeacherDaoProxy implements TeacherDao {
private TeacherDao target; // 被代理的对象
public TeacherDaoProxy(TeacherDao target){
this.target = target;
}
@Override
public void teach() {
System.out.println("代理开始");
// 这里可以写一些额外的逻辑,以达到扩展被代理对象的目的,相当于spring的前置通知
target.teach();
// 这里也可以写一些额外的逻辑,以达到扩展被代理对象的目的,相当于spring的后置通知
System.out.println("代理结束");
}
}
-
Client.java:调用代理对象
public class Client {
public static void main(String[] args){
// 创建被代理的对象
TeacherDao target = new TeacherDaoImpl();
// 创建代理对象
TeacherDaoProxy proxy = new TeacherDaoProxy(target);
// 通过代理对象调用方法
proxy.teach();
}
}
「3、静态代理的优缺点:」
-
优点:可以在不修改被代理对象的前提下扩展被代理的对象,做一些增强 -
缺点:需要实现相同的接口或者继承相同的父类,所以代理类会很多,而且如果接口或者父类有改动,代理对象和被代理对象都需要维护
三、动态代理(JDK代理)
「1、动态代理介绍:」
代理对象不要实现接口,但是被代理对象还是需要实现接口的。动态代理对象的生成,利用的是JDK的API,反射包下的Proxy类,动态地在内存中构建代理对象。
「2、java.lang.reflect.Proxy:」
这个类有一个newProxyInstance
方法,该方法接收三个参数,如下:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
「3、应用实例:」
-
定义一个接口:TeacherDao -
定义被代理的对象:TeacherDaoImpl,需要实现TeacherDao -
定义一个代理工厂ProxyFactory,有一个getProxyInstance方法,需要传入被代理的对象,然后返回代理对象实例,通过代理对象调用被代理对象的方法
代码实现:
-
TeacherDao.java:
public interface TeacherDao {
void teach();
}
-
TeacherDaoImpl.java:
public class TeacherDaoImpl implements TeacherDao {
@Override
public void teach() {
System.out.println("今天又是没妹子的一天(ノへ ̄、)");
}
}
-
ProxyFactory.java
public class ProxyFactory {
private Object target; // 被代理的对象
public ProxyFactory(Object target){
this.target = target;
}
// 给被代理的对象生成一个代理对象
public Object getProxyInstance(){
// 参数1:指定被代理对象的类加载器
// 参数2:被代理对象实现的接口类型
// 参数3:事件处理,执行被代理对象的方法
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始");
// 调用方法,args的方法的参数
Object returnValue = method.invoke(target, args);
System.out.println("JDK代理结束");
// 将执行结果return
return returnValue;
}
});
}
}
-
Client.java:通过代理调用方法
public class Client {
public static void main(String[] args){
// 创建被代理的对象
TeacherDao target = new TeacherDaoImpl();
// 创建代理对象
TeacherDao proxy = (TeacherDao) new ProxyFactory(target).getProxyInstance();
// 通过代理对象调用被代理对象的方法
proxy.teach();
}
}
四、cglib代理
「1、cglib代理介绍:」
静态代理和动态代理,被代理的对象,都需要实现接口,如果一个类没实现任何接口的,那就要用cglib代理了。cglib代理也叫子类代理,它会在内存中构建一个子类对象,从而实现对被代理对象的扩展。cglib代理底层是通过一个叫ASM的字节码处理框架来转换字节码并生成新的类从而实现代理的。被代理的类不能为final,否则会报错。被代理对象的方法如果是final/static,就不会被拦截,即不会执行被代理对象额外的业务方法。
「2、应用实例:」
-
首先要添加cglib相关依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
-
TeacherDaoImpl.java:
public class TeacherDaoImpl implements TeacherDao {
@Override
public void teach() {
System.out.println("今天又是没妹子的一天(ノへ ̄、)");
}
}
-
CglibProxyFactory.java:
// 需要实现MethodInterceptor并重写其方法
public class CglibProxyFactory implements MethodInterceptor {
private Object target;
public CglibProxyFactory(Object target){
this.target = target;
}
/**
* 返回target的代理对象
* @return
*/
public Object getProxyInstance(){
// 1. 创建工具类
Enhancer enhancer = new Enhancer();
// 2. 设置父类
enhancer.setSuperclass(target.getClass());
// 3. 设置回调函数
enhancer.setCallback(this);
// 4. 创建子类对象,即代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB代理开始");
Object returnValue = method.invoke(target, args);
System.out.println("CGLIB代理结束");
return returnValue;
}
}
-
Client.java:通过代理调用方法
public class Client {
public static void main(String[] args){
// 创建被代理的对象
TeacherDaoImpl target = new TeacherDaoImpl();
// 获取代理对象,并将被代理对象传给代理对象
TeacherDaoImpl proxy = (TeacherDaoImpl) new CglibProxyFactory(target).getProxyInstance();
// 执行方法,触发intecept方法,从而实现执行被代理对象的方法
proxy.teach();
}
}
本文分享自微信公众号 - java开发那些事(javawebkf)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
面试|Kafka常见面试问题总结
现如今,Kafka已不再是一个单纯的消息队列系统。Kafka是一个分布式的流处理平台,被越来越多的公司使用,Kafka可以被用于高性能的数据管道,流处理分析,数据集成等场景。本文分享总结了几个Kafka常见的面试问题,希望对你有所帮助。主要包括以下内容: Kafka是如何保障数据不丢失的? 如何解决Kafka数据丢失问题? Kafka可以保障永久不丢失数据吗? 如何保障Kafka中的消息是有序的? 如何确定Kafka主题的分区数量? 如何调整生产环境中Kafka主题的分区数量? 如何重平衡Kafka集群? 如何查看消费者组是否存在滞后消费? Q1:Kafka是如何保障数据不丢失的? 该问题已经成为了Kafka面试的惯例,如同Java的HashMap,属于高频出现的面试问题。那么,我们该怎么理解这个问题呢?问题是Kafka如何保障数据不丢失,即Kafka的Broker提供了什么机制保证数据不丢失的。 其实对于Kafka的Broker而言,Kafka 的复制机制和分区的多副本架构是Kafka 可靠性保证的核心。把消息写入多个副本可以使Kafka 在发生崩溃时仍能保证消息的持久性。 搞清楚了...
- 下一篇
企业中台化落地:从战略分析到战术实践及架构演进过程
谈及中台,大都雾里看花,抱有一份敬畏之心,恐误导众人。但愿通过自己的思考与一同思考实践的朋友们一些启发,让中台建设得到它应有的收益,总结出更多的成功经验。 最近接触到一些公司说在做中台,交流之后大都是应该使用什么样的技术,如何解决数据一致性问题等。其中公司发展时间有长有短,有十几二十年的传统企业,也有三四个月才起步的创业团队。交流下来心中不免有些担忧,不太清楚所谓中台是追求一种技术实现还是一个流行噱头。 经过较长时间的思考、学习和实践,我发现了解得越多越不敢讲自己做的称之为中台。它是一种企业级业务构架设计方法论,如何做好还得从企业的愿景发出分析企业发展目标,合理利用资源对系统架构进行持续性的演进。 每个企业的愿景和目标都不一样,对信息化诉求不一样,所构建出的中台系统自然也不一样,但是对企业经营有有效的提升是显尔易见的,所以设定好可量化的指标尤为重要。 背景 本文通过一个简单的例子来讲述如何进行中台化落地,企业实际过程远比这复杂得多。这是一家新零售企业,通过数字化转型获得新的业务增长点。“数字钱包”是公司的一个重点产品,项目特点是和其它业务相对独立且前端功能多样化,经商量解决引入中台化的...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS关闭SELinux安全模块
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS7设置SWAP分区,小内存服务器的救世主