package org.dayatang.ioc.spring.factory;
import org.dayatang.domain.InstanceProvider;
import org.dayatang.domain.IocInstanceNotUniqueException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* 实例提供者接口的Spring实现。
* SpringProvider内部通过Spring IoC的ApplicationContext实现对象创建。
*
* @author yyang (<a href="mailto:gdyangyu@gmail.com">gdyangyu@gmail.com</a>)
*/
public class SpringInstanceProvider implements InstanceProvider {
private ApplicationContext applicationContext;
/**
* 以一批spring配置文件的路径初始化spring实例提供者。
*
* @param locations spring配置文件的路径的集合。spring将从类路径开始获取这批资源文件。
*/
public SpringInstanceProvider(String... locations) {
applicationContext = new ClassPathXmlApplicationContext(locations);
}
/**
* 从ApplicationContext生成SpringProvider
*
* @param applicationContext
*/
public SpringInstanceProvider(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* 根据一批Spring配置文件初始化spring实例提供者。
*
* @param annotatedClasses
*/
public SpringInstanceProvider(Class<?>... annotatedClasses) {
applicationContext = new AnnotationConfigApplicationContext(annotatedClasses);
}
/**
* 根据类型获取对象实例。返回的对象实例所属的类是T或它的实现类或子类。如果找不到该类型的实例则返回null。
* 如果有部署了多个类型为T的Bean则抛出NoUniqueBeanDefinitionException异常。
*
* @param <T> 类型参数
* @param beanType 实例的类型
* @return 指定类型的实例。
*/
@Override
public <T> T getInstance(Class<T> beanType) {
try {
return applicationContext.getBean(beanType);
} catch (NoUniqueBeanDefinitionException e) {
throw new IocInstanceNotUniqueException(e);
} catch (NoSuchBeanDefinitionException e) {
return null;
}
}
/**
* 根据类型和Bean id获取对象实例。如果找不到该类型的实例则返回null。
* 假如有两个类MyService1和MyService2都实现了接口Service,在applicationContext中这样部署:
* <blockquote>
* <pre>
* <bean id="service1" class="MyService1"/>
* <bean id="service2" class="MyService2"/>
* </pre>
* </blockquote>
* 或者以配置类的方式部署:
* <blockquote>
* <pre>
*
* @param <T> 类型参数
* @param beanName 实现类在容器中配置的名字
* @param beanType 实例的类型
* @return 指定类型的实例。
* @Configuration public class SpringConfiguration {
* <p/>
* @Bean(name = "service1")
* public Service service1() {
* return new MyService1();
* }
* <p/>
* @Bean(name = "service2")
* public Service service2() {
* return new MyService2();
* }
* }
* </pre>
* </blockquote>
* 那么getInstance(Service.class, "service2")将返回MyService2的实例。
*/
@Override
public <T> T getInstance(Class<T> beanType, String beanName) {
try {
return (T) applicationContext.getBean(beanName, beanType);
} catch (NoUniqueBeanDefinitionException e) {
throw new IocInstanceNotUniqueException(e);
} catch (NoSuchBeanDefinitionException e) {
return null;
}
}
/**
* 根据类型和Annotation获取对象实例。如果找不到该类型的实例则返回null。
* 假如有两个类MyService1和MyService2都实现了接口Service,其中MyService2标记为
* TheAnnotation,那么getInstance(Service.class, TheAnnotation.class)将返回
* MyService2的实例。
*
* @param <T> 类型参数
* @param beanType 实例的类型
* @param annotationType 实现类的annotation类型
* @return 指定类型的实例。
*/
@Override
public <T> T getInstance(Class<T> beanType, Class<? extends Annotation> annotationType) {
if (annotationType == null) {
return getInstance(beanType);
}
Map<String, T> results = applicationContext.getBeansOfType(beanType);
List<T> resultsWithAnnotation = new ArrayList<T>();
for (Map.Entry<String, T> entry : results.entrySet()) {
if (applicationContext.findAnnotationOnBean(entry.getKey(), annotationType) != null) {
resultsWithAnnotation.add(entry.getValue());
}
}
if (resultsWithAnnotation.isEmpty()) {
return null;
}
if (resultsWithAnnotation.size() == 1) {
return resultsWithAnnotation.get(0);
}
throw new IocInstanceNotUniqueException();
}
@Override
public <T> Set<T> getInstances(Class<T> beanType) {
return new HashSet<T>(applicationContext.getBeansOfType(beanType).values());
}
@SuppressWarnings("unchecked")
public <T> T getByBeanName(String beanName) {
return (T) applicationContext.getBean(beanName);
}
}