代理模式前传

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

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

区区在下做为一名平时不怎么修边幅的小女子,突然之间接到电话,一个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();

}

}

运行结果:

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

代理模式后传之海底捞

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

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

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

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

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

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

  1. 什么是代理模式?代理模式有什么用?通过一个小程序分析静态代理和动态代理。自己简单实现动态代理。JDK动态代理和CGLIB动态代理的区别。

    1. 代理模式有什么用 ①功能增强,在实现目标功能的基础上,又增加了额外功能.就像生活中的中介一样,他跟两边客户会有私下的交流. ②控制访问,代理不让用户直接和目标接触.就像中间商一样,他们不会让我们 ...

  2. Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理

    Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...

  3. Java两种动态代理JDK动态代理和CGLIB动态代理

    目录 代理模式 JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式.为了对外开放协议,B往往实现了一个 ...

  4. Java中动态代理的两种方式JDK动态代理和cglib动态代理以及区别

    视频功能审核通过了,可以看视频啦!记得点关注啊~ 注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了 记得点关注啊,视频里的wx二维码失效了,wx搜索:"聊5毛钱的java&q ...

  5. 【Spring6】| GoF之代理模式(JDK动态代理和CGLIB动态代理)

    目录 一:GoF之代理模式 1. 对代理模式的理解 2. 静态代理 3. 动态代理 3.1 JDK动态代理 3.2 CGLIB动态代理 一:GoF之代理模式 1. 对代理模式的理解 生活场景1:牛村的 ...

  6. JAVA 进阶篇 动态代理 JDK动态代理和CGlib动态代理

    JDK动态代理和CGlib动态代理 JDK动态代理: 利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. CGlib动态代理: 利用ASM(开源的Java ...

  7. 利用代码分别实现jdk动态代理和cglib动态代理_面试之动态代理

    大家好!我是CSRobot,从今天开始,我将会发布一些技术文章,内容就是结合春招以来的面试所遇到的问题进行分享,首先会对知识点进行一个探讨和整理,在最后会给出一些面试题并作出解答,希望可以帮助到大家! ...

  8. Java中的原生动态代理和CGLIB动态代理的原理,我不信你全知道!

    作者:CarpenterLee cnblogs.com/CarpenterLee/p/8241042.html 动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询 ...

  9. jdk动态代理和cglib动态代理实现及区别

    代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 代理模式又分为:静态代理.jdk动态代 ...

  10. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

最新文章

  1. how is my appointment opened in HCP
  2. 【计算机组成原理】补码的加减运算方法
  3. react学习(6)----react样式多用内联
  4. 网络通道数2的倍数_限流笔记-通道限流(二)
  5. dede设置当前栏目的样式
  6. [转]webMethods公司简介
  7. VB如何根据窗口标题获得进程名称
  8. php下载功能,js php实现无刷新下载功能
  9. SpringBoot启动流程原理+自动装配原理
  10. Android studio开发Android图灵智能聊天机器人,课程设计报告
  11. JUCE学习笔记04-LookAndFeel类自定义Slider颜色
  12. 软件人员kpi制定模板_软件公司员工月度KPI考核表
  13. 《数独游戏的设计与实现》
  14. 广电700M,到底行不行?
  15. 尾部关性尾部风险平价和圣杯分布
  16. Java学习笔记_15 项目实战之天天酷跑(二):开始游戏界面
  17. 百度人脸识别的两个方式的使用
  18. Matlab中plot画图线型、标记和颜色
  19. 初见 http 401------谈谈401和403的区别
  20. 2019网易笔试(1-3题)

热门文章

  1. 吃鸡游戏中如何实现视野轻微的左右摆动和上下波动
  2. 苹果内购IAP流程(转载)
  3. 简单的使用protobuf和protostuff
  4. php px,px什么意思
  5. Kindle刷机安装微信读书。
  6. C++中的模板(templates)
  7. 3DMAX圆形结构插件LoopRegularizer使用教程
  8. 你享受着安逸,别人享受着复利
  9. 深度解析:也曾“风光无限好”的大数据公司为何“近黄昏”
  10. Hi-C data analysis tools and papers