今天,我重新学习了一遍动态代理[亲测有效]

今天,我重新学习了一遍动态代理[亲测有效]一、前言今天,认真的看了一遍昨天写的静态代理、jdk动态代理、cglib动态代理,但是其中动态代理只是简单的实现,并没有更深层次的去思考,去理解

欢迎大家来到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包
  • 所以小花需要使用代理的方式,给名称增加前缀

看完三件事❤️

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  1. 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  2. 关注头条号 『 JAVA后端架构 』,不定期分享原创知识。
  3. 同时可以期待后续文章ing
  4. 关注作者后台私信【888】有惊喜相送
今天,我重新学习了一遍动态代理[亲测有效]

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17677.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信