什麼是Bean?
談Bean的潛臺詞是在說Spring中的Bean,我們都知道Spring中的BeanFactory,而Bean這個概念也是由此而來。在Spring中,只要一個類能被實例化,並被Spring容器管理,這個類就稱爲一個Bean,或者SpringBean.
除此之外,我們還聽到一些其他的詞:
JavaBean、POJO、VO、DTO
這些叫法又是什麼意思?使用的場景又是什麼?
-
JavaBean
一個JavaBean是一個遵循Sun公司JavaBean規範的類。JavaBean可以理解爲java中可以複用的組件,它滿足下麪條件:
- 有一個公共的缺省構造方法
- 這個類的屬性使用getter和setter來訪問,並且命名遵從標準的規範
- 這個類可以序列化
-
POJO(Plain Ordinary Object )
POJO是一個歷史遺留名稱,爲什麼這樣講?因爲POJO是用來指明該對象不同於Entity Beans
EntityBeans是EJB中的概念,而EJB在Spring出現後,就漸漸淡出了歷史的舞臺。所以,POJO在Martin Fowler提出時,就是指那些沒有實現任何EJB接口的普通java類。而延用至今,嚴格講,所有的java類,都是POJO,因爲現在沒有人在使用ejb這些老古董了。但是有時我們爲了區分Spring Bean,可以將沒有被Spring管理的類稱爲POJO。
-
VO (Value Object)
VO指一個對象例如
java.lang.Integer
它持有一些數據,或數據對象。這個概念是 Martin Fowler企業應用架構中提出的概念。 -
DTO (Data Transfer Object)
DTO也是EJB種提出的一個概念,目的就是在數據傳輸時,通過直接傳輸對象,在網絡中傳輸數據。
小結:
所以對我們而言,VO和DTO沒有區別(但是Martin Fowler可能用它們表示了不同的細分概念),而大多數時候,它們遵循JavaBean規範,所以它們也都是JavaBean。當然,它們都是POJO。
可以看出,它們本質上都是在指一個java對象,爲了區分場景和功能,有了不同的叫法。開發中有時還會出現,Entity, Domain等。用來表示對實體的映射,或表的映射。一般可以這樣做來規範開發:
-
對於Spring管理的對象,稱爲Bean
-
映射到數據表的對象實體類,稱爲entity,放在entity目錄
-
對於接口用於封裝數據,比如接受一個json入參,爲了方便,定義一個對象封裝參數,可以叫dto (或pojo) 放在pojo包,以表明它不是某個表的映射類。
註解@Bean @Component …等都有什麼區別?
用SpringBoot開發應用時,我們會用註解將對象交給Spring容器管理。這些註解包括:
@Component ,@Service, @Bean, @Controller ,@Repository
這些註解本質上,都是Spring標識,用來進行Bean的自動檢測。標註這些註解的類會被Spring容器管理。
那爲何要有這些分類,爲何不使用一個註解就來搞定所有的工作?
首先這幾個註解根據語義,用在不同的層面
- @Componet 一般的組件
- @Service是Service層組件
- @Bean 這個要和@Configuration一塊使用,後邊再說
- @Controller是用在SpringMVC控制層
- @Repository是數據訪問層
Spring這樣設計,是因爲,這些註解不光是要做自動檢測。同時有不同的功能,比如@Repository註解,Spring會增加增強處理,進行相關的異常處理。@Controller的bean會處理網絡請求相關邏輯。所以你給所有的Bean都標註同一個註解,確實都會注入Spring容器,但是功能可能就會失效。而且隨着Spring版本升級,可能會增加更多差異化處理。所以我們應該按照規範來註解。
再說到@Bean,我們知道Spring早期,還是通過xml配置Bean例如:
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="operations" class="com.howtodoinjava.spring.beans.Operations"></bean>
<bean id="employee" class="com.howtodoinjava.spring.beans.Employee"></bean>
</beans>
現在,你可以理解@Configuration註解的類就是一個xml配置文件,中間@Bean註解就是xml中的bean節點
@Configuration
public class BeanConfiguration {
@Bean
public Operations operation(){
return new Operations();
}
@Bean
public Employee employee(){
return new Employee();
}
}
這兩種方式都是將@Bean註解返回值注入Spring容器。SpringBoot在啓動時,會掃描@Configuration註解,進行注入。
SpringBoot注入對象衝突如何解決?
好了,現在我們終於把想要的組件交給Spring容器管理。我們該如何使用?
我們可以用Spring上下文,獲取需要的對象
public static void main(String[] args) {
ApplicationContext application = SpringApplication.run(ConsumerApplication.class, args);
application.getBean(Employee.class);
}
一般我們用@Autowire
註解,獲取容器中的bean
@Autowire
private Employee employee;
有時我們在容器中需要注入一個類的多個實例,以滿足需求。比如一個接口的實現類有兩個,如果直接通過@Component注入容器,則會報錯。如何區分?
@Component("bean01")
public class Bean01 implement AA{
}
@Component("bean02")
public class Bean02 implement AA{
}
沒錯,通過在註解中標識一個名稱,來區分該對象的不同實例。
獲取時:最終會初始化一個Bean01
@Autowire
@Qualifier("bean01")
private AA a;
這樣有個問題,就是每次使用都需要顯示聲明@Qualifier來指定。有的場景下,我們可能想默認使用一個,其他情況再顯式指定。這就涉及到@Primary
在註解時,標註了@Primary的Bean在沒有指定的情況下,會默認加載。
比如:
@Component
@Primary
public class Bean01 implement AA{
}
@Component("bean02")
public class Bean02 implement AA{
}
使用時: 默認初始化的就是Bean01
@Autowire
private AA a;