新建 maven 项目,引入spring 依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
@Bean
配置类:
package top.wushanghui.annotation.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import top.wushanghui.annotation.entity.Person;
/**
* @author jue
* @date 2021/4/9 21:54
* @describe
*/
@Configuration
public class ComponentRegistrationConfig {
@Bean
public Person getPerson() {
return new Person("Jerry", 5);
}
}
测试类:
package top.wushanghui.annotation;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import top.wushanghui.annotation.config.ComponentRegistrationConfig;
import top.wushanghui.annotation.entity.Person;
/**
* @author jue
* @date 2021/4/9 21:51
* @describe
*/
public class ComponentRegistrationTest {
private final ApplicationContext context = new AnnotationConfigApplicationContext(ComponentRegistrationConfig.class);
@Test
public void testBeanAnnotation() {
Person person = context.getBean(Person.class);
System.out.println(person);
String[] beanNamesForType = context.getBeanNamesForType(Person.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
System.out.println("================================漂亮的分隔符================================");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
Person{name='Jerry', age=5}
getPerson
================================漂亮的分隔符================================
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
componentRegistrationConfig
getPerson
从测试结果可以看出:
- 容器中的Person对象bean名称是getPerson,是配置类中的方法名。
- 分隔符后面是打印的Spring IOC 容器的对象,配置类componentRegistrationConfig 和 getPerson 都在容器中
看下Bean注解:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
/** @deprecated */
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}
可以指定名称:
@Bean(name = "person1")
public Person getPerson() {
return new Person("Jerry", 5);
}
再测试一下:
Person{name='Jerry', age=5}
person1
================================漂亮的分隔符================================
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
componentRegistrationConfig
person1
@ComponentScan注解
配置类,使用ComponentScan 来扫描 包top.wushanghui.annotation
下面的标注了@Controller、@Service、@Repository、@Component的注解的类。
@ComponentScan("top.wushanghui.annotation")
@Configuration
public class ComponentRegistrationConfig {
测试一下:
@Test
public void testComponentScanAnnotation() {
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
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
componentRegistrationConfig
bookController
bookDao
bookService
person1
从打印可以看出以上类都在Spring IOC容器中,前五个是Spring 默认的类,都是全类名的,后面的都是我们自己建的类,都不带包名,从名称可以看出来除了我们上面指定的Person bean 名称为person1 其他的这些自己建的类 beanDefinitionName 都是类名首字母小写的。ComponentScan注解把标注了@Controller、@Service、@Repository 的类扫描进了容器中了。
看下ComponentScan注解源码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
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 "**/*.class";
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
使用 ComponentScan.Filter[] excludeFilters() 根据FilterType 类型 来排除相应的组件注册到容器中,如下面的例子,排除标注Controller注解的类注册到容器中:
@ComponentScan(value = {"top.wushanghui.annotation"}, excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})
@Configuration
public class ComponentRegistrationConfig {
测试一下:
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
componentRegistrationConfig
bookDao
bookService
person1
发现 bookController 并不在容器中。
当然也可以配置 ComponentScan.Filter[] includeFilters() default {}; 来指定容器中注册哪些类,但是需要useDefaultFilters = false,因为Spring 默认 boolean useDefaultFilters() default true; 全部注册的。
@ComponentScan(value = {"top.wushanghui.annotation"}, useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})
@Configuration
public class ComponentRegistrationConfig {
可以看到我们注册扫描的类 只有bookController
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
componentRegistrationConfig
bookController
person1
看一下Spring中给我们提供了哪些过滤规则:
public enum FilterType {
/**
* 过滤标记有给定注解的对象。
*/
ANNOTATION,
/**
* 筛选可分配给给定类型的候选。
*/
ASSIGNABLE_TYPE,
/**
* 筛选与给定的AspectJ类型模式表达式匹配的候选对象。
* @see org.springframework.core.type.filter.AspectJTypeFilter
*/
ASPECTJ,
/**
* 筛选与给定正则表达式模式匹配的候选对象。
* @see org.springframework.core.type.filter.RegexPatternTypeFilter
*/
REGEX,
/** 使用给定的自定义过滤候选人
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM
}
下面我们再试一下FilterType.CUSTOM 自定义的方式:
实现 TypeFilter 接口
比如只把包括service 的类名的组件注册道容器中
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
@Override
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();
System.out.println("--->" + className);
return className.contains("service");
}
}
@ComponentScan(value = {"top.wushanghui.annotation"}, useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})})
@Configuration
public class ComponentRegistrationConfig {
--->top.wushanghui.annotation.ComponentRegistrationTest
--->top.wushanghui.annotation.controller.BookController
--->top.wushanghui.annotation.dao.BookDao
--->top.wushanghui.annotation.entity.Person
--->top.wushanghui.annotation.fiter.MyTypeFilter
--->top.wushanghui.annotation.service.BookService
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
componentRegistrationConfig
bookService
person1
看结果只有ComponentScan 扫描的组件 bookService 注册到容器中了。
我们还看到ComponentScan 注解源码中还标注了 @Repeatable(ComponentScans.class),在ComponentScans 注解中可以重复标注ComponentScans注解:
@ComponentScans(value = {
@ComponentScan(value = {"top.wushanghui.annotation"}, useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}),
@ComponentScan(value = {"top.wushanghui.annotation"}, useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class})})
}
)
@Configuration
public class ComponentRegistrationConfig {
看上面的配置,把扫描到的Controller和Service注解标注的组件注册到容器中,
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
componentRegistrationConfig
bookController
bookService
person1
@Scope
@Scope bean作用域:
- prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
每次获取的时候才会调用方法创建对象; - singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
以后每次获取就是直接从容器(map.get())中拿 - request:同一次请求创建一个实例
- session:同一个session创建一个实例
Scope 注解的源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
/**
* Alias for {@link #scopeName}.
* @see #scopeName
*/
@AliasFor("scopeName")
String value() default "";
/**
* Specifies the name of the scope to use for the annotated component/bean.
* <p>Defaults to an empty string ({@code ""}) which implies
* {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
* @since 4.2
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
* @see #value
*/
@AliasFor("value")
String scopeName() default "";
/**
* Specifies whether a component should be configured as a scoped proxy
* and if so, whether the proxy should be interface-based or subclass-based.
* <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
* that no scoped proxy should be created unless a different default
* has been configured at the component-scan instruction level.
* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
* @see ScopedProxyMode
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
大家都知道spring 容器中的对象默认都是单例的,我们看下容器中Bean什么时候创建的:
public Person(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("Person 对象 构造器初始化");
}
在Person类中打印一下
直接测试:
private final ApplicationContext context = new AnnotationConfigApplicationContext(ComponentRegistrationConfig.class);
@Test
public void testScopeAnnotation() {
System.out.println("ioc容器创建完成....");
}
Person 对象 构造器初始化
ioc容器创建完成....
发现 Person 在ioc容器创建完成之前就被初始化了,那继续测试一下这个Person是不是单例的呢?
// 配置类中的
@Bean(name = "person1")
public Person getPerson() {
return new Person("Jerry", 5);
}
// 测试
@Test
public void testScopeAnnotation() {
System.out.println("ioc容器创建完成....");
Object bean1 = context.getBean("person1");
Object bean2 = context.getBean("person1");
System.out.println(bean1 == bean2);
}
Person 对象 构造器初始化
ioc容器创建完成....
true
使用多实例:
加上多实例
@Scope("prototype")
@Bean(name = "person1")
public Person getPerson() {
return new Person("Jerry", 5);
}
继续执行testScopeAnnotation 方法测试:
ioc容器创建完成....
Person 对象 构造器初始化
Person 对象 构造器初始化
false
这是我们就发现 :
- ioc容器创建完成之前并没有初始化Person对象,当我们
context.getBean("person1");
`获取的时候才初始化的; - 每次获取的都是不同的bean。
@Lazy
懒加载:
- 单实例bean:默认在容器启动的时候创建对象;
- 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
未标注懒加载注解,单实例bean:
@Bean(name = "person1")
public Person getPerson() {
System.out.println("给容器中添加Person....");
return new Person("Jerry", 5);
}
测试:
@Test
public void testScopeAnnotation() {
System.out.println("ioc容器创建完成....");
Object bean1 = context.getBean("person1");
Object bean2 = context.getBean("person1");
System.out.println(bean1 == bean2);
}
结果:
给容器中添加Person....
Person 对象 构造器初始化
ioc容器创建完成....
true
标注懒加载注解:
@Lazy
@Bean(name = "person1")
public Person getPerson() {
System.out.println("给容器中添加Person....");
return new Person("Jerry", 5);
}
继续执行上面测试:
ioc容器创建完成....
给容器中添加Person....
Person 对象 构造器初始化
true
@Conditional
按照条件注册bean
先定义两个 Condition 的实现类
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
assert property != null;
return property.toLowerCase().contains("linux");
}
}
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
assert property != null;
return property.toLowerCase().contains("windows");
}
}
接口:
public interface ShowCommand {
/**
* 显示cmd 命令
*/
String showCommandInfo();
}
实现类
public class WindowsShowCommand implements ShowCommand {
@Override
public String showCommandInfo() {
return "dir";
}
}
public class LinuxShowCommand implements ShowCommand {
@Override
public String showCommandInfo() {
return "ls";
}
}
在配置类中注入bean:
@Bean("showCmd")
@Conditional(WindowsCondition.class)
public ShowCommand winCmd() {
return new WindowsShowCommand();
}
@Bean("showCmd")
@Conditional(LinuxCondition.class)
public ShowCommand linuxCmd() {
return new LinuxShowCommand();
}
上面两个类,根据Conditional中指定的Condition 的条件,只会有一个类能注册到容器中。
测试一下:
@Test
public voidtestConditionalAnnotation() {
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
System.out.println(osName);
ShowCommand showCmd = (ShowCommand) context.getBean("showCmd");
System.out.println(showCmd.getClass());
System.out.println(showCmd.showCommandInfo());
}
Windows 10
class top.wushanghui.annotation.service.impl.WindowsShowCommand
dir
@Import
- @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
- ImportSelector:返回需要导入的组件的全类名数组;
- ImportBeanDefinitionRegistrar:手动注册bean到容器中
直接导入类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
看源码可知:value class数组中可以导入 类 或者 ImportSelector 选择器选择的类 再或者 ImportBeanDefinitionRegistrar 注册的类到容器中。
下面就一步一步来演示:
创建实体类
public class Cat {
}
public class Dog{
}
使用Import 注解直接导入:
@Import({Dog.class})
public class ComponentRegistrationConfig {
测试:
@Test
public void testImportAnnotation() {
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
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
componentRegistrationConfig
bookController
bookService
top.wushanghui.annotation.entity.Dog
person1
showCmd
看到 top.wushanghui.annotation.entity.Dog
在容器中了。
ImportSelector
导入选择器中选择导入Cat类
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 千万不能返回空值
return new String[]{"top.wushanghui.annotation.entity.Cat"};
}
@Override
public Predicate<String> getExclusionFilter() {
return null;
}
}
MyImportSelector 选择器加到Import注解上:
@Import({Dog.class, MyImportSelector.class})
public class ComponentRegistrationConfig {
直接测试:
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
componentRegistrationConfig
bookController
bookService
top.wushanghui.annotation.entity.Dog
top.wushanghui.annotation.entity.Cat
person1
showCmd
top.wushanghui.annotation.entity.Cat
也在容器中了。
ImportBeanDefinitionRegistrar
新建一个动物类:
public class Animal {
}
ImportBeanDefinitionRegistrar 的实现类:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition1 = registry.containsBeanDefinition("top.wushanghui.annotation.entity.Cat");
boolean definition2 = registry.containsBeanDefinition("top.wushanghui.annotation.entity.Dog");
if(definition1 && definition2){
//指定Bean定义信息;(Bean的类型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(Animal.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("animal", beanDefinition);
}
}
}
在 registerBeanDefinitions 方法中,如果Cat和Dog 都在容器中的话,我们把animal 注册到容器中。
把 MyImportBeanDefinitionRegistrar 添加到Import 注解中
@Import({Dog.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class ComponentRegistrationConfig {
直接测试:
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
componentRegistrationConfig
bookController
bookService
top.wushanghui.annotation.entity.Dog
top.wushanghui.annotation.entity.Cat
person1
showCmd
animal
animal 已经在容器中了。
用FactoryBean注册组件
public class Color {
}
实现 FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
/**
* 回一个Color对象,这个对象会添加到容器中
*/
@Override
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...create object and return obj...");
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 如果不重写,默认是true,单实例的
//true:这个bean是单实例,在容器中保存一份
//false:多实例,每次获取都会创建一个新的bean;
@Override
public boolean isSingleton() {
return false;
}
}
配置类中添加:
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
测试:
@Test
public void testFactoryBean() {
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
System.out.println("================================漂亮的分隔符================================");
Object bean1 = context.getBean("colorFactoryBean");
Object bean2 = context.getBean("colorFactoryBean");
Object bean3 = context.getBean("&colorFactoryBean");
Object bean4 = context.getBean("&colorFactoryBean");
System.out.println("bean1.getClass(): " + bean1.getClass());
System.out.println("bean2.getClass(): " + bean2.getClass());
System.out.println("bean3.getClass(): " + bean3.getClass());
System.out.println("bean4.getClass(): " + bean4.getClass());
System.out.println("color 是否是单例的:"+bean1 == bean2);
System.out.println("FactoryBean 是否是单例的:"+bean3 == bean4);
}
结果:
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
componentRegistrationConfig
bookController
bookService
top.wushanghui.annotation.entity.Dog
top.wushanghui.annotation.entity.Cat
person1
showCmd
colorFactoryBean
animal
================================漂亮的分隔符================================
ColorFactoryBean...create object and return obj...
ColorFactoryBean...create object and return obj...
bean1.getClass(): class top.wushanghui.annotation.entity.Color
bean2.getClass(): class top.wushanghui.annotation.entity.Color
bean3.getClass(): class top.wushanghui.annotation.entity.ColorFactoryBean
bean4.getClass(): class top.wushanghui.annotation.entity.ColorFactoryBean
false
false
从执行结果来看,容器中有我们放入的colorFactoryBean ,但是它的类型却是Color,而不是我们配置到配置类中的ColorFactoryBean。
如果要从容器中工厂bean,名称前加“&”,像这种 context.getBean("&colorFactoryBean")。