1. 程式人生 > >Spring裝配Bean的三種方式+匯入和混合配置

Spring裝配Bean的三種方式+匯入和混合配置

[toc] # Spring IoC與bean > A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. bean是由Spring IoC容器例項化、裝配和管理的物件,否則,bean只是應用程式中的眾多物件之一。 > bean及其之間的依賴關係反映在容器使用的配置元資料中。 我們已經瞭解,Spring IoC容器能夠幫我們操作bean,但是前提是我們需要配置元資料以告知Spring容器,它才能夠通過讀取這些配置,來例項化,裝配和管理bean物件。 而配置元資料的方式,就是我們今天要總結的三種,分別是XML,Java註解以及Java程式碼。我們通過這幾種方式,向Spring容器傳達這些物件之間豐富的相互依賴關係。 ![](https://img2020.cnblogs.com/blog/1771072/202004/1771072-20200407195900178-826422720.png) 該圖是Spring如何工作的高階檢視。可以看到,應用程式類與配置元資料相結合,在建立並初始化ApplicationContext之後,就可以獲得一個完全配置和可執行的系統或應用程式。 # 基於XML的顯式裝配 ## xml配置的基本結構 ```xml
``` id屬性表示bean的唯一標識。 class屬性定義bean的型別並使用完全限定的類名。 ## bean例項的三種建立方式 ```xml ``` ## 依賴注入的兩種方式 ### 構造器注入方式 在``標籤的內部定義``標籤。 ```java public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherBean; this.beanTwo = yetAnotherBean; this.i = i; } } ``` ```xml
``` value:用於提供基本型別和String型別的資料。 ref:用於提供其他的bean型別資料,在spring的ioc核心容器中出現過的bean物件。 > 在建立物件時,如果沒有提供構造器中的這些引數,將無法建立該物件。 ### setter方法注入方式 在``標籤的內部定義``標籤。 ```java public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } } ``` ```xml
``` name:指定注入時呼叫的set方法的屬性名稱。 value:提供基本型別和String型別的資料。 ref:提供其他的bean型別資料,在spring的ioc核心容器中出現過的bean物件。 > 如果某個成員必須有值,但並沒有提供相應的setter方法,將會出錯。 【集合型別的注入】:分為list和map兩類結構 ```xml AAA BBB BBB AAA BBB BBB AAA BBB BBB BBB CCC DDD ``` list結構可以使用list、array和set標籤。 map結構可以使用map和props標籤。 ## 利用名稱空間簡化xml 一、p-namespace使用bean元素的屬性來提供屬性值和協作bean,而不是使用巢狀的``元素,下面兩段bean的配置效果相同。 ```xml ``` 二、Spring 3.1中新引入的c-namespace允許使用內聯屬性來配置建構函式引數,而不是使用巢狀的``。 ```xml ``` # 基於Java的顯式裝配 ## @Bean 和 @Configuration 這兩個註解類是Spring's new java-configuration的核心構件。 @Bean註解用於指示方法例項化、配置和初始化要由Spring IoC容器管理的新物件,@Bean註解的作用與``標籤相同。簡單的理解就是這個註解可以告知spring,這個方法上面未來希望註冊一個應用上下文的bean物件,因此用@Bean註解的方法需要利用Java程式碼,定義返回一個bean例項的邏輯。 @Configuration註解一個類表明這個類的主要目的是作為bean定義的源,@Configuration類允許通過簡單地呼叫同一類中的其他@Bean方法來定義bean之間的依賴關係。簡單的理解就是一個配置類,自此之後,你可以在該配置類中完成在xml中完成的事,但形式會有所不同。 下面這個例子是一個最簡單的配置類的定義: ```java @Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } } ``` 它的作用和下面這段xml配置的方式等價: ```xml ``` ## Bean的依賴 一個@Bean註釋的方法可以有任意數量的引數來描述構建該bean所需的依賴關係。例如,如果我們的TransferService需要一個AccountRepository,我們可以通過一個方法引數來實現這個依賴: ```java @Configuration public class AppConfig { @Bean public TransferService transferService(AccountRepository accountRepository) { return new TransferServiceImpl(accountRepository); } } ``` 當spring呼叫transferService方法建立bean時,會自動裝配accountRepository到配置方法中,再次印證了那句話,帶有@Bean註解的方法可以編寫任何必要的Java程式碼來產生Bean的例項,例如構造器,setter方法,以及任何可以產生例項的方法。 ## 初始化Spring容器 AnnotationConfigApplicationContext是Spring 3.0中新增的。它不僅可以接受@Configuration配置類作為輸入,還可以接受普通的@Component類和使用JSR-330元資料註釋的類。 初始化spring容器,獲取Myservice物件,呼叫物件的方法。 ```java public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); } ``` ## 定製bean的命名 預設情況下,配置類將會使用@Bean註解的方法的名稱作為bean的名稱,這一點可以通過name屬性修改。 ```java @Configuration public class AppConfig { @Bean(name = "myFoo") public Foo foo() { return new Foo(); } } ``` 如上:如果沒有指定name屬性,該bean的名稱為foo,如果指定了name屬性,這裡的名稱就是myFoo。 # 基於註解的自動裝配 Spring從以下兩個角度實現自動裝配: - 元件掃描:Spring自動發現應用上下文中所建立的bean。 - 自動裝配:Spring自動滿足bean之間的依賴。 首先還是來看一段簡單的例子: ```java //定義一個UserService介面 public interface UserService { void add(); } ``` ```java //定義實現類,注意加上@Component註解,告知spring建立這個bean @Component public class NormalUserServiceImpl implements UserService { @Override public void add() { System.out.println("新增使用者"); } } ``` ```java //controller層,注意@Autowired註解,自動按型別注入Userservice @Component public class UserController { @Autowired private UserService userservice; public void add(){ userservice.add(); } } ``` ```java //定義配置類,注意@ComponentScan("com.my.demo")註解開啟元件掃描 @Configuration @ComponentScan("com.my.demo") public class Appconfig { } ``` ```java //整合junit測試類進行測試 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Appconfig.class) public class UserServiceTest { @Autowired private UserService userservice; @Test public void testMethod(){ userservice.add(); } } ``` 以上就是一套可以正常執行的簡單案例,當然其中有不少可能出現的問題或者是其他可以實現相同功能的方案,我們都暫且不提。其中出現了許多自動化裝配bean的註解,我們一一來看: ## 自動裝配的常用註解 【@Component】 * 作用:將當前類物件存入spring容器中。 * 屬性:value,用於指定bean的id,不指定value時,預設值為當前類名首字母小寫。 值得一提的是,在三層架構中,Spring框架提供了明確的三層註釋,作用與@Component相同,但語義更加清晰明瞭,分別是: Controller:表現層、Service:業務層、Respository:持久層 【@Autowired】 * 作用:自動按照型別注入,只要容器中有唯一的一個bean物件型別和要注入的變數型別匹配,就可以注入成功。 * 如果ioc容器中沒有任何bean型別和要注入的變數型別匹配,則報錯(解決方法是,設定required屬性的值為false,如果沒找到對應型別的bean,則會出於未裝配狀態),如果ioc容器中有多個型別匹配時,出現歧義性,也會報錯。 * 出現位置:既可以是構造器,也可以是setter方法,甚至任何其他的方法,Spring都會嘗試滿足方法引數上宣告的依賴。 * 細節:在使用註解注入時,set方法就不是必須的了。 當出現歧義性時,滿足型別要求的bean不是唯一時,可以考慮使用@Qualifier和@Resource註解,參考:[Spring解決自動裝配歧義性的幾種方案](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/105371399) 【@Configuration】 * 作用:指定當前類是一個配置類 * 細節:當配置類作為AnnotationConfigApplicationContext物件建立的引數時,該註解可以不寫。 【@ComponentScan】 * 作用:開啟元件掃描,用於通過註解指定spring在建立容器時要掃描的包。 * 屬性:value,和basePackages的作用相同,指定建立容器時要掃描的包。 * 如果不指定value或者basePackages的值,將會預設**掃描與配置類相同的包**。 設定spring元件掃描的基礎包的幾種方案: - `@ComponentScan("com.my.demo")` - `@ComponentScan(basePackages = {"com.my.demo.web","com.my.demo.service"})` - `@ComponentScan(basePackageClasses = {UserController.class, UserService.class, UserDao.class})`,相較於第二種,較為安全。 需要注意的是,元件掃描預設是不開啟的,我們需要通過該註解顯式通知Spring,告訴它去尋找帶有@Component註解的類,去建立該類的bean物件。 開啟元件掃描的xml方式: ```xml ``` 既然使用xml方式開啟元件掃描,那麼測試的時候需要謹慎,要讀取該xml檔案:`@ContextConfiguration("classpath:applicationContext.xml")`。 # 匯入和混合配置 直接以例子呈現: ```properties #jdbcConfig.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring jdbc.username=root jdbc.password=123456 ``` ```java /** * @author Summerday *

* 和spring連線資料庫相關的配置類 */ public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * 建立queryRunner物件 * * @param dataSource * @return */ @Bean(name = "runner") @Scope("prototype") public QueryRunner createQueryRunner(DataSource dataSource) { return new QueryRunner(dataSource); } /** * 建立資料來源物件 * * @return */ @Bean(name = "dataSource") public DataSource createDataSource() { try { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setDriverClass(driver); ds.setJdbcUrl(url); ds.setUser(username); ds.setPassword(password); return ds; } catch (Exception e) { throw new RuntimeException(e); } } } ``` 【@Value】 * 作用:用於基本型別和string型別的資料。 * 屬性:value,指定資料的值,可以使用spring中的SpEL,spring的el表示式。 * SpEL的寫法:${表示式}。 ```java /** * 主配置類 */ @Configuration @ComponentScan(basePackages = "com.smday") @Import(JdbcConfig.class) @PropertySource("classpath:JdbcConfig.properties") public class SpringConfiguration { } ``` 【@Import】 * 作用:用於匯入其他的配置類。 * 屬性:value,指定其他配置類的位元組碼,使用Import註解後,有該註解的類為父配置類,匯入的都是子配置類。 【@PropertySource】 * 作用:作用於指定properties檔案的位置。 * 屬性:value,指定檔案的名稱和路徑,關鍵字classpath表示類路徑下。 > 最後的最後,引用Spring in Action中作者的話:自動化配置、基於Java的顯式配置以及基於xml的顯式配置都描述了Spring應用中元件以及這些元件之間的關係。作者建議儘可能使用自動化的配置,其次如果需要顯式配置,希望優先選擇基於Java的配置,型別安全且