我们知道spring配置的方式有三种:
- xml配置方式
- JavaConfig方式
- 隐式bean扫描,基于Java的注解,自动配置
本篇文章主要讲解的是通过JavaConfig的方式进行spring配置中的组件注册的内容学习(该篇内容仅是个人在学习spring的过程中的笔记,如果有误希望指出^_^)
一、容器注册组件基础
通过xml的形式进行配置时,我们会通过如下方式来进行spring bean的配置:
<?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/spring-context.xsd">
<bean id="person" class="com.xyr.model.Person">
<property name="age" value="18"></property>
<property name="name" value="xyr"></property>
</bean>
</beans>而通过JavaConfig的方式,最基础的配置如下:
//配置类==配置文件
@Configuration //告诉spring这是一个配置类
public class SpringConfig {
/**
* 注册一个bean,类型为返回值的类型,默认方法名为id
* 也可以在bean注解之后指定
*/
@Bean
public Person person() {
return new Person(20, "xyr");
}
}其中的@Configuration标明了该类为一个配置文件,@Bean标明了需要注册的bean,bean的内容为下面方法对应的返回值,默认情况下,方法名即为bean的id。即:
public class TestBean {
public static void main(String[] args) {
//通过注解的方式获取对应的bean
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
}
}得到的结果为:
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
springConfig
person
springConfig为配置类也属于bean对象,person即为定义的bean名称
当然我们也可以通过bean的name属性为其赋予名称。bean注解的基本构成如下:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
/** @deprecated */
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}简单了解如下成员变量:
value: name属性的别名,在不需要其他属性时使用,也就是说value 就是默认值。
name: bean 的名称,或多个名称,主要的bean的名称加别名。如果未指定,则bean的名称是带注解方法的名称。如果指定了,方法的名称就会忽略,如果没有其他属性声明的话,bean的名称和别名可能通过value属性配置。
autowire : 此注解的方法表示自动装配的类型,返回一个
使用举栗:
//配置类==配置文件
@Configuration //告诉spring这是一个配置类
public class SpringConfig {
/**
* 注册一个bean,类型为返回值的类型,默认方法名为id
* 也可以在bean注解之后指定
*/
@Bean(value = "person")
public Person person01() {
return new Person(20, "xyr");
}
}public class TestBean {
public static void main(String[] args) {
//通过注解的方式获取对应的bean
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
}
}得出结果:
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
springConfig
person
虽然方法名被设置为
二、ComponentScan包扫描
和xml的配置方式中的包扫描一样
<!-- 表明要扫描的包 , 只要标注了@Controller、@Service、@Repository、@Component等spring对应的注解,且在对应的扫描包下就会被注入到bean容器当中 -->
<context:component-scan base-package="com.xyr" ></context:component-scan>我们也可以通过JavaConfig中的注解@ComponentScan来注明要扫描的包,从而对包含@Controller、@Service、@Repository、@Component等spring对应的注解的类进行bean注入。如下:
//配置类==配置文件
@Configuration //告诉spring这是一个配置类
//通过注解ComponentScan的方式指定扫描的包
@ComponentScan(basePackages = "com.xyr")
public class SpringConfig {
/**
* 注册一个bean,类型为返回值的类型,默认方法名为id
* 也可以在bean注解之后指定
* @return
*/
@Bean(value = "person")
public Person person01() {
return new Person(20, "xyr");
}
}配置完成之后,运行之前TestBean的main方法,可输出如下结果:
带有spring相关bean注解的对象都进行了扫描注入。
点开ComponentScan注解类,可以看到如下内容:
@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 "**/*.class";
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}在ComponentScan类中有两个属性:
- includeFilter:指明只扫描某些内容
- excludeFilter:指明不包含某些内容
其中过滤的方式可以有:
public enum FilterType {
//根据注解过滤
ANNOTATION,
//根据指定类型
ASSIGNABLE_TYPE,
//使用ASPECTJ表达式
ASPECTJ,
//使用正则表达式
REGEX,
//使用自定义规则
CUSTOM;
private FilterType() {
}
}那么我们就根据includeFilter来举两个例子:
1.根据注解过滤
//配置类==配置文件
@Configuration //告诉spring这是一个配置类
//includeFilters 指定只扫描的对应bean,useDefaultFilters=false不使用默认的过滤方式
@ComponentScan(basePackages = "com.xyr", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)
}, useDefaultFilters = false)
public class SpringConfig {
/**
* 注册一个bean,类型为返回值的类型,默认方法名为id
* 也可以在bean注解之后指定
*/
@Bean(value = "person")
public Person person01() {
return new Person(20, "xyr");
}
}上述代码标明了要扫描的包,以及只额外注入具有Controller注解的bean(当然配置类中的bean肯定是会注入的)。
此时再去运行TestBean代码,可得到如下结果:
虽然还有其他spring bean注解的类,当此时只有Controller对应的bean是被额外扫描注入的
2.根据自定义规则过滤
定义一个实现了TypeFilter接口的过滤类,实现只有bean名称包含er字符串的符合过滤条件:
public class MyFilter implements TypeFilter {
/**
* 匹配规则
* @param metadataReader 读取到当前正在扫描的类
* @param metadataReaderFactory 获取到当前其他的类信息
* @return
* @throws IOException
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取到当前的类注解
//AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取到当前的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取到当前的类资源(类路径)
//Resource resource = metadataReader.getResource();
String name = classMetadata.getClassName();
//打印名称
System.out.println(name);
if (name.contains("er")) {
//如果类名包含er字符串,则注入
return true;
}
return false;
}
}在配置类中加上ComponentScan,注明过滤方式用自定义的MyFilter类:
//配置类==配置文件
@Configuration //告诉spring这是一个配置类
@ComponentScan(basePackages = "com.xyr", includeFilters = {
//通过自定义的过滤器,过滤需要的bean
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyFilter.class)
}, useDefaultFilters = false)
public class SpringConfig {
/**
* 注册一个bean,类型为返回值的类型,默认方法名为id
* 也可以在bean注解之后指定
*/
@Bean(value = "person")
public Person person01() {
return new Person(20, "xyr");
}
}那么此时运行TestBean的main方法:
//myFilter执行match方法时获取到的类名称
com.xyr.configure.SpringLifeConfig
com.xyr.controller.PersonController
com.xyr.dao.PersonDao
com.xyr.model.Person
com.xyr.service.PersonService
com.xyr.test.TestBean
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
springConfig
personController
personDao
myFilter
person
personService
另外spring关于包扫描注解,还有一个ComponentScans用于多个扫描注解的组合使用。