Spring註解驅動開發——指定配置類和註冊Bean
1、通過註解指定配置類和註冊Bean
@Configuration
public class MainConfig {
@Bean
public Person person(){
return new Person("lisi",12);
}
}
@Configuration註解可以不加,建立ApplicationContext物件時傳入的類即可被認定為容器配置類,Spring並不會自動掃描註解有@Configuration的類並將其視為容器類,@Bean註解的方法的返回值即為註冊的Bean,Bean的id預設為方法名,id可通過@Bean註解的屬性指定
public class MainTest { public static void main(String args[]){ // ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); // Person p = (Person) context.getBean("person"); // System.out.print(p); ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); Person person = context.getBean(Person.class); System.out.print(person); } }
ClassPathXmlApplicationContext和AnnotationConfigApplicationContext均為ApplicationContext的實現類,前者用來載入spring的xml配置檔案,後者用來載入spring的配置類,載入完spring的xml配置檔案或配置類後spring容器就會建立物件,這兩種方式會有些不同:使用xml配置的方式會預設呼叫指定Class的無參構造器,也可以制定構造器,屬性的值是通過Setter方法注入的;而使用配置類時則會呼叫配置類中所有@Bean註解的方法,呼叫的構造器是在該方法中指明的。
2、通過註解指定容器類的掃描範圍和掃描規則
@Configuration
@ComponentScan(value = "com.bdm", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
@Configuration
@ComponentScan(value = "com.bdm", useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
@ComponentScan註解的value屬性指定了掃描的包,表示此包中使用了@Controller、@Service、@Repository、@Component等註解的類會被納入Spring容器的管理;excludeFilters屬性表示該包中哪些類別的類不會被納入Spring容器管理,它的屬性值是一個數組;includeFilters屬性表示指定的包中哪些類別的類會被納入到Spring容器管理,注意要同時設定useDefaultFilters的屬性為false。
JDK1.8之後一個容器類可以使用多次@ComponentScan註解,JDK1.8之前則可以使用@ComponentScans實現這個需求
@Configuration
@ComponentScans(value = {@ComponentScan(value = "com.bdm", useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}), @ComponentScan(value = "com.bdm", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
如果通過@ComponentScan設定的包有包含關係時,同一個被納入Spring容器的Class會建立多少個bean例項呢?答案是1個,因為通過包掃描的方式建立的bean例項的id是類名的首字母小寫,spring在建立bean時會先判斷該id的bean有無建立,已建立則不再建立,否則才建立;那如果除了包掃描中有該類,同時@Bean註解的方法中也有該Class的例項,這個時候會建立幾個bean例項呢?這個要看@Bean註解的方法的方法名或者通過@Bean的name屬性為該例項指定的id是否和類名的首字母小寫後相同,相同則僅建立一個,否則建立多個,說明spring容器在建立bean時會先檢查該id的bean是否已經存在,不存在則建立,否則不建立。
FilterType的值:
FilterType.ANNOTATION:指定註解註解的類
FilterType.ASSIGNABLE_TYPE:指定型別的類
FilterType.ASPECTJ:使用AspectJ表示式過濾
FilterType.REGEX:使用正則表示式過濾
FilterType.CUSTOM:自定義過濾規則
3、自定義過濾規則
自定義規則類需實現TypeFilter介面:
public class BdmTypeFilter implements TypeFilter{
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//註解
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
//路徑
Resource resource = metadataReader.getResource();
//類名
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
if(className.contains("action"))
return true;
MetadataReader metadataReader1 = metadataReaderFactory.getMetadataReader("");
return false;
}
}
MetadataReader:獲取正被掃描的類的資訊,包括該類的註解資訊、路徑資訊、類名資訊等
MetadataReaderFactory:獲取MetadataReader
使用自定義過濾規則:type=FilterType.CUSTOM
@Configuration
@ComponentScan(value="com.bdm",useDefaultFilters = false,includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BdmTypeFilter.class})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
type = FilterType.CUSTOM,classes = {BdmTypeFilter.class})})
public class MainConfig {
@Bean
public Person person() {
return new Person("lisi", 12);
}
}
則在指定包com.bdm及其子包下所有符合自定義規則(此處為類名中含有action字串)的類將被納入Spring容器管理
由此可以看出Spring容器在通過掃描的方式決定將哪些類納入容器管理前會先掃描指定包下所有的類,再看這些類是否符合被納入管理的條件(有沒有指定註解、類名是否符合條件、路徑是否符合條件等),所以如果在一個包下需要被Spring容器管理的類所佔的比例比較小時最好不要使用掃描包的方式,而使用@Bean註解的方式效能會更好些,因為減少了遍歷