手寫Spring框架之IOC

簡介
本篇博客主要實現(xiàn)兩個功能: Bean容器 和 IOC.
Bean容器
Bean容器也就是Spring容器, 在學(xué)習完Spring之后, 如果要我們用一句話來形容Spring, 我們經(jīng)常會說: Spring是一個容器, 管理著應(yīng)用中所有bean的裝配和生命周期. 從這句話中就可以看出Spring容器的重要性, Spring容器其實是一個Map映射, 里面存儲了應(yīng)用中所有bean的實例, key為該bean實例的Class對象. Spring有兩種容器, 分別是 BeanFactory 和 ApplicationContext, 二者的區(qū)別在于, BeanFactory采用延遲加載策略, 在第一次調(diào)用getBean()時才真正裝配該對象. 而 ApplicationContext會在應(yīng)用啟動時就把所有對象一次性全部裝配好.
handwritten-mvc-framwork 框架的bean容器是一個 ApplicationContext 式的容器.
IOC
IOC的實現(xiàn)思路如下:
首先有一個配置文件定義了應(yīng)用的基礎(chǔ)包, 也就是Java源碼路徑.
讀取基礎(chǔ)包名, 然后通過類加載器獲取到應(yīng)用中所有的Class對象, 存儲到一個集合中.
獲取應(yīng)用中所有Bean (Controller和Service) 的Class對象, 通過反射創(chuàng)建實例, 然后存儲到 Bean容器中.
遍歷Bean容器中的所有Bean, 為所有帶 @Autowired 注解的屬性注入實例.
IOC操作要在應(yīng)用啟動時就完成, 所以必須寫在靜態(tài)代碼塊中.
handwritten-mvc-framwork 實現(xiàn)
定義注解
(1) 處理器注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface?Controller {
}(2) 處理器方法注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public?@interface?RequestMapping {
????/**
?????* 請求路徑
?????* @return
?????*/
????String?value() default?"";
????/**
?????* 請求方法
?????* @return
?????*/
????RequestMethod method() default?RequestMethod.GET;
}
//請求方法枚舉類
public?enum?RequestMethod {
????GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}(3) 依賴注入注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface?Autowired {
}(4) 業(yè)務(wù)類注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface?Service {
}配置文件
handwritten-mvc-framwork 框架是不存在配置文件的, 配置文件是用戶需要自定義一些配置項, 所以創(chuàng)建一個配置文件, 而框架需要做的就是讀取用戶自定義的配置文件, 如果用戶沒有配置, 就使用默認的配置. handwritten-mvc-example 實例中的一個配置文件如下:
#數(shù)據(jù)源
handwritten.framework.jdbc.driver=com.mysql.jdbc.Driver
handwritten.framework.jdbc.url=jdbc:mysql://localhost:3306/tyshawn_test
handwritten.framework.jdbc.username=root
handwritten.framework.jdbc.password=123
#java源碼路徑
handwritten.framework.app.base_package=com.tyshawn
#jsp頁面路徑
handwritten.framework.app.jsp_path=/WEB-INF/view/
#靜態(tài)資源路徑
handwritten.framework.app.asset_path=/asset/那 handwritten-mvc-framwork 框架如何來加載用戶自定義的配置文件呢?
(1) ConfigConstant 常量接口
首先我們要定義一個名為 ConfigConstant 的常量接口, 讓它來維護配置文件中相關(guān)的配置項名稱, 代碼如下:
public?interface?ConfigConstant {
????//配置文件的名稱
????String?CONFIG_FILE = "handwritten.properties";
????//數(shù)據(jù)源
????String?JDBC_DRIVER = "handwritten.framework.jdbc.driver";
????String?JDBC_URL = "handwritten.framework.jdbc.url";
????String?JDBC_USERNAME = "handwritten.framework.jdbc.username";
????String?JDBC_PASSWORD = "handwritten.framework.jdbc.password";
????//java源碼地址
????String?APP_BASE_PACKAGE = "handwritten.framework.app.base_package";
????//jsp頁面路徑
????String?APP_JSP_PATH = "handwritten.framework.app.jsp_path";
????//靜態(tài)資源路徑
????String?APP_ASSET_PATH = "handwritten.framework.app.asset_path";
}(2) PropsUtil 工具類
然后使用 PropsUtil 工具類來讀取屬性文件
public?final class?PropsUtil?{
????private?static?final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);
????/**
?????* 加載屬性文件
?????*/
????public?static?Properties loadProps(String fileName) {
????????Properties props = null;
????????InputStream is?= null;
????????try?{
????????????is?= ClassUtil.getClassLoader().getResourceAsStream(fileName);
????????????if?(is?== null) {
????????????????throw?new?FileNotFoundException(fileName + " file is not found");
????????????}
????????????props = new?Properties();
????????????props.load(is);
????????} catch?(IOException e) {
????????????LOGGER.error("load properties file failure", e);
????????} finally?{
????????????if?(is?!= null) {
????????????????try?{
????????????????????is.close();
????????????????} catch?(IOException e) {
????????????????????LOGGER.error("close input stream failure", e);
????????????????}
????????????}
????????}
????????return?props;
????}
????/**
?????* 獲取 String 類型的屬性值(默認值為空字符串)
?????*/
????public?static?String getString(Properties props, String key) {
????????return?getString(props, key, "");
????}
????/**
?????* 獲取 String 類型的屬性值(可指定默認值)
?????*/
????public?static?String getString(Properties props, String key, String defaultValue) {
????????String value?= defaultValue;
????????if?(props.containsKey(key)) {
????????????value?= props.getProperty(key);
????????}
????????return?value;
????}
????/**
?????* 獲取 int 類型的屬性值(默認值為 0)
?????*/
????public?static?int?getInt(Properties props, String key) {
????????return?getInt(props, key, 0);
????}
????/**
?????* 獲取 int 類型的屬性值(可指定默認值)
?????*/
????public?static?int?getInt(Properties props, String key, int?defaultValue) {
????????int?value?= defaultValue;
????????if?(props.containsKey(key)) {
????????????value?= Integer.parseInt(props.getProperty(key));
????????}
????????return?value;
????}
????/**
?????* 獲取 boolean 類型屬性(默認值為 false)
?????*/
????public?static?boolean getBoolean(Properties props, String key) {
????????return?getBoolean(props, key, false);
????}
????/**
?????* 獲取 boolean 類型屬性(可指定默認值)
?????*/
????public?static?boolean getBoolean(Properties props, String key, boolean defaultValue) {
????????boolean value?= defaultValue;
????????if?(props.containsKey(key)) {
????????????value?= Boolean.parseBoolean(props.getProperty(key));
????????}
????????return?value;
????}
}(3) ConfigHelper 助手類
最后借助 PropsUtil 工具類來實現(xiàn) ConfigHelper 助手類, 框架通過 ConfigHelper 類就可以加載用戶自定義的配置文件了, 從代碼中可以看到, 部分配置項擁有默認值, 當用戶沒有自定義時將會使用默認配置.
public?final class?ConfigHelper?{
????/**
?????* 加載配置文件的屬性
?????*/
????private?static?final Properties CONFIG_PROPS = PropsUtil.loadProps(ConfigConstant.CONFIG_FILE);
????/**
?????* 獲取 JDBC 驅(qū)動
?????*/
????public?static?String getJdbcDriver()?{
????????return?PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_DRIVER);
????}
????/**
?????* 獲取 JDBC URL
?????*/
????public?static?String getJdbcUrl()?{
????????return?PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_URL);
????}
????/**
?????* 獲取 JDBC 用戶名
?????*/
????public?static?String getJdbcUsername()?{
????????return?PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_USERNAME);
????}
????/**
?????* 獲取 JDBC 密碼
?????*/
????public?static?String getJdbcPassword()?{
????????return?PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_PASSWORD);
????}
????/**
?????* 獲取應(yīng)用基礎(chǔ)包名
?????*/
????public?static?String getAppBasePackage()?{
????????return?PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_BASE_PACKAGE);
????}
????/**
?????* 獲取應(yīng)用 JSP 路徑
?????*/
????public?static?String getAppJspPath()?{
????????return?PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_JSP_PATH, "/WEB-INF/view/");
????}
????/**
?????* 獲取應(yīng)用靜態(tài)資源路徑
?????*/
????public?static?String getAppAssetPath()?{
????????return?PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_ASSET_PATH, "/asset/");
????}
????/**
?????* 根據(jù)屬性名獲取 String 類型的屬性值
?????*/
????public?static?String getString(String key)?{
????????return?PropsUtil.getString(CONFIG_PROPS, key);
????}
????/**
?????* 根據(jù)屬性名獲取 int 類型的屬性值
?????*/
????public?static?int?getInt(String key)?{
????????return?PropsUtil.getInt(CONFIG_PROPS, key);
????}
????/**
?????* 根據(jù)屬性名獲取 boolean 類型的屬性值
?????*/
????public?static?boolean getBoolean(String key)?{
????????return?PropsUtil.getBoolean(CONFIG_PROPS, key);
????}
}Class對象集合
在完成了第一步加載配置文件之后, 我們接下來的第二步就是將應(yīng)用中所有的Class對象都存儲到一個集合中.
(1) ClassUtil 工具類
ClassUtil 工具類可以通過加載全限定類名得到Class類, 以及獲取指定包名下的所有Class類.
public?final class?ClassUtil {
????private?static?final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);
????/**
?????* 獲取類加載器
?????*/
????public?static?ClassLoader getClassLoader() {
????????return?Thread.currentThread().getContextClassLoader();
????}
????/**
?????* 加載類
?????* @param className 類名
?????* @param isInitialized 是否初始化
?????* @return
?????*/
????public?static?Class> loadClass(String?className, boolean?isInitialized) {
????????Class> cls;
????????try?{
????????????cls = Class.forName(className, isInitialized, getClassLoader());
????????} catch?(ClassNotFoundException e) {
????????????LOGGER.error("load class failure", e);
????????????throw?new?RuntimeException(e);
????????}
????????return?cls;
????}
????/**
?????* 加載類(默認將初始化類)
?????*/
????public?static?Class> loadClass(String?className) {
????????return?loadClass(className, true);
????}
????/**
?????* 獲取指定包名下的所有類
?????*/
????public?static?Set> getClassSet(String?packageName) {
????????Set> classSet = new?HashSet>();
????????try?{
????????????Enumeration urls = getClassLoader().getResources(packageName.replace(".", "/"));
????????????while?(urls.hasMoreElements()) {
????????????????URL url = urls.nextElement();
????????????????if?(url != null) {
????????????????????String?protocol = url.getProtocol();
????????????????????if?(protocol.equals("file")) {
????????????????????????String?packagePath = url.getPath().replaceAll("%20", " ");
????????????????????????addClass(classSet, packagePath, packageName);
????????????????????} else?if?(protocol.equals("jar")) {
????????????????????????JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
????????????????????????if?(jarURLConnection != null) {
????????????????????????????JarFile jarFile = jarURLConnection.getJarFile();
????????????????????????????if?(jarFile != null) {
????????????????????????????????Enumeration jarEntries = jarFile.entries();
????????????????????????????????while?(jarEntries.hasMoreElements()) {
????????????????????????????????????JarEntry jarEntry = jarEntries.nextElement();
????????????????????????????????????String?jarEntryName = jarEntry.getName();
????????????????????????????????????if?(jarEntryName.endsWith(".class")) {
????????????????????????????????????????String?className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
????????????????????????????????????????doAddClass(classSet, className);
????????????????????????????????????}
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????}
????????????????}
????????????}
????????} catch?(Exception e) {
????????????LOGGER.error("get class set failure", e);
????????????throw?new?RuntimeException(e);
????????}
????????return?classSet;
????}
????private?static?void?addClass(Set> classSet, String?packagePath, String?packageName) {
????????File[] files = new?File(packagePath).listFiles(new?FileFilter() {
????????????public?boolean?accept(File file) {
????????????????return?(file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
????????????}
????????});
????????for?(File file : files) {
????????????String?fileName = file.getName();
????????????if?(file.isFile()) {
????????????????String?className = fileName.substring(0, fileName.lastIndexOf("."));
????????????????if?(StringUtils.isNotEmpty(packageName)) {
????????????????????className = packageName + "."?+ className;
????????????????}
????????????????doAddClass(classSet, className);
????????????} else?{
????????????????String?subPackagePath = fileName;
????????????????if?(StringUtils.isNotEmpty(packagePath)) {
????????????????????subPackagePath = packagePath + "/"?+ subPackagePath;
????????????????}
????????????????String?subPackageName = fileName;
????????????????if?(StringUtils.isNotEmpty(packageName)) {
????????????????????subPackageName = packageName + "."?+ subPackageName;
????????????????}
????????????????addClass(classSet, subPackagePath, subPackageName);
????????????}
????????}
????}
????private?static?void?doAddClass(Set> classSet, String?className) {
????????Class> cls = loadClass(className, false);
????????classSet.add(cls);
????}
} (2) ClassHelper 助手類
借助 ClassUtil 來實現(xiàn) ClassHelper 助手類, 這個類的功能很重要, 大家需要仔細看一下. ClassHelper 助手類在自身被加載的時候通過 ConfigHelper 助手類獲取應(yīng)用的基礎(chǔ)包名, 然后通過 ClassUtil 工具類來獲取基礎(chǔ)包名下所有類, 存儲到 CLASS_SET 集合中. 除此之外, 其他的方法在后面的代碼中會經(jīng)常被使用到.
public?final class?ClassHelper?{
????/**
?????* 定義類集合(存放基礎(chǔ)包名下的所有類)
?????*/
????private?static?final Set> CLASS_SET;
????static?{
????????//獲取基礎(chǔ)包名
????????String basePackage = ConfigHelper.getAppBasePackage();
????????//獲取基礎(chǔ)包名下所有類
????????CLASS_SET = ClassUtil.getClassSet(basePackage);
????}
????/**
?????* 獲取基礎(chǔ)包名下的所有類
?????*/
????public?static?Set> getClassSet() {
????????return?CLASS_SET;
????}
????/**
?????* 獲取基礎(chǔ)包名下所有 Service 類
?????*/
????public?static?Set> getServiceClassSet() {
????????Set> classSet = new?HashSet>();
????????for?(Class> cls : CLASS_SET) {
????????????if?(cls.isAnnotationPresent(Service.class)) {
????????????????classSet.add(cls);
????????????}
????????}
????????return?classSet;
????}
????/**
?????* 獲取基礎(chǔ)包名下所有 Controller 類
?????*/
????public?static?Set> getControllerClassSet() {
????????Set> classSet = new?HashSet>();
????????for?(Class> cls : CLASS_SET) {
????????????if?(cls.isAnnotationPresent(Controller.class)) {
????????????????classSet.add(cls);
????????????}
????????}
????????return?classSet;
????}
????/**
?????* 獲取基礎(chǔ)包名下所有 Bean 類(包括:Controller、Service)
?????*/
????public?static?Set> getBeanClassSet() {
????????Set> beanClassSet = new?HashSet>();
????????beanClassSet.addAll(getServiceClassSet());
????????beanClassSet.addAll(getControllerClassSet());
????????return?beanClassSet;
????}
????/**
?????* 獲取基礎(chǔ)包名下某父類的所有子類 或某接口的所有實現(xiàn)類
?????*/
????public?static?Set> getClassSetBySuper(Class> superClass) {
????????Set> classSet = new?HashSet>();
????????for?(Class> cls : CLASS_SET) {
????????????//isAssignableFrom() 指 superClass 和 cls 是否相同或 superClass 是否是 cls 的父類/接口
????????????if?(superClass.isAssignableFrom(cls) && !superClass.equals(cls)) {
????????????????classSet.add(cls);
????????????}
????????}
????????return?classSet;
????}
????/**
?????* 獲取基礎(chǔ)包名下帶有某注解的所有類
?????*/
????public?static?Set> getClassSetByAnnotation(Class extends Annotation> annotationClass) {
????????Set> classSet = new?HashSet>();
????????for?(Class> cls : CLASS_SET) {
????????????if?(cls.isAnnotationPresent(annotationClass)) {
????????????????classSet.add(cls);
????????????}
????????}
????????return?classSet;
????}
} Bean容器
在將應(yīng)用中所有的Class對象都存儲到 CLASS_SET 集合中之后, 我們就可以來構(gòu)建Bean容器了.
(1) ReflectionUtil 工具類
我們需要一個反射工具類, 進行各種反射操作.
public?final class?ReflectionUtil {
????private?static?final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class);
????/**
?????* 創(chuàng)建實例
?????*/
????public?static?Object?newInstance(Class> cls) {
????????Object?instance;
????????try?{
????????????instance = cls.newInstance();
????????} catch?(Exception e) {
????????????LOGGER.error("new instance failure", e);
????????????throw?new?RuntimeException(e);
????????}
????????return?instance;
????}
????/**
?????* 創(chuàng)建實例(根據(jù)類名)
?????*/
????public?static?Object?newInstance(String?className) {
????????Class> cls = ClassUtil.loadClass(className);
????????return?newInstance(cls);
????}
????/**
?????* 調(diào)用方法
?????*/
????public?static?Object?invokeMethod(Object?obj, Method method, Object... args) {
????????Object?result;
????????try?{
????????????method.setAccessible(true);
????????????result = method.invoke(obj, args);
????????} catch?(Exception e) {
????????????LOGGER.error("invoke method failure", e);
????????????throw?new?RuntimeException(e);
????????}
????????return?result;
????}
????/**
?????* 設(shè)置成員變量的值
?????*/
????public?static?void?setField(Object?obj, Field field, Object?value) {
????????try?{
????????????field.setAccessible(true); //去除私有權(quán)限
????????????field.set(obj, value);
????????} catch?(Exception e) {
????????????LOGGER.error("set field failure", e);
????????????throw?new?RuntimeException(e);
????????}
????}
}(2) Bean容器助手類
BeanHelper 在類加載時就會創(chuàng)建一個Bean容器 BEAN_MAP, 然后獲取到應(yīng)用中所有bean的Class對象, 再通過反射創(chuàng)建bean實例, 儲存到 BEAN_MAP 中.
public?final class?BeanHelper {
????/**
?????* BEAN_MAP相當于一個Spring容器, 擁有應(yīng)用所有bean的實例
?????*/
????private?static?final Map, Object> BEAN_MAP = new?HashMap, Object>();
????static?{
????????//獲取應(yīng)用中的所有bean
????????Set> beanClassSet = ClassHelper.getBeanClassSet();
????????//將bean實例化, 并放入bean容器中
????????for?(Class> beanClass : beanClassSet) {
????????????Object?obj = ReflectionUtil.newInstance(beanClass);
????????????BEAN_MAP.put(beanClass, obj);
????????}
????}
????/**
?????* 獲取 Bean 容器
?????*/
????public?static?Map, Object> getBeanMap() {
????????return?BEAN_MAP;
????}
????/**
?????* 獲取 Bean 實例
?????*/
????@SuppressWarnings("unchecked")
????public?static? T getBean(Class cls) {
????????if?(!BEAN_MAP.containsKey(cls)) {
????????????throw?new?RuntimeException("can not get bean by class: "?+ cls);
????????}
????????return?(T) BEAN_MAP.get(cls);
????}
????/**
?????* 設(shè)置 Bean 實例
?????*/
????public?static?void?setBean(Class> cls, Object?obj) {
????????BEAN_MAP.put(cls, obj);
????}
} 實現(xiàn) IOC 功能
最后就是實現(xiàn) IOC 了, 我們需要做的就是遍歷Bean容器中的所有bean, 為所有帶 @Autowired 注解的屬性注入實例. 這個實例從Bean容器中獲取.
public final class?IocHelper?{
????/**
?????* 遍歷bean容器所有bean的屬性, 為所有帶@Autowired注解的屬性注入實例
?????*/
????static?{
????????//遍歷bean容器里的所有bean
????????Map, Object> beanMap = BeanHelper.getBeanMap();
????????if?(MapUtils.isNotEmpty(beanMap)) {
????????????for?(Map.Entry, Object> beanEntry : beanMap.entrySet()) {
????????????????//bean的class類
????????????????Class> beanClass = beanEntry.getKey();
????????????????//bean的實例
????????????????Object?beanInstance = beanEntry.getValue();
????????????????//暴力反射獲取屬性
????????????????Field[] beanFields = beanClass.getDeclaredFields();
????????????????//遍歷bean的屬性
????????????????if?(ArrayUtils.isNotEmpty(beanFields)) {
????????????????????for?(Field beanField : beanFields) {
????????????????????????//判斷屬性是否帶Autowired注解
????????????????????????if?(beanField.isAnnotationPresent(Autowired.class)) {
????????????????????????????//屬性類型
????????????????????????????Class> beanFieldClass = beanField.getType();
????????????????????????????//如果beanFieldClass是接口, 就獲取接口對應(yīng)的實現(xiàn)類
????????????????????????????beanFieldClass = findImplementClass(beanFieldClass);
????????????????????????????//獲取Class類對應(yīng)的實例
????????????????????????????Object?beanFieldInstance = beanMap.get(beanFieldClass);
????????????????????????????if?(beanFieldInstance != null) {
????????????????????????????????ReflectionUtil.setField(beanInstance, beanField, beanFieldInstance);
????????????????????????????}
????????????????????????}
????????????????????}
????????????????}
????????????}
????????}
????}
????/**
?????* 獲取接口對應(yīng)的實現(xiàn)類
?????*/
????public static?Class> findImplementClass(Class> interfaceClass) {
????????Class> implementClass = interfaceClass;
????????//接口對應(yīng)的所有實現(xiàn)類
????????Set> classSetBySuper = ClassHelper.getClassSetBySuper(interfaceClass);
????????if?(CollectionUtils.isNotEmpty(classSetBySuper)) {
????????????//獲取第一個實現(xiàn)類
????????????implementClass = classSetBySuper.iterator().next();
????????}
????????return?implementClass;
????}
} 以上就是Bean容器和IOC功能的全部內(nèi)容, 當應(yīng)用啟動后, 就會生成Bean容器, 并實現(xiàn)IOC功能, 所有那些加了 @Autowired 注解的屬性, 別看他們在代碼中只是一個聲明, 其實在應(yīng)用啟動后他們都有實例啦!
原文鏈接:blog.csdn.net/litianxiang_kaola/article/details/86647022
