手写简易版Spring IOC容器02【学习】

接上篇文章中实现了通过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);
}

相关推荐

  1. 简易Spring IOC容器02学习

    2024-07-20 00:34:04       18 阅读
  2. 简易Spring IOC容器04学习

    2024-07-20 00:34:04       26 阅读
  3. 简易Spring IOC容器05学习

    2024-07-20 00:34:04       24 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 00:34:04       101 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 00:34:04       109 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 00:34:04       87 阅读
  4. Python语言-面向对象

    2024-07-20 00:34:04       96 阅读

热门阅读

  1. 新手教程---python-函数(新添加)

    2024-07-20 00:34:04       25 阅读
  2. Leetcode226.翻转二叉树

    2024-07-20 00:34:04       22 阅读