文章目录

  • 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分为:

  1. ANNOTATION:表示是否包含某个注解
  2. ASSIGNABLE_TYPE:表示是否是某个类
  3. ASPECTJ:表示否是符合某个Aspectj表达式
  4. REGEX:表示是否符合某个正则表达式
  5. CUSTOM:自定义

在Spring的扫描逻辑中,默认会添加一个AnnotationTypeFilterincludeFilters,表示默认情况下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技术就不需要进行类加载,直接去解析字节码文件