1.@Bean與@Configuration
(1) 標注于類之上的@Configuration注解與標注于方法之上的@Bean注解是支持基于Java的容器配置的核心,被@Bean注解標注的方法用于實體化bean并將其注入至容器中,它與基于xml配置中的<bean/>標簽起著相同的作用,@Bean可用在任何被@Component注解標注的類中,不過絕大部分情況下它們都被用于被@Configuration注解標注的類中;被@Configuration注解標注的類通常作為bean的定義源,如同基于xml配置中的<beans/>標簽,此外,還可在@Configuration標注的類中配置bean之間的依賴關系,如下
//兩個普通的類,其中ExampleB依賴ExampleA
public class ExampleA { }
public class ExampleB {
private ExampleA exampleA;
public ExampleB(ExampleA exampleA) {
this.exampleA = exampleA;
}
}
//配置類
@Configuration
public class Config {
//注入bean ExampleA
@Bean
public ExampleA exampleA() {
return new ExampleA();
}
//呼叫exampleA()方法來配置ExampleB
@Bean
public ExampleB exampleB() {
return new ExampleB(exampleA());
}
//上下這兩個exampleB方法等價,注意,在下面這個例子中,Spring會自動幫我們注入ExampleA物件
// @Bean
// public ExampleB exampleB(ExampleA exampleA) {
// return new ExampleB(exampleA);
// }
}
//啟動容器,列印注入的物件是否相同
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
System.out.println(ctx.getBean(ExampleB.class).getExampleA() == ctx.getBean(ExampleA.class));
//觀察結果,可見Spring執行了依賴注入,注入了容器中的ExampleA,而非new出來了一個新的ExampleA
true
(2) @Bean也可用在任何被@Component注解標注的類中,此時,我們稱其為lite @Bean模式,而在這種lite模式下,我們不能配置bean之間的依賴關系,如下所示
//取上面的例子,其他保持不變,只將Config類上的@Configuration注解變更為@Component注解,此時其中的@Bean注解就處于lite @Bean模式
@Component
public class Config {
//保持不變...
}
//啟動容器,觀察列印結果,為false,可見此時Spring并沒有執行依賴注入,而是直接new出來了一個新的ExampleA給ExampleB,因此在這種lite模式下,我們不能配置bean之間的依賴關系
2.通過AnnotationConfigApplicationContext實體化Spring容器
(1) AnnotationConfigApplicationContext作為Spring的容器,它不僅可以接受@Configuration類(同時這個類本身也會被注冊為一個bean)作為引數,還可以接受@Component類或用JSR-330注解標注的類作為引數,如下
//例一: 一個Spring配置類
@Configuration
public class Config {
}
public static void main(String[] args) {
//使用@Configuration類作為輸入,完全擺脫掉基于xml的配置
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
//列印,會觀察到Config也被注入到了容器中
Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
}
//例二: ExampleB被JSR 330標準注解標注
@Component
public class ExampleA {
}
@Named
public class ExampleB {
@Inject
private ExampleA exampleA;
public ExampleA getExampleA() {
return exampleA;
}
}
public static void main(String[] args) {
//使用@Component類或用JSR-330注解標注的類作為引數,容器會對它們進行依賴注入
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ExampleA.class, ExampleB.class);
System.out.println(ctx.getBean(ExampleB.class).getExampleA());
}
(2) 可以使用AnnotationConfigApplicationContext類的register(Class<?>…?)方法編程式的向容器中注冊,如下
public class ExampleA { }
public static void main(String[] args) {
//使用無參建構式
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
//編程式的向容器中注冊bean ExampleA
ctx.register(ExampleA.class);
//refresh()方法用于處理bean,一定要呼叫此方法,否則容器將拋出例外
ctx.refresh();
System.out.println(ctx.getBean(ExampleA.class));
}
(3) 我們可以使用@ComponentScan(basePackages = "...")注解或<context:component-scan base-package="..."/>標簽來開啟注解掃描,此外,AnnotationConfigApplicationContext也提供了scan(String…?)方法來開啟注解掃描,如下
//ExampleA位于cn.example.spring.boke包下
@Component
public class ExampleA { }
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
//開啟注解掃描,掃描cn.example.spring.boke"包下的bean
ctx.scan("cn.example.spring.boke");
ctx.refresh();
System.out.println(ctx.getBean(ExampleA.class));
}
(4) 在web環境中,可以使用AnnotationConfigWebApplicationContext,來配置ContextLoaderListener監聽器或DispatcherServlet,具體配置細節可參考官方檔案
3.使用@Bean注解
(1) @Bean注解是一個方法級別的注解,類似于基于xml配置中的<bean/>標簽,我們可以在@Configuration類或@Component類中使用該注解,它常用于宣告一個bean,如下
@Configuration
public class Config {
//@Bean注解用于將方法的回傳值注冊為容器中的一個bean,方法回傳型別就是該bean的型別,默認情況下,方法名就是bean的名稱
//下面這個例子:向容器中注入一個型別為ExampleA,名稱為exampleA的bean
@Bean
public ExampleA exampleA() {
return new ExampleA();
}
}
(2) 針對@Bean方法的回傳型別,也有些細節需要注意,如下所示
//有兩個介面A和B
public interface A { }
public interface B { }
//ExampleA實作了這兩個介面
public class ExampleA implements A, B { }
//而另一個bean ExampleB,它依賴了型別為B的bean
@Component
public class ExampleB {
@Autowired
private B b;
public B getB() {
return b;
}
}
//配置類
@Configuration
@ComponentScan(basePackages = "cn.example.spring.boke")
public class Config {
//注意,我們回傳了ExampleA的實體,不過卻將回傳型別宣告為了A
@Bean
public A exampleA() {
return new ExampleA();
}
}
//接著,啟動容器,會發現容器拋出NoSuchBeanDefinitionException: No qualifying bean of type 'cn.example.spring.boke.B'的例外,可見容器將回傳的ExampleA僅視作了A型別然后就用于注入,雖然它也可以被視作B型別
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
System.out.println(ctx.getBean(ExampleB.class).getB());
//針對上面的情況,有兩種辦法解決:
//方法一:最直接,直接回傳型別B而非A,如下
@Bean
public B exampleA() {
return new ExampleA();
}
//方法二:其他的不變,只在我們的ExampleB上,加上@Lazy注解,讓容器有時間去充分的識別ExampleA,使它意識到ExampleA還可以視作B型別
@Component
@Lazy
public class ExampleB {
//省略...
}
(3) @Bean方法可以擁有任意數量的引數,這些引數就是這個bean的依賴項,如下
@Configuration
public class Config {
@Bean
public ExampleB exampleB() {
return new ExampleB();
}
//ExampleA依賴了ExampleB,Spring會為我們自動注入容器中的這個ExampleB物件,類似于基于建構式的依賴注入
@Bean
public ExampleA exampleA(ExampleB exampleB) {
return new ExampleA(exampleB);
}
}
(4) 同普通的bean,由@Bean方法注入的bean也支持由JSR-250所定義的生命周期回呼注解,支持InitializingBean,DisposableBean或Lifecycle等介面,支持各種Aware介面用于注入容器內置組件,同時@Bean還提供了initMethod和destroyMethod屬性用于配置初始化和銷毀回呼,同<bean/>標簽的屬性,如下
public class ExampleA implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@PostConstruct
public void init() {
System.out.println("init...");
}
public void destroy() {
System.out.println("destroy...");
}
}
@Configuration
public class Config {
@Bean(destroyMethod = "destroy")
public ExampleA exampleA() {
return new ExampleA();
}
}
(5) 默認情況下,當我們使用基于Java的配置來定義一個bean的時候,如果在這個bean中有宣告public修飾的close或shutdown方法,那么這些方法會隨著該bean的銷毀回呼的觸發而同時被呼叫,這是Spring提供的一個默認機制,無需任何的配置都會生效,但如果我們想關閉掉這一機制,可通過設定@Bean中的屬性destroyMethod=""來達到這一目的,如下
//設定destroyMethod = "",禁用掉Spring會自動觸發public修飾的close或shutdown方法的機制
@Bean(destroyMethod = "")
public ExampleA exampleA() {
return new ExampleA();
}
(6) 我們可以通過使用@Scope注解來指定bean的作用域,如下
@Configuration
public class Config {
//由@Bean方法所宣告的bean的作用域默認為singleton
//@Scope注解還可用于類上
@Bean
@Scope("prototype")
public ExampleA exampleA() {
return new ExampleA();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/544690.html
標籤:Java
上一篇:springboot01
