Spring手撸源码系列-实现资源加载根据Spring.xml解析和注册Bean

Spring手撸源码系列-实现资源加载根据Spring.xml解析和注册Bean,第1张

Spring手撸源码系列-实现资源加载根据Spring.xml解析和注册Bean

之前的章节都是通过代码注册bean,设置属性等 *** 作,这一章是在Spring框架里是采用xml形式定义Bean对象,以及设置Bean的属性以及引用关系。

就是将以下的标红的框的代码移到xml文件去定义,然后通过加载xml文件,进行解析文件,再进行bean的注册和属性的填充。

按上面改造方式,我们需要加载xml的功能,所以就会出现资源加载器,那么我们根据文件的加载,我们要既能支持ClassPath,还有本地file,以及云文件(http)的加载。这是资源加载的 *** 作。其次是解析xml的功能 *** 作,解析完毕调用上几章的Bean注册以及属性填充 *** 作即可。

1.工程项目

 test单元测试:

 

2.UML类图

 这一章节需要用到之前的章节,所以下面蓝色的类图是对应的需要更改或添加的内容。

 这一章节主要讲黄色类图的内容,这部分内容就是加载资源xml,解析xml,最后注册到Bean里,咱们先来说一下资源加载进来然后转换文件流方式(留到最后进行文件解析)

2.1 资源加载器

Resource

Resource获取文件流的接口,需要在解析文件数据之前进行获取文件流的入口的目的。

ClassPathResource

Resource接口的实现,这个类为类路径文件流的处理,所以实现的都是处理classpath下的文件的处理,最后输出流。

FileSystemResource

Resource接口的实现,这个类是本地文件流的处理,所以实现的都是根据路径去获取一个file文件最后得到文件流。

UrlResource

Resource接口的实现,这个类是用来处理http请求方式得到的文件流,所以实现是传进来一个http地址的文件得到文件流的方式。

ResourceLoader

此接口是资源加载的接口,主要是将获取上面介绍的Resource

DefaultResourceLoader

是ResourceLoader实现接口,实现getResource()方法,其实是获取调用Resource的入口,通过参数来实例化不同的Resource的实现类,比如:传递的参数是类路径,那么就new ClassPathResource();

以上是加载文件的 *** 作。

2.2 解析文件以及Bean注册

BeanDefinitionReader

此接口为加载资源解析bean的接口,所以定义了获取Bean注册方法,getRegistry(),也定义了获取Resource资源getResourceLoader(),用于在解析xml文件之前进行资源文件的获取。

定义了加载bean定义的方法,loadBeanDefinitions(),通过重载不同此方法用来根据不同的方式得到相同的Resource方式,最后进行根据资源进行解析。

AbstractBeanDefinitionReader

此抽象类继承BeanDefinitionReader,通过构造函数将BeanDefinitionRegistry和ResourceLoader获取到数据。

XmlBeanDefinitionReader

核心类,继承AbstractBeanDefinitionReader,实现其loadBeanDefinitions()方法,通过此方法获取Resource,并抽象出doLoadBeanDefinitions()方法,参数为Resource.getInputStream()获取文件流,根据文件流解析xml中的文件,获取Bean的Class类,Bean的属性,最后通过getRegistry()注册bean和属性的 *** 作,

2.3 原有类修改点

BeanDefinitionRegistry

此类里添加了一个判断Bean是否已经存在在容器里的方法,containsBeanDefinition(String beanName);

DefaultListableBeanFactory

作为BeanDefinitionRegistry的实现类,那肯定是要实现查询map里是否存在此key为beanName的数据并返回true或false。

BeanFactory

此类里添加了获取bean返回实体方式的getBean方法();

AbstractBeanFactory

作为BeanFactory的实现类,将获取到的getBean转换为泛型实体。

3.代码实现

  Resource:资源处理器,用来获取inputStream();

// Spring框架下core.io包主要用来处理资源加载流
// 资源加载接口
public interface Resource {
    // 获取流的方法
    InputStream getInputStream() throws IOException;
}

ClassPathResource:类文件下的文件加载方式

// 类路径文件流处理
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) {
        // spring的断言,如果空就不能往下走报错必须是不为空
        Assert.notNull(path, "Path must not be null");
        this.path = path;
        this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
    }

    // 获取文件流返回
    @Override
    public InputStream getInputStream() throws IOException {
        // 需要动态获取某个位置的文件可以获取文件的资源,可以采用classLoader.getResourceAsStream
        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(File file) {
        this.file = file;
        // 有文件就能获取路径
        this.path = file.getPath();
    }

    public FileSystemResource(String path) {
        // 有路径就能创建文件
        this.file = new File(path);
        this.path = path;
    }

    // 通过指定文件路径的方式读取文件信息。
    @Override
    public InputStream getInputStream() throws IOException {
        return new FileInputStream(this.file);
    }
}

UrlResource:http文件流加载方式

// 通过http方式读取文件,我们可以把文件放入不同的服务器或者云上
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:对于上面的资源的包装类,通过此类获取inputStream的入口

// 包装资源加载器
// 按照资源加载的不同方式,资源加载器可以把这些方式集中到统一的类服务下进行处理,外部用户只需要传递资源地址即可
public interface ResourceLoader {
    String CLASSPATH_URL_PREFIX = "classpath:";

    // 获取资源
    Resource getResource(String location);
}

DefaultResourceLoader:实现ResourceLoader的方法,根据不同的参数获取不同的Resource

public class DefaultResourceLoader implements ResourceLoader {

    // 根据不同的方式创建不同的资源
    @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 {
            URL url = null;
            try {
                url = new URL(location);
                return new UrlResource(url);
            } catch (MalformedURLException e) {
                return new FileSystemResource(location);
            }
        }
    }
}

以上是对资源加载的类和接口全部内容,下面是获取资源和解析xml的数据

BeanDefinitionReader:

// bean定义读取接口
public interface BeanDefinitionReader {

    BeanDefinitionRegistry getRegistry();

    ResourceLoader getResourceLoader();

    // getRegistry()与getResourceLoader()都是用于提供给这三个方法的工具,加载和注册
    void loadBeanDefinitions(Resource resource);

    void loadBeanDefinitions(Resource... resources);

    void loadBeanDefinitions(String location);
}

AbstractBeanDefinitionReader:实现BeanDefinitionReader的获取BeanDefinitionRegistry和ResourceLoader

// bean定义读取的准备工作,定义成abstract就可以任意实现需要的类,不想实现的就可不实现
// 此类主要是获取BeanDefinitionRegistry和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:继承AbstractBeanDefinitionReader类,最主要的方法为doLoadBeanDefinitions(),这个是通过获取Resource以后再次获取inputStream(),然后通过流得到Bean的id,name,以及属性等等信息的解析,解析完毕即可注册到beanDeffinition里,最后注册到容器里。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    protected XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) {
        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) {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    // 核心方法-从xml中取出bean和属性信息进行bean的注册
    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;
            // 判断对象
            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.forName装入类并对类做初始化
            Class clazz = Class.forName(classname);
            // 优先级 id > name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }

            // 将解析出来的Bean放入BeanDefination的beanClass
            BeanDefination beanDefination = new BeanDefination(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;
                // 解析标签
                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) : attrValue;
                // 创建属性信息
                PropertyValue propertyValue = new PropertyValue(attrName, value);
                // 添加到属性集合里去
                beanDefination.getPropertyValues().addPropertyValue(propertyValue);
            }
            if (getRegistry().containsBeanDefinition(beanName)) {
                throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
            }
            // 注册beanDefinition
            getRegistry().registerBeanDefinition(beanName, beanDefination);
        }
    }
}

对于上节需要修改的内容

beanFactory:bean工厂添加getBean()返回值为实体方法

public interface BeanFactory {
    Object getBean(String name) throws BeansException;

    // 04章节-添加,可以传参数
    Object getBean(String name,Object... args);

     T getBean(String name, Class requiredType) throws BeansException;

}

 AbstractBeanFactory:在此类里实现以下一个方法即可,其他不用动。

 @Override
    public  T getBean(String name, Class requiredType) throws BeansException {
        return (T) getBean(name);
    }

 BeanDefinitionRegistry:这个接口需要添加是否在容器中包含Bean对象

public interface BeanDefinitionRegistry {
    
    void registerBeanDefinition(String beanName, BeanDefination beanDefinition);

    
    boolean containsBeanDefinition(String beanName);
}

DefaultListableBeanFactory:此类添加此方法即可,其他不用动

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinationMap.containsKey(beanName);
    }
4.测试

测试前准备

UserDao类

public class UserDao {
    private static Map hashMap = new HashMap<>();

    static {
        hashMap.put("10001", "hahaha");
        hashMap.put("10002", "ss");
        hashMap.put("10003", "sio");
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }
}

 UserService类

public class UserService {
    private String uId;
    private UserDao userDao;

    public void queryUserInfo() {
        System.out.println("查询用户信息:" + userDao.queryUserName(uId));
    }

    public String getuId() {
        return uId;
    }

    public void setuId(String uId) {
        this.uId = uId;
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

单元测试

public class ApiTest06 {
    //
    @Test
    public void test_xml() {
        // 1.初始化BeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 2.读取配置文件&注册bean
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("classpath:spring.xml");

        // 3.获取Bean对象调用方法
        UserService userService = beanFactory.getBean("userService", UserService.class);
        userService.queryUserInfo();
    }
}

测试

 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5573275.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-14
下一篇 2022-12-14

发表评论

登录后才能评论

评论列表(0条)

保存