• 代理模式:为其他对象提供一种代理以控制对这个对象的访问,在某种情况下,一个对象不适合或者不能够直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。
  • 可以这么理解:使用代理对象,是为了在不修改目标对象的基础上,增强主业务的逻辑。就相当于某个普通人(目标对象),他现在需要打官司,那么他可以自己学习法律,为自己辩护(相当于把业务代码逻辑自己来实现),这就是修改了目标对象,那么当然有一种更好的方法啦,那就是请律师(也相当于代理对象),业务代码(为自己辩护)可以由律师来实现。

代理一般可以分为三种:静态代理动态代理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等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。

2020年我写了什么?

开源编程笔记

平日时间宝贵,只能使用晚上以及周末时间学习写作,关注我,我们一起成长吧~

设计模式【3.1】-- 浅谈代理模式之静态、动态、cglib代理的更多相关文章

  1. 浅谈MVVM模式和MVP模式——Vue.js向

    浅谈MVVM模式和MVP模式--Vue.js向 传统前端开发的MVP模式 MVP开发模式的理解过程 首先代码分为三层: model层(数据层), presenter层(控制层/业务逻辑相关) view ...

  2. Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景

    我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...

  3. 设计模式 - 代理模式(proxy pattern) 未使用代理模式 具体解释

    代理模式(proxy pattern) 未使用代理模式 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 部分代码參考: http://blog.csdn. ...

  4. java 代理模式(静态代理、动态代理、Cglib代理) 转载

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  5. Java三种代理模式:静态代理、动态代理和cglib代理

    一.代理模式介绍 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 简言之,代理模式就是 ...

  6. Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式

    Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...

  7. Spring代理模式(jdk动态代理模式)

    有动态代理和静态代理: 静态代理就是普通的Java继承调用方法. Spring有俩种动态代理模式:jdk动态代理模式 和 CGLIB动态代理 jdk动态代理模式: 代码实现: 房东出租房子的方法(继承 ...

  8. 《Proxy系列专题》:代理模式(静态、JDK、CGLib)

    <Proxy系列专题>:代理模式(静态.JDK.CGLib)使用 现象:在如今互联网时代,项目的复杂度不断的提升,有些场景下需要一定的设计优化来支撑业务的扩展,如为了不改动原始类,但需要对 ...

  9. java设计模式(一)动态代理模式,JDK与CGLIB分析

    -本想着这个知识点放到Spring Aop说说可能更合适一点,但因为上一篇有所提到就简单分析下,不足之处请多多评论留言,相互学习,有所提高才是关键! 什么是代理模式: 记得有本24种设计模式的书讲到代 ...

  10. 设计模式——代理模式(静态代理和JDK、CGLib动态代理)

    简介 什么是代理模式? 代理模式就是多一个代理类出来,代替原对象进行一些操作.比如说租房的中介.打官司的律师.旅行社,他们可以代替我们做一些事情,这就是代理. 代理模式的应用场景: 如果已有的方法在使 ...

随机推荐

  1. Identity – Without Identity Framework

    前言 上一回研究 Authenticate 和 Authorization 已经是 2 年前了. 业务需求一直没有增长, 所以也没有再去提升它了. 但最近业务开始上去了. 荒废的功夫又得拾起来了. 上 ...

  2. Figma 学习笔记 – Border

    Figma 画 Border 其实还挺难的 4 边框 可以用 build-in 的 Stroke 实现 注: border 是在内部的, 和 HTML 一直 Border Top, Right, Bo ...

  3. 三牧校队训练题目 Solution

    前置知识: 搜索 队列 栈 递归 (提高难度)记忆化搜索 T1:P1226 [模板]快速幂 暴力想法:\(a\times a\) 进行 \(b\) 次,每次 \(a\times a\mod p\)​. ...

  4. @vue/cli eslint插件使用指南

    使用步骤 使用 npm 安装 @vue/cli-service 版本对应的 @vue/cli-plugin-eslint 例如:"@vue/cli-service": " ...

  5. 支持Docker的底层技术(Namespaces?CGroups?UnionFS?Docker 架构?)(二)

    上一篇介绍了Docker基本概念,这一篇介绍下支持Docker的底层技术 Docker 本质就是宿主机的一个特殊进程,Docker 是通过 namespace 实现资源隔离,通过cgroup 实现资源 ...

  6. .Net技术栈

    一.后端 1. 框架 C# 异步多线程 委托 事件 标准事件模式 泛型 特性 反射 Linq Lambda Expression表达式树 PLinq IO文件操作 XML操作 .Net IOC/DI依 ...

  7. Linux_动态库与静态库(其一)

    1.动态库和静态库的定义 动态库(.so):动态库是编译后不嵌入目标文件中的共享库,在程序运行的时候才去链接动态库的代码,可以被多个程序共享使用,通常以 .so 结尾. 静态库(.a):静态库是将一组 ...

  8. foobar2000 v2.1.4 汉化版(更新日期:2024.04.24)

    foobar2000 v2.1.4 汉化版 -----------------------[软件截图]---------------------- -----------------------[软件 ...

  9. C#/.NET/.NET Core技术前沿周刊 | 第 8 期(2024年10.01-10.06)

    前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...

  10. 什么是 Ajax,Ajax 的原理,Ajax 都有哪些优点和缺点

    ajax是异步的js和xml,是一种创建交互式网页的开发技术,是和服务器进行异步通讯的技术 : 核心就是使用XMLHttpRequest向服务器发送请求获取数据 : 优点: 页面不需要刷新,用户体验良 ...