Spring注解驱动开发系列:

  1. Spring 组件注册
  2. Spring Bean的生命周期
  3. Spring属性赋值
  4. Spring自动转配
  5. Spring注解驱动开发之AOP

组件注册

@Configuration

声明这是一个配置类

在配置类中可以使用@Bean修饰方法,将该组件加入容器中。组件类型为返回值类型,组件id为方法名。

1
2
3
4
5
6
7
8
@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

1
2
3
4
5
6
7
8
9
10
11
12
13
@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使用

1
2
3
4
5
6
7
8
9
10
11
12
@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接口的一个类

我们可以获取类的元信息,自定义匹配规则,包含或排除一些组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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对象

1
2
3
4
5
6
7
8
9
10
11
@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类如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

加在类上,将组件快速导入容器中

  1. 可以加类的Class对象,如:@Import(Cat.class)表示将Cat类加入容器中,组件名为类的全路径名
  2. 可以加一个ImportSelector的实现类,返回需要添加组件的全类名
  3. 可以加一个ImportBeanDefinitionRegistrar的实现类,直接在其方法中注册组件

使用@Import导入

1
@Import({Cat.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})

ImportSelector实现类

1
2
3
4
5
6
7
8
9
10
public class MyImportSelector implements ImportSelector {
/**
* 选择要导入的Bean
* @param importingClassMetadata 当前标注@Import注解的类的注解元信息
* @return 要注册的组件的全路径
*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"south.block.pojo.User"};
}
}

ImportBeanDefinitionRegistrar实现类

1
2
3
4
5
6
7
8
9
10
11
12
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接口即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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”);