|
2016年3月16日
在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); } } } 為避免影響過大, 設置了個開關, 是否寫入系統(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; }
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需要用 而且改用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)對象, 兩者其實是一個對象
知道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輸出關于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); } }
其他的請求,應該都是類似jolokia的exec get request的格式:
https://jolokia.org/reference/html/protocol.html#exec
<base url>/exec/<mbean name>/<operation name>/<arg1>/<arg2>/ .
用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我
默認的配置有時候點不亮顯示器,且分辨率很低,通過tvservice工具不斷調試,發(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
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>
- IDEA_JDK (or IDEA_JDK_64) environment variable
- jre/ (or jre64/) directory in IDEA home
- registry
- JDK_HOME environment variable
- JAVA_HOME environment variable
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);
1. zookeeper basic/fast paxsos 的形象表述 https://www.douban.com/note/208430424/ 2. 詳細介紹 http://blog.csdn.net/xhh198781/article/details/10949697
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ù)打開壓縮開關,第二個參數(shù)添加json reponse(尤其是為rest api),第三個參數(shù)是根據(jù)reponse的大小設置啟用壓縮的最小值(默認是2K,自己根據(jù)實際情況調整) 參考 http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#how-to-enable-http-response-compression
介紹centos7如何安裝3.0以上的新版本mongodb https://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat/
1. 默認的3個classloader: BootstrapClassloader (Native實現(xiàn)), ExtClassloader, AppClassloader (Java實現(xiàn)) 2. 3個加載器并不是真正的父子繼承關系,而是邏輯上的,JVM啟動先創(chuàng)建ExtClassloader instance,然后構造AppClassloader的時候傳入ExtClassloader實例作為parent Launcher.ExtClassLoader extcl; try { extcl = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); }
try { this.loader = Launcher.AppClassLoader.getAppClassLoader(extcl); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } 關于雙親委派原理: 在加載類的時候,會看看parent有沒有設定,如果設定了 就調用parent.loadClass方法,如果沒設定(==null)也就是parent應該是BootstrapClassloader, 會調用native的findBootstrapClass來加載類,代碼: try { if(this.parent != null) { c = this.parent.loadClass(name, false); } else { c = this.findBootstrapClassOrNull(name); } } catch (ClassNotFoundException var10) { ; }
目的是按照一定優(yōu)先級別裝載系統(tǒng)的lib,系統(tǒng)ext目錄的lib,以及classpath的lib,防止系統(tǒng)的默認行為或者類的實現(xiàn)被修改。 3. java 類的動態(tài)加載 Java內置的ClassLoader總會在加載一個Class之前檢查這個Class是否已經(jīng)被加載過,已經(jīng)被加載過的Class不會加載第二次。因此要想重新加載Class,我們需要實現(xiàn)自己的ClassLoader。 另外一個問題是,每個被加載的Class都需要被鏈接(link),這是通過執(zhí)行ClassLoader.resolve()來實現(xiàn)的,這個方法是 final的,因此無法重寫。Resove()方法不允許一個ClassLoader實例link一個Class兩次,因此,當你需要重新加載一個 Class的時候,你需要重新New一個你自己的ClassLoader實例。
|