为什么要提供配置的方式呢,之前的内容中我们测试的时候都是通过代码来进行的:

下面看下平时使用的时候,通过配置是什么样的: 可以看出,提供配置的方式的优点:手写Spring Config,最终一战,来瞅瞅撒!_Spring需要定义什么样的XML标记和注解呢?通过之前的内容知道,配置的内容就是Bean定义信息,那么Bean定义的内容就是需要配置的内容

XML配置的方式,首先需要定义一个DTD或者XSD文档,来定义一套标记信息,去指定Bean定义

可以看出,bean的配置指定的内容就是Bean定义接口中的信息

Bean配置的解析

手写Spring Config,最终一战,来瞅瞅撒!_Spring_02

  1. 注解方式的实现

手写Spring Config,最终一战,来瞅瞅撒!_Spring_03

那么可以使用外观模式,让用户只需要知道ApplicationContext和其子类就行了,ApplicationContext可以继承BeanFactory,继而把两个接口合在一起:

/**
 * @className: AbstractApplicationContext
 * @description: ApplicationContext的抽象类实现
 * @author: TR
 */
public abstract class AbstractApplicationContext implements ApplicationContext {

    /** 用组合的方式来持有BeanFactory,完成BeanFactory接口的方法 */
    protected BeanFactory beanFactory;

    public AbstractApplicationContext() {
        super();
        this.beanFactory = new PreBuildBeanFactory();
    }

    public AbstractApplicationContext(BeanFactory beanFactory) {
        super();
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getBean(String beanName) throws Exception {
        return this.beanFactory.getBean(beanName);
    }

    @Override
    public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        this.beanFactory.registerBeanPostProcessor(beanPostProcessor);
    }
}

xml配置方式的ApplicationContext实现类

XML方式

XML文件来源的处理

不同来源的XML文件,它的加载方式是不一样的,但是在解析的过程中,最后都希望获取到InputStream

/**
 * @className: Resource
 * @description: 输入流的资源扩展接口
 * @author: TR
 */
public interface Resource extends InputStreamSource {

    //classpath形式的xml配置文件
    String CLASS_PATH_PREFIX = "classpath:";

    //系统文件形式的xml配置文件
    String File_SYSTEM_PREFIX = "file:";

    /**
     * 判断资源是否存在
     * @return: boolean
     **/
    boolean exists();

    /**
     * 是否可读
     * @return: boolean
     **/
    boolean isReadable();

    /**
     * 是否打开
     * @return: boolean
     **/
    boolean isOpen();
    
    /**
     * 获取资源文件
     * @return: java.io.File
     **/
    File getFile();
}

InputStreamSource接口的实现类

 * @className: FileSystemResource
 * @description: 系统文件类型的资源实现类
 * @author: TR
 */
public class FileSystemResource implements Resource {

    /** 文件资源对象 */
    private File file;

    public FileSystemResource(String fileName) {
        super();
        this.file = new File(fileName);
    }

    public FileSystemResource(File file) {
        super();
        this.file = file;
    }

    @Override
    public boolean exists() {
        return this.file == null ? false : this.file.exists();
    }

    @Override
    public boolean isReadable() {
        return this.file == null ? false : this.file.canRead();
    }

    @Override
    public boolean isOpen() {
        return false;
    }

    @Override
    public File getFile() {
        return file;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return new FileInputStream(this.file);
    }
}

/**
 * @className: ClassPathResource
 * @description: classpath形式的资源实现类
 * @author: TR
 */
public class ClassPathResource implements Resource {

    //classpath所需要的信息
    private String path;

    private Class<?> clazz;

    private ClassLoader classLoader;

    public ClassPathResource(String path) {
        this(path, null );
    }

    public ClassPathResource(String path, Class<?> clazz) {
        this(path, clazz, null);
    }

    public ClassPathResource(String path, Class<?> clazz, ClassLoader classLoader) {
        super();
        this.path = path;
        this.clazz = clazz;
        this.classLoader = classLoader;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public void setClazz(Class<?> clazz) {
        this.clazz = clazz;
    }

    public ClassLoader getClassLoader() {
        return classLoader;
    }

    public void setClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public boolean exists() {
        if (StringUtils.isNotBlank(path)) {
            if (this.clazz != null) {
                return this.clazz.getResource(path) != null;
            }
            if (this.classLoader != null) {
                return this.classLoader.getResource(path.startsWith("/") ? path.substring(1) : path) != null;
            }
            return this.getClass().getResource(path) != null;
        }
        return false;
    }

    @Override
    public boolean isReadable() {
        return exists();
    }

    @Override
    public boolean isOpen() {
        return false;
    }

    @Override
    public File getFile() {
        return null;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        if (StringUtils.isNotBlank(path)) {
            if (this.clazz != null) {
                return this.clazz.getResourceAsStream(path);
            }
            if (this.classLoader != null) {
                return this.classLoader.getResourceAsStream(path.startsWith("/") ? path.substring(1) : path);
            }
            return this.getClass().getResourceAsStream(path);
        }
        return null;
    }
}

/**
 * @className: UrlResource
 * @description: URL形式的资源实现类
 * @author: TR
 */
public class UrlResource implements Resource {

    /** url的资源对象 */
    private URL url;

    public UrlResource(String url) throws IOException {
        this.url = new URL(url);
    }

    public UrlResource(URL url) {
        super();
        this.url = url;
    }

    public URL getUrl() {
        return url;
    }

    public void setUrl(URL url) {
        this.url = url;
    }

    @Override
    public boolean exists() {
        return this.url != null;
    }

    @Override
    public boolean isReadable() {
        return exists();
    }

    @Override
    public boolean isOpen() {
        return false;
    }

    @Override
    public File getFile() {
        return null;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return null;
    }
}

XML资源加载器

ResourceLoader接口:

在这里,还需要区分用户给的字符串代表的是哪种资源,所以需要定义字符串的规则:

扫描的包有哪些呢?

需要定义一个资源路径的匹配行为

扫描的类ClassPathBeanDefinitionScanner

解析成Bean定义

手写Spring Config,最终一战,来瞅瞅撒!_Spring_04

/**
 * @className: BeanDefinitionReader
 * @description: 将Resource资源解析成Bean定义的接口
 * @author: TR
 */
public interface BeanDefinitionReader {

    /**
     * 解析单个资源
     * @param resource:
     * @return: void
     **/
    void loadBeanDefintions(Resource resource) throws Throwable;

    /**
     * 解析多个资源
     * @param resource:
     * @return: void
     **/
    void loadBeanDefintions(Resource... resource) throws Throwable;
}

/**
 * @className: AbstractBeanDefinitionReader
 * @description: TODO
 * @date: 2021/6/10 15:58
 * @author: jinpeng.sun
 */
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {

    /** 持有BeanDefinitionRegistry接口,以便完成注册到BeanFactory中 */
    protected BeanDefinitionRegistry beanDefinitionRegistry;

    public AbstractBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super();
        this.beanDefinitionRegistry = beanDefinitionRegistry;
    }
}

/**
 * @className: XmlBeanDefinitionReader
 * @description: xml配置方式的bean定义解析器
 * @author: TR
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super(beanDefinitionRegistry);
    }

    @Override
    public void loadBeanDefintions(Resource resource) throws Throwable {
        this.loadBeanDefintions(new Resource[] {resource});
    }

    @Override
    public void loadBeanDefintions(Resource... resource) throws Throwable {
        if (resource != null && resource.length > 0) {
            for (Resource r : resource) {
                this.parseXml(r);
            }
        }
    }

    private void parseXml(Resource r) {
        //TODO 解析xml文档,获取bean定义,创建bean定义对象,注册到BeanDefinitionRegistry中
    }
}

 * @className: AnnotationBeanDefinitionReader
 * @description: 注解配置方式的bean定义解析器:
 * @author: TR
 */
public class AnnotationBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public AnnotationBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super(beanDefinitionRegistry);
    }

    @Override
    public void loadBeanDefintions(Resource resource) throws Throwable {
        this.loadBeanDefintions(new Resource[] {resource});
    }

    @Override
    public void loadBeanDefintions(Resource... resource) throws Throwable {
        if (resource != null && resource.length > 0) {
            for (Resource r : resource) {
                this.retriveAndRegistBeanDefinition(r);
            }
        }
    }

    private void retriveAndRegistBeanDefinition(Resource resource) {
        if(resource != null && resource.getFile() != null) {
            String className = getClassNameFormFile(resource.getFile());

            try {
                Class<?> clazz = Class.forName(className);
                Component component = clazz.getAnnotation(Component.class);
                if (component != null) {
                    GeneralBeanDefinition beanDefinition = new GeneralBeanDefinition();
                    beanDefinition.setBeanClass(clazz);
                    beanDefinition.setScope(component.scope());
                    beanDefinition.setFactoryMethodName(component.factoryMethodName());
                    beanDefinition.setFactoryBeanName(component.factoryBeanName());
                    beanDefinition.setInitMethodName(component.initMethodName());
                    beanDefinition.setDestroyMethodName(component.destroyMethodName());

                    //获取所有的构造方法,在构造方法上找Autowired注解,如果有的话,将这个构造方法set到bd
                    this.handleConstructor(clazz, beanDefinition);

                    //处理工厂方法参数依赖
                    if(StringUtils.isNotBlank(beanDefinition.getFactoryMethodName())) {
                        this.handleFactoryMethodArgs(clazz, beanDefinition);
                    }

                    //处理属性依赖
                    this.handlePropertyDi(clazz, beanDefinition);

                    String beanName = "".equals(component.value()) ? component.name() : null;
                    if (StringUtils.isBlank(beanName)) {
                        // TODO 应用名称生成规则生成beanName;
                        // 默认驼峰命名法
                        beanName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, clazz.getSimpleName());
                    }
                    // 注册bean定义
                    this.beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
                }
            } catch (ClassNotFoundException | BeanDefinitionException e) {
                e.printStackTrace();
            }

        }
    }

    private void handlePropertyDi(Class<?> clazz, GeneralBeanDefinition bd) {
        // TODO Auto-generated method stub

    }

    private void handleFactoryMethodArgs(Class<?> clazz, GeneralBeanDefinition bd) {
        // TODO Auto-generated method stub

    }

    private void handleConstructor(Class<?> clazz, GeneralBeanDefinition bd) {
        //获取所有的构造方法,在构造方法上找Autowired注解,如果有的话,将这个构造方法set到bd
        Constructor<?>[] constructors = clazz.getConstructors();
        if (constructors != null && constructors.length > 0) {
            for (Constructor c : constructors) {
                if (c.getAnnotation(Autowired.class) != null) {
                    bd.setConstructor(c);
                    Parameter[] ps = c.getParameters();
                    //遍历获取参数上的注解,及创建参数依赖
                    break;
                }
            }
        }
    }

    private int classPathAbsLength = AnnotationBeanDefinitionReader.class.getResource("/").toString().length();

    private String getClassNameFormFile(File file) {
        //返回绝对路径名字符串
        String absPath = file.getAbsolutePath();
        String name = absPath.substring(classPathAbsLength+1, absPath.indexOf("."));
        return StringUtils.replace(name, File.separator, ".");
    }
}

public class XmlApplicationContext extends AbstractApplicationContext {

    private List<Resource> resources;

    private BeanDefinitionReader definitionReader;

    public XmlApplicationContext(String... locations) throws Throwable {
        super();
        load(locations);
        //资源解析成BeanDefinition,外派给BeanDefinitionReader接口来实现
        this.definitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) this.beanFactory);
        Resource[] resourceArray = new Resource[resources.size()];
        resources.toArray(resourceArray);
        //将解析后的BeanDefinition装载到BeanFactory中
        definitionReader.loadBeanDefintions(resourceArray);
    }

    /**
     * 根据用户指定的配置文件位置,加载资源信息
     * @param locations:
     * @return: void
     **/
    private void load(String[] locations) throws IOException {
        if (resources == null) {
            resources = new ArrayList<Resource>();
        }
        //完成加载,创建好Resource
        if (locations != null && locations.length > 0) {
            for (String lo : locations) {
                Resource resource = getResource(lo);
                if (resource != null) {
                    this.resources.add(resource);
                }
            }
        }
    }

    @Override
    public Resource getResource(String location) throws IOException {
        if (StringUtils.isNotBlank(location)) {
            //根据字符串的前缀判断区分,class、系统文件、url三种资源的加载
            if (location.startsWith(Resource.CLASS_PATH_PREFIX)) {
                return new ClassPathResource(location.substring(Resource.CLASS_PATH_PREFIX.length()));
            } else if (location.startsWith(Resource.File_SYSTEM_PREFIX)) {
                return new FileSystemResource(location.substring(Resource.File_SYSTEM_PREFIX.length()));
            } else {
                return new UrlResource(location);
            }
        }
        return null;
    }
}

手写Spring Config,最终一战,来瞅瞅撒!_Spring_05