使用XML檔案配置包掃描
我們可以在Spring的XML配置檔案中配置包的掃描,在配置包掃描時,需要在Spring的XML檔案中的beans節點中引入context标簽,如下所示。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/context/spring-context.xsd ">
接下來,我們就可以在XML檔案中定義要掃描的包了,如下所示。
<context:component-scan base-package="io.mykit.spring"/>
整個beans.xml檔案如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context.xsd">
<context:component-scan base-package="io.mykit.spring"/>
<bean id = "person" class="io.mykit.spring.bean.Person">
<property name="name" value="binghe"></property>
<property name="age" value="18"></property>
</bean>
</beans>
此時,隻要在io.mykit.spring包下,或者io.mykit.spring的子包下标注了@Repository、@Service、@Controller、@Component注解的類都會被掃描到,并自動注入到Spring容器中。
此時,我們分别建立PersonDao、PersonService、和PersonController類,并在這三個類中分别添加@Repository、@Service、@Controller注解,如下所示。
- PersonDao
package io.mykit.spring.plugins.register.dao;
import org.springframework.stereotype.Repository;
/**
* @author binghe
* @version 1.0.0
* @description 測試的dao
*/
@Repository
public class PersonDao {
}
- PersonService
package io.mykit.spring.plugins.register.service;
import org.springframework.stereotype.Service;
/**
* @author binghe
* @version 1.0.0
* @description 測試的Service
*/
@Service
public class PersonService {
}
- PersonController
package io.mykit.spring.plugins.register.controller;
import org.springframework.stereotype.Controller;
/**
* @author binghe
* @version 1.0.0
* @description 測試的controller
*/
@Controller
public class PersonController {
}
接下來,我們在SpringBeanTest類中建立一個測試方法testComponentScanByXml()進行測試,如下所示。
@Test
public void testComponentScanByXml(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
}
運作測試用例,輸出的結果資訊如下所示。
personConfig
personController
personDao
personService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
person
可以看到,除了輸出我們自己建立的bean名稱之外,也輸出了Spring内部使用的一些重要的bean名稱。
接下來,我們使用注解來完成這些功能。
使用注解配置包掃描
使用@ComponentScan注解之前我們先将beans.xml檔案中的下述配置注釋。
<context:component-scan base-package="io.mykit.spring"></context:component-scan>
注釋後如下所示。
<!--<context:component-scan base-package="io.mykit.spring"></context:component-scan>-->
使用@ComponentScan注解配置包掃描就非常Easy了!在我們的PersonConfig類上添加@ComponentScan注解,并将掃描的包指定為io.mykit.spring即可,整個的PersonConfig類如下所示。
package io.mykit.spring.plugins.register.config;
import io.mykit.spring.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author binghe
* @version 1.0.0
* @description 以注解的形式來配置Person
*/
@Configuration
@ComponentScan(value = "io.mykit.spring")
public class PersonConfig {
@Bean("person")
public Person person01(){
return new Person("binghe001", 18);
}
}
沒錯,就是這麼簡單,隻需要在類上添加@ComponentScan(value = "io.mykit.spring")注解即可。
接下來,我們在SpringBeanTest類中新增testComponentScanByAnnotation()方法,如下所示。
@Test
public void testComponentScanByAnnotation(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
}
運作testComponentScanByAnnotation()方法輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
personController
personDao
personService
person
可以看到使用@ComponentScan注解同樣輸出了bean的名稱。
既然使用XML檔案和注解的方式都能夠将相應的類注入到Spring容器當中,那我們是使用XML檔案還是使用注解呢?我更傾向于使用注解,如果你确實喜歡使用XML檔案進行配置,也可以,哈哈,個人喜好嘛!好了,我們繼續。
關于@ComponentScan注解
我們點開ComponentScan注解類,如下所示。
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.type.filter.TypeFilter;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
這裡,我們着重來看ComponentScan類的兩個方法,如下所示。
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
includeFilters()方法表示Spring掃描的時候,隻包含哪些注解,而excludeFilters()方法表示不包含哪些注解。兩個方法的傳回值都是Filter[]數組,在ComponentScan注解類的内部存在Filter注解類,大家可以看下上面的代碼。
1.掃描時排除注解标注的類
例如,我們現在排除@Controller、@Service和@Repository注解,我們可以在PersonConfig類上通過@ComponentScan注解的excludeFilters()實作。例如,我們在PersonConfig類上添加了如下的注解。
@ComponentScan(value = "io.mykit.spring", excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})
這樣,我們就使得Spring在掃描包的時候排除了使用@Controller、@Service和@Repository注解标注的類。運作SpringBeanTest類中的testComponentScanByAnnotation()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
person
可以看到,輸出的結果資訊中不再輸出personController、personService和personDao說明Spring在進行包掃描時,忽略了@Controller、@Service和@Repository注解标注的類。
2.掃描時隻包含注解标注的類
我們也可以使用ComponentScan注解類的includeFilters()來指定Spring在進行包掃描時,隻包含哪些注解标注的類。
這裡需要注意的是,當我們使用includeFilters()來指定隻包含哪些注解标注的類時,需要禁用預設的過濾規則。
例如,我們需要Spring在掃描時,隻包含@Controller注解标注的類,可以在PersonConfig類上添加@ComponentScan注解,設定隻包含@Controller注解标注的類,并禁用預設的過濾規則,如下所示。
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
此時,我們再次運作SpringBeanTest類的testComponentScanByAnnotation()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
personController
person
可以看到,在輸出的結果中,隻包含了@Controller注解标注的元件名稱,并沒有輸出@Service和@Repository注解标注的元件名稱。
注意:在使用includeFilters()來指定隻包含哪些注解标注的類時,結果資訊中會一同輸出Spring内部的元件名稱。
3.重複注解
不知道小夥伴們有沒有注意到ComponentScan注解類上有一個如下所示的注解。
@Repeatable(ComponentScans.class)
我們先來看看@ComponentScans注解是個啥,如下所示。
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {
ComponentScan[] value();
}
可以看到,在ComponentScans注解類中隻聲明了一個傳回ComponentScan[]數組的value(),說到這裡,大家是不是就明白了,沒錯,這在Java8中是一個重複注解。
對于Java8不熟悉的小夥伴,可以到【 Java8新特性 】專欄檢視關于Java8新特性的文章。專欄位址小夥伴們可以猛戳下面的連結位址進行檢視: https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz=Mzg3MzE1NTIzNA==&scene=1&album_id=1325066823947321344#wechat_redirect
在Java8中表示@ComponentScan注解是一個重複注解,可以在一個類上重複使用這個注解,如下所示。
@Configuration
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Service.class})
}, useDefaultFilters = false)
public class PersonConfig {
@Bean("person")
public Person person01(){
return new Person("binghe001", 18);
}
}
運作SpringBeanTest類的testComponentScanByAnnotation()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
personController
personService
person
可以看到,同時輸出了@Controller注解和@Service注解标注的元件名稱。
如果使用的是Java8之前的版本,我們就不能直接在類上寫多個@ComponentScan注解了。此時,我們可以在PersonConfig類上使用@ComponentScans注解,如下所示。
@ComponentScans(value = {
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false),
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Service.class})
}, useDefaultFilters = false)
})
再次運作SpringBeanTest類的testComponentScanByAnnotation()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
personController
personService
person
與使用多個@ComponentScan注解輸出的結果資訊相同。
總結:我們可以使用@ComponentScan注解來指定Spring掃描哪些包,可以使用excludeFilters()指定掃描時排除哪些元件,也可以使用includeFilters()指定掃描時隻包含哪些元件。當使用includeFilters()指定隻包含哪些元件時,需要禁用預設的過濾規則