Configuration以及Bean註解的使用
該知識點在Spring中應該學過,沒有學過或者遺忘的的朋友需要預習或溫習前置知識點。SpringBoot其實就是Spring的進一步簡化,所以前置知識點還是有必要的學習的,這樣更能明白其底層的原理。
好了,廢話不多說,開始!
結構目錄:
pojo--User:
package com.xbhog.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private String name;
private int age;
}
config-MyConfig:
package com.xbhog.config;
import com.xbhog.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public User user(){
return new User("xbhog",18);
}
}
controller-Mycontroller:
package com.xbhog.controller;
import com.xbhog.config.MyConfig;
import com.xbhog.pojo.User;
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mycontroller {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user.toString());
}
}
前面這三個檔案埋了一個坑,使用SpringBoot啟動的話是找不到Bean的,因為我們必須把檔案放到與主啟動程式同一目錄下,這樣才能找到,可以這樣:這是由於SpringBootApplication的掃描路徑決定的
但是當我們把Myapp放入主程式資料夾時:發現並沒有找到相應的元件資訊
在不改變原來的程式的情況下,我們可以使用手動掃描的方式,設定自定義掃名路徑:
package com.xbhog.springboot1times;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog") //設定掃描路徑
public class Myapp {
public static void main(String[] args) {
/*1. 返回我們IOC容器*/
ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
/*2.檢視容器裡面的元件*/
String[] names = run.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
}
效果顯示:
單例項問題:
- 判斷元件在容器中是否為單例項:
package com.xbhog.springboot1times;
import com.xbhog.pojo.Pet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog")
public class Myapp {
public static void main(String[] args) {
/*1. 返回我們IOC容器*/
ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
/*2.從容器中獲取元件*/
Pet tom1 = run.getBean("tom11", Pet.class);
Pet tom2 = run.getBean("tom11", Pet.class);
System.out.println("元件是否為單例項:"+(tom1== tom2));
}
}
元件是否為單例項:true
Myconfig呼叫問題:
因為配置類也屬於元件,如果我們獲取配置類元件後,通過例項化物件在呼叫其中的bean,是呼叫普通方法呢,還是呼叫容器中的相應的元件?
package com.xbhog.springboot1times;
import com.xbhog.config.MyConfig;
import com.xbhog.pojo.Pet;
import com.xbhog.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog")
public class Myapp {
public static void main(String[] args) {
/*1. 返回我們IOC容器*/
ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
/*2.檢視容器裡面的元件*/
String[] names = run.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
/*3.從容器中獲取元件*/
Pet tom1 = run.getBean("tom11", Pet.class);
Pet tom2 = run.getBean("tom11", Pet.class);
System.out.println("元件是否為單例項:"+(tom1== tom2));
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
User user1 = bean.user();
User user2 = bean.user();
System.out.println("測試通過Myconfig類呼叫的user元件:"+(user1==user2));
}
}
效果如下:
元件是否為單例項:true
com.xbhog.config.MyConfig EnhancerBySpringCGLIB 9b1ae7c2@6c0905f6
測試通過Myconfig類呼叫的user元件:true
上面的效果也就是Configuration(proxyBeanMethods=true)的作用,保證每個@Bean方法被呼叫多少次返回的元件都是單例項的
實際上就是proxyBeanMethods:代理bean的方法(true),外部無論對配置類中的這個元件註冊方法呼叫多少次獲取的都是之前註冊容器中的單例項物件,也叫:FULL模式.
當proxyBeanMethods=true
時:
MyConfig返回的Bean本身就是代理物件,CGLIB,並且測試通過Myconfig類呼叫的user元件:true.
com.xbhog.config.MyConfig EnhancerBySpringCGLIB 9b1ae7c2@6c0905f6
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
//如果@Configuration(proxyBeanMethods = true)代理物件呼叫方法。
// SpringBoot總會檢查這個元件是否在容器中有。
User user1 = bean.user();
User user2 = bean.user();
System.out.println("測試通過Myconfig類呼叫的user元件:"+(user1==user2));
當:proxyBeanMethods=false
時(Lite模式-輕量級):
MyCnfig返回的就不是代理模式,測試通過Myconfig類呼叫的user元件:false
總結:
proxyBeanMethods:代理bean的方法 ;
- Full(proxyBeanMethods = true)、【保證每個@Bean方法被呼叫多少次返回的元件都是單例項的】
- Lite(proxyBeanMethods = false)【每個@Bean方法被呼叫多少次返回的元件都是新建立的】
- 元件依賴必須使用Full模式預設。其他預設是否Lite模式
使用場景:
現在向pojo.User中新增Pet物件:
package com.xbhog.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private String name;
private int age;
private Pet pet;
}
在proxyBeanMethods=true
模式下才是正確的;
package com.xbhog.config;
import com.xbhog.pojo.Pet;
import com.xbhog.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration //告訴SpringBoot這是一個配置類 == application.xml
public class MyConfig {
@Bean //給容器新增元件,以方法名作為元件的id,返回型別就是元件型別。返回的值,就是元件在容器中的例項
public User user(){
User user = new User("xbhog", 18);
//user元件依賴與Pet元件,使用者中的寵物與容器中的寵物時一樣的
user.setPet(tomcat());
return user;
}
@Bean("tom11") //設定bean別名--》id的名字
public Pet tomcat(){
return new Pet("tomcat");
}
}
User user = run.getBean("user", User.class);
Pet tom3 = run.getBean("tom11", Pet.class);
System.out.println("使用者的寵物"+(user.getPet() == tom3));
使用者的寵物true;
在proxyBeanMethods=false
模式後,就不會掃描容器,直接建立物件:
元件是否為單例項:true
com.xbhog.config.MyConfig@330c1f61
測試通過Myconfig類呼叫的user元件:false
使用者的寵物false
如果你看到這裡或者正好對你有所幫助,希望能點個關注或者推薦,感謝;
有錯誤的地方,歡迎在評論指出,作者看到會進行修改。