Skip to content

Commit

Permalink
优化config-web实现
Browse files Browse the repository at this point in the history
  • Loading branch information
colin-lee committed Oct 5, 2015
1 parent 2e0a61d commit 8f2b24e
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 137 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
rebel.xml
.svn
.git
.idea
Expand Down
6 changes: 3 additions & 3 deletions config-web/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mysql.url=
mysql.user=autoconf
mysql.password=!Love4Config~
mysql.url=jdbc:mysql://127.0.0.1:3306/autoconf?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
mysql.user=root
mysql.password=
21 changes: 12 additions & 9 deletions config-web/src/main/resources/applicationContext.xml
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<bean class="com.github.autoconf.spring.reloadable.ReloadablePropertyPostProcessor">
<constructor-arg ref="autoConfig"/>
</bean>
<bean class="com.github.autoconf.spring.reloadable.ReloadablePropertyPostProcessor"
c:placeholderConfigurer-ref="autoConfig"/>

<!-- 支持本地以及从远程加载 -->
<bean id="autoConfig"
class="com.github.autoconf.spring.reloadable.ReloadablePropertySourcesPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="false"/>
<property name="ignoreResourceNotFound" value="true"/>
<!-- 可以是逗号分隔的多个配置文件名 -->
<property name="configName" value="cas.properties, zookeeper.cfg, process.properties"/>
<property name="location" value="application.properties"/>
class="com.github.autoconf.spring.reloadable.ReloadablePropertySourcesPlaceholderConfigurer"
p:ignoreResourceNotFound="true"
p:ignoreUnresolvablePlaceholders="false"
p:configName=""
p:location="application.properties">
<property name="locations">
<list>log4j.properties</list>
</property>
</bean>

<import resource="spring/spring-shiro.xml"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
*/
@Component
public class ReloadablePropertyPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
private static final Logger LOG = LoggerFactory.getLogger(ReloadablePropertyPostProcessor.class);
private final PropertyChangedEventNotifier eventNotifier;
private final PropertyConversionService propertyConversionService;
private final ReloadablePropertySourcesPlaceholderConfigurer placeholderConfigurer;
private Logger log = LoggerFactory.getLogger(getClass());
private Multimap<String, BeanPropertyHolder> beanPropertySubscriptions = HashMultimap.create();

@Autowired
Expand All @@ -59,15 +59,15 @@ public ReloadablePropertyPostProcessor(final ReloadablePropertySourcesPlaceholde

@PostConstruct
protected void init() {
log.info("Registering ReloadablePropertyProcessor for properties file changes");
LOG.info("Registering ReloadablePropertyProcessor for properties file changes");
registerPropertyReloader();
}

/**
* Utility method to unregister the class from receiving events about property files being changed.
*/
public final void unregisterPropertyReloader() {
log.info("Unregistering ReloadablePropertyProcessor from property file changes");
LOG.info("Unregistering ReloadablePropertyProcessor from property file changes");
this.eventNotifier.unregister(this);
}

Expand All @@ -88,9 +88,9 @@ public final void registerPropertyReloader() {
*/
@Subscribe
public void handlePropertyChange(final PropertyModifiedEvent event) {
Collection<BeanPropertyHolder> holders =
this.beanPropertySubscriptions.get(event.getPropertyName());
for (final BeanPropertyHolder bean : holders) {
Collection<BeanPropertyHolder> c = beanPropertySubscriptions.get(event.getPropertyName());
System.err.println("====================" + event + ", holders:" + c);
for (final BeanPropertyHolder bean : c) {
updateField(bean, event);
}
}
Expand All @@ -103,29 +103,28 @@ public void updateField(final BeanPropertyHolder holder, final PropertyModifiedE

final Object convertedProperty = convertPlaceHolderForField(fieldToUpdate, rawValue);
try {
log.info("Reloading property [{}] on field [{}] for class [{}]", event.getPropertyName(), fieldToUpdate.getName(), canonicalName);
LOG.info("Reloading property [{}] on field [{}] for class [{}]", event.getPropertyName(), fieldToUpdate.getName(), canonicalName);
fieldToUpdate.set(beanToUpdate, convertedProperty);
} catch (final IllegalAccessException e) {
log.error("Unable to reloading property [{}] on field [{}] for class [{}]\n Exception [{}]", event.getPropertyName(), fieldToUpdate.getName(), canonicalName, e.getMessage());
LOG.error("Unable to reloading property [{}] on field [{}] for class [{}]\n Exception [{}]", event.getPropertyName(), fieldToUpdate.getName(), canonicalName, e.getMessage());
}
}

@Override
public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
if (log.isDebugEnabled()) {
log.debug("Setting Reloadable Properties on [{}]", beanName);
if (LOG.isDebugEnabled()) {
LOG.debug("Setting Reloadable Properties on [{}]", beanName);
}
setPropertiesOnBean(bean);
Collection<DynamicProperty> dynamicProperties =
placeholderConfigurer.getPlaceHolders().get(beanName);
for (DynamicProperty property : dynamicProperties) {
for (DynamicProperty property : placeholderConfigurer.getPlaceHolders().get(beanName)) {
String name = property.getPropertyName();
Field field = ReflectionUtils.findField(bean.getClass(), name);
if (field != null) {
ReflectionUtils.makeAccessible(field);
validateFieldNotFinal(bean, field);
for (String holder : property.getPlaceholders()) {
subscribeBeanToPropertyChangedEvent(holder, new BeanPropertyHolder(bean, field, property.getRawValue()));
BeanPropertyHolder bp = new BeanPropertyHolder(bean, field, property.getRawValue());
subscribeBeanToPropertyChangedEvent(holder, bp);
}
} else {
String methodName = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
Expand All @@ -138,13 +137,13 @@ public boolean postProcessAfterInstantiation(final Object bean, final String bea
ReflectionUtils.makeAccessible(i);
i.invoke(bean, value);
} catch (Exception e) {
log.error("cannot invoke {}.{}({})", bean.getClass(), methodName, value);
LOG.error("cannot invoke {}.{}({})", bean.getClass(), methodName, value);
}
break;
}
}
if (!found) {
log.error("cannot find {} in class: {}", methodName, bean.getClass());
LOG.error("cannot find {} in class: {}", methodName, bean.getClass());
}
}
}
Expand All @@ -164,17 +163,17 @@ public void doWith(final Field field) throws IllegalArgumentException, IllegalAc
validatePropertyAvailableOrDefaultSet(bean, field, annotation, property);

if (null != property) {
log.info("Attempting to convert and set property [{}] on field [{}] for class [{}] to type [{}]", property, field.getName(), bean.getClass().getCanonicalName(), field.getType());
LOG.info("Attempting to convert and set property [{}] on field [{}] for class [{}] to type [{}]", property, field.getName(), bean.getClass().getCanonicalName(), field.getType());

final Object convertedProperty = convertPropertyForField(field, annotation.value());

log.info("Setting field [{}] of class [{}] with value [{}]", field.getName(), bean.getClass().getCanonicalName(), convertedProperty);
LOG.info("Setting field [{}] of class [{}] with value [{}]", field.getName(), bean.getClass().getCanonicalName(), convertedProperty);

field.set(bean, convertedProperty);

subscribeBeanToPropertyChangedEvent(annotation.value(), new BeanPropertyHolder(bean, field, annotation.value()));
} else {
log.info("Leaving field [{}] of class [{}] with default value", field.getName(), bean.getClass().getCanonicalName());
LOG.info("Leaving field [{}] of class [{}] with default value", field.getName(), bean.getClass().getCanonicalName());
}
}
}
Expand Down Expand Up @@ -204,6 +203,7 @@ private boolean fieldDoesNotHaveDefault(final Field field, final Object value) t
}

private void subscribeBeanToPropertyChangedEvent(final String property, final BeanPropertyHolder fieldProperty) {
System.err.println("-------------property:" + property + ", bp: " + fieldProperty);
this.beanPropertySubscriptions.put(property, fieldProperty);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.springframework.util.StringValueResolver;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.List;
import java.util.Properties;
Expand All @@ -41,21 +42,16 @@
* @author James Morgan
*/
public class ReloadablePropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer implements EventPublisher {
private Logger log = LoggerFactory.getLogger(getClass());
private static final Logger LOG =
LoggerFactory.getLogger(ReloadablePropertySourcesPlaceholderConfigurer.class);
private PropertyChangedEventNotifier eventNotifier;
private Multimap<String, DynamicProperty> placeHolders = HashMultimap.create();
private PropertySourcesPropertyResolver propertyResolver;
private IConfigFactory configFactory;
private Resource[] locations;
/**
* 在cms系统中配置的名称,可以是逗号分隔的多个名字
*/
private String configName;
/**
* the application context is needed to find the beans again during reconfiguration
*/
private BeanFactory beanFactory;
private String beanName;

public ReloadablePropertySourcesPlaceholderConfigurer() {
}
Expand All @@ -82,7 +78,6 @@ protected void loadProperties(final Properties props) throws IOException {
IChangeableConfig config = configFactory.getConfig(configName, new IChangeListener() {
@Override
public void changed(IConfig config) {
log.info("CMS: {} changed", config.getName());
reloadCmsConfig(config);
}
}, false);
Expand All @@ -91,9 +86,9 @@ public void changed(IConfig config) {
}

private void reloadCmsConfig(IConfig config) {
final Properties reloadedProperties = new Properties();
reloadedProperties.putAll(config.getAll());
updateProperty(reloadedProperties);
final Properties p = new Properties();
p.putAll(config.getAll());
updateProperty(p);
}

private void updateProperty(Properties reloadedProperties) {
Expand Down Expand Up @@ -133,44 +128,58 @@ private void updateProperty(Properties reloadedProperties) {
}
}

@Override
public void setLocations(final Resource[] locations) {
super.setLocations(locations);
this.locations = locations;
}

@Override
public void onResourceChanged(final Resource resource) {
try {
LOG.info("{} changed", resource);
updateProperty(PropertiesLoaderUtils.loadProperties(resource));
} catch (final IOException e) {
log.error("Failed to reload properties file once change", e);
LOG.error("Failed to reload properties file once change", e);
}
}

public void startWatching() {
if (null == this.eventNotifier) {
throw new BeanInitializationException("Event bus not setup, you should not be calling this method...!");
}
if (this.locations != null) {
Resource[] resources = (Resource[]) getFieldValue("locations");
if (resources == null) {
return;
}
try {
// Here we actually create and set a FileWatcher to monitor the given locations
for (Resource i : resources) {
final LocalConfig c = new LocalConfig(i.getFilename(), i.getFile().toPath());
FileUpdateWatcher.getInstance().watch(c.getPath(), new IFileListener() {
@Override
public void changed(Path path, byte[] content) {
LOG.info("{} changed", path);
c.copyOf(content);
reloadCmsConfig(c);
}
});
}
} catch (Exception e) {
LOG.error("Unable to start properties file watcher", e);
}
}

private Object getFieldValue(String name) {
Class<?> clz = super.getClass();
while (clz != Object.class) {
try {
// Here we actually create and set a FileWatcher to monitor the given locations
for (Resource i : locations) {
final LocalConfig c = new LocalConfig(i.getFilename(), i.getFile().toPath());
FileUpdateWatcher.getInstance().watch(c.getPath(), new IFileListener() {
@Override
public void changed(Path path, byte[] content) {
log.info("{} changed", path);
c.copyOf(content);
reloadCmsConfig(c);
}
});
reloadCmsConfig(c);
Field f = clz.getDeclaredField(name);
if (!f.isAccessible()) {
f.setAccessible(true);
}
} catch (Exception e) {
log.error("Unable to start properties file watcher", e);
return f.get(this);
} catch (NoSuchFieldException ignored) {
clz = clz.getSuperclass();
} catch (IllegalAccessException e) {
LOG.error("cannot getFieldValue('{}')", name, e);
}
}
return null;
}

public PropertySourcesPropertyResolver getPropertyResolver() {
Expand All @@ -188,36 +197,6 @@ public Object resolveProperty(final Object property) {
return getPropertyResolver().getProperty(property.toString());
}

/**
* Only necessary to check that we're not parsing our own bean definition,
* to avoid failing on unresolvable placeholders in properties file locations.
* The latter case can happen with placeholders for system properties in
* resource locations.
*
* @see #setLocations
* @see org.springframework.core.io.ResourceEditor
*/
@Override
public void setBeanName(String beanName) {
super.setBeanName(beanName);
this.beanName = beanName;
}

/**
* Only necessary to check that we're not parsing our own bean definition,
* to avoid failing on unresolvable placeholders in properties file locations.
* The latter case can happen with placeholders for system properties in
* resource locations.
*
* @see #setLocations
* @see org.springframework.core.io.ResourceEditor
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
this.beanFactory = beanFactory;
}

private PropertySourcesPropertyResolver buildPropertyResolver(PropertySources sources) {
PropertySourcesPropertyResolver propertyResolver = new PropertySourcesPropertyResolver(sources);
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
Expand All @@ -229,10 +208,12 @@ private PropertySourcesPropertyResolver buildPropertyResolver(PropertySources so
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
String selfName = (String) getFieldValue("beanName");
BeanFactory selfFactory = (BeanFactory) getFieldValue("beanFactory");
for (String curName : beanNames) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
if (!(curName.equals(selfName) && beanFactoryToProcess.equals(selfFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
findDynamicProperties(curName, bd);
Expand Down
Loading

0 comments on commit 8f2b24e

Please sign in to comment.