基于SpringBoot實現(xiàn)自動裝配返回屬性的設(shè)計思路
一:需求背景
在業(yè)務(wù)開發(fā)中經(jīng)常會有這個一個場景,A(業(yè)務(wù)表)表中會記錄數(shù)據(jù)的創(chuàng)建人,通常我們會用userId字段記錄該數(shù)據(jù)的創(chuàng)建者,但數(shù)據(jù)的使用方會要求展示該數(shù)據(jù)的創(chuàng)建者姓名,故我們會關(guān)聯(lián)用戶表拿該用戶的姓名。還有一些枚舉值的含義也要展示給前端。導(dǎo)致原本一個單表的sql就要寫成多表的關(guān)聯(lián)sql,以及枚舉含義的轉(zhuǎn)換很是惡心。
例如:業(yè)務(wù)對象BusinessEntity.java
public class BusinessEntity {
/**
* 創(chuàng)建者id
*/
private Long createUserId;
* 創(chuàng)建者名稱 (需要關(guān)聯(lián)用戶表)
private String userName;
* 數(shù)據(jù)狀態(tài)(0:有效,1失效)
private String status;
* 數(shù)據(jù)狀態(tài)含義(需要解析0或1的含義給前端)
private String statusName;
* 數(shù)據(jù)集合
private List<BusinessEntity> list;
}二:設(shè)計思路
?就像@JsonFormat注解,可以指定返回日期格式。我們是不是可以也自定義一個注解,通過這個注解,我們可以自動的把需要聯(lián)表的數(shù)據(jù)userName自動填充,需要解析的數(shù)據(jù)數(shù)據(jù)statusName如何通過枚舉解析。
? 故定義枚舉@AutowiredAttribute如下
/**
* 自動裝配屬性
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
public @interface AutowiredAttribute {
/**
* 當為默認值時,表明該屬性為javaBean,且該javaBean需要自動注入屬性
* 否則為指向的某一個屬性
*
* @return
*/
String param() default "";
* 默認為BaseEnum.class,
* 當為默認時注入數(shù)據(jù)的來源時redis緩存,
* 否則為枚舉類型
Class<? extends BaseEnum> enumClass() default BaseEnum.class;
* 數(shù)據(jù)源
DataSourceEnum dataSource() default DataSourceEnum.EMPTY;
}定義公共枚舉繼承繼承接口BaseEnum
public interface BaseEnum {
String getCode();
String getMsg();
}定義數(shù)據(jù)源枚舉如下dataSource
public enum DataSourceEnum implements BaseEnum {
SYSTEM_DICT("sys:dict:", "系統(tǒng)字典值", "sys_dict_value", "name"),
USER_NAME("user:name:", "用戶的id與姓名的映射", "sys_user", "user_name"),
USER_ROLE("user:role:", "角色id于角色名稱映射", "sys_role", "name"),
DEPT_NAME("dept:name:", "部門的id與部門名稱的映射", "sys_dept", "name"),
EMPTY("00", "默認", "", "");
DataSourceEnum(String code, String msg, String tableName, String tableColumn) {
this.code = code;
this.msg = msg;
this.tableName = tableName;
this.tableColumn = tableColumn;
}
private String code;
private String msg;
/**
* 表明
*/
private String tableName;
* 表的列
private String tableColumn;
@Override
public String getCode() {
return code;
public String getMsg() {
return msg;
public String getTableName() {
return tableName;
public String getTableColumn() {
return tableColumn;
}三:使用方法
對比原對象:通過新增注解,就避免的關(guān)聯(lián)查詢和數(shù)據(jù)解析
public class BusinessEntity {
/**
* 創(chuàng)建者id
*/
private Long createUserId;
* 創(chuàng)建者名稱 (需要關(guān)聯(lián)用戶表)
@AutowiredAttribute(param = "createUserId", dataSource = DataSourceEnum.USER_NAME)
private String userName;
* 數(shù)據(jù)狀態(tài)(0:有效,1失效)
private String status;
* 數(shù)據(jù)狀態(tài)含義(需要解析0或1的含義給前端)
@AutowiredAttribute(param = "status", enumClass = StatusEnum.class)
private String statusName;
* 數(shù)據(jù)集合
@AutowiredAttribute
private List<BusinessEntity> list;
}四:注解解析器(核心代碼)
/**
* 填充相應(yīng)體
*/
@Component
@ControllerAdvice()
public class FillResponseBodyAdvice implements ResponseBodyAdvice {
@Autowired
RedissonClient redissonClient;
JdbcTemplate jdbcTemplate;
private static String GET_CODE_METHOD_NAME = "getCode";
private static String GET_MSG_METHOD_NAME = "getMsg";
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
if (ResponseResult.class.getName().equals(returnType.getMethod().getReturnType().getName())) {
return true;
}
return false;
}
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (((ResponseResult<?>) body).getCode() == 200) {//僅僅對相應(yīng)為200結(jié)果處理
Object data = ((ResponseResult<?>) body).getData();
Class<?> aClass = data.getClass();
if (data instanceof List) {
//集合對象設(shè)置屬性
setForListBeanArr((List) data);
} else {
//判斷是否為自定義java對象
if (aClass.getSuperclass() instanceof Object) {
setForJavaBeanArr(data, aClass);
}
}
return body;
/**
* 為集合對象設(shè)置屬性
*
* @param list
*/
void setForListBeanArr(List<Object> list) {
for (Object object : list) {
Class<?> aClass = object.getClass();
setForJavaBeanArr(object, aClass);
* 為自定義javaBean對象設(shè)置值
private void setForJavaBeanArr(Object data, Class<?> aClass) {
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
AutowiredAttribute annotation = field.getAnnotation(AutowiredAttribute.class);
if (annotation == null) {
continue;
//通過枚舉注入
String param = annotation.param();
try {
field.setAccessible(true);
if (param.equals("")) {//注解表明該對象時javaBean對象
//獲取該javaBean對象
Object data2 = field.get(data);
if (data2 == null) {
continue;
}
//屬性是list對象
if (data2 instanceof List) {
setForListBeanArr((List) data2);
} else if (data2.getClass().getSuperclass() instanceof Object) {
setForJavaBeanArr(data2, data2.getClass());
} else {
//反射獲取值
Field field1 = aClass.getDeclaredField(param);
field1.setAccessible(true);
Object o = field1.get(data);
if (annotation.enumClass().getName().equals(BaseEnum.class.getName())) {
//通過redis注入
injectByEnum(o, field, data);
} else {
//通過枚舉注入
injectByRedis(o, field, data);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
private void injectByEnum(Object param, Field field, Object data) throws IllegalAccessException {
AutowiredAttribute annotationAutowiredAttribute = field.getAnnotation(AutowiredAttribute.class);
DataSourceEnum dataSourceEnum = annotationAutowiredAttribute.dataSource();
if (dataSourceEnum.equals(DataSourceEnum.EMPTY)) {
//不規(guī)范的
} else if (dataSourceEnum.equals(DataSourceEnum.SYSTEM_DICT)) {
Object o = redissonClient.getMap(DataSourceEnum.SYSTEM_DICT.getCode()).get(param);
if (o == null) {
o = getDictAndSetRedis(DataSourceEnum.SYSTEM_DICT, param);
field.set(data, o);
private void injectByRedis(Object param, Field field, Object data) throws IllegalAccessException {
AutowiredAttribute annotation = field.getAnnotation(AutowiredAttribute.class);
Class<? extends BaseEnum> aClass = annotation.enumClass();
try {
// 獲取所有常量
Object[] objects = aClass.getEnumConstants();
//獲取指定方法
Method getMsg = aClass.getMethod(GET_MSG_METHOD_NAME);
Method getCode = aClass.getMethod(GET_CODE_METHOD_NAME);
for (Object obj : objects) {
if (getCode.invoke(obj).equals(param.toString())) {
field.set(data, getMsg.invoke(obj));
System.out.println(getMsg.invoke(obj));
} catch (Exception e) {
System.out.println(e.getMessage());
//
Object getDictAndSetRedis(DataSourceEnum dataSourceEnum, Object value) {
String sql = "select name from " + dataSourceEnum.getTableName() + " where id = " + value;
String s = jdbcTemplate.queryForObject(sql, String.class);
RMap<Object, Object> map = redissonClient.getMap(dataSourceEnum.getCode());
map.put(value, s);
return s;
}實現(xiàn)了從數(shù)據(jù)庫(mysql)自動查詢,并把結(jié)果緩沖到數(shù)據(jù)庫。
五:需要思考的技術(shù)點
1.為什么注解要用到枚舉和泛型class
2.注解解析器,為什么用ResponseBodyAdvice這里解析?不在Filter,Interceptors?
3.對于對象里面嵌套對象,或?qū)ο罄锩媲短准?,怎么解決注入?遞歸
到此這篇關(guān)于基于SpringBoot實現(xiàn)自動裝配返回屬性的文章就介紹到這了,更多相關(guān)SpringBoot自動裝配返回屬性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot使用hibernate validator校驗方式
hibernate validator提供了一套比較完善、便捷的驗證實現(xiàn)方式。下面小編給大家介紹下springboot使用hibernate validator校驗方式,感興趣的朋友一起看看吧2018-01-01

