成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(二)--實(shí)現(xiàn)Bean容器

paulquei / 3090人閱讀

摘要:容器實(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è)工具類:ValidateUtilClassUtil

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>。等一下在實(shí)現(xiàn)Bean容器的時(shí)候就會(huì)使用這個(gè)方法來(lái)掃描獲取對(duì)應(yīng)包下的所有類文件。

/**
 * 類操作工具類
 */
@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  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);
    }
}
實(shí)現(xiàn)Bean容器

現(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)記的類。

BeanContainer

Bean容器實(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 getBeans() {
        return new HashSet<>(beanMap.values());
    }

    /**
     * 添加一個(gè)Bean實(shí)例
     */
    public Object addBean(Class clz, Object bean) {
        return beanMap.put(clz, bean);
    }

    /**
     * 移除一個(gè)Bean實(shí)例
     */
    public void removeBean(Class clz) {
        beanMap.remove(clz);
    }

    /**
     * Bean實(shí)例數(shù)量
     */
    public int size() {
        return beanMap.size();
    }

    /**
     * 所有Bean的Class集合
     */
    public Set> getClasses() {
        return beanMap.keySet();
    }

    /**
     * 通過(guò)注解獲取Bean的Class集合
     */
    public Set> getClassesByAnnotation(Class annotation) {
        return beanMap.keySet()
                .stream()
                .filter(clz -> clz.isAnnotationPresent(annotation))
                .collect(Collectors.toSet());
    }

    /**
     * 通過(guò)實(shí)現(xiàn)類或者父類獲取Bean的Class集合
     */
    public Set> getClassesBySuper(Class superClass) {
        return beanMap.keySet()
                .stream()
                .filter(superClass::isAssignableFrom)
                .filter(clz -> !clz.equals(superClass))
                .collect(Collectors.toSet());
    }
}

我們不需要像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 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ān)文章

  • 從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易Java MVC框架

    摘要:不過(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è)功能類似的...

    neuSnail 評(píng)論0 收藏0
  • 從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)

    摘要:接下來(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í)不方便也不合理。...

    wupengyu 評(píng)論0 收藏0
  • 從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易Java MVC框架(八)--制作Starter

    摘要:服務(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...

    AprilJ 評(píng)論0 收藏0
  • 從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易Java MVC框架(六)--加強(qiáng)AOP功能

    摘要:在前面的文章中實(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é)文章...

    Loong_T 評(píng)論0 收藏0
  • 從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易Java MVC框架(九)--優(yōu)化MVC代碼

    摘要:前言在從零開(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邏...

    ruicbAndroid 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<