设计模式【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动态代理)
简介 什么是代理模式? 代理模式就是多一个代理类出来,代替原对象进行一些操作.比如说租房的中介.打官司的律师.旅行社,他们可以代替我们做一些事情,这就是代理. 代理模式的应用场景: 如果已有的方法在使 ...
随机推荐
- C#|.net core 基础 - 如何判断连续子序列
前两天同事遇到了一个小需求,想判断一个集合是不是在另一个集合中存在,并且要求顺序一致,然后一起讨论了下应该怎么做,有没有什么比较好的方式?下面分享一下我们想到的方法,如果你也有不同的想法也可以分享给我 ...
- 2023/11/16 NOIP 模拟赛
T1 基于1的算术 标签 暴力枚举 思路1 赛时想了个假的 DP,只拿了 77 分,,, 小于 \(10^{15}\) 的仅由 \(1\) 组成的数只有 \(15\) 个,直接枚举即可. 想了一个做法 ...
- CSS & JS Effect – 脉冲 Pulse Play Button
效果 参考 Youtube – Create a pulsing animation with CSS 重点 在背后做一个一样大的 div border 然后 animation scale up. ...
- OxyPlot公共属性一览
一.PlotModel 1.构造函数中设置的属性 public PlotModel() { this.Axes = new ElementCollection(this); //坐标轴集合; this ...
- VulnStack-红日靶机二
红日靶机二 环境搭建 只需要把虚拟机的 host-only(仅主机)网卡改为 10.10.10.0 网段,如下配置 把 NAT 网卡,改为 192.168.96.0 网段,如下 首先恢复到 v1.3 ...
- PasteForm最佳CRUD实践,实际案例PasteTemplate详解之3000问(二)
作为"贴代码"力推的一个CRUD实践项目PasteTemplate,在对现有的3个项目进行实战后效果非常舒服!下面就针对PasteForm为啥我愿称为最佳CRUD做一些回答: 哪里 ...
- USB硬件特性(速度、名称、供电)
USB传输速度 USB1.0版本,USB LS(Low Speed低速),速度1.5Mbps. USB1.1版本,USB FS(Full Speed全速),速度12Mbps. USB2.0版本,USB ...
- vue前端开发仿钉图系列(4)右侧行政区绘制的开发详解
行政区绘制是基于高德地图的api,需要在高德提供的代码基础上做好html代码在vue页面上的适配.核心功能就是选择省市区,可以根据需要绘制对应选中的地图图层.整理总结不易,如需全部代码,请联系我150 ...
- 忽略某个已经托管给git的文件,防止二次提交
# 执行命令将文件加入不提交队列 git update-index --assume-unchanged 你的文件路径 # 执行命令将文件取消加入不提交队列 git update-index --no ...
- ide 安装eval reset插件 Pycharm 永久破解
ide 安装eval reset插件 Pycharm 永久破解 1.安装eval reset的目的 Jetbrains家的产品有一个很良心的地方,他会允许你试用30天(这个数字写死在代码里了)以评估是 ...
