Spring注解驱动开发系列:
组件注册
@Configuration
声明这是一个配置类
在配置类中可以使用@Bean修饰方法,将该组件加入容器中。组件类型为返回值类型,组件id为方法名。
@Configuration
public class SpringConfig {
// 将组件加入容器中,组件类型为People,组件为People("ming",null,null),组件名称默认为为people1,但是@Bean注解中设置了为people
@Bean("people")
public People people1(){
return new People("ming",null,null);
}
}
@ComponentScan
开启组件扫描,将扫描范围中用@Component、@Repository、@Service、@Comtroller标注的类生成实例加入容器中。
@Repository、@Service、@Comtroller都继承@Component
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class) //继承了Component注解,@Service、@Comtroller也一样有这行代码
String value() default "";
}
@ComponentScan使用
@ComponentScan(
basePackages = {"south.block"},
includeFilters ={
@ComponentScan.Filter(type = FilterType.ANNOTATION,
value = { Service.class, Repository.class })
},
useDefaultFilters = false,
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
value = UserService.class )
}
)
@ComponentScan( basePackages = {“south.block”} ) 或者@ComponentScan( value = {“south.block”} ) 指定要扫描的包。
includeFilters 是一个 Filter[] ,表示包括的类型,可以使用注解类型、指定的类型、正则表达式、AspectJ表达式、自定义类等。但是,默认会开启一个默认的扫描所有的被@Component、@Repository、@Service、@Comtroller标注的类的过滤器,因此我们通常需要使用useDefaultFilters = false关闭
excludeFilters 也是一个 Filter[] ,表示排除的类型,同上
在设置包括或排除过滤器时,我们如果使用根据注解类型排除需要注意@Component与@Repository、@Service、@Comtroller的包括关系
自定义TypeFilter
上面代码中,我们的排除过滤器规则是自定义实现TypeFilter接口的一个类
我们可以获取类的元信息,自定义匹配规则,包含或排除一些组件
public class scanFilter implements TypeFilter {
/**
* 判断是否匹配。
* @param metadataReader 元信息阅读器,可以获取类的相关信息
* @param metadataReaderFactory 元信息阅读器工厂(比如父类或接口等),可以获取任何类的元信息阅读器
* @return 返回真,则表示符合过滤器,否则不符合
* @throws IOException
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// resource对象记录着一些文件存储上的信息
Resource resource = metadataReader.getResource();
// annotationMetadata 记录着一些类的注解相关的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// classMetadata 记录着关于类的一些信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
boolean isEr = className.contains("er");
return isEr;
}
}
@Scope
使用@Scope注解来表示组件的作用域
基础的有两种,singleton和prototype,分别表示单例和多例
Web环境中,还有request和session,分别表示每个请求对应一个和每个session对应一个
单例默认是在容器创建的时候直接创建
多例则默认不会
@Lazy()
我们使用@Lazy()来设置组件是否懒加载,默认true
@Lazy(false)则表示非懒加载,也就是容器创建时就生成对象
@Lazy(true)则表示懒加载,只有在用时才会去创建对象
@Conditional
可以修饰类或方法,修饰类表示条件满足时,才会将这个类中的组件加入容器。修饰方法表示条件满足时,才会将方法表示的组件加入容器
@Conditional()其中传入一个Condition实现类的Class对象集合
如下,表示window系统创建bill对象,linux系统创建linux对象
@Conditional({ WinCondition.class })
@Bean
public People linux(){
return new People("linux",null,null);
}
@Conditional({ Linux.class })
@Bean
public People bill(){
return new People("bill",null,null);
}
其中,WinCondition类如下
public class WinCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// Bean注册中心
BeanDefinitionRegistry registry = context.getRegistry();
// 资源加载器
ResourceLoader resourceLoader = context.getResourceLoader();
// 类加载器
ClassLoader classLoader = context.getClassLoader();
// bean工厂(容器)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 环境信息
Environment environment = context.getEnvironment();
String os = environment.getProperty("os.name");
if (os.contains("Windows")){
return true;
}
return false;
}
}
LinuxCondition类似省略
@Import
加在类上,将组件快速导入容器中
- 可以加类的Class对象,如:@Import(Cat.class)表示将Cat类加入容器中,组件名为类的全路径名
- 可以加一个ImportSelector的实现类,返回需要添加组件的全类名
- 可以加一个ImportBeanDefinitionRegistrar的实现类,直接在其方法中注册组件
使用@Import导入
@Import({Cat.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
ImportSelector实现类
public class MyImportSelector implements ImportSelector {
/**
* 选择要导入的Bean
* @param importingClassMetadata 当前标注@Import注解的类的注解元信息
* @return 要注册的组件的全路径
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"south.block.pojo.User"};
}
}
ImportBeanDefinitionRegistrar实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 判断是否含有people2组件
boolean existPeople2 = registry.containsBeanDefinition("people2");
if (existPeople2) {
// 设置好组件的类信息
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class);
// 注册并且为组件命名为user
registry.registerBeanDefinition("userRegistrar",rootBeanDefinition);
}
}
}
FactoryBean
其实更像一个工厂,用于生产某种Bean
自定义类实现FactoryBean接口即可
public class ColorFactory implements FactoryBean<Color> {
public Color getObject() throws Exception {
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
// 重写的默认方法,其余为实现
public boolean isSingleton() {
return true;
}
}
从容器中获取ColorFactory实例时,会去调用getObject()方法获取Color实例对象。当然如果是单例模式,则并不会每次都去调用getObject()方法
如果我们想要从容器中获取ColorFactory对象时,可以在bean名称前加一个&,例如想要获取ColorFactory对象,Object bean = context.getBean("&colorFactory");