posts - 88, comments - 3, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          2017年12月8日

          在Spring cloud config出來之前, 自己實現(xiàn)了基于ZK的配置中心, 杜絕了本地properties配置文件, 原理很簡單, 只是重載了PropertyPlaceholderConfigurer的mergeProperties():

          /**
          * 重載合并屬性實現(xiàn)
          * 先加載file properties, 然后并入ZK配置中心讀取的properties
          *
          * @return 合并后的屬性集合
          * @throws IOException 異常
          */
          @Override
          protected Properties mergeProperties() throws IOException {
          Properties result = new Properties();
          // 加載父類的配置
          Properties mergeProperties = super.mergeProperties();
          result.putAll(mergeProperties);
          // 加載從zk中讀取到的配置
          Map<String, String> configs = loadZkConfigs();
          result.putAll(configs);
          return result;
          }

          這個實現(xiàn)在spring項目里用起來還是挺順手的, 但是近期部分spring-boot項目里發(fā)現(xiàn)這種placeholder的實現(xiàn)跟spring boot的@ConfigurationProperties(prefix = "xxx") 不能很好的配合工作,
          也就是屬性沒有被resolve處理, 用@Value的方式確可以讀到, 但是@Value配置起來如果屬性多的話還是挺繁瑣的, 還是傾向用@ConfigurationProperties的prefix, 于是看了下spring boot的文檔發(fā)現(xiàn)PropertySource order:
             * Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
             * @TestPropertySource annotations on your tests.
             * @SpringBootTest#properties annotation attribute on your tests.
             * Command line arguments.
             * Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property)
             * ServletConfig init parameters.
             * ServletContext init parameters.
             * JNDI attributes from java:comp/env.
             * Java System properties (System.getProperties()).
             * OS environment variables.
             * A RandomValuePropertySource that only has properties in random.*.
             * Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
             * Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants)
             * Application properties outside of your packaged jar (application.properties and YAML variants).
             * Application properties packaged inside your jar (application.properties and YAML variants).
             * @PropertySource annotations on your @Configuration classes.
             * Default properties (specified using SpringApplication.setDefaultProperties).
          不難發(fā)現(xiàn)其會檢查Java system propeties里的屬性, 也就是說, 只要把mergerProperties讀到的屬性寫入Java system props里即可, 看了下源碼, 找到個切入點

          /**
          * 重載處理屬性實現(xiàn)
          * 根據(jù)選項, 決定是否將合并后的props寫入系統(tǒng)屬性, Spring boot需要
          *
          * @param beanFactoryToProcess
          * @param props 合并后的屬性
          * @throws BeansException
          */
          @Override
          protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
          // 原有邏輯
          super.processProperties(beanFactoryToProcess, props);
          // 寫入到系統(tǒng)屬性
          if (writePropsToSystem) {
          // write all properties to system for spring boot
          Enumeration<?> propertyNames = props.propertyNames();
          while (propertyNames.hasMoreElements()) {
          String propertyName = (String) propertyNames.nextElement();
          String propertyValue = props.getProperty(propertyName);
          System.setProperty(propertyName, propertyValue);
          }
          }
          }
          為避免影響過大, 設(shè)置了個開關(guān), 是否寫入系統(tǒng)屬性, 如果是spring boot的項目, 就開啟, 這樣對線上非spring boot項目做到影響最小, 然后spring boot的@ConfigurationProperties完美讀到屬性;

          具體代碼見: org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor

          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName)
          throws BeansException {
          ConfigurationProperties annotation = AnnotationUtils
          .findAnnotation(bean.getClass(), ConfigurationProperties.class);
          if (annotation != null) {
          postProcessBeforeInitialization(bean, beanName, annotation);
          }
          annotation = this.beans.findFactoryAnnotation(beanName,
          ConfigurationProperties.class);
          if (annotation != null) {
          postProcessBeforeInitialization(bean, beanName, annotation);
          }
          return bean;
          }

          posted @ 2017-12-08 14:13 Milo的海域 閱讀(903) | 評論 (0)編輯 收藏

          2017年4月15日

          Spring默認不允許對類的變量, 也就是靜態(tài)變量進行注入操作, 但是在某些場景比如單元測試的@AfterClass要訪問注入對象, 而Junit的這個方法必須是靜態(tài)的, 也就產(chǎn)生了悖論;

          解決思路有兩個:

          • 思路1: 想辦法對靜態(tài)變量注入, 也就是繞過Spring只能運行非靜態(tài)變量才能注入依賴的壁壘
          • 思路2: 想辦法@AfterClass改造為非靜態(tài)
            • 實現(xiàn)Junit RunListener, 覆蓋testRunFinished方法, 這里去實現(xiàn)類似@AfterClass的功能, 這個方法是非靜態(tài)的
            • 不要用Junit, 改用TestNG, TestNG里的AfterClass是非靜態(tài)的
            • 用Spring的TestExecutionListeners, 實現(xiàn)個Listener, 里面也有個類似非靜態(tài)的AfterClass的實現(xiàn), 覆蓋實現(xiàn)就行

          思路2的幾個方法都可以實現(xiàn), 但是單元測試Runner需要用

          @RunWith(Theories.class)

          而且改用TestNG工程浩大, 只能放棄掉這個思路

          繼續(xù)走思路1, 只能去繞過Spring的依賴注入的static壁壘了, 具體代碼如下:

          @Autowired
          private Destination dfsOperationQueue;
          private static Destination dfsOperationQueueStatic; // static version
          @Autowired
          private MessageQueueAPI messageQueueAPI;
          private static MessageQueueAPI messageQueueAPIStatic; // static version


          @PostConstruct
          public void init() {
          dfsOperationQueueStatic = this.dfsOperationQueue;
          messageQueueAPIStatic = this.messageQueueAPI;
          }

          @AfterClass
          public static void afterClass() {
          MessageVO messageVO = messageQueueAPIStatic.removeDestination(dfsOperationQueueStatic);
          System.out.println(messageVO);
          }

          其實就是用了@PostConstruct 來個偷梁換柱而已, 多聲明個靜態(tài)成員指向非靜態(tài)對象, 兩者其實是一個對象

          posted @ 2017-04-15 10:32 Milo的海域 閱讀(597) | 評論 (0)編輯 收藏

          2016年10月22日

          知道activemq現(xiàn)在已經(jīng)支持了rest api, 但是官方對這部分的介紹一筆帶過 (http://activemq.apache.org/rest.html),


          通過google居然也沒搜到一些有用的, 比如像刪除一個destination, 都是問的多,然后沒下文. 于是花了一些心思研究了一下:


          首先通過rest api獲取當前版本所有已支持的協(xié)議

              http://172.30.43.206:8161/api/jolokia/list


          然后根據(jù)json輸出關(guān)于removeTopic, removeQueue的mbean實現(xiàn)通過rest api刪除destination的方法, 注意到用GET請求而不是POST,不然會報錯 (官網(wǎng)的例子里用的wget給的靈感, 開始用了POST老報錯)


          import org.apache.activemq.command.ActiveMQQueue;
          import org.apache.activemq.command.ActiveMQTopic;
          import org.apache.http.auth.AuthScope;
          import org.apache.http.auth.UsernamePasswordCredentials;
          import org.apache.http.impl.client.BasicCredentialsProvider;
          import org.apache.http.impl.client.DefaultHttpClient;
          import org.springframework.http.HttpEntity;
          import org.springframework.http.HttpHeaders;
          import org.springframework.http.HttpMethod;
          import org.springframework.http.MediaType;
          import org.springframework.http.ResponseEntity;
          import org.springframework.http.client.ClientHttpRequestFactory;
          import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
          import org.springframework.web.client.RestTemplate;

          import javax.jms.Destination;
          import javax.jms.JMSException;
          import java.util.Arrays;


          public class MessageQueueAdmin {
              
          private static final RestTemplate restTemplate = getRestTemplate("admin""admin");

              
          private static String brokerHost = "172.30.43.206";
              
          private static String adminConsolePort = "8161";
              
          private static String protocol = "http";

              
          public static void removeDestination(Destination destination) throws JMSException {
                  String destName, destType;
                  
          if (destination instanceof ActiveMQQueue) {
                      destName 
          = ((ActiveMQQueue) destination).getQueueName();
                      destType 
          = "Queue";
                  } 
          else {
                      destName 
          = ((ActiveMQTopic) destination).getTopicName();
                      destType 
          = "Topic";
                  }

                  
          // build urls
                  String url = String.format("%s://%s:%s/api/jolokia/exec/org.apache.activemq:" +
                          
          "brokerName=localhost,type=Broker/remove%s/%s", protocol, brokerHost, adminConsolePort, destType, destName);
                  System.out.println(url);
                  
          // do operation
                  HttpHeaders headers = new HttpHeaders();
                  headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
                  HttpEntity
          <String> entity = new HttpEntity<String>("parameters", headers);
                  ResponseEntity response 
          = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
                  System.out.println(response.getBody());
              }

              
          public static void main(String[] args) throws JMSException {
                  ActiveMQTopic topic 
          = new ActiveMQTopic("test-activemq-topic");
                  removeDestination(topic);
              }


              
          private static RestTemplate getRestTemplate(String user, String password) {
                  DefaultHttpClient httpClient 
          = new DefaultHttpClient();
                  BasicCredentialsProvider credentialsProvider 
          = new BasicCredentialsProvider();
                  credentialsProvider.setCredentials(AuthScope.ANY, 
          new UsernamePasswordCredentials(user, password));
                  httpClient.setCredentialsProvider(credentialsProvider);
                  ClientHttpRequestFactory rf 
          = new HttpComponentsClientHttpRequestFactory(httpClient);

                  
          return new RestTemplate(rf);
              }
          }

          其他的請求,應(yīng)該都是類似jolokia的exec get request的格式:


          https://jolokia.org/reference/html/protocol.html#exec


          <base url>/exec/<mbean name>/<operation name>/<arg1>/<arg2>/.

          posted @ 2016-10-22 17:31 Milo的海域 閱讀(1441) | 評論 (0)編輯 收藏

          2016年10月12日

          用Spring JMS 的JmsTemplate從消息隊列消費消息時發(fā)現(xiàn),使用了CLIENT_ACKNOWLEDGE模式,消息返回后總是自動被ack,也就是被broker "Dequeued"

              protected Message doReceive(Session session, MessageConsumer consumer) throws JMSException {
                  
          try {
                      
          // Use transaction timeout (if available).
                      long timeout = getReceiveTimeout();
                      JmsResourceHolder resourceHolder 
          =
                              (JmsResourceHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
                      
          if (resourceHolder != null && resourceHolder.hasTimeout()) {
                          timeout 
          = Math.min(timeout, resourceHolder.getTimeToLiveInMillis());
                      }
                      Message message 
          = doReceive(consumer, timeout);
                      
          if (session.getTransacted()) {
                          
          // Commit necessary - but avoid commit call within a JTA transaction.
                          if (isSessionLocallyTransacted(session)) {
                              
          // Transacted session created by this template -> commit.
                              JmsUtils.commitIfNecessary(session);
                          }
                      }
                      
          else if (isClientAcknowledge(session)) {
                          
          // Manually acknowledge message, if any.
                          if (message != null) {
                              message.acknowledge();
                          }
                      }
                      
          return message;
                  }
                  
          finally {
                      JmsUtils.closeMessageConsumer(consumer);
                  }
              }

          但是使用異步listener 就不會出現(xiàn)這個情況,搜了下google,發(fā)現(xiàn)果然存在這個問題

               https://jira.spring.io/browse/SPR-12995
               https://jira.spring.io/browse/SPR-13255
               http://louisling.iteye.com/blog/241073

          同步方式拉取消息,暫時沒找到好的封裝,只能暫時用這。或者盡量用listener, 這個問題暫時標記下,或者誰有更好的解決方案可以comment我

          posted @ 2016-10-12 16:32 Milo的海域 閱讀(1541) | 評論 (0)編輯 收藏

          2016年6月15日

          默認的配置有時候點不亮顯示器,且分辨率很低,通過tvservice工具不斷調(diào)試,發(fā)現(xiàn)下面的參數(shù)可以完美匹配了
          修改 /boot/config.txt的下列參數(shù)

          disable_overscan=1
          hdmi_force_hotplug
          =1
          hdmi_group
          =1
          hdmi_mode
          =16
          hdmi_drive
          =2
          config_hdmi_boost
          =4
          dtparam
          =audio=on

          posted @ 2016-06-15 09:32 Milo的海域 閱讀(226) | 評論 (0)編輯 收藏

          2016年5月26日

          http://stackoverflow.com/questions/3294423/spring-classpath-prefix-difference



            

          SIMPLE DEFINITION

          The classpath*:conf/appContext.xml simply means that all appContext.xml files under conf folders in all your jars on the classpath will be picked up and joined into one big application context.

          In contrast
          , classpath:conf/appContext.xml will load only one such file the first one found on your classpath.


          <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
          <property name="locations">
          <list>
          <value>classpath:*.properties</value>
          <value>classpath*:*.properties</value>
          </list>
          </property>
          </bean>

          posted @ 2016-05-26 14:14 Milo的海域 閱讀(772) | 評論 (0)編輯 收藏

          2016年5月16日

          1. IDEA_JDK (or IDEA_JDK_64) environment variable
          2. jre/ (or jre64/) directory in IDEA home
          3. registry
          4. JDK_HOME environment variable
          5. JAVA_HOME environment variable

          posted @ 2016-05-16 08:49 Milo的海域 閱讀(170) | 評論 (0)編輯 收藏

          2016年4月21日

          java里如何修改console的歷史輸出信息呢?如果是當前行的修改可以簡單想到"\r"的方案,但是如果要修改上一行呢? google了下原來還是有方法的,需要用到ansi的control sequences
          ANSI code

          用java寫了個簡單的例子,例子就是把曾經(jīng)的output修改為其他字符串并恢復之后的打印,代碼里加了sleep,主要方便理解各種控制序列的含義
                  //print some test messages
                  System.out.println("1");
                  Thread.sleep(
          1000);
                  System.out.println(
          "22");
                  Thread.sleep(
          1000);
                  System.out.println(
          "333");
                  Thread.sleep(
          1000);
                  System.out.println(
          "4444");
                  Thread.sleep(
          1000);

                  
          /**
                   * modify "333" to "-"
                   
          */
                  
          // Move up two lines
                  int count = 2;
                  System.out.print(String.format(
          "\033[%dA", count));
                  Thread.sleep(
          1000);
                  
          // Erase current line content
                  System.out.print("\033[2K");
                  Thread.sleep(
          1000);
                  
          // update with new content
                  System.out.print("-");
                  Thread.sleep(
          1000);
                  
          // Move down two lines
                  System.out.print(String.format("\033[%dB", count));
                  Thread.sleep(
          1000);
                  
          // Move cursor to left beginning
                  System.out.print(String.format("\033[D", count));
                  
          // continue print others
                  Thread.sleep(1000);
                  System.out.println(
          "55555");
                  Thread.sleep(
          1000);

          posted @ 2016-04-21 17:06 Milo的海域 閱讀(419) | 評論 (0)編輯 收藏

          2016年3月31日

          1. zookeeper basic/fast paxsos 的形象表述 https://www.douban.com/note/208430424/
          2. 詳細介紹 http://blog.csdn.net/xhh198781/article/details/10949697

          posted @ 2016-03-31 14:06 Milo的海域 閱讀(190) | 評論 (0)編輯 收藏

          2016年3月29日

          server.compression.enabled=true 
          server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
          server.compression.min-response-size=4096
          第一個參數(shù)打開壓縮開關(guān),第二個參數(shù)添加json reponse(尤其是為rest api),第三個參數(shù)是根據(jù)reponse的大小設(shè)置啟用壓縮的最小值(默認是2K,自己根據(jù)實際情況調(diào)整)

          參考
          http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#how-to-enable-http-response-compression

          posted @ 2016-03-29 11:50 Milo的海域 閱讀(1906) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 千阳县| 安岳县| 新昌县| 时尚| 张家港市| 常宁市| 霍邱县| 潞西市| 丽水市| 申扎县| 平利县| 客服| 泽州县| 濉溪县| 德州市| 平果县| 揭东县| 常宁市| 都江堰市| 桑植县| 嘉禾县| 东乌珠穆沁旗| 石泉县| 利津县| 东宁县| 昌吉市| 拉萨市| 衡南县| 榆树市| 江口县| 永康市| 长顺县| 手机| 章丘市| 靖边县| 喀喇沁旗| 兴山县| 邢台市| 永安市| 利川市| 武定县|