一、包扫描@ComponentScan
@ComponentScan
只会扫描有以下四种注解中某一注解的类,并管理他们把他们加载到上下文中
@Component
:基本注解,标识了一个受Spring管理的组件,实际上只会扫描@Component,只是其他3个都继承于他
@Repository:
标识持久层组件
@Service
:标识服务层(业务层)组件
@Controller
:标识表现层组件
他们可以混用,因为Spring还没有聪明到能判断我们写的代码是那一层的,但最好那一层的注解,就用那一层,因为未来有可能有别的寓意
对于扫描到的组件,Spring有默认的命名策略:使用非限定类名,第一个字母小写(如UserService其默认id就是:userService)。也可以在注解中通过value属性值标识组件的名称
@ComponentScan的属性
//定义包路径
String[] value() default {}
String[] basePackages() default {}
//指定dean生成器,默认BeanNameGenerator
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
//处理与检测到的bean的scope范围,默认AnnotationScopeMetadataResolver
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class
//是否为检测到的组件生成代理
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
//控制符合组件检测条件的类文件 默认是包扫描下的 **/*.class
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
//是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的
boolean useDefaultFilters() default true;
//扫描到的类是都开启懒加载 ,默认是不开启的
boolean lazyInit() default false
//包扫描过滤与包括设置
Filter[] includeFilters() default {}
Filter[] excludeFilters() default {}
Filter信息说明
FilterType.ANNOTATION//注解类型 默认
FilterType.ASSIGNABLE_TYPE//指定固定类
FilterType.ASPECTJ//ASPECTJ类型
FilterType.REGEX//正则表达式
FilterType.CUSTOM//自定义类型
说明excludeFilters是过滤规则,是在基础规则上的进一步排除,includeFilters是包括,他和基础规则设置冲突,
基础规则会覆盖他,所以当我要使用includeFilters时应该使用useDefaultFilters=false
禁用基础规则或者不定义
案例
//只扫描hello包下标注注解@Controller的类
@ComponentScan(
value ={"hello”},
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)}
)
二、在组件中定义@Bean元数据
设置别名,如果未指定,则bean的名称是带注释的方法的名称。如果指定,则忽略方法名,一个bean可以设置多个别名
@Configuration
@EnableWebMvc
public class WebConfig{
@Bean
public Son son(){
return new Son();
}
}
如果一个类注册了多个bean,我可以使用@Qualifier点名要那个bean
三、@Import
说明:只能标注在类上,属性是
Class<?>[] value()
1、导入一个bean
@Import(Person.class)
@Configuration
public class App{}
public class Person {
}
其别名是“包名.类名”
2、导入@Configuration注解的配置类
@Import({TestConfig.class)
@Configuration
public class AppConfig {
}
@Configuration
public class TestConfig {
@Bean
public Person person(){
return new Person();
}
}
注意:这时候person的别名不是“包名.类名”,是和普通的一样,而TestConfig的别名是“包名.类名”
- 注意:如果主配置类AppConfig中如下
@Import({TestConfig.class)
@Configuration
public class AppConfig {
@Bean
public Person person(){
return new Person();
}
}
TestConfig中的person方法不会执行,而AppConfig中的会,除非AppConfig的没有
@Import({TestConfig.class)可以将配置类TestConfig中的bean全部导入到AppConfig中
延深:TestConfig可以不加@Configuration
因为@improt会将导入的class加上@component注解从而被spring容器管理,自然的class下@bean标注方法返回的实例也会被添加到spring容器中。但是@configuration和@component下的@bean是有点区别的。@configuration标注的类,其方法会被代理执行
3、导入ImportSelector的实现类
ImportSelector是Spring中导入外部配置的核心接口
public class MyImport implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"cn.dao.Color"}; }
}
@Configuration
@Import(MyImport.class)
public class CarFactory { }
导入的Color的别名是cn.dao.Color 别名是“包名.类名”,
4、导入DeferredImportSelector的实现类
public interface DeferredImportSelector extends ImportSelector {
default Class<? extends Group> getImportGroup() {
return null;
}
interface Group {
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
Iterable<Entry> selectImports();
}
5、导入ImportBeanDefinitionRegistrar的实现类
public class MyImport implements ImportBeanDefinitionRegistrar {
@Override //importingClassMetadata包含@import标注类的信息
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean sign = registry.containsBeanDefinition("car”);
if(!sign){
registry.registerBeanDefinition("color", new RootBeanDefinition(Color.class));
}
}
@Configuration
@Import(MyImport.class)
public class CarFactory {
}
四、@Conditional
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean,他可以标注在类和方法上
@Conditional中必须存放Condition类型
- Condition是个接口,需要实现matches方法,返回true则注入bean,false则不注入。
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
使用场景,当有2个bean,我们想根据一定条件注册,那么可以有这个注解
public class TrueCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
public class FalseCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
@Configuration
public class Appruner {
@Conditional({FalseCondition.class})
@Bean
public Persion persion1(){
return new Persion();
}
@Conditional({TrueCondition.class})
@Bean
public Persion persion2(){
return new Persion();
}
}
结论:
1、这种情况下只有别名是persion2会加载
2、如果@Conditional中有多个Condition实现类,只有当其全部为true时才会加载
五、FactoryBean
实现FactoryBean,然后把它注册到配置类中
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType”;
//返回的bean对象
@Nullable
T getObject() throws Exception;
//bean的类型
@Nullable
Class<?> getObjectType();
//是否是单列模式。默认是单列模式
default boolean isSingleton() {
return true;
}
}
案例
@Configuration
public class App{
@Bean
public Config config(){
return new Config();
}
}
public class Config implements FactoryBean<Persion> {
@Override
public Persion getObject() throws Exception {
return new Persion();
}
@Override
public Class<?> getObjectType() {
return Persion.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
测试
ApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(App.class);
System.out.println(annotationConfigApplicationContext.getBean("config").getClass());
//class hello.Persion
//如果我们想获取工厂对象,可以这样写
System.out.println(annotationConfigApplicationContext.getBean("&config").getClass());
//class hello.Config
思考;为什么要有FactoryBean,Persion类直接用@bean注册,不就行了吗,为什么非要多此一举多创建一个bean Config
答:FactoyBean主要是方便我们对附加bean的实例的注册。如果一个对象在实例化时要做很多事情,那么我们直接写在配置文件中就显得很臃肿,而FactoyBean就好像一个java的工具方法,我们直接调用就可以用