1. 程式人生 > >Spring核心技術--IoC container用法詳解

Spring核心技術--IoC container用法詳解

一直在使用Spring提供的IoC容器, 但是始終沒有系統化的梳理一下. 今天在這裡寫下, 也是以備以後參考之用.

Ioc container的核心是BeanFactory介面, 它提供的方法能夠管理任何型別的物件. ApplicationContext是它的子介面, 集成了Spring AOP的特性.

BeanFactory or ApplicationContext?

BeanFactory目前只是Spring為了維持向後相容性而保留的介面, 任何新開發的框架或者程式應該使用ApplicationContext而不是BeanFactory.

那麼ApplicationContext

相比BeanFactory提供了哪些好處呢? 簡單的來說就是很多增強的功能, 更細粒度的干預bean生命週期的定製, AOP, 國際化, 事件支援等, 詳見下表:

所有的bean在使用前都需要在ApplicationContext註冊(register), 常用的有以下幾種形式:

  • xml形式定義的;
  • 註解定義;
  • Java Config
  • 手動註冊在Spring容器之外例項化的bean;

Dependency Injection的實現方式

一共有三種方法: 構造器注入, 設值注入和方法注入. 構造器注入在設值注入之前.

  • 構造器注入: 注入強制性的依賴.
  • 設值注入: 注入可選依賴或配置(例如@ConfigurationProperties
    ); 當設定注入加入了@Required標識, 就會變成一個強制性依賴.
  • 方法注入: 通常是在bean構造後對bean進行增強設定.

Constructor-based

構造器注入是推薦的方式, 能夠實現一個immutable的bean, 確保所有的依賴不是null. 並能夠保證呼叫者使用時, 依賴是例項化完成的.

無需提供getter/setter方法.

setter-based

設值注入應被僅用於注入可選依賴, 或者是能夠被賦予預設值的依賴.

使用設值注入的好處是, 能夠在之後通過setter方法進行注入的變更, 在JMX MBeans管理上必須使用此種方式.

方法注入

下面是一個例子:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired // 自動注入--方法注入引數auth, 對已建立的AuthenticationManagerBuilder型別的bean進行增強.
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("USER");
    }
}

依賴解析步驟

如果bean是單例的, 並且被標註為pre-instantiated(預設就是這個)會在容器建立的時候被建立. 否則的話會按需建立.

lazy-initialized beans

通常來講單例bean會在容器建立的時候初始化, 並且會按照依賴關係順次進行初始化, 但是你也可以通過指定lazy-init=true到某個bean上使其在第一次使用的時候才被初始化.

注意: 即使某個bean開啟了lazy-init, 也不一定是延遲初始化的, 因為如果依賴它的bean是單例的, 且沒有開啟延遲初始化, 那麼Spring容器為了保證準確性, 會將這個bean也立即初始化.

Bean作用域(Bean Scopes)

bean一共有七種作用域:

作用域 描述
singleton 預設, 單例, 通常用於無狀態bean, 如dao. service, controller等
prototype 原型, 每次呼叫產生一個新的例項, 通常用在domain object上. 相當於java的new關鍵字, Spring在例項化bean之後, 執行完initialization lifecycle callback, 之後就不在負責這個bean的生命週期了, 完全交給客戶端負責
request HTTP 請求範圍
session HTTP Session範圍
application ServletContext的生命週期一致, 也就是web應用的範圍
websocket 和一個WebSocket的生命週期一致

request, session, application, websocket作用域

這些作用域需要執行緒與Http Request或者Http Sesson物件繫結. 當使用Spring MVC時, 自動繫結.

使用以下註解在bean上顯式指定:

  • @RequestScope
  • @SessionScope
  • @ApplicationScope

application scope

ServletContext級別的, 作為一個ServletContext的attribute存取, 這個範圍非常像singleton範圍, 但是有以下兩點主要區別:

  • 它是與ServletContext繫結, 不是與ApplicatinContext繫結. 一個ServletContext可以有多個ApplicationContext. 也就是說, application scope的bean是與ApplicationContext平行的, 二者都是ServletContext的一個屬性.
  • 它是ServletContext的一個屬性.

代理

一般有兩種型別:

  • interface-based proxy, 典型的是JDK內建的動態代理
  • 一種是inherated-based proxy, 典型的是CGLIB

interface-based proxy

優點是不需要引入額外的庫依賴, 缺點是想要代理的方法必須是在被代理類實現的介面中定義的.

inherated-based proxy

缺點是需要引入額外依賴, 優點是可以代理所有的共有型別方法.

Bean生命週期(lifecycle)

  • initialization callback 使用@PostConstruct
  • destruction callback 使用@PreDestroy

以下是一個使用的例子:

public class CachingMovieLister {

    @PostConstruct // 該方法會在bean初始化之後被回撥
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy // 該方法會在容器銷燬bean之前被回撥
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }

}

Startup and shotdown callbacks

是容器啟動和容器停止的回撥, 發生在所有的bean建立之前和銷燬之前.

優雅的關閉非web應用下的Spring IoC容器

需要為ApplicationContext在JVM裡註冊一個shutdown hook. 這會確保所有自定義的destroy方法被正確呼叫.

ApplicationContextAware and BeanNameAware

是bean用來感知容器存在的介面, 通常不需要實現這個介面, 因為通常bean是不需要顯式感知容器的存在的, 而且這樣做會導致與Spring框架耦合.

使用註解指導依賴注入

@Autowired

@Autowired是按照注入的型別名進行依賴查詢, 當同一個型別有多種實現bean的時候, 產生歧義並報錯.

@[email protected]

有兩個以上相同型別的bean時, 單獨的@Autowired會產生歧義, 可以用額外的@Qualifier("這裡是bean的名字")註解按bean的名字進行注入.

@Resource

相當於@Autowired+@Qualifire的組合.

自定義@Qualifier

@Qualifier註解支援繼承, 這樣我們就可以擴充套件@Qualifier的語義, 進行更細力度的@Quelifier定義, 例如:

// 擴充套件@Quelifier語義
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}

@Configuration
public class MovieConfiguration {

    @Bean
    @Offline // 使用自定義的@Qualifier子類區分bean
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...

}

public class MovieRecommender {

    @Autowired
    @Offline // 根據自定義的@Qualifier子類注入bean
    private MovieCatalog offlineCatalog;

    // ...

}

下面是一個更復雜的例子:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

    String genre();

    Format format();

}

public enum Format {
    VHS, DVD, BLURAY
}

public class MovieRecommender {

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Action")
    private MovieCatalog actionVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Comedy")
    private MovieCatalog comedyVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.DVD, genre="Action")
    private MovieCatalog actionDvdCatalog;

    @Autowired
    @MovieQualifier(format=Format.BLURAY, genre="Comedy")
    private MovieCatalog comedyBluRayCatalog;

    // ...

}

使用Java Config指導依賴注入

核心是使用@Configuration@Bean註解.

@Bean

@Bean是一個方法級的註解, 它的返回值將作為一個singleton的bean註冊到spring的IoC容器中.

如果一個@Bean的建立依賴於其他bean的建立, 那麼需要將所有依賴作為方法的引數, 會觸發類似@Autowired的效果.

下面是一個例子:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }

}

這種配置方式特別像contructor-based注入, 可以參照理解

@Configuration

所有的通過@Bean方式手動建立bean, 都建議在@Configuration的類中進行定義.

@Configuration標註的類會在執行時被CGLIB動態代理, 所有對原類方法的呼叫都會被攔截.

這個動態代理做了以下重要的事:

  • 通過快取singleton bean, 防止原類中重複例項化同一個名字的singleton bean.

@Import

@Import是用來組合多個@Configuration的定義. 使通過Java Config進行手動bean定義的配置檔案集中到一起.

下面是一個使用@Configuration + @Import進行跨檔案bean組裝的例子:

@Configuration
public class ServiceConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }

}

@Configuration
public class RepositoryConfig {

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }

}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

組合xml, 註解, Java Config形式的依賴注入

只需要在一個@Configuration類上加上@ImportResource註解即可, 以下是一個例子:

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
    // 正常的bean定義
}

Environment abstraction

環境允許通過定義多個profile檔案, 允許在不同環境(開發, 測試, 生產)使用不同的配置.

下面是一個例子:

@Configuration
@Profile("dev")
public class StandaloneDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }
}

@Configuration
@Profile("production")
public class JndiDataConfig {

    @Bean(destroyMethod="")
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}

想要啟動一個特定的配置, 在全域性配置中加上如下屬性設定即可:

# 啟用dev profile
spring.profiles.active=dev

除此以外, 我們還可以提供個一個預設的profile:

@Configuration
@Profile("default")
public class DefaultDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .build();
    }
}

上述配置在沒有顯式指定spring.profiles.active時自動啟用.

小結

以上.

參考連結:

相關推薦

Spring核心技術--IoC container用法

一直在使用Spring提供的IoC容器, 但是始終沒有系統化的梳理一下. 今天在這裡寫下, 也是以備以後參考之用. Ioc container的核心是BeanFactory介面, 它提供的方法能夠管理任何型別的物件. ApplicationContext是它的

SPRING MVC 中的 MULTIACTIONCONTROLLER 用法 (轉載)

http://www.blogjava.net/wuhen86/articles/288966.html Spring MVC 中 Controller 的層次實在是多,有些眼花繚亂了。在單個的基礎上,再新加兩三個叫做豐富,再多就未必是好事,反而會令人縮手新聞片腳,無

Spring筆記(9) - IOC實現方式

  IOC概念    控制反轉(Inversion of Control,IOC),是面向物件程式設計中的一種設計原則,它建議將不需要的職責移出類,讓類專注於核心職責,從而提供鬆散耦合,提高優化軟體程式設計。它把傳統上由程式程式碼直接操控的物件的呼叫權(new、get等操作物件)交給容器,通過容器來實現物件元

spring(7)---深入理解Spring核心技術——Spring中的各模組

深入理解Spring核心技術——Spring中的各模組詳解      Spring框架的兩個基本概念IOC容器和AOP,相信大家現在對Spring中的這兩個部分的基本概念有了一定的認識,好了,那麼今天我們就來正式的進入Spring框架的學習了。 前面提到過,

SpringIOC核心容器和Bean概念

    這一週忙了很多與程式碼無關的事,感覺心態上還是有些急躁,週中挑幾個晚上看了一些文章,上午起來總結了一下,下午開始寫部落格,因為沒有時間擼程式碼,所以就打算先把看到的概念梳理梳理,磨刀不誤砍柴工。    首先來看一看什麼是IOC,他的全稱是Inversion of Co

Spring核心思想,IoC與DI(如果還不明白,放棄java吧)

1.IoC是什麼? IoC(Inversion of Control)控制反轉,IoC是一種新的Java程式設計模式,目前很多輕量級容器都在廣泛使用的模式。 2.IoC解決了什麼問題? 在IoC出現以前,元件之間的協調關係是由程式內部程式碼來控制的,或者說,以前我們使用New關鍵字來實現兩組

Spring MVC 學習筆記(二):@RequestMapping用法

一、@RequestMapping 簡介 在Spring MVC 中使用 @RequestMapping 來對映請求,也就是通過它來指定控制器可以處理哪些URL請求,相當於Servlet中在web.xml中配置 <servlet>  

Spring AOP用法

trace 方法名 java 範式 advice work 配對 中文翻譯 roc 什麽是AOP AOP:Aspect Oriented Programming,中文翻譯為”面向切面編程“。面向切面編程是一種編程範式,它作為OOP面向對象編程的一種補充,用於處理系統中分布於

Spring中@Async用法及簡單例項

Spring中@Async用法 引言: 在Java應用中,絕大多數情況下都是通過同步的方式來實現互動處理的;但是在處理與第三方系統互動的時候,容易造成響應遲緩的情況,之前大部分都是使用多執行緒來完成此類任務,其實,在spring 3.x之後,就已經內建了@Async來完美解決這個問題,本文將完成

Spring MVC的一些關於請求的註解用法

    這段時間一直在著手於RESTful風格的介面設計。springmvc的RESTful風格的url是通過@RequestMapping 及@PathVariable annotation提供的。為此我好好研究了一下關於Springmvc請求這方面的內容,也借鑑了前人的

Spring框架 IOC(原理)(一)

一、Spring開源框架的簡介 Spring是一個開源框架,Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中闡述的部

Spring核心技術(一)——IoC容器和Bean簡介

IoC容器和Bean簡介 這章包括了Spring框架對於IoC規則的實現。Ioc也同DI(依賴注入)。而物件是通過建構函式,工廠方法,或者一些Set方法來定義物件之間的依賴的。容器在建立這些Bean物件的時候同時就會注入這些依賴。這個過程是根本上的反轉了,不再

Spring Boot中@RequestMapping 用法之地址對映(轉)

引言 前段時間使用springboot來開發專案,並且需要使用到傳輸JSON資料,並且踩了很多坑,無意中找到了這篇文章,詳細的說明了@RequestMapping的使用 簡介: @RequestMapping RequestMappin

spring RestTemplate用法

前面介紹過Spring的MVC結合不同的view顯示不同的資料,如:結合json的view顯示json、結合xml的view顯示xml文件。那麼這些資料除了在WebBrowser中用JavaScript來呼叫以外,還可以用遠端伺服器的Java程式、C#程式來呼叫。也就是說現

JavaEE基礎(02):Servlet核心API用法

本文原始碼:GitHub·點這裡 || GitEE·點這裡 一、核心API簡介 1、Servlet執行流程 Servlet是JavaWeb的三大元件之一(Servlet、Filter、Listener),它屬於動態資源。Servlet的作用是處理請求,伺服器會把接收到的請求交給Servlet來處理,在Se

Spring IoC getBean 方法

前言 本篇文章主要介紹 Spring IoC 容器 getBean() 方法。 下圖是一個大致的流程圖: 正文 首先定義一個簡單的 POJO,如下: public class User { private Long id; private String name; pu

Spring IoC createBean 方法

# 前言 本篇文章主要分析 Spring IoC 的 `createBean()` 方法的流程,以及 `bean` 的生命週期。 下面是一個大致的流程圖: ![Spring IoC createBean 方法流程.png](http://ww1.sinaimg.cn/large/006Vpl27gy1g

Spring IoC @Autowired 註解

# 前言 本系列全部基於 `Spring 5.2.2.BUILD-SNAPSHOT` 版本。因為 Spring 整個體系太過於龐大,所以只會進行關鍵部分的原始碼解析。 我們平時使用 Spring 時,想要 **依賴注入** 時使用最多的是 `@Autowired` 註解了,本文主要講解 Spring 是如

Spring IoC 公共註解

# 前言 本系列全部基於 `Spring 5.2.2.BUILD-SNAPSHOT` 版本。因為 Spring 整個體系太過於龐大,所以只會進行關鍵部分的原始碼解析。 什麼是公共註解?公共註解就是常見的Java註解,特別是JSR-250中的註解。例如:`@Resource`、`@PostConstruct

JavaScript中return的用法

style 返回 www log tle blog 意思 charset fun 1、定義:return 從字面上的看就是返回,官方定義return語句將終止當前函數並返回當前函數的值,可以看下下面的示例代碼: <!DOCTYPE html><html l