1. 程式人生 > >Spring Boot常用註解(二)

Spring Boot常用註解(二)

1.概述

Spring Boot常用註解(一) - 宣告Bean的註解 中學習了Spring Boot中宣告Bean的註解
那Spring容器中的Bean是怎麼實現自動裝配(依賴)的呢?
這就是接下來學習的注入註解咯

注入Bean的註解:

  • @Autowired
  • @Inject
  • @Resource

[email protected]註解

@Autowired註解原始碼:

package org.springframework.beans.factory.annotation;

/**
 1. @since 2.5
 2. @see AutowiredAnnotationBeanPostProcessor
 3. @see
Qualifier 4. @see Value */
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { boolean required() default true; }
  1. @Autowired註解作用在建構函式、方法、方法引數、類欄位以及註解上
  2. @Autowired註解可以實現Bean的自動注入

2.1 @Autowired註解原理

在Spring Boot應用啟動時,Spring容器會自動裝載一個org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor處理器,當容器掃掃描到@Autowired註解時,就會在IoC容器就會找相應型別的Bean,並且實現注入。

2.2 @Autowired註解使用

在Web MVC中控制層(Controller)訪問的是業務層(Service),而業務層(Service)訪問的是資料層(Dao),使用@Autowired註解實現注入

資料層:

package com.example.demo.chapter1.useannotation.autowired.dao;

/**
 * 資料層介面,用於訪問資料庫,返回資料給業務層
 * */
public interface IDao {
    public String get();
}

/**
 * 資料層介面實現類
 * 
 * 1.資料層Bean用@Repository標註
 * 2.當前Bean的名稱是"autowiredUserDaoImpl"
 * 3.設定當前Bean的為原型模式,即每次獲取Bean時都建立一個新例項
 * */
@Repository("autowiredUserDaoImpl")
@Scope("prototype")
public class UserDaoImpl implements IDao {

    public String get() {
        return "@Autowired註解實現自動裝配";
    }
}

業務層:

package com.example.demo.chapter1.useannotation.autowired.service;

/**
 * 業務層介面
 * 從資料層獲取資料,處理結果返回給控制層
 * */
public interface IService {
    public String get();
}

/**
 * 業務層介面實現
 * 
 * 1.資料層Bean用@Service標註
 * 2.當前Bean的名稱是"autowiredUserServiceImpl"
 * 3.設定當前Bean的為原型模式,即每次獲取Bean時都建立一個新例項
 * 4.業務層有一個數據層介面IDao,使用@Autowired註解標註
 * */

@Service("autowiredUserServiceImpl")
@Scope("prototype")
public class UserServiceImpl implements IService {
    /**
     * @Autowired實現自動裝配
     * Spring IoC容器掃描到@Autowired註解會去查詢IDao的實現類,並自動注入
     * */
    @Autowired
    private IDao dao;

    @Override
    public String get() {
        return dao.get();
    }
}
  1. Bean通過註解@Service宣告為一個Spring容器管理的Bean,Spring容器會掃描classpath路徑下的所有類,找到帶有@Service註解的UserServiceImpl類,並根據Spring註解對其進行初始化和增強,命名為autowiredUserServiceImpl
  2. Spring容器如果發現此類屬性dao也有註解@Autowired,則會從Spring容器中查詢一個已經初始化好的IDao的實現類,如果沒有找到,則先初始化一個IDao實現類

控制層:

package com.example.demo.chapter1.useannotation.autowired.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.chapter1.useannotation.autowired.service.IService;

/**
 1. 控制層
 2. 
 3. 1.控制層使用@RestController註解標註,返回json格式的字串
 4. 2.獲取業務層返回的資料,輸出到客戶端
 5. 3.請求:http:localhost:8080/autowiredController
 6. */
@RestController
public class AutowiredController {
    @Autowired
    private IService service;

    @RequestMapping("/autowiredController")
    public String get () {
        return service.get();
    }
}

測試結果:
1. 啟動Spring Boot應用
2. 瀏覽器輸入:http:localhost:8080/autowiredController
3. 返回結果:
這裡寫圖片描述

2.3 @Qualifier註解

@Qualifier註解原始碼:

package org.springframework.beans.factory.annotation;

/**
 * @since 2.5
 * @see Autowired
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

    String value() default "";

}
  1. 如果Spring 容器中有兩個資料層介面IDao的實現類
  2. 而業務層通過@Autowired註解標註的是IDao介面
  3. 這時就需要@Qualifier註解來指定需要自動裝配的Bean的名稱
  4. 否則會報如下的錯:
Description:

Field dao in com.example.demo.chapter1.useannotation.autowired.service.UserServiceImpl required a single bean, but 2 were found:
    - autowiredAdminDaoImpl: defined in file [D:\eclipse\workspace\boot\demo\target\classes\com\example\demo\chapter1\useannotation\autowired\dao\AdminDaoImpl.class]
    - autowiredUserDaoImpl: defined in file [D:\eclipse\workspace\boot\demo\target\classes\com\example\demo\chapter1\useannotation\autowired\dao\UserDaoImpl.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

2.4 @Qualifier註解使用

資料層介面IDao:

public interface IDao {
    public String get();
}

IDao實現類 - UserDaoImpl

@Repository("autowiredUserDaoImpl")
@Scope("prototype")
public class UserDaoImpl implements IDao {

    public String get() {
        return "@Autowired註解實現自動裝配 - UserDaoImpl";
    }
}

IDao實現類 - AdminDaoImpl

@Repository("autowiredAdminDaoImpl")
@Scope("prorotype")
public class AdminDaoImpl implements IDao {

    @Override
    public String get() {
        return "@Autowired註解實現自動裝配 - AdminDaoImpl";
    }
}

@Qualifier註解使用:

@Service("autowiredUserServiceImpl")
@Scope("prototype")
public class UserServiceImpl implements IService {
    /**
     * @Autowired實現自動裝配
     * Spring IoC容器掃描到@Autowired註解會去查詢IDao的實現類,並自動注入
     * */
    @Autowired
    @Qualifier("autowiredUserDaoImpl")
    private IDao dao;

    @Override
    public String get() {
        return dao.get();
    }
}

測試結果:
這裡寫圖片描述

2.4 @Autowired註解的requird屬性

在使用@Autowired註解時,首先在容器中查詢對應型別的bean

  1. 如果查詢結果Bean剛好為一個,自動注入
  2. 如果查詢結果Bean不止一個,通過@Qualifier註解指定自動裝配Bean的名稱
  3. 如果沒有查詢到對應型別的Bean,由於預設@Autowired(required=true),會丟擲異常,解決方法是使用@Autoiwired(quired=false)
  4. @Autowired(quired=true)意味著依賴是必須的
  5. @Autowired(quired=false)等於告訴 Spring:在找不到匹配 Bean 時也不報錯

資料層介面:

package com.example.demo.chapter1.useannotation.autowired.dao;

/**
 * 資料層介面
 * */
public interface ITask {
    public String get();
}

業務層介面:

package com.example.demo.chapter1.useannotation.autowired.service;

@Service("taskServiceImpl")
@Scope("prototype")
public class TaskServiceImpl implements IService {

    @Autowired
    private ITask task;

    @Override
    public String get() {
        return task.get();
    }
}

如果沒有介面ITask的實現類,啟動Spring Boot應用會報如下錯:
沒有找到ITask的實現類

Description:

Field task in com.example.demo.chapter1.useannotation.autowired.service.TaskServiceImpl required a bean of type 'com.example.demo.chapter1.useannotation.autowired.dao.ITask' that could not be found.


Action:

Consider defining a bean of type 'com.example.demo.chapter1.useannotation.autowired.dao.ITask' in your configuration.

解決方法 - 新增required=false

@Service("autowiredTaskServiceImpl")
@Scope("prototype")
public class TaskServiceImpl implements IService {

    /**
     * 介面ITask沒有實現類
     * 新增required=false不報錯
     * */
    @Autowired(required=false)
    private ITask task;

    @Override
    public String get() {
        return task.get();
    }
}

啟動成功(埠號是8090)
這裡寫圖片描述

因為ITask沒有實現類,瀏覽器訪問http:localhost:8090/requirdController時會報空指標:

java.lang.NullPointerException: null
    at com.example.demo.chapter1.useannotation.autowired.service.TaskServiceImpl.get(TaskServiceImpl.java:23) ~[classes/:na]
    at com.example.demo.chapter1.useannotation.autowired.controller.RequirdController.get(RequirdController.java:18) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_60]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_60]