成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

手寫Spring之DI依賴注入

Cruise_Chan / 1905人閱讀

摘要:如感興趣,可移步手寫之基于動(dòng)態(tài)創(chuàng)建對(duì)象手寫之基于注解動(dòng)態(tài)創(chuàng)建對(duì)象今天將詳細(xì)介紹如何手寫依賴注入,在運(yùn)行過(guò)程中如何動(dòng)態(tài)地為對(duì)象的屬性賦值。完成后在中會(huì)有相關(guān)的包出現(xiàn)進(jìn)行注入前需要?jiǎng)?chuàng)建工廠,在運(yùn)行時(shí)從工廠中取出對(duì)象為屬性賦值。

前兩篇文章介紹了關(guān)于手寫Spring IOC控制反轉(zhuǎn),由Spring工廠在運(yùn)行過(guò)程中動(dòng)態(tài)地創(chuàng)建對(duì)象的兩種方式。如感興趣,可移步:

手寫Spring之IOC基于xml動(dòng)態(tài)創(chuàng)建對(duì)象

手寫Spring之IOC基于注解動(dòng)態(tài)創(chuàng)建對(duì)象

今天將詳細(xì)介紹如何手寫Spring DI依賴注入,在運(yùn)行過(guò)程中如何動(dòng)態(tài)地為對(duì)象的屬性賦值。

首先還是創(chuàng)建項(xiàng)目,用于本次測(cè)試需要使用到j(luò)unit,因此創(chuàng)建的是Maven項(xiàng)目,方便添加依賴jar包,JDK環(huán)境還是1.7:

接下來(lái)在pom.xml文件中添加junit的依賴坐標(biāo):


  junit
  junit
  4.10
  test

第一次添加時(shí),若本地倉(cāng)庫(kù)中沒(méi)有此版本的jar包,Maven會(huì)根據(jù)配置的鏡像聯(lián)網(wǎng)下載,默認(rèn)是去中心倉(cāng)庫(kù)下載,中心倉(cāng)庫(kù)的服務(wù)器在國(guó)外,下載速度較慢,建議修改配置文件連接阿里云的Maven鏡像倉(cāng)庫(kù)下載,速度較快,如何配置在此不多贅述。你也可以根據(jù)自己本地倉(cāng)庫(kù)已有的junit版本 對(duì)依賴坐標(biāo)的版本進(jìn)行修改,這樣就可以直接使用本地倉(cāng)庫(kù)的jar包,不用耗時(shí)連外網(wǎng)去下載了。

完成后在Maven Dependencies中會(huì)有相關(guān)的jar包出現(xiàn):

進(jìn)行DI注入前需要?jiǎng)?chuàng)建工廠,在運(yùn)行時(shí)從工廠中取出對(duì)象為屬性賦值。因此先做一些準(zhǔn)備工作,創(chuàng)建幾個(gè)要用到的注解:

MyComponent注解內(nèi)容如下:

package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**@Target 屬性用于注明此注解用在什么位置,
 * ElementType.TYPE表示可用在類、接口、枚舉上等*/
@Target(ElementType.TYPE)
/**@Retention 屬性表示所定義的注解何時(shí)有效,
 * RetentionPolicy.RUNTIME表示在運(yùn)行時(shí)有效*/
@Retention(RetentionPolicy.RUNTIME)
/**@interface 表示注解類型*/
public @interface MyComponent {
    /**為此注解定義scope屬性*/
    public String scope() default "";
}

MyAutowired注解內(nèi)容如下:

package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {

}

MyValue注解內(nèi)容如下:

package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {
    /**定義value屬性*/
    public String value();
}

接下來(lái)創(chuàng)建實(shí)體類:

User實(shí)體類內(nèi)容如下,實(shí)體類中的屬性值暫用注解方式寫死作為測(cè)試(實(shí)際中并不會(huì)這么用),此實(shí)體類暫時(shí)為單例類(不注明scope屬性默認(rèn)為單例模式):

@MyComponent
public class User {
    @MyValue("1")
    private Integer id;
    @MyValue("zhangsan")
    private String name;
    @MyValue("zhangsan")
    private String password;
    
    public User() {
        System.out.println("無(wú)參構(gòu)造方法執(zhí)行");
    }
    
    public void login(){
        System.out.println("用戶登錄:id=" + id + ", name=" + name + ", password=" + password);
    }
    
    //setters和getters...
}

然后創(chuàng)建UserService類,在Service類中使用依賴注入U(xiǎn)ser:

UserService內(nèi)容如下:

package service;

import annotation.MyAutowired;
import annotation.MyComponent;
import entity.User;
@MyComponent
public class UserService {

    @MyAutowired
    User user1;
    
    @MyAutowired
    User user2;
    
    public void userLogin(){
        System.out.println("用戶1:"+user1);
        user1.login();
        System.out.println("用戶2:"+user2);
        user2.login();
    }
}

創(chuàng)建注解工廠類:

工廠類的內(nèi)容如下:

public class AnnotationConfigApplicationContext {
    /**此Map容器用于存儲(chǔ)類定義對(duì)象*/
    private Map> beanDefinationFacotry=new ConcurrentHashMap<>();
    /**此Map容器用于存儲(chǔ)單例對(duì)象*/
    private Map singletonbeanFactory=new ConcurrentHashMap<>();
    /**有參構(gòu)造方法,參數(shù)類型為指定要掃描加載的包名,此工廠可接收多個(gè)包路徑*/
    public AnnotationConfigApplicationContext(String... packageNames) {
        //遍歷掃描指定的所有包路徑
        for (String packageName : packageNames) {
            System.out.println("開(kāi)始掃描包:"+packageName);
            /**掃描指定的包路徑*/
            scanPkg(packageName);
        }
        /**進(jìn)行DI依賴注入*/
        dependencyInjection();
    }
}

在工廠類的構(gòu)造方法中,可以接收多個(gè)包路徑,并且遍歷循環(huán)掃描每一個(gè)包路徑,掃描包的scanPkg方法如下:

/**
 * 掃描指定包,找到包中的類文件。
 * 對(duì)于標(biāo)準(zhǔn)(類上有定義注解的)類文件反射加載創(chuàng)建類定義對(duì)象并放入容器中
 */
private void scanPkg(final String pkg){
    //替換包名中的".",將包結(jié)構(gòu)轉(zhuǎn)換為目錄結(jié)構(gòu)
    String pkgDir=pkg.replaceAll(".", "/");
    //獲取目錄結(jié)構(gòu)在類路徑中的位置(其中url中封裝了具體資源的路徑)
    URL url=getClass().getClassLoader().getResource(pkgDir);
    //基于這個(gè)路徑資源(url),構(gòu)建一個(gè)文件對(duì)象
    File file=new File(url.getFile());
    //獲取此目錄中指定標(biāo)準(zhǔn)(以".class"結(jié)尾)的文件
    File[] fs=file.listFiles(new FileFilter() {
        @Override
        public boolean accept(File file) {
            //獲取文件名
            String fName=file.getName();
            //判斷該文件是否為目錄,如為目錄,遞歸進(jìn)一步掃描其內(nèi)部所有文件
            if(file.isDirectory()){
                scanPkg(pkg+"."+fName);
            }else{
                //判定文件的后綴是否為.class
                if(fName.endsWith(".class")){
                    return true;
                }
            }
            return false;
        }
    });    
    //遍歷所有符合標(biāo)準(zhǔn)的File文件
    for(File f:fs){
        //獲取文件名
        String fName=f.getName();
        //獲取去除.class之后的文件名
        fName=fName.substring(0,fName.lastIndexOf("."));
        //將名字(類名,通常為大寫開(kāi)頭)的第一個(gè)字母轉(zhuǎn)換小寫(用它作為key存儲(chǔ)工廠中)
        String beanId=String.valueOf(fName.charAt(0)).toLowerCase()+fName.substring(1);
        //構(gòu)建一個(gè)類全名(包名.類名)
        String pkgCls=pkg+"."+fName;
        try{
            //通過(guò)反射構(gòu)建類對(duì)象
            Class c=Class.forName(pkgCls);
            //判定這個(gè)類上是否有MyComponent注解
            if(c.isAnnotationPresent(MyComponent.class)){
                //將類對(duì)象存儲(chǔ)到map容器中
                beanDefinationFacotry.put(beanId, c);
            }
        }catch(Exception e){
            throw new RuntimeException(e); 
        }
    }
}

掃描所有的包完成之后,對(duì)需要的屬性進(jìn)行注入,dependencyInjection方法如下:

/**
 * 此方法用于對(duì)屬性進(jìn)行依賴注入。
 * 從工廠中獲取所有的類對(duì)象,如果類中的屬性上有MyAutowired注解,
 * 那么首先從根據(jù)屬性名從工廠中獲取對(duì)象,或者根據(jù)對(duì)象類型獲取對(duì)象。
 * 最后用該對(duì)象對(duì)屬性進(jìn)行注入。
 */
private void dependencyInjection(){
    //獲取容器中所有的類定義對(duì)象
    Collection> classes = beanDefinationFacotry.values();
    //遍歷每一個(gè)類對(duì)象
    for (Class cls : classes) {
        //獲取類對(duì)象的名字全稱(包名+類名)
        String clsName = cls.getName();
        //獲取類名
        clsName = clsName.substring(clsName.lastIndexOf(".")+1);
        //將類名(通常為大寫開(kāi)頭)的第一個(gè)字母轉(zhuǎn)換小寫
        String beanId=String.valueOf(clsName.charAt(0)).toLowerCase()+clsName.substring(1);
        //獲取類中所有的屬性
        Field[] fields = cls.getDeclaredFields();
        //遍歷每一個(gè)屬性
        for (Field field : fields) {
            //如果這個(gè)屬性上有MyAutowired注解,進(jìn)行注入操作
            if(field.isAnnotationPresent(MyAutowired.class)){
                try {
                    //獲取屬性名
                    String fieldName = field.getName();
                    System.out.println("屬性名:"+fieldName);
                    //定義為屬性注入的bean對(duì)象(此對(duì)象從容器中獲取)
                    Object fieldBean = null;
                    //首先根據(jù)屬性名從容器中取出對(duì)象,如果不為null,則賦值給fieldBean對(duì)象
                    if(beanDefinationFacotry.get(fieldName) != null){
                        fieldBean = getBean(fieldName,field.getType());
                    }else{    //否則按照屬性的類型從容器中取出對(duì)象進(jìn)行注入
                        //獲取屬性的類型(包名+類名)
                        String type = field.getType().getName();
                        //截取最后的類名
                        type = type.substring(type.lastIndexOf(".")+1);
                        //將類名(通常為大寫開(kāi)頭)的第一個(gè)字母轉(zhuǎn)換小寫
                        String fieldBeanId=String.valueOf(type.charAt(0)).toLowerCase()+type.substring(1);
                        System.out.println("屬性類型ID:"+fieldBeanId);
                        //根據(jù)轉(zhuǎn)換后的類型beanId,從容器中獲取對(duì)象并賦值給fieldBean對(duì)象
                        fieldBean = getBean(fieldBeanId,field.getType());
                    }
                    System.out.println("要為屬性注入的值:"+fieldBean);
                    //如果fieldBean對(duì)象不為空,則為該屬性進(jìn)行注入
                    if(fieldBean != null){
                        //獲取此類定義的對(duì)象的實(shí)例對(duì)象
                        Object clsBean = getBean(beanId, cls);
                        //設(shè)置此屬性可訪問(wèn)
                        field.setAccessible(true);
                        //為該屬性注入值
                        field.set(clsBean, fieldBean);
                        System.out.println("注入成功!");
                    }else{
                        System.out.println("注入失敗!");
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在dependencyInjection方法中調(diào)用了getBean方法,內(nèi)容如下:

/**
 * 根據(jù)傳入的bean的id值獲取容器中的對(duì)象,類型為Object
 */
public Object getBean(String beanId){
    //根據(jù)傳入beanId獲取類對(duì)象
    Class cls = beanDefinationFacotry.get(beanId);
    //根據(jù)類對(duì)象獲取其定義的注解
    MyComponent annotation = cls.getAnnotation(MyComponent.class);
    //獲取注解的scope屬性值
    String scope = annotation.scope();
    try {
        //如果scope等于singleton,創(chuàng)建單例對(duì)象
        if("singleton".equals(scope) || "".equals(scope)){
            //判斷容器中是否已有該對(duì)象的實(shí)例,如果沒(méi)有,創(chuàng)建一個(gè)實(shí)例對(duì)象放到容器中
            if(singletonbeanFactory.get(beanId)==null){
                Object instance = cls.newInstance();
                setFieldValues(cls,instance);
                singletonbeanFactory.put(beanId,instance);
            }
            //根據(jù)beanId獲取對(duì)象并返回
            return singletonbeanFactory.get(beanId);
        }
        //如果scope等于prototype,則創(chuàng)建并返回多例對(duì)象
        if("prototype".equals(scope)){
            Object instance = cls.newInstance();
            setFieldValues(cls,instance);
            return instance;
        }
        //目前僅支持單例和多例兩種創(chuàng)建對(duì)象的方式
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    //如果遭遇異常,返回null
    return null;
}
/**
 * 此為重載方法,根據(jù)傳入的class對(duì)象在內(nèi)部進(jìn)行強(qiáng)轉(zhuǎn),
 * 返回傳入的class對(duì)象的類型
 */
public T getBean(String beanId, Class c){
    return (T)getBean(beanId);
}

在getBean方法中從工廠容器中獲取對(duì)象,并且需要調(diào)用setFieldValues方法為對(duì)象的屬性賦值,該方法內(nèi)容如下:

/**
 * 此方法用于為對(duì)象的屬性賦值
 * 內(nèi)部是通過(guò)獲取成員屬性上注解的值,在轉(zhuǎn)換為類型之后,通過(guò)反射為對(duì)象賦值
 * @param cls 類定義對(duì)象
 * @param obj 要為其賦值的實(shí)例對(duì)象
 */
private void setFieldValues(Class cls,Object obj){
    //獲取類中所有的成員屬性
    Field[] fields = cls.getDeclaredFields();
    //遍歷所有屬性
    for (Field field : fields) {
        //如果此屬性有MyValue注解修飾,對(duì)其進(jìn)行操作
        if(field.isAnnotationPresent(MyValue.class)){
            //獲取屬性名
            String fieldName = field.getName();
            //獲取注解中的值
            String value = field.getAnnotation(MyValue.class).value();
            //獲取屬性所定義的類型
            String type = field.getType().getName();
            //將屬性名改為以大寫字母開(kāi)頭,如:id改為ID,name改為Name
            fieldName = String.valueOf(fieldName.charAt(0)).toUpperCase()+fieldName.substring(1);
            //set方法名稱,如:setId,setName...
            String setterName = "set" + fieldName;
            try {
                //根據(jù)方法名稱和參數(shù)類型獲取對(duì)應(yīng)的set方法對(duì)象
                Method method = cls.getDeclaredMethod(setterName, field.getType());
                //判斷屬性類型,如類型不一致,則轉(zhuǎn)換類型后調(diào)用set方法為屬性賦值
                if("java.lang.Integer".equals(type) || "int".equals(type)){
                    int intValue = Integer.valueOf(value);
                    method.invoke(obj, intValue);
                } else if("java.lang.String".equals(type)){
                    method.invoke(obj, value);
                }
                //作為測(cè)試,僅判斷Integer和String類型,其它類型同理
            } catch (NoSuchMethodException | SecurityException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

最后是釋放工廠資源的close方法,內(nèi)容如下:

/**
 * 銷毀方法,用于釋放資源
 */
public void close(){
    beanDefinationFacotry.clear();
    beanDefinationFacotry=null;
    singletonbeanFactory.clear();
    singletonbeanFactory=null;
}

工廠類創(chuàng)建完畢后,開(kāi)始寫測(cè)試類進(jìn)行測(cè)試:

測(cè)試類內(nèi)容如下:

@MyComponent
public class TestSpringDi {
    /**創(chuàng)建AnnotationConfigApplicationContext對(duì)象*/
    AnnotationConfigApplicationContext ctx;
    /**創(chuàng)建UserService對(duì)象*/
    UserService userService;
    /**
     * 初始化方法
     */
    @Before
    public void init(){
        //實(shí)例化工廠類,傳入entity/service/springTest三個(gè)包路徑進(jìn)行掃描
        ctx = new AnnotationConfigApplicationContext("entity","service","springTest");
        //調(diào)用工廠的getBean方法動(dòng)態(tài)獲取對(duì)象
        userService = ctx.getBean("userService",UserService.class);
    }
    /**
     * 測(cè)試方法
     */
    @Test
    public void testSpringDi(){
        userService.userLogin();
    }
    /**
     * 銷毀方法
     */
    @After
    public void close(){
        ctx.close();
    }
}

以上是所有的代碼,寫完之后就可以運(yùn)行程序進(jìn)行測(cè)試了。運(yùn)行結(jié)果如下:

從控制臺(tái)打印輸出的結(jié)果可以看出,UserService類中的兩個(gè)User屬性都已經(jīng)成功注入,并調(diào)用了模擬用戶登錄的login方法,輸出的結(jié)果正是為User對(duì)象所設(shè)置的值。由于User類是單例的,因此UserService中的兩個(gè)User屬性所注入的值都是同一個(gè)對(duì)象(根據(jù)對(duì)象所映射的地址hashcode值相同可以證明這一點(diǎn)),而且無(wú)參的構(gòu)造方法也只執(zhí)行了一次。

那么如何為多例模式的對(duì)象進(jìn)行注入呢?我們?cè)赨ser類的注解中加上scope屬性,指定為prototype:

@MyComponent(scope="prototype")
public class User {
    ... ...
}

然后再次運(yùn)行程序進(jìn)行測(cè)試,結(jié)果如下:

現(xiàn)在可以看到,為兩個(gè)User屬性所賦的值已經(jīng)是不同的對(duì)象了,無(wú)參構(gòu)造方法執(zhí)行了兩次。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/68497.html

相關(guān)文章

  • Spring IOC知識(shí)點(diǎn)一網(wǎng)打盡!

    摘要:使用的好處知乎的回答不用自己組裝,拿來(lái)就用。統(tǒng)一配置,便于修改。 前言 只有光頭才能變強(qiáng) 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡(jiǎn)單啦 單例模式你會(huì)幾種寫法? 工廠模式理解了沒(méi)有? 在刷Spring書籍的時(shí)候花了點(diǎn)時(shí)間去學(xué)習(xí)了單例模式和工廠模式,總的來(lái)說(shuō)還是非常值得的! 本來(lái)想的是刷完《Spring 實(shí)戰(zhàn) (第4版)》和《精通Spring4.x 企業(yè)應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)》...

    djfml 評(píng)論0 收藏0
  • 搞懂依賴注入, 用 PHP 手寫簡(jiǎn)易 IOC 容器

    摘要:依賴注入控制反轉(zhuǎn)的一種具體實(shí)現(xiàn)方法。接下來(lái),我們使用依賴注入實(shí)現(xiàn)控制反轉(zhuǎn),使依賴關(guān)系倒置依賴被動(dòng)傳入。從單元測(cè)試的角度看,依賴注入更方便和操作,方便了測(cè)試人員寫出質(zhì)量更高的測(cè)試代碼。 前言 好的設(shè)計(jì)會(huì)提高程序的可復(fù)用性和可維護(hù)性,也間接的提高了開(kāi)發(fā)人員的生產(chǎn)力。今天,我們就來(lái)說(shuō)一下在很多框架中都使用的依賴注入。 一些概念 要搞清楚什么是依賴注入如何依賴注入,首先我們要明確一些概念。 D...

    antz 評(píng)論0 收藏0
  • Spring框架我見(jiàn)(三)——IOC、AOP

    摘要:模塊負(fù)責(zé)的所有面向切面的功能??偨Y(jié)的統(tǒng)一管理,降低了對(duì)象之間的耦合對(duì)主流的框架提供了很好的集成支持提供眾多組件,事務(wù)管理,等具有高度可開(kāi)放性,開(kāi)發(fā)者可以自由選擇部分或全部主要使用工廠模式和代理模式。 聊完了Spring框架中最重要的兩種設(shè)計(jì)模式,我們來(lái)看一下Spring框架的模塊和結(jié)構(gòu)圖。 Spring框架的結(jié)構(gòu) 下圖是Spring官方給出的Spring框架的結(jié)構(gòu)圖。 showImg(...

    khs1994 評(píng)論0 收藏0
  • 深入理解依賴注入

    摘要:上面這部分代碼不變,還是通過(guò)在構(gòu)造器中傳入依賴的方式初始化依賴調(diào)用這里,調(diào)用方無(wú)需了解內(nèi)部對(duì)的依賴。而配置一般用于上自動(dòng)掃描并注入的代碼如下這里只給出直接在依賴對(duì)象上添加注解的形式,還可以通過(guò)構(gòu)造器和注入依賴,這里就不多說(shuō)了。 前言 相信所有面試java開(kāi)發(fā)的童鞋一定都被問(wèn)到過(guò)是否使用過(guò)Spring,是否了解其IOC容器,為什么不直接使用工廠模式,以及究竟IOC和DI區(qū)別在于哪里這種問(wèn)...

    e10101 評(píng)論0 收藏0
  • Spring旅第一站(不得不佩服老外...)

    摘要:表示少女與緊耦合在它的構(gòu)造函數(shù)中自行創(chuàng)建了。面向切面編程往往被定義為促使軟件系統(tǒng)實(shí)現(xiàn)關(guān)注點(diǎn)的分離一項(xiàng)技術(shù)系統(tǒng)由許多不同的組件組成,每個(gè)組件各負(fù)責(zé)一特定的功能。我們可以把切面想象為覆蓋在很多組件之上的一個(gè)外殼。 第1章 Spring之旅 說(shuō)明 1、本文抄寫了《Spring 實(shí)戰(zhàn)》重點(diǎn)內(nèi)容,參考了GitHub上的代碼 2、每個(gè)人的學(xué)習(xí)方式不一樣,但目的是一樣的,活學(xué)活用。最近一直在聽(tīng)《我...

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

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

0條評(píng)論

閱讀需要支付1元查看
<