BeanDefinition
BeanDefinition
表示Bean
定义,BeanDefinition
中存在很多属性用来描述一个Bean
的特点。比如:
class
,表示Bean
类型scope
,表示Bean
作用域,单例或原型等lazyInit
,表示Bean
是否是懒加载initMethodName
,表示Bean
初始化时要执行的方法destroyMethodName
,表示Bean
销毁时要执行的方法- 还有很多…
在Spring
中,我们经常会通过以下几种方式来定义Bean
:
<bean/>
@Bean
@Component
(@Service
,@Controller
)
这些,我们可以称之申明式定义Bean
。
我们还可以编程式定义Bean
,那就是直接通过BeanDefinition
,比如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 生成一个BeanDefinition对象,并设置beanClass为User.class,并注册到ApplicationContext中
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
context.registerBeanDefinition("user", beanDefinition);
System.out.println(context.getBean("user"));
我们还可以通过BeanDefinition
设置一个Bean
的其他属性
beanDefinition.setScope("prototype"); // 设置作用域
beanDefinition.setInitMethodName("init"); // 设置初始化方法
beanDefinition.setLazyInit(true); // 设置懒加载
和申明式事务、编程式事务类似,通过<bean/>
,@Bean
,@Component
等申明式方式所定义的Bean
,最终都会被Spring
解析为对应的BeanDefinition
对象,并放入Spring
容器中。
BeanDefinitionReader
接下来,我们来介绍几种在Spring
源码中所提供的BeanDefinition
读取器(BeanDefinitionReader
),这些BeanDefinitionReader
在我们使用Spring
时用得少,但在Spring
源码中用得多,相当于Spring
源码的基础设施。
AnnotatedBeanDefinitionReader
可以直接把某个类转换为BeanDefinition
,并且会解析该类上的注解,比如
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(context);
// 将User.class解析为BeanDefinition
annotatedBeanDefinitionReader.register(User.class);
System.out.println(context.getBean("user"));
注意:它能解析的注解是:@Conditional
、@Scope
、@Lazy
、@Primary
、@DependsOn
、@Role
、@Description
XmlBeanDefinitionReader
可以解析<bean/>
标签
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);
int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");
System.out.println(context.getBean("user"));
ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner
是扫描器,但是它的作用和BeanDefinitionReader
类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component
注解,那么就会把这个类解析为一个BeanDefinition
,比如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.scan("com.zhouyu");
System.out.println(context.getBean("userService"));
BeanFactory
BeanFactory
表示Bean
工厂,所以很明显,BeanFactory
会负责创建Bean
,并且提供获取Bean
的API
。
而ApplicationContext
是BeanFactory
的一种,在Spring
源码中,是这么定义的:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
...
}
ApplicationContext
继承了ListableBeanFactory
和HierarchicalBeanFactory
,而ListableBeanFactory
和HierarchicalBeanFactory
都继承至BeanFactory
,所以我们可以认为ApplicationContext
继承了BeanFactory
,拥有BeanFactory
支持的所有功能,不过ApplicationContext
比BeanFactory
更加强大,ApplicationContext
还继承了其他接口,也就表示ApplicationContext
还拥有其他功能,比如MessageSource
表示国际化,ApplicationEventPublisher
表示事件发布,EnvironmentCapable
表示获取环境变量,等等,关于ApplicationContext
后面再详细讨论。
在Spring
的源码实现中,当我们new
一个ApplicationContext
时,其底层会new
一个BeanFactory
出来,当使用ApplicationContext
的某些方法时,比如getBean()
,底层调用的是BeanFactory
的getBean()
方法。
在Spring
源码中,BeanFactory
接口存在一个非常重要的实现类是:DefaultListableBeanFactory
,也是非常核心的。
所以,我们可以直接来使用DefaultListableBeanFactory
,而不用使用ApplicationContext
的某个实现类,比如:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
beanFactory.registerBeanDefinition("user", beanDefinition);
System.out.println(beanFactory.getBean("user"));
DefaultListableBeanFactory
是非常强大的,支持很多功能,可以通过查看DefaultListableBeanFactory
的类继承实现结构来看:
这部分现在看不懂没关系,源码熟悉一点后回来再来看都可以。
它实现了很多接口,表示它拥有很多功能:
AliasRegistry
:支持别名功能,一个名字可以对应多个别名BeanDefinitionRegistry
:可以注册、保存、移除、获取某个BeanDefinition
BeanFactory
:Bean
工厂,可以根据某个bean的名字、或类型、或别名获取某个Bean
对象SingletonBeanRegistry
:可以直接注册、获取某个单例Bean
SimpleAliasRegistry
:它是一个类,实现了AliasRegistry
接口中所定义的功能,支持别名功能ListableBeanFactory
:在BeanFactory
的基础上,增加了其他功能,可以获取所有BeanDefinition
的beanNames
,可以根据某个类型获取对应的beanNames
,可以根据某个类型获取{类型:对应的Bean
}的映射关系HierarchicalBeanFactory
:在BeanFactory
的基础上,添加了获取父BeanFactory
的功能DefaultSingletonBeanRegistry
:它是一个类,实现了SingletonBeanRegistry
接口,拥有了直接注册、获取某个单例Bean
的功能ConfigurableBeanFactory
:在HierarchicalBeanFactory
和SingletonBeanRegistry
的基础上,添加了设置父BeanFactory
、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL
表达式解析器(表示该BeanFactory
可以解析EL
表达式)、设置类型转化服务(表示该BeanFactory
可以进行类型转化)、可以添加BeanPostProcessor
(表示该BeanFactory
支持Bean
的后置处理器),可以合并BeanDefinition
,可以销毁某个Bean
等等功能FactoryBeanRegistrySupport
:支持了FactoryBean
的功能AutowireCapableBeanFactory
:是直接继承了BeanFactory
,在BeanFactory
的基础上,支持在创建Bean
的过程中能对Bean
进行自动装配AbstractBeanFactory
:实现了ConfigurableBeanFactory
接口,继承了FactoryBeanRegistrySupport
,这个BeanFactory
的功能已经很全面了,但是不能自动装配和获取beanNames
ConfigurableListableBeanFactory
:继承了ListableBeanFactory
、AutowireCapableBeanFactory
、ConfigurableBeanFactory
AbstractAutowireCapableBeanFactory
:继承了AbstractBeanFactory
,实现了AutowireCapableBeanFactory
,拥有了自动装配的功能DefaultListableBeanFactory
:继承了AbstractAutowireCapableBeanFactory
,实现了ConfigurableListableBeanFactory
接口和BeanDefinitionRegistry
接口,所以DefaultListableBeanFactory
的功能很强大
ApplicationContext
上面有分析到,ApplicationContext
是个接口,实际上也是一个BeanFactory
,不过比BeanFactory
更加强大,比如:
HierarchicalBeanFactory
:拥有获取父BeanFactory
的功能ListableBeanFactory
:拥有获取beanNames
的功能ResourcePatternResolver
:资源加载器,可以一次性获取多个资源(文件资源等等)EnvironmentCapable
:可以获取运行时环境(没有设置运行时环境功能)ApplicationEventPublisher
:拥有广播事件的功能(没有添加事件监听器的功能)MessageSource
:拥有国际化功能
我们先来看ApplicationContext
两个比较重要的实现类:
AnnotationConfigApplicationContext
ClassPathXmlApplicationContext
AnnotationConfigApplicationContext
这部分现在看不懂没关系,源码熟悉一点后回来再来看都可以。
ConfigurableApplicationContext
:继承了ApplicationContext
接口,增加了,添加事件监听器、添加BeanFactoryPostProcessor
、设置Environment
,获取ConfigurableListableBeanFactory
等功能AbstractApplicationContext
:实现了ConfigurableApplicationContext
接口GenericApplicationContext
:继承了AbstractApplicationContext
,实现了BeanDefinitionRegistry
接口,拥有了所有ApplicationContext
的功能,并且可以注册BeanDefinition
,注意这个类中有一个属性(DefaultListableBeanFactory beanFactory
)AnnotationConfigRegistry
:可以单独注册某个为类为BeanDefinition
(可以处理该类上的@Configuratio
n注解,已经可以处理@Bean
注解),同时可以扫描AnnotationConfigApplicationContext
:继承了GenericApplicationContext
,实现了AnnotationConfigRegistry
接口,拥有了以上所有的功能
ClassPathXmlApplicationContext
它也是继承了AbstractApplicationContext
,但是相对于AnnotationConfigApplicationContext
而言,功能没有AnnotationConfigApplicationContext
强大,比如不能注册BeanDefinition
国际化
先定义一个MessageSource
:
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
有了这个Bean
,你可以在你任意想要进行国际化的地方使用该MessageSource
。
同时,因为ApplicationContext
也拥有国际化的功能,所以可以直接这么用:
context.getMessage("test", null, new Locale("en_CN"))
资源加载
ApplicationContext
还拥有资源加载的功能,比如,可以直接利用ApplicationContext
获取某个文件的内容:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Resource resource = context.getResource("file://D:\\IdeaProjects\\spring-framework-5.3.10\\tuling\\src\\main\\java\\com\\zhouyu\\service\\UserService.java");
System.out.println(resource.contentLength());
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Resource resource = context.getResource("file://D:\\IdeaProjects\\spring-framework-5.3.10\\tuling\\src\\main\\java\\com\\zhouyu\\service\\UserService.java");
System.out.println(resource.contentLength());
System.out.println(resource.getFilename());
Resource resource1 = context.getResource("https://www.baidu.com");
System.out.println(resource1.contentLength());
System.out.println(resource1.getURL());
Resource resource2 = context.getResource("classpath:spring.xml");
System.out.println(resource2.contentLength());
System.out.println(resource2.getURL());
Resource[] resources = context.getResources("classpath:com/zhouyu/*.class");
for (Resource resource : resources) {
System.out.println(resource.contentLength());
System.out.println(resource.getFilename());
}
获取运行时环境
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment();
System.out.println(systemEnvironment);
System.out.println("=======");
Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties();
System.out.println(systemProperties);
System.out.println("=======");
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
System.out.println(propertySources);
System.out.println("=======");
System.out.println(context.getEnvironment().getProperty("NO_PROXY"));
System.out.println(context.getEnvironment().getProperty("sun.jnu.encoding"));
System.out.println(context.getEnvironment().getProperty("zhouyu"));
注意,可以这样来使得某个properties
文件中的参数添加到运行时环境中
@PropertySource("classpath:spring.properties")
事件发布
先定义一个事件监听器
@Bean
public ApplicationListener applicationListener() {
return new ApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("接收到了一个事件");
}
};
}
然后发布一个事件:
context.publishEvent("kkk");
类型转化
在Spring
源码中,有可能需要把String
转成其他类型,所以在Spring
源码中提供了一些技术来更方便的做对象的类型转化,关于类型转化的应用场景, 后续看源码的过程中会遇到很多。
PropertyEditor
这其实是JDK
中提供的类型转化工具类
public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {
@Override
public void setAsText(String text) throws IllegalArgumentException {
User user = new User();
user.setName(text);
this.setValue(user);
}
}
StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
propertyEditor.setAsText("1");
User value = (User) propertyEditor.getValue();
System.out.println(value);
如何向Spring
中注册PropertyEditor
:
@Bean
public CustomEditorConfigurer customEditorConfigurer() {
CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
Map<Class<?>, Class<? extends PropertyEditor>> propertyEditorMap = new HashMap<>();
// 表示StringToUserPropertyEditor可以将String转化成User类型,在Spring源码中,如果发现当前对象是String,而需要的类型是User,就会使用该PropertyEditor来做类型转化
propertyEditorMap.put(User.class, StringToUserPropertyEditor.class);
customEditorConfigurer.setCustomEditors(propertyEditorMap);
return customEditorConfigurer;
}
@Component
public class UserService {
@Value("xxx")
private User user;
public void test() {
System.out.println(user);
}
}
那么test
属性就能正常的完成属性赋值
ConversionService
Spring
中提供的类型转化服务,它比PropertyEditor
更强大
public class StringToUserConverter implements ConditionalGenericConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, User.class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
User user = new User();
user.setName((String)source);
return user;
}
}
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
User value = conversionService.convert("1", User.class);
System.out.println(value);
如何向Spring
中注册ConversionService
:
@Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));
return conversionServiceFactoryBean;
}
TypeConverter
整合了PropertyEditor
和ConversionService
的功能,是Spring
内部用的
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
//typeConverter.setConversionService(conversionService);
User value = typeConverter.convertIfNecessary("1", User.class);
System.out.println(value);
OrderComparator
OrderComparator
是Spring
所提供的一种比较器,可以用来根据@Order
注解或实现Ordered
接口来执行值进行比较,从而可以进行排序。
public class A implements Ordered {
@Override
public int getOrder() {
return 3;
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
public class B implements Ordered {
@Override
public int getOrder() {
return 2;
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
public class Main {
public static void main(String[] args) {
A a = new A(); // order=3
B b = new B(); // order=2
OrderComparator comparator = new OrderComparator();
System.out.println(comparator.compare(a, b)); // 1
List list = new ArrayList<>();
list.add(a);
list.add(b);
// 按order值升序排序
list.sort(comparator);
System.out.println(list); // B,A
}
}
另外,Spring
中还提供了一个OrderComparator
的子类:AnnotationAwareOrderComparator
,它支持用@Order
来指定order
值。比如:
@Order(3)
public class A {
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
@Order(2)
public class B {
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
public class Main {
public static void main(String[] args) {
A a = new A(); // order=3
B b = new B(); // order=2
AnnotationAwareOrderComparator comparator = new AnnotationAwareOrderComparator();
System.out.println(comparator.compare(a, b)); // 1
List list = new ArrayList<>();
list.add(a);
list.add(b);
// 按order值升序排序
list.sort(comparator);
System.out.println(list); // B,A
}
}
BeanPostProcessor
BeanPostProcess
表示Bena
的后置处理器,我们可以定义一个或多个BeanPostProcessor
,比如通过一下代码定义一个BeanPostProcessor
:
@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("初始化前");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("初始化后");
}
return bean;
}
}
一个BeanPostProcessor
可以在任意一个Bean
的初始化之前以及初始化之后去额外的做一些用户自定义的逻辑,当然,我们可以通过判断beanName
来进行针对性处理(针对某个Bean
,或某部分Bean
)。
我们可以通过定义BeanPostProcessor
来干涉Spring
创建Bean
的过程。
BeanFactoryPostProcessor
BeanFactoryPostProcessor
表示Bean
工厂的后置处理器,其实和BeanPostProcessor
类似,BeanPostProcessor
是干涉Bean
的创建过程,BeanFactoryPostProcessor
是干涉BeanFactory
的创建过程。比如,我们可以这样定义一个BeanFactoryPostProcessor
:
@Component
public class ZhouyuBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("加工beanFactory");
}
}
我们可以在postProcessBeanFactory()
方法中对BeanFactory
进行加工。
FactoryBean
上面提到,我们可以通过BeanPostPorcessor
来干涉Spring
创建Bean
的过程,但是如果我们想一个Bean
完完全全由我们来创造,也是可以的,比如通过FactoryBean
:
@Component
public class ZhouyuFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
UserService userService = new UserService();
return userService;
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
通过上面这段代码,我们自己创造了一个UserService
对象,并且它将成为Bean
。但是通过这种方式创造出来的UserService
的Bean
,只会经过初始化后,其他Spring
的生命周期步骤是不会经过的,比如依赖注入。
通过@Bean
也可以自己生成一个对象作为Bean
,那么和FactoryBean
的区别是什么呢?其实在很多场景下他俩是可以替换的,但是站在原理层面来说的,区别很明显,@Bean
定义的Bean
是会经过完整的Bean
生命周期的。
ExcludeFilter
和IncludeFilter
这两个Filter
是Spring
扫描过程中用来过滤的。ExcludeFilter
表示排除过滤器,IncludeFilter
表示包含过滤器。
比如以下配置,表示扫描com.zhouyu
这个包下面的所有类,但是排除UserService
类,也就是就算它上面有@Component
注解也不会成为Bean
。
@ComponentScan(value = "com.zhouyu",
excludeFilters = {@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = UserService.class)}.)
public class AppConfig {
}
再比如以下配置,就算UserService
类上没有@Component
注解,它也会被扫描成为一个Bean
。
@ComponentScan(value = "com.zhouyu",
includeFilters = {@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = UserService.class)})
public class AppConfig {
}
FilterType
分为:
ANNOTATION
:表示是否包含某个注解ASSIGNABLE_TYPE
:表示是否是某个类ASPECTJ
:表示否是符合某个Aspectj
表达式REGEX
:表示是否符合某个正则表达式CUSTOM
:自定义
在Spring
的扫描逻辑中,默认会添加一个AnnotationTypeFilter
给includeFilters
,表示默认情况下Spring
扫描过程中会认为类上有@Component
注解的就是Bean
。
MetadataReader
、ClassMetadata
、AnnotationMetadata
在Spring
中需要去解析类的信息,比如类名、类中的方法、类上的注解,这些都可以称之为类的元数据,所以Spring
中对类的元数据做了抽象,并提供了一些工具类。
MetadataReader
表示类的元数据读取器,默认实现类为SimpleMetadataReader
。比如:
public class Test {
public static void main(String[] args) throws IOException {
SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();
// 构造一个MetadataReader
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.zhouyu.service.UserService");
// 得到一个ClassMetadata,并获取了类名
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println(classMetadata.getClassName());
// 获取一个AnnotationMetadata,并获取类上的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
for (String annotationType : annotationMetadata.getAnnotationTypes()) {
System.out.println(annotationType);
}
}
}
需要注意的是,SimpleMetadataReader
去解析类时,使用的ASM
技术。
为什么要使用ASM
技术,Spring
启动的时候需要去扫描,如果指定的包路径比较宽泛,那么扫描的类是非常多的,会导致在Spring
启动时就把这些类全部加载进JVM
了,这样不太好,所以使用了ASM
技术。