Spring底层架构核心概念
文章目录
Spring底层架构核心概念
BeanDefinition
Bean定义,存在很多属性来描述一个Bean的特点。BeanDefinition
是一个接口
- beanClass:表示Bean的Class类型
- scope:表示Bean作用域,单例或原型等等
- lazyInit:是否为懒加载
- initMethodName:初始化时要执行的方法
- destroyMethodName:销毁时要执行的方法
- … …
声明式定义Bean:@Bean @Component <Bean/>
编程式定义Bean:
public static void main(String[] args) {
// 定义Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// 定义BeanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
beanDefinition.setScope("singleton");
// 注册进容器中
applicationContext.registerBeanDefinition("userService", beanDefinition);
}
误区:
- 在java配置类中并不是加了
@Bean
注解的方法就立刻会执行,而是会先生成一个BeanDefinition对象,再之后遍历找出非懒加载的单例Bean再去执行方法。
BeanDefinition
是一个接口
AbstractBeanDefinition
抽象类实现了BeanDefinition
接口
GenericBeanDefinition
类继承了AbstractBeanDefinition
抽象类,比较常见的类
RootBeanDefinition
类继承了AbstractBeanDefinition
抽象类,它跟合并BeanDefinition有关
ScannedGenericBeanDefinition
类继承了GenericBeanDefinition
类,ClassPathBeanDefinitionScanner的scan()方法创建的是该类
AnnotatedGenericBeanDefinition
类继承了GenericBeanDefinition
类,AnnotatedBeanDefinitionReader的register()方法创建的是该类
BeanDefinitionReader
BeanDefinition的读取器
我们有多种方式定义Bean,XML或者是注解,那么也就需要不同的类去读取解析这些内容并生成BeanDefinition,再存入Spring容器中,所以就定义了一个规范BeanDefinitionReader
接口
该接口有三个实现
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader
public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader
public class AnnotationConfigApplicationContext extends GenericApplicationContext
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader
AnnotatedBeanDefinitionReader
能直接将某个类转换为BeanDefinition,并解析类上的注解
它能解析的注解是:@Conditional、@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext);
annotatedBeanDefinitionReader.registerBean(User.class);
System.out.println(applicationContext.getBean("user"));
我们也可以直接使用applicationContext.register()
方法注册,它底层实际上就是调用的AnnotatedBeanDefinitionReader.register()
方法
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
扫描器,作用和BeanDefinitionReader类似,可以扫描某个包路径下的类。
比如扫描到的类上如果存在@Component这一类注解,那么就会把这个类解析为一个BeanDefinition
// 构造方法中没有指定配置类,也就没有包扫描路径
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.scan("com.hs");
System.out.println(context.getBean("userService"));
我们也可以直接使用applicationContext.scan()
方法执行包扫描路径,它底层实际上就是调用的ClassPathBeanDefinitionScanner.scan()
方法
BeanFactory
Bean工厂,复制创建Bean,并提供获取Bean的API方法。
而ApplicationContext也是BeanFactory的一种,他们都是接口,ApplicationContext继承了BeanFactory。但ApplicationContext还继承了很多其他的接口,它的功能更强大。比如MessageSource表示国际化,ApplicationEventPublisher表示事件发布,EnvironmentCapable表示获取环境变量,等等
当我们new一个ApplicationContext,其底层会new一个BeanFactory出来,当我们调用getBean()方法时实际上底层也是调用的BeanFactory的getBean()方法。
BeanFactory接口有一个很重要的实现类:DefaultListableBeanFactory
// 直接使用DefaultListableBeanFactory,而不是ApplicationContext的某个实现类
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建一个beanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
// 注册进BeanFactory中
beanFactory.registerBeanDefinition("user", beanDefinition);
System.out.println(beanFactory.getBean("user"));
DefaultListableBeanFactory它实现了很多接口,增加了很多功能
ApplicationContext
它也是一个BeanFactory,它还继承了一些其他的接口 有了一些其他的功能:
- EnvironmentCapable,可以获取运行时环境,但没有设置的功能
- ListableBeanFactory,拥有获取beanNames的功能
- HierarchicalBeanFactory,分层,拥有获取父BeanFactory的功能
- MessageSource,国际化功能
- ApplicationEventPublisher,拥有广播事件的功能,ApplicationContext没有添加事件监听器的功能
- ResourcePatternResolver,资源加载器,可以一次性获取多个资源(文件资源等等)
其中ApplicationContext有两个重要的实现类:AnnotationConfigApplicationContext和ClassPathXmlApplicationContext
现在比较常用的是AnnotationConfigApplicationContext,这两个实现类就不过多分析了。
国际化
我们首先创建一个国际化文件
指定文件名和语言
然后就有了这样的三个文件,文件名为errorMessage,语言有三种 en/sc/tc
定义一个Bean
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
// 指定文件名
messageSource.setBasename("errorMessage");
return messageSource;
}
有了这个Bean我们就可以在任意要进行国际化的地方使用该MessageSource,调用messageSource.getMessage()
方法
@Autowired
private MessageSource messageSource;
public void test(){
System.out.println(messageSource.getMessage("SYS001", null, new Locale("en")));
}
因为ApplicationContext也有国际化功能,我们也可以在类中实现ApplicationContextAware
得到ApplicationContext对象,再这样使用
System.out.println(applicationContext.getMessage("SYS001", null, new Locale("en")));
资源加载
很多东西资源,比如文件资源,网络资源,比如可以通过ApplicationContext获取某个文件的内容,或者是网络资源
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Resource resource = context.getResource("file://D:\project\tuling\src\main\java\com\zhouyu\aspect\ZhouyuAspect.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());
}
在Spring底层源码中,就可以通过该功能获取xml配置文件中的内容,还有包扫描。
获取运行时环境
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("=======");
// 这个是比较全的,包含了上面两种以及properties配置文件中的内容
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
System.out.println(propertySources);
System.out.println("=======");
// 上面打印的内容其实都是获取的Environment对象中内容,我们也可以直接获取特定的配置
System.out.println(context.getEnvironment().getProperty("JAVA_HOME"));
System.out.println(context.getEnvironment().getProperty("sun.jnu.encoding"));
// 在配置类上面使用 @PropertySource("classpath:spring.properties") 加载某个properties文件
System.out.println(context.getEnvironment().getProperty("这里就可以获取properties文件中的配置项"));
输出结果如下图所示
事件发布
在Spring框架中,有三个关键类接口
分别是
- 事件抽象类:ApplicationEvent
- 事件发布者接口:ApplicationEventPublisher
- 事件监听接口:ApplicationListenr
我们先定义一个事件,创建一个类,继承ApplicationEvent
,然后重写构造方法
public class EmailEvent extends ApplicationEvent {
public EmailEvent(Object source) {
super(source);
}
}
再定义一个事件监听器
@Bean
public ApplicationListener applicationListener() {
return new ApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof EmailEvent){
// 自定义逻辑
System.out.println("接收到了一个Email事件:" + event.getSource());
} else {
System.out.println("接收到了一个事件:" + event.getSource());
}
}
};
}
然后发布一个事件
context.publishEvent("hushang");
除了使用ApplicationContext对象发布时间之外,在平时的工作中,比较常见的是使用依赖注入的方式发布事件
@Component
public class UserService {
@Autowired
private OrderService orderService;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void test(){
System.out.println("test()...");
EmailEvent emailEvent = new EmailEvent("hs");
applicationEventPublisher.publishEvent(emailEvent);
}
}
此时会输出两遍,这是因为Spring在启动时就会发布一个事件。
接收到了一个事件:org.springframework.context.annotation.AnnotationConfigApplicationContext@7a8119...
test()...
接收到了一个Email事件:hs
类型转换
在Spring的源码中,关于类型转换的场景会很常见,比如就很有可能需要把String转换为其他类型。
比如我现在有一个User类
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
我现在先把String转换为User
@Component
public class UserService {
// 这里能够直接赋值到User对象中的那么name属性中
@Value("hushang")
private User user;
public void test(){
System.out.println("test()...");
}
}
这是会报错的,因为不能把String转换为User,我们需要进行类型转换相关的操作
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;
}
现在就能正常注入了
@Value("hushang")
private User user;
ConversionService
Spring中提供的类型转化服务,它比PropertyEditor更强大
创建一个类
public class StringToUserConverter implements ConditionalGenericConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
// 判断两个类型是否为String和User
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中注册PropertyEditor
@Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));
return conversionServiceFactoryBean;
}
TypeConverter
整合了PropertyEditor和ConversionService的功能,Spring底层源码中使用的是TypeConverter,因为它前两种转换都支持,Spring也不确定我们程序员会使用前两种的哪一种类型转换器,所以Spring就直接使用TypeConverter,将我们定义的类型转换器收集起来,然后直接调用convertIfNecessary()
方法转换即可
整合PropertyEditor方式
public static void main(String[] args) {
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
// 自定义的StringToUserPropertyEditor类
typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
User value = typeConverter.convertIfNecessary("1", User.class);
System.out.println(value);
}
整合了ConversionService方式
public static void main(String[] args) {
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
// 自定义的StringToUserConverter类
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
typeConverter.setConversionService(conversionService);
User value = typeConverter.convertIfNecessary("1", User.class);
System.out.println(value);
}
OrderComparator
OrderComparator是Spring所提供的一种比较器,可以用来根据@Order
注解或实现Ordered
接口来执行值进行比较,从而可以进行排序。
实现Ordered
接口的方式
// 先定义两个类 都实现Ordered接口,在getOrder()方法中指定要进行排序的数值
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
}
}
根据@Order
注解的方式
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
}
}
BeanPostProcess
Bean的后置处理器,我们可以定义多个BeanPostProcessor,在创建Bean时,每个Bean都会执行这其中的前置和后置方法,我们也可以加if来判断给特定某个Bean执行某些特定的方法。
我们可以自定义一个类,实现BeanPostProcessor接口,重写接口的抽象方法,然后把该类注册进Spring容器中
我们可以通过定义BeanPostProcessor来干涉Spring创建Bean的过程。
Spring框架的扩展性主要就是体现在PostProcessor中。
BeanFactoryPostProcessor
Bean工厂的后置处理器,和BeanPostProcessor类似,只不过他们针对的对象不一样,一个是Bean,一个的BeanFactory。
BeanFactoryPostProcessor是干涉BeanFactory的创建过程。比如,我们可以这样定义一个BeanFactoryPostProcessor:
@Component
public class MyFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("加工beanFactory");
}
}
典型的引用就是从配置文件中读取出来的${jdbc.username}
配置项,刚开始生成BeanDefinition时值还是这个字符串,经过BeanFactoryPostProcessor之后才会替换为具体的配置项
FactoryBean
Spring中一个Bean的创建过程有很多的步骤,如果我们想要一个Bean完全由我们自己来创建就可以使用FactoryBean机制。
// 刚开始下面会创建一个BeanName为myFactoryBean的bean对象,对应的value是MyFactoryBean类型的对象
// 当getBean()方法调用的时候就会判断是否实现了FactoryBean接口,然后返回的是getObject()方法返回的Bean对象
@Component
public class MyFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
UserService userService = new UserService();
return userService;
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
我们自己创建了一个UserService的Bean对象,这种方式创建的bean对象只会经过初始化后的步骤,依赖注入和初始化哪些就不会有了。
而普通@Bean
注解方式创建的Bean对象就会走完成的创建流程。虽然这两种方式都是在方法中直接new一个对象并返回。
ExcludeFilter和IncludeFilter
Spring在包扫描过程中使用的,ExcludeFilter表示排除过滤器,IncludeFilter表示包含过滤器。
案例
扫描com.hs包下面所有类,但是排除UserService类,也就是就算它上面有@Component
注解也不会成为Bean。
@ComponentScan(value = "com.hs",
excludeFilters = {@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = UserService.class)})
public class AppConfig {
}
扫描com.hs包下面所有类,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.hs.service.UserService");
// 得到一个ClassMetadata,并获取了类名
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println(classMetadata.getClassName());
// 获取一个AnnotationMetadata,并获取类上的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 判断类上面是否存在@Component注解
annotationMetadata.hasMetaAnnotation(Component.class.getName())
for (String annotationType : annotationMetadata.getAnnotationTypes()) {
System.out.println(annotationType);
}
}
}
SimpleMetadataReader去解析类时,使用的ASM技术。
为什么要使用ASM技术?
我们可以把一个字节码先通过类加载器加载之后,得到Class对象,再去解析,但这种方式不太好。
Spring启动的时候需要去扫描,如果指定的包路径比较宽泛,那么扫描的类是非常多的,那如果在Spring启动时就把这些类全部加载进JVM了,这样不太好,因为JVM的类加载是在类要使用时才会加载,而现在都没有使用就都加载了,所以使用了ASM技术。
ASM技术就不需要进行类加载,直接去解析字节码文件