image
介紹
嘗試寫一個(gè)自己的Web框架,先模仿,再探索走出自己的道路,最終超越??蚣苡玫亩嗔?,但是寫框架是第一次,而且模仿的還是Java生態(tài)中的基石——Spring。先了解其思路,一步一步去實(shí)現(xiàn),或許性能不如正主,但是可以一點(diǎn)點(diǎn)靠近。學(xué)習(xí)然后模仿是通往大牛道路的第一步。
一.注解
- Autowired注解是自動(dòng)注入,作用是將實(shí)例注入到帶有@Autowired注解的變量中。
- Component注解是組件,作用是框架會(huì)掃描所有帶@Component的class并進(jìn)行實(shí)例化。
- ComponentSacn注解是掃描路徑,作用是設(shè)置框架要掃描的包路徑。
- Scope注解是單例/原型標(biāo)識(shí),作用是不加默認(rèn)是單例Bean,加了@Scope("prototype")后將是原型Bean(多例模式)。
1.1 Autowired
package xyz.hcworld.jubilant.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName: Autowired
* @Author: 張紅塵
* @Date: 2021-06-15
* @Version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Autowired {
/**
* @return 組件名
*/
String value() default "";
}
1.2 Component
package xyz.hcworld.jubilant.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 注冊(cè)Bean組件
*
* @ClassName: Component
* @Author: 張紅塵
* @Date: 2021-06-13
* @Version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Component {
/**
* @return 組件名
*/
String value() default "";
}
1.3 ComponentSacn
package xyz.hcworld.jubilant.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 掃描配置文件注解
*
* @ClassName: ComponentSacn
* @Author: 張紅塵
* @Date: 2021-06-13
* @Version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScan {
/**
* @return 掃描路徑
*/
String value() default "";
}
1.4 Scope
package xyz.hcworld.jubilant.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 單例Bean/原型Bean標(biāo)識(shí)
* @ClassName: Scope
* @Author: 張紅塵
* @Date: 2021-06-15
* @Version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Scope {
String value();
}
二、Bean操作
- BeanDefinition類是建模對(duì)象,通過建模對(duì)象創(chuàng)建實(shí)例。
- BeanNameAware接口是bean名的Aware回調(diào)接口,繼承接口可以獲取bean的benaName。
- BeanPostProcessor接口是初始化前后的增強(qiáng)接口,繼承接口可以對(duì)bean進(jìn)行動(dòng)態(tài)增強(qiáng),可以使用JDK增強(qiáng)或者CGLib增強(qiáng)。(可以通過這個(gè)接口實(shí)現(xiàn)AOP)。
- InitializingBean接口是應(yīng)用級(jí)的初始化接口,繼承該接口后框架實(shí)例化Bean后會(huì)對(duì)Bean進(jìn)行初始化操作。
2.1 BeanDefinition
package xyz.hcworld.jubilant.beans;
/**
* bean的配置信息
* @ClassName: BeanDefinition
* @Author: 張紅塵
* @Date: 2021-06-15
* @Version: 1.0
*/
public class BeanDefinition {
/**
* 類的類型
*/
private Class<?> clazz;
/**
* 作用域 是否是懶加載
*/
private String scope;
public Class<?> getClazz() {
return clazz;
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
2.2 BeanNameAware
package xyz.hcworld.jubilant.beans;
/**
* bean名的Aware回調(diào)接口
* @ClassName: BeanNameAware
* @Author: 張紅塵
* @Date: 2021-06-16
* @Version: 1.0
*/
public interface BeanNameAware {
/**
* 名字
* @param name
*/
void setBeanName(String name);
}
2.3 BeanPostProcessor
/**
* 后置處理器(bean初始化前后的增強(qiáng)處理)
*
* @ClassName: BeanPostProcessor
* @Author: 張紅塵
* @Date: 2021-06-16
* @Version: 1.0
*/
public interface BeanPostProcessor {
/**
* bean初始化前調(diào)用
*
* @param bean bean本身
* @param beanName bean的名字
* @return
*/
default Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
/**
* bean初始化后調(diào)用
*
* @param bean bean本身
* @param beanName bean的名字
* @return
*/
default Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
2.4 InitializingBean
package xyz.hcworld.jubilant.beans;
/**
* 初始化機(jī)制
* @ClassName: Initializingbean
* @Author: 張紅塵
* @Date: 2021-06-16
* @Version: 1.0
*/
public interface InitializingBean {
/**
* 初始化
* @throws Exception
*/
void afterPropertiesSet() throws Exception;
}
三.容器類
- Search類是搜索class的類,分別對(duì)jar與非jar兩種情況的class進(jìn)行掃描。
- JubilantApplicationContext是核心容器類,ioc是整個(gè)流程都在當(dāng)前容器進(jìn)行。流程如下:獲取所有class的路徑->掃描所有帶@Component的class->生成所有帶@Component的class的BeanDefinition對(duì)象-->存入BeanDefinitionMap-->遍歷BeanDefinitionMap生成所有單例Bean存入單例池。
3.1 掃描class模塊
package xyz.hcworld.jubilant.core;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* @ClassName: Search
* @Author: 張紅塵
* @Date: 2021-06-18
* @Version: 1.0
*/
public class Search {
/**
* 獲取某包下(包括該包的所有子包)所有類
*
* @param packageName 包名
* @return 類的完整名稱
*/
List<String> getClassName(String packageName) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL url = loader.getResource(packageName.replace(".", "/"));
if (url == null) {
return new ArrayList<>();
}
return "file".equals(url.getProtocol())?
getClassNameByFile(url.getPath(), null, packageName.replace(".", System.getProperty("file.separator")))
:getClassNameByJar(url.getPath());
}
/**
* 從項(xiàng)目文件獲取某包下所有類
*
* @param filePath 文件路徑
* @param className 類名集合
* @return 類的完整名稱
*/
private static List<String> getClassNameByFile(String filePath, List<String> className, String packageName) {
List<String> myClassName = new ArrayList<>();
File file = new File(filePath);
File[] childFiles = file.listFiles();
if (childFiles == null) {
return myClassName;
}
for (File childFile : childFiles) {
if (childFile.isDirectory()) {
myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, packageName));
continue;
}
String childFilePath = childFile.getPath();
if (childFilePath.endsWith(".class")) {
//截取路徑
childFilePath = childFilePath.substring(childFilePath.indexOf(packageName), childFilePath.lastIndexOf("."));
//將反斜杠轉(zhuǎn)成.
myClassName.add(childFilePath.replace(System.getProperty("file.separator"), "."));
}
}
return myClassName;
}
/**
* 從jar獲取某包下所有類
*
* @param jarPath jar文件路徑
* @return 類的完整名稱
*/
private static List<String> getClassNameByJar(String jarPath) {
List<String> myClassName = new ArrayList<>();
try (JarFile jarFile = new JarFile(System.getProperty("user.dir") + System.getProperty("file.separator") + System.getProperty("java.class.path"))) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String entryName = jarEntry.getName();
if (entryName.startsWith(jarPath) && entryName.endsWith(".class")) {
entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf(".class"));
myClassName.add(entryName);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return myClassName;
}
}
3.2 容器化
package xyz.hcworld.jubilant.core;
import xyz.hcworld.jubilant.beans.BeanDefinition;
import xyz.hcworld.jubilant.beans.BeanNameAware;
import xyz.hcworld.jubilant.beans.BeanPostProcessor;
import xyz.hcworld.jubilant.beans.InitializingBean;
import xyz.hcworld.jubilant.annotation.Autowired;
import xyz.hcworld.jubilant.annotation.Component;
import xyz.hcworld.jubilant.annotation.ComponentScan;
import xyz.hcworld.jubilant.annotation.Scope;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 容器類
*
* @ClassName: WinterApplicationContext
* @Author: 張紅塵
* @Date: 2021-06-13
* @Version: 1.0
*/
public class JubilantApplicationContext {
/**
* 配置文件
*/
private Class configClass;
/**
* 單例池,保存單例對(duì)象
*/
private final ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
/**
* 所有Bean的配置數(shù)據(jù)的配置池
*/
private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/**
* 初始化bean的類
*/
private final List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
/**
* 構(gòu)造方法
*
* @param configClass 配置類
*/
public JubilantApplicationContext(Class<?> configClass) {
this.configClass = configClass;
// 判斷是否有ComponentScan注解,如果沒有就結(jié)束不往下走
if (!configClass.isAnnotationPresent(ComponentScan.class)) {
return;
}
// 解析配置類
// 對(duì)ComponentScan注解解析 -->掃描路徑 -->掃描-->生成BeanDefinition對(duì)象-->存入BeanDefinitionMap
scan(configClass);
// Aspect類先實(shí)例化
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
if (!entry.getValue().getClazz().isAnnotationPresent(Aspect.class)) {
continue;
}
Object bean = createBean(entry.getKey(), entry.getValue());
singletonObjects.put(entry.getKey(), bean);
}
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
// 創(chuàng)建所有的單例bean,已經(jīng)實(shí)例化的就不需要實(shí)例化了
if ("singleton".equals(entry.getValue().getScope()) && !singletonObjects.containsKey(entry.getKey())) {
Object bean = createBean(entry.getKey(), entry.getValue());
singletonObjects.put(entry.getKey(), bean);
}
}
}
/**
* 掃描
*
* @param configClass
*/
private void scan(Class<?> configClass) {
ComponentScan componentScanAnnotation = configClass.getDeclaredAnnotation(ComponentScan.class);
// 掃描路徑(不填路徑默認(rèn)獲取Application文件所在的包) xyz.hcworld
String path = componentScanAnnotation.value().isEmpty() ? configClass.getPackage().getName() : componentScanAnnotation.value();
Search search = new Search();
//獲取文件夾或者jar下的所有類名
List<String> classNames = search.getClassName(path);
if (classNames == null) {
return;
}
ClassLoader classLoader = JubilantApplicationContext.class.getClassLoader();
try {
for (String className : classNames) {
Class<?> clazz = classLoader.loadClass(className);
// 如果沒有Component代表這不是一個(gè)bean結(jié)束本次循環(huán)
if (!clazz.isAnnotationPresent(Component.class)) {
continue;
}
// 創(chuàng)建bean對(duì)象
// 解析類,判斷當(dāng)前Bean是單例還是原型(prototype)Bean 生成BeanDefinition對(duì)象
// 統(tǒng)一處理方式:BeanDefinition
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
// 獲取Bean的名字
String beanName = componentAnnotation.value();
//如果沒有自定義beanName則以類名(首字母小寫)為beanName
if (beanName.isEmpty()) {
String[] name = className.split("\\.");
StringBuilder nameBuffer = new StringBuilder(name[name.length - 1]);
beanName = nameBuffer.replace(0, 1, Character.toString(nameBuffer.charAt(0)).toLowerCase()).toString();
}
// bean的配置信息
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
// 判斷Scope是否存在,存在則是自定義配置,不存在則為單例
beanDefinition.setScope(
clazz.isAnnotationPresent(Scope.class) ?
clazz.getDeclaredAnnotation(Scope.class).value() : "singleton");
// 存進(jìn)配置池
beanDefinitionMap.put(beanName, beanDefinition);
// 將實(shí)現(xiàn)BeanPostProcessor(初始化bean)接口的類存到初始化配置池中
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor beanPostProcessor = (BeanPostProcessor) createBean(beanName, beanDefinitionMap.get(beanName));
beanPostProcessorList.add(beanPostProcessor);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 根據(jù)配置創(chuàng)建出一個(gè)bean對(duì)象(反射)
*
* @param beanDefinition 自定義配置
* @return bean對(duì)象
*/
public Object createBean(String beanName, BeanDefinition beanDefinition) {
Class<?> clazz = beanDefinition.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
// 依賴注入
for (Field declaredField : clazz.getDeclaredFields()) {
// 沒有Autowired就不注入
if (!declaredField.isAnnotationPresent(Autowired.class)) {
continue;
}
String[] name = declaredField.getType().getName().split("\\.");
StringBuilder nameBuffer = new StringBuilder(name[name.length - 1]);
nameBuffer.replace(0, 1, Character.toString(nameBuffer.charAt(0)).toLowerCase());
// 通過反射注入屬性
Object bean = getBean(nameBuffer.toString());
if (bean == null) {
throw new NullPointerException();
}
// 當(dāng)變量為private時(shí)需要忽略訪問修飾符的檢查
declaredField.setAccessible(true);
declaredField.set(instance, bean);
}
// Aware回調(diào)
if (instance instanceof BeanNameAware) {
((BeanNameAware) instance).setBeanName(beanName);
}
//初始化前的增強(qiáng)
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
}
// 初始化
if (instance instanceof InitializingBean) {
((InitializingBean) instance).afterPropertiesSet();
}
// BeanPostProcessor 外部擴(kuò)展機(jī)制(Bean的前后置處理)
//初始化后的增強(qiáng)
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
}
return instance;
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 獲取Bean對(duì)象
*
* @param beanName bean名字
* @return bean對(duì)象
*/
public Object getBean(String beanName) {
// 判斷bean是否存在,不存在拋出npe異常
if (!beanDefinitionMap.containsKey(beanName)) {
throw new NullPointerException();
}
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
// 判斷是否是單例,是返回單例對(duì)象,不是創(chuàng)建原型對(duì)象
return "singleton".equals(beanDefinition.getScope()) ?
singletonObjects.get(beanName) : createBean(beanName, beanDefinition);
}
/**
* 獲取文件制定包下的所有class文件(不管有多少層)
*
* @param file 目錄
* @param filePaths 臨時(shí)存儲(chǔ)
* @return 所有class文件的絕對(duì)路徑
*/
private Set<File> getFilePath(File file, Set<File> filePaths) {
File[] files = file.listFiles();
if (files == null || files.length == 0) {
return new HashSet<>();
}
for (File file1 : files) {
if (file1.isDirectory()) {
//遞歸調(diào)用
getFilePath(file1, filePaths);
continue;
}
//保存文件路徑到集合中
filePaths.add(file1);
}
return filePaths;
}
}
四、結(jié)果

ioc.png
未來發(fā)展
展望
- 第一步,通過模仿Spring實(shí)現(xiàn)IOC
- 第二步,通過模仿Spring實(shí)現(xiàn)AOP
- 第三步,實(shí)現(xiàn)MVC框架
說明
ioc 的代碼是跟著B站的某教程敲的。但是修改了許多不合理的部分,如掃描class部分,原教程打包后無法運(yùn)行,因?yàn)楂@取不了絕對(duì)路徑。修改了既能打包jar后運(yùn)行又能在ide中運(yùn)行。以及原本喪心病狂的if-else-for嵌套改成了防御性的if判斷,去除了else。
先模仿在摸索自己的道路。:doge:
不足
筆者水平有限也非大廠的大牛,如有不對(duì)之處請(qǐng)指正。以及部分硬編碼問題純粹是筆者懶,還沒來得及改,自行修改即可。
地址
GitHub:https://github.com/z875479694h/jubilant
Gitee:https://gitee.com/hcworld/jubilant
聯(lián)系方式
公眾號(hào):青山有錄
Github:https://github.com/z875479694h
博客:https://www.hcworld.xyz