首页
关于
友链
Search
1
wlop 4K 壁纸 4k8k 动态 壁纸
1,509 阅读
2
Nacos持久化MySQL问题-解决方案
959 阅读
3
Docker搭建Typecho博客
763 阅读
4
滑动时间窗口算法
746 阅读
5
Nginx反向代理微服务配置
716 阅读
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
登录
Search
标签搜索
java
javase
docker
java8
springboot
thread
spring
分布式
mysql
锁
linux
redis
源码
typecho
centos
git
map
RabbitMQ
lambda
stream
少年
累计撰写
189
篇文章
累计收到
24
条评论
首页
栏目
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
页面
关于
友链
搜索到
23
篇与
的结果
2022-04-26
SpringBoot源码(二):自动装配原理
springboot源码(二):自动装配原理 在之前的课程中我们讲解了springboot的启动过程,其实在面试过程中问的最多的可能是自动装配的原理,而自动装配是在启动过程中完成,只不过在刚开始的时候我们选择性的跳过了,下面详细讲解自动装配的过程。1、在springboot的启动过程中,有一个步骤是创建上下文,如果不记得可以看下面的代码:public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //此处完成自动装配的过程 prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }2、在prepareContext方法中查找load方法,一层一层向内点击,找到最终的load方法//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方法完成该功能 load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); } /** * Load beans into the application context. * @param context the context to load beans into * @param sources the sources to load * 加载bean对象到context中 */ protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } //获取bean对象定义的加载器 BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); } /** * Load the sources into the reader. * @return the number of loaded beans */ int load() { int count = 0; for (Object source : this.sources) { count += load(source); } return count; }3、实际执行load的是BeanDefinitionLoader中的load方法,如下: //实际记载bean的方法 private int load(Object source) { Assert.notNull(source, "Source must not be null"); //如果是class类型,启用注解类型 if (source instanceof Class<?>) { return load((Class<?>) source); } //如果是resource类型,启动xml解析 if (source instanceof Resource) { return load((Resource) source); } //如果是package类型,启用扫描包,例如@ComponentScan if (source instanceof Package) { return load((Package) source); } //如果是字符串类型,直接加载 if (source instanceof CharSequence) { return load((CharSequence) source); } throw new IllegalArgumentException("Invalid source type " + source.getClass()); }4、下面方法将用来判断是否资源的类型,是使用groovy加载还是使用注解的方式 private int load(Class<?> source) { //判断使用groovy脚本 if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { // Any GroovyLoaders added in beans{} DSL can contribute beans here GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); load(loader); } //使用注解加载 if (isComponent(source)) { this.annotatedReader.register(source); return 1; } return 0; }5、下面方法判断启动类中是否包含@Component注解,但是会神奇的发现我们的启动类中并没有该注解,继续更进发现MergedAnnotations类传入了一个参数SearchStrategy.TYPE_HIERARCHY,会查找继承关系中是否包含这个注解,@SpringBootApplication-->@SpringBootConfiguration-->@Configuration-->@Component,当找到@Component注解之后,会把该对象注册到AnnotatedBeanDefinitionReader对象中private boolean isComponent(Class<?> type) { // This has to be a bit of a guess. The only way to be sure that this type is // eligible is to make a bean definition out of it and try to instantiate it. if (MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).isPresent(Component.class)) { return true; } // Nested anonymous classes are not eligible for registration, nor are groovy // closures return !type.getName().matches(".*\\$_.*closure.*") && !type.isAnonymousClass() && type.getConstructors() != null && type.getConstructors().length != 0; } /** * Register a bean from the given bean class, deriving its metadata from * class-declared annotations. * 从给定的bean class中注册一个bean对象,从注解中找到相关的元数据 */ private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } abd.setInstanceSupplier(supplier); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); AnnotationConfigUtils.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)); } } } if (customizers != null) { for (BeanDefinitionCustomizer customizer : customizers) { customizer.customize(abd); } } BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); } /** * Register the given bean definition with the given bean factory. * 注册主类,如果有别名可以设置别名 */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } //@SpringBootApplication @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {} //@SpringBootConfiguration @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration {} //@Configuration @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration {}当看完上述代码之后,只是完成了启动对象的注入,自动装配还没有开始,下面开始进入到自动装配。6、自动装配入口,从刷新容器开始@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // 此处是自动装配的入口 invokeBeanFactoryPostProcessors(beanFactory); }7、在invokeBeanFactoryPostProcessors方法中完成bean的实例化和执行/** * Instantiate and invoke all registered BeanFactoryPostProcessor beans, * respecting explicit order if given. * <p>Must be called before singleton instantiation. */ protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { //开始执行beanFactoryPostProcessor对应实现类,需要知道的是beanFactoryPostProcessor是spring的扩展接口,在刷新容器之前,该接口可以用来修改bean元数据信息 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }8、查看invokeBeanFactoryPostProcessors的具体执行方法 public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Invoke BeanDefinitionRegistryPostProcessors first, if any. Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); //开始遍历三个内部类,如果属于BeanDefinitionRegistryPostProcessor子类,加入到bean注册的集合,否则加入到regularPostProcessors for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } } // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // Separate between BeanDefinitionRegistryPostProcessors that implement // PriorityOrdered, Ordered, and the rest. List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. //通过BeanDefinitionRegistryPostProcessor获取到对应的处理类“org.springframework.context.annotation.internalConfigurationAnnotationProcessor”,但是需要注意的是这个类在springboot中搜索不到,这个类的完全限定名在AnnotationConfigEmbeddedWebApplicationContext中,在进行初始化的时候会装配几个类,在创建AnnotatedBeanDefinitionReader对象的时候会将该类注册到bean对象中,此处可以看到internalConfigurationAnnotationProcessor为bean名称,容器中真正的类是ConfigurationClassPostProcessor String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); //首先执行类型为PriorityOrdered的BeanDefinitionRegistryPostProcessor //PriorityOrdered类型表明为优先执行 for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { //获取对应的bean currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); //用来存储已经执行过的BeanDefinitionRegistryPostProcessor processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); //开始执行装配逻辑 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered. //其次执行类型为Ordered的BeanDefinitionRegistryPostProcessor //Ordered表明按顺序执行 postProcessorNames = 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(); // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. //循环中执行类型不为PriorityOrdered,Ordered类型的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(); } // Now, invoke the postProcessBeanFactory callback of all processors handled so far. //执行父类方法,优先执行注册处理类 invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); //执行有规则处理类 invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); } else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. 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 } 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); } } // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); // Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); // Finally, invoke all other BeanFactoryPostProcessors. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); // Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanFactory.clearMetadataCache(); }9、开始执行自动配置逻辑(启动类指定的配置,非默认配置),可以通过debug的方式一层层向里进行查找,会发现最终会在ConfigurationClassParser类中,此类是所有配置类的解析类,所有的解析逻辑在parser.parse(candidates)中public void parse(Set<BeanDefinitionHolder> configCandidates) { for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { //是否是注解类 if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } //执行配置类 this.deferredImportSelectorHandler.process(); } ------------------- protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName)); } ------------------- protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { //循环处理bean,如果有父类,则处理父类,直至结束 sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }10、继续跟进doProcessConfigurationClass方法,此方式是支持注解配置的核心逻辑/** * Apply processing and build a complete {@link ConfigurationClass} by reading the * annotations, members and methods from the source class. This method can be called * multiple times as relevant sources are discovered. * @param configClass the configuration class being build * @param sourceClass a source class * @return the superclass, or {@code null} if none found or previously processed */ @Nullable protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { //处理内部类逻辑,由于传来的参数是启动类,并不包含内部类,所以跳过 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); } // Process any @PropertySource annotations //针对属性配置的解析 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations // 这里是根据启动类@ComponentScan注解来扫描项目中的bean Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately //遍历项目中的bean,如果是注解定义的bean,则进一步解析 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { //递归解析,所有的bean,如果有注解,会进一步解析注解中包含的bean parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations //递归解析,获取导入的配置类,很多情况下,导入的配置类中会同样包含导入类注解 processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations //解析@ImportResource配置类 AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods //处理@Bean注解修饰的类 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces // 处理接口中的默认方法 processInterfaces(configClass, sourceClass); // Process superclass, if any //如果该类有父类,则继续返回,上层方法判断不为空,则继续递归执行 if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; } 11、查看获取配置类的逻辑processImports(configClass, sourceClass, getImports(sourceClass), true); /** * Returns {@code @Import} class, considering all meta-annotations. */ private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { Set<SourceClass> imports = new LinkedHashSet<>(); Set<SourceClass> visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited); return imports; } ------------------ /** * Recursively collect all declared {@code @Import} values. Unlike most * meta-annotations it is valid to have several {@code @Import}s declared with * different values; the usual process of returning values from the first * meta-annotation on a class is not sufficient. * <p>For example, it is common for a {@code @Configuration} class to declare direct * {@code @Import}s in addition to meta-imports originating from an {@code @Enable} * annotation. * 看到所有的bean都以导入的方式被加载进去 */ private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException { if (visited.add(sourceClass)) { for (SourceClass annotation : sourceClass.getAnnotations()) { String annName = annotation.getMetadata().getClassName(); if (!annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited); } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } }12、继续回到ConfigurationClassParser中的parse方法中的最后一行,继续跟进该方法:this.deferredImportSelectorHandler.process() ------------- public void process() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { if (deferredImports != null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); deferredImports.forEach(handler::register); handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } } --------------- public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get( entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } } ------------ /** * Return the imports defined by the group. * @return each import with its associated configuration class */ public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this.group.selectImports(); } } ------------ public DeferredImportSelector getImportSelector() { return this.importSelector; } ------------ @Override public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }
2022年04月26日
248 阅读
0 评论
1 点赞
2022-04-26
SpringBoot源码解析(一):启动过程
springboot源码解析(一):启动过程1、springboot的入口程序@SpringBootApplication public class StartupApplication { public static void main(String[] args) { SpringApplication.run(StartupApplication.class, args); } }当程序开始执行之后,会调用SpringApplication的构造方法,进行某些初始参数的设置//创建一个新的实例,这个应用程序的上下文将要从指定的来源加载Bean public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { //资源初始化资源加载器,默认为null this.resourceLoader = resourceLoader; //断言主要加载资源类不能为 null,否则报错 Assert.notNull(primarySources, "PrimarySources must not be null"); //初始化主要加载资源类集合并去重 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //推断当前 WEB 应用类型,一共有三种:NONE,SERVLET,REACTIVE this.webApplicationType = WebApplicationType.deduceFromClasspath(); //设置应用上线文初始化器,从"META-INF/spring.factories"读取ApplicationContextInitializer类的实例名称集合并去重,并进行set去重。(一共7个) setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //设置监听器,从"META-INF/spring.factories"读取ApplicationListener类的实例名称集合并去重,并进行set去重。(一共11个) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //推断主入口应用类,通过当前调用栈,获取Main方法所在类,并赋值给mainApplicationClass this.mainApplicationClass = deduceMainApplicationClass(); }在上述构造方法中,有一个判断应用类型的方法,用来判断当前应用程序的类型: static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; } //WebApplicationType的类型 public enum WebApplicationType { /** * The application should not run as a web application and should not start an * embedded web server. * 非web项目 */ NONE, /** * The application should run as a servlet-based web application and should start an * embedded servlet web server. * servlet web 项目 */ SERVLET, /** * The application should run as a reactive web application and should start an * embedded reactive web server. * 响应式 web 项目 */ REACTIVE;springboot启动的运行方法,可以看到主要是各种运行环境的准备工作public ConfigurableApplicationContext run(String... args) { //1、创建并启动计时监控类 StopWatch stopWatch = new StopWatch(); stopWatch.start(); //2、初始化应用上下文和异常报告集合 ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //3、设置系统属性“java.awt.headless”的值,默认为true,用于运行headless服务器,进行简单的图像处理,多用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true configureHeadlessProperty(); //4、创建所有spring运行监听器并发布应用启动事件,简单说的话就是获取SpringApplicationRunListener类型的实例(EventPublishingRunListener对象),并封装进SpringApplicationRunListeners对象,然后返回这个SpringApplicationRunListeners对象。说的再简单点,getRunListeners就是准备好了运行时监听器EventPublishingRunListener。 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { //5、初始化默认应用参数类 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //6、根据运行监听器和应用参数来准备spring环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //将要忽略的bean的参数打开 configureIgnoreBeanInfo(environment); //7、创建banner打印类 Banner printedBanner = printBanner(environment); //8、创建应用上下文,可以理解为创建一个容器 context = createApplicationContext(); //9、准备异常报告器,用来支持报告关于启动的错误 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //10、准备应用上下文,该步骤包含一个非常关键的操作,将启动类注入容器,为后续开启自动化提供基础 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //11、刷新应用上下文 refreshContext(context); //12、应用上下文刷新后置处理,做一些扩展功能 afterRefresh(context, applicationArguments); //13、停止计时监控类 stopWatch.stop(); //14、输出日志记录执行主类名、时间信息 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //15、发布应用上下文启动监听事件 listeners.started(context); //16、执行所有的Runner运行器 callRunners(context, applicationArguments); }catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //17、发布应用上下文就绪事件 listeners.running(context); }catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //18、返回应用上下文 return context; }下面详细介绍各个启动的环节:1、创建并启动计时监控类,可以看到记录当前任务的名称,默认是空字符串,然后记录当前springboot应用启动的开始时间。StopWatch stopWatch = new StopWatch(); stopWatch.start(); //详细源代码 public void start() throws IllegalStateException { start(""); } public void start(String taskName) throws IllegalStateException { if (this.currentTaskName != null) { throw new IllegalStateException("Can't start StopWatch: it's already running"); } this.currentTaskName = taskName; this.startTimeNanos = System.nanoTime(); }2、初始化应用上下文和异常报告集合ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();3、设置系统属性java.awt.headless的值:/* java.awt.headless模式是在缺少显示屏、键盘或者鼠标的系统配置 当配置了如下属性之后,应用程序可以执行如下操作: 1、创建轻量级组件 2、收集关于可用的字体、字体指标和字体设置的信息 3、设置颜色来渲染准备图片 4、创造和获取图像,为渲染准备图片 5、使用java.awt.PrintJob,java.awt.print.*和javax.print.*类里的方法进行打印 */ private void configureHeadlessProperty() { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); }4、创建所有spring运行监听器并发布应用启动事件SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); //创建spring监听器 private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); } SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) { this.log = log; this.listeners = new ArrayList<>(listeners); } //循环遍历获取监听器 void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } } //此处的监听器可以看出是事件发布监听器,主要用来发布启动事件 @Override public void starting() { //这里是创建application事件‘applicationStartingEvent’ this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); } //applicationStartingEvent是springboot框架最早执行的监听器,在该监听器执行started方法时,会继续发布事件,主要是基于spring的事件机制 @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); //获取线程池,如果为空则同步处理。这里线程池为空,还未初始化 Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { //异步发送事件 executor.execute(() -> invokeListener(listener, event)); } else { //同步发送事件 invokeListener(listener, event); } } }5、初始化默认应用参数类ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); public DefaultApplicationArguments(String... args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; }6、根据运行监听器和应用参数来准备spring环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //详细环境的准备 private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 获取或者创建应用环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); // 配置应用环境,配置propertySource和activeProfiles configureEnvironment(environment, applicationArguments.getSourceArgs()); //listeners环境准备,广播ApplicationEnvironmentPreparedEvent ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); //将环境绑定给当前应用程序 bindToSpringApplication(environment); //对当前的环境类型进行判断,如果不一致进行转换 if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } //配置propertySource对它自己的递归依赖 ConfigurationPropertySources.attach(environment); return environment; } // 获取或者创建应用环境,根据应用程序的类型可以分为servlet环境、标准环境(特殊的非web环境)和响应式环境 private ConfigurableEnvironment getOrCreateEnvironment() { //存在则直接返回 if (this.environment != null) { return this.environment; } //根据webApplicationType创建对应的Environment switch (this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); } } //配置应用环境 protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { if (this.addConversionService) { ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService) conversionService); } //配置property sources configurePropertySources(environment, args); //配置profiles configureProfiles(environment, args); }7、创建banner的打印类Banner printedBanner = printBanner(environment); //打印类的详细操作过程 private Banner printBanner(ConfigurableEnvironment environment) { if (this.bannerMode == Banner.Mode.OFF) { return null; } ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner); if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } return bannerPrinter.print(environment, this.mainApplicationClass, System.out); }8、创建应用的上下文:根据不同哦那个的应用类型初始化不同的上下文应用类context = createApplicationContext(); protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }9、准备异常报告器exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }10、准备应用上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner); private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //应用上下文的environment context.setEnvironment(environment); //应用上下文后处理 postProcessApplicationContext(context); //为上下文应用所有初始化器,执行容器中的applicationContextInitializer(spring.factories的实例),将所有的初始化对象放置到context对象中 applyInitializers(context); //触发所有SpringApplicationRunListener监听器的ContextPrepared事件方法。添加所有的事件监听器 listeners.contextPrepared(context); //记录启动日志 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 注册启动参数bean,将容器指定的参数封装成bean,注入容器 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); //设置banner if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // 加载所有资源,指的是启动器指定的参数 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); //将bean加载到上下文中 load(context, sources.toArray(new Object[0])); //触发所有springapplicationRunListener监听器的contextLoaded事件方法, listeners.contextLoaded(context); } ------------------- //这里没有做任何的处理过程,因为beanNameGenerator和resourceLoader默认为空,可以方便后续做扩展处理 protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader()); } } if (this.addConversionService) { context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); } } --------------------- //将启动器类加载到spring容器中,为后续的自动化配置奠定基础,之前看到的很多注解也与此相关 protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); } --------------------- //springboot会优先选择groovy加载方式,找不到在选择java方式 private int load(Class<?> source) { if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { // Any GroovyLoaders added in beans{} DSL can contribute beans here GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); load(loader); } if (isComponent(source)) { this.annotatedReader.register(source); return 1; } return 0; }11、刷新应用上下文refreshContext(context); private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } ------------ public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //刷新上下文环境,初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //初始化beanfactory,解析xml,相当于之前的xmlBeanfactory操作 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. //为上下文准备beanfactory,对beanFactory的各种功能进行填充,如@autowired,设置spel表达式解析器,设置编辑注册器,添加applicationContextAwareprocessor处理器等等 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //提供子类覆盖的额外处理,即子类处理自定义的beanfactorypostProcess postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //激活各种beanfactory处理器 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //注册拦截bean创建的bean处理器,即注册beanPostProcessor registerBeanPostProcessors(beanFactory); // Initialize message source for this context. //初始化上下文中的资源文件如国际化文件的处理 initMessageSource(); // Initialize event multicaster for this context. //初始化上下文事件广播器 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //给子类扩展初始化其他bean onRefresh(); // Check for listener beans and register them. //在所有的bean中查找listener bean,然后 注册到广播器中 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //初始化剩余的非懒惰的bean,即初始化非延迟加载的bean finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //发完成刷新过程,通知声明周期处理器刷新过程,同时发出ContextRefreshEvent通知别人 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. 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... resetCommonCaches(); } } }12、应用上下文刷新后置处理afterRefresh(context, applicationArguments); //当前方法的代码是空的,可以做一些自定义的后置处理操作 protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { }13、停止计时监控类:计时监听器停止,并统计一些任务执行信息stopWatch.stop(); public void stop() throws IllegalStateException { if (this.currentTaskName == null) { throw new IllegalStateException("Can't stop StopWatch: it's not running"); } long lastTime = System.nanoTime() - this.startTimeNanos; this.totalTimeNanos += lastTime; this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime); if (this.keepTaskList) { this.taskList.add(this.lastTaskInfo); } ++this.taskCount; this.currentTaskName = null; }14、输出日志记录执行主类名、时间信息if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); }15、发布应用上下文启动完成事件:触发所有SpringapplicationRunListener监听器的started事件方法listeners.started(context); void started(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.started(context); } }16、执行所有Runner执行器:执行所有applicationRunner和CommandLineRunner两种运行器callRunners(context, applicationArguments); private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }17、发布应用上下文就绪事件:触发所有springapplicationRunnListener将挺起的running事件方法listeners.running(context); void running(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.running(context); } }18、返回应用上下文return context;注意:整个springboot框架中获取factorys文件的方式统一如下:private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } ------------------------------------- private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } ----------------------------- public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } } ------------------------- private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { //装载class文件到内存 Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); //通过反射创建实例 T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); } } return instances; }spring.factory文件中的类的作用:# PropertySource Loaders 属性文件加载器 org.springframework.boot.env.PropertySourceLoader=\ # properties文件加载器 org.springframework.boot.env.PropertiesPropertySourceLoader,\ # yaml文件加载器 org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners 运行时的监听器 org.springframework.boot.SpringApplicationRunListener=\ # 程序运行过程中所有监听通知都是通过此类来进行回调 org.springframework.boot.context.event.EventPublishingRunListener # Error Reporters 错误报告器 org.springframework.boot.SpringBootExceptionReporter=\ org.springframework.boot.diagnostics.FailureAnalyzers # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ # 报告spring容器的一些常见的错误配置 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ # 设置spring应用上下文的ID org.springframework.boot.context.ContextIdApplicationContextInitializer,\ # 使用环境属性context.initializer.classes指定初始化器进行初始化规则 org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ # 将内置servlet容器实际使用的监听端口写入到environment环境属性中 org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ # 应用上下文加载完成后对缓存做清除工作,响应事件ContextRefreshEvent org.springframework.boot.ClearCachesApplicationListener,\ # 监听双亲应用上下文的关闭事件并往自己的孩子应用上下文中传播,相关事件ParentContextAvailableEvent/ContextClosedEvent org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ # 如果系统文件编码和环境变量中指定的不同则终止应用启动。具体的方法是比较系统属性file.encoding和环境变量spring.mandatory-file-encoding是否相等(大小写不敏感)。 org.springframework.boot.context.FileEncodingApplicationListener,\ # 根据spring.output.ansi.enabled参数配置AnsiOutput org.springframework.boot.context.config.AnsiOutputApplicationListener,\ # EnvironmentPostProcessor,从常见的那些约定的位置读取配置文件,比如从以下目录读取#application.properties,application.yml等配置文件: # classpath: # file:. # classpath:config # file:./config/: # 也可以配置成从其他指定的位置读取配置文件 org.springframework.boot.context.config.ConfigFileApplicationListener,\ # 监听到事件后转发给环境变量context.listener.classes指定的那些事件监听器 org.springframework.boot.context.config.DelegatingApplicationListener,\ # 一个SmartApplicationListener,对环境就绪事件ApplicationEnvironmentPreparedEvent/应用失败事件ApplicationFailedEvent做出响应,往日志DEBUG级别输出TCCL(thread context class loader)的classpath。 org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ # 检测正在使用的日志系统,默认时logback,,此时日志系统还没有初始化 org.springframework.boot.context.logging.LoggingApplicationListener,\ # 使用一个可以和Spring Boot可执行jar包配合工作的版本替换liquibase ServiceLocator org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\ org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor # Failure Analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer # FailureAnalysisReporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ # 另外单独启动一个线程实例化并调用run方法,包括验证器、消息转换器等 org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\ org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\ org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\ org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\ org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\ org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer # Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
2022年04月26日
195 阅读
0 评论
1 点赞
2022-03-24
SpringBoot整合SpringCache
SpringBoot整合SpringCache1、引入依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>2、引入redis依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>配置redis地址密码等等,可参考springboo整合redis3、默认配置自动配置// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.autoconfigure.cache; import java.util.List; import java.util.stream.Collectors; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.CacheAspectSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.util.Assert; @Configuration( proxyBeanMethods = false ) @ConditionalOnClass({CacheManager.class}) @ConditionalOnBean({CacheAspectSupport.class}) @ConditionalOnMissingBean( value = {CacheManager.class}, name = {"cacheResolver"} ) @EnableConfigurationProperties({CacheProperties.class}) @AutoConfigureAfter({CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class}) @Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class, CacheAutoConfiguration.CacheManagerEntityManagerFactoryDependsOnPostProcessor.class}) public class CacheAutoConfiguration { public CacheAutoConfiguration() { } @Bean @ConditionalOnMissingBean public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) { return new CacheManagerCustomizers((List)customizers.orderedStream().collect(Collectors.toList())); } @Bean public CacheAutoConfiguration.CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { return new CacheAutoConfiguration.CacheManagerValidator(cacheProperties, cacheManager); } static class CacheConfigurationImportSelector implements ImportSelector { CacheConfigurationImportSelector() { } public String[] selectImports(AnnotationMetadata importingClassMetadata) { CacheType[] types = CacheType.values(); String[] imports = new String[types.length]; for(int i = 0; i < types.length; ++i) { imports[i] = CacheConfigurations.getConfigurationClass(types[i]); } return imports; } } static class CacheManagerValidator implements InitializingBean { private final CacheProperties cacheProperties; private final ObjectProvider<CacheManager> cacheManager; CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { this.cacheProperties = cacheProperties; this.cacheManager = cacheManager; } public void afterPropertiesSet() { Assert.notNull(this.cacheManager.getIfAvailable(), () -> { return "No cache manager could be auto-configured, check your configuration (caching type is '" + this.cacheProperties.getType() + "')"; }); } } @ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class}) @ConditionalOnBean({AbstractEntityManagerFactoryBean.class}) static class CacheManagerEntityManagerFactoryDependsOnPostProcessor extends EntityManagerFactoryDependsOnPostProcessor { CacheManagerEntityManagerFactoryDependsOnPostProcessor() { super(new String[]{"cacheManager"}); } } }所有相关配置都在CacheProperties。重点关注自动配置中的: static class CacheConfigurationImportSelector implements ImportSelector { CacheConfigurationImportSelector() { } public String[] selectImports(AnnotationMetadata importingClassMetadata) { CacheType[] types = CacheType.values(); String[] imports = new String[types.length]; for(int i = 0; i < types.length; ++i) { imports[i] = CacheConfigurations.getConfigurationClass(types[i]); } return imports; } }CacheConfigurations.getConfigurationClass(types[i]); static String getConfigurationClass(CacheType cacheType) { String configurationClassName = (String)MAPPINGS.get(cacheType); Assert.state(configurationClassName != null, () -> { return "Unknown cache type " + cacheType; }); return configurationClassName; }MAPPINGS.get(cacheType);static { Map<CacheType, String> mappings = new EnumMap(CacheType.class); mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class.getName()); mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class.getName()); mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class.getName()); mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class.getName()); mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class.getName()); mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class.getName()); mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName()); mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class.getName()); mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName()); mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName()); MAPPINGS = Collections.unmodifiableMap(mappings); }mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName());CacheAutoConfiguration会导入RedisCacheConfiguration;// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.autoconfigure.cache; import java.util.LinkedHashSet; import java.util.List; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.cache.CacheProperties.Redis; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ResourceLoader; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair; @Configuration( proxyBeanMethods = false ) @ConditionalOnClass({RedisConnectionFactory.class}) @AutoConfigureAfter({RedisAutoConfiguration.class}) @ConditionalOnBean({RedisConnectionFactory.class}) @ConditionalOnMissingBean({CacheManager.class}) @Conditional({CacheCondition.class}) class RedisCacheConfiguration { RedisCacheConfiguration() { } @Bean RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers, RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) { RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(this.determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader())); List<String> cacheNames = cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet(cacheNames)); } if (cacheProperties.getRedis().isEnableStatistics()) { builder.enableStatistics(); } redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> { customizer.customize(builder); }); return (RedisCacheManager)cacheManagerCustomizers.customize(builder.build()); } private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(CacheProperties cacheProperties, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ClassLoader classLoader) { return (org.springframework.data.redis.cache.RedisCacheConfiguration)redisCacheConfiguration.getIfAvailable(() -> { return this.createConfiguration(cacheProperties, classLoader); }); } private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(CacheProperties cacheProperties, ClassLoader classLoader) { Redis redisProperties = cacheProperties.getRedis(); org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig(); config = config.serializeValuesWith(SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader))); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixCacheNameWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; } } 关注里面的方法:@Bean RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers, RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) { RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(this.determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader())); List<String> cacheNames = cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet(cacheNames)); } if (cacheProperties.getRedis().isEnableStatistics()) { builder.enableStatistics(); } redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> { customizer.customize(builder); }); return (RedisCacheManager)cacheManagerCustomizers.customize(builder.build()); }相当于自动配置好了缓存管理器RedisCacheManager,cacheProperties.getCacheNames();方法会读取yml里面的配置,接着初始化缓存builder.initialCacheNames(new LinkedHashSet(cacheNames)); public RedisCacheManager.RedisCacheManagerBuilder initialCacheNames(Set<String> cacheNames) { Assert.notNull(cacheNames, "CacheNames must not be null!"); cacheNames.forEach((it) -> { this.withCacheConfiguration(it, this.defaultCacheConfiguration); }); return this; }this.withCacheConfiguration(it, this.defaultCacheConfiguration);public RedisCacheManager.RedisCacheManagerBuilder withCacheConfiguration(String cacheName, RedisCacheConfiguration cacheConfiguration) { Assert.notNull(cacheName, "CacheName must not be null!"); Assert.notNull(cacheConfiguration, "CacheConfiguration must not be null!"); this.initialCaches.put(cacheName, cacheConfiguration); return this; }RedisCacheConfiguration中默认缓存定义规则:private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(CacheProperties cacheProperties, ClassLoader classLoader) { Redis redisProperties = cacheProperties.getRedis(); org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig(); config = config.serializeValuesWith(SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader))); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixCacheNameWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; }redisProperties也就是从yml中得到的配置。4、配置需要配置的地方:配置使用redis作为缓存spring.cache.type=redis使用缓存,官方文档 https://docs.spring.io/spring-framework/docs/5.3.18-SNAPSHOT/reference/html/integration.html#cache重点关注几个注解:@Cacheable: Triggers cache population. @CacheEvict: Triggers cache eviction. @CachePut: Updates the cache without interfering with the method execution. @Caching: Regroups multiple cache operations to be applied on a method. @CacheConfig: Shares some common cache-related settings at class-level.@Cacheable: Triggers cache population.触发将数据保存到缓存的操作@CacheEvict: Triggers cache eviction.触发将数据从缓存删除的操作@CachePut: Updates the cache without interfering with the method execution.不影响方法执行更新缓存@Caching: Regroups multiple cache operations to be applied on a method.组合以上多个操作@CacheConfig: Shares some common cache-related settings at class-level.在类级别共享缓存的相同配置5、自定义规则第一步、启动类上开启缓存功能:@EnableCaching第二部、只需要注解就能完成缓存操作@Cacheable:1、代表当前方法的结果需要缓存,如果缓存中有,方法不用调用。如果缓存中没有,会调用方法,最后将方法的结果放入缓存。2、每一个需要缓存的数据我们都来指定要翻到那个名字的缓存【缓存的分区,推荐按照业务类型分】。3、key默认自动生成,value默认是使用jdk默认序列化机制。默认时间是-1,永不过期。因此需要我们自定义规则:自定义规则/** * @description: Cache redis数据格式配置 * @date: 2022/2/17 21:59 * @version: 1.0 */ import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer; import org.springframework.boot.autoconfigure.cache.CacheProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; @EnableConfigurationProperties(CacheProperties.class) @Configuration @EnableCaching public class MyCacheConfig { @Bean RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){ RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())); config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer())); CacheProperties.Redis redisProperties = cacheProperties.getRedis(); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } // if (redisProperties.getKeyPrefix() != null) { // config = config.prefixCacheNameWith(redisProperties.getKeyPrefix()); // } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; } }6、使用示例// 使用缓存注解方式,key:为方法名 @Override @Cacheable(value = {"record"},key = "#root.method.name") public RecordEntity getRecordAllInfoById(Long id) { //未使用注解缓存方案 // RecordEntity recordEntity = null; // // ValueOperations<String, String> forValue = redisTemplate.opsForValue(); // String recordData = forValue.get("RecordData"); // if(recordData==null){ // System.out.println("缓存没数据,执行查询数据库方法。。。。"); // recordEntity = getEntityByIdByFbsRedisson(id); // }else{ // System.out.println("从缓存中获取数据。。。。。"); // recordEntity = JSON.parseObject(recordData, new TypeReference<RecordEntity>() { // }); // } //使用注解缓存后 RecordEntity recordEntity = getEntityById(id); if(recordEntity!=null){ Long categoryId = recordEntity.getCategoryId(); Long[] catelogPath = findCatelogPath(categoryId); recordEntity.setCatelogPath(catelogPath); } return recordEntity; }key使用SPEL表达式规则可参考官网中 Using Custom Annotations使用规则。https://docs.spring.io/spring-framework/docs/5.3.18-SNAPSHOT/reference/html/integration.html#cache-annotation-stereotype
2022年03月24日
624 阅读
0 评论
8 点赞
2022-03-17
Maven依赖冲突解决办法
Maven依赖冲突解决方案1、找到项目pom文件。2、快捷键打开依赖图。ctlr+alt+shift+u3、红色表示冲突。4、右键exclude排除。
2022年03月17日
140 阅读
0 评论
5 点赞
2022-03-15
SpringBoot整合定时任务和异步任务
SpringBoot整合定时任务和异步任务一、定时任务SpringBoot整合quartz-scheduler,执行定时任务。1、开启定时任务@EnableScheduling2、开启一个定时任务@Scheduled3、编写cron表达式cron表达式格式请参考官方文档4、实例package com.yanxizhu.ifamily.booking.controller.scheduler; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** * SpringBoot整合Scheduling定时任务 * @author: <a href="mailto:vip@foxmail.com">清风</a> * @date: 2022/3/15 20:26 * @version: 1.0 */ @Slf4j @Component @EnableScheduling public class MyScheduler { /** * cron表达式 */ @Scheduled(cron = "1/3 1/1 * * * ? ") public void sayHell(){ log.info("Hello"); } }5、定时任务总结1、@EnableScheduling 开启一个定时任务2、@Scheduled 开启一个定时任务3、编写定时规则,也就是cron表达式。6、注意事项spring中6位组成,不允许第7位的年。在周几的位置,1-7代表周一到周日,MON-SUN定时任务不应该阻塞,默认时阻塞的,应该以异步任务执行。7、解决方案解决定时任务阻塞问题:使用异步+定时任务二、异步任务执行几种方式1、异步方式提交线程池可以让业务运行以异步的方式,自己提交到线程池(CompletableFuture异步编排)。@Slf4j @Component @EnableScheduling public class MyScheduler { public static ExecutorService executorService = Executors.newFixedThreadPool(10); /** * cron表达式 */ @Scheduled(cron = "1/3 1/1 * * * ? ") public void sayHell(){ CompletableFuture.runAsync(()->{ //调用server业务方法 }, executorService); } }2、定时任务线程池支持定时任务线程池(定时任务线程池)。Scheduling自动配置类,默认只有1个线程。// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.autoconfigure.task; import java.util.concurrent.ScheduledExecutorService; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.LazyInitializationExcludeFilter; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.task.TaskSchedulingProperties.Shutdown; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.task.TaskSchedulerBuilder; import org.springframework.boot.task.TaskSchedulerCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @ConditionalOnClass({ThreadPoolTaskScheduler.class}) @Configuration( proxyBeanMethods = false ) @EnableConfigurationProperties({TaskSchedulingProperties.class}) @AutoConfigureAfter({TaskExecutionAutoConfiguration.class}) public class TaskSchedulingAutoConfiguration { public TaskSchedulingAutoConfiguration() { } @Bean @ConditionalOnBean( name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"} ) @ConditionalOnMissingBean({SchedulingConfigurer.class, TaskScheduler.class, ScheduledExecutorService.class}) public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) { return builder.build(); } @Bean public static LazyInitializationExcludeFilter scheduledBeanLazyInitializationExcludeFilter() { return new ScheduledBeanLazyInitializationExcludeFilter(); } @Bean @ConditionalOnMissingBean public TaskSchedulerBuilder taskSchedulerBuilder(TaskSchedulingProperties properties, ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) { TaskSchedulerBuilder builder = new TaskSchedulerBuilder(); builder = builder.poolSize(properties.getPool().getSize()); Shutdown shutdown = properties.getShutdown(); builder = builder.awaitTermination(shutdown.isAwaitTermination()); builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); builder = builder.customizers(taskSchedulerCustomizers); return builder; } }默认只有一个线程package org.springframework.boot.autoconfigure.task; import java.time.Duration; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("spring.task.scheduling") public class TaskSchedulingProperties { private final TaskSchedulingProperties.Pool pool = new TaskSchedulingProperties.Pool(); private final TaskSchedulingProperties.Shutdown shutdown = new TaskSchedulingProperties.Shutdown(); private String threadNamePrefix = "scheduling-"; public TaskSchedulingProperties() { } public TaskSchedulingProperties.Pool getPool() { return this.pool; } public TaskSchedulingProperties.Shutdown getShutdown() { return this.shutdown; } public String getThreadNamePrefix() { return this.threadNamePrefix; } public void setThreadNamePrefix(String threadNamePrefix) { this.threadNamePrefix = threadNamePrefix; } public static class Shutdown { private boolean awaitTermination; private Duration awaitTerminationPeriod; public Shutdown() { } public boolean isAwaitTermination() { return this.awaitTermination; } public void setAwaitTermination(boolean awaitTermination) { this.awaitTermination = awaitTermination; } public Duration getAwaitTerminationPeriod() { return this.awaitTerminationPeriod; } public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) { this.awaitTerminationPeriod = awaitTerminationPeriod; } } public static class Pool { private int size = 1; public Pool() { } public int getSize() { return this.size; } public void setSize(int size) { this.size = size; } } }配置线程池数量spring.task.scheduling.pool.size=5这样定时任务就有5个线程可以执行了,如果1个线程,定时任务就会阻塞。3、让定时任务执行@EnableAsync 开启异步任务功能@Async 在希望异步执行的方法开启异步执行注解,普通方法也可以使用。默认线程数8异步任务自动配置类源码:// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.autoconfigure.task; import java.util.concurrent.Executor; import java.util.stream.Stream; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.task.TaskExecutionProperties.Pool; import org.springframework.boot.autoconfigure.task.TaskExecutionProperties.Shutdown; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.task.TaskExecutorBuilder; import org.springframework.boot.task.TaskExecutorCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @ConditionalOnClass({ThreadPoolTaskExecutor.class}) @Configuration( proxyBeanMethods = false ) @EnableConfigurationProperties({TaskExecutionProperties.class}) public class TaskExecutionAutoConfiguration { public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor"; public TaskExecutionAutoConfiguration() { } @Bean @ConditionalOnMissingBean public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties, ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers, ObjectProvider<TaskDecorator> taskDecorator) { Pool pool = properties.getPool(); TaskExecutorBuilder builder = new TaskExecutorBuilder(); builder = builder.queueCapacity(pool.getQueueCapacity()); builder = builder.corePoolSize(pool.getCoreSize()); builder = builder.maxPoolSize(pool.getMaxSize()); builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout()); builder = builder.keepAlive(pool.getKeepAlive()); Shutdown shutdown = properties.getShutdown(); builder = builder.awaitTermination(shutdown.isAwaitTermination()); builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); Stream var10001 = taskExecutorCustomizers.orderedStream(); var10001.getClass(); builder = builder.customizers(var10001::iterator); builder = builder.taskDecorator((TaskDecorator)taskDecorator.getIfUnique()); return builder; } @Lazy @Bean( name = {"applicationTaskExecutor", "taskExecutor"} ) @ConditionalOnMissingBean({Executor.class}) public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) { return builder.build(); } }默认线程8package org.springframework.boot.autoconfigure.task; import java.time.Duration; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("spring.task.execution") public class TaskExecutionProperties { private final TaskExecutionProperties.Pool pool = new TaskExecutionProperties.Pool(); private final TaskExecutionProperties.Shutdown shutdown = new TaskExecutionProperties.Shutdown(); private String threadNamePrefix = "task-"; public TaskExecutionProperties() { } public TaskExecutionProperties.Pool getPool() { return this.pool; } public TaskExecutionProperties.Shutdown getShutdown() { return this.shutdown; } public String getThreadNamePrefix() { return this.threadNamePrefix; } public void setThreadNamePrefix(String threadNamePrefix) { this.threadNamePrefix = threadNamePrefix; } public static class Shutdown { private boolean awaitTermination; private Duration awaitTerminationPeriod; public Shutdown() { } public boolean isAwaitTermination() { return this.awaitTermination; } public void setAwaitTermination(boolean awaitTermination) { this.awaitTermination = awaitTermination; } public Duration getAwaitTerminationPeriod() { return this.awaitTerminationPeriod; } public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) { this.awaitTerminationPeriod = awaitTerminationPeriod; } } public static class Pool { private int queueCapacity = 2147483647; private int coreSize = 8; private int maxSize = 2147483647; private boolean allowCoreThreadTimeout = true; private Duration keepAlive = Duration.ofSeconds(60L); public Pool() { } public int getQueueCapacity() { return this.queueCapacity; } public void setQueueCapacity(int queueCapacity) { this.queueCapacity = queueCapacity; } public int getCoreSize() { return this.coreSize; } public void setCoreSize(int coreSize) { this.coreSize = coreSize; } public int getMaxSize() { return this.maxSize; } public void setMaxSize(int maxSize) { this.maxSize = maxSize; } public boolean isAllowCoreThreadTimeout() { return this.allowCoreThreadTimeout; } public void setAllowCoreThreadTimeout(boolean allowCoreThreadTimeout) { this.allowCoreThreadTimeout = allowCoreThreadTimeout; } public Duration getKeepAlive() { return this.keepAlive; } public void setKeepAlive(Duration keepAlive) { this.keepAlive = keepAlive; } } }通过配置线程池线程数spring.task.execution.pool.core-size=5 spring.task.execution.pool.max-size=50实列import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Slf4j @Component @EnableAsync @EnableScheduling public class MyScheduler { public static ExecutorService executorService = Executors.newFixedThreadPool(10); /** * cron表达式 */ @Async @Scheduled(cron = "1/3 1/1 * * * ? ") public void sayHell(){ System.out.println("Hello ...."); } }要用线程池的时候,自己注入即可用了。
2022年03月15日
297 阅读
0 评论
12 点赞
2022-03-14
Nginx反向代理微服务配置
Nginx反向代理微服务配置一、本地域名服务访问过程二、正向代理和反向代理三、Nginx+Windows搭建域名访问环境1、修改C:\Windows\System32\drivers\etc下hosts文件,映射本地域名和192.168.56.10ip关系。每次通过记事本修改很麻烦,可以通过SwitchHosts软件修改。192.168.56.10 family.com2、现在就可以通过family.com域名访问了。想要访问部署在服务器上的其它端口应用也可以通过域名访问,比如:搭建的kibana是5601端口,通过域名http://family.com:5601/即可访问了(kibana时部署在56.10服务器上的)四、nginx.conf user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; }Nginx配置文件说明:1、全局块:配置影响nginx全局的指令。如:用户组、nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成wordker process数等。2、events块:配置影响nginx服务器或用户的网络链接。如:每个进程的最大连接数,选取那种事件驱动模型处理链接请求,是否允许同时接收多个网络链接,开启多个网络链接序列化等。3、http块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,是否使用sendfile传输文件,链接超时事件,单链接请求数等。 http块里面又包含: 3.1、http全局块:如upstream,错误页面,连接超时等。 3.2、server块: 配置虚拟注解的相关参数,一个http中可以有多个wever。 server块里面又包含: 3.2.1、location:配置请求的路由,以及各种页面的处理情况。 注意: server块里面可以包含多个location。4、include /etc/nginx/conf.d/*.conf;注意:会包含所有include /etc/nginx/conf.d/这个目录下的所有配置文件。比如/etc/nginx/conf.d/默认有个default.conf配置文件,里面就只包含了server块。server { listen 80; -----------nginx监听的端口 server_name localhost; ------------nginx监听的端口 #charset koi8-r; #access_log /var/log/nginx/log/host.access.log main; ----访问日志 location / { ---------------- /表示上面监听域名下所有的请求,都可以在root目录下找,包括首页index root /usr/share/nginx/html index index.html index.htm; } #error_page 404 /404.html; ----------404错误页面路径配置 # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; ---------500页面路径配置 location = /50x.html { root /usr/share/nginx/html; }五、反向代理配置配置实例:因为上面修改了host文件映射了域名到192.168.56.10服务器,所有通过服务上nginx访问本地127.0.0.1的springboot项目就可以通过配置反向代理域名访问。将通过nginx访问family.com的所有请求,通过nginx反向代理到本地的springboot项目地址本地springboot项目ip192.168.56.1:。server { listen 80; server_name family.com; #charset koi8-r; #access_log /var/log/nginx/log/host.access.log main; location / { proxy_pass http://192.168.56.1:40000; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }梳理流程:1、修改本地host文件,目的是模拟类似访问外网的一个域名,如果有服务器域名绑定了ip,就可以不用这步骤了。2、修改host映射family.com ------->192.168.56.10 (服务地址)。3、访问family.com就相当于访问192.168.56.10 (服务地址)。4、将所有family.com(这个域名默认端口为80)的所有请求,通过nginx反向代理到局域网内的其它服务器,比如这里就是本地搭建的springboot项目端口40000。5、通过反向代理配置listen 80; ------监控哪个端口 server_name family.com; ------监听哪个域名 location / { ---- /:表示所有请求 proxy_pass http://192.168.56.1:40000; --将family.com域名80端,反向代理到http://192.168.56.1:40000 }6、现在family.com:80端口的所有请求就会反向代理到http://192.168.56.1:40000了。反向代理后访问地址:七、nginx代理网关如果我们服务多了,每次都要修改域名,修改nginx配置反向代理到多个服务,这样比较麻烦。那我们就可以通过配置nginx代理到微服务的网关,让网关来负载均衡到各服务。nginx负载均衡参考地址nginx反向代理局域网内网关服务(192.168.56.1:88)。nginx配置如下: user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; upstream family { server 192.168.56.1:88; } include /etc/nginx/conf.d/*.conf; }注意修改的地方:upstream family { ----网关取的名字 server 192.168.56.1:88; -----网关服务地址 server srv2.example.com; ----可以配置多个网关地址,我这只有一个网关配一个即可 server srv3.example.com; server srv4.example.com; }修改yanxizhu.nginx配置:server { listen 80; server_name family.com; #charset koi8-r; #access_log /var/log/nginx/log/host.access.log main; location / { proxy_pass http://family; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }注意修改点:location / { proxy_pass http://family; ---这里就反向代理到family这个网关了,也就是上面取的网关名字 }接着就开始配置getway网关服务请求:注意:nginx代理给网关的时候会丢失host信息。解决办法;yanxizhu.conf添加代理host的header。proxy_set_header Host $proxy_host;family.conf整体如下:server { listen 80; server_name family.com; #charset koi8-r; #access_log /var/log/nginx/log/host.access.log main; location / { proxy_set_header Host $proxy_host; proxy_pass http://family; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }getway网关服务负载均衡配置: - id: family_host_route uri: lb://family-booking predicates: - Host=**.family.com比如访问http://family.com/时,nginx就会将请求反向代理给网关服务(192.168.56.1:88),而网关服务通过配置的负载均衡,又会将**.family.com域名的请求,负载均衡到family-booking微服务的80端口。以上就是nginx的反向代理配置。注意将getway网关的配置,放在最后,不然全部服务都被代理到family_host_route了。八、静态资源配置将前端静态资源上传到nginx静态资源目录,后端主页面路径加上static路径。server { listen 80; server_name family.com; #charset koi8-r; #access_log /var/log/nginx/log/host.access.log main; location /static/ { root /var/www/html; } location / { proxy_set_header Host $proxy_host; proxy_pass http://family; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }说明:location /static/ { root /var/www/html; }后端主页访问静态资源都添加上/static/静态资源目录,当请求static资源路径时,会被nginx处理请求nginx下配置的静态资源。
2022年03月14日
716 阅读
1 评论
11 点赞
2022-03-14
SpringBoot整合Sleuth+Zipkin 服务链路追踪
SpringBoot整合Sleuth+Zipkin 服务链路追踪一、整合 Sleu1、服务提供者与消费者导入依赖<!--整合 Sleut:服务提供者与消费者导入依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> <version>3.1.1</version> </dependency>2、打开 debug 日志logging: level: org.springframework.cloud.openfeign: debug org.springframework.cloud.sleuth: debug3、发起一次远程调用,观察控制台DEBUG [user-service,541450f08573fff5,541450f08573fff5,false]user-service:服务名 541450f08573fff5:是 TranceId,一条链路中,只有一个 TranceId 541450f08573fff5:是 spanId,链路中的基本工作单元 id false:表示是否将数据输出到其他服务,true 则会把信息输出到其他可视化的服务上观察二、整合 zipkin 可视化观察1、docker 安装 zipkin 服务器docker run --name zipkin -d -p 9411:9411 openzipkin/zipkin设置随docker启动docker update --restart=always zipkin2、导入依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> <version>2.2.8.RELEASE</version> </dependency>因为spring-cloud-starter-zipkin中引入了spring-cloud-starter-sleuth,因此上面的spring-cloud-starter-sleuth可以不用引入。3、添加 zipkin 相关配置spring: application: name: ifamily-booking zipkin: base-url: http://127.0.0.1:9411/ # zipkin 服务器的地址 # 关闭服务发现,否则 Spring Cloud 会把 zipkin 的 url 当做服务名称 discoveryClientEnabled: false sender: type: web # 设置使用 http 的方式传输数据 sleuth: sampler: probability: 1 # 设置抽样采集率为 100%,默认为 0.1,即 10%4、查看通过http://127.0.0.1:9411即可查看链路追踪信息。三、持久化链路追踪数据到ES启动命令docker run --env STORAGE_TYPE=elasticsearch --env ES_HOSTS=192.168.56.10:9200 openzipkin/zipkin-dependencies注意:这个是openzipkin/zipkin-dependencies,改版后的。
2022年03月14日
356 阅读
0 评论
7 点赞
2022-03-13
SpringBoot整合Sentinel
SpringBoot整合Sentinel1、引入依赖<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>2、下载安装Sentinel可视化控制台下载自己引入sentinel对应的核心包版本,下载地址:https://github.com/alibaba/Sentinel/releases运行Sentinel可视化控制台java -jar sentinel-dashboard-1.8.1.jar注意自己的版本。打开http://127.0.0.1:8080/,默认账号密码sentinel3、微服务配置sentinelapplication.ymlspring: cloud: sentinel: transport: port: 8719 dashboard: localhost:8080port: 8719端口随意,只要不被占用,用于各微服务与控制台通讯。4、查看监控信息启动微服务,随意访问一个接口,Sentinel控制台即可看到实施监控信息。5、启用相关功能可在Sentinel控制台调整相应功能。默认所有流控设置都是保存在内存中,重启服务就没有了。6、添加监控图标引入审计start,sentinel会自动根据spring-boot-starter-actuator监控。<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.6.1</version> </dependency>开启监控图标management.endpoints.web.exposure.include=*7、自定义流控返回提示信息import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSON; import com.yanxizhu.common.utils.R; import org.springframework.context.annotation.Configuration; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @description: Sentinel流控信息提示自定i * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/13 20:14 * @version: 1.0 */ @Configuration public class MySentinelConfig implements BlockExceptionHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception { R r = R.error(10000, "自定义提示"); httpServletResponse.setContentType("application/json;charset=utf-8"); httpServletResponse.getWriter().write(JSON.toJSONString(r)); } }限流规则可参考官网限流文档。每一个微服务都有一个自己的自定义流控返回提示信息,其它配置一样,只是提示信息不同。8、熔断、降级针对Feign远程调用熔断引入Feign远程调用依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>打开 Sentinel 对 Feign 的支持:feign.sentinel.enabled=true熔断实现@FeignClient(value = "renren-fast",fallback = SeckillFeignServiceFallBack.class) public interface CardFeignService { @GetMapping("/sys/user/list") public R list(@RequestParam Map<String, Object> params); }熔断回调@S1f4j @Component public class SeckillFeignServiceFallBack implements SeckillFeignService{ @Override public R getskuseckillInfo(Long skuld) { Log.info("熔断方法调用...getskuSecki11Info"); return R.error(com.yanxizhu.family.common.exception.BizCodeEnume.TOO_MWANY_REQUEST.getCode(); com.yanxizhu.family.common.exception.BizCodeEnume.TOO_MANY_REQUEST.getAsg()); } }调用方:手动指定远程服务的降级策略。远程服务被降级处理。触发我们的熔断回调方法。提供方:超大浏览的时侯,必须牺牲一些远程服务。在服务的提供方(远程服务)指定降级策略;提供方是在运行。但是不运行自己的业务逻辑,返回的是默认的降级数据(限流的数据)。@SentinelResource 注解用来标识资源是否被限流、降级。上述例子上该注解的属性 sayHello 表示资源名。@SentinelResource 还提供了其它额外的属性如 blockHandler,blockHandlerClass,fallback 用于表示限流或降级的操作(注意有方法签名要求),更多内容可以参考 Sentinel 注解支持文档。若不配置 blockHandler、fallback 等函数,则被流控降级时方法会直接抛出对应的 BlockException;若方法未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException。注:一般推荐将 @SentinelResource 注解加到服务实现上,而在 Web 层直接使用 Spring Cloud Alibaba 自带的 Web 埋点适配。Sentinel Web 适配同样支持配置自定义流控处理逻辑,参考 相关文档。9、自定义受保护的资源1)、代码try(Entry entry =SphU.entry("seckillSkus"))(/业务逻辑catch(Execption e)(}2)、基于注解。eSentinelResource(value ="getCurrentSeckiLLSkusResource",blockHandler ="blockHandler")无论是1,2方式一定要配置被限流以后的默认返回注:一般推荐将 @SentinelResource 注解加到服务实现上,而在 Web 层直接使用 Spring Cloud Alibaba 自带的 Web 埋点适配。Sentinel Web 适配同样支持配置自定义流控处理逻辑,参考 相关文档。10、网关限流引入网关sentinel依赖<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId> <version>x.y.z</version> </dependency>使用时只需注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可(若使用了 Spring Cloud Alibaba Sentinel,则只需按照文档进行配置即可,无需自己加 Configuration)。更多网关限流可参考官方文档11、定制网关流控返回参考官方文档
2022年03月13日
269 阅读
0 评论
5 点赞
2022-03-13
RabbitMQ延时队列及消息可靠性
RabbitMQ延时队列及消息可靠性
2022年03月13日
283 阅读
0 评论
5 点赞
1
2
3