目录
- 1使用@Bean注解
- .2 使用@ComponentScan包扫描配合@Component等注解
- 3.使用@Import注解
- 4. 使用Spring提供的FactoryBean
- 5. 注册Bean时的一些有用注解
1使用@Bean注解
当要向容器注册的Bean来自第三方包时可以采用这种方式。
创建一个配置类,并在类中写一个返回指定bean对象的方法,在该方法上标注@Bean注解
@Configuration //声明这是一个配置类
public class MainConfig1 {
@Bean(value = "myPerson") //value指定bean的id
public Person person(){ //bean的默认id为方法名
return new Person("rainboy",20);
}
}
bean的id默认为方法名,也可以通过@Bean注解的value属性进行指定
- 测试代码
@Test
public void test3(){
//指定配置类为MainConfig1
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig1.class);
String[] names = context.getBeanDefinitionNames();
//打印容器中所有bena的name
for(String name:names){
System.out.println(name);
}
}
- 测试结果
.2 使用@ComponentScan包扫描配合@Component等注解
和原来将包扫描信息定义在xml文件中不同,注解驱动开发时只需要在配置类上加上@ComponentScan注解,该注解有以下几个重要的属性
- 指定要扫描的包及其子包
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
由于是value属性,故实际使用时属性名可以省略。
- 指定过滤掉哪些Bean(即这些bean不向容器注册)
/**
* Specifies which types are not eligible for component scanning.
* @see #resourcePattern
*/
Filter[] excludeFilters() default {};
其中Filter是定义在注解@ComponentScan内部的注解,它有以下几个重要属性
/**
* The type of filter to use.
* <p>Default is {@link FilterType#ANNOTATION}.
* @see #classes
* @see #pattern
*/
FilterType type() default FilterType.ANNOTATION;
@AliasFor("value")
Class<?>[] classes() default {};
type返回一个FilterType枚举,指定过滤规则,默认是按注解类型过滤。classes是指定过滤规则后要过滤的类型。举个例子,如果过滤规则是按注解类型过滤,classes值设置为{Controller.class,Service.class},则所有标注以上两个注解的Bean都不会被容器注册。其他过滤规则还有
- FilterType.ASSIGNABLE_TYPE:按类过滤
- FilterType.ASPECTJ:按ASPECTJ表达式过滤
- FilterType.REGEX:按正则表达式:过滤
- FilterType.CUSTOM:按自定义类型过滤
- 指定要进行注册的bean
/**
* Specifies which types are eligible for component scanning.
* <p>Further narrows the set of candidate components from everything in {@link #basePackages}
* to everything in the base packages that matches the given filter or filters.
* <p>Note that these filters will be applied in addition to the default filters, if specified.
* Any type under the specified base packages which matches a given filter will be included,
* even if it does not match the default filters (i.e. is not annotated with {@code @Component}).
* @see #resourcePattern()
* @see #useDefaultFilters()
*/
Filter[] includeFilters() default {};
这个注解生效的前提是要将默认的过滤规则关闭,即将该属性设置为false
boolean useDefaultFilters() default true;
2.1 一个在包扫描时过滤指定的Bean的例子
分别在子包下创建BookController,BookService,BookDao
如果想要容器启动时不注册@Controller和@Service注解的Bean,可以进行如下设置
@Configuration
//扫描com.rainboy包及其子包,并过滤掉标注有@Controller和@Service注解的类
@ComponentScan(value = "com.rainboy", excludeFilters =
{@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class,Service.class})}
)
public class MainConfig2 {
}
- 测试代码
@Test
public void test4(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] names = context.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
- 运行结果如下
2.2 按自定义规则过滤指定的Bean
在2.1的基础上将以Dao结尾的Bean过滤掉,首先自定义过滤类
- 自定义过滤类
该类实现TypeFilter接口,并重写match方法,注意结果返回true表示要过滤:不向容器注册。返回false表示不过滤。
/**
* 自定义过滤器,在包扫描时忽略指定的Bean
*/
public class MyTypeFilter 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 className = classMetadata.getClassName();
if (className.endsWith("Dao")) {
return true; //以Dao结尾的Bean都过滤掉,这里返回true表示要过滤
}
return false;
}
}
修改配置类的ComponentScan参数
@Configuration
//扫描com.rainboy包及其子包,并过滤掉标注有@Controller和@Service注解的类
@ComponentScan(value = "com.rainboy", excludeFilters =
{@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class,Service.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
}
)
public class MainConfig2 {
}
最后测试结果如下
3.使用@Import注解
3.1 @Import的简单用法
使用@Import注解可以方便快速的向容器注册Bean。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
该注解只能加在类上。
- 配置类
@Configuration
@Import(Person.class) //注册Person
public class MainConfig4 {
}
- 测试代码
@Test
public void test6(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig4.class);
String[] names = context.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
- 测试结果
可以看到Person以及被注册到了容器中,且id默认为类的全类名。
3.2 @Import高级用法@ImportSelector
ImportSelector是一个接口,需要实现该接口并重写方法,返回需要注册的类的全类名。
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
- 自定义ImportSelector
public class MyImportSelector implements ImportSelector {
/**
*
* @param importingClassMetadata 标注有ImportSelector注解的类上的所有注解信息
* @return
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
"com.rianboy.bean.Student","com.rianboy.bean.User"
};
}
}
- 配置类
@Configuration
@Import({Person.class,MyImportSelector.class})
public class MainConfig4 {
}
- 测试运行结果
4. 使用Spring提供的FactoryBean
- 自定义FactoryBean
public class UserFactoryBean implements FactoryBean<User> {
//真正想容器注册的Bean
public User getObject() throws Exception {
System.out.println("UserFactoryBean....");
return new User("rainboy", 20);
}
public Class<?> getObjectType() {
return User.class;
}
public boolean isSingleton() {
return true;
}
}
- 配置类
@Configuration
@Import({Person.class, MyImportSelector.class})
public class MainConfig4 {
/**
* 向容器注册UserFactoryBean
* @return
*/
@Bean("user") //id为user
public UserFactoryBean userFactoryBean() {
return new UserFactoryBean();
}
}
- 测试代码
@Test
public void test6(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig4.class);
String[] names = context.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
System.out.println("----------");
Object user = context.getBean("user");
System.out.println(user.getClass());
}
- 测试结果
可以看到,虽然在配置类中我们创建了一个UserFactoryBean,但是实际向容器注册,并且能从容器中取出的对象是User,也就是在我们从容器中获取id为"user"的bean时,UserFactoryBean的getObject()方法会被调用。
如果想获取UserFactoryBean本身,则可以在获取时指定id为“&user”。
context.getBean("&user")
5. 注册Bean时的一些有用注解
5.1. Bean的作用域@Scope及懒加载@Lazy
通过@Scope("prototype")指定,Bean的作用域主要有以下几种
- singleton:单实例,ioc容器启动的时候就会创建单实例的bean并缓存起来,以后每次获取都是从缓存中拿。默认配置。
- prototype:多实例,ioc容器启动时不会创建对象,在获取的时候才创建,并且每次获取都是创建一个新的对象。
- request:同一次请求创建一个实例
- session:同一个session创建一个实例
对于单实例bean,默认容器启动就会创建Bean。也可以通过@Lazy注解将Bean的创建过程延迟到第一次获取Bean时
@Lazy
@Bean(value = "myPerson") //value指定bean的id
public Person person(){ //bean的默认id为方法名
return new Person("rainboy",20);
}
5.2. 条件注册@Conditional
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
该注解可以让Bean在满足一定条件后才向Spring容器注册
根据操作系统的类型注册Bean
- 配置类
@Configuration
public class MainConfig3 {
@Conditional(MacCondition.class) //只有操作系统为mac时该bean才被注册
@Bean("mac")
public Person person1() {
return new Person("mac", 20);
}
@Conditional(WindowCondition.class) //只有操作系统为windows时该bean才被注册
@Bean("windows")
public Person person2() {
return new Person("windows", 20);
}
}
该注解需要自定义实现Condition接口的类,并重写其匹配逻辑。
- Mac系统的匹配逻辑
/**
* 判断操作系统是否是mac系统
*/
public class MacCondition implements Condition {
/**
* 当操作系统时mac系统时,匹配成功,该注解标注的bean才能注册成功
* @param context 判断条件能使用的上下文环境
* @param metadata 标注了Condition注解的注释信息
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
return environment.getProperty("os.name").toLowerCase().contains("mac");
}
}
- Windows系统的匹配逻辑
/**
* 判断操作系统是否是windows系统
*/
public class WindowCondition implements Condition {
/**
* 当操作系统是windows系统时,匹配成功,该注解标注的bean才会被容器注册
* @param context
* @param metadata
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
return environment.getProperty("os.name").toLowerCase().contains("windows");
}
}
- 测试代码
@Test
public void test5() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
String[] names = context.getBeanNamesForType(Person.class);
for (String name : names) {
System.out.println(name);
}
}
- 运行结果
由于运行程序的机器为mbp,故而只有name为mac的bean会被注册进ioc容器中。