日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

配置分析

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

GeneralBeanDefinition bd = new GeneralBeanDefinition();bd.setBeanClass(Lad.class);List 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);

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

Springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.XSD">

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

 

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

 

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


 

配置的工作過程定義XML標記和注解

需要定義什么樣的XML標記和注解呢?通過之前的內容知道,配置的內容就是Bean定義信息,那么Bean定義的內容就是需要配置的內容

首先來看下Bean定義接口中有哪些信息:


 

XML配置的方式,首先需要定義一個DTD或者XSD文檔,來定義一套標記信息,去指定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: TRpublic interface ApplicationContext extends ResourceLoader,BeanFactory {ApplicationContext的抽象類實現* @className: abstractApplicationContext* @description: ApplicationContext的抽象類實現* @author: TRpublic 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;@Overridepublic Object getBean(String beanName) throws Exception {return this.beanFactory.getBean(beanName);@Overridepublic void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {this.beanFactory.registerBeanPostProcessor(beanPostProcessor);xml配置方式的ApplicationContext實現類* @className: XmlApplicationContext* @description: xml配置方式的ApplicationContext實現類* @author: TRpublic class XmlApplicationContext extends AbstractApplicationContext {注解配置方式的ApplicationContext實現類* @className: AnnotationApplicationContext* @description: 注解配置方式的ApplicationContext實現類* @author: TRpublic class AnnotationApplicationContext extends AbstractApplicationContext {配置的實現XML方式XML文件來源的處理

xml配置文件的來源會有多種,比如:


 

不同來源的XML文件,它的加載方式是不一樣的,但是在解析的過程中,最后都希望獲取到InputStream

這里也需要設計一套接口,對于不同來源的XML文件分別進行處理


 

InputStreamSource接口* @className: InputStreamSource* @description: 配置方式的最終統一接口* @author: TRpublic interface InputStreamSource {* 最終要獲取的就是輸入流* @return: JAVA.io.InputStreamInputStream getInputStream() throws IOException;Resource接口* @className: Resource* @description: 輸入流的資源擴展接口* @author: TRpublic interface Resource extends InputStreamSource {//classpath形式的xml配置文件String CLASS_PATH_PREFIX = "classpath:";//系統文件形式的xml配置文件String File_SYSTEM_PREFIX = "file:";* 判斷資源是否存在* @return: booleanboolean exists();* 是否可讀* @return: booleanboolean isReadable();* 是否打開* @return: booleanboolean isOpen();* 獲取資源文件* @return: java.io.FileFile getFile();InputStreamSource接口的實現類

FileSystemResource實現類:

* @className: FileSystemResource* @description: 系統文件類型的資源實現類* @author: TRpublic 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;@Overridepublic boolean exists() {return this.file == null ? false : this.file.exists();@Overridepublic boolean isReadable() {return this.file == null ? false : this.file.canRead();@Overridepublic boolean isOpen() {return false;@Overridepublic File getFile() {return file;@Overridepublic InputStream getInputStream() throws IOException {return new FileInputStream(this.file);

ClassPathResource實現類:

* @className: ClassPathResource* @description: classpath形式的資源實現類* @author: TRpublic 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;@Overridepublic 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;@Overridepublic boolean isReadable() {return exists();@Overridepublic boolean isOpen() {return false;@Overridepublic File getFile() {return null;@Overridepublic 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: TRpublic 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;@Overridepublic boolean exists() {return this.url != null;@Overridepublic boolean isReadable() {return exists();@Overridepublic boolean isOpen() {return false;@Overridepublic File getFile() {return null;@Overridepublic InputStream getInputStream() throws IOException {return null;XML資源加載器

用戶給定資源時是一個字符串,上面有三種資源,那么誰去負責創建這些資源呢

這里需要定義一個資源加載器,去分辨不同的資源,然后進行加載,這部分工作是由ApplicationContext來完成的,所以ApplicationContext需要繼承ResourceLoader接口

ResourceLoader接口:

* @className: ResourceLoader* 配置資源加載接口* 不同的配置方式,加載過程不一樣,所以需要抽象出來一個接口應對變化的部分* 雖然加載的方式不一樣,但是返回的資源結果是一樣的,都是Resource* @author: TRpublic interface ResourceLoader {* 加載資源* @param location:* @return: demo.context.ResourceResource getResource(String location) throws IOException;

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


 

注解方式如何掃描的

掃描的包有哪些呢?

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


 

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

掃描的結果

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

掃描的類ClassPathBeanDefinitionScanner

* @className: ClassPathBeanDefinitionScanner* @description: 掃描class文件* @author: TRpublic 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: voidpublic 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 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 IOExceptionprotected void doRetrieveMatchingFiles(String fullPattern, File dir, Set 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: TRpublic interface BeanDefinitionReader {* 解析單個資源* @param resource:* @return: voidvoid loadBeanDefintions(Resource resource) throws Throwable;* 解析多個資源* @param resource:* @return: voidvoid loadBeanDefintions(Resource... resource) throws Throwable;

AbstractBeanDefinitionReader抽象類:

* @className: AbstractBeanDefinitionReader* @description: TODO* @date: 2021/6/10 15:58* @author: jinpeng.sunpublic 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: TRpublic class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {super(beanDefinitionRegistry);@Overridepublic void loadBeanDefintions(Resource resource) throws Throwable {this.loadBeanDefintions(new Resource[] {resource});@Overridepublic 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: TRpublic class AnnotationBeanDefinitionReader extends AbstractBeanDefinitionReader {public AnnotationBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {super(beanDefinitionRegistry);@Overridepublic void loadBeanDefintions(Resource resource) throws Throwable {this.loadBeanDefintions(new Resource[] {resource});@Overridepublic 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到bdthis.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 stubprivate void handleFactoryMethodArgs(Class clazz, GeneralBeanDefinition bd) {// TODO Auto-generated method stubprivate void handleConstructor(Class clazz, GeneralBeanDefinition bd) {//獲取所有的構造方法,在構造方法上找Autowired注解,如果有的話,將這個構造方法set到bdConstructor[] 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 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: voidprivate void load(String[] locations) throws IOException {if (resources == null) {resources = new ArrayList();//完成加載,創建好Resourceif (locations != null && locations.length > 0) {for (String lo : locations) {Resource resource = getResource(lo);if (resource != null) {this.resources.add(resource);@Overridepublic 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);@Overridepublic Resource getResource(String location) throws IOException {return null;

分享到:
標簽:Spring
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定