如何动态修改JavaBean中注解的参数值

我这里有一个需求需要修改Person类中的一个属性上的注解的值进行修改,例如:

public class Person {
 private int age;
 @ApiParam(access="lala")
 private String name;
 //get set 方法忽略
}

将@ApiParam(access=“lala”) 修改为@ApiParam(access=“fafa”),经过分析是可以实现的,需要用到动态代理进行操作。

具体源码如下所示:

@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiParam {
 String access() default "";
}

反射+动态代理代码如下:

public class TestClazz {
 public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
 Person person = new Person();
 Field value = person.getClass().getDeclaredField("name");
 value.setAccessible(true);
 //APIParam 是一个自定义的注解
 ApiParam apiParam = (ApiParam) value.getAnnotation(ApiParam.class);
 java.lang.reflect.InvocationHandler invocationHandler = Proxy.getInvocationHandler(apiParam);
 Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
 //通过反射获取memberValues 这个属性是Map类型 存放着所有的属性。
  memberValues.setAccessible(true);
  Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
  String val = (String) values.get("access");
 System.out.println("------改之前:"+val);
 values.put("access", "fafa");//修改属性
 System.out.println("-----------------");
 //Field value1 = person.getClass().getDeclaredField("name");
 value.setAccessible(true);
 ApiParam apiParam1 = (ApiParam) value.getAnnotation(ApiParam.class);
 System.out.println("------改之后:"+apiParam1.access());
 //动态代理的方式不会改变原先class文件的内容
 }
}

补充:Java自定义注解并实现注解的伪动态参数传递

自定义注解,实现记录接口的调用日志,此注解可以实现传递伪动态参数。

一、需要引入的jar包:

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 </dependency>
<!-- test -->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-test</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>
<!-- json -->
 <dependency>
 <groupId>commons-lang</groupId>
 <artifactId>commons-lang</artifactId>
 <version>2.4</version>
 </dependency>
 <dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-lang3</artifactId>
 </dependency>
 <dependency>
 <groupId>commons-beanutils</groupId>
 <artifactId>commons-beanutils</artifactId>
 <version>1.8.0</version>
 </dependency>
 <dependency>
 <groupId>commons-collections</groupId>
 <artifactId>commons-collections</artifactId>
 <version>3.2.1</version>
 </dependency>
 <dependency>
 <groupId>commons-logging</groupId>
 <artifactId>commons-logging</artifactId>
 <version>1.1.1</version>
 </dependency>
 <dependency>
 <groupId>net.sf.json-lib</groupId>
 <artifactId>json-lib</artifactId>
 <version>2.4</version>
 </dependency>
 </dependencies>

二、自定义注解:

package com.example.demo.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiOperationLog {
 String resourceId() default "";
 String operationType();
 String description() default "";
}

三、定义切面:

package com.example.demo.aspect;
import com.example.demo.annotation.ApiOperationLog;
import net.sf.json.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class ApiOperationAspect {
 @Pointcut("@annotation ( com.example.demo.annotation.ApiOperationLog)")
 public void apiLog() {
 }

 @AfterReturning(pointcut = "apiLog()")
 public void recordLog(JoinPoint joinPoint) {
  MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  // 获取方法上的指定注解
  ApiOperationLog annotation = signature.getMethod().getAnnotation(ApiOperationLog.class);
  // 获取注解中的参数
  String resourceId = getAnnotationValue(joinPoint, annotation.resourceId());
  String operationType = getAnnotationValue(joinPoint, annotation.operationType());
  String description = getAnnotationValue(joinPoint, annotation.description());
  System.out.println("resourceId:" + resourceId);
  System.out.println("operationType:" + operationType);
  System.out.println("description:" + description);
  // 将注解中测参数值保存到数据库,实现记录接口调用日志的功能(以下内容省略...)
 }

 /**
  * 获取注解中传递的动态参数的参数值
  *
  * @param joinPoint
  * @param name
  * @return
  */
 public String getAnnotationValue(JoinPoint joinPoint, String name) {
  String paramName = name;
  // 获取方法中所有的参数
  Map<String, Object> params = getParams(joinPoint);
  // 参数是否是动态的:#{paramName}
  if (paramName.matches("^#\\{\\D*\\}")) {
   // 获取参数名
   paramName = paramName.replace("#{", "").replace("}", "");
   // 是否是复杂的参数类型:对象.参数名
   if (paramName.contains(".")) {
    String[] split = paramName.split("\\.");
    // 获取方法中对象的内容
    Object object = getValue(params, split[0]);
    // 转换为JsonObject
    JSONObject jsonObject = JSONObject.fromObject(object);
    // 获取值
    Object o = jsonObject.get(split[1]);
    return String.valueOf(o);
   }
   // 简单的动态参数直接返回
   return String.valueOf(getValue(params, paramName));
  }
  // 非动态参数直接返回
  return name;
 }

 /**
  * 根据参数名返回对应的值
  *
  * @param map
  * @param paramName
  * @return
  */
 public Object getValue(Map<String, Object> map, String paramName) {
  for (Map.Entry<String, Object> entry : map.entrySet()) {
   if (entry.getKey().equals(paramName)) {
    return entry.getValue();
   }
  }
  return null;
 }

 /**
  * 获取方法的参数名和值
  *
  * @param joinPoint
  * @return
  */
 public Map<String, Object> getParams(JoinPoint joinPoint) {
  Map<String, Object> params = new HashMap<>(8);
  Object[] args = joinPoint.getArgs();
  MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  String[] names = signature.getParameterNames();
  for (int i = 0; i < args.length; i++) {
   params.put(names[i], args[i]);
  }
  return params;
 }
}

四:测试前的准备内容:

// 实体类
package com.example.demo.model;
public class User {

 private Long id;
 private String name;
 private int age;

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }

 @Override
 public String toString() {
  return "User{" +
    "id=" + id +
    ", name='" + name + '\'' +
    ", age=" + age +
    '}';
 }
} 

// controller层内容
package com.example.demo.controller;
import com.example.demo.annotation.ApiOperationLog;
import com.example.demo.model.User;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {

 @ApiOperationLog(resourceId = "#{user.id}",operationType = "SAVE",description = "测试注解传递复杂动态参数")
 public void saveUser(User user,String id){
  System.out.println("测试注解...");
 }

 @ApiOperationLog(resourceId = "#{id}",operationType = "UPDATE",description = "测试注解传递简单动态参数")
 public void updateUser(User user,String id){
  System.out.println("测试注解...");
 }

}

五、测试类:

package com.example.demo.aspect;
import com.example.demo.DemoApplication;
import com.example.demo.controller.LoginController;
import com.example.demo.model.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class ControllerTest {

 @Autowired
 private LoginController loginController;

 @Test
 public void test(){
  User user = new User();
  user.setId(1L);
  user.setName("test");
  user.setAge(20);
  loginController.saveUser(user,"123");
  loginController.updateUser(user,"666");
 }
}

测试结果:

测试注解...
resourceId:1
operationType:SAVE
description:测试注解传递复杂动态参数
测试注解...
resourceId:666
operationType:UPDATE
description:测试注解传递简单动态参数

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • java 注解默认值操作

    我就废话不多说了,大家还是直接看代码吧~ package com.zejian.annotationdemo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Created by wuzejian on 2017

  • java注解之运行时修改字段的注解值操作

    今天遇到需求:导入Excel时候列头会发生变化,客户是大爷要求你改代码, 导入Excel是用easypoi做的,识别表头是用注解@Excel(name = "xxx")通过这个name来匹配 那你表头要动,我这个注解是硬编码 所以就有动态设置这个表头 public class JavaVo{ @Excel(name = "xxx") private String userName; //省略getset方法 } ExcelImportUtil.importExcel

  • Java cglib为实体类(javabean)动态添加属性方式

    1.应用场景 之前对接三方平台遇到一个参数名称是变化的,然后我就想到了动态javabean怎么生成,其实是我想多了,用个map就轻易解决了,但还是记录下动态属性添加的实现吧. 2.引入依赖 <!--使用cglib 为javabean动态添加属性--> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId>

  • 如何动态修改JavaBean中注解的参数值

    我这里有一个需求需要修改Person类中的一个属性上的注解的值进行修改,例如: public class Person { private int age; @ApiParam(access="lala") private String name; //get set 方法忽略 } 将@ApiParam(access="lala") 修改为@ApiParam(access="fafa"),经过分析是可以实现的,需要用到动态代理进行操作. 具体源码

  • Java通过反射,如何动态修改注解的某个属性值

    Java反射动态修改注解的某个属性值 昨晚看到一条问题,大意是楼主希望可以动态得建立多个Spring 的定时任务. 这个题目我并不是很熟悉,不过根据题目描述和查阅相关Spring 创建定时任务的资料,发现这也许涉及到通过Java代码动态修改注解的属性值. 今天对此尝试了一番, 发现通过反射来动态修改注解的属性值是可以做到的: 众所周知,java/lang/reflect这个包下面都是Java的反射类和工具. Annotation注解,也是位于这个包里的.注解自从Java 5.0版本引入后,就成为

  • php中动态修改ini配置

    1,运行时改变配置 在前一篇中曾经谈到,ini_set函数可以在php执行的过程中,动态修改php的部分配置.注意,仅仅是部分,并非所有的配置都可以动态修改.关于ini配置的可修改性,参见:http://php.net/manual/zh/configuration.changes.modes.php 我们直接进入ini_set的实现,函数虽然有点长,但是逻辑很清晰: 复制代码 代码如下: PHP_FUNCTION(ini_set) {     char *varname, *new_value

  • 解决vue中无法动态修改jqgrid组件 url地址的问题

    在项目开发中使用封装的jqgrid组件时需要动态修改URL地址,直接修改URL地址jqgrid请求的url地址并不会改变,这时我们可以强行修改jqgrid的url地址,修改如下: $("#accountGrid").setGridParam( //G,P要大写 { url:UrlService.url('www/1') } ) .trigger("reloadGrid"); 以上这篇解决vue中无法动态修改jqgrid组件 url地址的问题就是小编分享给大家的全部内

  • 聊聊Vue 中 title 的动态修改问题

    由于之前的 Vue 项目打包成果物一直是嵌入集成平台中,所以一直没有关注过项目的 title.直到最近,突然有个需求,要求点击按钮在集成平台外新开一个页面,此时我才发现,原来我的项目的 title 一直是万年不变的 vue-project.理所应当的,这个问题被测试爸爸提了一个大大的缺陷. 犯了错的我赶紧解决这个问题,但是经过一段时间的摸索,我却发现,这一个小小的问题,却有着很多不同的解法. 首先,毫无疑问的是,我们应该使用 document.title 方法通过 DOM 操作来修改 title

  • 解决vue单页面应用中动态修改title问题

    详细信息查看:vue-weachat-title 解决问题: 1.Vuejs 单页应用在iOS系统下部分APP的webview中 标题不能通过 document.title = xxx 的方式修改 该插件只为解决该问题而生(兼容安卓) 2.在vue单页面中,通过浏览器分享到QQ.微信等应用中的链接,只有一个首页标题和默认icon图片 已测试:APP 微信 QQ 支付宝 淘宝 安装 npm install vue-wechat-title --save 用法 1.在main.js中引入 impor

  • 在vue中动态修改css其中一个属性值操作

    我就废话不多说了,大家还是直接看代码吧~ <template> <!--此div的高度取屏幕可视区域的高度--> <div class="hello" :style="{'height':getClientHeight}"> <h5>{{ msg }}</h5> </div> </template> <script> export default { data() { r

  • vue中动态修改animation效果无效问题详情

    目录 问题描述 问题原因 解决办法 1.将 keyframes 下的内容放到 scoped 的外边 2.去掉scoped 问题描述 鼠标移入移出,并没有出现闪动: <template> <div class="alarmIcon" ref="alarmIcon" @mouseenter="handleEnter" @mouseleave="handleLeave" ></div> </

  • Java Agent入门学习之动态修改代码

    前言 最近用了一下午总算把Java agent给跑通了,本篇文章记录一下具体的操作步骤,以免遗忘.下面话不多说,来一起看看详细的介绍: 通过java agent可以动态修改代码(替换.修改类的定义),进行AOP. 目标: 为所有添加@ToString注解的类实现默认的toString方法 需要两个程序,一个是用来测试的程序,一个agent用于修改代码. 1. 测试程序 被测试的程序包括: - ToString.Java - Foo.java - Main.java 具体代码如下: ToStrin

  • Spring实现动态修改时间参数并手动开启关停操作

    spring实现定时任务的方式有三种,分别是java自带的timer类.spring task和quartz三种. 本文只介绍spring自带的task和第三方quartz.spirng task可以将它比作一个轻量级的Quartz,使用起来非常简单,除spring相关的包外不需要额外的包,而且支持注解和配置文件两种.接着直接演示spring task注解实现方式. 首先,创建任务类,如下: @Component public class MyScheduler { int i =0; publ

随机推荐