写BUG的派大星

Patrick Star

  • 首页
  • 归档

  • 搜索
设计模式 Gis Kafka Druid 微信小程序 Java 开源项目源码 物体识别 机器学习 Mybatis 微服务 Feign OpenVPN CSS Streamsets CDH SpringCloud SpringBoot maven 分布式 Shell Tree Linux js WebSocket 多线程 集群 Hadoop 大数据 JDK ElasticSearch MySQL 数据库 Redis Http Nginx

动态代理模式

发表于 2021-03-13 | 分类于 Java | 0 | 阅读次数 572

概念

他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。

静态代理与动态代理的区别

静态代理在生成Jar包的时候class文件就确定下来了。

动态代理基于反射等技术,是在程序运行过程中动态生成的。

两种动态代理方式

  1. JDK内提供的动态代理
  2. cglib方式实现的动态代理

JDK动态代理实现方式与原理

JDK动态代理是基于反射去做的,在开发的时候要实现一个InvocationHandler接口,并重写其中的invoke()方法。

实现方式

使用的时候通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)拿到代理后的对象。

在这过程中如果已有代理类,可以直接获取到,如果没有,则会调用工具类生成一个动态代理类。

在动态代理类中,重写了目标类的方法,改为h.invoke()去调用(这里的h就是我们自己实现的那个类)。

JDK动态代理与Cglib动态代理的区别

  1. JDK动态代理基于反射实现,Cglib通过继承实现,所以如果一个类不是接口的实现类,无法通过JDK方式去代理;如果一个方法是被final修饰的,则无法通过Cglib去代理。
  2. 通过Cglib方式生成代理类的速度会比JDK快一点,但是调用速度要慢于通过JDK方式实现的代理类。
  3. 因为第二点,所以Cglib适用于单例模式的场景。

代码

代码示例中包含以下:

  • 接口类HelloService
  • 接口类的实现类HelloServiceImpl
  • 通过JDK去实现的动态代理JDKDynamicProxy
  • 通过Cglib实现的动态代理CglibProxy
  • 测试方法

接口类HelloService

package com.example.dynamicproxy.service;

import java.util.List;

/**
 * HelloService
 *
 * @author mawengang
 * @date 2021/02/02 15:00
 */
public interface HelloService {

    Integer testInteger(Integer integer);


    int testInt(Integer integer);

    String testString(String s);

    List<String> testList(List<String> list);

}

接口类的实现类HelloServiceImpl

package com.example.dynamicproxy.service.impl;

import com.example.dynamicproxy.service.HelloService;

import java.util.List;

/**
 * HelloServiceImpl
 *
 * @author mawengang
 * @date 2021/02/02 15:02
 */
public class HelloServiceImpl implements HelloService {
    @Override
    public Integer testInteger(Integer integer) {
        System.out.println("实现类 testInteger " + integer.toString());
        return integer;
    }

    @Override
    public final int testInt(Integer integer) {
        System.out.println("实现类 testInt " + integer.toString());
        return integer == null ? 0 : integer;
    }

    @Override
    public String testString(String s) {
        System.out.println("实现类 testString " + s);
        return s;
    }

    @Override
    public List<String> testList(List<String> list) {
        System.out.println("实现类 testList " + list.size());
        return list;
    }
}

JDK实现的动态代理JDKProxy

package com.example.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDKProxy
 *
 * @author mawengang
 * @date 2021/02/02 15:04
 */
public class JDKProxy implements InvocationHandler {

    private Object target; //添加被代理类引用

    public JDKProxy(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        ClassLoader loader = this.getClass().getClassLoader();
        //类可以实现多个接口,因此这里的接口是个数组
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //this即JDKProxy实例,其包含被代理类的引用,以及重写的方法,newProxyInstance方法将利用这些参数创建一个代理类的实例
        return Proxy.newProxyInstance(loader, interfaces, this);
    }

    /**
     * 通过下面这种方法,就不用强转了。
     * @param <T>
     * @return
     */
    public <T> T getProxy2() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK 代理 " + method.getName());
        return method.invoke(target, args);
    }
}

Cglib实现的动态代理CglibProxy

package com.example.dynamicproxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CglibProxy
 *
 * @author mawengang
 * @date 2021/02/02 15:05
 */
public class CglibProxy implements MethodInterceptor {

    private Class proxied;

    public CglibProxy(Class proxied) {
        this.proxied = proxied;
    }

    public Object getProxy(){
        //cglib 中增强器,用来创建动态代理
        Enhancer enhancer = new Enhancer();
        //设置要创建动态代理的类
        enhancer.setSuperclass(proxied);
        //设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截
        enhancer.setCallback(this);
        //创建代理类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {
        System.out.println("CglibProxy " + method.getName());
        return methodProxy.invokeSuper(object, args);
//        return methodProxy.invokeSuper(object, args);
    }
}

测试方法

package com.example.dynamicproxy;

import com.example.dynamicproxy.service.HelloService;
import com.example.dynamicproxy.service.impl.HelloServiceImpl;

import java.lang.reflect.Proxy;
import java.util.LinkedList;
import java.util.List;

/**
 * CglibProxyTest
 *
 * @author mawengang
 * @date 2021/02/02 15:07
 */
public class Test {
    public static void main(String[] args) {
        // 实现类
        System.out.println("Cgilib ===========");
        CglibProxy cglibProxy = new CglibProxy(HelloServiceImpl.class);
        HelloServiceImpl helloServiceCg = (HelloServiceImpl)cglibProxy.getProxy();
        helloServiceCg.testInt(1);
        helloServiceCg.testInteger(11);

        // 传入实现类, 强转接口类
        JDKProxy jdkProxy = new JDKProxy(new HelloServiceImpl());
        HelloService helloService1 = (HelloService) jdkProxy.getProxy();
        // 或者这样
        HelloService helloService2 = jdkProxy.getProxy2();

        System.out.println("JDKProxy1 ===========");
        helloService1.testInt(2);
        helloService1.testInteger(22);
        System.out.println("JDKProxy2 ===========");
        helloService2.testInt(3);
        helloService2.testInteger(33);
    }
}


测试结果

image-20210202164855312

  • 本文作者: Patrick
  • 本文链接: https://www.write1bug.cn/archives/动态代理模式
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 设计模式 # Gis # Kafka # Druid # 微信小程序 # Java # 开源项目源码 # 物体识别 # 机器学习 # Mybatis # 微服务 # Feign # OpenVPN # CSS # Streamsets # CDH # SpringCloud # SpringBoot # maven # 分布式 # Shell # Tree # Linux # js # WebSocket # 多线程 # 集群 # Hadoop # 大数据 # JDK # ElasticSearch # MySQL # 数据库 # Redis # Http # Nginx
ELK中定时清除过期索引的脚本
关于红黑树
  • 文章目录
  • 站点概览
Patrick

Patrick

不是在改BUG,就是在写BUG。

52 日志
9 分类
36 标签
RSS
E-mail
Creative Commons
© 2018 — 2023 Patrick
人生如逆旅|我亦是行人
鲁ICP备18043140号-1