前提:先搭建好本地的单机运行项目:http://www.cnblogs.com/EasonJim/p/7643630.htmlhtml
说明:下面的示例是基于Spring Boot搭建的,对于Spring项目基本通用。迁移旧项目的配置下一篇说明,这里先就如何快速的集成Client和获取配置的值进行实践。java
0、下面的示例都是基于官方提供的教程去实践的,能够参考以下网址:git
一、先新建好Spring Boot项目,这里使用了Web作测试,因此引用了Thymeleaf模板。spring
搭建参考:http://www.cnblogs.com/EasonJim/p/7519854.htmlspringboot
二、登陆Apollo上新建App和相关的配置项,能够参考以下配置:app
三、在POM上引入Client的依赖,此时会有两个包引入Client和Core。ide
<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>0.8.0</version> </dependency>
四、在项目上配置上面设置的app.id,注意此时是经过新建META-INF/app.properties文件实现的。函数
五、代码实现
实现的功能是经过配置中心修改后,再次刷新页面而不重启应用的状况下能看到值的改变。
实现方式主要几种在两种,1为经过API的形式增长监听回调函数来监听值的改变后直接修改,2为经过注入Bean的方式使用Bean下的专用监听注解实现回调监听。
5.一、经过API的方式
代码实现以下:
package com.jsoft.springboottest.springboottest1.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigChangeListener; import com.ctrip.framework.apollo.ConfigService; import com.ctrip.framework.apollo.model.ConfigChange; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; @Controller public class IndexController { private static final Logger logger = LoggerFactory.getLogger(IndexController.class); private Config config; private int timeout; private int batch; private String url; @RequestMapping(value = "/index", method = RequestMethod.GET) public String index(Model model) { Entry entry = new Entry(); entry.setText("Text"); entry.setTitle("Title"); model.addAttribute("entries", entry); model.addAttribute("entry", new Entry()); model.addAttribute("url", url); model.addAttribute("timeout",timeout); model.addAttribute("batch",batch); logger.info("timeout:{}", timeout); logger.info("batch:{}", batch); logger.info("url:{}", url); return "index"; } public IndexController() { config = ConfigService.getAppConfig(); config.addChangeListener(new ConfigChangeListener() { @Override public void onChange(ConfigChangeEvent changeEvent) { logger.info("Changes for namespace {}", changeEvent.getNamespace()); for (String key : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(key); logger.info("Change - key: {}, oldValue: {}, newValue: {}, changeType: {}", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType()); if (key.equals("url")) { url = change.getNewValue(); } if(key.equals("batch")) { batch = Integer.valueOf(change.getNewValue()); } if (key.equals("timeout")) { timeout = Integer.valueOf(change.getNewValue()); } } } }); } }
说明:
经过新建Config对象,使用的是ConfigService.getAppConfig()获取默认配置,也就是配置中心中的application.properties的,固然,getAppConfig能够指定不能的namespage。
经过获取Config对象增长监听回调函数addChangeListener。监听指定的值变化后,从新赋值变量。
这种方式应该是最简单的,不用写特殊的注解去实现,可是可能在运行时也会发现,程序在一启动时不会去获取默认的配置值,好比timeout这些是空的,要解决这个问题时须要在Class上增长@EnableApolloConfig的注解,而后在属性上增长@Value的值便可,改动以下:
package com.jsoft.springboottest.springboottest1.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigChangeListener; import com.ctrip.framework.apollo.ConfigService; import com.ctrip.framework.apollo.model.ConfigChange; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; @Controller @EnableApolloConfig public class IndexController { private static final Logger logger = LoggerFactory.getLogger(IndexController.class); private Config config; @Value("${timeout:200}") private int timeout; @Value("${batch:200}") private int batch; @Value("${url:http://easonjim.com}") private String url; @RequestMapping(value = "/index", method = RequestMethod.GET) public String index(Model model) { Entry entry = new Entry(); entry.setText("Text"); entry.setTitle("Title"); model.addAttribute("entries", entry); model.addAttribute("entry", new Entry()); model.addAttribute("url", url); model.addAttribute("timeout",timeout); model.addAttribute("batch",batch); logger.info("timeout:{}", timeout); logger.info("batch:{}", batch); logger.info("url:{}", url); return "index"; } public IndexController() { config = ConfigService.getAppConfig(); config.addChangeListener(new ConfigChangeListener() { @Override public void onChange(ConfigChangeEvent changeEvent) { logger.info("Changes for namespace {}", changeEvent.getNamespace()); for (String key : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(key); logger.info("Change - key: {}, oldValue: {}, newValue: {}, changeType: {}", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType()); if (key.equals("url")) { url = change.getNewValue(); } if(key.equals("batch")) { batch = Integer.valueOf(change.getNewValue()); } if (key.equals("timeout")) { timeout = Integer.valueOf(change.getNewValue()); } } } }); } }
说明:约定俗成,在@Value上获取值时记得增长默认值,以防止获取为空。
上面代码或许有些冗余,能够再提炼一下,由于config对象的值会实时更新,因此也不须要监听onChange事件,也不须要本身建立一个变量,最后代码实现以下:
package com.jsoft.springboottest.springboottest1.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigChangeListener; import com.ctrip.framework.apollo.ConfigService; import com.ctrip.framework.apollo.model.ConfigChange; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; @Controller public class IndexController2 { private static final Logger logger = LoggerFactory.getLogger(IndexController2.class); private Config config = ConfigService.getAppConfig(); @RequestMapping(value = "/index2", method = RequestMethod.GET) public String index(Model model) { Entry entry = new Entry(); entry.setText("Text"); entry.setTitle("Title"); model.addAttribute("entries", entry); model.addAttribute("entry", new Entry()); model.addAttribute("url", config.getProperty("url", "")); model.addAttribute("timeout",config.getProperty("timeout", "")); model.addAttribute("batch",config.getProperty("batch", "")); logger.info("timeout:{}", config.getProperty("timeout", "")); logger.info("batch:{}", config.getProperty("batch", "")); logger.info("url:{}", config.getProperty("url", "")); return "index"; } }
5.二、经过注入Bean的方式
5.2.一、新建的Bean以下:
package com.jsoft.springboottest.springboottest1.controller; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.model.ConfigChange; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfig; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import javax.annotation.PostConstruct; public class AnnotatedBean { private static final Logger logger = LoggerFactory.getLogger(AnnotatedBean.class); @Value("${timeout:200}") private int timeout; private int batch; @ApolloConfig private Config config; @ApolloConfig("FX.apollo") private Config anotherConfig; @PostConstruct void initialize() { logger.info("timeout is {}", timeout); logger.info("batch is {}", batch); logger.info("Keys for config: {}", config.getPropertyNames()); logger.info("Keys for anotherConfig: {}", anotherConfig.getPropertyNames()); } @Value("${batch:100}") public void setBatch(int batch) { this.batch = batch; } public int getBatch() { return batch; } public int getTimeout() { return timeout; } @ApolloConfigChangeListener("application") private void someChangeHandler(ConfigChangeEvent changeEvent) { logger.info("[someChangeHandler]Changes for namespace {}", changeEvent.getNamespace()); if (changeEvent.isChanged("timeout")) { refreshTimeout(); } if (changeEvent.isChanged("batch")) { setBatch(Integer.valueOf(changeEvent.getChange("batch").getNewValue())); } } @ApolloConfigChangeListener({ "application", "FX.apollo" }) private void anotherChangeHandler(ConfigChangeEvent changeEvent) { logger.info("[anotherChangeHandler]Changes for namespace {}", changeEvent.getNamespace()); for (String key : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(key); logger.info("[anotherChangeHandler]Change - key: {}, oldValue: {}, newValue: {}, changeType: {}", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType()); } } private void refreshTimeout() { // do some custom logic to update placeholder value timeout = config.getIntProperty("timeout", timeout); logger.info("Refreshing timeout to {}", timeout); } }
5.2.二、经过@Configuration注入这个Bean,代码以下:
package com.jsoft.springboottest.springboottest1.controller; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; @Configuration @EnableApolloConfig public class AnnotatedConfig { @Bean public AnnotatedBean annotatedBean() { return new AnnotatedBean(); } }
注意:要增长@EnableApolloConfig和@Configuration,否则不会生效。而且留意到@Bean的注解,若是没有这个时,@ApolloConfigChangeListener不会生效。这个是关键所在,@ApolloConfigChangeListener只能用于Bean注入上,这个和API的方式有明显区别。
在代码上使用:
package com.jsoft.springboottest.springboottest1.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { private static final Logger logger = LoggerFactory.getLogger(TestController.class); @Autowired private AnnotatedBean annotatedBean; @RequestMapping("/show") public String show(){ logger.info("batch:{}",annotatedBean.getBatch()); logger.info("timeout:{}",annotatedBean.getTimeout()); return "Hello World"+" batch:"+annotatedBean.getBatch()+" timeout:"+annotatedBean.getTimeout(); } }
说明:能够看出,只要@Autowired注入刚才的Bean就能够直接使用。
六、运行,通常只须要在/opt/settings/server.properties中配置了env=DEV就能够直接直接启动(由于Client在本地仓库的包上已经有了meta_server的信息),可是在IDE上也能够经过指定VM的参数,增长系统属性变量-D来实现调试,配置以下:
若是处处JAR运行,直接java -jar Spring-Boot-Demo.jar便可,不须要增长什么参数(但前提是配置了/opt/settings/server.properties的env的值)。
测试代码:https://github.com/easonjim/5_java_example/tree/master/apollotest/test1