大廠面試官最常問(wèn)的@Configuration+@Bean(JDKConfig編程方式)案例分享
現(xiàn)在大部分的Spring項(xiàng)目都采用了基于注解的配置,采用了@Configuration 替換標(biāo)簽的做法。一行 簡(jiǎn)單的注解就可以解決很多事情。但是,其實(shí)每一個(gè)注解背后都有很多值得學(xué)習(xí)和思考的內(nèi)容。這 些思考的點(diǎn)也是很多大廠面試官喜歡問(wèn)的內(nèi)容。
@Configuration處理類:org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
@Bean處理類:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$ConfigurationClassBeanDefinition
· 項(xiàng)目包結(jié)構(gòu)
F:.
├─java
│ └─com
│ └─example
│ └─demo4
│ │ Demo4Application.java
│ │
│ ├─configuration
│ │ PersonConfiguration.java
│ │ StuConfiguration.java
│ │
│ ├─controll
│ │ StuController.java
│ │
│ ├─dao
│ │ StuDao.java
│ │ StuDaoImp.java
│ │
│ ├─entity
│ │ Person.java
│ │ Stu.java
│ │
│ └─server
│ StuService.java
│ StuServiceImp.java
│
└─resources
application.properties
project.text
· Beans依賴圖
·
·
問(wèn)題1:@Configuration和@Component區(qū)別**(@Configuration自動(dòng)cglib動(dòng)態(tài)代理)**
·
o @Configuration 中所有帶 @Bean 注解的方法都會(huì)被動(dòng)態(tài)代理,因此調(diào)用該方法返回的都是同一個(gè)實(shí)例(Spring啟動(dòng)時(shí)會(huì)專門(mén)處理@Configuration)。proxyBeanMethods可以設(shè)置成false,取消代理。除此之外以下@Bean無(wú)法動(dòng)態(tài)代理。
o 配置類必須以類的形式提供(不能是工廠方法返回的實(shí)例),允許通過(guò)生成子類在運(yùn)行時(shí)增強(qiáng)(cglib 動(dòng)態(tài)代理)。
o 配置類不能是 final 類
o 配置類必須是非本地的(即不能在方法中聲明,不能是 private)。
o 任何嵌套配置類都必須聲明為static。
o @Bean必須是單例(默認(rèn)就是,別改成prototype)
o @Component里面的@Bean不是代理的
o 代理和不代理的區(qū)別如下代碼:
//拿到@Bean->person
Person person = context.getBean(Person.class);
//拿到組件
PersonConfiguration personConfiguration = context.getBean(PersonConfiguration.class);
//執(zhí)行組件里面的person()方法
Person person1=personConfiguration.person();
//使用stu
System.out.println(stu.getClass().getName());
//獲取組件里面的person()方法對(duì)象的hashCode
System.out.println("person1:"+person1.getClass().getName() + "@" + Integer.toHexString(person1.hashCode()));
//@Bean->person對(duì)象的hashCode
System.out.println("person:"+person.getClass().getName() + "@" + Integer.toHexString(person.hashCode()));
//從IOC容器中取出存在的personBean
Map<String, Person> beansOfType = context.getBeansOfType(Person.class);
//對(duì)比知道不代理執(zhí)行組件里面的person()方法的對(duì)象不會(huì)被Spring管理,代理就會(huì)管理
System.out.println("beansOfType:"+beansOfType.get("person").getClass().getName() + "@" + Integer.toHexString(person.hashCode()));
·
問(wèn)題2:expected single matching bean but found 2
·
o
同類型多個(gè)Beans,引發(fā)原因比如使用了context.getBean(Stu.class)、@Autowired只用ByType類獲取或者注入Beans的時(shí)候
o
o
使用名稱獲取,不優(yōu)先使用ByType,==如果是其它第三方(也許第三方直接ByType)那么可以采取【禁止使用】或【優(yōu)先使用】
o
o
禁止使用
o
o @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
o application.properties里配置:spring.autoconfigure.exclude=全類名,全類名
o
優(yōu)先使用(在設(shè)計(jì)層面不建議這樣使用)
o
o
@Bean
@Primary
public Person person() {
return new Person();
}
o
o
==正確姿態(tài)約定好命名規(guī)則尤為重要==
o
·
問(wèn)題3:could not be registered. A bean with that name has already been defined in file
·
o 組件重復(fù)問(wèn)題,大多使用配置:spring.main.allow-bean-definition-overriding=true
o 以上配置會(huì)覆蓋Bean,并且依然產(chǎn)生具體對(duì)象
具體底層實(shí)現(xiàn)類(誰(shuí)定義存儲(chǔ)了這些注冊(cè)類??):
·
AnnotatedGenericBeanDefinition:存儲(chǔ)@Configuration注解注釋的類
·
·
ScannedGenericBeanDefinition:存儲(chǔ)@Component、@Service、@Controller等注解注釋的類
·
·
spring初始化時(shí),會(huì)用GenericBeanDefinition或是ConfigurationClassBeanDefinition(用@Bean注解注釋的類)存儲(chǔ)用戶自定義的Bean,在初始化Bean時(shí),又會(huì)將其轉(zhuǎn)換為RootBeanDefinition