在xml文件中定义bean
代码地址: github.com/WangChao-ly…
建议订阅博主专栏,从下到上系统手写spring源码,体会其中过程!
有了资源加载器,就可以在xml格式配置文件中声明式地定义bean的信息,资源加载器读取xml文件,解析出bean的信息,然后往容器中注册BeanDefinition。BeanDefinitionReader是读取bean定义信息的抽象接口,XmlBeanDefinitionReader是从xml文件中读取的实现类。BeanDefinitionReader需要有获取资源的能力,且读取bean定义信息后需要往容器中注册BeanDefinition,因此BeanDefinitionReader的抽象实现类AbstractBeanDefinitionReader拥有ResourceLoader和BeanDefinitionRegistry两个属性。 由于从xml文件中读取的内容是String类型,所以属性仅支持String类型和引用其他Bean。后面会讲到类型转换器,实现类型转换。 为了方便后面的讲解和功能实现,并且尽量保持和spring中BeanFactory的继承层次一致,对BeanFactory的继承层次稍微做了调整。
到目前为止,整体的层次图形:
改造原有BeanFactory
- 为BeanFactory接口添加根据名称和类型查找bean方法
public interface BeanFactory {
/**
* 获取bean
*
* @param name
* @return
* @throws BeansException bean不存在时
*/
Object getBean(String name) throws BeansException;
/**
* 根据名称和类型查找bean
*
* @param name
* @param requiredType
* @param <T>
* @return
* @throws BeansException
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
}
- 编写BeanFactory的三个继承接口ListableBeanFactory、HierarchicalBeanFactory、AutowireCapableBeanFactory
- ListableBeanFactory:新增返回指定类型的所有实例和返回定义的所有bean名称方法
public interface ListableBeanFactory extends BeanFactory {
/**
* 返回指定类型的所有实例
*
* @param type
* @param <T>
* @return
* @throws BeansException
*/
<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
/**
* 返回定义的所有bean的名称
*
* @return
*/
String[] getBeanDefinitionNames();
}
- HierarchicalBeanFactory:目前只是单纯继承BeanFactory接口
public interface HierarchicalBeanFactory extends BeanFactory {
}
- AutowireCapableBeanFactory:目前只是单纯继承BeanFactory接口
public interface AutowireCapableBeanFactory extends BeanFactory {
}
- 编写ConfigurableBeanFactory:该接口继承了HierarchicalBeanFactory、SingletonBeanRegistry,BeanFactory和单例bean注册等功能
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
}
- 由于继承了抽象类DefaultSingletonBeanRegistry实现了ConfigurableBeanFactory接口,因此随着BeanFactory的改变,该类可以选择性的实现上边新增的方法,实现根据名称和类型获取bean的方法
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
@Override
public Object getBean(String name) throws BeansException {
Object bean = getSingleton(name);
if (bean != null) {
return bean;
}
BeanDefinition beanDefinition = getBeanDefinition(name);
return createBean(name, beanDefinition);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return ((T) getBean(name));
}
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;
protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;
}
- 编写ConfigurableListableBeanFactory接口,该接口继承了ListableBeanFactory, AutowireCapableBeanFactory和ConfigurableBeanFactory。也就是说该接口具备BeanFactory三个实现类的全部功能,并且拥有单例注册表的功能,同时该接口提供了根据name查找BeanDefinition的功能
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
/**
* 根据名称查找BeanDefinition
*
* @param beanName
* @return
* @throws BeansException 如果找不到BeanDefintion
*/
BeanDefinition getBeanDefinition(String beanName) throws BeansException;
}
- AbstractAutowireCapableBeanFactory之前已经介绍过,现在只为它扩展了public T getBean(String name, Class requiredType) 方法(通过继承DefaultSingletonBeanRegistry获得的)
- 实现了ListableBeanFactory中的getBeansOfType方法,和getBeanDefinitionNames方法,同时实现了BeanDefinitionRegistry接口,具备了关于BeanDefinition相关的方法外还具备了获取bean等方法,具体实现如下:
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
throw new BeansException("No bean named '" + beanName + "' is defined");
}
return beanDefinition;
}
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
Map<String, T> result = new HashMap<>();
beanDefinitionMap.forEach((beanName, beanDefinition) -> {
Class beanClass = beanDefinition.getBeanClass();
if (type.isAssignableFrom(beanClass)) {
T bean = (T) getBean(beanName);
result.put(beanName, bean);
}
});
return result;
}
@Override
public String[] getBeanDefinitionNames() {
Set<String> beanNames = beanDefinitionMap.keySet();
return beanNames.toArray(new String[beanNames.size()]);
}
}
- 总结:到目前为止,之前关于bean的类和接口改造完毕,现在需要扩展关于从xml中加载 bean的类和接口了!
Xml文件中定义Bean
- 编写读取bean定义信息即BeanDefinition的接口,该接口用在xml中抽象读取bean的相关方法
public interface BeanDefinitionReader {
//获取注册
BeanDefinitionRegistry getRegistry();
//获取资源加载器
ResourceLoader getResourceLoader();
//通过Resource接口,调用Resource中的方法,加载xml资源
void loadBeanDefinitions(Resource resource) throws BeansException;
void loadBeanDefinitions(String location) throws BeansException;
void loadBeanDefinitions(String[] locations) throws BeansException;
}
- AbstractBeanDefinitionReader,该抽象类实现BeanDefinitionReader,实现了其中的部分方法,并且拥有成员变量BeanDefinitionRegistry和ResourceLoader接口对象,后续可以传他们的实现类,可以让AbstractBeanDefinitionReader类拥有注册和资源加载两个功能,具体代码如下:
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
private final BeanDefinitionRegistry registry;
private ResourceLoader resourceLoader;
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, new DefaultResourceLoader());
}
public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
this.registry = registry;
this.resourceLoader = resourceLoader;
}
@Override
public BeanDefinitionRegistry getRegistry() {
return registry;
}
@Override
public void loadBeanDefinitions(String[] locations) throws BeansException {
for (String location : locations) {
loadBeanDefinitions(location);
}
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
}
- 编写最终实现类XmlBeanDefinitionReader,该类继承了AbstractBeanDefinitionReader,实现了相关方法,核心部分开始解析xml文件,将解析的内容,通过BeanDefinitionRegistry,将bean注册到容器中。
- 细节点1:bean标签,id优先于name,如果id和name都为空,将类名的第一个字母转为小写后作为bean的名称
- 细节点2:bean标签中配置的className,将会被用到Class.forName(className),反射加载类
代码如下,代码中的常量定义方法,和super(registry)的调用等,都是我们值得学习的,注意,我们都是面向接口编程,调用时候,需要传入该接口的具体实现类才行:
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public static final String BEAN_ELEMENT = "bean";
public static final String PROPERTY_ELEMENT = "property";
public static final String ID_ATTRIBUTE = "id";
public static final String NAME_ATTRIBUTE = "name";
public static final String CLASS_ATTRIBUTE = "class";
public static final String VALUE_ATTRIBUTE = "value";
public static final String REF_ATTRIBUTE = "ref";
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
super(registry, resourceLoader);
}
@Override
public void loadBeanDefinitions(String location) throws BeansException {
ResourceLoader resourceLoader = getResourceLoader();
Resource resource = resourceLoader.getResource(location);
loadBeanDefinitions(resource);
}
@Override
public void loadBeanDefinitions(Resource resource) throws BeansException {
try {
InputStream inputStream = resource.getInputStream();
try {
doLoadBeanDefinitions(inputStream);
} finally {
inputStream.close();
}
} catch (IOException ex) {
throw new BeansException("IOException parsing XML document from " + resource, ex);
}
}
protected void doLoadBeanDefinitions(InputStream inputStream) {
Document document = XmlUtil.readXML(inputStream);
Element root = document.getDocumentElement();
NodeList childNodes = root.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
if (childNodes.item(i) instanceof Element) {
if (BEAN_ELEMENT.equals(((Element) childNodes.item(i)).getNodeName())) {
//解析bean标签
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute(ID_ATTRIBUTE);
String name = bean.getAttribute(NAME_ATTRIBUTE);
String className = bean.getAttribute(CLASS_ATTRIBUTE);
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new BeansException("Cannot find class [" + className + "]");
}
//id优先于name
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
//如果id和name都为空,将类名的第一个字母转为小写后作为bean的名称
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition(clazz);
for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
if (bean.getChildNodes().item(j) instanceof Element) {
if (PROPERTY_ELEMENT.equals(((Element) bean.getChildNodes().item(j)).getNodeName())) {
//解析property标签
Element property = (Element) bean.getChildNodes().item(j);
String nameAttribute = property.getAttribute(NAME_ATTRIBUTE);
String valueAttribute = property.getAttribute(VALUE_ATTRIBUTE);
String refAttribute = property.getAttribute(REF_ATTRIBUTE);
if (StrUtil.isEmpty(nameAttribute)) {
throw new BeansException("The name attribute cannot be null or empty");
}
Object value = valueAttribute;
if (StrUtil.isNotEmpty(refAttribute)) {
value = new BeanReference(refAttribute);
}
PropertyValue propertyValue = new PropertyValue(nameAttribute, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
}
}
if (getRegistry().containsBeanDefinition(beanName)) {
//beanName不能重名
throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
//注册BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
总结测试,完整代码看开头链接:
public class XmlFileDefineBeanTest {
@Test
public void testXmlFile() throws Exception {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions("classpath:spring.xml");
Person person = (Person) beanFactory.getBean("person");
System.out.println(person);
assertThat(person.getName()).isEqualTo("derek");
assertThat(person.getCar().getBrand()).isEqualTo("porsche");
Car car = (Car) beanFactory.getBean("car");
System.out.println(car);
assertThat(car.getBrand()).isEqualTo("porsche");
}
}
spring.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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-4.0.xsd">
<bean id="person" class="org.springframework.test.ioc.bean.Person">
<property name="name" value="derek"/>
<property name="car" ref="car"/>
</bean>
<bean id="car" class="org.springframework.test.ioc.bean.Car">
<property name="brand" value="porsche"/>
</bean>
</beans>