前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring源码篇(二)核心概念熟悉

spring源码篇(二)核心概念熟悉

作者头像
用针戳左手中指指头
发布2021-03-22 15:31:05
5310
发布2021-03-22 15:31:05
举报
文章被收录于专栏:学习计划

spring核心概念

spring中的几个核心概念,在看源码前先了解这些概念,后面再去看源码会更容易理解源码。

1. beanDefinition

bean的定义,它存储着一个bean对象需要的各种信息,如class信息、beanName、作用范围scope等信息,那么为什么要有这个定义呢?spring是扫描class字节码文件加载类的,在启动时扫描一次,之后我们要通过getBean获取一个bean,要判断这个bean是否懒加载,是否单例等等,那么这时候就不能再去解析了,因为在启动时就扫描解析过一次了,所以在第一次扫描解析会把bean的各种信息保存用来创建bean,这就是beanDefinition的作用。

定义bean的方式:

  1. @Bean
  2. @Component/@Service/@Controller…

如下一个通过beanDefinition去注册到容器,然后创建bean的一个例子

代码语言:javascript
复制
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

//		UserService userService = applicationContext.getBean("userService", UserService.class);
//
//		userService.test();

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(MenuService.class);
beanDefinition.setScope("singleton");
applicationContext.registerBeanDefinition("MenuService", beanDefinition);
MenuService bean = applicationContext.getBean(MenuService.class);
System.out.println(bean);

2. beanDefinitionReader

beanDefinition的读取器,用于注册beanDefinition, 提供了注册、获取资源加载器、获取类加载器、beanname生成、加载beanDefinition等接口。

分xml方式的和注解方式的:

  • XmlBeanDefinitionReader:扫描标签
  • AnnotatedBeanDefinitionReader:扫描由注解定义的类,能够解析的注解:@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description
  • ClassPathBeanDefinitionScanner:和上面两个一样,多了一个路径扫描,并将扫描到的含有注解的类注册
代码语言:javascript
复制
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		// 扫描xml注册bean
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(beanFactory);
		int i = xmlReader.loadBeanDefinitions("spring.xml");
		Object userService = beanFactory.getBean("userService");
		System.out.println(userService);

		// 扫描配置类 注册bean
		AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(applicationContext);
		reader.register(MenuService.class);
		MenuService bean = applicationContext.getBean(MenuService.class);
		System.out.println(bean);

		// 扫描类路径 注册
		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
		int scan = scanner.scan("com.lry.service");
		Object userService2 = beanFactory.getBean("userService");
		System.out.println(userService2);

关于为什么XmlBeanDefinitionReaderAnnotatedBeanDefinitionReader这个两个不是同一个beanDefinitionReader接口,我是这样理解的,原先的BeanDefinitionReader接口所实现的功能是针对xml的,从它的接口定义可以看出,加载beanDefinition是通过路径去读的,而AnnotatedBeanDefinitionReader功能和xml的功能一样,但其实现却有根本的区别,一个是读取xml路径,加载beanDefinition,一个是通过class加载。

3. beanFactory

beanFactory有存放bean,生成bean的功能,但它只是一个接口,spring中最核心的是DefaultListableBeanFactory,它是beanFactory最底层的类,获取beanDefinition、注册beanDefinition、别名功能、bean注册、获取bean、自动装配功能等等。

因为启动spring就是创建beanFactory,然后扫描bean的定义,拿到beanFactory,创建bean,再通过beanFactory拿到bean。

图中的说明并不完善,只是觉得在上面标的话会更加清晰,就不用一个个去找了。

  1. AliasRegistry:支持别名功能,一个名字可以对应多个别名
  2. BeanDefinitionRegistry:可以注册、保存、移除、获取某个BeanDefinition
  3. BeanFactory:Bean工厂,可以根据某个bean的名字、或类型、或别名获取某个Bean对象
  4. SingletonBeanRegistry:可以直接注册、获取某个单例Bean
  5. SimpleAliasRegistry:它是一个类,实现了AliasRegistry接口中所定义的功能,支持别名功能
  6. ListableBeanFactory:在BeanFactory的基础上,增加了其他功能,可以获取所有BeanDefinition的beanNames,可以根据某个类型获取对应的beanNames,可以根据某个类型获取{类型:对应的Bean}的映射关系
  7. HierarchicalBeanFactory:在BeanFactory的基础上,添加了获取父BeanFactory的功能
  8. DefaultSingletonBeanRegistry:它是一个类,实现了SingletonBeanRegistry接口,拥有了直接注册、获取某个单例Bean的功能
  9. ConfigurableBeanFactory:在HierarchicalBeanFactory和SingletonBeanRegistry的基础上,添加了设置父BeanFactory、类加载器(表示可以指定某个类加载器进行类的加载)、设置Spring EL表达式解析器(表示该BeanFactory可以解析EL表达式)、设置类型转化服务(表示该BeanFactory可以进行类型转化)、可以添加BeanPostProcessor(表示该BeanFactory支持Bean的后置处理器),可以合并BeanDefinition,可以销毁某个Bean等等功能
  10. FactoryBeanRegistrySupport:支持了FactoryBean的功能
  11. AutowireCapableBeanFactory:是直接继承了BeanFactory,在BeanFactory的基础上,支持在创建Bean的过程中能对Bean进行自动装配
  12. AbstractBeanFactory:实现了ConfigurableBeanFactory接口,继承了FactoryBeanRegistrySupport,这个BeanFactory的功能已经很全面了,但是不能自动装配和获取beanNames
  13. ConfigurableListableBeanFactory:继承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory
  14. AbstractAutowireCapableBeanFactory:继承了AbstractBeanFactory,实现了AutowireCapableBeanFactory,拥有了自动装配的功能
  15. DefaultListableBeanFactory:继承了AbstractAutowireCapableBeanFactory,实现了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,所以DefaultListableBeanFactory的功能很强大

4. ApplicationContext

首先ApplicationContext不止继承了beanFactory,还有registry、resourceLoader等,可以完成资源加载、解析、注册bean、获取bean等功能。

  1. HierarchicalBeanFactory:拥有获取父BeanFactory的功能
  2. ListableBeanFactory:拥有获取beanNames的功能
  3. ResourcePatternResolver:资源加载器,可以一次性获取多个资源(文件资源等等)
  4. EnvironmentCapable:可以获取运行时环境(没有设置运行时环境功能)
  5. ApplicationEventPublisher:拥有广播事件的功能(没有添加事件监听器的功能)
  6. MessageSource:拥有国际化功能

5. AnnotationConfigApplicationContext

  1. ConfigurableApplicationContext:继承了ApplicationContext接口,增加了,添加事件监听器、添加BeanFactoryPostProcessor、设置Environment,获取ConfigurableListableBeanFactory等功能
  2. AbstractApplicationContext:实现了ConfigurableApplicationContext接口
  3. GenericApplicationContext:继承了AbstractApplicationContext,实现了BeanDefinitionRegistry接口,拥有了所有ApplicationContext的功能,并且可以注册BeanDefinition,注意这个类中有一个属性(DefaultListableBeanFactory beanFactory)
  4. AnnotationConfigRegistry:可以单独注册某个为类为BeanDefinition(可以处理该类上的**@Configuration注解**,已经可以处理**@Bean注解**),同时可以扫描
  5. AnnotationConfigApplicationContext:继承了GenericApplicationContext,实现了AnnotationConfigRegistry接口,拥有了以上所有的功能
  6. AutoCloseable:资源关闭
  7. Lifecycle:回调类

6. ClassPathXmlApplicationContext

这个功能比起AnnotationConfigApplicationContext没有注册beanDefinition功能。

7. 国际化

可配置国际通用的配置,配置格式:文件基础名称_语言.properties

代码语言:javascript
复制
// 这种文件基础名称	
@Bean
	public MessageSource messageSource() {
		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
		messageSource.setBasename("message");
		return messageSource;
	}
代码语言:javascript
复制
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// 通过key或对应信息
// 这里的Locale设置语言,有提供默认的,也可以自己设置:new Locale("文件后缀")
String message = applicationContext.getMessage("key", null, Locale.ENGLISH);
		System.out.println(message);
		System.out.println(applicationContext.getMessage("key", null, new Locale("zh_ch")));;

获取中文的信息出现乱码是因为编码的不同

"

8. 资源加载

代码语言:javascript
复制
Resource resource = applicationContext.getResource("spring.xml");
System.out.println(resource.getFilename());
System.out.println(applicationContext.getResource("file:E:\\Program File\\work\\workspace\\other_people\\spring-framework2\\luban\\src\\main\\java\\com\\lry"));

9. 运行时环境

可以做到如下的操作

代码语言:javascript
复制
// 获取JVM所允许的操作系统的环境
annotationConfigApplicationContext.getEnvironment().getSystemEnvironment();

// 获取JVM本身的一些属性,包括-D所设置的
annotationConfigApplicationContext.getEnvironment().getSystemProperties();

// 还可以直接获取某个环境或properties文件中的属性
annotationConfigApplicationContext.getEnvironment().getProperty("lubanyyy")
代码语言:javascript
复制
@Component
@ConfigurationProperties(prefix = "test")
@PropertySource(value = {"classpath:pro.properties"})
//@PropertySource(value = {"file:E:/pro.properties"})
public class TestClass {

    @Value("${test.name}")
    private String name;

    @Value("${test.age}")
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

10. 事件发布

代码语言:javascript
复制
@Bean
public ApplicationListener applicationListener() {
    return new ApplicationListener() {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            System.out.println("接收到了一个事件");
        }
    };
}
代码语言:javascript
复制
applicationContext.publishEvent("ss");

这里出现两次打印,是因为在spring启动完后会有一次,然后我手动调用一次,一共两次

11. 类型转化

PropertyEditor

jdk提供的工具类,PropertyEditor看名字就知道,将spring转化为对象

代码语言:javascript
复制
public class CustomPropertyEditor extends PropertyEditorSupport implements PropertyEditor {

	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		User user = new User();
		user.setName(text);
		this.setValue(user);
	}
}

继承了PropertyEditorSupport

代码语言:javascript
复制
CustomPropertyEditor editor = new CustomPropertyEditor();
editor.setAsText("测试");
Object value = editor.getValue();
System.out.println(((User)value).getName());

ConversionService

对比PropertyEditor,功能更多

代码语言:javascript
复制
public class CustomConvertor 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("ceshi");
		return user;
	}
}
代码语言:javascript
复制
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new CustomConvertor());
User ceshi = conversionService.convert("ceshi", User.class);
System.out.println(ceshi.getName());

TypeConverter

这个转换器整合了PropertyEditorConversionService的功能

代码语言:javascript
复制
// 将value转化为requiredType类型
convertIfNecessary(Object value, Class<T> requiredType) 
代码语言:javascript
复制
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.registerCustomEditor(User.class, new CustomPropertyEditor());
User user = typeConverter.convertIfNecessary("ceshi", User.class);
System.out.println(user.getName());
代码语言:javascript
复制

12. BeanPostProcessor

bean的后置处理器,可以在创建每个Bean的过程中进行干涉,如在bean的实例化前或后,修改bean,或者做一些处理,如aop

13. BeanFactoryPostProcessor

bean工厂的后置处理器,可以对beanFactory继续设置,比如修改beanFactory的属性,增加xml方式注入的忽略接口等。

14. FactoryBean

它和beanFactory不一样,首先factoryBean可以注册成为一个bean,而它的子类更是添加了获取bean工厂的接口,如AbstractFactoryBean,继承子类你就有了bean工厂的功能。

理解的话,有点像工厂模式,用于生产某个bean的,它的getObject方法只调用一次。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/03/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • spring核心概念
    • 1. beanDefinition
      • 2. beanDefinitionReader
        • 3. beanFactory
          • 4. ApplicationContext
            • 5. AnnotationConfigApplicationContext
              • 6. ClassPathXmlApplicationContext
                • 7. 国际化
                  • 8. 资源加载
                    • 9. 运行时环境
                      • 10. 事件发布
                        • 11. 类型转化
                          • PropertyEditor
                          • ConversionService
                          • TypeConverter
                        • 12. BeanPostProcessor
                          • 13. BeanFactoryPostProcessor
                            • 14. FactoryBean
                            相关产品与服务
                            容器服务
                            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档