首页 > 编程语言 >Java动态代理

Java动态代理

时间:2023-03-11 10:22:23浏览次数:42  
标签:DynamicProxy java Object 代理 helloWorld Java 动态

前言

这周工作比较忙,在学习上面就温习了一下动态代理,以前也看过这块的知识,但是一直没有动手去写下代码。学习就是这样,不动手写一写总有种没有掌握的感觉,记录下这个学习过程
动态代理有什么用呢? 它的作用就是增强,对于一个方法,我想在执行它之前执行另外一个log方法,那我可以修改代码,我可以直接去加,但是假如有很多方法都需要加这个log,那工作量就大了,我们可以用动态代理来加上这个log方法,然后通过代理来调用原来的方法,达到不修改原来方法,增加功能的目的。
动态代理实现上有两种方法JDK动态代理和CGLib动态代理,下面我们分别来演示一下

JDK动态代理

JDK动态代理是JDK自带的,通过反射来实现,需要基于接口来做,
首先我们定义一个需要代理的接口和它的实现类

public interface IHelloWorld {
    String say();
}

public class HelloWorld implements  IHelloWorld{
    @Override
    public String say() {
        System.out.println("this is helloworld");
        return "Hello World";
    }
}

我们需要在调用say前面和后面都加些日志,直接上动态代理的代码

public class DynamicProxy implements InvocationHandler {

    private Object target;
    public DynamicProxy(Object target){
        this.target = target;
    }

    private void before(){
        System.out.println("this is before");
    }

    private void after(){
        System.out.println("this is after");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target, args);
        after();
        return result;
    }
}

在main方法中加入如下代码来调用这个动态代理

        IHelloWorld helloWorld = new HelloWorld();
        DynamicProxy dynamicProxy1 = new DynamicProxy(helloWorld);

        IHelloWorld helloWorldProxy = (IHelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(),
                helloWorld.getClass().getInterfaces(), dynamicProxy1);

        String hello = helloWorldProxy.say();

输出如下

this is before
this is helloworld
this is after

我们可以看到调用helloWorldProxy.say()方法后,
先调用了before(),然后调用helloWorld的say(), 然后再调用了after().
也就是DynamicProxy里面的invoke方法里面的顺序来执行。
这样写代码有点复杂了
IHelloWorld helloWorld1 = (IHelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(),
helloWorld.getClass().getInterfaces(), dynamicProxy1);
在黄勇的《架构探险-从零开始写Java Web框架》中又包装了一下,在DynamicProxy中定义了这个方法

@SuppressWarnings("unchecked")
    public <T> T getProxy()
    {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

调用起来简单明了很多

    IHelloWorld helloWorld = new HelloWorld();
    DynamicProxy dynamicProxy1 = new DynamicProxy(helloWorld);
    IHelloWorld helloWorldProxy = dynamicProxy1.getProxy();
    helloWorldProxy.say();

如果我们又有个方法需要加before()和after(),那么就可以直接用

        IUserService userService = new UserService();
        DynamicProxy dynamicProxy = new DynamicProxy(userService);
        IUserService userServiceProxy = dynamicProxy.getProxy();
        userServiceProxy.send();

这就是JDK动态代理,缺点就是必须基于接口,
也就是必须这样写
IUserService userServiceProxy = dynamicProxy.getProxy();
直接写
UserService userServiceProxy = dynamicProxy.getProxy();
会报如下的转化错误
“java.lang.ClassCastException: class jdk.proxy1.$Proxy0 cannot be cast to class ken.proxy.demo.UserService”

CGLib动态代理

CGLib动态代理需要引入一个包

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

代码比较简单,直接贴代码出来

public class CGLibProxy implements MethodInterceptor {
    private void before(){
        System.out.println("this is before");
    }

    private void after(){
        System.out.println("this is after");
    }

    public <T> T getProxy(Class<T> cls) {
        return (T) Enhancer.create(cls,this);
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        before();
        Object result =  methodProxy.invokeSuper(obj, args);
        after();
        return result;
    }
}

调用代码

CGLibProxy cgLibProxy = new CGLibProxy();
HelloWorld helloWorld = cgLibProxy.getProxy(HelloWorld.class);
helloWorld.say();

输出和JDK动态代理一样。 它不要求HelloWorld一定去实现某个接口,相对比JDK动态代理要好,据说许多框架都用到了它。
这里遇到个坑, 我用Java19 运行这段代码会报如下错误

Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @182decdb

网上查找是Java兼容性错误,在VM Option里面加上“--add-opens java.base/java.lang=ALL-UNNAMED” 可以解决。 如果直接用Java8 也不会出现这个错误。

总结

动态代理这块的内容比较基础,关键是要尝试在实际的项目中用到它,理解它的思想,然后实际应用上去就比较好了。 这周内容比较简单,坚持学习,加油!

标签:DynamicProxy,java,Object,代理,helloWorld,Java,动态
From: https://www.cnblogs.com/dk168/p/17205371.html

相关文章

  • Java 流程控制简介及顺序结构
    流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块。程序设计中规定的三种流程结构,即:顺序结构程序从上到下逐行地执行,中......
  • java面向对象知识点
    thisthis(id,name);//调用另一个方法只能放在第一行thisid=id;thisname=name;publicStudent(intid,Stringname,chargender){this.id=id;......
  • java 反射
    反射1.获取类对象的三种方法publicstaticvoidmethod2()throwsClassNotFoundException{//获取类对象方法1Class<Monkey>c1=Monkey.class;......
  • P1433 吃奶酪 标签: 动态规划,dp | 状态压缩
    详见:https://www.luogu.com.cn/problem/P1433就不写基础原理了,直接看注释吧点击打开非map版#include<iostream>#include<cstdio>#include<cmath>#include<alg......
  • 1_认识JAVAWEB项目结构
    -project项目的根目录-静态资源文件/jsp-WEB-INF受保护的资源目录     -libjar包目录     -classesjava字节码目录     -web.xml项目的配置......
  • 1_认识JAVAWEB项目结构
    -project项目的根目录-静态资源文件/jsp-WEB-INF受保护的资源目录     -libjar包目录     -classesjava字节码目录     -web.xml项目的配置......
  • 1_认识JAVAWEB项目结构
    -project项目的根目录-静态资源文件/jsp-WEB-INF受保护的资源目录     -libjar包目录     -classesjava字节码目录     -web.xml项目......
  • 1_认识JAVAWEB项目结构
    -project项目的根目录-静态资源文件/jsp-WEB-INF受保护的资源目录     -libjar包目录     -classesjava字节码目录     -web.xml项目......
  • 代码随想录算法Day38 | 动态规划理论基础 ,509. 斐波那契数 ,70. 爬楼梯 ,746. 使用最
    动态规划理论基础动态规划五步曲:确定dp数组(dptable)以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组509.斐波那契数题目链接:509.斐......
  • 2023-03-10 Java中使用ArrayDeque实现栈和队列
    栈和队列的实现实际上完全可以用JDK自带的类ArrayDeque来实现作为队列使用publicabstractbooleanadd(EparamE);//加入元素到队尾publicabstractbooleanoffe......