1. 程式人生 > 其它 >手寫Spring Config,最終一戰,來瞅瞅撒!

手寫Spring Config,最終一戰,來瞅瞅撒!

上一篇說到了手寫Spring AOP,來進行功能的增強,下面本篇內容主要是手寫Spring Config。通過配置的方式來使用Spring

前面內容連結:

我自橫刀向天笑,手寫Spring IOC容器,快來Look Look!

手寫Spring DI依賴注入,嘿,你的益達!

手寫Spring AOP,快來瞧一瞧看一看撒!

配置分析

為什麼要提供配置的方式呢,之前的內容中我們測試的時候都是通過程式碼來進行的:

GeneralBeanDefinition bd = new GeneralBeanDefinition();
bd.setBeanClass(Lad.class);
List<Object> args = new ArrayList<>();
args.add("sunwukong");
args.add(new BeanReference("magicGril"));
bd.setConstructorArgumentValues(args);
bf.registerBeanDefinition("swk", bd);

bd = new GeneralBeanDefinition();
bd.setBeanClass(MagicGril.class);
args = new ArrayList<>();
args.add("baigujing");
bd.setConstructorArgumentValues(args);
bf.registerBeanDefinition("magicGril", bd);

下面看下平時使用的時候,通過配置是什麼樣的:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="girl" class="di.MagicGirl"
     init-method="start" destroy-method="end">
        <constructor-arg type="java.lang.String" value="girl"></constructor-arg>
        <property name="friend" ref="boy"></property>
    </bean>

    <bean id="boy" class="di.Lad">
        <constructor-arg type="java.lang.String" value="boy"></constructor-arg>
        <constructor-arg type="di.MagicGirl" value="girl"></constructor-arg>
    </bean>
</beans>

可以看出,提供配置的方式的優點:

  • 實用簡單,改動起來比較靈活
  • 而且不需要改動程式碼

常用的配置方式,就是XML和註解的形式,它們的工作過程如下:

配置的工作過程

定義XML標記和註解

需要定義什麼樣的XML標記和註解呢?通過之前的內容知道,配置的內容就是Bean定義資訊,那麼Bean定義的內容就是需要配置的內容

首先來看下Bean定義介面中有哪些資訊:

XML配置的方式,首先需要定義一個DTD或者XSD文件,來定義一套標記資訊,去指定Bean定義

<bean id="girl" class="di.MagicGirl"
     init-method="start" destroy-method="end">
        <constructor-arg type="java.lang.String" value="girl"></constructor-arg>
        <property name="friend" ref="boy"></property>
</bean>

可以看出,bean的配置指定的內容就是Bean定義介面中的資訊

註解的方式,需要定義一套註解,那麼需要哪些註解呢,也是Bean定義介面中的內容:

  • 指定類、指定BeanName、指定scope、指定工廠方法、指定工廠Bean、指定init method、指定destroy method,這些在我們使用Spring的時候是通過@Component來實現的
  • 指定構造引數的依賴:@Autowired、@Qualifier
  • 指定屬性依賴:@Value

Bean配置的解析

Bean配置的解析過程,需要單獨的介面來實現,而不是在BeanFactory中來做,要做到單一職責原則,所以需要定義單獨的介面來解析Bean配置,然後再向BeanFactory註冊Bean定義

ApplicationContext介面

ApplicationContext這個介面就是用來完成Bean配置解析的,上面說到實現配置的方式有XML和註解,所以會有兩個實現類來實現ApplicationContext介面

  1. XML方式的實現:
  • XML檔案可能存在多個,所以這裡使用了list
  • 需要完成:載入xml、解析xml、建立Bean定義、註冊Bean定義的任務
  1. 註解方式的實現
  • 掃描的包也會存在多個,這裡也使用list
  • 需要完成:掃描包、獲取註解、建立Bean定義、註冊Bean定義的任務

因為需要建立和註冊Bean定義,所以會使用到BeanFactory和BeanDefinitionRegistry介面,那麼這部分程式碼在子類中分別實現的話就會重複,所以抽象出來放在父類中:

使用者在使用的使用需要知道哪些介面和類呢?

  1. 指定配置相關:xml、註解
  2. 獲取bean相關:BeanFactory

那麼可以使用外觀模式,讓使用者只需要知道ApplicationContext和其子類就行了,ApplicationContext可以繼承BeanFactory,繼而把兩個介面合在一起:

ApplicationContext介面:

/**
 * @className: ApplicationContext
 * 用來構建整個應用環境的介面,用來完成Bean的配置和解析
 * 1:為了減少使用者對框架類介面的依賴,擴充套件了BeanFactory介面,
 * Bean的配置和Bean的獲取都可以通過ApplicationContext介面來完成
 * 2:配置資源的方式有xml和註解,所以存在xml和註解兩種子類的實現
 * 3. Bean配置解析首先需要載入,所以實現了配置資源Resource的載入介面ResourceLoader
 * @author: TR
 */
public interface ApplicationContext extends ResourceLoader,BeanFactory {

}

ApplicationContext的抽象類實現

/**
 * @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實現類

/**
 * @className: XmlApplicationContext
 * @description: xml配置方式的ApplicationContext實現類
 * @author: TR
 */
public class XmlApplicationContext extends AbstractApplicationContext {

}

註解配置方式的ApplicationContext實現類

/**
 * @className: AnnotationApplicationContext
 * @description: 註解配置方式的ApplicationContext實現類
 * @author: TR
 */
public class AnnotationApplicationContext extends AbstractApplicationContext {

}

配置的實現

XML方式

XML檔案來源的處理

xml配置檔案的來源會有多種,比如:

不同來源的XML檔案,它的載入方式是不一樣的,但是在解析的過程中,最後都希望獲取到InputStream

這裡也需要設計一套介面,對於不同來源的XML檔案分別進行處理

InputStreamSource介面

/**
 * @className: InputStreamSource
 * @description: 配置方式的最終統一介面
 * @author: TR
 */
public interface InputStreamSource {

    /**
     * 最終要獲取的就是輸入流
     * @return: java.io.InputStream
     **/
    InputStream getInputStream() throws IOException;
}

Resource介面

/**
 * @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介面的實現類

FileSystemResource實現類:

/**
 * @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);
    }
}

ClassPathResource實現類:

/**
 * @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;
    }
}

UrlResource實現類:

/**
 * @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資源載入器

使用者給定資源時是一個字串,上面有三種資源,那麼誰去負責建立這些資源呢

這裡需要定義一個資源載入器,去分辨不同的資源,然後進行載入,這部分工作是由ApplicationContext來完成的,所以ApplicationContext需要繼承ResourceLoader介面

ResourceLoader介面:

/**
 * @className: ResourceLoader
 * 配置資源載入介面
 * 不同的配置方式,載入過程不一樣,所以需要抽象出來一個介面應對變化的部分
 * 雖然載入的方式不一樣,但是返回的資源結果是一樣的,都是Resource
 * @author: TR
 */
public interface ResourceLoader {

    /**
     * 載入資源
     * @param location:
     * @return: demo.context.Resource
     **/
    Resource getResource(String location) throws IOException;
}

在這裡,還需要區分使用者給的字串代表的是哪種資源,所以需要定義字串的規則:

註解方式

如何掃描的

掃描的包有哪些呢?

需要到指定的包目錄下找出所有的類檔案,而且要包含子孫包下的

需要定義一個資源路徑的匹配行為

掃描的結果

掃描到了包下的class檔案後,需要的是類名,而且掃描的是class檔案,直接使用上面的FileResource即可

掃描的類ClassPathBeanDefinitionScanner

/**
 * @className: ClassPathBeanDefinitionScanner
 * @description: 掃描class檔案
 * @author: TR
 */
public class ClassPathBeanDefinitionScanner {

    private static Log logger = LogFactory.getLog(ClassPathBeanDefinitionScanner.class);

    private BeanDefinitionRegistry registry;

    private BeanDefinitionReader reader;

    private PathMatcher pathMatcher = new AntPathMatcher();

    private String resourcePatter = "**/*.class";

    public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        super();
        this.registry = registry;
        this.reader = new AnnotationBeanDefinitionReader(registry);
    }

    /**
     * 掃描包的方法
     * @param basePackages:
     * @return: void
     **/
    public void scan(String... basePackages) throws Throwable {
        if (basePackages != null && basePackages.length > 0) {
            for (String b : basePackages) {
                this.reader.loadBeanDefintions(doScan(b));
            }
        }
    }

    /**
     * 將掃描的class轉為Resource
     * @param basePackage:
     * @return: demo.context.Resource[]
     **/
    private Resource[] doScan(String basePackage) throws IOException {
        // 掃描包下的類
        // 構造初步匹配模式串,= 給入的包串 + / + **/*.class,替換裡面的.為/
        String pathPattern = StringUtils.replace(basePackage, ".", "/") + "/" + this.resourcePatter;
        if (pathPattern.charAt(0) != '/') {
            pathPattern = "/" + pathPattern;
        }
        // 找出模式的根包路徑
        String rootPath = this.determineRootDir(pathPattern);
        // 得到檔名匹配的絕對路徑模式
        String fullPattern = this.getClass().getResource("/").toString() + pathPattern;
        // 根據根包理解得到根包對應的目錄
        File rootDir = new File(this.getClass().getResource(rootPath).toString());
        // 存放找到的類檔案的resource集合
        Set<Resource> scanedClassFileResources = new HashSet<>();
        // 呼叫doRetrieveMatchingFiles來掃描class檔案
        this.doRetrieveMatchingFiles(fullPattern, rootDir, scanedClassFileResources);
        return (Resource[]) scanedClassFileResources.toArray();
    }

    private String determineRootDir(String location) {
        int rootDirEnd = location.length();
        rootDirEnd = location.indexOf('*');
        int zi = location.indexOf('?');
        if (zi != -1 && zi < rootDirEnd) {
            rootDirEnd = location.lastIndexOf('/', zi);
        }
        if (rootDirEnd != -1) {
            return location.substring(0, rootDirEnd);
        } else {
            return location;
        }
    }

    /**
     * 遞迴找指定目錄下的所有類,匹配模式的加入到結果中。
     *
     * @param fullPattern
     * @param dir
     * @param result
     * @throws IOException
     */
    protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<Resource> result) throws IOException {
        if (logger.isTraceEnabled()) {
            logger.trace("Searching directory [" + dir.getAbsolutePath() + "] for files matching pattern ["
                    + fullPattern + "]");
        }
        for (File content : listDirectory(dir)) {
            String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
            if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
                if (!content.canRead()) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipping subdirectory [" + dir.getAbsolutePath()
                                + "] because the application is not allowed to read the directory");
                    }
                } else {
                    doRetrieveMatchingFiles(fullPattern, content, result);
                }
            }
            if (getPathMatcher().match(fullPattern, currPath)) {
                result.add(new FileSystemResource(content));
            }
        }
    }

    protected File[] listDirectory(File dir) {
        File[] files = dir.listFiles();
        if (files == null) {
            if (logger.isInfoEnabled()) {
                logger.info("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
            }
            return new File[0];
        }
        Arrays.sort(files, Comparator.comparing(File::getName));
        return files;
    }

    public BeanDefinitionRegistry getRegistry() {
        return registry;
    }

    public void setRegistry(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }

    public BeanDefinitionReader getReader() {
        return reader;
    }

    public void setReader(BeanDefinitionReader reader) {
        this.reader = reader;
    }

    public PathMatcher getPathMatcher() {
        return pathMatcher;
    }

    public void setPathMatcher(PathMatcher pathMatcher) {
        this.pathMatcher = pathMatcher;
    }

    public String getResourcePatter() {
        return resourcePatter;
    }

    public void setResourcePatter(String resourcePatter) {
        this.resourcePatter = resourcePatter;
    }
}

解析成Bean定義

XML和註解最終的輸出結果都是Resource,在這裡還需要把Resource解析成Bean定義資訊才行

需要定義介面來進行解析:

BeanDefinitionReader介面:

/**
 * @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;
}

AbstractBeanDefinitionReader抽象類:

/**
 * @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;
    }
}

xml配置方式的bean定義解析器:

/**
 * @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中
    }
}

註解配置方式的bean定義解析器:

 * @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, ".");
    }
}

完善XmlApplicationContext和AnnotationApplicationContext:

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;
    }
}
public class AnnotationApplicationContext extends AbstractApplicationContext {

    private ClassPathBeanDefinitionScanner scanner;

    public AnnotationApplicationContext(String... locations) throws Throwable {
        scanner = new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) this.beanFactory);
        scanner.scan(locations);
    }

    @Override
    public Resource getResource(String location) throws IOException {
        return null;
    }
}