首页
关于
友链
Search
1
wlop 4K 壁纸 4k8k 动态 壁纸
713 阅读
2
Docker搭建Typecho博客
566 阅读
3
Nacos持久化MySQL问题-解决方案
430 阅读
4
SpringBoot整合SpringCache
388 阅读
5
keytool证书导入
381 阅读
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
开发工具
百度网盘资源
天翼网盘资源
阿里网盘资源
登录
Search
标签搜索
java
javase
docker
java8
springboot
thread
spring
分布式
mysql
锁
linux
redis
源码
typecho
centos
git
map
lambda
stream
nginx
少年
累计撰写
183
篇文章
累计收到
10
条评论
首页
栏目
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
开发工具
百度网盘资源
天翼网盘资源
阿里网盘资源
页面
关于
友链
搜索到
20
篇与
的结果
2022-05-09
Docker安装Jenkins自动部署SpringBoot项目
Docker安装Jenkins自动部署SpringBoot项目根据之前文章《使用Docker安装好Jenkins》为前提搭建好Jenkins,不明白请看https://www.yanxizhu.com/index.php/archives/138/。环境说明:jenkins为docker部署,Docker+Jenkins+Gitee+JDK11+Maven3.8.5。以后每次改动代码,push提交到giee码云后会自动部署,不用手动点击部署。一、全局工具配置【首页】-【系统管理】-【全局工具配置】我之前启动jenkins容器映射参数如下,根据自己映射路径自行修改。docker run -p 10240:8080 -p 10241:50000 --name jenkins \ -u root \ -v /mydata/jenkins_home:/var/jenkins_home \ -v /mydata/maven/apache-maven-3.8.5:/maven/apache-maven-3.8.5 \ -v /mydata/jdk/jdk-11.0.10/:/jdk/jdk-11.0.10 \ -v /mydata/maven/repo:/mydata/maven/repo \ -v /usr/bin/docker:/usr/bin/docker \ -v /var/run/docker.sock:/var/run/docker.sock \ -d jenkins/jenkins:lts上面很重要,注意。jdk配置jdk11路径/jdk/jdk-11.0.10maven配置maven3.8.5路径/maven/apache-maven-3.8.5git配置Default路径/usr/bin/gitdocker配置docker路径/usr/bin注意点:1、jenkins容器里面自带git,可通过命令查看路径。2、注意自己jdk、mavn、docker安装路径。查看jenkins自带git路径命令:which git二、插件安装【首页】-【系统管理】-【插件管理】插件1:Publish Over SSH插件2:Gitee Plugin如果插件安装慢,可以修改源,请参考修改方案,https://www.yanxizhu.com/index.php/archives/138/注意:如果在【全局工具配置】没有对应的选项,就是缺少相应插件。三、系统配置1、SSH remote hosts配置新增加配置ssh登陆凭证,此步骤的主要作用是jenkins 打包镜像后,能够远程去登陆和执行脚本文件。Hostname:xxx.xxx.xxx.x..(需要登陆的服务器ip)Port:22(ssh登陆端口)Credentials:登陆账号和密码(此处点击[添加]按钮增加一个)如果是本机可以不用配置2、Gitee 配置链接名:giteeGitee 域名 URL:https://gitee.com添加凭证Gitee API V5 的私人令牌(获取地址 https://gitee.com/profile/personal_access_tokens)通过上面连接创建一个令牌,然后添加到这里。四、准备项目1、本地新建一个SpringBoot项目,新建HellocerConller控制层package com.yanxizhu.jenkins.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @description: Jenkins自动部署测试 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/5/8 17:30 * @version: 1.0 */ @RestController public class HelloController { @GetMapping("/hello") public String hello(){ return "Hello World!"; } }本地启动项目确保通过127.0.0.1:8080/hello能够访问。2、编写Dockerfile# 指定是基于哪个基础镜像 FROM openjdk:11 # 作者信息 MAINTAINER batis # 挂载点声明 VOLUME /tmp # 将本地的一个文件或目录,拷贝到容器的文件或目录里 ADD /target/jenkins-demo-0.0.1-SNAPSHOT.jar springboot.jar #shell脚本 RUN bash -c 'touch /springboot.jar' # 将容器的8000端口暴露,给外部访问。 EXPOSE 8000 # 当容器运行起来时执行使用运行jar的指令 ENTRYPOINT ["java", "-jar", "springboot.jar"]注意:修改jdk版本、打包后名称、端口信息五、代码上传登录码云新建仓库,名字随意将代码关联并提交到码云。新建仓库、代码push自行google。六、WebHooks 管理配置1、打开仓库 -> 管理 -> 右侧的webhooksURL:填入服务器公网IP地址WebHook密码通过以下生成。七、部署SpringBoot项目1、新建部署任务任务名字随意、构建一个自由风格的软件项目。2、General描述随意填写,丢弃旧的构建策略,保持构建的天数1,保持构建的最大个数3,根据自己需要自行修改。3、源码管理选择gitRepository URL为自己gitee码云仓库地址。Credentials点击“添加”,Credentials凭证,选择通过用户名密码添加,id、备注可以为空。4、构建触发器其它默认:找到Gitee WebHook 密码,点击“生成按钮”生成,然后将该密码填入上面 “六、WebHooks 管理配置”中。轮询 SCM策略:* * * * *注意:*中间有空格,当您输入 "* * * * *" 时,意思为"每分钟"?也许您希望 "H * * * *" 每小时轮询。5、构建选择执行shell脚本#!/bin/bash -lex docker rm -f app_docker sleep 1 docker rmi -f app_docker:1.0 sleep 1 mvn clean install -Dmaven.test.skip=true sleep 1 docker build -t app_docker:1.0 -f ./src/main/Dockerfile . sleep 1 docker run -d -p 8000:8000 --name app_docker app_docker:1.0 注意自己端口名称。6、访问测试通过自己xxx.xxx.xx.xx:8000/hello即可自己写的helloword了。以后每次改动代码,push提交到giee码云后会自动部署,不用手动点击部署。7、问题记录及解决方案比如:1、查不到mvn、docker、jdk命令,可能是jenkins容器中环境配置问题,可以参考《Jenkins容器docker部署springboot项目-问题记录》2、如果开启了防火墙注意开发相应端口或关闭防火墙3、部署遇到问题,查看部署日志,以及google、baidu相关参考:Docker开启Remote API访问docker启动Jenkins报错Docker安装JenkinsNginx配置Jenkins二级域名,以及443 SSL证书访问Jenkins容器docker部署springboot项目-问题记录
2022年05月09日
237 阅读
0 评论
6 点赞
2022-05-09
Jenkins容器docker部署springboot项目-问题记录
Jenkins容器docker部署springboot项目-问题记录一、docker容器内不能使用vim解决方案:以root进入容器内docker exec -it -user root jenkins /bin/bash更新软件包apt-get update升级过程可能非常慢,因为是从海外站点拉取镜像,所以我们可以配置一个国内的镜像源,加速镜像拉取更新。备份原文件mv /etc/apt/sources.list /etc/apt/sources.list.bak查看容器中Debian版本cat /etc/issue修改配置sources.list文件根据自己版本修改成对应内容,修改内容参考阿里镜像https://developer.aliyun.com/mirror/debian我容器Debian为11.x版本,修改内容为:cat >/etc/apt/sources.list <<EOF deb http://mirrors.aliyun.com/debian/ bullseye main non-free contrib deb-src http://mirrors.aliyun.com/debian/ bullseye main non-free contrib deb http://mirrors.aliyun.com/debian-security/ bullseye-security main deb-src http://mirrors.aliyun.com/debian-security/ bullseye-security main deb http://mirrors.aliyun.com/debian/ bullseye-updates main non-free contrib deb-src http://mirrors.aliyun.com/debian/ bullseye-updates main non-free contrib deb http://mirrors.aliyun.com/debian/ bullseye-backports main non-free contrib deb-src http://mirrors.aliyun.com/debian/ bullseye-backports main non-free contrib EOF重新执行apt-get update安装vimapt-get install -y vim安装rpmapt-get install rpm -y二、docker容器内vim不能粘贴内容vim右键进入visual模式无法粘贴解决方案vim /usr/share/vim/vim80/defaults.vim修改内容:第70行,在mouse=a的=前面加个-,修改后如下:if has('mouse') set mouse-=a endif三、docker容器内环境配置修改环境变量配置vi /etc/profile新增jdk、mavn环境变量配置# java环境变量 export JAVA_HOME=/jdk/jdk-11.0.10 export JRE_HOME=$JAVA_HOME/jre export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=./:JAVA_HOME/lib:$JRE_HOME/lib # maven环境变量 export M2_HOME=/maven/apache-maven-3.8.5 export PATH=$PATH:$JAVA_HOME/bin:$M2_HOME/bin重新加载环境变量source /etc/profile检验是否配置成功java -version mvn -v
2022年05月09日
101 阅读
0 评论
4 点赞
2022-04-26
SpringBoot源码解析(四):监听器
springboot源码解析(四):监听器 在看springboot的源码过程中,发现内部使用了大量的监听器,下面来看下监听器的作用。在springboot的监听器有如下两类:# Run Listeners #事件发布运行监听器,是springboot中配置的唯一一个应用运行监听器,作用是通过一个多路广播器,将springboot运行状态的变化,构建成事件,并广播给各个监听器 org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener(),\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer 当程序开始运行的时候,可以看到启动了一个运行时监听器,并且创建了一个SpringApplicationRunListeners对象,该对象是一个封装工具类,封装了所有的启动监听器:代码如下class SpringApplicationRunListeners { private final Log log; //启动类监听器 private final List<SpringApplicationRunListener> listeners; SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) { this.log = log; this.listeners = new ArrayList<>(listeners); } //启动上下文事件监听 void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } } //environment准备完毕事件监听 void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); } } //spring上下文准备完毕事件监听 void contextPrepared(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.contextPrepared(context); } } //上下文配置类加载事件监听 void contextLoaded(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.contextLoaded(context); } } //上下文刷新调用事件 void started(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.started(context); } } //上下文刷新完成,在run方法执行完之前调用该事件 void running(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.running(context); } } //在运行过程中失败调起的事件 void failed(ConfigurableApplicationContext context, Throwable exception) { for (SpringApplicationRunListener listener : this.listeners) { callFailedListener(listener, context, exception); } } private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception) { try { listener.failed(context, exception); } catch (Throwable ex) { if (exception == null) { ReflectionUtils.rethrowRuntimeException(ex); } if (this.log.isDebugEnabled()) { this.log.error("Error handling failed", ex); } else { String message = ex.getMessage(); message = (message != null) ? message : "no error message"; this.log.warn("Error handling failed (" + message + ")"); } } } } 在启动源码的流程中,我们知道不同的方法会在不同的时间点触发执行,然后广播出不同的事件,进入到EventPublishingRunListener类中public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } 在当前类的构造方法中默认创建了SimpleApplicationEventMulticaster类,用来完成创建全局的事件发布功能@Override public void starting() { this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { this.initialMulticaster .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context)); } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context)); } @Override public void started(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context)); } @Override public void running(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context)); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception); if (context != null && context.isActive()) { // Listeners have been registered to the application context so we should // use it at this point if we can context.publishEvent(event); } else { // An inactive context may not have a multicaster so we use our multicaster to // call all of the context's listeners instead if (context instanceof AbstractApplicationContext) { for (ApplicationListener<?> listener : ((AbstractApplicationContext) context) .getApplicationListeners()) { this.initialMulticaster.addApplicationListener(listener); } } this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); this.initialMulticaster.multicastEvent(event); } } 在进行事件广播的时候,会进入如下方法:@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); } } } 在进行事件广播之前,需要将监听器进行过滤,符合类型的留下,不符合类型的过滤掉protected Collection<ApplicationListener<?>> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { Object source = event.getSource(); Class<?> sourceType = (source != null ? source.getClass() : null); ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); // Quick check for existing entry on ConcurrentHashMap... ListenerRetriever retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { // Fully synchronized building and caching of a ListenerRetriever synchronized (this.retrievalMutex) { retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } retriever = new ListenerRetriever(true); Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever); this.retrieverCache.put(cacheKey, retriever); return listeners; } } else { // No ListenerRetriever caching -> no synchronization necessary return retrieveApplicationListeners(eventType, sourceType, null); } } 实际处理判断逻辑的类:private Collection<ApplicationListener<?>> retrieveApplicationListeners( ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) { List<ApplicationListener<?>> allListeners = new ArrayList<>(); Set<ApplicationListener<?>> listeners; Set<String> listenerBeans; synchronized (this.retrievalMutex) { listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners); listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans); } // Add programmatically registered listeners, including ones coming // from ApplicationListenerDetector (singleton beans and inner beans). for (ApplicationListener<?> listener : listeners) { if (supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { retriever.applicationListeners.add(listener); } allListeners.add(listener); } } // Add listeners by bean name, potentially overlapping with programmatically // registered listeners above - but here potentially with additional metadata. if (!listenerBeans.isEmpty()) { ConfigurableBeanFactory beanFactory = getBeanFactory(); for (String listenerBeanName : listenerBeans) { try { if (supportsEvent(beanFactory, listenerBeanName, eventType)) { ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { if (beanFactory.isSingleton(listenerBeanName)) { retriever.applicationListeners.add(listener); } else { retriever.applicationListenerBeans.add(listenerBeanName); } } allListeners.add(listener); } } else { // Remove non-matching listeners that originally came from // ApplicationListenerDetector, possibly ruled out by additional // BeanDefinition metadata (e.g. factory method generics) above. Object listener = beanFactory.getSingleton(listenerBeanName); if (retriever != null) { retriever.applicationListeners.remove(listener); } allListeners.remove(listener); } } catch (NoSuchBeanDefinitionException ex) { // Singleton listener instance (without backing bean definition) disappeared - // probably in the middle of the destruction phase } } } AnnotationAwareOrderComparator.sort(allListeners); if (retriever != null && retriever.applicationListenerBeans.isEmpty()) { retriever.applicationListeners.clear(); retriever.applicationListeners.addAll(allListeners); } return allListeners; }在监听器实例化之前,检查是否符合固定的类型 protected boolean supportsEvent( ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) { //判断监听器是否是GenericApplicationListener子类,如不是返回一个GenericApplicationListenerAdapter GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); }public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered { boolean supportsEventType(ResolvableType eventType); default boolean supportsSourceType(@Nullable Class<?> sourceType) { return true; } @Override default int getOrder() { return LOWEST_PRECEDENCE; } }此时可以看到GenericApplicationListener类,该类是spring提供的用于重写匹配监听器事件的接口,如果需要判断的监听器是GenericApplicationListener的子类,说明类型匹配方法已被重现,就调用子类的匹配方法,如果不是,提供一个默认的适配器来匹配GenericApplicationListenerAdapter public boolean supportsEventType(ResolvableType eventType) { if (this.delegate instanceof SmartApplicationListener) { Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve(); return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass)); } else { return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType)); } }可以看到该类最终调用的是declaredEventType.isAssignableFrom(eventType)方法,也就是说,如果我们没有重写监听器匹配方法,那么发布的事件 event 会被监听 event以及监听event的父类的监听器监听到。
2022年04月26日
108 阅读
0 评论
4 点赞
2022-04-26
SpringBoot源码解析(三):SpringBoot内嵌Tomcat
springboot源码解析(三):springboot内嵌tomcat 在使用springboot搭建一个web应用程序的时候,我们发现不需要自己搭建一个tomcat服务器,只需要引入spring-boot-starter-web,在应用启动时会自动启动嵌入式的tomcat作为服务器,下面来分析下源码的分析流程: 之前我们已经讲过了自动装配的原理,其实tomcat的实现机制也是从自动装配开始的。1、ServletWebServerFactoryAutoConfiguration类@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { @Bean public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer(serverProperties); } @Bean @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); } @Bean @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class) @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework") public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() { ForwardedHeaderFilter filter = new ForwardedHeaderFilter(); FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter); registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR); registration.setOrder(Ordered.HIGHEST_PRECEDENCE); return registration; } /** * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via * {@link ImportBeanDefinitionRegistrar} for early registration. */ public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof ConfigurableListableBeanFactory) { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory == null) { return; } registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); } private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) { if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) { RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(name, beanDefinition); } } } } 从这个类上可以看到,当前配置类主要导入了BeanPostProcessorRegister,该类实现了ImportBeanDefinitionRegister接口,可以用来注册额外的BeanDefinition,同时,该类还导入了EmbeddedTomcat,EmbeddedJetty,EmbeddedUndertow三个类,可以根据用户的需求去选择使用哪一个web服务器,默认情况下使用的是tomcat2、当自动装配功能完成之后会接着执行onRefresh的方法(ServletWebServerApplicationContext)@Override protected void onRefresh() { //创建主题对象,不用在意 super.onRefresh(); try { //开始创建web服务 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }3、创建web服务,默认获取的是tomcat的web容器(ServletWebServerApplicationContext)private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { //获取servletWebServerFactory,从上下文注册bean中可以找到 ServletWebServerFactory factory = getWebServerFactory(); //获取servletContextInitializer,获取webServer this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } //替换servlet相关的属性资源 initPropertySources(); }如何获取tomcat的bean的实例对象呢?从如下代码中可以看出ServletWebServerApplicationContextprotected ServletWebServerFactory getWebServerFactory() { // Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean."); } if (beanNames.length > 1) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }protected ServletWebServerFactory getWebServerFactory() { // Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean."); } if (beanNames.length > 1) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }DefaultListableBeanFactoryf/* 第一个参数type表示要查找的类型 第二个参数表示是否考虑非单例bean 第三个参数表示是否允许提早初始化 */ @Override public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) { //配置还未被冻结或者类型为null或者不允许早期初始化 if (!isConfigurationFrozen() || type == null || !allowEagerInit) { return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit); } //此处注意isConfigurationFrozen为false的时候表示beanDefinition可能还会发生更改和添加,所以不能进行缓存,如果允许非单例bean,那么从保存所有bean的集合中获取,否则从单例bean中获取 Map<Class<?>, String[]> cache = (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType); String[] resolvedBeanNames = cache.get(type); if (resolvedBeanNames != null) { return resolvedBeanNames; } //如果缓存中没有获取到,那么只能重新获取,获取到之后就存入缓存 resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true); if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) { cache.put(type, resolvedBeanNames); } return resolvedBeanNames; } private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { List<String> result = new ArrayList<>(); // Check all bean definitions. for (String beanName : this.beanDefinitionNames) { // Only consider bean as eligible if the bean name // is not defined as alias for some other bean. //如果时别名则跳过(当前集合会保存所有的主beanname,并且不会保存别名,别名由beanfactory中别名map维护) if (!isAlias(beanName)) { try { //获取合并的beandefinition,合并的beandefinition是指spring整合了父beandefinition的属性,将其beandefinition编程了rootBeanDefinition RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Only check bean definition if it is complete. //抽象的beandefinition是不做考虑,抽象的就是拿来继承的,如果允许早期初始化,那么直接短路,进入方法体,如果不允许早期初始化,那么需要进一步判断,如果是不允许早期初始化的,并且beanClass已经被加载或者它是可以早期初始化的,那么如果当前bean是工厂bean,并且指定的bean又是工厂那么这个bean就必须被早期初始化,也就是说就不符合我们制定的allowEagerInit为false的情况,直接跳过 if (!mbd.isAbstract() && (allowEagerInit || (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) && !requiresEagerInitForType(mbd.getFactoryBeanName()))) { //如果当前bean是工厂bean boolean isFactoryBean = isFactoryBean(beanName, mbd); //如果允许早期初始化,那么基本上会调用最后的isTypeMatch方法,这个方法会导致工厂的实例化,但是当前不允许进行早期实例化在不允许早期实例化的情况下,如果当前bean是工厂bean,那么它只能在已经被创建的情况下调用isTypeMatch进行匹配判断否则只能宣告匹配失败,返回false BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); boolean matchFound = false; boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName); boolean isNonLazyDecorated = dbd != null && !mbd.isLazyInit(); if (!isFactoryBean) { if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) { matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); } } else { //如果没有匹配到并且他是个工厂bean,那么加上&前缀,表示要获取factorybean类型的bean if (includeNonSingletons || isNonLazyDecorated || (allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) { matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); } if (!matchFound) { // In case of FactoryBean, try to match FactoryBean instance itself next. beanName = FACTORY_BEAN_PREFIX + beanName; matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); } } //找到便记录到result集合中,等待返回 if (matchFound) { result.add(beanName); } } } catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) { if (allowEagerInit) { throw ex; } // Probably a placeholder: let's ignore it for type matching purposes. LogMessage message = (ex instanceof CannotLoadBeanClassException) ? LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) : LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName); logger.trace(message, ex); onSuppressedException(ex); } } } // Check manually registered singletons too. //从单例注册集合中获取,这个单例集合石保存spring内部注入的单例对象,他们的特点就是没有beanDefinition for (String beanName : this.manualSingletonNames) { try { // In case of FactoryBean, match object created by FactoryBean. //如果是工厂bean,那么调用其getObjectType去匹配是否符合指定类型 if (isFactoryBean(beanName)) { if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) { result.add(beanName); // Match found for this bean: do not match FactoryBean itself anymore. continue; } // In case of FactoryBean, try to match FactoryBean itself next. beanName = FACTORY_BEAN_PREFIX + beanName; } // Match raw bean instance (might be raw FactoryBean). //如果没有匹配成功,那么匹配工厂类 if (isTypeMatch(beanName, type)) { result.add(beanName); } } catch (NoSuchBeanDefinitionException ex) { // Shouldn't happen - probably a result of circular reference resolution... logger.trace(LogMessage.format("Failed to check manually registered singleton with name '%s'", beanName), ex); } } return StringUtils.toStringArray(result); }4、tomcat对象的初始化(ServletWebServerApplicationContext)private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { return this::selfInitialize; } private void selfInitialize(ServletContext servletContext) throws ServletException { //使用给定的完全加载的servletContext准备WebApplicationContext prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); //使用给定的BeanFactory注册特定于web的作用域bean(contextParameters,contextAttributes) WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }5、完成内嵌tomcat的api调用(TomcatServletWebServerFactory)@Override public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } //完成tomcat的api调用 Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } //准备tomcatEmbeddedContext并设置到tomcat中 prepareContext(tomcat.getHost(), initializers); //构建tomcatWebServer return getTomcatWebServer(tomcat); }6、获取tomcat服务(TomcatServletWebServerFactory)protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, getPort() >= 0); } public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; //初始化 initialize(); }7、完成tomcat的初始化private void initialize() throws WebServerException { logger.info("Tomcat initialized with port(s): " + getPortsDescription(false)); synchronized (this.monitor) { try { //engineName拼接instanceId addInstanceIdToEngineName(); Context context = findContext(); context.addLifecycleListener((event) -> { if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) { // Remove service connectors so that protocol binding doesn't // happen when the service is started. //删除Connectors,以便再启动服务时不发生协议绑定 removeServiceConnectors(); } }); // Start the server to trigger initialization listeners //启动服务触发初始化监听器 this.tomcat.start(); // We can re-throw failure exception directly in the main thread //在主线程中重新抛出失败异常 rethrowDeferredStartupExceptions(); try { ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } catch (NamingException ex) { // Naming is not enabled. Continue } // Unlike Jetty, all Tomcat threads are daemon threads. We create a // blocking non-daemon to stop immediate shutdown //所有的tomcat线程都是守护线程,我们创建一个阻塞非守护线程来避免立即关闭 startDaemonAwaitThread(); } catch (Exception ex) { //异常停止tomcat stopSilently(); destroySilently(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } } ----------------------- private void removeServiceConnectors() { for (Service service : this.tomcat.getServer().findServices()) { Connector[] connectors = service.findConnectors().clone(); //将将要移除的conntector放到缓存中暂存 this.serviceConnectors.put(service, connectors); for (Connector connector : connectors) { //移除connector service.removeConnector(connector); } } }8、除了refresh方法之外,在finishRefresh()方法中也对tomcat做了相关的处理(ServletWebServerApplicationContext) protected void finishRefresh() { //调用父类的finishRefresh方法 super.finishRefresh(); //启动webServer WebServer webServer = startWebServer(); if (webServer != null) { //发布webServer初始化完成事件 publishEvent(new ServletWebServerInitializedEvent(webServer, this)); } }ServletWebServerApplicationContext private WebServer startWebServer() { WebServer webServer = this.webServer; if (webServer != null) { //启动webserver webServer.start(); } return webServer; }TomcatWebServer public void start() throws WebServerException { synchronized (this.monitor) { if (this.started) { return; } try { //添加之前移除的connector addPreviouslyRemovedConnectors(); Connector connector = this.tomcat.getConnector(); if (connector != null && this.autoStart) { //延迟加载启动 performDeferredLoadOnStartup(); } //检查connector启动状态是否为失败,失败抛出异常 checkThatConnectorsHaveStarted(); this.started = true; logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '" + getContextPath() + "'"); } catch (ConnectorStartFailedException ex) { //异常停止tomcat stopSilently(); throw ex; } catch (Exception ex) { if (findBindException(ex) != null) { throw new PortInUseException(this.tomcat.getConnector().getPort()); } throw new WebServerException("Unable to start embedded Tomcat server", ex); } finally { Context context = findContext(); //context解绑classload ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } } }private void addPreviouslyRemovedConnectors() { Service[] services = this.tomcat.getServer().findServices(); for (Service service : services) { //从上面移除connector添加的缓存中取出connector Connector[] connectors = this.serviceConnectors.get(service); if (connectors != null) { for (Connector connector : connectors) { //添加到tomcat service中 service.addConnector(connector); if (!this.autoStart) { //如果不是自动启动,则暂停connector stopProtocolHandler(connector); } } //添加完成后移除 this.serviceConnectors.remove(service); } } }private void performDeferredLoadOnStartup() { try { for (Container child : this.tomcat.getHost().findChildren()) { if (child instanceof TomcatEmbeddedContext) { //延迟加载启动 ((TomcatEmbeddedContext) child).deferredLoadOnStartup(); } } } catch (Exception ex) { if (ex instanceof WebServerException) { throw (WebServerException) ex; } throw new WebServerException("Unable to start embedded Tomcat connectors", ex); } } void deferredLoadOnStartup() throws LifecycleException { doWithThreadContextClassLoader(getLoader().getClassLoader(), () -> getLoadOnStartupWrappers(findChildren()).forEach(this::load)); }9、应用上下文关闭时会调用tomcat的关闭在refreshContext中注册一个关闭的钩子函数,而钩子函数可以完成关闭的功能ServletWebServerApplicationContext @Override protected void onClose() { super.onClose(); stopAndReleaseWebServer(); } private void stopAndReleaseWebServer() { WebServer webServer = this.webServer; if (webServer != null) { try { webServer.stop(); this.webServer = null; } catch (Exception ex) { throw new IllegalStateException(ex); } } }TomcatWebServer@Override public void stop() throws WebServerException { synchronized (this.monitor) { boolean wasStarted = this.started; try { this.started = false; try { stopTomcat(); this.tomcat.destroy(); } catch (LifecycleException ex) { // swallow and continue } } catch (Exception ex) { throw new WebServerException("Unable to stop embedded Tomcat", ex); } finally { if (wasStarted) { containerCounter.decrementAndGet(); } } } }
2022年04月26日
139 阅读
0 评论
2 点赞
2022-04-26
Spring的自动配置原理
spring的自动配置原理springboot配置文件的装配过程1、springboot在启动的时候会加载主配置类,开启了@EnableAutoConfiguration。2、@EnableAutoConfiguration的作用:利用AutoConfigurationImportSelector给容器导入一些组件。查看selectImports方法的内容,返回一个AutoConfigurationEntryAutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); ------ List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); ------ protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }可以看到SpringFactoriesLoader.loadFactoryNames,继续看又调用了loadSpringFactories方法,获取META-INF/spring.factories资源文件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); } }总结:将类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;每一个xxxAutoConfiguration类都是容器中的一个组件,最后都加入到容器中,用来做自动配置,每一个自动配置类都可以进行自动配置功能使用HttpEncodingAutoConfiguration来解释自动装配原理/* 表名这是一个配置类, */ @Configuration(proxyBeanMethods = false) /* 启动指定类的ConfigurationProperties功能,进入HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来,并把HttpProperties加入到ioc容器中 */ @EnableConfigurationProperties(HttpProperties.class) /* spring底层@Confitional注解,根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效 此时表示判断当前应用是否是web应用,如果是,那么配置类生效 */ @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) /* 判断当前项目由没有这个类CharacterEncodingFilter,springmvc中进行乱码解决的过滤器 */ @ConditionalOnClass(CharacterEncodingFilter.class) /* 判断配置文件中是否存在某个配置:spring.http.encoding.enabled 如果不存在,判断也是成立的, 即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的 */ @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration { //和springboot的配置文件映射 private final HttpProperties.Encoding properties; //只有一个有参构造器的情况下,参数的值就会从容器中拿 public HttpEncodingAutoConfiguration(HttpProperties properties) { this.properties = properties.getEncoding(); } //给容器中添加一个组件,这个组件的某些值需要从properties中获取 @Bean @ConditionalOnMissingBean//判断容器中是否有此组件 public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; } @Bean public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() { return new LocaleCharsetMappingsCustomizer(this.properties); } private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered { private final HttpProperties.Encoding properties; LocaleCharsetMappingsCustomizer(HttpProperties.Encoding properties) { this.properties = properties; } @Override public void customize(ConfigurableServletWebServerFactory factory) { if (this.properties.getMapping() != null) { factory.setLocaleCharsetMappings(this.properties.getMapping()); } } @Override public int getOrder() { return 0; } } } 根据当前不同的条件判断,决定这个配置类是否生效!总结: 1、springboot启动会加载大量的自动配置类 2、查看需要的功能有没有在springboot默认写好的自动配置类中华 3、查看这个自动配置类到底配置了哪些组件 4、给容器中自动配置类添加组件的时候,会从properties类中获取属性@Conditional:自动配置类在一定条件下才能生效@Conditional扩展注解作用@ConditionalOnJava系统的java版本是否符合要求@ConditionalOnBean容器中存在指定Bean@ConditionalOnMissingBean容器中不存在指定Bean@ConditionalOnExpression满足SpEL表达式@ConditionalOnClass系统中有指定的类@ConditionalOnMissingClass系统中没有指定的类@ConditionalOnSingleCandidate容器中只有一个指定的Bean,或者是首选Bean@ConditionalOnProperty系统中指定的属性是否有指定的值@ConditionalOnResource类路径下是否存在指定资源文件@ConditionOnWebApplication当前是web环境@ConditionalOnNotWebApplication当前不是web环境@ConditionalOnJndiJNDI存在指定项
2022年04月26日
91 阅读
0 评论
2 点赞
1
2
...
4