動手實作Spring的IOC功能(基于注解)
檔案結構

設計技術
主要設計兩大技能點:反射,單例模式
實作過程
首先從啟動spring開始,啟動spring需要一個ApplicationContext的類,這個類中傳入一個配置類,這個配置類主要是說明了包掃描的路徑
建立一個配備了包掃描路徑的類,掃描com/ssm/service下面的檔案
AppConfig檔案
@IComponentScan("com.ssm.service")
public class AppConfig {
}
還需要實作@IComponentScan的注解
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IComponentScan {
String value() default "";
}
另外再寫兩個Service,Service中需要三類注解,所有需要自己先實作這三類注解:@IComponent注解,@IAutowired注解和@Scope注解
實作@IComponent注解,主要作用是标注一個類為spring容器的bean,等下我需要講這個類執行個體化
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IComponent {
String value() default "";
}
實作@IAutowired注解,主要是在類中引入xxxservice執行個體
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.METHOD})
//可用在字段和構造方法和普通方法上面
public @interface IAutowired {
}
實作@Scope注解,我主要實作它的兩種方式
singleton單例模式:全局有且僅有一個執行個體,預設為單例模式
prototype原型模式:每次擷取bean的時候都會有一個新執行個體
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value() default "singleton";
}
UserService檔案如下
@IComponent("userService")
public class UserService {
}
OrderService檔案如下
先不用關注實作的接口InitializingBean和BeanNameAware,主要是OrderService中使用到@IAutowired注解拿到UserService對象
import com.spring.*;
/**
* @author ssm
*/
@IComponent("orderService")
@Scope("prototype")
public class OrderService implements InitializingBean, BeanNameAware {
@IAutowired
private UserService userService;
//這個beanName主要想要實作spring能夠自動将OrderService這個Bean的名字(即Component裡面的名字)指派給這個beanName
private String beanName;
public void test(){
//這裡測試是看輸出的這個userservice是否為空,如果不為空就說明我們寫的依賴注入功能實作了
System.out.println(userService);
}
@Override
public void afterPropertiesSet() {
//bean在生成的過程當中會調用這個方法
System.out.println("初始化");
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
//測試是否在生成bean時候自動指派了beanName
System.out.println("beanName: "+beanName);
}
}
最重要的部分是ApplicationContext的内容
我在代碼中做了詳細的注釋,主要就是:
掃描配置類,通過配置類上面的IComponent注解得到掃描的路徑,掃描這個路徑下的所有檔案并且得到每個檔案的Class對象,講Class對象存入List,從List中一個一個解析類,這裡的解析類主要是看類上面是否有IComponent注解,解析得到每個類的相關資訊(BeanName,BeanClass,Scope),将每個類的相關資訊存入beanDefinitionMap,beanDefinitionMap是map結構,key是BeanName,value是BeanDefinition,BeanDefinition是每個Bean相關的資訊
生成bean執行個體化,主要使用了反射調用構造方法,即beanClass.getDeclaredConstructor()生成執行個體,用beanClass.getDeclaredFields()來判斷類中的成員變量上是否有@IAutowired注解,如果有該注解則去調用getBean(beanName)方法,該方法從beanDefinitionMap中取bean的資訊來生成bean對象,getBean方法中拿到Bean執行個體後将其指派給字段上加了@IAutowired注解的對象
import com.ssm.service.UserService;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author ssm
*/
public class SSMApplicationContext {
//主要儲存了bean的資訊,比如儲存了class,scope,beanName
private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
//單例池,主要存着執行個體化出來的單例對象
private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
public SSMApplicationContext(Class configClass){
//構造方法主要是思考spring啟動的時候需要做什麼?
//1:掃描類
//2:建立bean
// 思考:spring啟動是所有bean都要建立嗎?主要建立哪種類型的bean呢?
// 是非懶加載的單例!啟動spring的時候就建立bean
//2:第二個步驟概括就是要生成單例bean,并且要把生成的bean放入單例池中
//掃描到類之後要幹什麼?解析這個類,具體解析些什麼資訊,比如有component注解
//掃描配置檔案下的類,得到所有類對象
List<Class> classList = scan(configClass);
for (Class clazz : classList) {
//一個一個解析類,将所有類對象的基本資訊存入beanDefinition
//ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap,key是beanName,value是BeanDefinition(主要是scope和beanClass)
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClass(clazz);
IComponent component = (IComponent) clazz.getAnnotation(IComponent.class);
String beanName = component.value();
if (clazz.isAnnotationPresent(Scope.class)){
Scope scope = (Scope) clazz.getAnnotation(Scope.class);
beanDefinition.setScope(scope.value());
}else {
beanDefinition.setScope("singleton");
}
//判斷掃描IComponent的類是不是實作了beanPostProcessor
if (BeanPostProcessor.class.isAssignableFrom(clazz)){
//判斷clazz是否是BeanPostProcessor的實作類/子類
try {
BeanPostProcessor bpp = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
beanPostProcessorList.add(bpp);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
beanDefinitionMap.put(beanName,beanDefinition);
}
for (String beanName: beanDefinitionMap.keySet()){
//beanDefinitionMap裡面存着bean執行個體化的基本資訊
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")){
//生成這個bean(和bean的生命周期有關,如下)
Object bean = createBean(beanName,beanDefinition);
singletonObjects.put(beanName,bean);//把建立出來的bean放入單例池中儲存起來
}
}
}
private Object createBean(String beanName, BeanDefinition beanDefinition) {
//生成這個bean(和bean的生命周期有關,如下)
Class beanClass = beanDefinition.getBeanClass();
try {
//執行個體化(用class調用構造方法來進行執行個體化)
Object bean = beanClass.getDeclaredConstructor().newInstance();
//填充屬性
Field[] fields = beanClass.getDeclaredFields();//思考:類中的什麼屬性是需要填充的
for (Field field : fields) {
if (field.isAnnotationPresent(IAutowired.class)){
//在字段上判斷是否加了Autowired注解
//思考:存在加了Autowired注解的字段,那我填充屬性,拿什麼東西填充呢?填充對象是?
//OrderService類中userService加了注解,那我應該拿一個UserService的對象去給這個加了注解的userservice指派
// UserService userService = (UserService) getBean(field.getName());
Object userService = getBean(field.getName());
field.setAccessible(true);//反射中必須設定true才可以通過反射通路字段,才能給執行個體化指派
field.set(bean,userService);//用userService給這個執行個體化的bean指派
}
}
//Aware
if (bean instanceof BeanNameAware){
//bean是否實作了BeanNameAwaren接口
//實作了則将bean進行強制類型轉化
((BeanNameAware)bean).setBeanName(beanName);
}
//...可實作程式員定義的邏輯
//可以實作多個BeanPostProcessor,是以循環
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean,beanName);
}
//初始化(instanceof隻能針對執行個體來寫,不能用于針對類來寫)
if (bean instanceof InitializingBean){
//bean是否實作了InitializingBean接口
//實作了則将bean進行強制類型轉化
((InitializingBean)bean).afterPropertiesSet();
}
//...可實作程式員定義的邏輯
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
}
return bean;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
public Object getBean(String beanName){
//還需要提供一個getBean方法,傳回值是Object
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("prototype")){
return createBean(beanName,beanDefinition);
}else {
//單例生成,從單例池中拿對象
Object bean = singletonObjects.get(beanName);
if (bean == null){
Object o = createBean(beanName,beanDefinition);
singletonObjects.put(beanName,o);
return o;
}
return bean;
}
}
private List<Class> scan(Class configClass) {
List<Class> classList = new ArrayList<>();
//掃描類,主要是為了得到掃描的路徑
//如何得到掃描路徑,先拿到Annocation注解,因為我們知道是IComponentScan注解,
// 是以這裡進行了強轉,然後調用value得到了掃描的路徑
IComponentScan iComponentScan = (IComponentScan) configClass.getAnnotation(IComponentScan.class);
String scanPath = iComponentScan.value();
// System.out.println(scanPath); com.ssm.service
//其實掃描是為了掃描目錄,但scanPath隻是得到了路徑,真的目錄的格式應該是com/ssm/service,是以需要轉換scanPath
scanPath = scanPath.replace(".","/");//粗暴轉為目錄格式,替換.為/
//思考:如何掃描類(使用類加載器,調用類加載器上的getResource來獲得)
ClassLoader classLoader = SSMApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(scanPath);
// System.out.println(resource);
//resource在沒有轉為檔案目錄的時候輸出是null,轉為檔案目錄以後輸出是 file:/C:/Users/SSM/Desktop/exercise/my_spring/target/classes/com/ssm/service
File file = new File(resource.getFile());
File[] files = file.listFiles();//掃描目錄下所有的檔案,什麼格式都會掃描進來
for (File f : files) {
//System.out.println(f);
//C:\Users\SSM\Desktop\exercise\my_spring\target\classes\com\ssm\service\OrderService.class
//C:\Users\SSM\Desktop\exercise\my_spring\target\classes\com\ssm\service\UserService.class
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
//System.out.println(absolutePath);
//再次把斜線變成點
absolutePath = absolutePath.replace("\\",".");//com.ssm.service.OrderService
try {
Class<?> clazz = classLoader.loadClass(absolutePath);//加載類
classList.add(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return classList;
}
}
BeanDefinition檔案如下:主要用來存儲bean資訊的
public class BeanDefinition {
private String scope;
private Class beanClass;
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
}
在上面的ApplicationContext中有以下代碼單獨摘出來如下:
//Aware
if (bean instanceof BeanNameAware){
//bean是否實作了BeanNameAwaren接口
//實作了則将bean進行強制類型轉化
((BeanNameAware)bean).setBeanName(beanName);
}
//...可實作程式員定義的邏輯
//可以實作多個BeanPostProcessor,是以循環
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean,beanName);
System.out.println(bean+"------------------------beanPostProcessor");
}
//初始化(instanceof隻能針對執行個體來寫,不能用于針對類來寫)
if (bean instanceof InitializingBean){
//bean是否實作了InitializingBean接口
//實作了則将bean進行強制類型轉化
((InitializingBean)bean).afterPropertiesSet();
}
//...可實作程式員定義的邏輯
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
System.out.println(bean+"------------------------beanPostProcessor");
}
這裡參考bean的生命周期,這裡主要實作了Aware接口,bean初始化,BeanPostProcessor接口,這部分代碼省略
最後附上代碼:https://gitee.com/vampire-boom/spring-ioc