BeanDefinition
BeanDefinition 表示 Bean 的定义, BeanDefinition 中存在很多属性来描述 Bean 的特征。比如:
- class, 表示 bean 的类型
- scope, 表示 bean 的作用域,单例(singleton)或者原型(prototype)
- lazyInit, 表示 bean 是否懒加载
- initMethodName, 表示 bean 的初始化需要执行的方法
- destoryMethodName, 表示 bean 销毁时需要执行 bean 的方法
- and more ...
在 Spring 中,我们可以通过一下几种方式来定义 bean 1、xml 方式,可以通过 标签定义一个 bean 2、注解方式,可以通过 @Bean、@Component(@Service, @Controller,@Repository) 这几种方式,我们称为** 申明式定义 Bean**
我们还可以通过编程式定义 bean, 那就是直接通过 BeanDefinition , 比如:
// 定义 BeanDefinition AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); // 设置 scope beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON); // 设置bean类型 beanDefinition.setBeanClass(BeanTest.class); // 设置懒加载 beanDefinition.setLazyInit(true); // 注册 bean applicationContext.registerBeanDefinition("test", beanDefinition);
和申明式事务,编程式事务类似,通过 、@Bean 、@Component 等申明方式所定义的 Bean , 最终都会被 Spring 解析为对应的 BeanDefinition 对象,并且放入 Spring 容器中。
BeanDefinitionReader
BeanDefinitionReader 是 Spring 容器中提供的 BeanDefinition 读取器,这些 BeanDefinitionReader 在我们使用 Spring 开的时候较少使用,但是 Spring 源码中使用的比较多,相当于是 Spring 内部的基础设施。
AnnotationBeanDefinitionReader
可以直接把 类转换为 BeanDefinition , 并且会解析类上的注解,例如:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotatedBeanDefinitionReaderTest.class); AnnotatedBeanDefinitionReader annotationBeanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext); annotationBeanDefinitionReader.register(UserService.class); System.out.println(applicationContext.getBean(UserService.class));
AnnotationBeanDefinitionReader
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(XmlBeanDefinitionReaderTest.class); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(applicationContext); xmlBeanDefinitionReader.loadBeanDefinitions("classpath:spring-context.xml"); System.out.println(applicationContext.getBean(UserService.class));
ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner 是扫描器,但是它的作用和 BeanDefinitionReader 类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在 @Component 注解,那么就会把这个类解析为一个 BeanDefinition ,比如:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ClassPathBeanDefinitionScannerTest.class); ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(applicationContext); classPathBeanDefinitionScanner.scan("com.summer.test.service"); System.out.println(applicationContext.getBean(UserService.class));
BeanFactory
BeanFactory 表示 Bean 工厂,所以很明显, BeanFactory 负责创建 Bean,并且提供获取 Bean 的 API。 而 SpringApplicationContext 是 BeanFactory 的子类,在 Spring 的源码中是这样定义的:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver { // ... }
首先,在 Java 中,接口可以**多继承, **ApplicationContext 继承了 ListableBeanFactory 和 HierarchicalBeanFactory , 而 ListableBeanFactory 和 HierarchicalBeanFactory 都继承自 BeanFactory , 所以我们可以认为 ApplicationContext 继承了 BeanFactory ,相当于是苹果继承了水果,宝马汽车继承了汽车一样, ApplicationContext 也是 BeanFactory 的一种,用友了 BeanFactory 支持的所有功能,不过 ApplicationContext 比 BeanFactory 更加强大, ApplicationContext 还实现了其他的基础接口。 比如 MessageSource 表示国际化, ApplicationEventPublisher 表示事件发布, EnvironmentCapable 表示获取环境变量,等等,关于 ApplicationContext 后面详细讨论。
在 Spring 源码的实现中,当我们 new 一个 ApplicationContext 时,其底层会 new 一个 BeanFactory 出来, 相当于使用了 ApplicationContext 的某些方法时,比如 getBean() , 底层调用的 BeanFactory 的 getBean 。
在 Spring 源码中, BeanFactory 接口存在一个非常重要的实现类:DefaultLIsttableBeanFactory, 也是非常核心的。
所以,我们可以直接使用 DefaultLIsttableBeanFactory , 而不使用 ApplicationContext 的某个实现类:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(UserService.class); beanFactory.registerBeanDefinition("userService", beanDefinition); System.out.println(beanFactory.getBean("userService"));
DefaultLIsttableBeanFactory 非常强大,我们可以监看它的继承关系:
从继承关系来看,它实现了很多接口,具备一下功能: 1、AliasRegistry: 支持别名功能,一个名字可以对应多个别名。 2、BeanDefinitionRegistry: 可以注册,保存,移除,获取某个 BeanDefinition 3、SingletonBeanFactory: 可以直接注册,获取一个单例 Bean 4、SimpleAliasRegistry: 它是一个雷,实现了 AliasRegistry 接口中所有的定义,支持别名功能 5、ListableBeanFactory: 在 BeanFactory 的基础上,增加了其他功能呢,可以获取所有的 BeanDefinition 的定义信息。
ApplicationContext
AnnotationConfigApplicationContext
ClassPathXmlApplicationContext
国际化
先定义个 MessageSource:
@Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("messages"); return messageSource; }
创建完这个 Bean , 我们可以在任何需要使用国际化的地方使用该 MessageSource 的 Bean 对象。 同时,因为 ApplicationContext 也拥有国际化功能,所以可以直接这么使用:
context.getMessage("test", null, new Locale("en"));
资源加载
ApplicationContext 还拥有资源加载的功能,比如,可以直接使用 ApplicationConext 获取某个文件的内容:
AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class); Resource resource = context.getResource("file://C:\\a.txt") System.out.println(resource.contextLength())
通过 ApplicationConext 来实现这个功能,可以提高我们的开发效率。 比如我们还可以这样使用:
AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class); Resource resource = context.getResource("classpath:spring-context.xml") System.out.println(resource.contextLength()) Resource resource2 = context.getResource("https://baidu.com"); System.out.println(resource2.getURL());
我们也可以获取多个资源
AnnotationConfigApplicationContext context = new AnnotaionConfigApplicaitonContext(AppConfig.class); Resource resource = context.getResource("classpath:spring-context.xml") System.out.println(resource.contextLength())
获取运行时环境变量
Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment(); System.out.println(systemEnvironment); Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties(); System.out.println(systemProperties); MutablePropertySources propertySources = context.getEnvironment().getPropertySources(); System.out.println(propertySources);
也可以解析文件
@PropertySource("classpath:spring.properties")
可以让某一个 properties 文件添加到我们的环境变量中,可以通过一下的代码来获取:
String abc = context.getEnvironment().getProperty("abc", "1"); System.out.println(abc);