文章目录
- Spring底层架构核心概念
- BeanDefinition
- BeanDefinitionReader
- AnnotatedBeanDefinitionReader
- XmlBeanDefinitionReader
- ClassPathBeanDefinitionScanner
- BeanFactory
- ApplicationContext
- 国际化
- 资源加载
- 获取运行时环境
- 事件发布
- 类型转换
- PropertyEditor
- ConversionService
- TypeConverter
- OrderComparator
- BeanPostProcess
- BeanFactoryPostProcessor
- FactoryBean
- ExcludeFilter和IncludeFilter
- MetadataReader、ClassMetadata、AnnotationMetadata
Spring底层架构核心概念
BeanDefinition
Bean定义,存在很多属性来描述一个Bean的特点。BeanDefinition
是一个接口
- beanClass:表示Bean的Class类型
- scope:表示Bean作用域,单例或原型等等
- lazyInit:是否为懒加载
- initMethodName:初始化时要执行的方法
- destroyMethodName:销毁时要执行的方法
- … …
声明式定义Bean:@Bean @Component
编程式定义Bean:
public static void main(String[] args) {// 定义Spring容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);// 定义BeanDefinitionAbstractBeanDefinition 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 BeanDefinitionReaderpublic class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReaderpublic class AnnotationConfigApplicationContext extends GenericApplicationContextpublic 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
可以解析标签
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();// 创建一个beanDefinitionAbstractBeanDefinition 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
@Beanpublic MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();// 指定文件名 messageSource.setBasename("errorMessage"); return messageSource;}
有了这个Bean我们就可以在任意要进行国际化的地方使用该MessageSource,调用messageSource.getMessage()
方法
@Autowiredprivate 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);}}
再定义一个事件监听器
@Beanpublic ApplicationListener applicationListener() {return new ApplicationListener() {@Overridepublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof EmailEvent){// 自定义逻辑System.out.println("接收到了一个Email事件:" + event.getSource());} else {System.out.println("接收到了一个事件:" + event.getSource());}}};}
然后发布一个事件
context.publishEvent("hushang");
除了使用ApplicationContext对象发布时间之外,在平时的工作中,比较常见的是使用依赖注入的方式发布事件
@Componentpublic class UserService {@Autowiredprivate OrderService orderService;@Autowiredprivate 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
@Componentpublic 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
@Beanpublic 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 {@Overridepublic boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {// 判断两个类型是否为String和Userreturn sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);}@Overridepublic Set<ConvertiblePair> getConvertibleTypes() {// 可转换的类型return Collections.singleton(new ConvertiblePair(String.class, User.class));}@Overridepublic 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
@Beanpublic 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 {@Overridepublic int getOrder() {return 3;}@Overridepublic String toString() {return this.getClass().getSimpleName();}}public class B implements Ordered {@Overridepublic int getOrder() {return 2;}@Overridepublic String toString() {return this.getClass().getSimpleName();}}
接下来进行排序比较
public class Main {public static void main(String[] args) {A a = new A(); // order=3B b = new B(); // order=2OrderComparator comparator = new OrderComparator();System.out.println(comparator.compare(a, b));// 1List 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 {@Overridepublic String toString() {return this.getClass().getSimpleName();}}@Order(2)public class B {@Overridepublic String toString() {return this.getClass().getSimpleName();}}
进行排序比较
public class Main {public static void main(String[] args) {A a = new A(); // order=3B b = new B(); // order=2AnnotationAwareOrderComparator comparator = new AnnotationAwareOrderComparator();System.out.println(comparator.compare(a, b)); // 1List 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:
@Componentpublic class MyFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic 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对象@Componentpublic class MyFactoryBean implements FactoryBean {@Overridepublic Object getObject() throws Exception {UserService userService = new UserService();return userService;}@Overridepublic 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();// 构造一个MetadataReaderMetadataReader 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技术就不需要进行类加载,直接去解析字节码文件