java的动态代理无法获取实现类上的注解(问题解决记录)

2023-10-27

java的动态代理无法获取实现类上的注解(问题解决记录)

问题描述

使用
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 方法,
创建代理对象时,无法获取实现类上的注解,只能获取接口上的注解

原因

使用 newProxyInstance 方法时,该对象是一个实现了 指定接口 的匿名类(我的理解)。

因此,无法去获取到指定要被代理的类的相关注解,即注解只有声明在接口的相关方法上才能被发现并起效果的原因。

这也是为什么返回类型无法强转为代理时声明的实现类的类型的原因,因为这两压根不是继承关系,但是可以使用接口类型来接收返回值。

解决方法(这个看了应该就明白了)

可以通过传入被代理的实现类的 Class,来解决。我们直接通过该 Class 对象,反射获取它的方法和类以及变量,这样就可以直接获取到相应的注解,然后,修改对应的方法即可。

这个问题我是这样想的,首先你肯定是能知道代理对象的类的,不然你如何写代理,所以这个解决方法应该不存在不行的问题。

示例代码

注解类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Aspect {
    Class[] types();
}

接口类

public interface IOrder {

    void pay() throws InterruptedException;

    void show();
}

要被代理的实现类

@Aspect(types = TimeUsageIAspect.class)
public class Order implements IOrder {
    private int status = 0;

    @Override
    @Aspect(types = {TimeUsageIAspect.class})
    public void pay() throws InterruptedException {
        // 模拟支付耗时
        Thread.sleep(50);
        this.status = 1;
    }

    @Override
    public void show() {
        System.out.println("status: " +  this.status);
    }

    public static void main(String[] args) throws InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
    	// 实现代理的方法,传入实现类的 Class
        IOrder order = ObjectFactory.newInstance2(Order.class);
        order.pay();
        order.show();
    }
}

ObjectFactory类

public class ObjectFactory {
    public static <T> T newInstance(Class<T> cls) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 存放所有的切面类
        LinkedList<IAspect> aspects = new LinkedList<>();
        if (cls.isAnnotationPresent(Aspect.class)) {
            Class[] types = cls.getAnnotation(Aspect.class).types();
            for (Class type : types) {
                IAspect aspect = (IAspect) type.getConstructor().newInstance();
                aspects.push(aspect);
            }
        }


        T target = cls.getConstructor().newInstance();
        return (T) Proxy.newProxyInstance(
                cls.getClassLoader(),
                cls.getInterfaces(),
                (proxy, method, args) -> {
                    for (IAspect aspect : aspects) {
                        aspect.before();
                    }
                    Object result = method.invoke(target, args);
                    for (IAspect aspect : aspects) {
                        aspect.after();
                    }
                    return result;
                }
        );
    }

    public static <T> T newInstance2(Class<T> cls) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    	// 记录方法与相关处理的映射之间的关系
        HashMap<String,LinkedList<IAspect> > methodAspectMap = new HashMap<>();
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Aspect.class)) {
                Class[] types = method.getAnnotation(Aspect.class).types();
                LinkedList<IAspect> aspects = new LinkedList<>();
                for (Class type : types) {
                    IAspect aspect = (IAspect) type.getConstructor().newInstance();
                    aspects.push(aspect);
                }
                methodAspectMap.put(method.getName(), aspects);
            }
        }

        T target = cls.getConstructor().newInstance();
        return (T) Proxy.newProxyInstance(
                cls.getClassLoader(),
                cls.getInterfaces(),
                (proxy, method, args) -> {
                	// 根据方法名,执行对应注解的该干的事
                    if (methodAspectMap.containsKey(method.getName())) {
                        LinkedList<IAspect> aspects = methodAspectMap.get(method.getName());
                        for (IAspect aspect : aspects) {
                            aspect.before();
                        }
                        Object result = method.invoke(target, args);
                        for (IAspect aspect : aspects) {
                            aspect.after();
                        }
                        return result;
                    }
                    return method.invoke(target, args);
                }
        );
    }
}
题外话

这个示例代码,大部分是我学其他教程时写的,然后边学边瞎改的就遇到了这个问题,百度又没百度到方法(这个不用想,肯定是姿势不对),灵机一动就想到了这个办法,记录一下。代码肯定有许多莫名其妙的地方,别去管就完事。比如这方法的注解判断,感觉我整的好烂。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java的动态代理无法获取实现类上的注解(问题解决记录) 的相关文章

随机推荐

  • 创建线程的方式打开记事本

    更好的阅读体验 huge color red 更好的阅读体验 更好的阅读体验 今天操作系统课老师讲到进程 提出了一个有趣的小实验 能否以系统调用的方式利用 Windows 创建进程的系统调用函数来打开一个软件 闲着蛋疼的我立马来了兴趣 姑且
  • unity开发VR,没有VR设备解决方式

    文章目录 前言 一 环境搭建 1 普通VR环境搭建 2 虚拟相机搭建 二 模拟相机的操作 总结 前言 开发实例环境为unity2018 4 11 VRTK3 3 0 steamVR1 2 23 当我们身边没有HTC VIVE设备时我们不能去
  • Android Studio中的mavenCentral、jcenter、google仓库

    一 Android Studio中依赖是从哪里得到 是从工程的build gradle里面定义的Maven仓库服务器去下载library的 总的来说 只有两个标准的Android library文件服务器 mavenCentral和jcen
  • AES加密和解密详解

    本文使用的是cyrpto js库 以AES CBC为例 先安装cyrpto js cyrpto js是js专门用来加密和解密用到的一个库 第一步 先确认一下电脑是否有node和npm 输入node version显示 v 版本号就可以下一步
  • RPMB分区介绍

    RPMB Replay Protected Memory Block重放保护内存块 Partition 是 eMMC 中的一个具有安全特性的分区 eMMC 在写入数据到 RPMB 时 会校验数据的合法性 只有指定的 Host 才能够写入 同
  • Java之解压Tar.gz和Gz文件到指定的目录下

    工作中的需求 需要读取指定路径下的压缩文件 然后解压到指定的目录下 引入maven依赖
  • msvcp140.dll丢失的4个解决方法,msvcp140.dll丢失的常见原因

    msvcp140 dll是Windows操作系统中的一个动态链接库文件 由Microsoft Visual C 程序库所提供 它包含了许多C 函数和类的定义 可以为应用程序提供一些基本服务 比如内存管理 文件输入 输出和网络连接等功能 我们
  • phpstorm表单递交post出错get正确的解决方法

    好吧 这是我第二次因为这个问题搞得凌晨才睡觉 这次一定要记录下来 phpstorm版本2016 1 1 问题详细描述 在html写好表单之后以post方式递交给php文件 返回结果在谷歌浏览器是 Automatically populati
  • Allegro如何做镂空丝印操作指导

    Allegro如何做镂空丝印操作指导 在PCB设计丝印的时候 会需要画镂空的丝印 Allegro升级到了172版本的时候 可以画镂空的丝印 如下图 具体操作如下 选择Shape Add Rect命令 Options选择需要画到的层面 比如S
  • nginx文档合集

    1 nginx documentation 2 14个Nginx的核心功能点 建议收藏 3 Nginx之负载均衡模块 ngx http upstream module 途径日暮不赏丶的博客 CSDN博客 4 tomcat redis ses
  • 华为OD机试 - 完美走位(Python)

    完美走位 题目描述 输入一个长度为4的倍数的字符串 字符串中仅包含WASD四个字母 将这个字符串中的连续子串用同等长度的仅包含WASD的字符串替换Q 如果替换后整个字符串中WASD四个字母出现的频数相同 那么我们称替换后的字符串是 完美走位
  • Android Studio 快捷键盘

    Alt 回车 导入包 自动修正 Crtl X 剪贴 删除本行 之前用Eclipse Ctrl D 就是删除 在AndroidStudio 中是复制本行到下一行 找了好久都没找到删除本行快捷键的 汗 Ctrl N 查找类 Ctrl Shift
  • CTO、技术总监、首席架构师的区别

    一 高级程序员 如果你是一个刚刚创业的公司 公司没有专职产品经理和项目经理 你就是公司的产品经理 你如果对你现在的开发员能力不满 那么你只需要的是一个高级程序员 你定义功能 你做计划推进和管理 他可以带1 2个副手把你规划的功能实现了 他是
  • PostgresSQL 用linux命令重启时出错:pg_ctl: server does not shut down

    出错原因 在建一个新的数据库 然后restore好久都没成功 就把服务器直接关掉重启了 然后通过linux去重启数据库就一直不成功 下面是出错信息和解决步骤 用service postgresql restart去重启数据库 总是报以下错误
  • 长/短 链接/轮询 和websocket

    短连接和长连接 短连接 http协议底层基于socket的tcp协议 每次通信都会新建一个TCP连接 即每次请求和响应过程都经历 三次握手 四次挥手 优点 方便管理 缺点 频繁的建立和销毁连接占用资源 长连接 客户端和服务端之间只有一条TC
  • 链表的有序构建和查找

    题目描述 单链表结点的存储结构包含两部分 数据 下一结点指针 默认为空 单链表包含头结点 存储实际数据的结点位置从1开始 现输入一批无序的整数队列 编写程序完成以下要求 1 构建单链表并且把数据按递增顺序插入到链表中 并且统计非空指针发生变
  • vuex有哪几种属性?

    一 vuex是什么 vuex 是 Vue 配套的 公共数据管理工具 它可以把一些共享的数据 保存到 vuex 中 方便整个程序中的任何组件直接获取或修改我们的公共数据 注意点 只有需要共享的才放到vuex上 不需要共享的数据依然放到组件自身
  • Acwing-4455. 出行计划

    暴力解法TLE了 过了70 的数据 include
  • 【计算机视觉】直接线性变换(DLT)求解P矩阵(附C++和MATLAB代码)

    引言 本科阶段学习计算机视觉的时候也写过相机检校的程序 里面求解相机变换矩阵的时候使用的就是DLT算法 但这一次的作业只是要求计算投影矩阵 即P矩阵 原理 讲解DLT算法的原理的帖子已经很多了 推荐下面这个链接 双目视觉算法研究 二 相机模
  • java的动态代理无法获取实现类上的注解(问题解决记录)

    java的动态代理无法获取实现类上的注解 问题解决记录 问题描述 使用 Proxy newProxyInstance ClassLoader loader Class