一、Spring 初次嘗試
1、通過xml形式配置IOC容器Bean
在Project下面找到一個resources 文件夾創建一個beans.xml (xml的文件名隨意)。如下,把你需要加載的bean 插入到bean節點下面。定義id 和 class類。當application被創建的時候,他就會去掃對應的xml文件,將這裏定義的bean加載到IOC容器。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="lsn01.cap01.Person">
<property name="name" value="jack"/>
<property name="age" value="19"/>
</bean>
</beans>
測試方式:(xml形式 需要使用ClassPathXmlApplicationContext)
ApplicationContext appxml = new ClassPathXmlApplicationContext("beans.xml");
Person person = appxml.getBean("person", Person.class);
System.out.println(person.toString());
2、通過註解的形式加載bean
註解1:@Configuration 作用於類,被這個註解標註的類可以理解成beans.xml
註解2:@Bean 作用於方法,主要作用往IOC容器中註冊一個Bean, 值是id 返回值是Bean的類型
@Configuration
public class MainConfig {
@Bean("springPerson")
public Person getPerson(){
return new Person("spring",17);
}
}
測試方式:
ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
Person springPerson = (Person) app.getBean("springPerson");
System.out.println(springPerson.toString());
String[] beanNamesForType = app.getBeanNamesForType(Person.class);
for (String name : beanNamesForType) {
System.out.println(name);
}
作爲一個小白,第一次看到這樣的操作註解,並不知道這裏有什麼作用。
Spring Bean和Configuration 升級
1、ComponentScan 定義掃描組件規則
通過註解我們可以定義配置類也就是Configuration可以掃描到那些類。以MVC爲例我一般會定義 Controller、Service、Dao。 所以我們會定義很多這些相關的類,但是並不是所有類都需要加載到IOC容器。所謂Component 其實Controller、Service、Dao(Repository) 都是Component沒有本質上的區別,就是一個類,註解被標記的時候,表明它的作用,起到一個提示作用。
如下目錄結構:
-test2
-controller
OrderControlller.java
-dao
OrderDao.java
-service
OrderService.java
@Controller
public class OrderControlller {
}
@Repository
public class OrderDao {
}
@Service
public class OrderService {
}
接下來定義配置類
@Configuration
@ComponentScan(value = {"lsn01.cap02"}, includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
public class Cap01MainConfig {
}
定義測試方式:
@Test
public void testComponentScan(){
ApplicationContext app = new AnnotationConfigApplicationContext(Cap02MainConfig.class);
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = "+beanDefinitionName);
}
}
ComponentScan註解主要屬性,
1、value 配置掃碼的包路徑String[] value() default {}
2、includeFilters 包含哪些Filter。 可以通過定義type 和classs 指定哪些可以被掃描到。
如:@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
打印結果:
beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = cap02MainConfig
beanDefinitionName = orderControlller
beanDefinitionName = orderDao
beanDefinitionName = orderService
顯然並沒有起到一個過濾效果,依然把所有的組件全部掃描進來,而我們需要的只是包含Controller
註解的類。
原因在於 useDefaultFilters = false 設置,關閉到默認的Filters
beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = cap02MainConfig
beanDefinitionName = orderControlller
3、excludeFilters 不包含哪些Filters 注意:如果使用這個註解就不需要將設置成true 否則將不會掃描任何組件
如:
excludeFilters =
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class}),
useDefaultFilters = false
打印結果:
beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = cap02MainConfig
4、自定義Filters
public class CustomFilter implements TypeFilter {
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
//當前掃描類的註解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
for (String annotationType : annotationTypes) {
System.out.println("annotationType : "+annotationType);
}
//當前掃描類的類的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
System.out.println("classMetadata className = "+className);
//當前掃描類的資源
Resource resource = metadataReader.getResource();
String filename = resource.getFilename();
System.out.println("filename = "+filename);
//返回true就是包含,false就是不包含
return false;
}
}
相關的自定義可以根據上面的三種信息定義屬於的結果邏輯返回。
打印結果
classMetadata className = lsn01.cap02.config.CustomFilter
filename = CustomFilter.class
annotationType : org.springframework.stereotype.Controller
classMetadata className = lsn01.cap02.controller.OrderControlller
filename = OrderControlller.class
annotationType : org.springframework.stereotype.Repository
classMetadata className = lsn01.cap02.dao.OrderDao
filename = OrderDao.class
annotationType : org.springframework.stereotype.Service
classMetadata className = lsn01.cap02.service.OrderService
filename = OrderService.class
beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = cap02MainConfig
1、從打印結果上看我們可以看到 最先掃描的是CustomFilter
2、FilterType.ANNOTATION:按照註解
FilterType.ASSIGNABLE_TYPE:按照給定的類型;比如按BookService類型
FilterType.ASPECTJ:使用ASPECTJ表達式
FilterType.REGEX:使用正則指定
FilterType.CUSTOM:使用自定義規則,自已寫類,實現TypeFilter接口>
2、scope 定義掃描規則
scope 主要作用是定義Bean在IOC容器中的創建規則
prototype: 多實例:IOC容器啓動並不會去調用方法創建對象放在容器中,而是每次獲取的時候纔會調用方法創建對象
singleton: 單實例(默認),IOC容器啓動會調用方法創建對象放到IOC容器中以後每交獲取就是直接從容器(理解成從map.get對象)中拿
request: 主要針對WEB應用,同一次請求創建一個實例
session: 同一個session創建一個實例
@Configuration
public class Cap02MainConfig {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public Person person(){
System.out.println("創建了一個person對象");
return new Person();
}
}
@Test
public void testScope(){
ApplicationContext app = new AnnotationConfigApplicationContext(Cap02MainConfig.class);
System.out.println("IOC容器初始化完成了");
Person person1 = app.getBean("person", Person.class);
Person person2 = app.getBean("person", Person.class);
System.out.println("person1 == person2 is "+(person2 == person1));
}
打印結果:
如果不加,@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
創建了一個person對象
IOC容器初始化完成了
person1 == person2 is true
加@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
IOC容器初始化完成了
創建了一個person對象
創建了一個person對象
person1 == person2 is false
得出結論默認情況在AnnotationConfigApplicationContext創建的時候,Spring自動就完成了所有相關bean的創建,並且是單例類;如果加入Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 是多實例類,並且不是隨着ApplicationContext的創建而創建。
3、lazy懶加載
@Configuration
public class Cap02MainConfig {
//@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
@Bean
public Person person(){
System.out.println("創建了一個person對象");
return new Person();
}
}
@Test
public void testScope(){
ApplicationContext app = new AnnotationConfigApplicationContext(Cap02MainConfig.class);
System.out.println("IOC容器初始化完成了");
Person person1 = app.getBean("person", Person.class);
Person person2 = app.getBean("person", Person.class);
System.out.println("person1 == person2 is "+(person2 == person1));
}
打印結果:
IOC容器初始化完成了
創建了一個person對象
person1 == person2 is true