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

代理一般可以分为三种:静态代理动态代理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. Vert.x HttpClient调用后端服务时使用Idle Timeout和KeepAlive Timeout的行为分析

    其实网上有大量讨论HTTP长连接的文章,而且Idle Timeout和KeepAlive Timeout都是HTTP协议上的事情,跟Vert.x本身没有太大关系,只不过最近在项目上遇到了一些问题,用到 ...

  2. HTML & CSS – dir, direction, writing-mode, ltr (left to rigth), rtl (right to left)

    前言 世界上有很多语言的阅读方向是不同的. 英文 中文 (以前才有竖排文字, 现在中文和英语一样了) 阿拉伯文 (Arabic) 面对不同的语言, HTML 和 CSS 就需要不同的写法. 虽然我没有 ...

  3. 全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类

    全网最适合入门的面向对象编程教程:50 Python 函数方法与接口-接口和抽象基类 摘要: 在 Python 中,接口和抽象基类(Abstract Base Classes, ABCs)都用于定义类 ...

  4. 可持久化线段————主席树(洛谷p3834)

    洛谷P3834 可持久化线段树 2 问题描述: 给定n各整数构成的序列,求指定区间[L,R]内的第k小值(求升序排序后从左往右数第k个整数的数值) 输入: 第一行输入两个整数n,m,分别代表序列长度n ...

  5. [namespace hdk] Balanced_tree 整合

    代码 #include<bits/stdc++.h> using namespace std; namespace hdk{ namespace balanced_tree{ const ...

  6. 支付宝 APP登录 获取用户信息 PHP(转)

    转载自:https://blog.csdn.net/wang78699425/article/details/78666401 支付宝 APP登录 获取用户信息 PHP(转) 支付宝APP登录服务端流 ...

  7. window配置onnxruntime,运行c++版本

    为了使用ONNX-Runtime-Inference这个项目,但是我缺少onnxruntime这个库, 网上找了很多教程,但是大多数都是关于linux的,这里简单记录一下我的配置流程 找到onnxru ...

  8. mysql进阶-存储引擎篇

    本篇是将基础篇的知识进行深化了解底层机制的同时讲解企业中涉及到的高层级知识. 存储引擎 1.MySQL体系结构 连接层 最上层是一些客户端和链接服务,主要完成一些类似于连接处理.授权认证.及相关的安全 ...

  9. 9.24 csp(没学会的网络流)

    T1.商品 因为边界 l , r 是线性移动的,所以答案可以线性改变,直接用set维护连续段(小于l的和大于r的)的个数,并维护ans即可. 因为set的一个小错误调了两个小时,代码打成了一坨,结果最 ...

  10. 云原生爱好者周刊:野心很大的云原生数据库 SurrealDB

    开源项目推荐 SurrealDB SurrealDB 是一个开源的端到端云原生数据库,同时支持 Table.Document 和 Graph 等多种数据模型,对外提供 SurrealQL.GraphQ ...