(二)Spring-bean的作用域、xml和註解方式自動裝配以及註解開發
(二)Spring-bean的作用域、xml和註解方式自動裝配以及註解開發
一、 bean的作用域
1.1 定義
當您建立一個 bean 定義時,您建立了一個用於建立由該 bean 定義定義的類的實際例項的方法。bean 定義是一個配方的想法很重要,因為這意味著,與一個類一樣,您可以從一個配方建立許多物件例項。
-
您不僅可以控制要插入到從特定 bean 定義建立的物件中的各種依賴項和配置值,還可以控制從特定 bean 定義建立的物件的範圍。
-
這種方法功能強大且靈活,因為您可以通過配置選擇您建立的物件的範圍,而不必在 Java 類級別定義物件的範圍。可以將 Bean 定義為部署在多個範圍之一中。Spring 框架支援六個範圍,其中四個僅在您使用 web-aware 時可用
ApplicationContext
1.2 六個作用域
Scope | Description |
---|---|
singleton |
(Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
1 單例模式
spring預設機制
<bean id="accountService" class="com.something.DefaultAccountService"/>
<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
2 原型模式
每次從容器get的時候,都會產生一個新的物件!
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
3 其餘的request,session,application
這些個只能在web 開發中使用的。
二、bean使用xml自動裝配
2.1 定義
- bean裝配是spring滿足bean依賴的一種方式!
- spring會在上下文中自動尋找,並自動給bean裝配屬性!
2.2 spring中3種裝配方式
- 在xml中顯示的配置
- 在java中顯示配置
- 隱式的自動裝配bean【重要】
2.3 手動裝配VS自動裝配
2.3.1 手動裝配
package com.happy.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor@AllArgsConstructor
public class People {
private String name;
private Dog dog;
private Cat cat;
}
package com.happy.pojo;
public class Cat {
public void shout(){
System.out.println("miao miao~");
}
}
package com.happy.pojo;
public class Dog {
public void shout(){
System.out.println("wong wong ~");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" class="com.happy.pojo.Dog"></bean>
<bean id="cat" class="com.happy.pojo.Cat"></bean>
<bean id="people" class="com.happy.pojo.People">
<property name="name" value="happy"></property>
<property name="dog" ref="dog"></property>
<property name="cat" ref="cat"></property>
</bean>
</beans>
測試使用
package com.happy.service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.happy.pojo.People;
public class PeoPleService {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
People people = context.getBean("people", People.class);
System.out.println(people);
people.getDog().shout();
}
}
2.3.2 自動裝配
1 ByName
注意:Byname是要保證,待裝配物件的set方法和beanid一致,而並非待裝配物件的屬性名。
package com.happy.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
public class People {
private String name;
private Dog dog1;
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog1;
}
public void setDog(Dog dog) {
this.dog1 = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", dog1=" + dog1 +
", cat=" + cat +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" class="com.happy.pojo.Dog"></bean>
<bean id="cat" class="com.happy.pojo.Cat"></bean>
<!-- ByName:會自動在容器上下文中查詢,和自己物件set方法後面的值對應的bean-id-->
<bean id="people" class="com.happy.pojo.People" autowire="byName">
</bean>
</beans>
2 ByType
注意:
byType有自己的弊端,必須待裝配物件的屬性的型別保證在上下文裡唯一
- byType的依賴屬性可以不用id都可以。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" class="com.happy.pojo.Dog"></bean>
<bean class="com.happy.pojo.Cat"></bean>
<!-- ByType:會自動在容器上下文中查詢,和自己物件屬性型別相同的bean-id-->
<bean id="people" class="com.happy.pojo.People" autowire="byType">
</bean>
</beans>
2.4 總結
- byName的時候,需要保證所有bean的id唯一,並且這bean需要和自動注入的set方法後面的名字一致。
- byType的時候,需要保證所有bean的class唯一,並且這bean需要和自動注入屬性的型別一致。
三、bean使用註解自動裝配
3.1 使用條件
前提:
-
和xml一樣,所有依賴的型別都必須在xml即context中有了。
-
jdk1.5,spring2.5以後支援註解。
匯入約束和註解支援<context:annotation-config/>
要使用註解須知:
- 匯入約束
- 配置註解的支援context:annotation-config/
<?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
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
實現如下:
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/beans/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop.xsd">
<bean id="dog" class="com.happy.pojo.Dog"></bean>
<bean id="cat" class="com.happy.pojo.Cat"></bean>
<bean id="people" class="com.happy.pojo.People"></bean>
</beans>
3.2 @Autowired
-
直接在屬性上使用即可,可以在屬性和set方法上注入,相當於xml得到property
-
使用Autowired我們可以不用編寫set方法了,前提是你這個自動裝配的屬性已經在IOC容器中存在,且符合按照ByType注入。
-
先按照type找相同型別的bean注入,如果容器中存在兩個以上相同的,再按照set後面的name進行注入。即先ByType再ByName
-
如果ByType再ByName都存在兩個以上(ByName沒有找到相同的),則需要使用@Qualifier
3.3 @Qualifier
- 自動裝配屬性的時候,在容器中存在兩個相同的type能匹配上,又不能匹配name,則需要使用這個註解。
- 即如果自動裝配的環境比較複雜,自動裝配無法通過一個註解@Autowired完成的時候,我們可以通過Autowired和Qualifier配合使用,就能指定一個唯一的bean物件注入
package com.happy.pojo;
import com.sun.istack.internal.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
public class People {
private String name;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
@Autowired
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@NotNull
public Dog getDog() {
return dog;
}
@Nullable
public void setDog(@NotNull Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
3.4 @Resource
@Resource=@Autowired+@Qualifer
package com.happy.pojo;
import com.sun.istack.internal.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import javax.annotation.Resource;
public class People {
private String name;
// @Autowired
// @Qualifier(value = "dog2")
@Resource(name = "dog2")
private Dog dog;
@Autowired
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@NotNull
public Dog getDog() {
return dog;
}
@Nullable
public void setDog(@NotNull Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
3.5 Autowired vs @Resource
Autowired | Resource | |
---|---|---|
相同點 | 自動裝配 | 自動裝配 |
不同點 | 先type後name | 先name後type |
四、使用註解開發
4.1 使用條件
- 在Spring4之後,要使用註解開發,必須要保證aop的包匯入了。
- 使用註解還需要匯入context名稱空間和約束,增加註解的支援!
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="com.happy.pojo"></context:component-scan>
</beans>
匯入約束和註解支援<context:annotation-config/>
- 註解驅動,加上這個就可以使用resource這些註解了
匯入包掃描標籤</context:component-scan>
- 掃描包
- 配置該標籤後,如果除了這個包沒有在其他地方註解了,就不需要再配置context:annotation-config
4.2 bean
4.2.1 @Component
- 將component放在類上,說明這個類被Spring 容器管理了,這就是bean。
- 相當於<bean id="user" class="com.happy.pojo.User"></bean>
package com.happy.pojo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
/*<!-- <bean id="user" class="com.happy.pojo.User"></bean>-->*/
@Component
public class User {
public String name ="高興";
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
User user = context.getBean("user",User.class);
System.out.println(user);
}
}
4.3 屬性如何注入
4.3.1 @Value
- @value注入可以不用set方法
- 相當於<property name="name" value="happybyxml"></property>
- 複雜型別,如map,list等還是建議用xml注入屬性,而不用註解
package com.happy.pojo;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
/*<!-- <bean id="user" class="com.happy.pojo.User"></bean>-->*/
@Component
public class User {
@Value("happy518")
public String name ;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
User user = context.getBean("user",User.class);
System.out.println(user);
}
}
4.3 衍生的註解
- @Component有幾個衍生註解,我們再web開發中,會按照mvc三層架構分層。
- 這3個註解和@Component是等價的,人為定義@Component以後是註解非這3個註解的bean上
4.3.1 Dao層->@Repository
package com.happy.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}
4.3.2 Service層->@Service
package com.happy.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}
4.3.3 Controller層->@Controller
package com.happy.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}
4.4 自動裝配置
見第三章節。
4.5 作用域
@Scope
package com.happy.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
/*<!-- <bean id="user" class="com.happy.pojo.User"></bean>-->*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
//@Scope("singleton")
@Scope("prototype")
public class User {
@Value("happy518")
public String name ;
}
package com.happy;
import com.happy.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
// 單例模式,每次get為同一個物件
User user1 = context.getBean("user", User.class);
User user2 = context.getBean("user", User.class);
System.out.println(user1 == user2);
}
}
4.6 小結
XML vs 註解
Xml | 註解 | |
---|---|---|
不同 | xml更加萬能,適用於任何場合! | 註解:不是自己類使用不了 |
集中管理,維護簡單 | 維護相對複雜 | |
解耦 | 耦合較強 |
最佳實踐:
- xml用來管理bean
- 註解負責完成屬性注入
- 我們再使用的過程中,只需要注意一個問題,必須要讓註解生效,必須開啟註解的支援
五、使用Java配置類
-
使用java的方式配置spring,完全不用xml配置了,全權交給java來做
-
JavaConfig是Spring的一個子專案,在Spring 4之後,它成為另一個核心功能。
5.1 @Configuration介紹
1 使用和註冊配置類
-
在類上加上這個註解,代表該類是一個spring的配置類,
等價於一個beans的application-context.xml檔案。
-
會被spring容器託管,註冊到容器中,因為他本身也是一個component。
2 使用配置類註冊bean
- 註冊一個bean,就相當於我們之前寫的一個bean標籤
- 配置類中的方法名字,就相當於bean標籤中的id屬性。
- 這個方法的返回型別,就相當於bean標籤中的class屬性。
- 方法的返回物件就是要注入bean的物件
3 獲取容器
- 完全使用了配置類方式去做的方式,我們需要通過ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class)來獲取容器了。
5.2 配置類使用步驟
1 實體類
package com.happy.pojo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
@Data
public class User {
@Value("gaoxing")
private String name;
}
2 配置類:
package com.happy.config;
import com.happy.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Configuration
//@Component
//@ComponentScan("com.happy.pojo")
public class MyConfig {
@Bean
public User getUser(){
return new User();
}
}
3 測試類
package com.happy;
import com.happy.config.MyConfig;
import com.happy.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User) context.getBean("getUser");
System.out.println(user);
}
}
這種純java的配置方式,在SpringBoot中隨處可見!