Spring Bean管理

来源:https://www.cnblogs.com/yangyuanhu/archive/2020/01/07/12164380.html
-Advertisement-
Play Games

IOC容器 工廠只負責創建對象,而Spring當然不僅僅是一個對象工廠;其核心是一個對象容器,由於具備控制反轉的能力,所以也叫它IOC容器; 容器可以理解為存放對象的地方,當然不僅僅是存儲,還有對象的管理,包括 創建 銷毀 裝配; 這樣原本程式要做的事情交給了Spring,所以這屬於IOC,稱之為I ...


IOC容器

工廠只負責創建對象,而Spring當然不僅僅是一個對象工廠;其核心是一個對象容器,由於具備控制反轉的能力,所以也叫它IOC容器;

容器可以理解為存放對象的地方,當然不僅僅是存儲,還有對象的管理,包括-創建-銷毀-裝配; 這樣原本程式要做的事情交給了Spring,所以這屬於IOC,稱之為IOC容器;

Spring有兩個容器介面ApplicationContext是BeanFactory的子介面。它們都可以作為Spring的容器;

image-20200104180458270

兩種容器的區別:

  • BeanFactory採取的懶載入的方式,在獲取對象時才會實例化
  • ApplicationContext會在工廠初始化時立即實例化對象
  • BeanFactory作為頂級介面主要面向於Spring框架本身,僅提供了基礎基本的容器功能如DI

  • ApplicationContext,是BeanFactory的子介面,意味著功能比BeanFactory更多,諸如國際化,註解配置,XML配置等等,因此ApplicationContext使用更多
    • ApplicationContext的兩個實現類區別:
    • ClassPath表示從類路徑中獲取配置文件,
    • FileSystem表示從文件系統獲取配置文件

SpringBean的管理

Bean的實例化

1. 使用類構造器(預設無參數)

該方式Bean類中必須存在無參構造器

<bean id="UserService1" class="com.yyh.serviceimpl.UserServiceImpl"/>

2. 使用靜態工廠方法

xml配置:

<bean id="userService" class="com.yyh.serviceimpl.ServiceFactory" factory-method="getService"/>

工廠:

import com.yyh.service.UserService;
public class ServiceFactory {
    public static UserService getService() {
          System.out.println("factory static run!");
        return new UserServiceImpl();
    }
}

3. 使用實例工廠方法

xml配置:

<!--工廠Bean-->
<bean id="serviceFactory" class="com.yyh.serviceimpl.ServiceFactory"/>
<!--Service Bean-->
<bean id="userService2" factory-bean="serviceFactory" factory-method="getService2"/>

工廠添加方法:

public UserService getService2() {
    System.out.println("factory instance run!");
    return new UserServiceImpl();
}

Bean的命名

配置Bean時,可以使用 id 或者 name 屬性給bean命名。 id 和 name 屬性作用上一樣,推薦使用id。

  • id取值要求嚴格些,必須滿足XML的命名規範。id是唯一的,配置文件中不允許出現兩個id相同的bean。

  • name取值比較隨意,甚至可以用數字開頭。在配置文件中允許出現多個name相同的bean,在用getBean()返回實例時,最後的一個Bean將被返回。

    註意:在spring5中name和id一樣也不允許有重覆的名稱。

  • 如果沒有id,name,則用類的全名作為name

    <bean class="test.Test"> ,可以使用 getBean("test.Test") 返回該實例。

  • 如果存在多個id和name都沒有指定,且類都一樣的,如:

    <bean class="com.yh.service.UserService"/>
    <bean class="com.yh.service.UserService"/> 
    <bean class="com.yh.service.UserService"/>

    則可以通過getBean(“完整類名#索引”)來獲得,如:getBean("com.yh.service.UserService#1"),索引從0開始,若要獲取第一個則可以忽略索引,直接寫類名

  • name中可以使用分號(“;”)、空格(“ ”)或逗號(“,”)來給這個Bean添加多個名稱(相當於別名 alias 的作用)。如:

    "
    name=“a b c d”等同於 name=“a,b,c,d” 這樣寫相當於有 1 2 3 4(4個)個標識符標識當前bean id=“1 2 3 4” 這樣寫相當於有 “1 2 3 4”(1個)個標識符標識當前bean
    "

    而id中的任何字元都被作為一個整體 ;

  • 如果既配置了 id ,也配置了 name ,則兩個都生效。當然也不能重覆;

  • 當註解中出現與xml配置中相同的id或相同name時,優先是用xml中的配置

Bean的作用域

類別 說明
singleton 預設值; 在Spring IoC容器中僅存在一個Bean實例,Bean以單例方式存在。
prototype 每次從容器中調用Bean時,都返回一個新的實例;
request 每次HTTP請求都會創建一個新的Bean,該作用域僅適用於WebApplicationContext環境
session 同一個HTTP Session 共用一個Bean,不同Session使用不同Bean,僅適用於WebApplicationContext 環境
application Bean的作用域為ServletContext ,僅適用於WebApplicationContext環境。

作用域就是指作用範圍:單例則表示對象的作用範圍是整個Spring容器,而prototype則表示不管理作用範圍,每次get就直接創建新的

生命周期

init和destroy

Spring提供了非入侵(不強制類繼承或實現)方式的生命周期方法,可以在Bean的初始化以及銷毀時做一些額外的操作

<bean id="service" class="com.yh.service.UserService" scope="singleton"
      init-method="init" destroy-method="destroy"/>
<!--
init-method     用於初始化操作
detroy-method       用於銷毀操作

註意:destroy僅在scope為singleton時有效 因為多例情況下

Bean的完整生命周期

執行順序及其含義:

1 構造對象
2 設置屬性
3 瞭解Bean在容器中的name
4 瞭解關聯的beanFactory
5 初始化前處理
6 屬性設置完成
7 自定義初始化方法
8 初始化後處理
9 業務方法
10 Bean銷毀方法
11 自定義銷毀方法

依賴註入

依賴指的是當前對象在運行過程中需要使用到的其他參數,Spring可以幫助我們來完成這個依賴關係的建立,說的簡單點就是把你需要參數的給你,而你不用管參數怎麼來的,已達到儘可能的解耦 ;

舉個例子:

Controller 中需要Service對象,Spring可以把Service自動丟到你的Controller中,你不需要關係Service是怎麼來的,用就完了;

要使用依賴註入,必須現在需要依賴的一方(Controller)中為被依賴的一方(Service)定義屬性,用以接收註入;

構造方法註入

bean:

public class User2 {
    private String name;
    private int age;
    private Phone phone;

    public User2(String name, int age, Phone phone) {
        this.name = name;
        this.age = age;
        this.phone = phone;
    }
    @Override
    public String toString() {
        return "User2{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phone=" + phone +
                '}';
    }
}

xml:

 <!--依賴註入-->
    <bean id="user" class="com.yh.demo2.User2">
        <!--按參數名稱註入        -->
        <constructor-arg name="name" value="jerry"/>
        <!--按參數位置註入           -->
        <constructor-arg index="1" value="18"/>

        <!--參數類型為其他bean對象時value換成ref        -->
        <constructor-arg name="phone" ref="phone"/>

        <!--type指定類型不常用        -->
        <!--<constructor-arg type="java.lang.String" name="name" value="jerry"/>-->
    </bean>
    <!--user需要的依賴phoneBean-->
    <bean id="phone" class="com.yh.demo2.Phone"/>

setter方法註入

依然對上面的User2類的依賴進行註入

<!--setter方法註入(屬性註入)  -->
<bean id="user2" class="com.yh.demo2.User2">
    <property name="name" value="jerry"/> <!--註入常量值-->
    <property name="age" value="20"/>
    <property name="phone" ref="phone"/> <!--註入其他Bean-->
</bean>

註意:上述配置要求User2必須存在空參構造器

c命名標簽

上面通過嵌套標簽constructor的方式註入依賴,在需要註入的依賴較多時導致xml顯得很臃腫,C名稱空間來簡化xml中<constructor-arg>標簽的書寫

使用前需要先在xml頭部進行聲明

xmlns:c="http://www.springframework.org/schema/c"

image-20200107161358548

使用:

<!--c命名空間的使用-->
<bean id="user3" class="com.yh.demo2.User2" c:name="jerry" c:_1="21" c:phone-ref="phone"></bean>
<!--
c:name              指定為name參數賦值
c:_1                指定為構造函數的第2個參數賦值
c:phone-ref   指定為構造函數的第phone參數賦值為id為"phone"的Bean
-->

p命名標簽

同樣的p命名空間則是用於簡化<property>標簽的書寫

聲明:

xmlns:p="http://www.springframework.org/schema/p"

使用:

<bean id="user4" class="com.yh.demo2.User2" p:name="jerry" p:age="20" p:phone-ref="phone"/>
<!--
p:name              指定為name屬性賦值
p:age               指定為age屬性賦值
p:phone-ref   為phone屬性賦值為id為"phone"的Bean
-->

SpEL註入

SpEL即Spring Expression Language的縮寫,與JSTL一樣是表達式語言,可以支持使用更加複雜的語法註入依賴,包括標準數學運算符,關係運算符,邏輯運算符,條件運算符,集合和正則表達式等;

語法:#{表達式}

用例:

<!--SpEL    -->
<bean id="user5" class="com.yh.demo2.User2">
<!--<property name="name" value="#{'jerry'}"/>-->           <!--字元常量-->
<!--<property name="age" value="#{100.0}"/>-->              <!--數字常量-->
<!--<property name="phone" value="#{phone}"/>-->            <!--對象引用-->
<!--<property name="name" value="#{phone.model.concat(' jerry')}"/>--> <!--方法調用-->
<!--<property name="age" value="#{1+100}"/>-->              <!--算數符-->
<!--<property name="name" value="#{'11' > '22'}"/>-->       <!--比較符-->
<!--<property name="name" value="#{true or false}"/>-->     <!--邏輯符-->
<!--<property name="name" value="#{1 > 0?1:0}"/>-->         <!--三目-->
</bean>

容器類型的註入

<!--    容器數據類型註入-->
<bean id="user100" class="com.yh.demo2.User3">
    <!--set註入        -->
    <property name="set">
        <set>
            <value>3</value>
            <value>3</value>
            <value>a</value>
        </set>
    </property>
    <!--list註入        -->
    <property name="list">
        <list>
            <value>3</value>
            <value>3</value>
            <value>a</value>
        </list>
    </property>
    <!--map註入        -->
    <property name="map">
        <map>
            <entry key="name" value="jerry"/>
            <entry key="age" value="18"/>
            <entry key="sex" value="man"/>
        </map>
    </property>
    <!--properties註入        -->
    <property name="properties">
        <props>
            <prop key="jdbc.user">root</prop>
            <prop key="jdbc.password">admin</prop>
            <prop key="jdbc.driver">com.mysql.jdbc.Driver</prop>
        </props>
    </property>
</bean>

強調:Spring的依賴註入要麼通過構造函數,要麼通過setter,什麼介面註入都tm扯犢子;

介面註入不是一種註入方式,只不過由於OOP的多態,Spring在按照類型註入時,會在容器中查找類型匹配的Bean,如果沒有則查找該類的子類,如果容器中有多個匹配的子類Bean時會拋出異常,坑了一堆人,然後就開始意淫給這個問題取個名字吧.....介面註入.....

註解配置

註解配置Bean

通用註解

​ @Component 用於在Spring中加入Bean

MVC場景下

​ @Controller 等價於 @Component 標註控制層

​ @Service 等價於 @Component 標註業務層

​ @Repository 等價於 @Component 標註數據訪問層(DAO)

在實現上沒有任何不同,僅僅是為了對Bean進行分層是結構更清晰

使用步驟:

​ 1.需要依賴context和aop兩個jar包

​ 2.添加命名空間

​ 3.指定掃描的註解所在的包

<?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
       http://www.springframework.org/schema/context/spring-context.xsd">


    <!--指定要掃描註解的包    -->
    <context:component-scan base-package="com.yh.demo"/>


</beans>

要註冊的Bean:

import org.springframework.stereotype.Component;

@Component("userService)
public class UserService {

    public String hello(String name){
        return "hello " + name;
    }
}

測試:

public class Tester {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService service = (UserService) context.getBean("userService");
        System.out.println(service.hello("jerry"));
    }
}

若註解中沒有指定id則預設使用簡單類名且小寫開頭,userService

註解註入

@Value用於對基本類型屬性進行註入

@Autowired將容器中的其他Bean註入到屬性中

@Qualifier("BeanID") 指定要註入的Bean的ID

準備UserDAO類:

import org.springframework.stereotype.Repository;

@Repository("userDAO")
public class UserDAO {
    public void save(){
        System.out.println("user saved!");
    }
}

UserService類:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("userService")
public class UserService {

    @Value("hello")//基本類型
    private String info;
        //@Autowired(required = false) //預設為true表示屬性時必須的不能為空
    @Autowired //註入類型匹配的Bean
    //@Qualifier("userDAO") //明確指定需要的BeanID
    private UserDAO userDAO;

//set/get.....
}

測試:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Tester {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService service = (UserService) context.getBean("userService");
        System.out.println(service.getInfo());//普通屬性測試
        service.getUserDAO().save();//對象屬性測試
    }
}

Autowired預設自動註入類型一致的Bean;required屬性用於設置屬性是否是必須的預設為true

Qualifier需要和Autowired搭配使用,用於明確指定要註入的Bean的ID

註意:

當Spring中存在多個類型都匹配的Bean時直接報錯

介面:

public interface PersonDao {
}

兩個實現類:

@Repository()
public class PersonDaoImpl1 implements PersonDao{
}
@Repository()
public class PersonDaoImpl2 implements PersonDao{
}

註入:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("userService")
public class UserService {
    @Autowired
    private PersonDao personDao;
}

@Resource

Qualifier和Autowired書寫繁瑣,@Resource可將兩個標簽的功能整合,即註入指定ID的Bean

@Resource標準註解的支持是JSR-250中定義的,所以時使用需要導入擴展包,Maven依賴如下:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

Resource預設按照使用屬性名稱作為id查找,查找失敗則使用類型查找

可以利用name屬性指定通過id查找

也可通過type指定類型,當出現相同類型的多個Bean時拋出異常

import javax.annotation.Resource;

@Component("userService")
public class UserService {
        //@Resource()//預設按照id/name
    //@Resource(name="xx")//指定name
    //@Resource(type = PersonDaoImpl1.class) //指定type
    @Resource(name="xx",type = PersonDaoImpl1.class)//同時指定name和type
    private PersonDao personDao;
}

@Scope

用於標註Bean的作用域

@Repository()
@Scope("prototype") //每次get都創建新的 
public class UserDAO {
    public void save(){
        System.out.println("user saved!");
    }
}

因為註解的表達能力有限,很多時候無法滿足使用需求;我們可以將註解和XML配合使用,讓XML負責管理Bean,註解僅負責屬性註入;


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 0-前言 我們一個個微服務構建好了,外部的應用如何來訪問內部各種各樣的微服務呢?在微服務架構中,後端服務往往不直接開放給調用端,而是通過一個API網關根據請求的url,路由到相應的服務。當添加API網關後,在第三方調用端和服務提供方之間就創建了一個代理層,這個代理層直接與調用方通信進行許可權控制,後將 ...
  • 0-為什麼需要熔斷器 在分散式系統中,各個服務相互調用相互依賴,如果某個服務掛了,很可能導致其他調用它的一連串服務也掛掉或者在不斷等待中耗盡伺服器資源,這種現象稱之為雪崩效應; 未來防止系統雪崩,熔斷機制必不可少,就是當一個服務掛掉後,調用它的服務能快速熔斷,不再耗費資源,快速失敗並提供回退方案; ...
  • 醫院信息集成平臺(ESB)實施、建設方案 基於中立、標準、開放的IT架構和數據標準,打造插拔式醫院應用生態。 解決方案 基於ESB集成匯流排,構建醫院信息化建設頂層設計。 集成後 實施方案 業務監控平臺 自動發現業務應用拓撲,準確定位影響業務的性能問題和技術棧.端到端事務監控,分散式跨應用交易追蹤,可 ...
  • 0、為什麼需要eureka 當我們從當體系統拆分為多個獨立服務項目之後,如果aaa.com/uer、aaa.com/order;;相互之間調用,如果只是一個服務一個實例,那還可以直接通過固定地址(如http://111.111.11.1:7119/user/getuser/1)直接訪問; 但是,我們 ...
  • 大家好,我是沉默王二,今天在逛 programcreek 的時候,我發現了一些專註細節但價值連城的主題。比如說:Java 的 Comparable 和 Comparator 是兄弟倆嗎?像這類靈魂拷問的主題,非常值得深入地研究一下。 Comparable 和 Comparator 是 Java 的兩 ...
  • 題目: 給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和為目標值的那 兩個 整數,並返回他們的數組下標。 你可以假設每種輸入只會對應一個答案。但是,你不能重覆利用這個數組中同樣的元素。 示例: 給定 nums = [2, 7, 11, 15], target = 9 因為 ...
  • 有2個實體:用戶、會員卡,一個用戶只能辦理一張會員卡,即一對一。 user_tb : 引入card_tb的主鍵card_no作為外鍵。 card_tb: 方式一:使用擴展類實現一對一 (1)在pojo包下新建User類: package com.chy.pojo; public class User ...
  • 一、編寫一個酒店管理系統 1.直接上代碼 package com.bjpowernode.java_learning; ​ public class D69_1_ { //編寫一個程式模擬酒店的管理系統:預定房間、退房....... public static void main(String[] ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...