接上篇文章中实现了通过beanFactory注册beanDefinition, 通过上层父类生成bean的实例同时可以调用有参构造创建bean实例,本章主要实现注入属性和依赖对象;以xml方式配置bean并注入
(一) 注入属性和依赖对象
PropertyValue
定义属性对象值.
public class PropertyValue {
private final String name;
private final Object value;
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
// get set方法
}
PropertyValues
属性对象的整合
public class PropertyValues {
private final List<PropertyValue> propertyValueList = new ArrayList<>();
public void addPropertyValue(PropertyValue pv){
this.propertyValueList.add(pv);
}
public List<PropertyValue> getPropertyValues() {
return propertyValueList;
}
public PropertyValue getPropertyValue(String propertyName){
for (PropertyValue pv : this.propertyValueList) {
if(pv.getName().equals(propertyName)){
return pv;
}
}
return null;
}
}
BeanReference 标注一个对象为Bean
public class BeanReference {
private final String beanName;
public BeanReference(String beanName) {
this.beanName = beanName;
}
public String getBeanName() {
return beanName;
}
}
有了对象属性值之后下一步就是优化BeanDefinition为其添加属性值字段
BeanDefinition 添加属性字段
public class BeanDefinition {
private Class beanClass;
private PropertyValues propertyValues;
public BeanDefinition(Class beanClass){
this.beanClass = beanClass;
this.propertyValues = new PropertyValues();
}
public BeanDefinition(Class beanClass,PropertyValues propertyValues){
this.beanClass = beanClass;
this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}
}
因为我们就可以在创建bean时为字段注入值
AbstractAutowireCapableBeanFactory
其中applyPropertyValues用于从beanDefinition中读取属性的配置信息然后通过BeanUtil(hutool-all中提供的类)为其赋值
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition,beanName,args);
applyPropertyValues(beanName,bean,beanDefinition);
}catch (Exception e){
e.printStackTrace();
}
addSingleton(beanName,bean);
return bean;
}
// 创建bean实例
protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
Constructor constructorToUse = null;
Class<?> beanClass = beanDefinition.getBeanClass();
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
for (Constructor ctor : declaredConstructors) {
if (null != args && ctor.getParameterTypes().length == args.length) {
constructorToUse = ctor;
break;
}
}
return instantiationStrategy.instantiate(beanDefinition, beanName, constructorToUse, args);
}
// 设置属性值
protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition){
try {
// 从beanDefinition中获取property
PropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
String name = propertyValue.getName();
Object value = propertyValue.getValue();
if(value instanceof BeanReference){
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
}
BeanUtil.setFieldValue(bean,name,value);
}
}catch (Exception e){
throw new BeansException("Error setting property values" + beanName);
}
}
}
注入属性和依赖对象 [测试]
@Test
public void test_BeanFactory() {
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2. UserDao 注册
beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));
// 3. UserService 设置属性[uId、userDao]
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("uId", "10001"));
propertyValues.addPropertyValue(new PropertyValue("userDao",new BeanReference("userDao")));
// 4. UserService 注入bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class, propertyValues);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 5. UserService 获取bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
}
(二) xml方式定义属性值和文件注册bean
Resource资源接口
通过Resource获取文件流
public interface Resource {
InputStream getInputStream() throws IOException;
}
ClassPathResource (通过path读取文件)
通过path读取文件
public class ClassPathResource implements Resource{
private final String path;
private ClassLoader classLoader;
public ClassPathResource(String path){
this(path,(ClassLoader) null);
}
public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
this.path = path;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
@Override
public InputStream getInputStream() throws IOException {
InputStream is = classLoader.getResourceAsStream(path);
if(is == null){
throw new FileNotFoundException(
this.path + " cannot be opened because it does not exist"
);
}
return is;
}
}
FileSystemResource
public class FileSystemResource implements Resource{
private final File file;
private final String path;
public FileSystemResource(String path) {
this.file = new File(path);
this.path = path;
}
@Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
public String getPath() {
return path;
}
}
UrlResource
public class UrlResource implements Resource{
private final URL url;
public UrlResource(URL url) {
Assert.notNull(url,"URL must not be null");
this.url = url;
}
@Override
public InputStream getInputStream() throws IOException {
URLConnection con = this.url.openConnection();
try {
return con.getInputStream();
}catch (IOException ex){
if (con instanceof HttpURLConnection){
((HttpURLConnection) con).disconnect();
}
throw ex;
}
}
}
ResourceLoader
资源加载器,提供加载Resource的方法.
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = "classpath:";
Resource getResource(String location);
}
DefaultResourceLoader
具体的实现类,根据你传入的location的类型来判断需要那种Resource资源加载器
public class DefaultResourceLoader implements ResourceLoader{
// 根据传入path的类型来决定使用哪个加载器进行加载获得资源流
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if(location.startsWith(CLASSPATH_URL_PREFIX)){
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
}else{
try {
URL url = new URL(location);
return new UrlResource(url);
}catch (MalformedURLException e){
return new FileSystemResource(location);
}
}
}
}
BeanDefinitionReader
bean定义读取,包括registry(DefaultListableFactory) 注册beanDefinitin到bean工厂中, resourceloader() 资源加载器
public interface BeanDefinitionReader {
BeanDefinitionRegistry getRegistry();
ResourceLoader getResourceLoader();
void loadBeanDefinitions(Resource resource) throws BeansException;
void loadBeanDefinitions(Resource... resources) throws BeansException;
void loadBeanDefinitions(String location) throws BeansException;
}
AbstractBeanDefinitionReader
抽象类中保存了所有beanDefinitionReader通用的方法也就是接收registry和resourceLoader
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 ResourceLoader getResourceLoader() {
return resourceLoader;
}
}
具体的实现类 XmlBeanDefinitionReader
通过解析xml文件来获得beanDefinition然后注册到beanFactory中,同时为属性进行赋值。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader{
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
super(registry, resourceLoader);
}
@Override
public void loadBeanDefinitions(Resource resource) throws BeansException {
try {
try (InputStream inputStream = resource.getInputStream()){
doLoadBeanDefinitions(inputStream);
}
}catch (IOException | ClassNotFoundException e){
throw new BeansException("IOException parsing XML document from " + resource,e);
}
}
@Override
public void loadBeanDefinitions(Resource... resources) throws BeansException {
for (Resource resource : resources) {
loadBeanDefinitions(resource);
}
}
@Override
public void loadBeanDefinitions(String location) throws BeansException {
ResourceLoader resourceLoader = getResourceLoader();
// 判断是哪种Resource.
Resource resource = resourceLoader.getResource(location);
loadBeanDefinitions(resource);
}
protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
Document doc = XmlUtil.readXML(inputStream);
Element root = doc.getDocumentElement();
NodeList childNodes = root.getChildNodes();
for(int i=0;i<childNodes.getLength();i++){
// 判断元素
if(!(childNodes.item(i) instanceof Element)) continue;
// 判断对象是否是<bean> 开头的
if(!"bean".equals(childNodes.item(i).getNodeName())) continue;
// 解析标签
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute("id");
String name = bean.getAttribute("name");
String className = bean.getAttribute("class");
// 获取 Class,方便获取类中的名称
Class<?> clazz = Class.forName(className);
// 优先级 id > name
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
// 定义Bean.
BeanDefinition beanDefinition = new BeanDefinition(clazz);
// 读取属性并填充
for(int j=0;j<bean.getChildNodes().getLength();j++){
if (!(bean.getChildNodes().item(j) instanceof Element)) continue;
if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) continue;
// 解析标签:property
Element property = (Element) bean.getChildNodes().item(j);
String attrName = property.getAttribute("name");
String attrValue = property.getAttribute("value");
String attrRef = property.getAttribute("ref");
// 获取属性值:引入对象、值对象
Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrRef;
// 创建属性信息
PropertyValue propertyValue = new PropertyValue(attrName,value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
if(getRegistry().containsBeanDefinition(beanName)){
throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
getRegistry().registerBeanDefinition(beanName,beanDefinition);
}
}
}
测试
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
@Test
public void test_classpath(){
// 1.初始化 BeanFactory registry
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2. 读取配置文件&注册Bean
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("classpath:spring.xml");
// 3. 获取Bean对象调用方法 (beanFactory.registryBeanDefinition) getBean -> createBean -> applyProperties
UserService userService = (UserService) beanFactory.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果: " + result);
}