设计模式【3.1】-- 浅谈代理模式之静态、动态、cglib代理
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问,在某种情况下,一个对象不适合或者不能够直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。
- 可以这么理解:使用代理对象,是为了在不修改目标对象的基础上,增强主业务的逻辑。就相当于某个普通人(目标对象),他现在需要打官司,那么他可以自己学习法律,为自己辩护(相当于把业务代码逻辑自己来实现),这就是修改了目标对象,那么当然有一种更好的方法啦,那就是请律师(也相当于代理对象),业务代码(为自己辩护)可以由律师来实现。
代理一般可以分为三种:静态代理,动态代理,cglib代理;
1.静态代理
静态代理使用的时候,一般是定义接口或者父类,目标对象(被代理的对象)与代理对象都要实现相同的接口或者继承同样的父类。
下面实现静态代理
代码结构:
创建一个接口类 (IBuyDao.calss)买东西:
public interface IBuyDao {
public void buySomething();
}
然后创建一个实现了接口的目标类(BuyDao.calss )即要买东西的客户:
public class BuyDao implements IBuyDao {
@Override
public void buySomething() {
System.out.println("我是客户,我想买东西");
}
}
代理类(BuyDaoProxy):将目标对象当成属性传进去,对目标对象进行增强
public class BuyDaoProxy implements IBuyDao{
private IBuyDao target;
public BuyDaoProxy(IBuyDao target){
this.target = target;
}
@Override
public void buySomething() {
System.out.println("开始代理方法(购物)");
target.buySomething();
System.out.println("结束代理方法");
}
}
测试方法(Test.class):
public class Test {
public static void main(String [] args){
IBuyDao target = new BuyDao();
//应该写成这样
IBuyDao proxy = new BuyDaoProxy(target);
//下面的这样写就不算严格意义的代理了,代理应该是返回目标对象或接口对象(java一切皆对象)
//BuyDaoProxy proxy = new BuyDaoProxy(target);
proxy.buySomething();
}
}
结果如下:

- 在这里有一个疑惑,就是如果BuyDaoProxy.class 没有实现接口的话,也是可以跑起来,而且结果一样。假如改成这样子:
public class BuyDaoProxy {
private IBuyDao target;
public BuyDaoProxy(IBuyDao target){
this.target = target;
}
public void buySomething() {
System.out.println("开始代理方法(购物)");
target.buySomething();
System.out.println("结束代理方法");
}
}
个人理解:如果没有实现接口的话,也是可以实现的,这就相当于接口调用,但是一般我们使用代理都会是相同方法名字,使用接口的话,可以强制性使用相同的方法名,而不是随意起一个名字,不使用接口时使用相同方法名也是没有问题的,只是容易写错名字,特别是同一个代理有很多方法的时候。但是这样写就不能用 IBuyDao proxy = new BuyDaoProxy(target); ,那么这意义也就不能算是代理了,代理应该返回接口或目标对象
实现多个接口的例子(新增加了一个学生买书的接口,以及实现类):
接口:
public interface IStudent {
public void Buybook();
}
接口实现类:
public class Student implements IStudent{
@Override
public void Buybook() {
System.out.println("我是学生,我想买书");
}
}
代理类:
public class BuyDaoProxy implements IStudent,IBuyDao{
private IBuyDao target;
public Student student;
public BuyDaoProxy(IBuyDao target){
this.target = target;
}
public BuyDaoProxy(Student student){
this.student =student;
}
@Override
public void buySomething() {
System.out.println("开始代理方法(购物)");
target.buySomething();
System.out.println("结束代理方法");
}
@Override
public void Buybook() {
System.out.println("开始代理方法(买书)");
student.Buybook();
System.out.println("结束代理方法");
}
}
测试类:
public class Test {
public static void main(String [] args){
Student target = new Student();
BuyDaoProxy proxy = new BuyDaoProxy(target);
proxy.Buybook();
}
}
结果:
个人理解:实现多个接口的时候,要是没有去实现多个接口,就很容易把名字写错,所以强制性使用接口,实现一致的名字,对目标类进行功能增强(在目标类方法之前或者之后处理)。
缺点:代理对象需要和目标对象实现一样的接口,所以目标类多了,或者接口增加方法,目标类以及代理的类都要维护。
2.动态代理(即JDK代理,接口代理)
- 代理对象不需要实现接口,但是目标对象一定要实现接口
- 使用的是jdk的API,动态的创建代理对象
我们来看代理的方法源码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
从return cons.newInstance(new Object[]{h}); 这句我们可以知道,我们需要去操作h,h其实就是 InvocationHandler h 这个参数,那么我们需要重新定义 InvocationHandler h
- ClassLoader loader:是一个类加载器,这个获取类加载器的方法是固定的,我们不能坐任何改变
- Class<?>[] interfaces :这是接口类的数组,使用泛型确认接口类型,这时候接口参数就只能是目标对象所实现的接口
- InvocationHandler h:重要的是这个参数,重写它的invoke()方法,就可以实现对目标对象的接口的增强。
类的结构如下(之所以实现两个接口,是因为多接口的时候更容易分清):
代码如下:
IBuyDao.java(买东西的接口)
public interface IBuyDao {
public void buySomething();
}
IPlayDao.java(玩的接口)
public interface IPlayDao {
void play();
}
StudentDao.java(实现了买东西,玩的接口的学生类)
public class StudentDao implements IBuyDao,IPlayDao {
@Override
public void buySomething() {
System.out.println("我是学生,我想买东西");
}
@Override
public void play() {
System.out.println("我是学生,我想出去玩");
}
}
MyProxy.java 代理类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyProxy {
private Object target;
public MyProxy(Object target){
this.target=target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
//一个接口可能很多方法,要是需要针对某一个方法,那么需要在函数里判断method
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务2");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
}
);
}
}
测试类(Test.java)
public class Test {
public static void main(String [] args){
StudentDao studentDao =new StudentDao();
IBuyDao target = studentDao;
System.out.println(target.getClass());
IBuyDao proxy = (IBuyDao) new MyProxy(target).getProxyInstance();
System.out.println(proxy.getClass());
// 执行方法 【代理对象】
proxy.buySomething();
System.out.print("=========================================================");
IPlayDao target2 = studentDao;
System.out.println(target2.getClass());
IPlayDao proxy2 = (IPlayDao) new MyProxy(target2).getProxyInstance();
System.out.println(proxy2.getClass());
// 执行方法 【代理对象】
proxy2.play();
}
}
结果如下:
个人理解:代理对象类不需要实现接口,通过对象的增强返回一个接口类对象(实际上是代理后产生的),然后再调用接口方法即可。缺点:目标对象一定要实现接口,否则就无法使用动态代理,因为方法参数有一个是接口名。
3.cglib代理
Student.class:
package test;
public class Student {
public void buy() {
System.out.println("我是学生,我想买东西");
}
}
MyProxy.class(代理类)
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyProxy implements MethodInterceptor {
public 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();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("代理前-------");
proxy.invokeSuper(obj, args);
System.out.println("代理后-------");
return null;
}
}
测试类(Test.class)
public class Test {
public static void main(String[] args){
MyProxy myProxy =new MyProxy();
Student student = (Student)myProxy.getInstance(new Student());
student.buy();
}
}
结构结果:
- cgilib 可以实现没有接口的目标类的增强,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,采用的是继承,所以不能对final修饰的类进行代理。
【作者简介】:
秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java源码解析,JDBC,Mybatis,Spring,redis,分布式,剑指Offer,LeetCode等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。
平日时间宝贵,只能使用晚上以及周末时间学习写作,关注我,我们一起成长吧~
设计模式【3.1】-- 浅谈代理模式之静态、动态、cglib代理的更多相关文章
- 浅谈MVVM模式和MVP模式——Vue.js向
浅谈MVVM模式和MVP模式--Vue.js向 传统前端开发的MVP模式 MVP开发模式的理解过程 首先代码分为三层: model层(数据层), presenter层(控制层/业务逻辑相关) view ...
- Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景
我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...
- 设计模式 - 代理模式(proxy pattern) 未使用代理模式 具体解释
代理模式(proxy pattern) 未使用代理模式 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 部分代码參考: http://blog.csdn. ...
- java 代理模式(静态代理、动态代理、Cglib代理) 转载
Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...
- Java三种代理模式:静态代理、动态代理和cglib代理
一.代理模式介绍 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 简言之,代理模式就是 ...
- Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式
Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...
- Spring代理模式(jdk动态代理模式)
有动态代理和静态代理: 静态代理就是普通的Java继承调用方法. Spring有俩种动态代理模式:jdk动态代理模式 和 CGLIB动态代理 jdk动态代理模式: 代码实现: 房东出租房子的方法(继承 ...
- 《Proxy系列专题》:代理模式(静态、JDK、CGLib)
<Proxy系列专题>:代理模式(静态.JDK.CGLib)使用 现象:在如今互联网时代,项目的复杂度不断的提升,有些场景下需要一定的设计优化来支撑业务的扩展,如为了不改动原始类,但需要对 ...
- java设计模式(一)动态代理模式,JDK与CGLIB分析
-本想着这个知识点放到Spring Aop说说可能更合适一点,但因为上一篇有所提到就简单分析下,不足之处请多多评论留言,相互学习,有所提高才是关键! 什么是代理模式: 记得有本24种设计模式的书讲到代 ...
- 设计模式——代理模式(静态代理和JDK、CGLib动态代理)
简介 什么是代理模式? 代理模式就是多一个代理类出来,代替原对象进行一些操作.比如说租房的中介.打官司的律师.旅行社,他们可以代替我们做一些事情,这就是代理. 代理模式的应用场景: 如果已有的方法在使 ...
随机推荐
- SQL Server 语句日期格式查找方法
1. SQL Server中,处理日期格式和查找特定日期格式方法示例 在SQL Server中,处理日期格式和查找特定日期格式的记录是一个常见的需求.SQL Server提供了多种函数和格式选项来处理 ...
- 聊聊 iframe, CSP, 安全, 跨域
refer : https://www.cnblogs.com/kunmomo/p/12131818.html (跨域) https://segmentfault.com/a/119000000450 ...
- SSD-KD:天翼云&清华出品,最新无原始数据的蒸馏研究 | CVPR'24
无数据知识蒸馏能够利用大型教师网络所学到的知识,来增强较小型学生网络的训练,而无需访问原始训练数据,从而避免在实际应用中的隐私.安全和专有风险.在这方面的研究中,现有的方法通常遵循一种反演蒸馏的范式, ...
- Spring —— 注解开发(依赖注入)
自动装配 引用类型 简单类型 加载properties文件
- 系统编程-进程-当文件操作遇上fork
我的关联博文: 系统编程-进程-fork深度理解.vfork简介 系统编程-进程-先后fork或open一个文件的区别 test1: lseek基本使用 #include <stdio.h& ...
- 如何判断一个网站是用的Nginx,还是Apache
事件起因: 接手了同事移交过来的一个网站,但是不知道这个网站是用什么做代理的,于是就去网上查资料 解决办法: 打开cmd窗口,输入以下命令即可 curl --head 域名/IP 注意,--hea ...
- 2款.NET开源且免费的Git可视化管理工具
Git是什么? Git是一种分布式版本控制系统,它可以记录文件的修改历史和版本变化,并可以支持多人协同开发.Git最初是由Linux开发者Linus Torvalds创建的,它具有高效.灵活.稳定等优 ...
- Python技术书示例代码相关内容
二维码用微信扫,按提示填写你的邮箱,转到电脑上打开邮箱下载.清华国企网盘,比较快速.安全.放心下载. 百度网盘链接 用Edge/Chrome浏览器下载. <Python自动化运维快速入门> ...
- USB configfs与USB functionfs的区别及关系
configfs 和 functionfs 是 Linux 内核中用于配置和管理 USB 设备行为的两种不同的文件系统,它们各自的用途和工作方式有所不同,但在 USB Gadget 子系统中可以结合使 ...
- “技术沙龙”来袭,邀您一同探讨 Serverless 数据库技术最佳实践
如今,随着数据库的上云趋势,企业用户对业务连续性的要求越来越高,基于Serverless架构下的数据库也应运而生. Serverless数据库技术可以满足客户在公有云计算环境下根据业务发展弹性扩展集群 ...
