演示工程

@Configuration和@Bean给容器中注册组件
@Configuration
1
2
3
4
5
6
7
8
9
|
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 本身也是一个组件
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
|
@Bean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {}; // name就是bean的id
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.eh.config;
import com.eh.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 配置类=配置文件
@Configuration // 告诉Spring这是一个配置类
public class MainConfig {
// 给Bean中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean
public Person person() {
return new Person();
}
}
|
注册的bean默认id是方法名,可以通过下面方式获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.eh;
import com.eh.bean.Person;
import com.eh.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
/**
* 因为一个类型可能对应多个不同id的bean,所以使用数组接受
*/
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType) {
System.out.println(name);
}
}
}
|
@ComponentScan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class) // 可以在一个类上重复注解多次,和@ComponentScans效果一样
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
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 ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {}; // 包括哪些规则的类
Filter[] excludeFilters() default {}; // 排除哪些规则的类
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
|
使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.eh.config;
import com.eh.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 配置类=配置文件
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(basePackages = "com.eh")
public class MainConfig {
// 给Bean中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean
public Person person() {
return new Person();
}
}
|
打印所有的bean id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.eh;
import com.eh.bean.Person;
import com.eh.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
// 获取所有的bean id
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
|
可以看到标注了@Component注解的类的bean都注入进来了,包括@Configuration
1
2
3
4
5
6
7
8
9
10
|
Person(name=null, age=null)
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
mainConfig
bookController
bookDao
person
|
同配置文件中一样,@ComponentScan也可以过滤和包括一些符合规则的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package com.eh.config;
import com.eh.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
@Configuration
// 指定要扫描的包
@ComponentScan(
basePackages = "com.eh",
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件,同配置文件一样一样要禁掉useDefaultFilters,也就是useDefaultFilters=false,默认是true
excludeFilters = {
// 过滤标注了Controller注解的类
// @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)
//FilterType.ANNOTATION:按照注解
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则
}
)
public class MainConfig {
}
|
FilterType.CUSTOM:使用自定义规则 ,举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->" + className);
if (className.contains("er")) {
return true;
}
return false;
}
}
|
使用:
1
2
3
4
5
6
7
8
9
10
|
@Configuration
@ComponentScans(
value = {
@ComponentScan(value = "com.eh", includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}, useDefaultFilters = false)
}
)
public class MainConfig {
}
|
@Scope
调整作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
/**
* ConfigurableBeanFactory#SCOPE_PROTOTYPE
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
下面这两个在web项目下菜生效
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION sesssion
* @return\
* @Scope:调整作用域
* prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
* 每次获取的时候才会调用方法创建对象;
* singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
* 以后每次获取就是直接从容器(map.get())中拿,
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*/
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
|
@Lazy
懒加载
1
2
3
4
5
6
7
8
9
10
11
12
|
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {
/**
懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
单实例bean:默认在容器启动的时候创建对象;可以加上这个注解在第一次使用的时候创建对象并初始化
*/
boolean value() default true;
}
|
@Conditional
按照一定的条件进行判断,满足条件则在容器中注册Bean,这个注解在Sringboot中大量使用
1
2
3
4
5
6
7
8
|
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
|
ok, 我们写两个Condition的实现类,一个表示Windows环境,一个表示Mac OS X 环境
MacCondition.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package com.eh.condition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 判断是否是Mac系统
*/
public class MacCondition implements Condition {
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注解信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//1、能获取到ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取当前环境信息
Environment environment = context.getEnvironment();
//4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
// 我们使用环境信息
return environment.getProperty("os.name").contains("Mac");
}
}
|
WindowsCondition.java
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.eh.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Windows");
}
}
|
注册Bean时进行判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package com.eh.config;
import com.eh.bean.Person;
import com.eh.condition.MacCondition;
import com.eh.condition.WindowsCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig {
@Conditional(WindowsCondition.class)
@Bean("Windows")
public Person person1() {
return new Person("gates", 50);
}
@Conditional(MacCondition.class)
@Bean("Mac")
public Person person2() {
return new Person("jobs", 52);
}
}
|
通过切换系统环境来测试,可以使用虚拟机变量-Dos.name=Windows
切换到windows环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.eh;
import com.eh.bean.Person;
import com.eh.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Map;
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Map<String, Person> map = applicationContext.getBeansOfType(Person.class);
System.out.println(map);
}
// 输出 {Mac=Person(name=jobs, age=52)}
}
|
还可以使用registry判断容器中是否已经存在某个Bean
1
2
|
//可以判断容器中的bean注册情况,也可以给容器中注册bean
boolean exists = registry.containsBeanDefinition("person");
|
@Conditional还可以放在类上,对类中所有组件统一按照条件判断进行设置。满足当前条件,这个类中配置的所有bean注册才能生效。
@Import
快速给容器中导入组件,容器中会自动注册注解里要求的组件,id默认是全类名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
以下分别对应
1. 使用了Configuration注解的类,将该类下的Bean注册进容器,无需使用容器启动时指定配置类的方式
2. 实现了ImportSelector的类,返回需要导入的组件的全类名数组
3. 实现了ImportBeanDefinitionRegistrar的类,手动注册bean到容器中
4. 普通的java类
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
|
演示:
ImportSelector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.eh.condition;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
//返回值,就是到导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//方法不要返回null值
return new String[]{"com.eh.bean.Red", "com.eh.bean.Yellow"};
}
}
|
ImportBeanDefinitionRegistrar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.eh.condition;
import com.eh.bean.RainBow;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
// 调用BeanDefinitionRegistry.registerBeanDefinition将所有需要添加到容器中的bean手工注册进来
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("com.eh.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.eh.bean.Yellow");
boolean definition3 = registry.containsBeanDefinition("com.eh.bean.Green");
boolean definition4 = registry.containsBeanDefinition("com.eh.bean.Pink");
// 如果容器中有红色和黄色就注册彩虹
if (definition && definition2 & definition3 & definition4) {
//指定Bean定义信息;(Bean的类型)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}
|
使用@Import注解
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package com.eh.config;
import com.eh.bean.Green;
import com.eh.bean.Pink;
import com.eh.condition.MyImportBeanDefinitionRegistrar;
import com.eh.condition.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({MyImportSelector.class, MyImportBeanDefinitionRegistrar.class, Green.class, Pink.class})
public class MainConfig {
}
|
注意:MyImportBeanDefinitionRegistrar会在其他类注册后进行注册
测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.eh;
import com.eh.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
printAllBeanNames(applicationContext);
}
public static void printAllBeanNames(ApplicationContext applicationContext) {
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
Arrays.asList(beanDefinitionNames).forEach(System.out::println);
}
}
|
运行结果:
1
2
3
4
5
6
|
mainConfig
com.eh.bean.Red
com.eh.bean.Yellow
com.eh.bean.Green
com.eh.bean.Pink
rainBow
|
使用FactoryBean接口创建Bean
整合其他框架的时候用的特别多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package com.eh.bean;
import org.springframework.beans.factory.FactoryBean;
// 工厂Bean
public class PersonFactoryBean implements FactoryBean<Person> {
// 返回的对象
@Override
public Person getObject() throws Exception {
return new Person();
}
// 返回的Bean类型
@Override
public Class<?> getObjectType() {
return Person.class;
}
// 是否单例
@Override
public boolean isSingleton() {
return true;
}
}
|
使用工厂Bean
1
2
3
4
5
6
7
8
9
10
|
package com.eh.config;
import com.eh.bean.PersonFactoryBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(PersonFactoryBean.class)
public class MainConfig {
}
|
测试,注意注释部分,默认使用getBean获取的是工厂Bean创建的对象,要获取工厂Bean需要加上前缀&
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package com.eh;
import com.eh.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Object personFactoryBean = applicationContext.getBean("com.eh.bean.PersonFactoryBean");
// 输出 class com.eh.bean.Person,所以getBean获取的是工厂Bean创建的对象
System.out.println(personFactoryBean.getClass());
/**
* 我们也可以使用 & {@link org.springframework.beans.factory.BeanFactory} 获取工厂Bean本身
*/
Object personFactoryBean1 = applicationContext.getBean("&com.eh.bean.PersonFactoryBean");
// 输出 class com.eh.bean.PersonFactoryBean
System.out.println(personFactoryBean1.getClass());
}
public static void printAllBeanNames(ApplicationContext applicationContext) {
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
Arrays.asList(beanDefinitionNames).forEach(System.out::println);
}
}
|
总结
四种注册组件的方式
-
包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)
一般自己写的类这样使用
-
@Bean
一般导入的第三方包里面的组件这样使用
-
@Import
可以快速给容器中导入一个组件,id默认是全类名
- ImportSelector:返回需要导入的组件的全类名数组;
- ImportBeanDefinitionRegistrar:手动注册bean到容器中
-
使用Spring提供的 FactoryBean(工厂Bean),整合其他框架的时候用的特别多
- 默认获取到的是工厂bean调用getObject创建的对象
- 要获取工厂Bean本身,我们需要给id前面加一个&