Spring启动流程

  • 话外音
  • 环境准备
  • 整体流程简图
  • AnnotationConfigApplicationContext
    • 父类初始化 DefaultListableBeanFactory
  • 1、this()
    • AnnotatedBeanDefinitionReader初始化
      • AnnotationConfigUtils.registerAnnotationConfigProcessors
      • 总结
    • ClassPathBeanDefinitionScanner初始化
      • registerDefaultFilters
      • 总结
      • 疑问:如何扫描@Service、@Controller等注解
    • this方法总结
  • 2、register(componentClasses)
  • 3、refresh
    • prepareRefresh
    • obtainFreshBeanFactory
    • prepareBeanFactory(beanFactory)
    • postProcessBeanFactory
    • invokeBeanFactoryPostProcessors(beanFactory);
      • 重点:beanFactoryPostProcessors加载的优先级判断
      • ConfigurationClassPostProcessor的加载
      • 总结
    • registerBeanPostProcessors(beanFactory);
    • initMessageSource();
    • initApplicationEventMulticaster();
    • onRefresh();
    • registerListeners();
    • finishBeanFactoryInitialization(beanFactory);
    • finishRefresh();

话外音

  • 真是受益良多啊,开始是想做个系统的整理与复习,然后思考了一下某个地方是如何实现,以及自己该怎么讲述这些东西;
  • 于是开始代码 debug,不知道不觉就陷入了大山,越走越深,不清楚的越来越多,又一个接一个了解清楚,然后,开始循环了……
  • 几个小时后,猛然抬头,啊这…… 好像和我 debug 的初衷有点儿对不上啊……
  • 每每念及于此,只能再次对 Spring 这座大山……膜拜啊……

环境准备

废话少说,开始吧!下载Spring5.x源码,导入Idea并构建,创建包扫描类。

@Configuration
@ComponentScan("com.slc")
public class AppConfig {}class StartApplication {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);//Person类在另外一个文件里面,简单用@Component标注即可,此处不展示Person bean = context.getBean(Person.class);}
}

启动项目,成功打印 Person 类的相关信息

整体流程简图

AnnotationConfigApplicationContext

Spring启动过程中,在通过 new AnnotationConfigApplicationContext 这一步骤已经完成了所有bean的扫描和管理。作为整个程序的入口,我们先来简单分析一下 AnnotationConfigApplicationContext

首先分析一下继承图:
提一下几个重要的接口和类:

  1. BeanFactory:访问Spring Bean的根接口,此接口和实现类实现依赖注入,在获取 Bean 实例 的时候如果当前容器找不到,会从父容器里面找
  2. BeanDefinitionRegistry:用于保存 Bean 定义 (BeanDefinition) 的接口,定义了保存、查看、移除相关的接口
  3. DefaultListableBeanFactory: Spring容器管理的核心类,具备成熟的容器管理能力,并且继承了 DefaultSingletonBeanRegistry 定义的 Spring 三级缓存 (详情点击)
  4. AnnotationConfigRegistry:注册配置注册表,定义注册和扫描配置类的方法
  5. ApplicationContext:定义基本功能方法(通过继承各种接口),包括访问 Bean 工厂,国际化,事件监听,加载文件资源等方法定义
  6. AbstractApplicationContext: 抽象上下文,主要完成 registerBeanPostProcessors 即BeanPostProcessors的注册功能
  7. GenericApplicationContext:继承 AbstractApplicationContext ,以模板方法定义部分功能,需要各种子类的实现,并且 持有DefaultListableBeanFactory引用

总的来说 AnnotationConfigApplicationContext 实现了以下功能:

  • 完成了组件类输入,包含 @Configuration@Component 和JSR-330兼容的类 eg:@Resource
  • 并且可以注册和进行类路径扫描,是一个独立的应用程序上下文,
  • 类似的类还有 ClassPathXmlApplicationContext,属于不同的功能实现。

点击 AnnotationConfigApplicationContext的构造方法,开始解读:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {//1.初始化读取器和扫描器,//2.指定bean factory实现类(DefaultListableBeanFactory),//3.获取相关的环境餐撒胡配置,如系统变量参数和进程启动参数//4.添加默认的6个内部处理器的BeanDefinition到factory的map中,其中jpa的处理器需要判断环境中是否导入相关包this();//注册一个或多个AppConfig,即包扫描类到BeanDefinitionMap中,包括@Condition,@Primary等属性值的读取,register(componentClasses);//初始化各种类refresh();
}

父类初始化 DefaultListableBeanFactory

在调用构造器内的方法前,由于java的类加载机制,需要先进行父类的静态代码块和构造器的初始化

AnnotationConfigApplicationContext的父类 GenericApplicationContext 进行了 DefaultListableBeanFactory 的初始化操作。

public GenericApplicationContext() {this.beanFactory = new DefaultListableBeanFactory();
}

1、this()

点进 this 方法,初始化了一个读取器和扫描器。核心内容在里面,先跟进Reader的初始化操作。

public AnnotationConfigApplicationContext() {//读取器this.reader = new AnnotatedBeanDefinitionReader(this);//扫描器this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotatedBeanDefinitionReader初始化

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {this(registry, getOrCreateEnvironment(registry));
}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);
}

跳过方法重写部分,有两个入参:BeanDefinitionRegistryEnvironment

  • AnnotationConfigApplicationContext本就实现了BeanDefinitionRegistry接口,调用时通过this将自身作为入参传了进来。此时向上转型为BeanDefinitionRegistry(明确职责),可以理解为现在需要使用BeanDefinitionRegistry定义的方法,而该接口负责BeanDefinition注册的相关内容。
  • Environment通过getOrCreateEnvironment获得,如果没有手动设置过的话,最终得到一个 StandardEnvironment对象,其实就是用来获取 java 启动时的参数和系统变量。

重点在于AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)这一行代码。

AnnotationConfigUtils.registerAnnotationConfigProcessors

主动调用 AnnotationConfigUtils,先加载静态代码块,这里先判断环境中是否存在jpa和@Resource,为之后添加Spring的内置处理器作出准备。

static {ClassLoader classLoader = AnnotationConfigUtils.class.getClassLoader();jsr250Present = ClassUtils.isPresent("javax.annotation.Resource", classLoader);jpaPresent = ClassUtils.isPresent("javax.persistence.EntityManagerFactory", classLoader) &&ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, classLoader);
}

点进registerAnnotationConfigProcessors,是个重写方法,跳过

//方法重写,跳过
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {registerAnnotationConfigProcessors(registry, null);
}

进入具体的方法内部,逐行分析

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {//为BeanFactory提供@Ordered和Priority支持if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}//为BeanFactory提供对@Lazy和限定符解析的支持if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);//根据判断,为BeanDefinitionMap中添加默认的spring处理器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中,并放回BeanDefinitionHolder* @param registry 工厂* @param definition definition* @param beanName bean的名称* @return 持有BeanDefinition的类*/
private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(beanName, definition);return new BeanDefinitionHolder(definition, beanName);
}
  • 首先是为工厂设置一些默认的支持:@Ordered@Priority支持,为 Bean Factory提供对@Lazy和限定符解析的支持
  • 判断当前BeanDefinitionMap中是否存在某些内容,没有的话就添加对应的内容。

正如方法名registerAnnotationConfigProcessors(注册注解配置处理器)所描述的一样,这个方法的主要功能是在BeanDefinitionMap中添加Spring内部提供的各种处理器,其中包含以下6个处理器:

  • ConfigurationClassPostProcessor:用于对@Configuration的解析支持,是所有处理器中第一个添加的,很重要
  • AutowiredAnnotationBeanPostProcessor:用于支持自动装配;
  • CommonAnnotationBeanPostProcessor:用于支持JSR-250约束,包括@Resource@PostConstruct@PreDestroy的注解解析;
  • PersistenceAnnotationBeanPostProcessor:用户支持JPA持久层解析,会判断当前是否有持久层对应的jar包,不一定会加载;
  • EventListenerMethodProcessor:对@EventListener注解的支持,
  • DefaultEventListenerFactory:同样是对Spring Event的支持,和EventListenerMethodProcessor共同作用

总结

AnnotatedBeanDefinitionReader的初始化,大概完成了以下几件事情:

  • Bean Factory设置属性,主要是对@Ordered@Priority@Lazy的解析支持;
  • 注册5~6个spring内部使用的解析处理器,用于后面各种解析的支持;

ps:这里需要明确一个概念,目前仅仅只是在BeanDefinitionMap中添加了内部处理器,并没有加载

ClassPathBeanDefinitionScanner初始化

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {this(registry, true);
}public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
}public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {this(registry, useDefaultFilters, environment, (registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
}public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,Environment environment, @Nullable ResourceLoader resourceLoader) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");this.registry = registry;if (useDefaultFilters) {//为@Component注册默认过滤器,隐式注册@Repository、@Service和@Controller等注释//并且支持JAVAEE6 提供的@ManagedBean和JSR-330的@NamedregisterDefaultFilters();}setEnvironment(environment);setResourceLoader(resourceLoader);
}

跳过一层层的方法调用,查看重点:registerDefaultFilters

registerDefaultFilters

@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {//在过滤器中添加@Component扫描支持this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {// 在过滤器中添加@ManagedBean扫描支持this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");}catch (ClassNotFoundException ex) {// 如果不支持JSR-250,Java EE 6,直接跳过}try {// 在过滤器中添加@Named扫描支持this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {// 如果不支持JSR-330 直接跳过}
}

总结

ClassPathBeanDefinitionScanner主要功能如下:

  • BeanFactory添加扫描过滤器:添加@Component和可能存在的JSR-250+JSR330注解;
  • 提供该类的scan方法,为后面包扫描提供支持;

疑问:如何扫描@Service、@Controller等注解

这里有一个注意点:只添加了@Component的拦截支持,如何扫描到@Service@Controller等等注解的呢?

参看registerDefaultFilters的Spring源码注释:

 /*** Register the default filter for {@link Component @Component}.* <p>This will implicitly register all annotations that have the* {@link Component @Component} meta-annotation including the* {@link Repository @Repository}, {@link Service @Service}, and* {@link Controller @Controller} stereotype annotations.* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and* JSR-330's {@link javax.inject.Named} annotations, if available.**/
protected void registerDefaultFilters() {
//...
}

翻译一下:

  • 为@Component注册默认过滤器。
  • 这将隐式注册所有具有@Component元注释的注释,包括@Repository 、 @Service和@Controller型注释。
  • 还支持 Java EE 6 的javax.annotation.ManagedBean和 JSR-330 的javax.inject.Named注释(如果可用)。

并没有提到如何隐式注册@Repository 、 @Service的。

简单提一下如何实现的:

查看这几个高级注解,可以发现他们的注解定义上均有@Component修饰

首先要明确一个概念:

  • 注解是不能继承的。如果A注解修饰B注解,然后B注解注释某个类,是不能通过类直接获取到A注解的。
  • 我们常说的注解的继承指的是,子类获取父类的注解。这并不是注解的继承,出发点在于类。

但是可以间接达到目的,Spring通过获取注解的元注解,然后递归扫描,判断类是否是Spring Bean

(判断方式Spring在5.x进行了优化,暂时不多做研究,但大概的思路就是这样的)

this方法总结

  • 初始化读取器和扫描器;
  • 间接添加了5到6个BeanPostProcessor,为之后的操作提供支持;
  • 添加各个注解的过滤器,为之后的包扫描提供支持;

2、register(componentClasses)

进入三大方法的第二个:register

@Override
public void register(Class<?>... componentClasses) {Assert.notEmpty(componentClasses, "At least one component class must be specified");this.reader.register(componentClasses);
}public void register(Class<?>... componentClasses) {for (Class<?> componentClass : componentClasses) {registerBean(componentClass);}
}public void registerBean(Class<?> beanClass) {doRegisterBean(beanClass, null, null, null, null);
}

主要是循环处理componentClasses(包扫描类可以传入多个),切入重点:doRegisterBean

private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,@Nullable BeanDefinitionCustomizer[] customizers) {//为注册类创建一个AnnotatedGenericBeanDefinition,主要是获取注解信息的方法支持AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);//结合 @Condition 来判断是否跳过,本次调用作用不大if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {return;}abd.setInstanceSupplier(supplier);//@Scope属性判断ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());// 调用beanNameGenerator生成名称,用于BeanDefinitionMap的keyString beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));// 为注册类需要注册的BeanDefinition添加各种属性值:lazy,primary,dependsOn,role,descriptionAnnotationConfigUtils.processCommonDefinitionAnnotations(abd);if (qualifiers != null) {for (Class<? extends Annotation> qualifier : qualifiers) {if (Primary.class == qualifier) {abd.setPrimary(true);}else if (Lazy.class == qualifier) {abd.setLazyInit(true);}else {abd.addQualifier(new AutowireCandidateQualifier(qualifier));}}}// Spring新提供的方法,自定义注册一个 bean,本次调用没用到if (customizers != null) {for (BeanDefinitionCustomizer customizer : customizers) {customizer.customize(abd);}}// 分装BeanDefinitionHolder,BeanDefinitionHolder持有BeanDefitionBeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);// 读取@Scop的proxyMethod属性值,确定代理方式definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);// 注册Bean(添加到BeanDefinitionMap中)BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

那么register(componentClasses)的作用就很明显了:用来注册 AppConfig的Bean定义

基本的注释在代码都有提及。
需要注意的是:doRegisterBean是一个注册Bean的通用方法,里面包含其他componentClasses的判断。

3、refresh

终于到这个部分了,spring启动流程的精华就在这个方法内部,我们切入主题。

@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {//刷新前的准备工作,主要是一些容器状态还有环境的设置prepareRefresh();//获取DefaultListableBeanFactory (register中初始化的factory)ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//做一些准备工作,初始化容器的一些标准特性prepareBeanFactory(beanFactory);try {//此处AbstractApplicationContext没有实现该方法,留给子类实现,postProcessBeanFactory(beanFactory);/*** 执行实现 BeanFactoryPostProcessors接口的类(并且@Component标注),因为原理是通过getBean(name,class)获取后执行** 1.委托给PostProcessorRegistrationDelegate来进行“调用”这一动作,内部处理存在特殊处理(如:优先级判断决定执行的先后顺序),* 2.最原始的先后顺序上,先执行为 BeanDefinitionRegistryPostProcessor 的子类,再执行 BeanFactoryPostProcessor 的子类* --- BeanFactoryPostProcessor* --- BeanDefinitionRegistryPostProcessor  为前者的子类** 查看spring最开始就加载的配置类之一:ConfigurationClassPostProcessor,该类为spring* ConfigurationClassPostProcessor**/invokeBeanFactoryPostProcessors(beanFactory);//同样委托给PostProcessorRegistrationDelegate 注册需要执行的BeanPostProcessorsregisterBeanPostProcessors(beanFactory);//国际化的一些初始化操作initMessageSource();//初始化 ApplicationEventMulticaster到 单例池中,用于管理 ApplicationListener ,默认为 SimpleApplicationEventMulticaster。initApplicationEventMulticaster();//模板方法,用于其他Context子类 实例化一些特殊的BeanonRefresh();//往 applicationEventMulticaster 添加实现ApplicationListener接口的类registerListeners();//初始化所有非 Lazy 的单例 BeanfinishBeanFactoryInitialization(beanFactory);//完成上下文刷新,会调用LifecycleProcessor的onRefresh()方法并发布ContextRefreshedEvent 。finishRefresh();} catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}//Spring original comment----------------- Destroy already created singletons to avoid dangling resources.destroyBeans();//Spring original comment----------------- Reset 'active' flag.cancelRefresh(ex);//Spring original comment----------------- Propagate exception to caller.throw ex;} finally {//Spring original comment----------------- Reset common introspection caches in Spring's core, since we//Spring original comment----------------- might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}

一行一行来吧:

prepareRefresh

刷新前的准备工作,包括spring状态的设置,一些验证和事件的初始化设置

protected void prepareRefresh() {//设置为开启状态this.startupDate = System.currentTimeMillis();this.closed.set(false);this.active.set(true);if (logger.isDebugEnabled()) {if (logger.isTraceEnabled()) {logger.trace("Refreshing " + this);} else {logger.debug("Refreshing " + getDisplayName());}}//初始化占位符属性源,留给子类实现,在AnnotationConfigApplicationContext没有实现,其他需要的子类应当实现initPropertySources();//验证setRequiredProperties指定的每个属性是否存在并解析为非null值。getEnvironment().validateRequiredProperties();//存储早期事件监听if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);} else {this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}//用来存储早期的事件,在广播器可用后发布this.earlyApplicationEvents = new LinkedHashSet<>();
}

obtainFreshBeanFactory

获取BeanFactory,另外CAS设置状态,避免一个Bean Factory多次刷新。

 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {//refresh校验refreshBeanFactory();return getBeanFactory();}@Overrideprotected final void refreshBeanFactory() throws IllegalStateException {if (!this.refreshed.compareAndSet(false, true)) {throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");}this.beanFactory.setSerializationId(getId());}

prepareBeanFactory(beanFactory)

做一些准备工作,初始化一些标准特性,比较重要的部分如下:

  • 提供了BeanFactoryProcessor的支持,在Spring大法属于很重要的环节;
  • 注册了ApplicationListener相关的处理器;
  • registerResolvableDependency为解决 ”无法找到同接口多实现类“ 提供了一种解决方法,可以在日常中考虑使用(@Qualifier也不错)
  • 提供LoadTimeWeaver支持
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {//上下文类加载器beanFactory.setBeanClassLoader(getClassLoader());//设置Bean表达式解析器,在属性填充(populateBean)的时候会用到里面的方法beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));//提供属性编辑器,可以自定义,通过实现PropertyEditorSupport接口beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));//添加 BeanFactoryProcessor 支持的重要环节beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));//忽略给定的自动装配依赖接口,在addBeanPostProcessor 已经初始化了beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);//为避免自动装配时,找不到对应的实现类,给特定的接口添加默认的实现类,beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);//注册早期的 ApplicationListene 处理器beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));//果存在的话,提供 LoadTimeWeaver 支持if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));//为类型匹配设置一个临时的 ClassLoader。beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}//注册默认的环境类相关Beanif (!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());}
}

postProcessBeanFactory

该方法在父类 AbstractApplicationContext中定义,在AnnotationConfigApplicationContext中并没有实现它。

子类可以通过重写这个方法,在BeanFactory准备好后进行额外操作。

  • GenericWebApplicationContext作为另外的子类,就实现了该方法:添加了ServletContextAwareProcessor处理器。(有兴趣的可以看看)
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}

invokeBeanFactoryPostProcessors(beanFactory);

 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {//委托PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());//如果检测到 LoadTimeWeaver ,则准备编织//例如通过 ConfigurationClassPostProcessor 注册的 @Bean 方法if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}}

这个方法的主要作用是:实例化并调用所有已注册的 BeanFactoryPostProcessor,分析一下关键代码。

首先是委托给了PostProcessorRegistrationDelegate进行调用操作。点进去查看委托的操作。 这部分有点儿意思。

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {//如果有的话,首先调用 BeanDefinitionRegistryPostProcessors。Set<String> processedBeans = new HashSet<>();//如果factory属于BeanDefinitionRegistry,则执行以下逻辑(通过扫描获取哪些BeanFactoryPostProcessor需要执行)//而else里面则直接执行 beanFactoryPostProcessors 集合里面的内容//(该类容一般传参的方式为工厂的成员变量 factory.getBeanFactoryPostProcessors())if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();//对于DefaultListableBeanFactory而言,此处为Empty -->factory.getBeanFactoryPostProcessors 为空for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {BeanDefinitionRegistryPostProcessor registryProcessor =(BeanDefinitionRegistryPostProcessor) postProcessor;registryProcessor.postProcessBeanDefinitionRegistry(registry);registryProcessors.add(registryProcessor);} else {regularPostProcessors.add(postProcessor);}}//不要在此处初始化 FactoryBeans:我们需要保留所有常规 bean 未初始化以让 bean 工厂后处理器应用于它们!// 将实现 PriorityOrdered、Ordered 和其余部分的 BeanDefinitionRegistryPostProcessor 分开。List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();// 1.首先,调用实现 PriorityOrdered 的 BeanDefinitionRegistryPostProcessors。String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {//获取BeanDefinitionRegistryPostProcessor 接口定义的类,//spring内部定义的扫描类,优先级高于 BeanFactoryPostProcessor,为 BeanFactoryPostProcessor的子类currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();// 2。然后,调用实现 Ordered 的 BeanDefinitionRegistryPostProcessorspostProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();// 3.最后,调用所有其他 BeanDefinitionRegistryPostProcessor 直到不再出现。boolean reiterate = true;while (reiterate) {reiterate = false;postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);reiterate = true;}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();}//现在,调用到目前为止处理的所有处理器的 postProcessBeanFactory 回调。invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);} else {//调用在上下文实例中注册的工厂处理器invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);}//不要在此处初始化 FactoryBeans:我们需要保留所有常规 bean 未初始化以让 bean 工厂后处理器应用于它们!String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);//将实现 PriorityOrdered、Ordered 和其余部分的 BeanFactoryPostProcessors 分开。List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<String> orderedPostProcessorNames = new ArrayList<>();List<String> nonOrderedPostProcessorNames = new ArrayList<>();for (String ppName : postProcessorNames) {if (processedBeans.contains(ppName)) {/*** skip - already processed in first phase above* 已经先调用完毕了,比如ConfigurationClassPostProcessor,该类实现了 BeanDefinitionRegistryPostProcessor 接口* 而BeanDefinitionRegistryPostProcessor是 BeanFactoryPostProcessor的子类,所以这个集合里面还是会得到该类的名称,* 所以此处跳过(不做处理)*/} else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));} else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);} else {nonOrderedPostProcessorNames.add(ppName);}}// 1.首先,调用实现 PriorityOrdered 的 BeanFactoryPostProcessors。sortPostProcessors(priorityOrderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);// 2。然后,调用实现 Ordered 的 BeanFactoryPostProcessorsList<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String postProcessorName : orderedPostProcessorNames) {orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}sortPostProcessors(orderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);// 3.最后,调用所有其他 BeanFactoryPostProcessorsList<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String postProcessorName : nonOrderedPostProcessorNames) {nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);//清除缓存的 merged bean definitions,因为后处理器可能已经修改了原始元数据,例如替换值中的占位符...beanFactory.clearMetadataCache();}

这一段的代码较长,但是重复的段落较多,整体思想还是比较简单的。一层一层的来解析他。

  • 首先明确调用此方法的BeanFactory,是否是用来注册的(beanFactory instanceof BeanDefinitionRegistry),否则直接调用该工厂内定义的beanFactoryPostProcessors

本次调用的是DefaultListableBeanFactory,自然间接继承或实现了BeanDefinitionRegistry

我们进入内部复杂的初始化操作。在简单看完这里面的代码之后,我们可以发现一些很有趣的东西:

  • 该方法主要是按顺序调用了实现BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor接口的方法。
  • 这个顺序是按照所实现方法上的注解进行判断的,最优先的为@PriorityOrdered标注的实现类,其次是@Ordered,最后是什么都没有的。

重点:beanFactoryPostProcessors加载的优先级判断

其实BeanDefinitionRegistryPostProcessor是继承BeanFactoryPostProcessor的子接口

  1. 那么为什么要spring要区分对待呢?
  2. 都是beanFactoryPostProcessors处理器,按照@PriorityOrdered@Ordered什么都没有的顺序处理一次不就可以了吗?

通过断点,我们发现被扫描出来的,实现了BeanDefinitionRegistryPostProcessor接口的类为:ConfigurationClassPostProcessor

我们之前就提到过了ConfigurationClassPostProcessor:在registerAnnotationConfigProcessors中被注册,是第一个被调用的处理器。

ConfigurationClassPostProcessor负责支持@Configuration的解析,从作用上来说,应当早于所有其他的beanFactoryPostProcessors

到这里我们需要知道两点就可以了:

  1. 实现BeanDefinitionRegistryPostProcessor的方法 早于 实现beanFactoryPostProcessors的方法。
  2. invokeBeanFactoryPostProcessors中是先处理的前者。二者调用时都是@PriorityOrdered@Ordered什么都没有的优先级判断

另外在调用的同时两个接口的实现类会通过getBean()加载到DefaultListableBeanFactory持有的由父级的单例池singletonObjects里面

ConfigurationClassPostProcessor的加载

我们来查看第一个调用并添加到单例池BeanFactory处理器:ConfigurationClassPostProcessor究竟做了什么:

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {int factoryId = System.identityHashCode(beanFactory);if (this.factoriesPostProcessed.contains(factoryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);}this.factoriesPostProcessed.add(factoryId);if (!this.registriesPostProcessed.contains(factoryId)) {// BeanDefinitionRegistryPostProcessor hook apparently not supported...// Simply call processConfigurationClasses lazily at this point then.processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);}//对Configuration类进行增强enhanceConfigurationClasses(beanFactory);//添加一个 ImportAwareBeanPostProcessor 后置处理器,//其作用是为EnhanceConfiguration类的beanFactory属性赋值。beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

主要做了两点:

  1. Configuration类创建代理;
  2. 添加一个 ImportAwareBeanPostProcessor后置处理器,为EnhanceConfiguration类的beanFactory属性赋值

这里面牵扯到@ImportAware的实现原理,篇幅有限,暂不展开。具体内容会在其他博客中详细描述。

总结

invokeBeanFactoryPostProcessors(beanFactory)完成了以下功能:

  • 委托给PostProcessorRegistrationDelegate调用beanFactoryPostProcessors的实现类(包括BeanDefinitionRegistryPostProcessor);
  • 加载beanFactoryPostProcessors的实现类到单例池里面;
  • 调用ConfigurationClassPostProcessorpostProcessBeanFactory的方法,创建对应代理,并添加一个ImportAwareBeanPostProcessor

registerBeanPostProcessors(beanFactory);

调用并注册BeanPostProcessors,原理和invokeBeanFactoryPostProcessors如出一辙,简单分析一下:

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

BeanFactoryProcessors类似,同样是委托,此处委托给PostProcessorRegistrationDelegate进行相关操作。我们直接点进去:

public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);// Register BeanPostProcessorChecker that logs an info message when// a bean is created during BeanPostProcessor instantiation, i.e. when// a bean is not eligible for getting processed by all BeanPostProcessors.//注册 BeanPostProcessorChecker,它会在 BeanPostProcessor 实例化期间创建 bean 时记录信息消息,即当 bean 没有资格被所有 BeanPostProcessor 处理时。int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));// Separate between BeanPostProcessors that implement PriorityOrdered,// Ordered, and the rest.List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();List<String> orderedPostProcessorNames = new ArrayList<>();List<String> nonOrderedPostProcessorNames = new ArrayList<>();for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);priorityOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}} else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);} else {nonOrderedPostProcessorNames.add(ppName);}}// First, register the BeanPostProcessors that implement PriorityOrdered.sortPostProcessors(priorityOrderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);// Next, register the BeanPostProcessors that implement Ordered.List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String ppName : orderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);orderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}sortPostProcessors(orderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, orderedPostProcessors);// Now, register all regular BeanPostProcessors.List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String ppName : nonOrderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);nonOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);// Finally, re-register all internal BeanPostProcessors.sortPostProcessors(internalPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, internalPostProcessors);//重新注册处理器以将内部 bean 检测为 ApplicationListeners,将其移动到处理器链的末尾(用于获取代理等)beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

大体内容是一样的:按照@PriorityOrdered@Ordered什么都没有的优先级调用并注册BeanPostProcessor

不同的地方有以下两个地方:

  1. 添加了一个BeanPostProcessorChecker:这是用于记录某些没有被所有BeanPostProcessorBean信息。
  2. 重新注册了ApplicationListenerDetector:Spring Event的相关机制,此时吧``ApplicationListenerDetector`添加到了处理器的末尾

*ps:addBeanPostProcessor*内部操作时,会先remove,然后再新增,并且放在最后面:addLast方法

initMessageSource();

国际化的一些初始化操作

initApplicationEventMulticaster();

初始化 ApplicationEventMulticaster。如果上下文中没有定义,则使用 SimpleApplicationEventMulticaster

代码如下,没什么好说的。

protected void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);if (logger.isTraceEnabled()) {logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");}} else {this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);if (logger.isTraceEnabled()) {logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}}
}

onRefresh();

postProcessBeanFactory,使个模板方法。交给子类实现。AnnotationConfigApplicationContext并没有实现它。

例如GenericWebApplicationContext在这个时候设置了ThemeSource

ThemeSource:主题资源,web时可能需要,没用过。参考即可。

registerListeners();

往 applicationEventMulticaster 添加实现ApplicationListener接口的类

finishBeanFactoryInitialization(beanFactory);

初始化所有非 Lazy 的单例 Bean

这是 Spring启动流程的重点,这是Spring启动流程的重点,这是Spring启动流程的重点。重要的事说三遍!

循环依赖的解决及三级缓存的设计都在里面。

关于本方法会在另一篇博客里面详细描述。此处不展开

finishRefresh();

完成上下文刷新,会调用LifecycleProcessor的onRefresh()方法并发布ContextRefreshedEvent

攀爬Spring珠穆拉玛峰:Spring的启动流程相关推荐

  1. 攀爬Spring珠穆拉玛峰:preInstantiateSingletons方法、三级缓存、循环依赖

    Spring启动流程 1)Spring 三级缓存与循环依赖 前文回顾 preInstantiateSingletons getBean→doGetBean 自动装载与循环依赖 三级缓存 getSing ...

  2. 攀爬Spring珠穆拉玛峰:@Import、ImportAware的使用与原理分析

    @Import解析 @Import提供了不同于@Bean的导入方式,本文将从用法和原理介绍这个由Spring提供的注解. 用法1:@Import+普通类 @Configuration @Import( ...

  3. 折纸珠峰c语言程序,c语言折纸超过珠穆拉玛峰

    很简单,如果n = 1是折叠1次,应该为纸张的厚度*2,也就是0.05*1.为0.1mm.但是根据本答案提供的式子m*2^(n-1),答案应为m也就是0.05.在此即跟题意有所出入. 提供Python ...

  4. 实现统计纸片对折关键算法。假设一张足够大的纸,纸张的厚度为 0.5 毫米。请问对折多少次以后,可以达到珠穆朗 玛峰的高度(最新数据:8844.43 米)。请编写程序输出对折 次数。

    实现统计纸片对折关键算法.假设一张足够大的纸,纸张的厚度为 0.5 毫米.请问对折多少次以后,可以达到珠穆朗 玛峰的高度(最新数据:8844.43 米).请编写程序输出对折 次数. 注意:使用循环结构 ...

  5. SpringBoot:Spring boot 主程序的功能SpringApplication.run(启动流程)

    1.美图 # 2. 主程序 import org.springframework.boot.SpringApplication; import org.springframework.boot.aut ...

  6. 攀爬天梯的手机厂商,能从LG的滑落中学到什么?

    最近,一部韩剧用极为狗血离谱荒谬的剧情,击碎了本平民对上流社会的想象,没错,它就是<顶楼>. 里面的有钱人到底有多奇葩呢?歌唱家去别人家偷东西,也要穿上拖地礼裙:因为平民比自己专业技能强, ...

  7. 【Unity3D日常开发】自动寻路系统Navigation实现人物上楼梯、走斜坡、攀爬、跳跃

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875 参考文章:列表 Unity3D深入浅出 - 导航网格自动寻路(Navigat ...

  8. 【Unity3D】自动寻路系统Navigation实现人物上楼梯、走斜坡、攀爬、跳跃

    @toc 参考文章:列表 Unity3D深入浅出 - 导航网格自动寻路(Navigation Mesh) unity3D--自带寻路Navmesh入门教程(二) Unity3D自动寻路系统Naviga ...

  9. 藤本植物和攀爬植物模型包 Globe Plants – Bundle 23 – Vines and Creepers 03 (3D Models)

    藤本植物和攀爬植物模型包 Globe Plants – Bundle 23 – Vines and Creepers 03 (3D Models) 全球植物–第23束–藤本植物和攀缘植物03 (3D模 ...

最新文章

  1. Swift 3.0 预告:将 Objc 库转换成更符合 Swift 语法风格的形式
  2. 程序员晒元宵节福利,网友:看了我想砸键盘......
  3. css3 animation(左右摆动) (放大缩小)
  4. 设计模式之四:适配器模式(Adapter Pattern)
  5. python 删除文件或文件夹
  6. 存储知识课堂(二):磁盘读写磁头揭秘
  7. MySQL 安全性知识要点
  8. python package_python之package定义
  9. C++调用PyTorch模型:LibTorch
  10. 【Spring】Spring 自定义scope
  11. Kryo 为什么比 Hessian 快
  12. 【JavaScript】正则表达式 1
  13. python3.7适用的opencv_通过python3.7.3使用openCV截图一个区域
  14. Editplus配置VC++(1) 及相关注意事项
  15. malloc.h头文件以及malloc函数
  16. python bartender,python集成Bartender的多线程编程
  17. 都9012年了还没用过typescript?
  18. 电子护照阅读器现身出入境办证大厅
  19. python关闭excel进程_python win32com关闭Excel进程
  20. HihoCoder - 1272 买零食

热门文章

  1. Leetcode刷题986. 区间列表的交集
  2. Python 画图学习入门
  3. 微信or支付宝交易限额
  4. 箱梁终张拉后弹性上拱度计算_时速350公里客运专线铁路无砟轨道后张法预应力混凝土简支箱梁(双线)预应力张拉探讨...
  5. MyBatis初学总结(五)
  6. 潜龙号开启水下机器人_潜龙二号水下机器人:我的老家在沈阳
  7. warning和error的区别
  8. 一篇文章把你带入到JavaScript中的闭包与高级函数
  9. 解决迅雷无法使用右键下载的问题
  10. 学习 Google Gadgets (一)