package org.quickbundle.base.beans.factory;
import java.util.ArrayList;
import java.util.List;
import org.quickbundle.base.web.servlet.RmHolderServlet;
import org.quickbundle.project.init.RmConfig;
import org.quickbundle.tools.helper.RmStringHelper;
import org.quickbundle.tools.helper.xml.RmXmlHelper;
import org.quickbundle.tools.support.log.RmLogHelper;
import org.quickbundle.tools.support.path.RmPathHelper;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class RmBeanFactory {
private static Logger log = RmLogHelper.getLogger(RmBeanFactory.class);
/**
* 全局单例的BeanFactory
*/
private static BeanFactory beanFactory = null;
/**
* 已经开始初始化过程
*/
private static volatile boolean initBeanStarted = false;
public static void setInitBeanStarted(boolean initBeanStarted_) {
initBeanStarted = initBeanStarted_;
}
/**
* 初始化过程完毕
*/
private static volatile boolean isInitBean = false;
/**
* 初始化时的锁,用于将调用getBean的消费者排队等待
*/
public static Object lockInitFactory = new Object();
/**
* 自执行初始化BeanFactory
*/
private static void defaultInitBeanFactory() {
try {
long startTime = System.currentTimeMillis();
log.info("start init bean......");
log.info(RmPathHelper.initWarRoot());
String springPath = "/WEB-INF/config/spring/*.xml";
if (RmHolderServlet.getDefaultServletContext() != null) {
springPath = RmHolderServlet.getDefaultServletContext().getInitParameter("contextConfigLocation");
}
if(springPath.indexOf(",") > -1) {
List<String> lPath = new ArrayList<String>();
String[] paths = springPath.split(",");
for(String path : paths) {
if(path.trim().length() == 0) {
continue;
}
lPath.add(RmXmlHelper.formatToUrl(RmPathHelper.getWarDir() + path.trim()));
}
beanFactory = new FileSystemXmlApplicationContext(lPath.toArray(new String[0]));
} else {
beanFactory = new FileSystemXmlApplicationContext(RmXmlHelper.formatToUrl(RmPathHelper.getWarDir() + springPath));
}
log.info("end init bean, time=" + (System.currentTimeMillis() - startTime) + " milliseconds");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("defaultInitBeanFactory fail", e);
}
}
/**
* 返回BeanFactory
*
* @return
*/
public static BeanFactory getBeanFactory() {
if(!isInitBean) {
synchronized(lockInitFactory) { //lock and init
if(!isInitBean) {
setInitBeanStarted(true);
defaultInitBeanFactory();
isInitBean = true;
}
}
}
return beanFactory;
}
/**
* 手动设置BeanFactory,一般由Web.xml配置启动的Listener调用
*
* @param bf
*/
public static void setBeanFactory(BeanFactory bf) {
if(bf == null) {
throw new RuntimeException("BeanFactory can not be null!");
}
beanFactory = bf;
isInitBean = true;
}
private static BeanFactory getBeanFactorySafe() {
if(!isInitBean && initBeanStarted) { //如果未启动完毕,但已经开始启动,则不走getBeanFactory()防止触发创建第2个BeanFactory
if(beanFactory != null) {
return beanFactory;
}
synchronized(lockInitFactory) { //wait for process that initing beanFactory
//nothing
}
if(!isInitBean) {
StringBuilder warnInfo = new StringBuilder("RmBeanFactory.getBeanFactorySafe(\"");
warnInfo.append("\") return null, StackTrace:");
if(RmConfig.systemDebugMode()) {
warnInfo.append("\n");
warnInfo.append(RmStringHelper.getStackTrace(10000));
}
log.warn(warnInfo.toString());
return null;
}
}
return getBeanFactory();
}
/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>This method allows a Spring BeanFactory to be used as a replacement for the
* Singleton or Prototype design pattern. Callers may retain references to
* returned objects in the case of Singleton beans.
* <p>Translates aliases back to the corresponding canonical bean name.
* Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there is no bean definition
* with the specified name
* @throws BeansException if the bean could not be obtained
*/
public static Object getBean(String beanName) {
BeanFactory bf = getBeanFactorySafe();
if(bf != null) {
return bf.getBean(beanName);
}
return null;
}
/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>Behaves the same as {@link #getBean(String)}, but provides a measure of type
* safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the
* required type. This means that ClassCastException can't be thrown on casting
* the result correctly, as can happen with {@link #getBean(String)}.
* <p>Translates aliases back to the corresponding canonical bean name.
* Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @param requiredType type the bean must match. Can be an interface or superclass
* of the actual class, or {@code null} for any match. For example, if the value
* is {@code Object.class}, this method will succeed whatever the class of the
* returned instance.
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there is no such bean definition
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
* @throws BeansException if the bean could not be created
*/
public static <T> T getBean(String name, Class<T> requiredType) throws BeansException {
BeanFactory bf = getBeanFactorySafe();
if(bf != null) {
return bf.getBean(name, requiredType);
}
return null;
}
/**
* Return the bean instance that uniquely matches the given object type, if any.
* @param requiredType type the bean must match; can be an interface or superclass.
* {@code null} is disallowed.
* <p>This method goes into {@link ListableBeanFactory} by-type lookup territory
* but may also be translated into a conventional by-name lookup based on the name
* of the given type. For more extensive retrieval operations across sets of beans,
* use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
* @return an instance of the single bean matching the required type
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
* @since 3.0
* @see ListableBeanFactory
*/
public static <T> T getBean(Class<T> requiredType) throws BeansException {
BeanFactory bf = getBeanFactorySafe();
if(bf != null) {
return bf.getBean(requiredType);
}
return null;
}
/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
* overriding the specified default arguments (if any) in the bean definition.
* @param name the name of the bean to retrieve
* @param args arguments to use if creating a prototype using explicit arguments to a
* static factory method. It is invalid to use a non-null args value in any other case.
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there is no such bean definition
* @throws BeanDefinitionStoreException if arguments have been given but
* the affected bean isn't a prototype
* @throws BeansException if the bean could not be created
* @since 2.5
*/
public static Object getBean(String name, Object... args) throws BeansException {
BeanFactory bf = getBeanFactorySafe();
if(bf != null) {
return bf.getBean(name, args);
}
return null;
}
}