java開發兩年,連Spring的依賴注入的方式都搞不清楚,你工作可能有點懸!

Spring依賴注入

常的java開發中,程序員在某個類中需要依賴其它類的方法,則通常是new一個依賴類再調用類實例的方法,這種開發存在的問題是new的類實例不好統一管理,spring提出了依賴注入的思想,即依賴類不由程序員實例化,而是通過spring容器幫我們new指定實例並且將實例注入到需要該對象的類中。依賴注入的另一種說法是“控制反轉”,通俗的理解是:平常我們new一個實例,這個實例的控制權是我們程序員,而控制反轉是指new實例工作不由我們程序員來做而是交給spring容器來做。

構造函數注入

在bean標籤的內部使用constructor-arg標籤就可以進行構造函數注入了。
constructor-arg標籤的屬性:

  • type:用於指定要注入的數據的數據類型,該數據類型也是構造函數中某個或某些參數的類型
  • index:用於指定要注入的數據給構造函數中指定索引位置的參數賦值,索引的位置從0開始
  • name:用於給指定構造函數中指定名稱的參數賦值
  • value:用於提供基本類型和String類型的數據
  • ref:用於指定其他的bean類型數據,就是在IOC容器中出現過的bean對象
    bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountService" class="com.sks.service.imp.AccountServiceImpl">
        <constructor-arg type="java.lang.String" value="張三"/>
        <constructor-arg index="1" value="20"/>
        <constructor-arg name="birthday" ref="birthday"/>
    </bean>

    <bean id="birthday" class="java.util.Date"/>

</beans>

AccountServiceImpl 類

public class AccountServiceImpl implements AccountService {

    private String name;
    private Integer age;
    private Date birthday;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "AccountServiceImpl{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }

    public AccountServiceImpl(String name, Integer age, Date birthday) {
        System.out.println("含參的構造方法被調用了");
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public AccountServiceImpl() {
        System.out.println("構造方法調用");
    }

    @Override
    public int addMoney(int money) {
        System.out.println("向賬戶中加錢:" + money);
        return 0;
    }

    @Override
    public void saveAccount(Account account) {
        System.out.println("saveAccount方法執行了");
    }
}

測試

    /**
     * 測試構造函數注入
     */
    @Test
    public void test8() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");;

        AccountService accountService = (AccountService) applicationContext.getBean("accountService");

        System.out.println(accountService.toString());
    }

運行測試以後,可以在控制檯看到以下內容:


優點:在獲取bean對象時,注入數據是必須的操作,否則對象無法創建成功。
缺點:改變了bean對象的實例化方式,使我們在創建對象時,如果用不到這些數據也必須提供。

setter方法注入

在bean標籤內部使用property標籤進行配置。
property標籤的屬性:

  • name:用於指定注入時所調用的set方法名稱
  • value:用於提供基本類型和String類型的數據
  • ref:用於指定其他的bean類型數據

這裏面我們注入了基本類型、包裝類型、日期類型數據。
AccountServiceImpl 類

public class AccountServiceImpl implements AccountService {

    private String name;
    private Integer age;
    private Date birthday;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("給name設置值");
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        System.out.println("給age設置值");
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        System.out.println("給birthday設置值");
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "AccountServiceImpl{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }

    public AccountServiceImpl(String name, Integer age, Date birthday) {
        System.out.println("含參的構造方法被調用了");
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public AccountServiceImpl() {
        System.out.println("構造方法調用");
    }

    @Override
    public int addMoney(int money) {
        System.out.println("向賬戶中加錢:" + money);
        return 0;
    }

    @Override
    public void saveAccount(Account account) {
        System.out.println("saveAccount方法執行了");
    }
}

bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountService" class="com.sks.service.imp.AccountServiceImpl">
        <!--注入基本類型、包裝類型、日期類型數據-->
       <property name="age" value="22"/>
        <property name="name" value="李四"/>
        <property name="birthday" ref="birthday"/>
    </bean>

    <bean id="birthday" class="java.util.Date"/>
</beans>

測試

    /**
     * 測試setter方法注入
     */
    @Test
    public void test9() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");;

        AccountService accountService = (AccountService) applicationContext.getBean("accountService");

        System.out.println(accountService.toString());
    }

運行測試以後,可以在控制檯看到以下內容:


優勢:創建對象時沒有明確的限制,可以直接使用默認構造函數。
缺點:如果又某個成員必須有值,則獲取對象有可能是set方法沒有執行。

對集合類型數據進行注入

AccountService2Impl 類

public class AccountService2Impl implements AccountService2 {

    private String[] myStrs;

    private List<String> myList;

    private Set<String> mySet;

    private Map<String, String> myMap;

    private Properties myProps;

    public String[] getMyStrs() {
        return myStrs;
    }

    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }

    public List<String> getMyList() {
        return myList;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public Set<String> getMySet() {
        return mySet;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public Map<String, String> getMyMap() {
        return myMap;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public Properties getMyProps() {
        return myProps;
    }

    public void setMyProps(Properties myProps) {
        this.myProps = myProps;
    }

    @Override
    public String toString() {
        return "AccountService2Impl{" +
                "myStrs=" + Arrays.toString(myStrs) +
                ", myList=" + myList +
                ", mySet=" + mySet +
                ", myMap=" + myMap +
                ", myProps=" + myProps +
                '}';
    }

}

bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountService2" class="com.sks.service.imp.AccountService2Impl">
        <property name="myStrs">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>

        <property name="myList">
            <list>
                <value>list1</value>
                <value>list2</value>
                <value>list3</value>
            </list>
        </property>

        <property name="mySet">
            <set>
                <value>set1</value>
                <value>set2</value>
                <value>set3</value>
            </set>
        </property>

        <property name="myProps">
            <props>
                <prop key="name">柯森</prop>
                <prop key="age">23</prop>
            </props>
        </property>

        <property name="myMap">
            <map>
                <entry key="key1" value="value1"/>
                <entry key="key2" value="value2"/>
                <entry key="key3">
                    <value>value3</value>
                </entry>
            </map>
        </property>  
    </bean>

</beans>

測試

    /**
     * 測試注入複雜類型/集合數據
     */
    @Test
    public void test10() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");

        AccountService2 accountService2 = (AccountService2) applicationContext.getBean("accountService2");

        System.out.println(accountService2.toString());
    }

運行測試以後,可以看到在控制檯打印輸出了以下內容:


這說明我們注入集合類型數據成功了。

註解注入

用於注入數據的註解

bean.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--創建bean時要掃描的包-->
    <context:component-scan base-package="com.sks"/>

</beans>

AccountService4Impl 類

@Component
public class AccountService4Impl implements AccountService3 {

    @Autowired
    private AccountDao accountDao;

    @Override
    public void addMoney(int money) {
        System.out.println("向賬戶中加錢....AccountService3Impl");
    }
}

假設此時只有一個AccountDao的實現類,並且這個類也加上了@Repository註解,那麼我們這樣注入是可以成功的,但是如果容器中存在多個AccountDao的實現類,此時僅僅使用AccountDao是不能完成數據注入的,需要配合@Qualifier註解使用注入數據。

假設現有如下兩個實現類,那我們應該怎麼寫才能成功注入數據?


@Component
public class AccountService4Impl implements AccountService3 {

    //錯誤寫法,默認會去容器中查找名稱爲accountDao的bean
     //@Autowired
    //private AccountDao accountDao;

    //正確寫法
    //@Autowired
    //private AccountDao accountDao1

    //正確寫法
    //@Autowired
    //private AccountDao accountDao1;

    //正確寫法
    @Autowired
    @Qualifier("accountDao1")
    private AccountDao accountDao;

    @Override
    public void addMoney(int money) {
        System.out.println("向賬戶中加錢....AccountService3Impl");
    }

}

測試

    @Test
    public void test2() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");
        AccountService4Impl accountService4 = (AccountService4Impl) applicationContext.getBean("accountService4Impl");

        System.out.println("accountService4:" + accountService4);
    }

@Value註解的基本使用
在使用@Value注入基本類型和String類型的數據時使用"#“號;使用@Value讀取配置文件的值時需要使用”$"符號,同時使用@PropertySource註解指定配置文件的位置。

@Component
@PropertySource("classpath:db.properties")
public class AccountService4Impl implements AccountService3 {

    @Autowired
    @Qualifier("accountDao1")
    private AccountDao accountDao;

    //使用SPEL表達式只注入值
    @Value("#{19 - 9}")
    private int age;

    @Value("zhangsan")
    private String name;

    //讀取操作系統的名稱
    @Value("#{systemProperties['os.name']}")
    private String osname;

    //讀取數據庫配置文件中的值
    @Value("${password}")
    private String password;

    @Override
    public void addMoney(int money) {
        System.out.println("向賬戶中加錢....AccountService3Impl");
    }

}

測試

    @Test
    public void test2() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");
        AccountService4Impl accountService4 = (AccountService4Impl) applicationContext.getBean("accountService4Impl");

        System.out.println("accountService4:" + accountService4 + " " + accountService4.getName() + " " + accountService4.getAge());

    }

斷點調試可以看到如下結果,說明我們使用@Value注入數據成功。


最後

歡迎關注公衆號:前程有光,領取一線大廠Java面試題總結+各知識點學習思維導+一份300頁pdf文檔的Java核心知識點總結!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章