階段2:
// 1.編寫自己的Spring容器,實現掃描包,得到bean的class對象.2.掃描將 bean 信息封裝到 BeanDefinition對象,并放入到Map.
思路:
1.將 bean 信息封裝到 BeanDefinition對象中,再將其放入到BeanDefinitionMap集合中,集合的結構大概是
key[beanName]–value[beanDefintion]
key--------->對應指定的名字,未指定則以類的首字母小寫為其名字
value------->對應封裝好的BeanDefintion對象
2.因為bean的作用域可能是singleton,也可能是prototype,所以Spring需要掃描到bean信息,保存到集合,這樣當getBean()根據實際情況處理.
具體實現
1.加一個自定義Scope注解
package com.elf.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author 45~* @version 1.0* Scope 可以指定一個Bean的作用范圍[singleton,prototype]*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {//通過value可以指定singleton,prototypeString value() default "";
}
2.在MonsterService.java上加上@Scope多實例注解
package com.elf.spring.component;
import com.elf.spring.annotation.Component;
import com.elf.spring.annotation.Scope;/*** @author 45~* @version 1.0* 說明 MonsterService 是一個Servic*/
@Component //把MonsterService注入到我們自己的spring容器中
@Scope(value = "prototype")
public class MonsterService {}
3.準備ioc包下寫一個BeanDefinition.java 用于封裝記錄Bean信息.
package com.elf.spring.ioc;/*** @author 45~* @version 1.0* BeanDefinition 用于封裝和記錄Bean的信息 [1.scope 2.存放bean對應的Class對象,反射可以生成對應的對象]* 2:因為將來getBean()時有可能是多實例,有可能是動態生成的,還要存放bean的class對象*/
public class BeanDefinition {private String scope;private Class clazz;//存放bean的class對象public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}public Class getClazz() {return clazz;}public void setClazz(Class clazz) {this.clazz = clazz;}@Overridepublic String toString() {return "BeanDefinition{" +"scope='" + scope + '\'' +", clazz=" + clazz +'}';}
}
3.pom.xml文件引入jar包下的工具類commons-lang,完成首字母小寫的功能.而不用springframework自帶的StringUtils工具類
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.elf</groupId><artifactId>elf-myspring1207</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency></dependencies></project>
4.容器文件,把構造器里邊的方法抽取出來封裝成一個方法,直接在構造器中調用,使代碼簡潔.
這里完成生成BeanDefinition對象并放入到Map里面
添加內容1:
//定義屬性BeanDefinitionMap -> 存放BeanDefinition對象(多例對象)private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<>();//定義屬性SingletonObjects -> 存放單例對象 (跟原生容器的名字保持一致)//因為將來存放單例池的時候肯定要指定單例對象是對應哪個Bean的,所以k用String來充當//存放單例對象的類型是不確定的,可能是Dog,Cat,或者其他的對象,所以用Objectprivate ConcurrentHashMap<String,Object> singletonObjects =new ConcurrentHashMap<>();
添加內容2:
//先得到beanName(有可能通過經典4注解,例如Component注解的value值來指定)//1.得到類上的Component注解,此時的clazz已經是當前bean的class對象,通過類加載器得到的 反射知識Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);//2.得到配置的valueString beanName = componentAnnotation.value();if("".equals(beanName)){//如果沒有寫value,空串//將該類的類名首字母小寫作為beanName//StringUtils其實是在springframework的框架下面的類,而這里本身我就是要自己寫所以不用beanName = StringUtils.uncapitalize(className);}//3.將Bean的信息封裝到BeanDefinition對象中,然后將其放入到BeanDefinitionMap集合中BeanDefinition beanDefinition = new BeanDefinition();//!!!多看看這里多理解beanDefinition.setClazz(cla);//由類加載器通過反射得到對象,Class<?> cla = classLoader.loadClass(classFullName);//4.獲取Scope值if (cla.isAnnotationPresent(Scope.class)){//如果配置了Scope,就獲取它配置的值Scope scopeAnnotatiion = cla.getDeclaredAnnotation(Scope.class);beanDefinition.setScope(scopeAnnotatiion.value());}else{//如果沒有配置Scope,就以默認的值singletonbeanDefinition.setScope("singleton");}//將beanDefinitionMap對象放入MapbeanDefinitionMap.put(beanName,beanDefinition);}else {//如果該類沒有使用了@Component注解,說明是一個Spring beanSystem.out.println("這不是一個Spring bean" + cla + " 類名=" + className);}
容器文件
package com.elf.spring.ioc;import com.elf.spring.annotation.*;
import org.apache.commons.lang.StringUtils;import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;/*** @author 45~* @version 1.0*/
public class ElfSpringApplicationContext {//第一步,掃描包,得到bean的class對象,排除包下不是bean的,因此還沒有放到容器中//因為現在寫的spring容器比原先的基于注解的,要更加完善,所以不會直接把它放在ConcurrentHashMapprivate Class configClass;//定義屬性BeanDefinitionMap -> 存放BeanDefinition對象private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<>();//定義屬性SingletonObjects -> 存放單例對象 (跟原生容器的名字保持一致)//因為將來存放單例池的時候肯定要指定單例對象是對應哪個Bean的,所以k用String來充當//存放單例對象的類型是不確定的,可能是Dog,Cat,或者其他的對象,所以用Objectprivate ConcurrentHashMap<String,Object> singletonObjects =new ConcurrentHashMap<>();//構造器public ElfSpringApplicationContext(Class configClass) {beanDefinitionScan(configClass);//調用封裝方法,簡潔System.out.println("beanDefinitionMap=" + beanDefinitionMap);}//構造器結束//該方法完成對指定包的掃描,并將Bean信息封裝到BeanDefinition對象,再放入到Map中public void beanDefinitionScan(Class configClass){this.configClass = configClass;/**獲取要掃描的包:1.先得到ElfSpringConfig配置的 @ComponentScan(value= "com.elf.spring.component")2.通過 @ComponentScan的value => 即要掃描的包 **/ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);String path = componentScan.value();System.out.println("要掃描的包path=" + path);/*** 得到要掃描包下的所有資源(類.class)* 1.得到類的加載器 -> APP類加載器是可以拿到 target目錄下的classes所有文件的* 2.通過類的加載器獲取到要掃描的包的資源url => 類似一個路徑* 3.將要加載的資源(.class)路徑下的文件進行遍歷 => io*/ClassLoader classLoader = ElfSpringApplicationContext.class.getClassLoader();path = path.replace(".", "/"); // 把.替換成 /URL resource = classLoader.getResource(path);System.out.println("resource=" + resource);File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) { //把所有的文件都取出來System.out.println("============================");System.out.println("f.getAbsolutePath()=" + f.getAbsolutePath());String fileAbsolutePath = f.getAbsolutePath();//到target的classes目錄下了//這里只處理.class文件if (fileAbsolutePath.endsWith(".class")) {//1.獲取類名String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,fileAbsolutePath.indexOf(".class"));//2.獲取類的完整路徑(全類名)String classFullName = path.replace("/", ".") + "." + className;System.out.println("classFullName=" + classFullName);//3.判斷該類是否需要注入,就看是不是有注解@Component @Service @Contoller @Re....try {Class<?> cla = classLoader.loadClass(classFullName);if (cla.isAnnotationPresent(Component.class) ||cla.isAnnotationPresent(Controller.class) ||cla.isAnnotationPresent(Service.class) ||cla.isAnnotationPresent(Repository.class)) {//演示機制//如果該類使用了@Component注解,說明是一個Spring beanSystem.out.println("這是一個Spring bean=" + cla + " 類名=" + className);//先得到beanName(有可能通過經典4注解,例如Component注解的value值來指定)//1.得到類上的Component注解,此時的clazz已經是當前bean的class對象,通過類加載器得到的 反射知識Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);//2.得到配置的valueString beanName = componentAnnotation.value();if("".equals(beanName)){//如果沒有寫value,空串//將該類的類名首字母小寫作為beanName//StringUtils其實是在springframework的框架下面的類,而這里本身我就是要自己寫所以不用beanName = StringUtils.uncapitalize(className);}//3.將Bean的信息封裝到BeanDefinition對象中,然后將其放入到BeanDefinitionMap集合中BeanDefinition beanDefinition = new BeanDefinition();//!!!多看看這里多理解beanDefinition.setClazz(cla);//由類加載器通過反射得到對象,Class<?> cla = classLoader.loadClass(classFullName);//4.獲取Scope值if (cla.isAnnotationPresent(Scope.class)){//如果配置了Scope,就獲取它配置的值Scope scopeAnnotatiion = cla.getDeclaredAnnotation(Scope.class);beanDefinition.setScope(scopeAnnotatiion.value());}else{//如果沒有配置Scope,就以默認的值singletonbeanDefinition.setScope("singleton");}//將beanDefinitionMap對象放入MapbeanDefinitionMap.put(beanName,beanDefinition);}else {//如果該類沒有使用了@Component注解,說明是一個Spring beanSystem.out.println("這不是一個Spring bean" + cla + " 類名=" + className);}} catch (Exception e) {e.printStackTrace();}}}//遍歷文件for循環結束System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~");}}//編寫放法返回容器中的對象public Object getBean(String name) {return null;}
}
運行結果
beanDefinitionMap={
monsterService=BeanDefinition{scope=‘prototype’, clazz=class com.elf.spring.component.MonsterService},
monsterDao=BeanDefinition{scope=‘singleton’, clazz=class com.elf.spring.component.MonsterDao}
}
ok
這里存在一個問題:單例多例對象都是放在beanDefinitionMap, singletonObjects里沒有單例對象.