<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      背景

      項目中為了統一管理項目的配置,比如接口地址,操作類別等信息,需要一個統一的配置管理中心,類似nacos。
      我根據項目的需求寫了一套分布式配置中心,測試無誤后,改為單體應用并耦合到項目中。項目中使用配置文件多是取配置文件(applicatoion.yml)的值,使用@Value獲取,為了秉持非侵入性的原則,我決定寫一套自定義注解,以實現最少的代碼量實現業務需求。

      思路

      需要實現類似springboot @Value注解獲取配置文件對應key的值的功能。但區別在于 我是從自己寫的自動配置中獲取,原理就是數據庫中查詢所有的配置信息,并放入一個對象applicationConfigContext,同時創建一個bean交給spring托管,同時寫了個aop,為被注解的屬性賦入applicationConfigContext的對應的值。
      換句話說,自定義的這個注解為類賦值的時間線大概是

       spring bean初始化 —->  第三方插件初始化 --> 我寫的自動配置初始化   ---- 用戶調用某個方法,觸發aop機制,我通過反射動態改變了觸發aop的對象的bean的屬性,將值賦值給他。
      

      難點

      本項目的難點在于如何修改對象的值。看似簡單,其實里面的文章很多。

      自動配置代碼

      配置映射數據庫pojo

      import lombok.AllArgsConstructor;
      import lombok.Builder;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.util.Date;
      
      /**
       * @Describtion config bean
       * @Author yonyong
       * @Date 2020/7/13 15:43
       * @Version 1.0.0
       **/
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Builder(toBuilder = true)
      public class TblConfig {
          private Integer id;
      
          /**
           * 配置名稱
           */
          private String keyName;
      
          /**
           * 默認配置值
           */
          private String keyValue;
      
          /**
           * 分類
           */
          private String keyGroup;
      
          /**
           * 備注
           */
          private String description;
      
          /**
           * 創建時間
           */
          private Date insertTime;
      
          /**
           * 更新時間
           */
          private Date updateTime;
      
          /**
           * 創建人
           */
          private String creator;
      
          private Integer start;
      
          private Integer rows;
      
          /**
           * 是否是系統自帶
           */
          private String type;
      
          /**
           * 修改人
           */
          private String modifier;
      }
      

      創建用于防止配置信息的對象容器

      import lombok.AllArgsConstructor;
      import lombok.Builder;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.util.List;
      import java.util.stream.Collectors;
      
      /**
       * @Describtion config container
       * @Author yonyong
       * @Date 2020/7/13 15:40
       * @Version 1.0.0
       **/
      @Data
      @Builder(toBuilder = true)
      @AllArgsConstructor
      @NoArgsConstructor
      public class ConfigContext {
      
          /**
           * config key-val map
           */
          private List<TblConfig> vals;
      
          /**
           * env type
           */
          private String group;
      
          /**
           * get config
           * @param key
           * @return
           */
          public String getValue(String key){
              final List<TblConfig> collect = vals.stream()
                      .filter(tblConfig -> tblConfig.getKeyName().equals(key))
                      .collect(Collectors.toList());
              if (null == collect || collect.size() == 0)
                  return null;
              return collect.get(0).getKeyValue();
          }
      }
      

      創建配置,查詢出數據庫里配置并創建一個容器bean

      import org.apache.commons.lang3.StringUtils;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.beans.factory.config.ConfigurableBeanFactory;
      import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
      import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Scope;
      
      import javax.annotation.Resource;
      import java.util.List;
      
      /**
       * @Describtion manual auto inject bean
       * @Author yonyong
       * @Date 2020/7/13 15:55
       * @Version 1.0.0
       **/
      @Configuration
      @ConditionalOnClass(ConfigContext.class)
      public class ConfigContextAutoConfig {
      
          @Value("${config.center.group:DEFAULT_ENV}")
          private String group;
      
          @Resource
          private TblConfigcenterMapper tblConfigcenterMapper;
      
          @Bean(name = "applicationConfigContext")
          @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
          @ConditionalOnMissingBean(ConfigContext.class)
          public ConfigContext myConfigContext() {
              ConfigContext configContext = ConfigContext.builder().build();
              //set group
              if (StringUtils.isNotBlank(group))
                  group = "DEFAULT_ENV";
              //set vals
              TblConfig tblConfig = TblConfig.builder().keyGroup(group).build();
              final List<TblConfig> tblConfigs = tblConfigcenterMapper.selectByExample(tblConfig);
              configContext = configContext.toBuilder()
                      .vals(tblConfigs)
                      .group(group)
                      .build();
              return configContext;
          }
      }
      

      AOP相關代碼

      創建自定義注解

      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      
      /**
       * @Author yonyong
       * @Description //配置
       * @Date 2020/7/17 11:20
       * @Param 
       * @return 
       **/
      @Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface MyConfig {
          /**
           * 如果此value為空,修改值為獲取當前group,不為空正常獲取配置文件中指定key的val
           * @return
           */
          String value() default "";
          Class<?> clazz() default MyConfig.class;
      }
      

      創建aop業務功能

      import lombok.extern.slf4j.Slf4j;
      import org.apache.commons.lang3.StringUtils;
      import org.aspectj.lang.JoinPoint;
      import org.aspectj.lang.annotation.Aspect;
      import org.aspectj.lang.annotation.Before;
      import org.aspectj.lang.annotation.Pointcut;
      import org.aspectj.lang.reflect.MethodSignature;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
      
      import java.lang.reflect.Field;
      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;
      import java.util.Date;
      
      /**
       * @Describtion config service aop
       * @Author yonyong
       * @Date 2020/7/17 11:21
       * @Version 1.0.0
       **/
      @Aspect
      @Component
      @Slf4j
      public class SystemConfigAop {
      
          @Autowired
          ConfigContext applicationConfigContext;
      
          @Autowired
          MySpringContext mySpringContext;
      
          @Pointcut("@annotation(com.ai.api.config.configcenter.aop.MyConfig)")
          public void pointcut(){}
      
          @Before("pointcut()")
          public void before(JoinPoint joinPoint){
              final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
              Method method = signature.getMethod();
              MyConfig myConfig = method.getAnnotation(MyConfig.class);
              Class<?> clazz = myConfig.clazz();
              final Field[] declaredFields = clazz.getDeclaredFields();
              Object bean = mySpringContext.getBean(clazz);
              for (Field declaredField : declaredFields) {
                  final MyConfig annotation = declaredField.getAnnotation(MyConfig.class);
                  if (null != annotation && StringUtils.isNotBlank(annotation.value())){
                      log.info(annotation.value());
                      String val = getVal(annotation.value());
                      try {
      //                    setFieldData(declaredField,clazz.newInstance(),val);
      //                    setFieldData(declaredField,bean,val);
                          buildMethod(clazz,bean,declaredField,val);
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }
      //        mySpringContext.refresh(bean.getClass());
          }
      
          private void setFieldData(Field field, Object bean, String data) throws Exception {
              // 注意這里要設置權限為true
              field.setAccessible(true);
              Class<?> type = field.getType();
              if (type.equals(String.class)) {
                  field.set(bean, data);
              } else if (type.equals(Integer.class)) {
                  field.set(bean, Integer.valueOf(data));
              } else if (type.equals(Long.class)) {
                  field.set(bean, Long.valueOf(data));
              } else if (type.equals(Double.class)) {
                  field.set(bean, Double.valueOf(data));
              } else if (type.equals(Short.class)) {
                  field.set(bean, Short.valueOf(data));
              } else if (type.equals(Byte.class)) {
                  field.set(bean, Byte.valueOf(data));
              } else if (type.equals(Boolean.class)) {
                  field.set(bean, Boolean.valueOf(data));
              } else if (type.equals(Date.class)) {
                  field.set(bean, new Date(Long.valueOf(data)));
              }
          }
      
          private String getVal(String key){
              if (StringUtils.isNotBlank(key)){
                  return applicationConfigContext.getValue(key);
              }else {
                  return applicationConfigContext.getGroup();
              }
          }
      
          private void buildMethod(Class<?> clz ,Object obj,Field field,String propertiedValue) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
              // 獲取屬性的名字
              String name = field.getName();
              // 將屬性的首字符大寫, 構造get,set方法
              name = name.substring(0, 1).toUpperCase() + name.substring(1);
              // 獲取屬性的類型
              String type = field.getGenericType().toString();
              // 如果type是類類型,則前面包含"class ",后面跟類名
              // String 類型
              if (type.equals("class java.lang.String")) {
                  Method m = clz.getMethod("set" + name, String.class);
                  // invoke方法傳遞實例對象,因為要對實例處理,而不是類
                  m.invoke(obj, propertiedValue);
              }
              // int Integer類型
              if (type.equals("class java.lang.Integer")) {
                  Method m = clz.getMethod("set" + name, Integer.class);
                  m.invoke(obj, Integer.parseInt(propertiedValue));
              }
              if (type.equals("int")) {
                  Method m = clz.getMethod("set" + name, int.class);
                  m.invoke(obj, (int) Integer.parseInt(propertiedValue));
              }
              // boolean Boolean類型
              if (type.equals("class java.lang.Boolean")) {
                  Method m = clz.getMethod("set" + name, Boolean.class);
                  if (propertiedValue.equalsIgnoreCase("true")) {
                      m.invoke(obj, true);
                  }
                  if (propertiedValue.equalsIgnoreCase("false")) {
                      m.invoke(obj, true);
                  }
              }
              if (type.equals("boolean")) {
                  Method m = clz.getMethod("set" + name, boolean.class);
                  if (propertiedValue.equalsIgnoreCase("true")) {
                      m.invoke(obj, true);
                  }
                  if (propertiedValue.equalsIgnoreCase("false")) {
                      m.invoke(obj, true);
                  }
              }
              // long Long 數據類型
              if (type.equals("class java.lang.Long")) {
                  Method m = clz.getMethod("set" + name, Long.class);
                  m.invoke(obj, Long.parseLong(propertiedValue));
              }
              if (type.equals("long")) {
                  Method m = clz.getMethod("set" + name, long.class);
                  m.invoke(obj, Long.parseLong(propertiedValue));
              }
              // 時間數據類型
              if (type.equals("class java.util.Date")) {
                  Method m = clz.getMethod("set" + name, java.util.Date.class);
                  m.invoke(obj, DataConverter.convert(propertiedValue));
              }
          }
      }
      

      使用方式demo類

      @RestController
      @RequestMapping("/version")
      @Api(tags = "版本")
      @ApiSort(value = 0)
      @Data
      public class VersionController {
          
          @MyConfig("opcl.url")
          public String url = "1";
          
          @GetMapping(value="/test", produces = "application/json;charset=utf-8")
          @MyConfig(clazz = VersionController.class)
          public Object test(){
              return url;
          }
      
      }
      

      這里如果想在VersionController 注入配置url,首先需要在配置url上添加注解MyConfig,value為配置在容器中的key;其次需要在使用url的方法test上添加注解MyConfig,并將當前class傳入,當調用此方法,便會觸發aop機制,更新url的值

      開發過程遇到的問題

      簡述

      在aop中我使用幾種方式進行修改對象的屬性。

      最終是是第三種證實修改成功。首先spring的bean都是采用動態代理的方式產生。而默認的都是采用單例模式。所以我們需要搞清楚:

      versioncontroller方法中拿取url這個屬性時,拿取者是誰?

      versioncontroller方法中拿取url這個屬性時,拿取者是誰,是VersionController還是spring進行cglib動態代理產生的bean(以下簡稱bean)?

      這里可以看到Versioncontroller的方法執行時,這里的this是Versioncontroller@9250,這其實代表著是對象本身而非代理對象。后面我們會看到,springbean其實是代理對象代理了被代理對象,執行了其(Versioncontroller)方法。

      我們的目的是修改什么?是修改VersionController還是這個bean?

      我們講到,springbean其實是代理對象代理了被代理對象,執行了其(Versioncontroller)方法。那么我們修改的理所應該是被代理對象的屬性值。

      當進行反射賦值的時候,我們修改的是VersionController這個類還是bean?

      首先上面已經明確,修改的應該是被代理對象的屬性值。
      我這里三種方法。第一種只修改一個新建對象的實例,很明顯與springbean理念相悖,不可能實現我們的需求,所以只談后兩種。

      先看第二種是通過工具類獲取bean,然后通過反射為對應的屬性賦值。
      這里寫一個testController便于驗證。

      package com.ai.api.controller;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      @RequestMapping("/test")
      public class TestController {
          @Autowired
          VersionController versionController;
      
          @GetMapping("/1")
          public Object getUrl(){
              System.out.println(versionController.getUrl());
              System.out.println(versionController.url);
              return versionController.getUrl();
          }
      }
      
      

      這里我們是直接為bean的屬性賦值。我們先調用VersionController中的test方法,讓其先走一遍Aop。因為springbean如果沒有配置,默認的都是單例模式,所以說如果修改成功,那么testController中,注入的VersionController,因為是同一個VersionController的實例,它的代理對象一定也被修改。我們調試后得出:

      我們可以看到,我們確實修改掉了bean的值,但被代理對象的url仍然是1。并沒有實現我們想要的效果。

      第三種,通過獲取這個bean,通過這個代理bean的set方法,間接修改被代理對象VersionController的屬性值。我們先調用VersionController中的test方法,讓其先走一遍Aop,因為springbean如果沒有配置,默認的都是單例模式。如果修改成功,那么testController中,注入的VersionController,因為是同一個VersionController的實例,它的代理對象一定也被修改了。
      我們調用TestController 方法可以看到:

      這里我們可以看到,被代理的對象已經被成功修改,大功告成!

      posted on 2020-07-19 14:18  yonyong  閱讀(1546)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 成人午夜伦理在线观看| 国产精品538一区二区在线| 办公室强奷漂亮少妇视频| 亚洲国产区男人本色| 亚洲成av人片在www鸭子| 亚洲一区二区三区18禁| 亚洲首页一区任你躁xxxxx| 国产精品九九久久精品女同| 狠狠综合久久久久综| 高清自拍亚洲精品二区| 老子午夜精品888无码不卡| 国产999久久高清免费观看| 亚洲精品一品区二品区三品区| 国产美女久久久亚洲综合| 国产精品色内内在线播放| 看亚洲黄色不在线网占| 午夜福利影院不卡影院| 亚洲成人一区二区av| 久久亚洲中文字幕伊人久久大 | 精品蜜臀国产av一区二区| 东京热人妻丝袜无码AV一二三区观| 免费人妻无码不卡中文18禁| 久久亚洲欧美日本精品| 国产成人亚洲一区二区三区| 韩国三级+mp4| 40岁成熟女人牲交片20分钟| 国产精品无码成人午夜电影 | 巨熟乳波霸若妻在线播放| 欧美色欧美亚洲高清在线视频| 亚洲精品尤物av在线网站| 亚洲高潮喷水无码AV电影| 吉林省| 国产成人精品区一区二区| 国产AV午夜精品一区二区三区| 精品人妻二区中文字幕| 国产精品视频免费一区二区| 人妻丝袜AV中文系列先锋影音| 综合久久国产九一剧情麻豆| 久久人妻夜夜做天天爽| 亚洲色欲色欲WWW在线丝| 亚洲天堂在线观看完整版|