主頁 > 後端開發 > 一起來讀官方檔案-----SpringIOC(10)

一起來讀官方檔案-----SpringIOC(10)

2020-10-30 23:08:27 後端開發

1.11,使用JSR 330標準注解
從Spring 3.0開始,Spring提供了對JSR-330標準注解(依賴注入)的支持,
這些注解的掃描方式與Spring注解相同,
要使用它們,您需要在類路徑中包含相關的jar,

如果你使用Maven, javax,  
注入工件在標準Maven存盤庫中可用
(https://repo1.maven.org/maven2/javax/inject/javax.inject/1/),  

你可以添加以下依賴到你的檔案pom.xml:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

1.11.1,@Inject和@Named
除了@Autowired,您可以使用@javax.inject.Inject注入:

import javax.inject.Inject;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.findMovies(...);
        // ...
    }
}

與@Autowired一樣,你可以在欄位級、方法級和構造引數級使用@Inject,

此外,您可以將注入點宣告為提供者,從而允許按需訪問作用域較短的bean,或者通過Provider.get()延遲呼叫訪問其他bean,
以下示例提供了先前示例的變體:

import javax.inject.Inject;
import javax.inject.Provider;

public class SimpleMovieLister {

    private Provider<MovieFinder> movieFinder;

    @Inject
    public void setMovieFinder(Provider<MovieFinder> movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.get().findMovies(...);
        // ...
    }
}

如果要為應該注入的依賴項使用qualified名稱,則應使用@Named批注,如以下示例所示:

import javax.inject.Inject;
import javax.inject.Named;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}

與一樣@Autowired,@Inject也可以與java.util.Optional或 一起使用@Nullable,
這在這里更為適用,因為@Inject它沒有required屬性,
以下示例展示了如何使用@Inject和 @Nullable:

public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        // ...
    }
}
public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        // ...
    }
}

1.11.2,@Named和@ManagedBean:
您可以使用@javax.inject.Named或@javax.annotation.ManagedBean代替@Component

如以下示例所示:

import javax.inject.Inject;
import javax.inject.Named;

@Named("movieListener")  // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

在不指定Component名稱的情況下使用@Component是很常見的,
@Named也可以以類似的方式使用,如下面的示例所示:

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

當使用@Named或時@ManagedBean,可以使用與使用Spring注解完全相同的方式來使用組件掃描,如以下示例所示:

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    // ...
}
與相比@Component,JSR-330的@Named和JSR-250的ManagedBean 注解是不可組合的,
1.11.3,JSR-330標準注解的局限性

當你使用標準注解時,你應該知道一些重要的特性是不可用的,如下表所示:

Spring javax.inject.* javax.inject限制/解釋
@Autowired @Inject @Inject沒有“required”屬性,
可以與Java 8一起使用Optional
@Component @Named/@ManagedBean JSR-330沒有提供可組合模型,
只是提供了一種識別已命名組件的方法,
就是說這倆注解僅僅是把類標識為bean
@Scope("singleton") @Singleton JSR-330的默認作用域類似于Spring的prototype,
但是,為了保持它與Spring的一般默認值一致,
在Spring容器中宣告的JSR-330 bean在默認情況下是單例的,
為了使用除singleton之外的作用域,
您應該使用Spring的@Scope注解,
javax.inject包中還提供了一個@Scope注解,
不過,這個注解僅用于創建您自己的注解,
具體例子就看同層級包下的@Singleton注解就可以了
@Qualifier @Qualifier / @Named javax.inject.Qualifier只是用于構建自定義限定符的元注解,
具體String qualifiers (例如Spring的帶有value的@Qualifier)
可以通過javax.inject.Named關聯,
@Value -- no equivalent
@Required -- no equivalent
@Lazy -- no equivalent
ObjectFactory Provider javax.inject.Provider是Spring的
直接替代方法ObjectFactory,
只是get()方法名稱較短,
它也可以與Spring@Autowired或
非注解建構式和setter方法結合使用,
1.12,基于Java的容器配置

本節介紹如何在Java代碼中使用注解來配置Spring容器,它包括以下主題:

  • 基本概念:@Bean和@Configuration
  • 使用實體化Spring容器 AnnotationConfigApplicationContext
  • 使用@Bean注解
  • 使用@Configuration注解
  • 組成基于Java的配置
  • Bean定義組態檔
  • PropertySource 抽象化
  • 使用 @PropertySource
  • 宣告中的占位符決議
1.12.1,基本概念:@Bean和@Configuration

Spring的新Java配置支持中的主要組件是-帶@Configuration注解的類和-帶@Bean注解的方法,

@Bean注解用于指示方法實體化、配置和初始化的新物件并將由Spring IoC容器管理,
對于那些熟悉Spring的XML配置的人來說,@Bean注解扮演著與元素相同的角色,

您可以對任何Spring @Component使用@bean注解的方法,
但是,它們最常與@Configuration bean一起使用,

用@Configuration注解類表明它的主要用途是作為bean定義的源,
此外,@Configuration類通過呼叫同一類中的其他@Bean方法來定義bean間的依賴關系,
最簡單的@Configuration類如下:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

上一AppConfig類等效于以下Spring XML:

<beans>
    <bean id="myService" />
</beans>
Full @Configuration 與  “lite” @Bean?
當@Bean方法在沒有使用@Configuration注解的類中宣告時,它們被稱為在“lite”模式下處理,

與 full 的@Configuration不同,lite@Bean方法不能宣告bean之間的依賴關系,
    Spring會正常實體化 lite @Bean,并執行依賴注入
    但是單獨呼叫這個@Bean方法的話,就僅僅是執行new 操作沒有進行依賴注入

在常見的場景中,@Bean方法將在@Configuration類中宣告,以確保始終使用“full”模式,這樣可以使用方法之間互相呼叫,
并防止通過常規Java呼叫意外地呼叫相同的@Bean方法,這有助于減少在“lite”模式下操作時難以跟蹤的細微錯誤,

下面幾節將深入討論@Bean和@Configuration注解,
但是,首先,我們將介紹使用基于java的配置創建spring容器的各種方法,

1.12.2,使用AnnotationConfigApplicationContext實體化Spring容器

下面幾節介紹了Spring3.0中引入的AnnotationConfigApplicationContext,
這個多功能的ApplicationContext實作不僅可以接受@Configuration類作為輸入,還可以接受普通的@Component類和用JSR-330元資料注解的類,

當@Configuration類作為輸入提供時,@Configuration類本身被注冊為bean定義,類中所有宣告的@bean方法也被注冊為bean定義,
當@Component和JSR-330類被提供時,它們被注冊為bean定義,并且會被用在在必要的地方進行注入,比如@Autowired或@Inject,

Simple Construction

與使用Spring XML檔案需要實體化ClassPathXmlApplicationContext用作輸入的方式幾乎相同,使用@Configuration類則需要實體化AnnotationConfigApplicationContext,

如下面的示例所示,這允許完全不使用XML來使用Spring容器:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

如前所述,AnnotationConfigApplicationContext不僅限于僅使用@Configuration類,@Component可以將任何一個或帶有JSR-330注解的類作為輸入提供給建構式,如以下示例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

前面的例子中假定MyServiceImpl,Dependency1以及Dependency2使用Spring依賴注入注解,例如@Autowired,

通過使用編程方式構建容器 register(Class<?>…?)

您可以AnnotationConfigApplicationContext使用no-arg建構式實體化一個物件,然后使用register()方法配置它,
以編程方式構建AnnotationConfigApplicationContext時,此方法特別有用,
以下示例顯示了如何執行此操作:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
使用啟用組件掃描 scan(String…?)

要啟用組件掃描,您可以@Configuration按如下方式注解您的類,此處不用加(.*):

@Configuration
@ComponentScan(basePackages = "com.acme") 
public class AppConfig  {
    ...
}

ComponentScan注解啟用組件掃描,

有經驗的Spring用戶可能熟悉Springcontext:命名空間中的XML宣告,如以下示例所示:

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

在前面的示例中,將掃描com.acme包以查找任何帶 @Component注解的類,
并將這些類注冊為容器內的Spring bean定義,
AnnotationConfigApplicationContext公開此scan(String…?)方法以允許相同的組件掃描功能,如以下示例所示:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}
請記住,@Configuration的元注解用@Component,所以他們是組件掃描候選人,  
在前面的示例中,假設AppConfig在com.acme包(或下面的任何包)中宣告,則在呼叫scan()期間將掃描到,  
通過refresh()方法,其所有@Bean方法都將在容器內進行處理并注冊為Bean定義,
支持Web應用程式 AnnotationConfigWebApplicationContext

一個基于AnnotationConfigApplicationContext的WebApplicationContext變種是AnnotationConfigWebApplicationContext,
可以使用此實作來配置Spring ContextLoaderListenerservlet監聽器,Spring MVC的 DispatcherServlet,
以下web.xml代碼片段配置了典型的Spring MVC Web應用程式(請注意使用contextClasscontext-param和init-param),這里的啟動流程前面的文章有講過的這里不再贅述:

<web-app>
    <!-- 配置 ContextLoaderListener來使用AnnotationConfigWebApplicationContext
        替換默認的 default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- 指定 配置類-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置 ContextLoaderListener來使用AnnotationConfigWebApplicationContext替換默認的 default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- 指定mvc config-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>
1.12.3,使用@Bean注解

@Bean是方法級別的注解,是XML元素的直接類似物,
注解提供的某些屬性,例如:* init-method * destroy-method * autowiring * name *,

你可以一個注解@Configuration-annotated或在 @Component-annotated類使用@Bean,

宣告一個bean

要宣告一個bean,可以用注解對方法進行@Bean注解,
方法回傳值的型別就是在ApplicationContext中注冊的bean定義的型別,
默認情況下,Bean名稱與方法名稱相同,
以下示例顯示了@Bean方法宣告:

@Configuration
public class AppConfig {

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

前面的配置與下面的Spring XML完全等效:

<beans>
    <bean id="transferService" />
</beans>

這兩個宣告都使名為transferService的bean在ApplicationContext中可用,該bean系結到型別為TransferServiceImpl的物件實體,
如下圖所示:

transferService -> com.acme.TransferServiceImpl

您還可以使用@Bean將介面(或基類)作為回傳型別宣告您的方法,
如以下示例所示:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}
如果您一直通過宣告的服務介面參考您的型別,
@Bean回傳型別可以安全地加入到設計決策中,

但是,對于實作多個介面的組件或其實作型別可能參考的組件,
宣告最具體的回傳型別更安全(至少與參考bean的注入點所需的回傳型別相同),
Bean依賴

帶@Bean注解的方法可以具有任意數量的引數,這些引數描述了構建該bean所需的依賴關系,
例如,如果我們TransferService需要AccountRepository,我們可以使用方法引數來實作該依賴關系,如以下示例所示:

@Configuration
public class AppConfig {

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

決議機制與基于建構式的依賴注入幾乎相同,

接收生命周期回呼

使用@Bean注解定義的任何類都支持常規的生命周期回呼,并且可以使用JSR-250中的@PostConstruct和@PreDestroy注解,

還完全支持常規的Spring生命周期回呼,
如果bean實作InitializingBean,DisposableBean或Lifecycle,則容器將呼叫它們各自的方法,

還完全支持標準*Aware介面集(例如BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware等),

該@Bean注解支持指定任意初始化和銷毀回呼方法,就像SpringXML中的init-method和destroy-method屬性的bean元素,如下面的示例所示:

public class BeanOne {

    public void init() {
        // initialization logic
    }
}

public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}
默認情況下,使用Java配置定義的具有公共close或shutdown方法的bean將自動與銷毀回呼一起呼叫,
比如這樣:在容器銷毀的時候就會被呼叫,不需要繼承任何介面
@Bean
public class ServiceFour  {
	public void close(){
		System.out.println("20201030-close");
	}
}
如果您有一個公共的close或shutdown方法,并且不希望在容器關閉時呼叫它,
那么可以將@Bean(destroyMethod=“”)添加到Bean定義中,以禁用默認(推斷)模式,

@Bean(destroyMethod="")
public class ServiceFour  {
	public void close(){
		System.out.println("20201030-close");
	}
}

默認情況下,您可能希望對使用JNDI獲取的資源執行此操作,因為它的生命周期是在應用程式外部管理的,
特別是,確保總是對資料源執行此操作,

以下示例顯示如何阻止資料源的自動銷毀回呼:
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
    return (DataSource) jndiTemplate.lookup("MyDS");
}

此外,對于@Bean方法,通常使用程式化JNDI查找,
方法是使用SpringJndiTemplate或JndiLocatorDelegate輔助方法,或者直接InitialContext使用JNDI, 
但不使用JndiObjectFactoryBean變體
(這將迫使您將回傳型別宣告為FactoryBean型別,而不是實際的目標型別,不是目標型別的話就很難使其他的@Bean方法來交叉呼叫此方法),

對于上述示例中的BeanOne,在構造程序中直接呼叫init()方法同樣有效,如下例所示:

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        BeanOne beanOne = new BeanOne();
        beanOne.init();
        return beanOne;
    }

    // ...
}
當您直接使用Java作業時,您可以對物件執行任何操作,而不必總是依賴于容器生命周期,
指定Bean范圍

Spring包含@Scope注解,以便您可以指定bean的范圍,

使用@Scope注解

@Bean默認作用域是singleton,但是您可以使用@Scope注解覆寫它,如以下示例所示:

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }
}
@Scope 和 scoped-proxy

Spring提供了一種通過作用域代理處理作用域依賴關系的方便方法,
在使用XML配置時創建這樣一個代理的最簡單方法是aop:scoped-proxy/元素,

	<bean id="serviceOne"  >
		<aop:scoped-proxy />
	</bean>

用@Scope注解在Java中配置bean提供了與proxyMode屬性相同的支持,
默認設定為無代理(ScopedProxyMode.NO),但您可以指定ScopedProxyMode.TARGET_CLASS or ScopedProxyMode.INTERFACES,

<?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- http session 的作用域 將作為代理出現 -->
    <bean id="userPreferences"  scope="session">
        <aop:scoped-proxy/> 
    </bean>

    <!-- 單例注入 的是一個代理不是 一個實體 避免只參考http session的一個實體 -->
    <bean id="userService" >
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>

如果您使用Java Config將上述XML參考檔案中的作用域代理示例移植到我們的@Bean,它類似于以下內容:

// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
    return new UserPreferences();
}

@Bean
public Service userService() {
    UserService service = new SimpleUserService();
    // a reference to the proxied userPreferences bean
    service.setUserPreferences(userPreferences());
    return service;
}
自定義Bean命名

默認情況下,配置類使用@Bean方法的名稱作為結果bean的名稱,
但是,可以使用name屬性覆寫此功能,如以下示例所示:

@Configuration
public class AppConfig {

    @Bean(name = "myThing")
    public Thing thing() {
        return new Thing();
    }
}
Bean別名

正如在命名bean中所討論的,有時需要為單個bean指定多個名稱,或者稱為bean別名,
為此,@Bean注解的name屬性接受一個字串陣列,
下面的示例演示如何為bean設定多個別名:

@Configuration
public class AppConfig {

    @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }
}
bean描述

有時,提供bean的更詳細的文本描述是很有幫助的,當bean(可能通過JMX)公開以進行監視時,這一點特別有用,

要向@Bean添加說明,可以使用@description注解,如下示例所示:

@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Thing thing() {
        return new Thing();
    }
}
1.12.4,使用@Configuration注解

@Configuration是一個類級別的注解,來指定當前類是一些bean definition的聚合,
@Configuration 類通過public@Bean注解方法宣告Bean,

對@Configuration類上的@Bean方法的呼叫也可以用來定義Bean間的依賴關系,

注入bean間的依賴關系

當bean彼此依賴時,表達這種依賴就像讓一個bean方法呼叫另一個一樣簡單,如以下示例所示:

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo());
    }

    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

在前面的示例中,通過建構式注入beanOne接收對的參考beanTwo,

僅當@Bean在@Configuration類中宣告該方法時,此宣告bean間依賴關系的方法才有效,
您不能使用普通@Component類宣告bean間的依賴關系,
Lookup Method注入

如前所述,Lookup Method注入是一種高級特性,應該很少使用,
在單例范圍bean依賴于原型范圍bean的情況下,它非常有用,

將Java用于此類配置提供了實作此模式的自然方法,
以下示例演示如何使用查找方法注入:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

通過使用Java配置,可以創建一個覆寫CommandManager抽象createCommand()方法的子類,該方法將以某種方式查找新的(原型)命令物件,以下示例顯示了如何執行此操作:

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    return command;
}

@Bean
public CommandManager commandManager() {
    // return CommandManager 的匿名實作類 并且實作了 createCommand()方法
    // return a new 原型類的 Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}
有關基于Java的配置如何在內部作業的更多資訊

考慮以下示例,該示例顯示了一個帶@Bean注解的方法被呼叫兩次:

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }
}

clientDao()被clientService1()呼叫一次和被clientService2()呼叫一次,
由于此方法創建的新實體ClientDaoImpl并回傳它,因此通常來說希望有兩個實體(每個服務一個),

那肯定是有問題的:在Spring中,實體化的bean singleton默認具有作用域,
這就是神奇的地方:所有@Configuration類在啟動時都使用子類化CGLIB,在子類中,子方法在呼叫父方法并創建新實體之前,首先檢查容器中是否有任何快取(作用域)的bean,

根據bean的范圍,行為可能有所不同,  
我們在這里談論單例,
從Spring 3.2開始,不再需要將CGLIB添加到您的類路徑中,
因為CGLIB類已經被重新打包org.springframework.cglib并直接包含在spring-core JAR中,
由于CGLIB在啟動時動態添加特性,因此有一些限制,尤其是,配置類不能是final,
但是,從4.3開始,配置類上允許使用任何建構式,包括使用@Autowired或一個用于默認注入的非默認建構式宣告,

如果您希望避免任何CGLIB強加的限制,請考慮在非@configuration 類上宣告@Bean方法(例如,在普通@Component類上),
然后@Bean方法之間的跨方法呼叫不會被攔截,但是因此您必須在建構式或方法級別專門進行依賴的注入,直接呼叫非@configuration的@Bean方法Spring將不再負責依賴注入,
1.12.5,組成基于Java的配置

Spring基于Java的配置特性允許您撰寫注解,這可以降低配置的復雜性,

使用@Import注解

正如Spring XML檔案中使用該元素來幫助模塊化配置一樣,該@Import注解允許@Bean從另一個配置類加載定義,如以下示例所示:

@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}

現在,不需要同時指定兩者ConfigA.class和ConfigB.class實體化背景關系,只需ConfigB顯式提供,如以下示例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

這種方法簡化了容器實體化,因為只需要處理一個類,而不是要求您@Configuration在構造程序中記住大量潛在的類,

從Spring Framework 4.2開始,@Import還支持對常規component 類的參考,類似于該AnnotationConfigApplicationContext.register方法,  
如果要通過使用一些配置類作為入口點來顯式定義所有組件,從而避免組件掃描,則此功能特別有用,
注入對匯入@Bean定義的依賴

前面的示例有效,但過于簡單,
在大多數實際情況下,Bean在配置類之間相互依賴,

使用XML時,這不是問題,因為不涉及編譯器,并且您可以宣告ref="someBean"并信任Spring在容器初始化期間對其進行處理,

使用@Configuration類時,Java編譯器會在配置模型上施加約束,因為對其他bean的參考必須是有效的Java語法,

幸運的是,解決這個問題很簡單,
正如我們已經討論的那樣,一種@Bean方法可以具有任意數量的描述Bean依賴關系的引數,
考慮以下具有多個@Configuration類的更真實的場景,每個類都取決于其他類中宣告的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");
}

還有另一種方法可以達到相同的結果,
請記住,@Configuration類是在容器中最終只有一個bean:這意味著他們可以利用 @Autowired與@Value注入等功能相同的其它bean,

確保以這種方式注入的依賴項只屬于最簡單的型別,
@Configuration 類在背景關系初始化程序中很早就被處理,強制以這種方式注入依賴項可能會導致意外的早期初始化,
只要有可能,就盡可能使用基于引數的注入,如前面的示例所示,

另外,通過@Bean使用BeanPostProcessor和BeanFactoryPostProcessor定義要特別小心,這些方法通常應該宣告為static@Bean方法,而不是觸發其包含的配置類的實體化,

如果自定義的BeanPostProcessor創建為早于AutowiredAnnotationBeanPostProcessor的bean實體,那么,@Autowired和@Value不適用于配置類本身,

以下示例說明如何將一個bean自動連接到另一個bean:

@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;

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

@Configuration
public class RepositoryConfig {

    private final DataSource dataSource;

    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public AccountRepository accountRepository() {
        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");
}
從springframework4.3開始,@Configuration類中的建構式注入才受支持,

還要注意,如果目標bean只定義了一個建構式,則不需要指定@Autowired,
Fully-qualifying imported beans for ease of navigation

在前面的場景中,使用@Autowired可以很好地作業并提供所需的模塊性,但是準確地確定注入的bean定義的宣告位置仍然有些模糊,
例如,作為一個正在查看ServiceConfig的開發人員,您如何確切地知道@autowiredaccountrepository bean是在哪里宣告的?
如果這種模糊性是不可接受的,并且您希望在IDE中從一個@Configuration類直接導航到另一個@Configuration類,可以考慮自動連接配置類本身,

下面的示例演示如何執行此操作:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        // navigate 'through' the config class to the @Bean method!
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

在前面的情況下,AccountRepository的定義是完全顯式的,
但是,ServiceConfig現在與RepositoryConfig緊密耦合,
這就是取舍,
通過使用基于介面或基于抽象類的@Configuration類,可以在一定程度上緩解這種緊耦合,
考慮以下示例:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

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

@Configuration
public interface RepositoryConfig {

    @Bean
    AccountRepository accountRepository();
}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

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

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class})  // import the concrete config!
public class SystemTestConfig {

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

}

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

現在ServiceConfig就具體而言已松散耦合DefaultRepositoryConfig
并且內置的IDE工具仍然有用:您可以輕松地獲得實作的型別層次結構RepositoryConfig,
通過這種方式,導航@Configuration類及其依賴項與導航基于介面的代碼的通常程序沒有什么不同,

如果您想影響某些bean的啟動創建順序,可以考慮將其中一些bean宣告為@Lazy(用于在首次訪問時而不是在啟動時創建)或@DependsOn某些其他bean(確保在當前bean之前創建特定的其他bean,而不是后者的直接依賴關系),
結合Java和XML配置

Spring的@Configuration類支持并不是要完全替代Spring XML,
有些工具(如Spring XML名稱空間)仍然是配置容器的理想方式,
在XML方便或必要的情況下,你有一個選擇:要么通過使用ClassPathXmlApplicationContext在容器實體化在一個“以XML為中心”的方式使用,或通過使用AnnotationConfigApplicationContext搭配@ImportResource注解匯入XML,

以XML為中心的@Configuration類使用

最好是從XML引導Spring容器,并以特別的方式包含@Configuration類,
例如,在使用Spring XML的大型現有代碼庫中,更容易根據需要創建@Configuration類并從現有XML檔案中包含它們,
在本節的后面,我們將介紹在這種“以xml為中心”的情況下使用@Configuration類的選項,

請記住,@Configuration類最終是容器中的bean定義,
在本系列示例中,我們創建一個@Configuration名為的類,AppConfig并將其包含在其中system-test-config.xml作為定義,
因為 context:annotation-config/已打開,所以容器會識別 @Configuration注解并 正確處理@Bean宣告的方法AppConfig,

以下示例顯示了Java中的普通配置類:

@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

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

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

以下示例顯示了示例system-test-config.xml檔案的一部分:

<beans>

    <context:annotation-config/>
    
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean />

    <bean >
        <property name="url" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.url}"/>
        <property name="username" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.username}"/>
        <property name="password" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.password}"/>
    </bean>
</beans>

以下示例顯示了一個可能的jdbc.properties檔案:

jdbc.url = jdbc:hsqldb:hsql:// localhost / xdb
jdbc.username = sa
jdbc.password =
public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

因為@Configuration元注解是@Component,@Configuration注解的類會自動成為組件掃描的候選類,

使用與前一個例子中描述的相同場景,我們可以重新定義system-test-config.xml利用組件掃描,

注意,在這種情況下,我們不需要顯式宣告context:annotation-config/,因為context:component-scan/啟用相同的功能,

以下示例顯示了修改后的system-test-config.xml檔案:

<beans>
    <context:component-scan base-package="com.acme"/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean >
        <property name="url" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.url}"/>
        <property name="username" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.username}"/>
        <property name="password" value="https://www.cnblogs.com/dabaieyangzhijidi/p/${jdbc.password}"/>
    </bean>
</beans>
以@Configuration 類為中心搭配 @ImportResource 的使用XML

在@Configuration類是配置容器的主要機制的應用程式中,仍然可能需要至少使用一些XML,
在這些場景中,您可以使用@ImportResource只定義所需的XML,

這樣做可以實作“以Java為中心”的方法來配置容器,并將XML保持在最低限度,
下面的示例(包括一個配置類、一個定義bean的XML檔案、一個屬性檔案和一個主類)演示了如何使用@ImportResource注解來實作“以Java為中心”的配置,并根據需要使用XML:

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}

properties-config.xml

<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>

jdbc.properties

jdbc.url = jdbc:hsqldb:hsql:// localhost / xdb
jdbc.username = sa
jdbc.password = adsdfas
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

本文由博客一文多發平臺 OpenWrite 發布!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/195920.html

標籤:Java

上一篇:mybatis 框架學習筆記

下一篇:還記得這門古老的編程語言么,送你一份perl書單!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more