Spring學習日記(一)
Spring學習日記(一)----IoC
一、IoC容器簡介
Spring IoC容器是一個管理Bean的容器。在Spring的定義中,所有的IoC容器都需要實現介面BeanFactory。
下面是BeanFactory
原始碼:
public interface BeanFactory {
//字首
String FACTORY_BEAN_PREFIX = "&";
//多個getBean方法
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
//是否包含Bean
boolean containsBean(String var1);
//是否是單例
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
//是否是原型
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
//是否型別匹配
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
//獲取Bean型別
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
//獲取Bean別名
String[] getAliases(Str
ing var1);
}
多個getBean
方法,用於按型別、名稱等獲取Bean;
isSingleton
方法判斷Bean是否單例,預設情況Bean都是單例的;
isPrototype
方法與isSingleton
方法相反,如果isPrototype
結果為true,使用getBean
時,IoC容器會返回一個新的Bean給呼叫者。
二、裝配Bean
Spring可以使用XML或Java配置檔案來裝配Bean,或者使用註解掃描裝配
1、通過配置檔案
首先,我們定義一個POJO
public class Student {
private Long id;
private String name;
public Student(Long id, String name) {
this.id = id;
this.name = name;
}
//get、set方法省略
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
然後,在bean.xml檔案中裝配它
<bean id="student" class="XMLComponent.Student">
<constructor-arg name="id" value="1001"></constructor-arg>
<constructor-arg name="name" value="xml_student"></constructor-arg>
</bean>
接下來,就可以通過getBean
獲取這個Bean
public class XMLComponent {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = (Student)context.getBean("student");
System.out.println(student);
}
}
剛剛獲取的Bean:
除了通過xml配置檔案可以裝配bean,通過Java配置檔案(使用@Configuration
註解)也可以裝配Bean:
我們定義一個Java配置檔案:
@Configuration
public class StudentConfig {
@Bean(name="java_config_bean")
public Student init(){
Student student = new Student();
student.setId(10003L);
student.setName("java_config_name");
return student;
}
}
使用@Configuration
註解表明類StudentConfig
是一個配置檔案,@Bean
註解表示init方法將返回POJO裝配到IoC容器中,name屬性為Bean的名字(為配置將使用方法名init作為Bean的名字)
下面,我們使用getBean
方法獲取這個bean
public class JavaConfigComponent {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(StudentConfig.class);
Student student = (Student)ac.getBean("java_config_bean");
System.out.println(student);
}
}
成功獲取到Bean:
2、通過註解掃描
一個個裝配Bean太麻煩,我們可以使用@Component
和@ComponentScan
註解進行掃描裝配
註解@Component
標明將被IoC容器掃描裝配的類,註解中value的值為Bean的名稱,如果未註明value,IoC容器將會將類名首字母小寫後的類名作為Bean名稱。
@Component("teacher")
public class Teacher {
@Value("1234")
private Long id;
@Value("annotationScan")
private String name;
//省略get、set方法
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
指定Bean,還需要指定何種策略去掃描裝配Bean,為了讓IoC容器裝配該類,需要改造類AppConfig
,使用註解@ComponentScan
對當前包和子包進行掃描:
@Configuration
@ComponentScan
public class AppConfig {
}
這樣,就可以使用getBean方法來獲取裝配好的Bean:
public class AnnotationScan {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
Teacher teacher = (Teacher)ac.getBean("teacher");
System.out.println(teacher);
}
}
測試一下,成功的獲取到了裝配的Bean:
註解@ComponentScan
可以自定義掃描規則:
定義掃描的包名:
@ComponentScan( basePackages = {"cn.codeaper.*"})
定義掃描的類:
@ComponentScan( basePackageClass = {User.class})
此外,還可以加上註解@Filter定義過濾條件
使用includeFilters
定義滿足過濾器條件才掃描:
@ComponentScan(basePackages = "AnnotationScan",includeFilters = {@ComponentScan.Filter(classes = Service.class)})
使用exculdeFilters
定義不掃描滿足過濾條件的Bean:
@ComponentScan(basePackages = "AnnotationScan",excludeFilters = {@ComponentScan.Filter(classes = Service.class)})
注:在excludedFilters
中的class屬性指定的是Controller,Service之類的,不是具體的類。
三、依賴注入
1、註解@Autowired
有時候,Bean之間會有依賴關係,比如,人類(Person
)使用餐具(Tableware
)吃飯,一個Bean會使用到其它Bean,在Spring IoC容器中,稱為依賴注入(DI)。
人類(Person
):
@Component("person")
public class Person {
@Autowired
private Tableware tableware;
public void eat(){
this.tableware.use();
}
public void setTableware(Tableware tableware){
this.tableware = tableware;
}
}
餐具(Tableware
)介面:
public interface Tableware {
public void use();
}
筷子(Chopsticks
)實現類:
@Component("chopsticks")
public class Chopsticks implements Tableware{
public void use() {
System.out.println("使用筷子夾菜...");
}
}
在人類介面中,使用註解@Autowired
將餐具(Tableware
)例項注入,註解@Autowired
根據屬性的型別找到對應的Bean進行注入,
下面我們測試一下:
@Configuration
@ComponentScan
public class AppConfig {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = ac.getBean(Person.class);
person.eat();
}
}
測試成功,我們成功的在人類(Person
)呼叫了tableware
的use
方法,說明餐具(Tableware
)例項成功注入:
如果此時,再為餐具(Tableware
)新增一個實現類勺子(Spoon
):
@Component("spoon")
public class Spoon implements Tableware{
public void use() {
System.out.println("使用勺子舀湯...");
}
}
那麼IoC容器將會丟擲異常:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'DI.Tableware' available: expected single matching bean but found 2: chopsticks,spoon
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:173)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 14 more
因為找到了兩個型別為Tableware
的bean。
註解@Autowired
首先根據型別尋找,如果找到的Bean不是唯一的,將根據屬性名稱和Bean的名稱進行匹配,如果依然不是唯一的,那麼將會丟擲異常。
2、消除歧義
對類使用註解@Primary
,該註解將會告訴IoC容器,發現多個Bean時優先選擇被這個註解標註的bean。
為勺子(Spoon
)添加註解:
@Primary
@Component("spoon")
public class Spoon implements Tableware{
public void use() {
System.out.println("使用勺子舀湯...");
}
}
IoC將會優先將它注入:
使用註解@Qualifier
也可以消除歧義,註解@Qualifier
與註解@Autowired
結合使用,按照型別和名稱譯器尋找bean注入:
name為chopsticks
型別為Tableware
的Bean被注入:
四、使用屬性檔案
在開發應用程式時,經常需要讀取配置檔案。最常用的配置方法是以key=value
的形式寫在.properties
檔案中。
Spring容器還提供了一個更簡單的@PropertySource
來自動讀取配置檔案。我們只需要在@Configuration
配置類上再新增一個註解:
@Component("datasource")
@PropertySource("mysql.properties") // 表示讀取classpath的app.properties
public class MysqlConfig {
@Value("${port:3306}")
private int port;
@Value("${database.username}")
private String username;
@Value("${database.password}")
private String password;
//get、set方法省略
@Override
public String toString() {
return "MysqlConfig{" +
"port=" + port +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
${port:3306}
表示使用port的值,如果沒有,九使用預設值3306。
測試一下,成功的讀取到值:
ord;
//get、set方法省略
@Override
public String toString() {
return "MysqlConfig{" +
"port=" + port +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
`${port:3306}`表示使用port的值,如果沒有,九使用預設值3306。
測試一下,成功的讀取到值:
![image-20210117143857818](https://img.796t.com/res/2021/02-03/06/3ca7f38b491e07d209d3cd813a301282.png)
![image-20210117143843740](https://img-blog.csdnimg.cn/img_convert/63ac570c80eeac6622b99cae8604d90d.png)
原文連結:[新增連結描述](http://www.codeaper.cn/blog/QZLMRsqblKk3)