摘要:容器實(shí)際上就是存放所有的地方,即以及相關(guān)信息對(duì)應(yīng)其實(shí)體的容器,為什么稱之為呢,因?yàn)樵谥?,定義信息和實(shí)例的東西叫。了解到這個(gè)以后接下來(lái)就可以開(kāi)始編寫容器了,在包下創(chuàng)建一個(gè)類叫。獲取容器實(shí)例至此,這個(gè)容器就完成了。
項(xiàng)目準(zhǔn)備
首先確保你擁有以下環(huán)境或者工具
idea
java 8
maven 3.3.X
lombok插件
然后我們創(chuàng)建一個(gè)maven工程,編寫pom.xml引入一些需要的依賴
1.8 1.8 UTF-8 1.7.25 1.16.20 org.slf4j slf4j-log4j12 ${slf4j-api.version} org.projectlombok lombok ${lombok.version} provided
目前只需要lombok和log4j兩個(gè)依賴就可以完成前面幾個(gè)功能的實(shí)現(xiàn),其他需要的依賴等到后面需要的時(shí)候再加。
接著把項(xiàng)目一些基本的包結(jié)構(gòu)創(chuàng)建一下,如下圖
resources文件夾下的log4j.properties文件為log4j輸出格式化參數(shù),大家可以根據(jù)自己的喜好和需求編寫,我自己的只是為了方便調(diào)試使用的,下面是我自己的。
### 設(shè)置### log4j.rootLogger = debug,stdout ### 輸出信息到控制抬 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %c %d{ISO8601} -- %p -- %m%n創(chuàng)建工具類
為了方便后續(xù)代碼的編寫,我們先創(chuàng)建工具類。
在com.zbw.util包下創(chuàng)建兩個(gè)工具類:ValidateUtil和ClassUtil。
ValidateUtil主要負(fù)責(zé)屬性的驗(yàn)證,這個(gè)類的完整代碼就不貼了,就是檢查各種類型的值是否為空或者是否不為空。
/** * 驗(yàn)證相關(guān)工具類 */ public final class ValidateUtil { /** * Object是否為null */ public static boolean isEmpty(Object obj) { return obj == null; } /** * String是否為null或"" */ public static boolean isEmpty(String obj) { return (obj == null || "".equals(obj)); } ... /** * Object是否不為null */ public static boolean isNotEmpty(Object obj) { return !isEmpty(obj); } /** * String是否不為null或"" */ public static boolean isNotEmpty(String obj) { return !isEmpty(obj); } ... }
ClassUtil主要是Class的一些相關(guān)操作。這其中除了一些類常用的實(shí)例反射等操作,還有一個(gè)重要方法就是getPackageClass(),這個(gè)方法會(huì)遞歸遍歷傳入的包名下的所有類文件,并返回一個(gè)Set
/** * 類操作工具類 */ @Slf4j public final class ClassUtil { /** * file形式url協(xié)議 */ public static final String FILE_PROTOCOL = "file"; /** * jar形式url協(xié)議 */ public static final String JAR_PROTOCOL = "jar"; /** * 獲取classLoader */ public static ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } /** * 獲取Class */ public static Class> loadClass(String className) { try { return Class.forName(className); } catch (ClassNotFoundException e) { log.error("load class error", e); throw new RuntimeException(e); } } /** * 實(shí)例化class */ @SuppressWarnings("unchecked") public static實(shí)現(xiàn)Bean容器T newInstance(String className) { try { Class> clazz = loadClass(className); return (T) clazz.newInstance(); } catch (Exception e) { log.error("newInstance error", e); throw new RuntimeException(e); } } /** * 實(shí)例化class */ @SuppressWarnings("unchecked") public static T newInstance(Class> clazz) { try { return (T) clazz.newInstance(); } catch (Exception e) { log.error("newInstance error", e); throw new RuntimeException(e); } } /** * 設(shè)置類的屬性值 */ public static void setField(Field field, Object target, Object value) { setField(field, target, value, true); } /** * 設(shè)置類的屬性值 */ public static void setField(Field field, Object target, Object value, boolean accessible) { field.setAccessible(accessible); try { field.set(target, value); } catch (IllegalAccessException e) { log.error("setField error", e); throw new RuntimeException(e); } } /** * 獲取包下類集合 */ public static Set > getPackageClass(String basePackage) { URL url = getClassLoader() .getResource(basePackage.replace(".", "/")); if (null == url) { throw new RuntimeException("無(wú)法獲取項(xiàng)目路徑文件"); } try { if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)) { // 若為普通文件夾,則遍歷 File file = new File(url.getFile()); Path basePath = file.toPath(); return Files.walk(basePath) .filter(path -> path.toFile().getName().endsWith(".class")) .map(path -> getClassByPath(path, basePath, basePackage)) .collect(Collectors.toSet()); } else if (url.getProtocol().equalsIgnoreCase(JAR_PROTOCOL)) { // 若在 jar 包中,則解析 jar 包中的 entry JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); return jarURLConnection.getJarFile() .stream() .filter(jarEntry -> jarEntry.getName().endsWith(".class")) .map(ClassUtil::getClassByJar) .collect(Collectors.toSet()); } return Collections.emptySet(); } catch (IOException e) { log.error("load package error", e); throw new RuntimeException(e); } } /** * 從Path獲取Class */ private static Class> getClassByPath(Path classPath, Path basePath, String basePackage) { String packageName = classPath.toString().replace(basePath.toString(), ""); String className = (basePackage + packageName) .replace("/", ".") .replace("", ".") .replace(".class", ""); return loadClass(className); } /** * 從jar包獲取Class */ private static Class> getClassByJar(JarEntry jarEntry) { String jarEntryName = jarEntry.getName(); // 獲取類名 String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); return loadClass(className); } }
現(xiàn)在開(kāi)始可以實(shí)現(xiàn)Bean容器了。
基礎(chǔ)注解在spring中我們總是用各種注解去標(biāo)注我們的組件,如controller等。所以我們也要先寫一些注解來(lái)標(biāo)注一些必要的組件。在zbw.core包下再創(chuàng)建一個(gè)annotation包,然后再創(chuàng)建四個(gè)最基本的組件.
// Component注解,用于標(biāo)記組件 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component { } // Controller注解,用于標(biāo)記Controller層的組件 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Controller { } // Repository注解,用于標(biāo)記Dao層的組件 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Repository { } // Service注解,用于標(biāo)記Service層的組件 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { }
這四個(gè)注解都是只能標(biāo)注在類上的,他們實(shí)際上沒(méi)有任何作用,只是用來(lái)標(biāo)記這個(gè)類的,我們?cè)诤竺娴念惣现芯涂梢院芊奖愕墨@取和區(qū)分被這些注解標(biāo)記的類。
BeanContainerBean容器實(shí)際上就是存放所有Bean的地方,即Class以及相關(guān)信息對(duì)應(yīng)其實(shí)體的容器,為什么稱之為"Bean"呢,因?yàn)樵趕pring中,定義Class信息和實(shí)例的東西叫BeanDefinition。這是一個(gè)接口,他有一個(gè)模板類AbstractBeanDefinition,這里面就有一個(gè)beanClass變量存放Class類和propertyValues變量存放類屬性,以及很多類相關(guān)參數(shù)和初始化之類的參數(shù)。大家可以去spring中看看,spring的所有都是依賴于這個(gè)Bean生成的,可以說(shuō)這是spring的基石。
了解到這個(gè)以后接下來(lái)就可以開(kāi)始編寫B(tài)ean容器了,在zbw.core包下創(chuàng)建一個(gè)類叫BeanContainer。
/** * Bean容器 */ @Slf4j public class BeanContainer { /** * 存放所有Bean的Map */ private final Map, Object> beanMap = new ConcurrentHashMap<>(); /** * 獲取Bean實(shí)例 */ public Object getBean(Class> clz) { if (null == clz) { return null; } return beanMap.get(clz); } /** * 獲取所有Bean集合 */ public Set
我們不需要像spring那樣存放很多的信息,所以用一個(gè)Map來(lái)存儲(chǔ)Bean的信息就好了。Map的Key為Class類,Value為這個(gè)Class的實(shí)例Object。配合getBean(),addBean()等方法就可以很方便的操作Class和它的實(shí)例。
然而現(xiàn)在這個(gè)Map里還沒(méi)有存放任何的Bean數(shù)據(jù),所以編寫一個(gè)loadBeans()方法來(lái)初始化加載Bean。
首先在BeanContainer中添加一個(gè)變量isLoadBean和一個(gè)常量BEAN_ANNOTATION
//BeanContainer ... /** * 是否加載Bean */ private boolean isLoadBean = false; /** * 加載bean的注解列表 */ private static final List> BEAN_ANNOTATION = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class); ...
然后編寫loadBeans()方法去加載被BEAN_ANNOTATION中的注解類注解的類,以及對(duì)應(yīng)的實(shí)例。通過(guò)剛才的ClassUtil.getPackageClass(basePackage)獲取我們項(xiàng)目下所有的Class,然后判斷該Class是否被BEAN_ANNOTATION中注解類注解,如果有就說(shuō)明該Class是一個(gè)Bean,對(duì)其實(shí)例化并且放入Map中。
//BeanContainer ... /** * 掃描加載所有Bean */ public void loadBeans(String basePackage) { if (isLoadBean()) { log.warn("bean已經(jīng)加載"); return; } Set> classSet = ClassUtil.getPackageClass(basePackage); classSet.stream() .filter(clz -> { for (Class extends Annotation> annotation : BEAN_ANNOTATION) { if (clz.isAnnotationPresent(annotation)) { return true; } } return false; }) .forEach(clz -> beanMap.put(clz, ClassUtil.newInstance(clz))); isLoadBean = true; } /** * 是否加載Bean */ public boolean isLoadBean() { return isLoadBean; } ...
最后,為了能夠保證整個(gè)項(xiàng)目全局Bean的唯一性,我們要保證這個(gè)BeanContainer是唯一的,將該類單例化。
通過(guò)lombok的注解@NoArgsConstructor(access = AccessLevel.PRIVATE)生成私有構(gòu)造函數(shù),再用內(nèi)部枚舉生成唯一的BeanContainer實(shí)例。
@Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class BeanContainer { /** * 獲取Bean容器實(shí)例 */ public static BeanContainer getInstance() { return ContainerHolder.HOLDER.instance; } ... private enum ContainerHolder { HOLDER; private BeanContainer instance; ContainerHolder() { instance = new BeanContainer(); } } }
至此,這個(gè)Bean容器就完成了。我們可以通過(guò)loadBeans()方法初始化Bean,然后可以通過(guò)getBean(),addBean(),removeBean()等方法去操作這個(gè)Bean,為后面的IOC,AOP等功能打下基礎(chǔ)。
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(一)--前言
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(二)--實(shí)現(xiàn)Bean容器
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(三)--實(shí)現(xiàn)IOC
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(六)--加強(qiáng)AOP功能
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(七)--實(shí)現(xiàn)MVC
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(八)--制作Starter
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(九)--優(yōu)化MVC代碼
源碼地址:doodle
原文地址:從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架--實(shí)現(xiàn)Bean容器
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/71459.html
摘要:不過(guò)仔細(xì)了解了一段時(shí)候發(fā)現(xiàn),其實(shí)他的原理是很簡(jiǎn)單的,所以想要自己也動(dòng)手實(shí)現(xiàn)一個(gè)功能類似的框架。原文地址從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架 前言 最近在看spring-boot框架的源碼,看了源碼之后更是讓我感受到了spring-boot功能的強(qiáng)大。而且使用了很多的設(shè)計(jì)模式,讓人在看的時(shí)候覺(jué)得有點(diǎn)難以下手。 不過(guò)仔細(xì)了解了一段時(shí)候發(fā)現(xiàn),其實(shí)他的原理是很簡(jiǎn)單的,所以想要自己也動(dòng)手實(shí)現(xiàn)一個(gè)功能類似的...
摘要:接下來(lái)就可以把這個(gè)切點(diǎn)類加入到我們之前實(shí)現(xiàn)的功能中了。實(shí)現(xiàn)的切點(diǎn)功能首先改裝注解,把之前改成來(lái)存儲(chǔ)表達(dá)式。測(cè)試用例在上一篇文章從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架四實(shí)現(xiàn)中的測(cè)試用例的基礎(chǔ)上修改測(cè)試用例。 前言 在上一節(jié)從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP中我們實(shí)現(xiàn)了AOP的功能,已經(jīng)可以生成對(duì)應(yīng)的代理類了,但是對(duì)于代理對(duì)象的選擇只能通過(guò)指定的類,這樣確實(shí)不方便也不合理。...
摘要:服務(wù)器相關(guān)配置啟動(dòng)類資源目錄目錄靜態(tài)文件目錄端口號(hào)目錄目錄實(shí)現(xiàn)內(nèi)嵌服務(wù)器在上一章文章從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架七實(shí)現(xiàn)已經(jīng)在文件中引入了依賴,所以這里就不用引用了。 spring-boot的Starter 一個(gè)項(xiàng)目總是要有一個(gè)啟動(dòng)的地方,當(dāng)項(xiàng)目部署在tomcat中的時(shí)候,經(jīng)常就會(huì)用tomcat的startup.sh(startup.bat)的啟動(dòng)腳本來(lái)啟動(dòng)web項(xiàng)目 而在spring-b...
摘要:在前面的文章中實(shí)現(xiàn)的功能時(shí),目標(biāo)類都只能被一個(gè)切面代理,如果想要生成第二個(gè)代理類,就會(huì)把之前的代理類覆蓋。改裝原有功能現(xiàn)在要改裝原來(lái)的的實(shí)現(xiàn)代碼,讓的功能加入到框架中為了讓切面能夠排序,先添加一個(gè)注解,用于標(biāo)記排序。 前言 在前面從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP和從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)這兩節(jié)文章...
摘要:前言在從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架七實(shí)現(xiàn)中實(shí)現(xiàn)了框架的的功能,不過(guò)最后指出代碼的邏輯不是很好,在這一章節(jié)就將這一部分代碼進(jìn)行優(yōu)化。 前言 在從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(七)--實(shí)現(xiàn)MVC中實(shí)現(xiàn)了doodle框架的MVC的功能,不過(guò)最后指出代碼的邏輯不是很好,在這一章節(jié)就將這一部分代碼進(jìn)行優(yōu)化。 優(yōu)化的目標(biāo)是1.去除DispatcherServlet請(qǐng)求分發(fā)器中的http邏...
閱讀 2343·2021-09-30 09:47
閱讀 2963·2019-08-30 11:05
閱讀 2536·2019-08-29 17:20
閱讀 1923·2019-08-29 13:01
閱讀 1731·2019-08-26 13:39
閱讀 1258·2019-08-26 13:26
閱讀 3215·2019-08-23 18:40
閱讀 1833·2019-08-23 17:09