天天看點

Spring 基于注解管理bean

說明:

參考資料:b戰尚矽谷楊博超老師的ssm整合視訊

2.3基于注解管理bean

2.3.1、實驗一:标記與掃描

①注解

和 XML 配置檔案一樣,注解本身并不能執行,注解本身僅僅隻是做一個标記**,具體的功能是架構檢測 到注解标記的位置,然後針對這個位置按照注解标記的功能來執行具體操作。**

**本質上:**所有一切的操作都是Java代碼來完成的,XML和注解隻是告訴架構中的Java代碼如何執行。

舉例:元旦聯歡會要布置教室,藍色的地方貼上元旦快樂四個字,紅色的地方貼上拉花,黃色的地方貼 上氣球。

Spring 基于注解管理bean
②掃描

Spring 為了知道程式員在哪些地方标記了什麼注解,就需要通過掃描的方式,來進行檢測。然後根據注 解進行後續操作。

③建立Maven Module

配置pom.xml。 注意:建立Maven Module後做的第一件事就是配置pom.xml

<dependencies>
    <!-- 基于Maven依賴傳遞性,導入spring-context依賴即可導入目前所需所有jar包 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.1</version>
    </dependency>
    <!-- junit測試 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
           
④建立spring配置檔案

取名applicationContext.xml,放到resources下.

暫時不寫内容

⑤辨別元件的常用注解

每一個bean都是ioc容器中的元件,使用下面這些 辨別元件的注解,可以将類的對象交給ioc容器管理。

這4個注解的功能是一樣的。

@Component:将類辨別為普通元件

@Controller:将類辨別為控制層元件

@Service:将類标 識為業務層元件

@Repository:将類辨別為持久層元件

問:以上四個注解有什麼關系和差別?

Spring 基于注解管理bean

通過檢視源碼我們得知,@Controller、@Service、@Repository這三個注解隻是在@Component注解 的基礎上起了三個新的名字。

對于Spring使用IOC容器管理這些元件來說沒有差別。是以@Controller、@Service、@Repository這 三個注解隻是給程式員看的,讓我們能夠便于分辨元件的作用、功能。

4個注解的功能一樣。

注意:雖然它們本質上一樣,但是為了代碼的可讀性,為了程式結構嚴謹我們程式員肯定不能随便胡亂标記。

⑥建立元件

先建立好三層,即controller層、service層、dao層,然後使用注解建立元件. 注意:這4個注解不能放到接口上,因為接口不能建立對象。

Spring 基于注解管理bean

建立控制層元件UserController

@Controller
public class UserController {
}

           

建立接口UserService

public interface UserService {
}

           

建立業務層元件UserServiceImpl

@Service
public class UserServiceImpl implements UserService {
}
           

建立接口UserDao

public interface UserDao {
}

           

建立持久層元件UserDaoImpl

@Repository
//我是這樣記得。因為dao層是與資料庫互動的,而Repository是倉庫的意思,都有 庫 的含義,是以,用@Repository辨別
//建立dao層元件
public class UserDaoImpl implements UserDao {
}

           
⑦掃描元件

需要在spring的配置檔案中配置。我這的spring配置檔案叫 applicationContext.xml

掃描就是讓spring架構知道,我在哪加了注解,加了什麼注解。然後,spring架構就會根據注解執行相應的操作

使用context:component-scan 标簽

情況一:最基本的掃描方式

通過包來掃描,會掃描包下的所有類。
<context:component-scan base-package="com.atguigu.spring">
</context:component-scan>
           

情況二:指定要排除的元件

<context:component-scan base-package="com.atguigu.spring">
        <!-- context:exclude-filter标簽:指定排除規則。就是不掃描哪些元件 -->
        <!--
        type:設定排除的依據
        type="annotation",根據注解排除,expression中設定要排除的注解的全類名 (常用)
        type="assignable",根據類型排除,expression中設定要排除的類型的全類名
        -->
        <!--不掃描com.atguigu.spring包下,使用Controller注解的類-->
        <context:exclude-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"/>
        <!--<context:exclude-filter type="assignable"
        expression="com.atguigu.controller.UserController"/>-->
    </context:component-scan>
           
注解的全類名:找到寫的注解,然後右鍵,copy Reference(快捷鍵:Ctrl+Alt+Shift+c)。當然,也可以自己敲

情況三:僅掃描指定元件(很少用)

<context:component-scan base-package="com.atguigu.spring" use-default-filters="false">
        <!-- context:include-filter标簽:包含掃描,指定在原有掃描規則的基礎上追加的規則 -->
        <!-- use-default-filters屬性:取值false表示關閉預設掃描規則 -->
        <!-- 此時必須設定use-default-filters="false",因為預設規則就是掃描指定包下所有類 -->
        <!--
        type:設定包含的依據
        type="annotation",根據注解包含,expression中設定要包含的注解的全類名
        type="assignable",根據類型包含,expression中設定要包含的類型的全類名
        -->
        <!--隻掃描com.atguigu.spring包下,使用Controller注解的類-->
        <context:include-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"/>
        <!--<context:include-filter type="assignable"
        expression="com.atguigu.controller.UserController"/>-->
    </context:component-scan>
           
⑧測試
@Test
    public void testIocByAnnotation(){
        //擷取ioc容器
        ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
        //擷取對象. 因為咱已經通過注解,将些類的對象交給了ioc容器管理,是以,ioc容器中有這些對象,可以直接擷取。
        UserController userController = ioc.getBean(UserController.class);
        UserService userService = ioc.getBean(UserService.class);//通過類型擷取時,可以使用類本身 |父類| 實作的接口類型都可
        UserDao userDao = ioc.getBean(UserDao.class);
        System.out.println(userController);
        System.out.println(userService);
        System.out.println(userDao);
    }
           

能有輸出結果(輸出的都是位址),說明我們已經實作了 通過注解的方式将對象交給ioc容器管理了。

⑨bean的id

在我們使用XML方式管理bean的時候,每個bean都有一個唯一辨別(使用bean标簽的id屬性設定),便于在其他地方引用。現在使用 注解後,每個元件仍然應該有一個唯一辨別。

a>預設情況 :

類名首字母小寫就是bean的id。

例如:UserController類對應的bean的id就是userController。

b> 自定義bean的id :

可通過辨別元件的注解的value屬性設定自定義的bean的id

@Service(“userService”)//預設為userServiceImpl public class UserServiceImpl implements UserService {}

隻設定注解的value屬性時,value=“”可以省略。

bean的預設id

@Test
    public void testIocByAnnotation(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
        //通過注解+掃描所配置的bean的id,預設為類的小駝峰,即類名的首字母小寫
        UserController userController = (UserController) ioc.getBean("userController");
        System.out.println(userController);
         //注意,通過預設的id擷取時,不能用接口,得用類(or實作類)
        UserService userService = (UserService) ioc.getBean("userServiceImpl");
        System.out.println(userService);
    }
           

2.3.2、實驗二:基于注解的自動裝配

回顧:

自動裝配:根據指定的政策,在IOC容器中比對某一個bean,自動為指定的bean中所依賴的類類型或接口類 型屬性指派

①場景模拟

參考基于xml的自動裝配

在UserController中聲明UserService對象

在UserServiceImpl中聲明UserDao對象

Spring 基于注解管理bean
②@Autowired注解

在成員變量上直接标記@Autowired注解即可完成自動裝配,不需要提供setXxx()方法。以後我們在項

目中的正式用法就是這樣。

@Autowired: 實作自動裝配功能的注解

  1. @Autowired注解能夠辨別(放)的位置。 辨別–在這是 動詞
    • 放在成員變量上,此時不需要設定成員變量的set方法
    • 放在set方法上
    • 放在 為目前成員變量指派的有參構造上
@Controller
public class UserController {
    //在UserController中需要用到UserService
    @Autowired
    private UserService userService;

    public void saveUser(){
        userService.saveUser();
    }
}
           
public interface UserService {
    void saveUser();
}
           
@Service
public class UserServiceImpl implements UserService {
    //在UserServiceImpl中需要用到UserDao
    @Autowired
    private UserDao userDao;

    @Override
    public void saveUser() {
        userDao.insertUser();
    }
}
           
public interface UserDao {
    void insertUser();
}

           
@Repository
public class UserDaoImpl implements UserDao {

    @Override
    public void insertUser() {
        System.out.println("儲存使用者---儲存成功");
    }
}

           

spring的配置檔案applicationContext.xml中,隻有注解掃描的配置

③測試
@Test
    public void testIocByAnnotation(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserController userController = ioc.getBean(UserController.class);
        userController.saveUser(); //成功執行,說明使用注解自動裝配 成功
    }
           
④@Autowired注解其他細節
@Autowired注解可以标記在構造器和set方法上
@Controller
public class UserController {
    //在UserController中需要用到UserService
   
    private UserService userService;
    //放在 為目前成員變量指派的有參構造上
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void saveUser(){
        userService.saveUser();
    }
}
           
@Controller
public class UserController {
    //在UserController中需要用到UserService

    private UserService userService;
    //放在成員變量的set方法上
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void saveUser(){
        userService.saveUser();
    }
}

           
⑤@Autowired工作流程
@Autowired的工作原理。
Spring 基于注解管理bean
Spring 基于注解管理bean
private UserService userService;//userService就是變量名
private UserService a;//a就是變量名
           

我的對@Autowire工作原理的了解:

a》預設通過byType的方式,在ioc容器中通過類型比對某個bean為屬性指派

b》若ioc中有多個類型比對的bean,此時會自動轉換為byName的方式實作自動裝配的效果

​ 即,将要指派的屬性的屬性名作為bean的id 去比對某個bean為屬性指派

c》若byType和byName的方式都無法實作自動裝配,即ioc容器中有多個類型比對的bean且這些bean的

​ id和要指派的屬性的屬性名都不一緻,此時抛出異常:NoUniqueDefinitionException

d》此時,可以在要指派的屬性上,添加一個注解@Qualifier(“xxxx”),通過該注解的value屬性值,指定

​ 某個bean的id,将這個bean為屬性指派。

@Controller
public class UserController {
    //在UserController中需要用到UserService
    @Autowired
    @Qualifier("userServiceImpl")
    private UserService userService;
    
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void saveUser(){
        userService.saveUser();
    }
}
           

@Autowired中有屬性required,預設值為true,是以在自動裝配無法找到相應的bean時,會裝 配失敗

可以将屬性required的值設定為false,則表示能裝就裝,裝不上則使用bean屬性的 預設值 。

但是實際開發時,基本上所有需要裝配元件的地方都是必須自動裝配的(Autowired),用不上這個required屬性。

開發中,一個類型的bean在ioc中不會設定多個