代理模式(Java版)-静态代理、JDK动态代理和CGLib动态代理

代理模式前传

前几天有个小朋友问到我代理模式,这个我拿手的嘛。废话不多说,直接开讲?这是不可能的。

一般呢,讲模式之前呢,礼貌上都要讲个前传。象我这种老了根本不怕没有故事祭奠的人,随手一个前传开始。

区区在下做为一名平时不怎么修边幅的小女子,突然之间接到电话,一个long long ago之前暗恋过的男神之一打来的。一直暗恋,从没明恋,他也从不知道曾被我暗恋过的男神说他出差到广州,老同学很多年没见了,问要不吃个便饭?那,就吃一个?怎么的我也算半个地主吧?

挂上电话之后,我浅浅的思考了一下,随随便便搞了一个购物清单。

虽早已不恋,男神之一当年仅有的最突出的优点---帅也早已不复存在,存在的只有突出的大肚子。但,什么也不能阻止我此时默默地开个屏~~

先找帽子,和卖帽子的店家进行售前沟通、咨询、比较,再找外套,然后衬衣~~~基本上是这样子的

就,心好累~~

简单一列就这样了,还不包括化妆品,护肤品,小饰品~~万一想穿裙子呢?要搞多几个造型对比选择呢?看着最后几个小……,世界已无爱~~确定要开屏吗?不认真地思考了一下,还是要的~~

然后看一眼,我买东西的店家其实也辛苦,它们要和我进行售前咨询,交易完成后还要保持与我的售后沟通服务。

这样看,每个商家每天要面对大量的客户,也是累并快乐着呀~~

如果,我是说如果哈,如果此时我有一名造型师呢?情况就变成这样子了

我只需要将我的需求告诉造型师,造型师帮我去找到各种商品。并保证我的售后沟通。

各商家呢?也再不用管售前咨询和售后沟通的问题,专注核心事业:搞产品。完美~~

至此,结论就来了:造型师,就是代理类。各商家就是被代理的类。我就是客户端的请求。

什么男神呀,都是不存在的,浮云。我爱代码,代码爱我,我为代码开个屏。

代理模式前传,懂?前几天问我代理模式的小朋友注意了哈,敲黑板了~~

没看懂的告诉我,看懂了的也可以告诉我。

先看懂前传,咱们后面接着静态代理~~

静态代理

好了,正传开始,此处已无男神~~区区在下小女子目下无尘,只有代码。

现在,我们开始假装要搞一个商城了。商城先得有商品吧,有商品就得把商品存到数据库里去吧。那我们就在此顺势建一个将商品持久化的类。(注:此处可能有小朋友会举手问什么是持久化,持久化你目前可以理解为就是把商品的数据保存到数据库里,以后想看,可以拿出来看。持久化了的东东,就不是只活在内存里,一关机就不见了。而是放到数据库里,保存到硬盘上了的。如果还有小朋友想问什么是内存,什么是硬盘,来,给钱我,我慢慢告诉你~~放心,我不得打小朋友的~~)。

将商品保存到数据库,增删改查是必须的。但太简单了,so easy(你确定?),暂时不理它~~(注:此处是胡说八道,实际情况是,查我们要单独处理,容后再说~~可能也许大概吧~~)

就此决定,先建三个方法:增删改:

第一代代码

商品持久化类 ProductDaoImpl

public class ProductDaoImpl {public void insert(){System.out.println("新增商品");}public void update(){System.out.println("修改商品");}public void delete(){System.out.println("删除商品");}}

客户端测试

public class ProxyTest {public static void main(String[] args) {ProductDaoImpl productDao=new ProductDaoImpl ();productDao.delete();}}

正常运行,打印出"删除商品" 没有问题。

好了,这三个特别特别复杂的方法我们写好了,核心功能都实现了(是吧,是吧,都打印出来了的~~)。然后,对数据库操作,得有事务吧(不懂什么是事务的小朋友请举手,我们另外处理。),得写日志吧,还得先判断当前登录用户对这个商品是否有操作权限吧?(注:看明白了吧?至少确实不能开个事务来独占资源,所以不一起说~~)

于是,这三个特别复杂的方法瞬间又上一个档次的复杂了起来。

第二代代码

public class ProductDaoImpl {public void insert(){System.out.println("判断是否有操作权限");System.out.println("开始事务");System.out.println("新增商品");System.out.println("结束事务");System.out.println("写日志");}public void update(){System.out.println("判断是否有操作权限");System.out.println("开始事务");System.out.println("修改商品");System.out.println("结束事务");System.out.println("写日志");}public void delete(){System.out.println("判断是否有操作权限");System.out.println("开始事务");System.out.println("删除商品");System.out.println("结束事务");System.out.println("写日志");}}

代码这么多,还大多是重复的,这不应该是人干的活,这应该是ChatGPT干的事儿哇~~纵观以上代码,每个方法中,核心业务只有一行~~边边角角,又必须存在的代码有4行之多~~所以,我们必须想个办法消灭它们,让我们核心突出。主角得有主角的待遇。

所以,我们另外建一个类,专注处理边边角角,可以吗?那是当然~~来,走起~~

新建一个代理类ProxyDao

  1. 首先,这个代理类有商品持久化类ProductDaoImpl的各同名方法。

public class ProxyDao {public void insert(){}public void update(){}public void delete(){}}
  1. 类里只需要一个成员变量productDao,该成员变量的类型就是ProductDaoImpl。

public class ProxyDao {private ProductDaoImpl productDao;public void insert(){}public void update(){}public void delete(){}}
  1. 要保证只要代理类ProxyDao的对象被创建,其属性productDao就必须是一个已构建好的ProductDaoImpl的对象,而不会是null。所以用带参构造方法给productDao赋初值。ProxyDao只有一个构造方法,是带一个参数的构造方法,参数就是ProductDaoImpl的对象。

public class ProxyDao {private  ProductDaoImpl productDao;public ProxyDao(ProductDaoImpl productDao){this.productDao=productDao;}public void insert(){}public void update(){}public void delete(){}
}
  1. 在代理类ProxyDao的各方法中,调用属性productDao的同名方法。

public class ProxyDao {private  ProductDaoImpl productDao;public ProxyDao(ProductDaoImpl productDao){this.productDao=productDao;}public void insert(){productDao.insert();}public void update(){productDao.update();}public void delete(){productDao.delete();}
}

代码写到这里,相信大家也看出来了,我们使用这个代理类ProxyDao的各个方法,与直接使用商品持久化类ProductDaoImpl的各个方法,产生的效果是一样的。那么,如果我们在代理类ProxyDao的各方法前后各加一些边边角角的内容呢?象这样?

public class ProxyDao {private ProductDaoImpl productDao;public ProxyDao(ProductDaoImpl productDao){this.productDao=productDao;}public void insert(){System.out.println("判断是否有操作权限");System.out.println("开始事务");productDao.insert();System.out.println("结束事务");System.out.println("写日志");}public void update(){System.out.println("判断是否有操作权限");System.out.println("开始事务");productDao.update();System.out.println("结束事务");System.out.println("写日志");}public void delete(){System.out.println("判断是否有操作权限");System.out.println("开始事务");productDao.delete();System.out.println("结束事务");System.out.println("写日志");}}

这样是不是商品持久化类ProductDaoImpl中的代码,只需要保留核心的那一行代码就可以了?

此时客户端测试,就不是直接调商品持久化类ProductDaoImpl对象的方法了,而是调代理类ProxyDao对象的方法。

public class ProxyTest {public static void main(String[] args) {ProductDaoImpl productDao=new ProductDaoImpl ();ProxyDao proxyDao=new ProxyDao(productDao);proxyDao.delete();}

再观察一下,我们还可以把代理类ProxyDao中重复的代码再抽一抽,该封装就封装一哈。

在客户端,productDao这个变量也只是做为参数传一传,就没用了。调方法根本不关它事,于是,感觉它可以不必拥有姓名?我们在new 代理类的时候,传一个商品持久化类ProductDaoImpl的匿名对象进去,不香吗?外面根本找不到,不用在客户端扰我视线,乱我思维。

于是最后代码成了这样

第三代代码

代理类ProxyDao

属性productDao是商品持久化类ProductDaoImpl的对象。并通过一个有参构造进行初始化。

ProxyDao类中有与ProductDaoImpl类相同的public方法,用于调用ProductDaoImpl类中的同名方法,处理核心业务。并在调用前后,进行事务、日志、判断操作权限等非核心的相关业务的处理。

注:此例中,将核心功能前后的业务处理都设计为相同,并分别封装在begin()和last()两个私有方法中。实际应用中,可根据具体情况进行处理。

public class ProxyDao {private ProductDaoImpl productDao;public ProxyDao(ProductDaoImpl productDao){this.productDao=productDao;}public void insert(){begin();productDao.insert();last();}public void update(){begin();productDao.update();last();}public void delete(){begin();productDao.delete();last();}private void begin(){System.out.println("判断是否有操作权限");System.out.println("开始事务");}private void last(){System.out.println("结束事务");System.out.println("写日志");}}

被代理的类

商品持久化类ProductDaoImpl又恢复了原样,边边角角都交给别人去处理了,拒绝拼盘,从代理做起。

public class ProductDaoImpl {public void insert(){System.out.println("新增商品");}public void update(){System.out.println("修改商品");}public void delete(){System.out.println("删除商品");}}

测试类

此时客户端测试

public class ProxyTest {public static void main(String[] args) {ProxyDao proxyDao=new ProxyDao(new ProductDaoImpl());proxyDao.delete();}}

运行结果:

代理了?对不对?代理了哇~~别激动,这才刚开始呢~~

这个时候呢,我们就得思考了,商城哇,怎么也得有订单吧?订单呢?要不来个订单持久化先?

于是订单持久化来了

新增的类订单持久化类OrderDaoImpl

public class OrderDaoImpl {public void insert(){System.out.println("新增订单");}public void update(){System.out.println("修改订单");}public void delete(){System.out.println("删除订单");}}

那订单持久化的边边角角怎么办呢?又搞一个代理类?格局小了吧~~

此时,理应接口闪亮登场了~~请跟着我念:面向接口编程~~

增加一个接口,并修改两个地方:

  1. 接口有增删改三个方法,与商品持久化类ProductDaoImpl的方法一致。

  1. 订单持久化类OrderDaoImpl和商品持久化类ProductDaoImpl都实现这个接口

  1. 代理类ProxyDao的成员变量改为接口,构造方法的形参也改为接口。

代码如下:

第四代代码

接口IGeneralDao

public interface IGeneralDao {void insert();void update();void delete();}

代理类ProxyDao

public class ProxyDao {private IGeneralDao generalDao;public ProxyDao(IGeneralDao generalDao){this.generalDao=generalDao;}public void insert(){begin();generalDao.insert();last();}public void update(){begin();generalDao.update();last();}public void delete(){begin();generalDao.delete();last();}private void begin(){System.out.println("判断是否有操作权限");System.out.println("开始事务");}private void last(){System.out.println("结束事务");System.out.println("写日志");}}

两个被代理的类

商品持久化类ProductDaoImpl

public class ProductDaoImpl implements IGeneralDao {public void insert(){System.out.println("新增商品");}public void update(){System.out.println("修改商品");}public void delete(){System.out.println("删除商品");}}

订单持久化类OrderDaoImpl

public class OrderDaoImpl implements IGeneralDao {public void insert(){System.out.println("新增订单");}public void update(){System.out.println("修改订单");}public void delete(){System.out.println("删除订单");}}

写到这里,是不是觉得想怎么代理就怎么代理了?轻松方便?

搞个测试?

测试类

public class ProxyTest {public static void main(String[] args) {ProxyDao proxyDao=new ProxyDao(new ProductDaoImpl());proxyDao.delete();System.out.println("-----------------------------");ProxyDao proxyDao2=new ProxyDao(new OrderDaoImpl());proxyDao2.insert();}}

运行结果:

好象挺完美了?世界有我,我有世界~~

但是,你以为就这样结束了吗?告诉你,并没有~~~

那设计模式,不是开玩笑的,是老几辈优秀的科学家智慧的结晶,严谨得很的~~(注:是的,long long ago之前的程序员这活,只有科学家才干得了~~)

谁能思考出哪里不严谨吗?举手没奖。

那个,代理类ProxyDao的几个public方法,是不是要和被代理的类的方法一致呀?所以呢,ProxyDao是不是也可以和被代理的类实现同一个接口呢?这样就能保证两者的方法一致,不至于被写错了?请再跟着我念:面向接口编程~~

所以,最后一改,就是把ProxyDao类改成接口的实现类。

本次静态代理模式示例代码最终的完整呈现如下:

静态代理模式代码

接口

public interface IGeneralDao {void insert();void update();void delete();}

代理类

public class ProxyDao implements IGeneralDao {private IGeneralDao generalDao;public ProxyDao(IGeneralDao generalDao){this.generalDao=generalDao;}public void insert(){begin();generalDao.insert();last();}public void update(){begin();generalDao.update();last();}public void delete(){begin();generalDao.delete();last();}private void begin(){System.out.println("判断是否有操作权限");System.out.println("开始事务");}private void last(){System.out.println("结束事务");System.out.println("写日志");}}

两个被代理的类

商品持久化类

public class ProductDaoImpl implements IGeneralDao {

public void insert(){

System.out.println("新增商品");

}

public void update(){

System.out.println("修改商品");

}

public void delete(){

System.out.println("删除商品");

}

}

订单持久化类

public class OrderDaoImpl implements IGeneralDao {

public void insert(){

System.out.println("新增订单");

}

public void update(){

System.out.println("修改订单");

}

public void delete(){

System.out.println("删除订单");

}

}

测试类

public class ProxyTest {

public static void main(String[] args) {

IGeneralDao proxyDao=new ProxyDao(new ProductDaoImpl());

proxyDao.delete();

System.out.println("-----------------------------");

proxyDao=new ProxyDao(new OrderDaoImpl());

proxyDao.insert();

}

}

运行结果:

好了,静态代理打完收工。都到这份上了,类图可以自己画否?

动态代理

前言

静态代理只能代理指定接口的实现类。即一个类只要实现了接口,就可以用一个实现了同一接口的代理类来代理它。那么问题来了,如果我有两个类,是分别实现了不同的接口,其内部方法各不相同,又都想有代理,且代理内容一样,怎么办呢?生成两个代理类?

比如,我们有一个订单管理类,一个商品管理类。订单管理类需要新增订单、更改订单状态、查询订单信息。商品管理类需要上架商品,下架商品,更新库存,查询商品信息。以上各业务每接受一次请求都需要写入日志。

我们学过静态代理模式,可以很快确定,写日志这件事,交给代理类去做,订单管理类和商品管理类专注核心功能即可。但,这就需要建两个代理类?这两个代理类干的边边角角的活还一样?明显不优雅了吧。

于是,动态代理他来了。

JDK提供了一种动态代理,只要被代理的类是实现了接口的类,就能被代理。注意:是只要实现了接口就可,不是要实现同一个接口才可。区别很大,思考一下就感觉到天地宽广了许多。

但有些类,它就是没有实现接口,又还是想要有代理类帮它处理边边角角怎么办呢?不慌,我们有CGLib动态代理。它不是java自带的,而是由第三方提供的优秀类库。CGLib动态代理的被代理类不需要实现接口,只要是能被继承类,都能被代理。(注:即被final修饰的类不能实现CGLib动态代理)

JDK动态代理

jdk动态代理,存在于java自带的核心内库,不需要引入jar包依赖什么的。

现在我们来回忆一下静态代理,代理类与被代理类实现了相同的接口,它们具有相同的方法进行代理和被代理。总结:我们需要代理类与被代理类的方法相同。

那么可否有一个方法,传出与被代理对象继承了同一个接口的代理对象。这样,我们就可以得到一个与被代理对象有同样的方法的代理对象了。

当然,方法得写在内中,那就建一个生产动态代理对象的类JdkDynamicProxy

  1. 被代理的对象还是设为类属性,通过构造方法传入。因为被代理类的类型不确定,设为Object类型。(注:别忘了所有实现了接口的类都可以做为被代理类,所以我们用顶级父类来接。)

  1. 方法getProxy()返回与被代理类实现了同一个接口的对象做为代理对象。同样,因为被代理类的类型不确定,这个方法的返回值也设为Object.

生产代理对象的类JdkDynamicProxy

public class JdkDynamicProxy {

//被代理对象

private Object obj;

//一个参数的构造方法,传入被代理对象

public JdkDynamicProxy(Object obj){

this.obj=obj;

}

/**

*

* @return 代理类对象。它与被代理对象实现同样的接口

*/

Object getProxy(){

return 返回一个与 obj实现了相同接口的对象,做为代理类;

}

来个客户端测试

public class JdkDynamicProxyTest {

public static void main(String[] args) {

JdkDynamicProxy jdkDynamicProxy=new JdkDynamicProxy(new ProductDaoImpl());

IGeneralDao proxy =(IGeneralDao) jdkDynamicProxy.getProxy();

proxy.delete();

}

}

好象架子搭起来了?

只是目前还有两个问题没解决

  1. 怎么造一个实现了被代理对象的接口的类,并生成一个代理对象出来。

  1. proxy.delete()调用的方法在哪里?怎么写代码?

先来解决第一个问题:怎么造一个实现了被代理对象的接口的类,并生成一个代理对象出来。

此时Proxy类闪亮登场。Proxy类在java.lang.reflect包中,使用它的静态方法newProxyInstance()就可以得到我们想要的代理对象。

Proxy.newProxyInstance方法

此方法需要传入三个参数,返回值是Object.很明显,返回的Object就是我们需要的代理对象。

参数​列表:

  1. 1. ClassLoader loader:被代理对象的类加载器,用于定义代理类

得到类加载器的代码实现:

ClassLoader classLoader=obj.getClass().getClassLoader();

  1. 2. Class<?>[] interfaces:被代理对象实现的接口列表,代理类统统都要实现。(友情提醒,java类是可以实现多个接口的,所以这里是个数组)

得到接口列表代码实现:

Class<?>[] interfaces=obj.getClass().getInterfaces();

事情进展到这里,我们已经得到了类加载器,可以造类了。也得到了接口列表,可以造一个把这列表里的接口统统实现了的类,没问题吧。类动态构造好了,类里的方法又怎么动态写呢?比如:delete()这个方法,怎么在代理对象里加上边边角角,在核心被代理类的delete()方法里走一圈,又回到代理对象里加边边角角呢?newProxyInstance方法的第三个参数帮你解决所有疑问。

  1. 3. InvocationHandler h:

InvocationHandler是一个接口,那我们就写一个这个接口的实现类,再new它的对象传进去试试先?

这个接口只有一个方法invoke()方法。我们需要一个实现了这个接口的对象做为参数传入,只要实现这一个方法就可以了。不管三七二十一,走一波看看效果。

InvocationHandler的实现类

注:暂时将这个类写成JdkDynamicProxy的内部类

/**

* InvocationHandler的实现类

*/

class InvocationHandlerImpl

implements InvocationHandler{

@Override

public Object invoke(

Object proxy,

Method method,

Object[] args)

throws Throwable {

//注意这句,测试效果看这里

System.out.println("成功了");

return null;

}

}

getProxy()方法

/**

* @param obj 被代理的对象

* @return 代理对象,它与被代理对象实现同样的接口

*/

public Object getProxy(){

//得到被代理对象的类加载器

ClassLoader classLoader=

obj.getClass().getClassLoader();

//得到被代理对象实现的接口列表

Class<?>[] interfaces=

obj.getClass().getInterfaces();

Object o = Proxy.newProxyInstance(

classLoader,

interfaces,

new InvocationHandlerImpl()); //

return o;

}

客户端测试运行结果:

显然有运行invoke方法

我们的俄罗斯套娃又进去一层,再来分析invoke()方法吧。

invoke方法

传入参数有三个

  1. Object proxy:代理对象

  1. Method method:要执行的方法(如delete)

  1. Object[] args: 要执行的方法的参数列表(此例中delete方法没有参数,则args为null)

返回值:

Object:执行的方法的返回值(此例中delete的返回值是void)

这个方法里要怎么干,好像也很明显了?反射,强大的无所不能的反射出现了。

我们有对象,有方法,还怕不能调用吗?不可能撒。于是我们把这个方法改成这样试一试呢

@Override

public Object invoke(Object proxy,

Method method,

Object[] args)

throws Throwable {

System.out.println("进来代理了");

Object result=method.invoke(obj);

System.out.println("我写日志了");

return result;

}

客户端测试,运行结果:

代理了成功!

动态代理成功,但还没打完。

我们继续思考一下那个内部类,它的存在好象有些累赘?不想要它,觉得碍眼,不优雅。此时我们有两种解决方案。

1. 用JdkDynamicProxy来实现InvocationHandler接口,重写invoke方法。在调用newProxyInstance方法时传入this即可。

2. 在调用newProxyInstance方法时,第三个参数直接new一个匿名内部类对象,用这个匿名内部 类实现InvocationHandler接口,并重写invoke方法。

JDK动态代理代码:

生产代理对象的类JdkDynamicProxy(实现接口的方式)

顺手把边边角角也封装到begin 和last方法中

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class JdkDynamicProxy

implements InvocationHandler{

//被代理对象

private Object obj;

//一个参数的构造方法,传入被代理对象

public JdkDynamicProxy(Object obj){

this.obj=obj;

}

/**

*

* @return代理类对象。它与被代理对象实现同样的接口

*/

public Object getProxy(){

//得到被代理对象的类加载器

ClassLoader classLoader=

this.obj.getClass().getClassLoader();

//得到被代理对象实现的接口列表

Class<?>[] interfaces=

this.obj.getClass().getInterfaces();

Object o = Proxy.newProxyInstance(

classLoader,

interfaces,

this);

return o;

}

@Override

public Object invoke(

Object proxy,

Method method,

Object[] args) throws Throwable {

begin();

Object result=method.invoke(obj);

last();

return result;

}

private void begin(){

System.out.println("进来代理了");

}

private void last(){

System.out.println("我写日志了");

}

}

生产代理对象的类JdkDynamicProxy(匿名内部类的方式)

注意这个方式中 调用Proxy.newProxyInstance()方法的第三个参数,是直接new的一个匿名类内部的对象,个人感觉有点乱,不是太推荐这种写法。

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class JdkDynamicProxy{

//被代理对象

private Object obj;

//一个参数的构造方法,传入被代理对象

public JdkDynamicProxy(Object obj){

this.obj=obj;

}

/**

*

* @return代理类对象。它与被代理对象实现同样的接口

*/

public Object getProxy(){

//得到被代理对象的类加载器

ClassLoader classLoader=

this.obj.getClass().getClassLoader();

//得到被代理对象实现的接口列表

Class<?>[] interfaces=

this.obj.getClass().getInterfaces();

Object o = Proxy.newProxyInstance(

classLoader,

interfaces,

new InvocationHandler() {

@Override

public Object invoke(

Object proxy,

Method method,

Object[] args) throws Throwable {

begin();

Object result=method.invoke(obj);

last();

return result;

}

});

return o;

}

private void begin(){

System.out.println("进来代理了");

}

private void last(){

System.out.println("我写日志了");

}

}

客户端测试类

public class JdkDynamicProxyTest {

public static void main(String[] args) {

//将生成的代理类字节码文件写到磁盘上 路径在当前项目目录下 /com/sun/proxy目录下

System.getProperties().

put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

JdkDynamicProxy jdkDynamicProxy=new JdkDynamicProxy(new ProductDaoImpl());

IGeneralDao proxy =(IGeneralDao) jdkDynamicProxy.getProxy();

proxy.delete();

}

}

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 将生成的代理类字节码文件写到磁盘上 路径在当前项目目录下 /com/sun/proxy目录下

JDK动态代理讲完

CGLib动态代理

今天讲另一种动态代理,CGLib动态代理。前面说过,JDK动态代理能代理实现了接口的类,那如果一些类没有实现任何接口,硬是需要动态代理一批批呢?这种情况,JAVA基本类库是不管的,但我们不慌。JAVA社区这么大,还能解决不了这问题?于是CGLib来了。

JDK动态代理的工作原理就是:根据接口,动态造一个实现类,实现一样的方法来达到代理的目的。

CGLib没有接口,那就反向思考,没有上一代我就造一个下一代。造个子类总能得相同的方法了吧。于是,造子类。动态造一个被代理类的子类做为代理类。

导包和依赖

这是一个第三方类库,所以我们要导包。

普通java项目导两个包

cglib-3.3.0.jar 和 asm-7.1.jar

记着别漏了asm-7.1.jar包,否则会报错

maven项目呢,依赖如下:

<dependency>

<groupId>cglib</groupId>

<artifactId>cglib</artifactId>

<version>3.3.0</version>

</dependency>

准备工作完成,下面开始套娃讲解。

还是熟悉的配方,此时我们需要一个生产动态代理类,并造出代理对象的类。这个类得有一个属性,是被代理对象。

生产代理对象的类CGLibDynamicProxy类草稿

public class CGLibDynamicProxy {

//被代理对象

private Object obj;

//一个参数的构造方法,传入被代理对象

public CGLibDynamicProxy(Object obj){

this.obj=obj;

}

/**

*

* @return 代理对象。它与被代理对象实现同样的接口

*/

public Object getProxy(){

return proxy;

}

我们已知,getProxy方法需要造一个子类出来,再给这个子类造一个对象。那么,怎么造子类?

Enhancer类

这里会用到这个类的三个方法:

  1. 1. setSuperclass(Class) 字面意思:设置父类,形参是一个Class。很明显,把被代理类传进去就可以了。

  1. 2. setCallback(Callback) 形参是一个空接口Callback。这个什么用呢?下层套娃再说,先放这里。

  1. 3. create()。没有参数,返回一个Object。这个就是造代理类和对象的方法了。

现在,我们可以来稍微完善一下getProxy()方法了。

getProxy方法草稿

public Object getProxy(){

Enhancer enhancer=new Enhancer();

enhancer.setSuperclass(obj.getClass());

enhancer.setCallback(?);

Object proxy=enhancer.create();

return proxy;

}

事情进展到这里,根据已有经验,还缺处理代理的方法。MethodInterceptor接口亮相的时候到了。这个接口继承了Callback接口,且必须实现的方法就一个intercept()。

intercept方法

参数:

  1. Object 代理对象

  1. 2. Method 被代理对象要执行的方法

  1. 3. Object[] 被代理对象要执行的方法传入的参数列表

  1. 4. MethodProxy 生成的代理对象要执行的方法

返回值:

Object :执行的代理对象的方法的返回值

代码实现:

@Override

public Object intercept(Object o,

Method method,

Object[] objects,

MethodProxy methodProxy)

throws Throwable {

System.out.println("进来代理了");

Object result=method.invoke(obj,objects);

System.out.println("我写日志了");

return result;

}

此时就很明显了,我们需要一个实现了MethodInterceptor接口的类,在intercept方法中书写我们代理方法的代码。这个类自然也是实现了Callback接口的,于是可以做为Enhancer对象的setCallback方法参数。既然如此,我们不如就直接将CGLibDynamicProxy类做为MethodInterceptor接口的实现类。懒得再多写了嘛,而且还能保持优雅。

生产代理对象的类CGLibDynamicProxy类最终版

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLibDynamicProxy implements MethodInterceptor {

//被代理对象

private Object obj;

//一个参数的构造方法,传入被代理对象

public CGLibDynamicProxy(Object obj){

this.obj=obj;

}

/**

*

* @return 代理对象。它与被代理对象实现同样的接口

*/

public Object getProxy(){

Enhancer enhancer=new Enhancer();

enhancer.setSuperclass(obj.getClass());

enhancer.setCallback(this);

Object proxy=enhancer.create();

return proxy;

}

@Override

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

begin();

Object result=method.invoke(obj,objects);

last();

return result;

}

private void begin(){

System.out.println("进来代理了");

}

private void last(){

System.out.println("我写日志了");

}

}

客户端测试:

public class CGLibDynamicProxyTest {

public static void main(String[] args) {

CGLibDynamicProxy cgLibDynamicProxy=

new CGLibDynamicProxy(new ProductDaoImpl());

IGeneralDao proxy=

(IGeneralDao)cgLibDynamicProxy.getProxy();

proxy.insert();

}

}

运行结果:

整个代理模式,打完收工。

代理模式后传之海底捞

要不大家就以下场景写个代理?

约了朋友去海底捞吃饭,一去,发现人多得不得了,根本找不到位置。服务员小姐姐就特别热情的招呼我们,登记排队、搬凳子、拿水果、小吃、帮忙登记排队涂指甲什么的,搞得热闹得很。直到店里通知:有位置了(有资源),可以进来了。于是进店。

好不容易排队进去了,点菜之后继续等。服务员小哥哥又来忙了,端茶倒水、送热毛巾、上水果拼盘等等。终于厨房表示菜做好了(资源有了),于是上菜的小哥把菜上来了。

店里只提供两个资源:店中位置,菜品。

服务员就是代理,他们不断地询问有位没?有菜没?如果没有,他们就继承服务,直到我们需要的资源有了(店内座位或菜品),他们停止询问,我们获得资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/17187.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

『行业分析神器』ChatGPT麦肯锡方法,帮你快速摸清一个行业……

作为一名营销策划人员&#xff0c;是不是经常在做策划方案的时候&#xff0c;面对一个完全不了解的全新的行业&#xff0c;不知道如何下手&#xff1f; 其实&#xff0c;对于咱们营销人来说&#xff0c;行业分析对我们来说非常重要。这是因为只有了解行业趋势、竞争对手、目标…

一文读懂ChatGPT的前世今生(附相关论文下载)

2022年11月&#xff0c;人工智能公司OpenAI推出了一款啥都会的聊天机器人&#xff1a;ChatGPT。它能聊天、能翻译、能做题&#xff0c;还会写情书、写论文、写小说……功能强大到马斯克都表示“我们离强大到危险的 AI 不远了”。 ChatGPT是平地起高楼吗&#xff1f;是横空出世吗…

【ChatGPT】InstructGPT —— 如果这么看的话,ChatGPT 不过就是人类集体智慧调教出来的一个大知识模型?

目录 概述 根据人类反馈进行调教:基于人类反馈的强化学习,RLHF 问题背景

论文阅读之Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer(2020)

文章目录 AbstractintroductionSetupmodelThe Colossal Clean Crawled CorpusDownstream TasksInput and Output Format Experiments总结参考 文章标题翻译过来&#xff0c;大概是&#xff1a;用统一的文本到文本转换器探索迁移学习的极限。 确实挺极限的。 这篇文章主要探讨了…

好家伙!GPT-4 下周要来了!

转自&#xff1a;量子位 GPT-4 将在下周发布 3 月 9 日&#xff0c;微软德国 CTO Andreas Braun 在一场名为 “AI in Focus - Digital Kickoff” 的活动中表示&#xff0c;GPT-4 将在下周发布&#xff0c;将提供多模态模型。自 3 月初发布 Kosmos-1 以来&#xff0c;微软正在与…

Transformer:《Attention is all you need》(论文精读/原理解析/模型架构解读/源码解析/相关知识点解析/相关资源提供)

本文解读Transformer较为详细&#xff0c;是一篇两万字的长文&#xff0c;如果想看简短版的&#xff0c;请参考这篇文章 目录 1 相关背景 1.1 Transformer 1.2《Attention is all you need》 1.3 论文作者 1.4 Google brain 1.5 NIPS 1.6 BLEU score 2 摘要、结论…

独立产品灵感周刊 DecoHack #046 - 试试用 ChatGPT 写周刊

本周刊记录有趣好玩的独立产品设计开发相关内容&#xff0c;每周发布&#xff0c;往期内容同样精彩&#xff0c;感兴趣的伙伴可以 点击订阅我的周刊。为保证每期都能收到&#xff0c;建议邮件订阅。欢迎通过 Twitter 私信推荐或投稿。 &#x1f4bb; 产品推荐 1. 转山 - 全自动…

亚马逊数据 各国亚马逊数据API 管理工具

什么是 API 管理&#xff1f; 应用程序编程接口管理&#xff08;简称为“API 管理”&#xff09;由一系列工具和服务组成&#xff0c;使开发人员和公司能够在安全的环境中构建、分析、操作和扩展 API。可以在本地、通过云或使用混合本地 – SaaS&#xff08;软件即服务&#x…

跨境电商如何使用WhatsApp Business与客户沟通?这几个错误需要避免

关键词&#xff1a;跨境电商&#xff0c;WhatsApp Business&#xff0c;客户沟通 WhatsApp Business缩小了客户和企业之间的巨大差距&#xff0c;并提供了直接即时通信的媒介。与客户的个人互动确实帮助许多企业扭转了局面。但很多时候&#xff0c;错误的交流方式可能会使商业…

基于Android的个人健康管理系统

目 录 基于Android的个人健康管理系统 Personal Health Management System Based On Android 1 引言 1 1.1 课题背景 1 1.2 编写目的 1 1.3 关于Android 1 1.4 关于MVC框架 3 2 可行性研究 6 2.1 技术可行性 6 2.2 经济可行性 6 2.3 时间可行性 6 3 需求分析 7 4 总体设计 8 …

大健康生态应用PC管理后台、运动健康、医疗服务、商城系统、内容管理、健康数据统计、系统管理、医疗问诊、慢病管理、科室管理、问诊订单、医疗后台管理、挂号预约、体检订单、运动健康、疾病管理、血压、血糖

大健康生态应用PC管理后台/运动健康/医疗服务/商城系统/内容管理/健康数据统计/系统管理/医疗问诊/慢病管理/科室管理/问诊订单/医疗后台管理/挂号预约/体检订单/运动健康/疾病管理/血压/血糖/运动/睡眠/计步/心率/医院管理/医生管理 Axure原型演示及下载地址&#xff1a;Axur…

ai绘画软件免费的么?如何白嫖ai绘画?

关于ai绘画的大名&#xff0c;相信很多人都听说过吧&#xff1f;但大部分都是只闻其名&#xff0c;未闻其声&#xff0c;而且很多人也都不知道怎么去用ai绘画软件&#xff0c;去哪里用这个ai绘画软件&#xff0c;其实造成这样的原因&#xff0c;主要是这些ai绘画软件都是属于国…

cursor编辑器

openAI合作伙伴推出的&#xff0c;内置GPT-4的编辑器Cursor。 不需要你有openAI账号和key&#xff0c;你就可以白嫖。可以把他当成免费版的Copilot&#xff0c;代码能力完全不输后者&#xff0c;支持多种编程语言。 当然你也可把他当做chatgpt终端来用&#xff0c;白嫖AI聊天和…

巴比特 | 元宇宙每日必读:用户可绕过付费墙白嫖内容?ChatGPT紧急暂停Bing集成,AI背后的版权、道德问题何解?...

摘要&#xff1a;据 IT 之家 7 月 4 日报道&#xff0c;OpenAI 的聊天机器人 ChatGPT 在上周推出了一个新功能&#xff0c;叫“用 Bing 浏览&#xff08;Browse with Bing&#xff09;”。但这个功能很快就被发现有一个漏洞&#xff0c;用户可以利用 ChatGPT 绕过一些网站的付费…

拒绝“白嫖”!Stack Overflow 将矛头直指 ChatGPT 等产品:用了我的数据训练,得先给钱!...

整理 | 屠敏 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 打不过就加入&#xff01; 继去年 12 月 Stack Overflow 称 ChatGPT 生成的答案正确率非常低并决定宣布临时封禁 ChatGPT 之后&#xff0c;其开始以另一种身份加入这场 AI 竞赛中。 据外媒 Wired 报道&…

ChatGPT紧急下线联网模式,曾被曝能白嫖付费网页

克雷西 发自 凹非寺量子位 | 公众号 QbitAI 用ChatGPT白嫖网页付费内容&#xff0c;马上就要行不通了。 就在昨天&#xff0c;OpenAI宣布&#xff0c;暂时禁用ChatGPT的官方网页浏览模式。 虽然公告说的很隐晦&#xff0c;但此次功能下架&#xff0c;主要目的就是针对“翻越付费…

轻松白嫖GPT-4,已经标星38K,不再害怕高昂的AI模型费用!

文章目录 白嫖GPT-4当前可白嫖站点 白嫖GPT-4 计算机专业学生xtekky在GitHub上发布了一个名为gpt4free的开源项目&#xff0c;该项目允许您免费使用GPT4和GPT3.5模型。这个项目目前已经获得了380000颗星。 开源地址&#xff1a;https://github.com/xtekky/gpt4free 简而言之&a…

这3个网站能够让你白嫖GPT4

1、perplexity&#xff08;https://www.perplexity.ai/&#xff09; 特点&#xff1a; 1&#xff09;保存试用上限5次GPT4&#xff0c;每4小时恢复1次 2&#xff09;试用需连接外网谷歌账号 3&#xff09;可以查看其他用户的提出的热门问题 4&#xff09;可以开启对话线程。在…

王琤:当数据治理遇上ChatGPT

以ChatGPT为代表的人工智能等技术正在“狂飙”&#xff0c;为全球带来一场翻天覆地的变革。4月27日在2023数据治理新实践峰会上&#xff0c;Datablau数语科技创始人&CEO王琤先生以《数据治理新实践与人工智能》为主题进行了分享&#xff0c;与参会同仁共同探索当数据治理遇…

免费AI工具推荐:Chat8

最近很火的ChatGPT&#xff0c;很多人想尝试但都无从入手&#xff0c;这里推荐一款免费使用的国内平替ChatGPT的AI聊天工具【Chat8】。 【Chat8官网介绍】 Chat8 是一款基于OpenAi的ChatGPT3.5接口开发的聊天网站&#xff0c;旨在为用户提供高效便捷的沟通体验。相较于ChatGPT&…