欢迎大家来到IT世界,在知识的湖畔探索吧!
一、前言
今天,认真的看了一遍昨天写的静态代理、jdk动态代理、cglib动态代理,但是其中动态代理只是简单的实现,并没有更深层次的去思考,去理解他的含义,今天又是一个加班的夜晚,我重新好好学习一下动态代理。
二、动态代理
2.1 什么是动态代理?
2.1.1 代理
在说动态代理之前,我们在重新复习一遍,什么是代理?
为其他对象提供一种代理以控制对这歌对象的访问
简单的理解,可以理解为我们访问一个方法时,不是直接new出对象,然后访问对象的方法,而是通过另外一个类去访问目标方法(这个类也就是代理类,代理类中持有被代理类的对象),举个例子:
我们需要购买红烧牛肉的方便面,我们不是直接跟厂家买,而是通过食品批发部去买
我============>食品批发部(代理类)===========>康师傅厂家(被代理类)
食品批发部持有康师傅厂家的联系方式(new 对象),这里食品批发部就被称为代理类,康师傅厂家就被称为代理类
复制代码
欢迎大家来到IT世界,在知识的湖畔探索吧!
这样是不是很结合生活,理解起来简单许多!
有这么几个作用:
- **控制访问:**在代理中,控制是否可以调用目标类的方法
- **功能增强:**可以在完成目标类的方法调用时,附加一些额外的功能(功能增强) –>比如飞猪,在订票的同时,还提供一些保险、接送机服务,也就是功能增强
2.1.2 动态代理
言归正传,开始动态代理的解释
动态代理:在程序执行过程中,使用反射机制,并动态的指定要代理的目标类
简单理解为动态代理是一种创建java对象的能力,可以不需要new出对象,就能创建代理类对象
三、动态代理的分类
- JDK代理
- cglib代理
3.1 JDK代理
使用JDK反射包( java.lang.reflect )中的类和接口实现动态代理的功能,需要依赖于接口和实现类
主要有以下几个核心类/接口:
- InvocationHandler:它是一个接口,他有一个方法为: invoke(): 表示代理对象要执行的操作,比如调用目标方法 /** * * @param proxy jdk创建的代理对象,不需要传递,由jdk自动完成 * @param method 目标类中的方法,不需要传递,由jdk自动完成 * @param args 目标方法的参数,不需要传递,由jdk自动完成 * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 使用方法为: 1. 实现InvocationHandler接口 2. 重写invoke方法,把需要完成的功能写在这里 复制代码
- Method:表示方法,也就是目标类中的方法,通过Method可以执行某个目标类的方法 method.invoke(); 其中invoke方法与Invocation中的invoke不相同,只是同名而已 //用来执行目标类中的方法 method.invoke(ticketService, “普洱思茅机场”, “昆明长水机场”); //等同于以下: int money = ticketService.buyTicket(“普洱思茅机场”, “昆明长水机场”); 复制代码
- Proxy:核心的对象,用于创建代理对象,之前我们创建对象使用new来创建,但是现在使用 //创建代理对象 Proxy.newProxyInstance() //等同于: ITickService ticketService = new TicketServiceImpl(); 复制代码 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {}; ClassLoader loader: 类加载器,通过a.getClass().getClassLoader(),目标兑现的类加载器 Class<?>[] interfaces: 接口,目标对象实现的接口,通过反射获取 InvocationHandler h: 自定义的InvocationHandler实现类,代理类需要完成的操作 返回值: 创建的代理对象 复制代码
3.1.1 Method
通过反射获取Method对象,表示类中的方法
创建接口
欢迎大家来到IT世界,在知识的湖畔探索吧!public interface ITicketService {
public int buyTicket(String from,String to);
}
复制代码
创建实现类
/**
* 购票实现类
*/
public class TicketServiceImpl implements ITicketService {
private Logger log = LoggerFactory.getLogger(TicketServiceImpl.class);
/**
* 购票逻辑
* @param from
* @param to
* @return
*/
@Override
public int buyTicket(String from, String to) {
int money = 260;
log.info("您选择的{}到{}的机票价格为{}",from,to,money);
return money;
}
}
复制代码
启动类
欢迎大家来到IT世界,在知识的湖畔探索吧!/**
* 通过反射机制执行buyTicket方法
* TODO: 获取ITicketService类中的方法--->Method
*/
@Test
void contextLoads() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ITicketService ticketService = new TicketServiceImpl();
/**
* 通过反射获取ITicketService中的buyTicket的方法
* 参数:
* 1.需要调用的方法名称
* 2.第二个参数为可变长参数,表示参数的类型
*/
Method method = ITicketService.class.getMethod("buyTicket", String.class, String.class);
/**
* 执行类中的方法调用
* 参数:
* 1. Object 表示要执行的对象
* 2. Object...args 方法执行时的参数
* 返回值:
* 方法的返回值
*/
Object result = method.invoke(ticketService, "普洱思茅机场", "昆明长水机场");
System.out.println("获取到的返回值:"+result);
}
复制代码
3.1.2 实现JDK动态代理
接口类和实现类和以上一致
代理类
实现思路如下:
- 创建ProxyObject类,实现InvocationHandler方法 public class ProxyObject implements InvocationHandler 复制代码
- 2.通过构造方法,注入目标对象 //目标类 private Object object; //构造方法,注入目标类的对象 public ProxyObject(Object object){ this.object = object; } 复制代码
- 3.通过Proxy类的newProxyInstance方法创建代理对象
/**
* 获取代理对象
* @return
*/
public Object getProxyInstance(){
return Proxy.newProxyInstance(
object.getClass().getClassLoader(), //目标类的类加载器
object.getClass().getInterfaces(), //目标类的接口
this //自定义的InvocationHandler实现类
) ;
}
复制代码
- 4.重写invoke方法,用于调用目标类的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强代码
log.info("正在查询金额,请稍等!");
Thread.sleep(3000);
//调用目标方法
Object result = method.invoke(this.object,args);
//代码增强
log.info("查询成功!");
return result;
}
复制代码
- 5.调用目标类的方法
/**
* jdk动态代理
*/
@Test
public void jdkProxy(){
ITicketService ticketService = (ITicketService) new ProxyObject(new TicketServiceImpl()).getProxyInstance();
ticketService.buyTicket("普洱思茅机场","昆明长水机场");
}
复制代码
也可以使用下面的写法:
//创建目标类
ITicketService ticketService = new TicketServiceImpl();
//创建代理类的对象
ProxyObject proxyObject = new ProxyObject(ticketService);
ticketService = (ITicketService) proxyObject.getProxyInstance();
//调用目标方法
ticketService.buyTicket("普洱思茅机场","昆明长水机场");
复制代码
2021-01-19 23:24:35.820 com.yangzinan.proxydemo.jdk.ProxyObject : 正在查询金额,请稍等!
2021-01-19 23:24:38.826 c.y.proxydemo.common.TicketServiceImpl : 您选择的普洱思茅机场到昆明长水机场的机票价格为260
2021-01-19 23:24:38.839 com.yangzinan.proxydemo.jdk.ProxyObject : 查询成功!
复制代码
3.2 cglib代理
原来代理的对象不能被final修饰,根本上和上面的区别在于不需要有接口,直接使用实现类
为什么不能被final修饰?
因为cglib主要是通过继承来实现,因为被final修饰以后的类或者属性,不能被修改和不能被继承
cglib核心是MethodInterceptor接口,只需要实现该接口即可
3.2.1 cglib实现
依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
复制代码
被代理对象
public class CglibTicketService {
private Logger log = LoggerFactory.getLogger(CglibTicketService.class);
/**
* 购票逻辑
* @param from
* @param to
* @return
*/
public int buyTicket(String from, String to) {
int money = 260;
log.info("您选择的{}到{}的机票价格为{}",from,to,money);
return money;
}
}
复制代码
创建代理类
public class CglibProxy implements MethodInterceptor {
//目标对象
private Object object;
//通过构造方法,注入目标对象
public CglibProxy(Object object){
this.object = object;
}
/**
* 创建代理对象
* @return
*/
public Object getCglibProxyInstance(){
//创建增强器
Enhancer enhancer = new Enhancer();
//创建目标方法的父类
enhancer.setSuperclass(this.object.getClass());
//创建回调函数
enhancer.setCallback(this);
return enhancer.create();
}
/**
* MethodInterceptor接口方法,用于调用目标类的目标方法
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//代码增强
System.out.println("cglib正在查询...");
//调用目标方法
method.invoke(this.object,objects);
//代码增强
System.out.println("cglib查询完成");
return null;
}
}
复制代码
启动类
@Test
public void cgLibProxy(){
CglibTicketService cglibTicketService = (CglibTicketService) new CglibProxy(new CglibTicketService()).getCglibProxyInstance();
cglibTicketService.buyTicket("普洱思茅机场","昆明长水机场");
}
复制代码
cglib正在查询...
c.y.proxydemo.common.CglibTicketService : 您选择的普洱思茅机场到昆明长水机场的机票价格为260
cglib查询完成
复制代码
以上就是今天重新对动态代理的理解,他可以在实际业务中解决一些问题,比如:
- 小明开发了一套系统,小花需要调用小明其中的一个方法
- 但是小花发现,这个方法的返回值并不能满足实际需要,需要在名字前面增加前缀,wechat_yangzinan
- 实际开发当中,小花不可能去修改小明的代码,因为小明将代码已经打成jar包
- 所以小花需要使用代理的方式,给名称增加前缀
看完三件事❤️
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注头条号 『 JAVA后端架构 』,不定期分享原创知识。
- 同时可以期待后续文章ing
- 关注作者后台私信【888】有惊喜相送
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17677.html