3. SpringIOC的註解應用

1 使用JUnit

  1. 添加pom依賴
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
  1. 在方法上使用@Test註解,方法就可以被直接執行
    1. 給測試類起名字時,千萬不要定義成Test
    2. 測試類必須放在test包內
    3. 測試方法不能是static、不能有參數、不能有返回值

2 註冊bean到IOC容器中

  1. xml完整、全面,但註解的方式更快
  2. 如果想要將自定義的bean對象添加到IOC容器中,完成註冊bean的功能,就需要在類上添加某些註解
    1. @Controller:用於將控制層對象添加到IOC容器
    2. @Service:用於將業務邏輯對象添加到IOC容器
    3. @Repository:用於將數據訪問層對象添加到IOC容器
    4. @Component:給不屬於以上幾層的bean添加到IOC容器
  3. 我們雖然人爲的給不同的層添加不同的註解,但是在spring運行過程中,不會對這四個註解做任何區分,可以在任意層添加任意註解,提供了四個不同的註解只是爲了提高可讀性,實際開發中,最好能區分
  4. 使用註解需要如下步驟
    1. 添加上述四個註解中的任意一個
    2. 添加context命名空間
    3. 添加自動掃描的標籤context:component-scan,和指定掃描的基礎包base-package,默認情況下,spring會在啓動的時候會將基礎包及子包下,所有加了任意註解的類都自動掃描進IOC容器
<?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
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.mashibing"></context:component-scan>
</beans>
  1. bean的id默認就是組件的類名首字符小寫,如果非要改名字的話,需要在註解中添加參數值value
@Controller(value = "person")
public class PersonController
  1. 組件默認情況下都是單例的,如果需要配置多例模式的話,可以在註解下添加@Scope註解,並將其value屬性設置爲prototype
@Controller(value = "person")
@Scope(value = "prototype")
public class PersonController
  1. PersonController
package com.mashibing.controller;

import org.springframework.stereotype.Controller;

@Controller
public class PersonController {
    public PersonController() {
        System.out.println("創建對象");
    }
}
  1. PersonService
package com.mashibing.service;

import org.springframework.stereotype.Service;

@Service
public class PersonService {
}
  1. PersonDao.java
package com.mashibing.dao;

import org.springframework.stereotype.Repository;

@Repository("personDao")
@Scope(value="prototype")
public class PersonDao {
}

3 定義掃描包時要包含的類和不要包含的類

  1. component-scan的幾個屬性
    1. base-package:Spring將掃描的基礎package名,Spring會掃描該包以及其子孫包下的所有類
    2. use-default-filters:默認爲true,會將@Component、@Repository、@Service、@Controller修飾的類都自動實例化爲bean,並將其添加到IOC容器中,如果設置爲false,所有註釋的類都不實例化,可以理解爲默認使用一個可以允許所有註釋生效的include-filter
    3. include-filter:指定掃描時需要實例化的類型,我們可以從名字看到這是一個Filter,你可以自己定義該Filter,Spring爲我們提供了一套方便的實現,我們可以根據標註、類、包等相關信息決定當掃描到該類時是否需要實例化該類,需要注意的是如果你僅僅想掃描如@Controller不僅要加includeFilters,還需要將useDefaultFilters設置爲false
    4. exclude-filter:指定掃描到某個類時需要忽略它,實現和上一個Filter一樣,區別只是如果Filter匹配,Spring會忽略該類
  2. 首先通過exclude-filter 進行黑名單過濾,然後通過include-filter 進行白名單過濾,剩餘排除,但要注意的是,設置了use-default-filters爲true,就相當於已經設定了如下幾個include-filter
<context:component-scan base-package="com.mashibing" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  1. 一般實際開發使用exclude-filter較多,include-filter基本不用。如果引入的第三方包中某些類被@Component、@Repository、@Service、@Controller外的註解修飾,而我們又想將這些類交給Spring管理,此時就需要使用include-filter功能
  2. include-filtery與include-filter中的type屬性表示過濾規則
    1. annotation:按照註解進行排除,標註了指定註解的組件不要,expression表示要過濾的註解的全限定名
    2. assignable:指定排除某個具體的類,按照類排除,expression表示不註冊的具體類名,不能寫*,如果想寫多個,只能寫多條排除
    3. aspectj:後面講aop的時候說明要使用的aspectj表達式,不用
    4. custom:定義一個typeFilter,自己寫代碼(繼承某個類)決定哪些類被過濾掉,不用
    5. regex:使用正則表達式過濾,不用
  3. applicationContext.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
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.mashibing" use-default-filters="false">
        <context:include-filter type="assignable" expression="com.mashibing.service.PersonService"/>
    </context:component-scan>
</beans>

4 在屬性上使用@AutoWired進行自動注入

  1. 下面的實驗,主要實驗向PersonController中,注入不同的PersonService實例
  2. 使用AutoWired註解的時候,自動裝配的時候是根據類型實現的
    1. 如果只找到一個,則直接進行賦值
    2. 如果一個都沒找到(例如:需要注入的類存在,但該類沒被@Service註釋,也就是該類對應的bean沒由spring容器管理),則直接拋出異常
    3. 如果找到多個(例如PersonService 和PersonServiceExt兩個類之間是繼承關係,它們都是PersonService類型,而PersonController用的是PersonService類型定義的變量),此時會用PersonController類中變量名,查找容器中是否有與其同名的bean,如果找到了就進行裝配,找不到還是會報錯
  3. 使用@AutoWired與使用xml自動裝配不同
    1. @AutoWired是一定能夠裝配上的,因爲裝配不上就會報錯,而xml允許裝配不上,不會報錯,而是將該屬性值賦爲null
    2. @AutoWired是根據反射注入的,即使沒有setter方法,屬性也能注入成功,而xml方式中的byType和byName是通過setter方法注入的(constructor不是),沒有setter方法無法注入成功
  4. PersonController
package com.mashibing.controller;

import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {

    @Autowired
    private PersonService personService;

    public PersonController() {
        System.out.println("創建對象");
    }

    public void getPerson(){
        personService.getPerson();
    }
}
  1. PersonService
package com.mashibing.service;

import com.mashibing.dao.PersonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersonService {

    @Autowired
    private PersonDao personDao;

    public void getPerson(){
        personDao.getPerson();
    }
}
  1. PersonDao
package com.mashibing.dao;

        import org.springframework.stereotype.Repository;

@Repository
public class PersonDao {

    public void getPerson(){
        System.out.println("PersonDao:getPerson");
    }
}
  1. PersonServiceExt:用於測試有兩個相同類型相同的bean對象
package com.mashibing.service;

import com.mashibing.dao.PersonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersonServiceExt extends PersonService{

    @Autowired
    private PersonDao personDao;

    public void getPerson(){
        System.out.println("PersonServiceExt......");
        personDao.getPerson();
    }
}
  1. PersonController.java
package com.mashibing.controller;

import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {
	//修改了變量名後,裝配的就是PersonServiceExt實例了
    @Autowired
    private PersonService personServiceExt;

    public PersonController() {
        System.out.println("創建對象");
    }

    public void getPerson(){
        personServiceExt.getPerson();
    }
}
  1. 還可以使用@Qualifier註解,來指定當@AutoWired按類型找到多個bean時,不通過變量名與bean的id進行匹配,而是使用@Qualifier中的value值,與bean中id進行匹配
  2. PersonController.java
package com.mashibing.controller;

import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {

    @Autowired
    //不再使用變量名personServiceExt2與容器中bean的id進行匹配
    @Qualifier("personService")
    private PersonService personServiceExt2;

    public PersonController() {
        System.out.println("創建對象");
    }

    public void getPerson(){
        personServiceExt2.getPerson();
    }
}

5 在方法上使用@AutoWired

  1. 當我們查看@AutoWired註解的源碼的時候發現,此註解不僅可以使用在成員變量上,也可以使用在方法上
  2. 當方法上有@AutoWired註解時
    1. 此方法在bean創建的時候會自動調用
    2. 這個方法的每一個參數都會自動注入值,默認按照類型進行匹配
  3. @Qualifier註解也可以用在形參上,可以指定參數去匹配容器中指定id的bean
  4. PersonController.java
package com.mashibing.controller;

import com.mashibing.dao.PersonDao;
import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {

    @Qualifier("personService")
    @Autowired
    private PersonService personServiceExt2;

    public PersonController() {
        System.out.println("創建對象");
    }

    public void getPerson(){
        System.out.println("personController..."+personServiceExt2);
//        personServiceExt2.getPerson();
    }
    
    @Autowired
    public void test(PersonDao personDao){
        System.out.println("此方法被調用:"+personDao);
    }
    
    @Autowired
    public void test2(@Qualifier("personServiceExt") PersonService personService){
        System.out.println("此方法被調用:"+personService);
    }
}

6 自動裝配的註解@AutoWired,@Resource間區別

  1. 在使用自動裝配的時候,除了可以使用@AutoWired註解之外,還可以使用@Resource註解
  2. 二者區別
    1. @AutoWired是spring中提供的註解,@Resource:是jdk中定義的註解,依靠的是java的標準
    2. @AutoWired默認是按照類型進行裝配,如果需要按名稱裝配,需要與@Qualifier協同使用,且默認情況下要求依賴的對象必須存在,如果允許不存在需要將其required屬性設置爲false,@Resource默認是按照名字進行裝配的,名稱默認使用屬性名,也可以通過name屬性進行指定,名找不到再按type找,但要注意一旦設定了name屬性,就只能按名字裝配
    3. @AutoWired只能在spring框架中使用,而@Resource可以在其他框架中使用,擴展性更好

7 泛型依賴注入

  1. 自動注入時,如果屬性類型中存在泛型,默認會按類型+泛型一同進行裝配
  2. Student.java
package com.mashibing.bean;

public class Student {
}
  1. Teacher.java
package com.mashibing.bean;

public class Teacher {
}
  1. BaseDao.java
package com.mashibing.dao;

import org.springframework.stereotype.Repository;

@Repository
public abstract class BaseDao<T> {

    public abstract void save();
}
  1. StudentDao.java
package com.mashibing.dao;

import com.mashibing.bean.Student;
import org.springframework.stereotype.Repository;

@Repository
public class StudentDao extends BaseDao<Student>{
    public void save() {
        System.out.println("保存學生");
    }
}
  1. TeacherDao.java
package com.mashibing.dao;

import com.mashibing.bean.Teacher;
import org.springframework.stereotype.Repository;

@Repository
public class TeacherDao extends BaseDao<Teacher> {
    public void save() {
        System.out.println("保存老師");
    }
}
  1. MyService.java
package com.mashibing.service;

import com.mashibing.bean.Teacher;
import com.mashibing.dao.BaseDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @Autowired
    private BaseDao<Teacher> studentDao;
    //當屬性的泛型類型不同時,通過MyTest執行結果觀察到,會注入不同的對象
    //private BaseDao<Student> studentDao;

    public void save() {
        studentDao.save();
    }
}
  1. MyTest.java
package com.mashibing.bean;

import com.mashibing.service.MyService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.sql.SQLException;

public class MyTest {
    public static void main(String[] args) throws SQLException {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        MyService studentService = context.getBean("myService", MyService.class);
        studentService.save();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章