如何動態(tài)修改JavaBean中注解的參數(shù)值
我這里有一個需求需要修改Person類中的一個屬性上的注解的值進(jìn)行修改,例如:
public class Person { private int age; @ApiParam(access="lala") private String name; //get set 方法忽略 }
將@ApiParam(access=“l(fā)ala”) 修改為@ApiParam(access=“fafa”),經(jīng)過分析是可以實(shí)現(xiàn)的,需要用到動態(tài)代理進(jìn)行操作。
具體源碼如下所示:
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ApiParam { String access() default ""; }
反射+動態(tài)代理代碼如下:
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()); //動態(tài)代理的方式不會改變原先class文件的內(nèi)容 } }
補(bǔ)充:Java自定義注解并實(shí)現(xiàn)注解的偽動態(tài)參數(shù)傳遞
自定義注解,實(shí)現(xiàn)記錄接口的調(diào)用日志,此注解可以實(shí)現(xiàn)傳遞偽動態(tài)參數(shù)。
一、需要引入的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); // 獲取注解中的參數(shù) 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); // 將注解中測參數(shù)值保存到數(shù)據(jù)庫,實(shí)現(xiàn)記錄接口調(diào)用日志的功能(以下內(nèi)容省略...) } /** * 獲取注解中傳遞的動態(tài)參數(shù)的參數(shù)值 * * @param joinPoint * @param name * @return */ public String getAnnotationValue(JoinPoint joinPoint, String name) { String paramName = name; // 獲取方法中所有的參數(shù) Map<String, Object> params = getParams(joinPoint); // 參數(shù)是否是動態(tài)的:#{paramName} if (paramName.matches("^#\\{\\D*\\}")) { // 獲取參數(shù)名 paramName = paramName.replace("#{", "").replace("}", ""); // 是否是復(fù)雜的參數(shù)類型:對象.參數(shù)名 if (paramName.contains(".")) { String[] split = paramName.split("\\."); // 獲取方法中對象的內(nèi)容 Object object = getValue(params, split[0]); // 轉(zhuǎn)換為JsonObject JSONObject jsonObject = JSONObject.fromObject(object); // 獲取值 Object o = jsonObject.get(split[1]); return String.valueOf(o); } // 簡單的動態(tài)參數(shù)直接返回 return String.valueOf(getValue(params, paramName)); } // 非動態(tài)參數(shù)直接返回 return name; } /** * 根據(jù)參數(shù)名返回對應(yīng)的值 * * @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; } /** * 獲取方法的參數(shù)名和值 * * @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; } }
四:測試前的準(zhǔn)備內(nèi)容:
// 實(shí)體類 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層內(nèi)容 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 = "測試注解傳遞復(fù)雜動態(tài)參數(shù)") public void saveUser(User user,String id){ System.out.println("測試注解..."); } @ApiOperationLog(resourceId = "#{id}",operationType = "UPDATE",description = "測試注解傳遞簡單動態(tài)參數(shù)") 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"); } }
測試結(jié)果:
測試注解... resourceId:1 operationType:SAVE description:測試注解傳遞復(fù)雜動態(tài)參數(shù) 測試注解... resourceId:666 operationType:UPDATE description:測試注解傳遞簡單動態(tài)參數(shù)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Java?Ribbon與openfeign區(qū)別和用法講解
Ribbon是基于Netflix?Ribbon實(shí)現(xiàn)的一套客戶端負(fù)載均衡的工具,主要功能是提供客戶端的軟件負(fù)載均衡算法和服務(wù)調(diào)用。openfeign對Feign進(jìn)行了增強(qiáng),使其支持Spring MVC注解,另外還整合了Ribbon和Nacos,從而使得Feign的使用更加方便2022-08-08springboot+spring?data?jpa實(shí)現(xiàn)新增及批量新增方式
這篇文章主要介紹了springboot+spring?data?jpa實(shí)現(xiàn)新增及批量新增方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11Java實(shí)現(xiàn)讀取SFTP服務(wù)器指定目錄文件的方法
SFTP是一種在安全通道上傳輸文件的協(xié)議,它是基于SSH(Secure Shell)協(xié)議的擴(kuò)展,用于在客戶端和服務(wù)器之間進(jìn)行加密的文件傳輸,這篇文章主要介紹了Java實(shí)現(xiàn)讀取SFTP服務(wù)器指定目錄文件,感興趣的朋友跟隨小編一起看看吧2023-08-08Java socket通信模擬QQ實(shí)現(xiàn)多人聊天室
Socket在Java實(shí)戰(zhàn)網(wǎng)絡(luò)通信編程應(yīng)用中有非常重要的作用,你想要跟別人聯(lián)系都得通過socket占據(jù)端口來實(shí)現(xiàn),掌握Socket技術(shù)不僅在聊天應(yīng)用程序中需要用到(比如QQ什么的都都是用socket來寫的),而且對于學(xué)習(xí) Asp.net 也非常有幫助2022-07-07淺談Android開發(fā)中項(xiàng)目的文件結(jié)構(gòu)及規(guī)范化部署建議
這篇文章主要介紹了Android開發(fā)中項(xiàng)目的文件結(jié)構(gòu)及規(guī)范化部署建議,組織好代碼文件的結(jié)構(gòu)有利于維護(hù)團(tuán)隊(duì)合作的效率,需要的朋友可以參考下2016-03-03Spring在多線程環(huán)境下如何確保事務(wù)一致性問題詳解
這篇文章主要介紹了Spring在多線程環(huán)境下如何確保事務(wù)一致性問題詳解,說到異步執(zhí)行,很多小伙伴首先想到Spring中提供的@Async注解,但是Spring提供的異步執(zhí)行任務(wù)能力并不足以解決我們當(dāng)前的需求,需要的朋友可以參考下2023-11-11logback輸出日志屏蔽quartz的debug等級日志方式
這篇文章主要介紹了logback輸出日志屏蔽quartz的debug等級日志方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08