org.beangle.inject.spring.config.SpringConfigProcessor Maven / Gradle / Ivy
The newest version!
/*
* Beangle, Agile Development Scaffold and Toolkits.
*
* Copyright © 2005, The Beangle Software.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package org.beangle.inject.spring.config;
import org.beangle.commons.bean.Disposable;
import org.beangle.commons.bean.Initializing;
import org.beangle.commons.collection.CollectUtils;
import org.beangle.commons.event.EventListener;
import org.beangle.commons.inject.Resources;
import org.beangle.commons.inject.Scope;
import org.beangle.commons.inject.bind.BeanConfig;
import org.beangle.commons.inject.bind.BeanConfig.Definition;
import org.beangle.commons.inject.bind.BeanConfig.ReferenceValue;
import org.beangle.commons.inject.bind.BindModule;
import org.beangle.commons.inject.bind.BindRegistry;
import org.beangle.commons.lang.Assert;
import org.beangle.commons.lang.ClassLoaders;
import org.beangle.commons.lang.Strings;
import org.beangle.commons.lang.reflect.Reflections;
import org.beangle.commons.lang.time.Stopwatch;
import org.beangle.commons.web.util.HttpUtils;
import org.beangle.inject.spring.SpringEventMulticaster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.*;
import org.springframework.core.io.UrlResource;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
/**
* 完成springbean的自动注册和再配置
*
* @author chaostone
* @version $Id: $
*/
public class SpringConfigProcessor implements BeanDefinitionRegistryPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(SpringConfigProcessor.class);
private Resources reconfigResources;
public static String reconfigUrl = null;
/**
* Automate register and wire bean
* Reconfig beans
*/
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry definitionRegistry)
throws BeansException {
// 自动注册
BindRegistry registry = new SpringBindRegistry(definitionRegistry);
Map newDefinitions = registerModules(registry);
// should register after all beans
registerBuildins(registry);
// 再配置
reconfig(definitionRegistry, registry);
lifecycle(registry, definitionRegistry);
autowire(newDefinitions, registry);
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
factory.registerCustomEditor(Resources.class, ResourcesEditor.class);
}
private void reconfig(BeanDefinitionRegistry registry, BindRegistry bindRegistry) {
Stopwatch watch = new Stopwatch(true);
if (!Strings.isBlank(SpringConfigProcessor.reconfigUrl)) {
try {
if (null == reconfigResources) reconfigResources = new Resources();
reconfigResources.getLocals().add(new URL(SpringConfigProcessor.reconfigUrl));
} catch (Exception e) {
}
}
if (null == reconfigResources || reconfigResources.isEmpty()) return;
Set beanNames = CollectUtils.newHashSet();
BeanDefinitionReader reader = new BeanDefinitionReader();
for (URL url : reconfigResources.getAllPaths()) {
if (url.getProtocol().contains("http")) {
if (HttpUtils.access(url)) {
logger.info("loading remote config {}", url);
} else {
logger.info("ignore remote config {}", url);
continue;
}
}
List holders = reader.load(new UrlResource(url));
for (ReconfigBeanDefinitionHolder holder : holders) {
// choose primary key
if (holder.getConfigType().equals(ReconfigType.PRIMARY)) {
Class> clazz = ClassLoaders.loadClass(holder.getBeanDefinition().getBeanClassName());
if (clazz.isInterface()) {
List names = bindRegistry.getBeanNames(clazz);
boolean finded = names.contains(holder.getBeanName());
if (finded) {
for (String name : names) {
BeanDefinition bd = registry.getBeanDefinition(name);
bd.setPrimary(name.equals(holder.getBeanName()));
}
}
}
// let's do property update and merge.
holder.setConfigType(ReconfigType.UPDATE);
holder.getBeanDefinition().setBeanClassName(null);
}
if (holder.getConfigType().equals(ReconfigType.UPDATE)) {
if (registry.containsBeanDefinition(holder.getBeanName())) {
BeanDefinition definition = registry.getBeanDefinition(holder.getBeanName());
String successName = mergeDefinition(definition, holder);
if (null != successName) beanNames.add(successName);
} else {
logger.warn("No bean {} to reconfig defined in {}.", holder.getBeanName(), url);
continue;
}
}
}
}
if (!beanNames.isEmpty()) logger.info("Reconfig {} in {}", beanNames, watch);
}
/**
*
* lifecycle.
*
*
* @param registry a {@link org.beangle.commons.inject.bind.BindRegistry} object.
* @param definitionRegistry a
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} object.
*/
protected void lifecycle(BindRegistry registry, BeanDefinitionRegistry definitionRegistry) {
for (String name : registry.getBeanNames()) {
Class> clazz = registry.getBeanType(name);
String springName = name;
if (springName.startsWith("&")) springName = springName.substring(1);
if (!definitionRegistry.containsBeanDefinition(springName)) continue;
AbstractBeanDefinition def = (AbstractBeanDefinition) definitionRegistry.getBeanDefinition(springName);
if (Initializing.class.isAssignableFrom(clazz) && null == def.getInitMethodName()
&& !def.getPropertyValues().contains("init-method")) {
def.setInitMethodName("init");
}
if (Disposable.class.isAssignableFrom(clazz) && null == def.getDestroyMethodName()
&& !def.getPropertyValues().contains("destroy-method")) {
def.setDestroyMethodName("destroy");
}
}
}
/**
*
* registerBuildins.
*
*
* @param registry a {@link org.beangle.commons.inject.bind.BindRegistry} object.
*/
protected void registerBuildins(BindRegistry registry) {
List listenerBeans = registry.getBeanNames(EventListener.class);
Definition eventMulticaster = new Definition(SpringEventMulticaster.class.getName(),
SpringEventMulticaster.class, Scope.SINGLETON.toString());
eventMulticaster.property("listenerBeans", listenerBeans);
registerBean(eventMulticaster, registry);
}
/**
* 合并bean定义
*
* @param target a {@link org.springframework.beans.factory.config.BeanDefinition} object.
* @param source a
* {@link org.beangle.inject.spring.config.context.spring.ReconfigBeanDefinitionHolder}
* object.
* @return a {@link java.lang.String} object.
*/
protected String mergeDefinition(BeanDefinition target, ReconfigBeanDefinitionHolder source) {
if (null == target.getBeanClassName()) {
logger.warn("ingore bean definition {} for without class", source.getBeanName());
return null;
}
// 当类型变化后,删除原有配置
if (null != source.getBeanDefinition().getBeanClassName()
&& !source.getBeanDefinition().getBeanClassName().equals(target.getBeanClassName())) {
target.setBeanClassName(source.getBeanDefinition().getBeanClassName());
for (PropertyValue pv : target.getPropertyValues().getPropertyValues()) {
target.getPropertyValues().removePropertyValue(pv);
}
target.getConstructorArgumentValues().clear();
}
MutablePropertyValues pvs = source.getBeanDefinition().getPropertyValues();
for (PropertyValue pv : pvs.getPropertyValueList()) {
String name = pv.getName();
target.getPropertyValues().addPropertyValue(name, pv.getValue());
logger.debug("config {}.{} = {}", new Object[]{source.getBeanName(), name, pv.getValue()});
}
logger.debug("Reconfig bean {} ", source.getBeanName());
return source.getBeanName();
}
/**
*
* findRegistedModules.
*
*
* @param registry a {@link org.beangle.commons.inject.bind.BindRegistry} object.
* @return a {@link java.util.Map} object.
*/
protected Map registerModules(BindRegistry registry) {
Stopwatch watch = new Stopwatch(true);
List modules = registry.getBeanNames(BindModule.class);
Map newBeanDefinitions = CollectUtils.newHashMap();
for (String name : modules) {
Class> beanClass = registry.getBeanType(name);
BeanConfig config = null;
try {
config = ((BindModule) beanClass.newInstance()).getConfig();
} catch (Exception e) {
logger.error("class initialization error of " + beanClass, e);
continue;
}
List definitions = config.getDefinitions();
for (BeanConfig.Definition definition : definitions) {
String beanName = definition.beanName;
if (registry.contains(beanName)) {
logger.warn("Ingore exists bean definition {}", beanName);
} else {
BeanDefinition def = registerBean(definition, registry);
newBeanDefinitions.put(beanName, def);
}
}
}
logger.info("Auto register {} beans in {}", newBeanDefinitions.size(), watch);
return newBeanDefinitions;
}
private BeanDefinition convert(Definition definition) {
GenericBeanDefinition def = new GenericBeanDefinition();
def.setBeanClass(definition.clazz);
def.setScope(definition.scope);
if (null != definition.initMethod) def.setInitMethodName(definition.initMethod);
if (null != definition.destroyMethod) def.setDestroyMethodName(definition.destroyMethod);
MutablePropertyValues mpv = new MutablePropertyValues();
for (Map.Entry entry : definition.getProperties().entrySet()) {
Object value = entry.getValue();
if (value instanceof Collection>) {
if (value instanceof List>) {
List