我们总经常听说Spring容器,那Spring容器到底是什么,在介绍创建Spring应用程序上下文之前,我们先来聊一聊Spring容器到底是什么。
容器顾名思义就是用来装东西的,装的是什么?装的是Bean。
Bean是Spring的基本单位,在基于Spring的web应用中,所有的组件都被当做bBan处理、包括数据源、Hiberate的SessionFactory、事务管理器等。在Spring中,Bean是一个非常广义的概念,任何java对象,Java组件都被当做Bean处理。
那容器仅仅是用来保存Bean这么简单么?不是。
当我们需要某个Bean时,容器会自动帮我们创建,并在适当时销毁。当某个 Bean 中需创建另一个 Bean 时,也就是 Bean 之间有依赖关系,这种依赖的 Bean 也是由容器自动创建。在外界有一个标准的名词,前者称呼为 IOC,也就是控制反转,后者称呼为 DI,也就是依赖注入。
IOC (Inversion of Control) 控制反转:所谓控制反转,就是当我们需要某个 Bean 时,将 Bean 的名称告知容器,由容器去创建该 Bean,而不是我们手动 new 一个,这里 Bean 创建管理的控制权都交给了容器,所以这是一种控制权的反转。其通俗点讲就是需要什么东西让别人送过来,而不是自己去拿。
DI (Dependency Injection) 依赖注入:就是指当 A Bean 里面需创建 B Bean 时,会在创建 A Bean 的时候,自动将依赖的 B Bean 注入进去,其 B Bean 是被动接受注入而不是自己主动去找。换句话说就是指 A Bean 不是从容器中查找它依赖的 B Bean,而是在容器创建 A Bean 候主动将它依赖的 B Bean 注入给它。
IOC 和 DI 其实归根结底实现的功能是相同的,只是同样的功能站在不同的角度来阐述罢了。当然,在真实场景中,交由 Spring 容器创建的 Bean 泛指在应用程序中的表现层、业务层、持久层等各层对应的 Bean,如 Controller、Service 等;进行数据交互的模型,如 DTO、VO 等就不需交由 Spring 来创建。
所以,容器本质上可以也可以看作是 Bean 工厂,该工厂管理 Bean 的生命周期,以及 Bean 之间的依赖关系。外界也将 Spring 容器称为 IOC 容器。当然,这里容器仅仅是 Spring 的抽象概念,代码中将其具象化为 BeanFactory 或 ApplicationContext,容器功能也由具象化的类进行处理。
容器的实现类并不是唯一的,Spring 框架提供了多个容器的实现,这些容器分为两套体系:一套是早期的 BeanFactory 体系;还有一套是现在常用的 ApplicationContext,也可称为应用上下文,它继承了 BeanFactory,它除了有 BeanFactory 的功能外 ,还提供了其他服务,例如事务和 AOP 服务、国际化(il8n)的消息源以及应用程序事件处理等企业级的服务。
说到这,就不得不说 Spring 的两种配置方式,在早期都是 XML 配置文件的方式,而现在使用的是注解配置的方式。BeanFactory 体系的容器一般用来处理 XML 配置文件的方式,而 ApplicationContext 体系则都可以处理。
下表列出了BeanFactory 和 ApplicationContext 接口和实现所提供的功能:
功能/特点 | BeanFactory | ApplicationContext |
Bean 实例化/装配 | 有 | 有 |
BeanPostProcessor 自动注册 | 没有 | 有 |
BeanFactoryPostProcessor 自动注册 | 没有 | 有 |
MessageSource 便捷访问(针对i18n) | 没有 | 有 |
ApplicationEvent 发布 | 没有 | 有 |
两者还有一个区别是:
BeanFactory 是容器最基础的类,它定义了容器的基本功能规范:
public interface BeanFactory { // 对 FactoryBean 的转义定义,因为如果使用 bean 的名字检索 FactoryBean 得到的对象是工厂生成的对象, // 如果需要得到工厂本身,需要转义(FactoryBean 在后续会详细介绍) String FACTORY_BEAN_PREFIX = "&"; // 根据 bean 的名字,获取在容器中 bean 实例 Object getBean(String name) throws BeansException; //根据 bean 的名字和 Class 类型来得到 bean 实例,增加了类型安全验证机制。 <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; // 提供对 bean 的检索,看看是否在容器有这个名字的 bean boolean containsBean(String name); // 根据 bean 名字,判断这个 bean 是不是单例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // 根据 bean 名字,判断这个 bean 是不是原型 boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 根据 bean 名字,判断是否与指定的类型匹配 boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException; // 得到 bean 实例的 Class 类型 Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 得到bean 的别名,如果根据别名检索,那么其原名也会被检索出来 String[] getAliases(String name); }
在 BeanFactory 里只对容器的基本行为作了定义,其根本不关心你的 Bean 是如何定义怎样加载的。 正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。而要知道工厂是如何产生对象的,我们就需要看具体的容器了,也就是 BeanFactory 的子类。
BeanFactory 体系中常用的实现类有:
以上三个是 BeanFactory 的直系亲属,这个三个直系亲属下面又派生了两个复杂的容器:
最后是核心容器:
DefaultListableBeanFactory:它实现了以上所有的接口,在 BeanFactory 体系中可以作为一个独立的容器使用。 BeanFactory 大致的继承关系如下:
但我们基本不单独使用 BeanFactory ,而是直接使用 ApplicationContext ,因为 ApplicationContext 包含了 BeanFactory。
上面说过 ApplicationContext 是 BeanFactory 子类,它不仅包含 BeanFactory 所有功能,还对其进行了扩展,而我们喜欢将 ApplicationContext 称为应用上下文,因为容器只是它的基本功能。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver { // 返回此应用程序上下文的唯一ID @Nullable String getId(); // 返回此上下文所属的应用程序名称 String getApplicationName(); // 返回应用上下文具像化的类名 String getDisplayName(); // 返回第一次加载此上下文时的时间戳 long getStartupDate(); // 获取父级应用上下文 @Nullable ApplicationContext getParent(); // 将 AutowireCapableBeanFactory 接口暴露给外部使用 AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; }
ApplicationContext 自身提供的方法非常简单,但它继承了六个接口,来扩展自身功能:
ApplicationContext 同样提供了非常多的实现类,其又可细分为两大类, ConfigurableApplicationContext 和 WebApplicationContext。
该接口是比较重要的一个接口,几乎所有的应用上下文都实现了该接口。该接口在ApplicationContext的基础上提供了配置应用上下文的能力,此外提供了生命周期的控制能力。
public
interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { // 应用上下文配置时,这些符号用于分割多个配置路径 String CONFIG_LOCATION_DELIMITERS = ",; \t\n"; // BeanFactory中,ConversionService类所对应的bean的名字。如果没有此类的实例的话吗,则使用默认的转换规则 String CONVERSION_SERVICE_BEAN_NAME = "conversionService"; //LoadTimeWaver类所对应的Bean在容器中的名字。如果提供了该实例,上下文会使用临时的 ClassLoader ,这样,LoadTimeWaver就可以使用bean确切的类型了 String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver"; // Environment 类在容器中实例的名字 String ENVIRONMENT_BEAN_NAME = "environment"; // System 系统变量在容器中对应的Bean的名字 String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties"; // System 环境变量在容器中对应的Bean的名字 String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment"; // 设置容器的唯一ID void setId(String id); // 设置此容器的父容器 void setParent(@Nullable ApplicationContext parent); // 设置容器的 Environment 变量 void setEnvironment(ConfigurableEnvironment environment); // 以 ConfigurableEnvironment 的形式返回此容器的环境变量。以使用户更好的进行配置 @Override ConfigurableEnvironment getEnvironment(); // 此方法一般在读取应用上下文配置的时候调用,用以向此容器中增加BeanFactoryPostProcessor。增加的Processor会在容器refresh的时候使用。 void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor); // 向容器增加一个 ApplicationListener,增加的 Listener 用于发布上下文事件,如 refresh 和 shutdown 等 void addApplicationListener(ApplicationListener<?> listener); // 向容器中注入给定的 Protocol resolver void addProtocolResolver(ProtocolResolver resolver); // 这是初始化方法,因此如果调用此方法失败的情况下,要将其已经创建的 Bean 销毁。 // 换句话说,调用此方法以后,要么所有的Bean都实例化好了,要么就一个都没有实例化 void refresh() throws BeansException, IllegalStateException; // 向JVM注册一个回调函数,用以在JVM关闭时,销毁此应用上下文 void registerShutdownHook(); // 关闭此应用上下文,释放其所占有的所有资源和锁。并销毁其所有创建好的 singleton Beans @Override void close(); // 检测此 FactoryBean 是否被启动过 boolean isActive(); // 返回此应用上下文的容器。 // 千万不要使用此方法来对 BeanFactory 生成的 Bean 做后置处理,因为单例 Bean 在此之前已经生成。 // 这种情况下应该使用 BeanFactoryPostProcessor 来在 Bean 生成之前对其进行处理 ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException; }
该接口下又有几个重要的实现类:
该接口是专门为 Web 应用准备的,其允许从相对于 Web 根目录的路径中装载配置文件完成初始化。
public interface WebApplicationContext extends ApplicationContext { // 整个 Web 应用上下文是作为属性放置在 ServletContext 中的,该常量就是应用上下文在 ServletContext 属性列表中的 key String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; // 定义了三个作用域的名称 String SCOPE_REQUEST = "request"; String SCOPE_SESSION = "session"; String SCOPE_APPLICATION = "application"; // 在工厂中的 bean 名称 String SERVLET_CONTEXT_BEAN_NAME = "servletContext"; // ServletContext 初始化参数名称 String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters"; // 在工厂中 ServletContext 属性值环境bean的名称 String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes"; // 用来获取 ServletContext 对象 @Nullable ServletContext getServletContext(); }
该接口的核心实现类有:
我们接着上一节继续分析run方法中的核心部分代码:应用上下文的创建。
public class SpringApplication { public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; ... private WebApplicationType webApplicationType; ... public ConfigurableApplicationContext run(String... args) { ... ConfigurableApplicationContext context = null; try { ... // 1、通过 createApplicationContext 方法创建对应的 ApplicationContext 应用上下文,进入 1.1 查看具体实现 context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 2、该方法实质是启动 Spring 应用上下文的,但 Spring Boot 嵌入式容器也在该过程中被启动,入参是上下文对象,我们进入 2.1 进行跟踪 refreshContext(context); ... } ... } // 1.1、 protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { // 这里就是通过 webApplicationType 属性,判断应用类型,来创建不同的 ApplicationContext 应用上下文 switch (this.webApplicationType) { case SERVLET: // 返回的是 Servlet Web ,具体对象为 AnnotationConfigServletWebServerApplicationContext, // 该类有一个关键父类 ServletWebServerApplicationContext contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: // 返回的是 Reactive Web,具体对象为 AnnotationConfigReactiveWebServerApplicationContext contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: // 应用类型是非 Web 时,返回 AnnotationConfigApplicationContext contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } ... } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); } // 2.1 private void refreshContext(ConfigurableApplicationContext context) { // 里面调用的是 refresh 方法,进入 2.2 继续跟踪 refresh(context); ... } // 2.2 protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); // 最终调用了 所有应用上下文的统一抽象类 AbstractApplicationContext 中的 refresh 方法,进入 3 查看实现 ((AbstractApplicationContext) applicationContext).refresh(); } ... }
这里也是通过 webApplicationType 属性来确定应用类型从而创建 String 上下文,上篇文章说到该属性值是在 Spring Boot 准备阶段推导出来的。这里我们的应用类型是 Servlet ,所以创建的是 AnnotationConfigServletWebServerApplicationContext 对象。
通过 BeanUtils.instantiateClass会调用AnnotationConfigServletWebServerApplicationContext 的无参构造函数,而在Java的继承中,会先调用父类的构造方法。所以会先调用AnnotationConfigServletWebServerApplicationContext 的父类GeniricApplicationContext的构造方法:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { private final DefaultListableBeanFactory beanFactory; @Nullable private ResourceLoader resourceLoader; private boolean customClassLoader = false; private final AtomicBoolean refreshed = new AtomicBoolean(); /** * Create a new GenericApplicationContext. * @see #registerBeanDefinition * @see #refresh */ public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); } ... }
在父类中初始化beanFactory,即直接new了一个DefaultListableBeanFactory:
/** * Create a new {@link AnnotationConfigServletWebServerApplicationContext} that needs * to be populated through {@link #register} calls and then manually * {@linkplain #refresh refreshed}. */ public AnnotationConfigServletWebServerApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
在构造函数中通过new AnnotatedBeanDefinitionReader(this)实例化了一个Bean读取器:
/** * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry. * <p>If the registry is {@link EnvironmentCapable}, e.g. is an {@code ApplicationContext}, * the {@link Environment} will be inherited, otherwise a new * {@link StandardEnvironment} will be created and used. * @param registry the {@code BeanFactory} to load bean definitions into, * in the form of a {@code BeanDefinitionRegistry} * @see #AnnotatedBeanDefinitionReader(BeanDefinitionRegistry, Environment) * @see #setEnvironment(Environment) */ public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } /** * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry, * using the given {@link Environment}. * @param registry the {@code BeanFactory} to load bean definitions into, * in the form of a {@code BeanDefinitionRegistry} * @param environment the {@code Environment} to use when evaluating bean definition * profiles. * @since 3.1 */ public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }
最后会调用到AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry,Object source)方法:
/** * Register all relevant annotation post processors in the given registry. * @param registry the registry to operate on * @param source the configuration source element (already extracted) * that this registration was triggered from. May be {@code null}. * @return a Set of BeanDefinitionHolders, containing all bean definitions * that have actually been registered by this call */ public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; }
这个方法向BeanDefinitionMap中添加了5个类:
/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
这几个类分别为ConfigurationClassPostProcessor、DefaultEventListenerFactory、EventListenerMethodProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor。
接着 调用this.scanner = new ClassPathBeanDefinitionScanner(this)来初始化一个扫描器,这个扫描器在后面扫描包的时候,并没有用到,
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
创建完 Spring 应用上下文之后,执行 prepareContext 方法进入准备上下文阶段:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }
我们来看看主要做了哪些操作:
1、设置了 Spring 应用上下文的 ApplicationArguments,上面说过是处理外部化配置的,具体类型为 StandardServletEnvironment 。
2、Spring 应用上下文后置处理,主要是覆盖当前 Spring 应用上下文默认所关联的 ResourceLoader 和 ClassLoader,以及初始化beanFactory的conversionService。
3、执行 Spring 的初始化器,上篇文章说过在 Spring Boot 准备阶段初始化了一批在 spring.factories 文件中定义好的 ApplicationContextInitializer ,这里就是执行它们的 initialize 方法,同时向应用上下文beanFactoryPostProcessors列表添加两个对象:
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
/** BeanFactoryPostProcessors to apply on refresh. */ private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();
4、执行 SpringApplicationRunListeners 的 contextPrepared 阶段方法,表示 ApplicationContext 准备完成,同时向 Spring Boot 监听器发布 ApplicationContextInitializedEvent 事件 。
5、将 springApplicationArguments 和 springBootBanner 注册为 Bean。
/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
6、加载 Spring 应用上下文的配置源到BeanDefinitionMap,也是在上篇文章 Spring Boot 准备阶段获取的 primarySources ,primarySources 来源于 SpringApplication 构造器参数;
7、最后执行 SpringApplicationRunListeners 的 contextLoaded 阶段方法,表示 ApplicationContext 完成加载但还未启动,同时向 Spring Boot 监听器发布 ApplicationPreparedEvent 事件 。
接下来就是真正启动阶段,执行的是 refreshContext 方法:
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } }
这里,最终也是调用 ApplicationContext 的 refresh 方法来启动上下文:
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
可以看到,底层调用的是 AbstractApplicationContext 的 refresh 方法,
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 初始化 refresh 的上下文环境,就是记录下容器的启动时间、标记已启动状态、处理配置文件中的占位符 prepareRefresh(); // 2. 初始化 BeanFactory,加载并解析配置 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); /* ---至此,已经完成了简单容器的所有功能,下面开始对简单容器进行增强--- */ // 3. 对 BeanFactory 进行功能增强,如设置BeanFactory的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean prepareBeanFactory(beanFactory); try { // 4. 后置处理 beanFactory,添加后置处理器 postProcessBeanFactory(beanFactory); // 5. 调用已注册的 BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // 6. 注册 BeanPostProcessor,仅仅是注册,调用在getBean的时候 registerBeanPostProcessors(beanFactory); // 7. 初始化国际化资源 initMessageSource(); // 8. 初始化事件广播器 initApplicationEventMulticaster(); // 9. 留给子类实现的模板方法 onRefresh(); // 10. 注册事件监听器 registerListeners(); // 11. 实例化所有非延迟加载的单例 finishBeanFactoryInitialization(beanFactory); // 12. 完成刷新过程,发布应用事件 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex); } // 13.销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源 this.destroyBeans(); // Reset ‘active‘ flag. this.cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring‘s core, since we // might not ever need metadata for singleton beans anymore... this.resetCommonCaches(); } } }
我们先从 refresh 中的 prepareRefresh 方法开始讨论:
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { ... @Override protected void prepareRefresh() { // 清除 Class 的元数据缓存。底层用 Map 保存元数据,执行 Map 的 clear 方法 this.scanner.clearCache(); // 调用父类,也就是 AbstractApplicationContext 的 prepareRefresh 方法 super.prepareRefresh(); } ... }
public abstract class AbstractApplicationContext { ... private long startupDate; private final AtomicBoolean active = new AtomicBoolean(); private final AtomicBoolean closed = new AtomicBoolean(); private Set<ApplicationEvent> earlyApplicationEvents; ... protected void prepareRefresh() { // 记录此上下文开始时的系统时间(以毫秒为单位) this.startupDate = System.currentTimeMillis(); // 记录此上下文是否已关闭,这里设置为未关闭 this.closed.set(false); // 记录此上下文是否处于活动状态,这里设置为活动状态 this.active.set(true); if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // 这也是交由子类扩展的方法。具体子类为 GenericWebApplicationContext,主要是初始化属性源, // 将 ServletContext 和 ServletConfig 属性配置添加到 Environment 环境上下文中 initPropertySources(); // 校验 Environment 中那些必备的属性配置是否存在,不存在则抛异常。 getEnvironment().validateRequiredProperties(); // 创建 ApplicationEvent 事件集合 this.earlyApplicationEvents = new LinkedHashSet<>(); } }
refresh 中的 prepareRefresh 方法执行结束,主要是记录容器的启动时间、活动状态、检查必备属性是否存在。
接着进入 refresh 中的 obtainFreshBeanFactory 方法:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 该方法也是由子类扩展,其子类有 AbstractRefreshableApplicationContext 和 GenericApplicationContext, // 因当前是 Servlet Web 应用,所以执行的是 GenericApplicationContext 中的 refreshBeanFactory 方法。 // 该方法主要设置 BeanFactory 的 serializationId 属性值,也就是序列化id refreshBeanFactory(); // 通过 getBeanFactory 返回 BeanFactory 对象。同样也是由子类扩展,调用的是 GenericApplicationContext 类中的 getBeanFactory 方法。 // 返回的是 DefaultListableBeanFactory 。 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
之后,该方法还返回了 BeanFactory 对象,从这也可以看出 ApplicationContext 底层是以 BeanFactory 为基础,逐步扩展 Spring 容器功能。
接着进入 refresh 中的 prepareBeanFactory 方法。prepareBeanFactory 方法主要是对 BeanFactory 做一些配置,包含各种类加载器、需要忽略的依赖以及后置处理器、解析器等:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 设置类加载器 beanFactory.setBeanClassLoader(getClassLoader()); // 设置表达式解析器,主要用来解析 EL 表达式; Bean 初始化完成后填充属性时会用到 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); // 设置属性注册解析器,主要用来解析 Bean 中的各种属性类型,如 String、int 等 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // 添加一个后置处理器:ApplicationContextAwareProcessor。 // 该后置处理器用于向实现了 Aware 系列接口的 bean 设置相应属性。 // (后置处理器和 Aware 接口也是比较核心的概念) beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 以下接口,在自动注入时会被忽略,其都是 Aware 系列接口 beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // 当以下特殊的 Bean 需自动注入时,指定其注入的类型 。 // 如:注入 BeanFactory 时,注入的类型对象为 ConfigurableListableBeanFactory 。 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // 添加 ApplicationListenerDetector 后置处理器。 // 该后置处理器用来检测那些实现了 ApplicationListener 接口的 bean,并将其添加到应用上下文的事件广播器上。 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // 判断容器中是否存在 loadTimeWeaver Bean,如果存在则上下文使用临时的 ClassLoader 进行类型匹配。 // 集成 AspectJ 时会用到 loadTimeWeaver 对象。 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // 注册和环境相关的 Bean,如 environment、systemProperties、systemEnvironment if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
在 prepareBeanFactory 方法中,主要对 BeanFactory 添加了一系列属性项,如添加忽略自动注入的接口、添加 BeanPostProcessor 后置处理器、手动注册部分特殊的 Bean及环境相关的 Bean 。
/** * Dependency interfaces to ignore on dependency check and autowire, as Set of * Class objects. By default, only the BeanFactory interface is ignored. */ private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
/** BeanPostProcessors to apply in createBean. */ private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();
/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
postProcessBeanFactory 方法是上下文准备的最后一步,主要用来注册 Web 请求相关的处理器、Bean及配置。
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner; private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>(); private String[] basePackages; ... @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 先执行父类 ServletWebServerApplicationContext 的 postProcessBeanFactory 方法。 // 跳转到 1 查看父类实现 super.postProcessBeanFactory(beanFactory); // basePackages 存储的是类路径。先判断是否为 null,不为 null 则通过 ClassPathBeanDefinitionScanner 的 scan 方法 // 扫描该路径下符合条件的 Class,并将 Class 信息包装成 BeanDefinition 注册到容器中, // 当然,这里没有指定扫描路径,所以不会进入这个 if。 // (BeanDefinition 概念会在后面章节详细讨论) if (this.basePackages != null && this.basePackages.length > 0) { this.scanner.scan(this.basePackages); } // annotatedClasses 存储的 Class 集合。先判断该集合是否为空,不为空则通过 // AnnotatedBeanDefinitionReader 的 register 方法将 Class 信息包装成 BeanDefinition 注册到容器中, // 这里同样没有设置 Class 集合内容,所以不会进入这个 if。 if (!this.annotatedClasses.isEmpty()) { this.reader.register(ClassUtils.toClassArray(this.annotatedClasses)); } } } //1 public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { ... @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 添加 BeanPostProcessor 后置处理器:WebApplicationContextServletContextAwareProcessor, // 该后置处理器主要是从 ConfigurableWebApplicationContext 上下文中获取 ServletContext 和 ServletConfig 对象 beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this)); // 添加一个 忽略自动注入的接口 beanFactory.ignoreDependencyInterface(ServletContextAware.class); } ... }
postProcessBeanFactory 方法执行的操作和前面类似,也是添加了后置处理器和忽略自动注入的接口。
原计划是对接下来的 invokeBeanFactoryPostProcessors 方法进行讨论,但该方法涉及 Spring 中一个非常重要的概念: BeanDefinition,所以,这里先对 BeanDefinition 进行讨论,这样也有利于完善 Spring 的知识体系。
现如今,我们一般获取对象的方式有两种:
BeanDefinition 用于保存 Bean 的相关信息,包括属性、构造方法参数、依赖的 Bean 名称及是否单例、延迟加载等,它是实例化 Bean 的原材料,Spring 就是根据 BeanDefinition 中的信息实例化 Bean。
BeanDefinition 是一个接口,它有多个实现类,这些实现类分别描述不同类型的 Bean。
一个 BeanDefinition 描述了一个 Bean 实例,实例包含属性值、构造方法参数值以及更多实现信息。该 BeanDefinition 只是是一个最小的接口,主要目的是允许修改属性值和其他 Bean 元数据,这里列出几个核心方法。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { // 单例、原型标识符 String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; // 标识 Bean 的类别,分别对应 用户定义的 Bean、来源于配置文件的 Bean、Spring 内部的 Bean int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; // 设置、返回 Bean 的父类名称 void setParentName(@Nullable String parentName); String getParentName(); // 设置、返回 Bean 的 className void setBeanClassName(@Nullable String beanClassName); String getBeanClassName(); // 设置、返回 Bean 的作用域 void setScope(@Nullable String scope); String getScope(); // 设置、返回 Bean 是否懒加载 void setLazyInit(boolean lazyInit); boolean isLazyInit(); // 设置、返回当前 Bean 所依赖的其它 Bean 名称。 void setDependsOn(@Nullable String... dependsOn); String[] getDependsOn(); // 设置、返回 Bean 是否可以自动注入。只对 @Autowired 注解有效 void setAutowireCandidate(boolean autowireCandidate); boolean isAutowireCandidate(); // 设置、返回当前 Bean 是否为主要候选 Bean 。 // 当同一个接口有多个实现类时,通过该属性来配置某个 Bean 为主候选 Bean。 void setPrimary(boolean primary); boolean isPrimary(); // 设置、返回创建该 Bean 的工厂类。 void setFactoryBeanName(@Nullable String factoryBeanName); String getFactoryBeanName(); // 设置、返回创建该 Bean 的工厂方法 void setFactoryMethodName(@Nullable String factoryMethodName); String getFactoryMethodName(); // 返回该 Bean 构造方法参数值、所有属性 ConstructorArgumentValues getConstructorArgumentValues(); MutablePropertyValues getPropertyValues(); // 返回该 Bean 是否是单例、是否是非单例、是否是抽象的 boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); // 返回 Bean 的类别。类别对应上面的三个属性值。 int getRole(); ... }
可以看到 BeanDefinition 接口提供了一系列操作 Bean 元数据的set、get方法,这些操作为 Bean 的描述定义了一套模板,具体的实现则交由子类。
AnnotatedBeanDefinition 是 BeanDefinition 子接口之一,该接口扩展了 BeanDefinition 的功能,其用来操作注解元数据。一般情况下,通过注解方式得到的 Bean(@Component、@Bean),其 BeanDefinition 类型都是该接口的实现类。
public interface AnnotatedBeanDefinition extends BeanDefinition { // 获得当前 Bean 的注解元数据 AnnotationMetadata getMetadata(); // 获得当前 Bean 的工厂方法上的元数据 MethodMetadata getFactoryMethodMetadata(); }
该接口可以返回两个元数据的类:
AbstractBeanDefinition 是 BeanDefinition 的子抽象类,也是其他 BeanDefinition 类型的基类,其实现了接口中定义的一系列操作方法,并定义了一系列的常量属性,这些常量会直接影响到 Spring 实例化 Bean 时的策略。核心属性如下。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable { // 默认的 SCOPE,默认是单例 public static final String SCOPE_DEFAULT = ""; // 不进行自动装配 public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO; // 根据 Bean 的名字进行自动装配,byName public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME; // 根据 Bean 的类型进行自动装配,byType public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE; // 根据构造器进行自动装配 public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR; // 首先尝试按构造器自动装配。如果失败,再尝试使用 byType 进行自动装配。(Spring 3.0 之后已废除) public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT; // 通过依赖检查来查看 Bean 的每个属性是否都设置完成 // 以下常量分别对应:不检查、对依赖对象检查、对基本类型,字符串和集合进行检查、对全部属性进行检查 public static final int DEPENDENCY_CHECK_NONE = 0; public static final int DEPENDENCY_CHECK_OBJECTS = 1; public static final int DEPENDENCY_CHECK_SIMPLE = 2; public static final int DEPENDENCY_CHECK_ALL = 3; // 关闭应用上下文时需调用的方法名称 public static final String INFER_METHOD = "(inferred)"; // 存放 Bean 的 Class 对象 private volatile Object beanClass; // Bean 的作用范围 private String scope = SCOPE_DEFAULT; // 非抽象 private boolean abstractFlag = false; // 非延迟加载 private boolean lazyInit = false; // 默认不自动装配 private int autowireMode = AUTOWIRE_NO; // 默认不依赖检查 private int dependencyCheck = DEPENDENCY_CHECK_NONE; // 依赖的 Bean 列表 private String[] dependsOn; // 可以作为自动装配的候选者,意味着可以自动装配到其他 Bean 的某个属性中 private boolean autowireCandidate = true; // 创建当前 Bean 实例工厂类名称 private String factoryBeanName; // 创建当前 Bean 实例工厂类中方法名称 private String factoryMethodName; // 存储构造方法的参数 private ConstructorArgumentValues constructorArgumentValues; // 存储 Bean 属性名称以及对应的值 private MutablePropertyValues propertyValues; // 存储被覆盖的方法信息 private MethodOverrides methodOverrides; // init、destroy 方法名称 private String initMethodName; private String destroyMethodName; // 是否执行 init 和 destroy 方法 private boolean enforceInitMethod = true; private boolean enforceDestroyMethod = true; // Bean 是否是用户定义的而不是应用程序本身定义的 private boolean synthetic = false; // Bean 的身份类别,默认是用户定义的 Bean private int role = BeanDefinition.ROLE_APPLICATION; // Bean 的描述信息 private String description; // Bean 定义的资源 private Resource resource; ... }
以上是 AbstractBeanDefinition 中定义的一些常量和属性,该类中还有一部分是操作这些属性的 set 和 get 方法,这些方法都由子类来操作,且应用程序中真正使用的也是这些子类 BeanDefinition。 先来看 AbstractBeanDefinition 直接实现类:RootBeanDefinition、GenericBeanDefinition、ChildBeanDefinition。
该类继承自 AbstractBeanDefinition,它可以单独作为一个 BeanDefinition,也可以作为其他 BeanDefinition 的父类。 RootBeanDefinition 在 AbstractBeanDefinition 的基础上定义了更多属性。
public class RootBeanDefinition extends AbstractBeanDefinition { // BeanDefinitionHolder 存储 Bean 的名称、别名、BeanDefinition private BeanDefinitionHolder decoratedDefinition; // AnnotatedElement 是java反射包的接口,通过它可以查看 Bean 的注解信息 private AnnotatedElement qualifiedElement; // 允许缓存 boolean allowCaching = true; // 工厂方法是否唯一 boolean isFactoryMethodUnique = false; // 封装了 java.lang.reflect.Type,提供了泛型相关的操作 volatile ResolvableType targetType; // 缓存 Class,表示 RootBeanDefinition 存储哪个类的信息 volatile Class<?> resolvedTargetType; // 缓存工厂方法的返回类型 volatile ResolvableType factoryMethodReturnType; // 这是以下四个构造方法字段的通用锁 final Object constructorArgumentLock = new Object(); // 用于缓存已解析的构造方法或工厂方法 Executable resolvedConstructorOrFactoryMethod; // 将构造方法参数标记为已解析 boolean constructorArgumentsResolved = false; // 用于缓存完全解析的构造方法参数 Object[] resolvedConstructorArguments; // 缓存待解析的构造方法参数 Object[] preparedConstructorArguments; // 这是以下两个后处理字段的通用锁 final Object postProcessingLock = new Object(); // 表明是否被 MergedBeanDefinitionPostProcessor 处理过 boolean postProcessed = false; // 在生成代理的时候会使用,表明是否已经生成代理 volatile Boolean beforeInstantiationResolved; // 实际缓存的类型是 Constructor、Field、Method 类型 private Set<Member> externallyManagedConfigMembers; // InitializingBean中 的 init 回调函数名 afterPropertiesSet 会在这里记录,以便进行生命周期回调 private Set<String> externallyManagedInitMethods; // DisposableBean 的 destroy 回调函数名 destroy 会在这里记录,以便进生命周期回调 private Set<String> externallyManagedDestroyMethods; ... }
该类继承自 AbstractBeanDefinition。其相当于一个子类,不可以单独存在,必须依赖一个父 BeanDetintion,构造 ChildBeanDefinition 时,通过构造方法传入父 BeanDetintion 的名称或通过 setParentName 设置父名称。它可以从父类继承方法参数、属性值,并可以重写父类的方法,同时也可以增加新的属性或者方法。若重新定义 init 方法,destroy 方法或者静态工厂方法,ChildBeanDefinition 会重写父类的设置。
从 Spring 2.5 开始,以编程方式注册 Bean 定义的首选方法是 GenericBeanDefinition,GenericBeanDefinition 可以有效替代 ChildBeanDefinition 的绝大分部使用场合。
GenericBeanDefinition 是 Spring 2.5 以后新引入的 BeanDefinition,是 ChildBeanDefinition 更好的替代者,它同样可以通过 setParentName 方法设置父 BeanDefinition。
该类继承自 RootBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述在标注 @Configuration 注解的类中,通过 @Bean 注解实例化的 Bean。
其功能特点如下:
1、如果 @Bean 注解没有指定 Bean 的名字,默认会用方法的名字命名 Bean。
2、标注 @Configuration 注解的类会成为一个工厂类,而标注 @Bean 注解的方法会成为工厂方法,通过工厂方法实例化 Bean,而不是直接通过构造方法初始化。
3、标注 @Bean 注解的类会使用构造方法自动装配;
该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Configuration 注解的 Bean。
该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。 这个 BeanDefinition 用来描述标注 @Component 注解的 Bean,其派生注解如 @Service、@Controller 也同理。
最后,我们来做个总结。BeanDefinition 主要是用来描述 Bean,其存储了 Bean 的相关信息,Spring 实例化 Bean 时需读取该 Bean 对应的 BeanDefinition。BeanDefinition 整体可以分为两类,一类是描述通用的 Bean,还有一类是描述注解形式的 Bean。一般前者在 XML 时期定义 <bean> 标签以及在 Spring 内部使用较多,而现今我们大都使用后者,通过注解形式加载 Bean。
参考文章:
[1]Spring(二)核心容器 - 简介 、BeanFactory、ApplicationContext(转载)
[2]Spring(三)核心容器 - ApplicationContext 上下文启动准备(转载)
[3]Spring(四)核心容器 - BeanDefinition 解析(转载)
原文:https://www.cnblogs.com/zyly/p/13197783.html