Spring的@Bean注解用於告訴方法,產生一個Bean對象,然后將這個Bean對象交給Spring管理, 和xml配置中的bean標簽的作用是一樣的。
@Bean是一個方法級別上的注解,主要用在@Configuration注解的類里,也可以用在@Component注解的類里,添加的bean的id為方法名。
@Configuration與@Bean結合使用。@Configuration可理解為用spring的時候xml里面的<beans>標簽,@Bean可理解為用spring的時候xml里面的<bean>標簽。Spring Boot不是spring的加強版,所以@Configuration和@Bean同樣可以用在普通的spring項目中,而不是Spring Boot特有的,只是在spring用的時候,注意加上掃包配置。
Bean注解的作用之一就是能夠管理第三方jar包內的類到容器中。
現在我們引入一個第三方的jar包,這其中的某個類,StringUtil需要注入到我們的IndexService類中,因為我們沒有源碼,不能再StringUtil中增加@Component或者@Service注解。這時候我們可以通過使用@Bean的方式,把這個類交到Spring容器進行管理,最終就能夠被注入到IndexService實例中。
在@Configuration中被@Bean標記的方法,會被Spring進行CGLIB代理,從而進行增強。
一、Spring注解分類
從廣義上Spring注解可以分為兩類:
1、一類注解是用於注冊Bean
假如IOC容器就是一間空屋子,首先這間空屋子啥都沒有,我們要吃大餐,我們就要從外部搬運食材和餐具進來。這里把某一樣食材或者某一樣餐具搬進空屋子的操作就相當於每個注冊Bean的注解作用類似。
注冊Bean的注解作用就是往IOC容器中放(注冊)東西!
用於注冊Bean的注解: 比如@Component , @Repository , @ Controller , @Service , @Configration,這些注解就是用於注冊Bean,放進IOC容器中,一來交給spring管理方便解耦,二來還可以進行二次使用。
那么什么是二次使用呢?這里的二次使用可以理解為:在你開始從外部搬運食材和餐具進空屋子的時候,一次性搬運了豬肉、羊肉、鐵勺、筷子四樣東西,這個時候你要開始吃大餐,首先你吃東西的時候肯定要用筷子或者鐵勺,別說你手抓,只要你需要,你就會去找,這個時候發現你已經把筷子或者鐵勺放進了屋子,你就不用再去外部拿筷子進屋子了,意思就是IOC容器中已經存在,就可以直接拿去用,而不必再去注冊!
而拿屋子里已有的東西的操作就是下面要講的用於使用Bean的注解。
2、另一類注解是用於使用Bean
用於使用Bean的注解:比如@Autowired、@Resource注解,這些注解就是把屋子里的東西直接拿來用。
如果你要拿,前提一定是屋子(IOC容器)里有的,不然就會報錯,比如你要做一道牛肉拼盤,需要五頭牛做原材料才行,你現在鍋里只有四頭牛,這個時候你知道,自己往屋子里搬過五頭牛,這個時候就直接把屋子里的那頭牛直接放進鍋里,完成牛肉拼盤的組裝。
這些注解就是需要啥想要啥,只要容器中有就往容器中拿!而這些注解又有各自的區別:比如@Autowired用在筷子上,這筷子你可能只想用木質的,或許只想用鐵質的,@Autowired作用在什么屬性的筷子就那什么筷子,而@Resource如果用在安格斯牛肉上面,就指定要名字就是安格斯牛肉的牛肉。
3、總結:
使用Bean的注解,就是把已經在xml文件中配置好的Bean拿來用,完成屬性、方法的組裝;比如@Autowired , @Resource,可以通過byTYPE(@Autowired)、byNAME(@Resource)的方式獲取Bean;
而注冊Bean的注解,比如:@Component , @Repository , @ Controller , @Service , @Configration,就是把你要實例化的對象轉化成一個Bean,放在IOC容器中,等你要用的時候,它會和上面的@Autowired , @Resource配合到一起,把對象、屬性、方法完美組裝。
二、@Bean注解概述
@Bean這個注解屬於用於注冊Bean的注解。
@Bean 就放在方法上,其作用就是讓方法去產生一個 Bean,然后交給Spring容器。
產生這個Bean對象的方法Spring只會調用一次,隨后這個Spring將會將這個Bean對象放在自己的IOC容器中。
Spring的@Bean注解明確的指示了一個方法,什么方法呢 —— 產生一個 Bean 的方法,並且交給 Spring 容器去管理。從這我們就明白了為啥 @Bean 是放在方法上了,因為它明確的告訴了被注解的方法,你給我產生一個Bean,然后交給Spring容器,剩下的你就不用管了。
如下就能讓accountDao方法產生一個 AccountDao 對象,然后這個AccountDao 對象交給Spring管理
class A{ @Bean public AccountDao accountDao(){ return new AccountDao(); } }
實際上,@Bean注解和xml配置中的bean標簽的作用是一樣的。
三、為什么要有@Bean注解?
不知道大家有沒有想過,用於注冊Bean的注解的有那么多個,為何還要出現@Bean注解?
原因很簡單:類似@Component , @Repository , @ Controller , @Service 這些注冊Bean的注解存在局限性,只能局限作用於自己編寫的類,如果是一個jar包第三方庫要加入IOC容器的話,這些注解就手無縛雞之力了。
是的,@Bean注解就可以做到這一點。
當然除了@Bean注解能做到外,還有@Import也能把第三方庫中的類實例交給spring管理,而且@Import更加方便快捷。
使用@Bean注解的另一個好處就是能夠動態獲取一個Bean對象,能夠根據環境不同得到不同的Bean對象。
四、@Bean注解總結
1、Spring的@Bean注解用於告訴方法,產生一個Bean對象,然后這個Bean對象交給Spring管理。 產生這個Bean對象的方法Spring只會調用一次,隨后這個Spring將會將這個Bean對象放在自己的IOC容器中。
2、@Component , @Repository , @ Controller , @Service 這些注解只局限於自己編寫的類,而@Bean注解能把第三方庫中的類實例加入IOC容器中並交給spring管理。
3、@Bean注解的另一個好處就是能夠動態獲取一個Bean對象,能夠根據環境不同得到不同的Bean對象。
4、記住:@Bean就放在方法上,就是讓方法去產生一個Bean,然后交給Spring容器,剩下的你就別管了。
五、使用代碼示例
我們使用一個案例來解釋:
// 1、功能性的Bean:主要展示 sayHello 方法
public class FunctionService { public String sayHello(String word){ return "hello,"+word+"!"; } } // 2、使用功能性的Bean:使用上面定義的Bean的 sayHello 方法
public class UseFunctionService { FunctionService functionService; public void setFunctionService(FunctionService functionService){ this.functionService = functionService; } public String sayHello(String word){ return this.functionService.sayHello(word); } } // 3、配置類
@Configuration public class JavaConfig { @Bean // 注冊上面那個功能性Bean進IOC容器 public FunctionService functionService() { return new FunctionService(); } @Bean // 注冊上面那個使用功能性的Bean進IOC容器 public UseFunctionService useFunctionService(FunctionService functionService){ UseFunctionService useFunctionService = new UseFunctionService(); useFunctionService.setFunctionService(functionService); return useFunctionService; } }
// 測試
public class Main { public static void main(String args[]){ AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(JavaConfig.class); UseFunctionService useFunctionService = context.getBean(UseFunctionService.class); System.out.println(useFunctionService.sayHello("java config")); context.close(); } }
代碼解釋:
1、我們使用的是Java配置的方式和注解混合配置。沒有使用xml配置加注解的方式,Java配置的方式是Spring 4.x推薦的配置方式,可以完全代替xml配置。
2、使用@Configuration表明當前類是一個配置類。此處沒有使用包掃描,是因為所有的Bean都在類中定義了。
3、Spring的@Bean注解用於告訴方法,產生一個Bean對象,然后這個Bean對象交給Spring管理。產生這個Bean對象的方法Spring只會調用一次,隨后這個Spring將會將這個Bean對象放在自己的IOC容器中。使用@Bean注解的好處就是能夠動態獲取一個Bean對象,能夠根據環境不同得到不同的Bean對象。或者說將Spring和其他組件分離(其他組件不依賴Spring,但是又想Spring管理生成的bean)。
4、這種方法相當於在xml中配置bean標簽,配置類相當於配置文件,方法名是bean的id。
六、Spring的三種裝配機制
Spring中提供了三種主要的裝配機制:
1、在xml中進行顯式配置
也就是在xml配置文件中使用bean標簽聲明。
2、在java中進行顯式配置
也就是我們代碼示例里使用的這種。
3、隱式bean發現機制和自動裝配
隱式bean發現機制就是:Spring支持掃描使用@Service、@Compent、@Repository、@Controller的類,並注冊成Bean。
自動裝配指的就是:Spring會嘗試滿足在使用@Autowired的屬性值和方法相對應的依賴(屬性值就是本身的實體,方法就是參數中的依賴),並將符合的bean填充過來。
七、基於XML方式Bean使用
// 定義一個user用戶bean對象如下
public class User { private String userName; private Integer age; }
在src\main\resources目錄下邊新建一個beans.xml文件如下
<?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" xmlns:tx="http://www.springframework.org/schema/tx" 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-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 定義一個id為user的bean對象 -->
<bean id="user" class="com.zhang.bean.User">
<property name="age" value="26"></property>
<property name="userName" value="gg"></property>
</bean>
</beans>
// 測試
public class ApplicationTest { @SuppressWarnings("resource") public static void main(String[] args) { // 使用ClassPathXmlApplicationContext獲取spring容器ApplicationContext
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); // 根據bean id獲取bean對象
User bean = (User) applicationContext.getBean("user"); System.out.println(bean); } } // 運行結果如下:User [userName=gg, age=26]
將如上例子用注解實現:首先定義一個注解配置文件如下
// 定義一個注解配置文件 必須要加上@Configuration注解
@Configuration public class MainConfig { // 定義一個bean對象
@Bean public User getUser(){ return new User("gg",26); } }
// 使用AnnotationConfigApplicationContext獲取spring容器ApplicationContext2
ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class); User bean2 = applicationContext2.getBean(User.class); System.out.println(bean2);
八、@Bean注解的源代碼分析
//能夠作用在方法和注解上,作用在方法上和作用在注解上的用處不同,通常作用在方法上。 //作用在注解上時會使得該注解也可以用於注冊主鍵,屬於Bean的衍生注解,與@Component類似。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Bean { //設置生成的組件的id,如果不設置時,組件id默認為注解到方法名。
@AliasFor("name") String[] value() default {}; //與value屬性互為別名,作用一樣。
@AliasFor("value") String[] name() default {}; /** @deprecated */
//在spring5.1之后該屬性不推薦使用。設置Autowire自動裝配時首選的裝配方式。
@Deprecated Autowire autowire() default Autowire.NO; //該組件能否用@Autowired注解進行自動裝配,默認true。 //如果設置為false,則用Autowired注解進行自動裝配時會報錯。
boolean autowireCandidate() default true; //指定bean創建的初始化方法
String initMethod() default ""; //指定bean的銷毀回調方法。
String destroyMethod() default "(inferred)"; }
@baen注解的@Target是ElementType.METHOD,ElementType.ANNOTATION_TYPE也就說@Bean注解可以在使用在方法上,以及一個注釋類型聲明。
具體參數如下:
value -- bean別名和name是相互依賴關聯的,value,name如果都使用的話值必須要一致
name -- bean名稱,如果不寫會默認為注解的方法名稱
autowire -- 自定裝配默認是不開啟的,建議盡量不要開啟,因為自動裝配不能裝配基本數據類型、字符串、數組等,這是自動裝配設計的局限性,以及自動裝配不如顯示依賴注入精確
boolean autowireCandidate() default true;該屬性設置該組件能否用@Autowired注解進行自動裝配,默認true。如果設置為false,則用Autowired注解進行自動裝配時會報錯。
initMethod -- bean的初始化之前的執行方法,該參數一般不怎么用,因為可以完全可以在代碼中實現
destroyMethod -- bean銷毀執行的方法
1、為user用戶bean增加注解名稱如下:
如果value和name內容不一樣如:,會報錯:
attribute 'name' and its alias 'value' are declared with values of [{user1}] and [{user0}], but only one is permitted
2、在用戶bean中增加初始化和銷毀的方法如下:
public void initUser(){ System.out.println("初始化用戶bean之前執行"); } public void destroyUser(){ System.out.println("bean銷毀之后執行"); }
注解如下:
(1)String initMethod() default “”;
設置組件實例化時的初始化方法,會在依賴注入之后執行。屬於bean生命周期的其中一個時期。初始化方法不能帶有參數。
(2)String destroyMethod() default “(inferred)”;
設置組件實例化時的銷毀方法,通常用於釋放一些資源,觸發時機是IOC容器關閉或者該組件從IOC容器中移除時觸發該方法。銷毀方法也不能帶有參數。