首页 文章 精选 留言 我的

精选列表

搜索[部署工具],共10000篇文章
优秀的个人博客,低调大师

Docker 部署 Spring Boot 项目初探

仅想在Docker里运行一个Spring Boot项目,捣鼓了许久。。。 本文主要适用于Windows环境下的Docker 一、运行环境 Windows 10 Maven 3.5 Docker18.06.1-ce-win73 (19507) 二、创建Spring Boot项目 空空如也。。。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> @RestController @SpringBootApplication public class GreetApplication { @GetMapping("hello") public String hello() { return "Hello Docker!"; } public static void main(String[] args) { SpringApplication.run(GreetApplication.class, args); } } 三、创建一个Dockerfile 参考:https://spring.io/guides/gs/spring-boot-docker/ 内容:和官方指南完全一样;下边只说FROM,其他几个官网解释的很好 FROM openjdk:8-jdk-alpine VOLUME /tmp ARG JAR_FILE COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 1、什么是FORM 我们定制镜像,需要以一个镜像为基础,FROM 指代的就是。 2、官方用哪个 openjdk:8-jdk-alpine 因为不知道为啥是这个,毕竟也是第一次见,就先试试其他的。 依次下载了他们,openjdk、oracle/openjdk 和 openjdk:8-jdk-alpine 3、具体差异 3.1 使用 openjdk 3.2 使用 openjdk:8-jdk-alpine 差异很明显吧,镜像小了874MB(......) 四、修改pom.xml配置 <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <skipTests>true</skipTests> <docker.image.prefix>amos</docker.image.prefix> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <finalName>greet</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version>1.3.6</version> <configuration> <repository>${docker.image.prefix}/${project.artifactId}</repository> <buildArgs> <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE> </buildArgs> </configuration> </plugin> </plugins> </build> 五、指南正文 1、Maven 不识别 docker:build 你刚开始百度到的命令可能是:mvn clean package docker:build,然后运行,接着就失败了... [ERROR]No plugin found for prefix 'docker' in the current project and in the plugin groups... 解决办法是修改 maven 的配置文件 settings.xml。( pluginGroups 在 settings.xml 中的位置很靠上) <pluginGroups> <pluginGroup>com.spotify</pluginGroup> </pluginGroups> 后来,你可能会发现,使用mvn clean package dockerfile:build,就不会有上边的错误。这个仇先记下了。。。 2、Must specify baseImage if dockerDirectory 接下来,开始运行了,以为正常了。 [ERROR] ... Exception caught: Must specify baseImage if dockerDirectory is null -> [Help 1] 翻到https://github.com/spotify/dockerfile-maven,你会发现应该使用mvn clean package dockerfile:build,而不是mvn clean package docker:build。 3、Connect to localhost:2375 ... failed [ERROR] ... Connect to localhost:2375 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect 右键运行中的docker,Settings,接着如下图 六、完 生成镜像到本地:mvn package dockerfile:build 查看镜像:docker images 运行镜像:docker run -d -p 8088:8080 --name greet amos/greet 浏览器内访问:

优秀的个人博客,低调大师

mybatis mapper.xml 热部署

package com.guangeryi.mall.core.utils.mybatis; import com.guangeryi.mall.common.CommonConstant; import com.guangeryi.mall.core.utils.StringUtils; import org.apache.ibatis.binding.MapperProxyFactory; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.logging.log4j.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Component; import java.io.IOException; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Component public class MyBatisMapperRefresher implements DisposableBean, InitializingBean, ApplicationContextAware { private final Logger logger = LoggerFactory.getLogger(getClass()); /** * 扫描周期,单位秒 */ private int periodSeconds = 5; /** * 初始化完成,延迟扫描时间,单位秒 */ private int initialDelay = 5; /** * 是否启用 */ @Value("${mapper.auto.load}") private boolean enabled; private ConfigurableApplicationContext context = null; private transient Resource[] basePackage = null; private HashMap<String, String> fileMapping = new HashMap<>(); private Set<String> isChangedMapper = new HashSet<>(); private Scanner scanner = null; private ScheduledExecutorService service = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = (ConfigurableApplicationContext) applicationContext; } @Override public void afterPropertiesSet() throws Exception { if (!enabled) { logger.info("MyBatisMapperRefresher is Disabled"); return; } try { service = Executors.newScheduledThreadPool(1); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); basePackage = resolver.getResources("classpath*:xx.xml");// 加载配置xml文件 logger.info("监控以下" + basePackage.length + "个xml文件:"); // 触发文件监听事件 scanner = new Scanner(); scanner.scan(); service.scheduleAtFixedRate(new Task(), MyBatisMapperRefresher.this.initialDelay, MyBatisMapperRefresher.this.periodSeconds, TimeUnit.SECONDS); } catch (Exception e1) { logger.error("can not starter Mybatis xml refresher,exception:{}", e1); } } class Task implements Runnable { @Override public void run() { try { if (scanner.isChanged()) { scanner.reloadXML(); } } catch (Exception ex) { logger.error("MyBatisMapperRefresher,exception:{}", ex); } } } @Override public void destroy() throws Exception { if (service != null) { service.shutdownNow(); } } class Scanner { private Resource[] mapperXmlFiles; public Scanner() { mapperXmlFiles = MyBatisMapperRefresher.this.basePackage; } public void reloadXML() throws Exception { SqlSessionFactory factory = context.getBean(SqlSessionFactory.class); Configuration configuration = factory.getConfiguration(); Set<Class<?>> knownMapperKeys = new HashSet<>(); Field field = configuration.getMapperRegistry().getClass().getDeclaredField("knownMappers"); field.setAccessible(true); Map<Class<?>, MapperProxyFactory<?>> knownMappers = (Map<Class<?>, MapperProxyFactory<?>>) field.get(configuration.getMapperRegistry()); knownMapperKeys.addAll(knownMappers.keySet()); // 移除加载项 removeConfig(configuration, isChangedMapper); Set<String> isChangedMapperTemp = isChangedMapper.stream().map(item -> StringUtils.split(item, CommonConstant.CHAR_SPOT)[0]).collect(Collectors.toSet()); Iterator<Class<?>> classIterator = knownMapperKeys.iterator(); while (classIterator.hasNext()) { Class clazz = classIterator.next(); if (clazz == null || Strings.isEmpty(clazz.getName())) continue; String[] clazzNames = StringUtils.split(clazz.getName(), CommonConstant.CHAR_SPOT); if (isChangedMapperTemp.contains(clazzNames[clazzNames.length - 1])) { knownMappers.remove(clazz); configuration.addMapper(clazz); } } isChangedMapper.clear(); } private void removeConfig(Configuration configuration, Set<String> isChangedMapper) throws Exception { Class<?> classConfig = configuration.getClass(); clearMap(classConfig, configuration, "mappedStatements", isChangedMapper); clearMap(classConfig, configuration, "caches", isChangedMapper); clearMap(classConfig, configuration, "resultMaps", isChangedMapper); clearMap(classConfig, configuration, "parameterMaps", isChangedMapper); clearMap(classConfig, configuration, "keyGenerators", isChangedMapper); clearMap(classConfig, configuration, "sqlFragments", isChangedMapper); clearSet(classConfig, configuration, "loadedResources", isChangedMapper); } private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName, Set<String> isChangedMapper) throws Exception { Set<String> isChangedMapperTemp = isChangedMapper.stream().map(item -> "repository." + StringUtils.split(item, CommonConstant.CHAR_SPOT)[0]).collect(Collectors.toSet()); Field field = classConfig.getDeclaredField(fieldName); field.setAccessible(true); Map mapConfig = (Map) field.get(configuration); Map tempMap = new HashMap(); tempMap.putAll(mapConfig); tempMap.forEach((key, value) -> { if (StringUtils.StringContains((String) key, isChangedMapperTemp)) mapConfig.remove(key); }); } private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName, Set<String> isChangedMapper) throws Exception { Set<String> isChangedMapperTemp = isChangedMapper.stream().map(item -> "repository/" + StringUtils.split(item, CommonConstant.CHAR_SPOT)[0]).collect(Collectors.toSet()); isChangedMapperTemp.addAll(isChangedMapper.stream().map(item -> "repository." + StringUtils.split(item, CommonConstant.CHAR_SPOT)[0]).collect(Collectors.toSet())); Field field = classConfig.getDeclaredField(fieldName); field.setAccessible(true); Set setConfig = (Set) field.get(configuration); Set tempMap = new HashSet<>(); tempMap.addAll(setConfig); tempMap.forEach(item -> { if (StringUtils.StringContains((String) item, isChangedMapperTemp)) setConfig.remove(item); }); } public void scan() throws IOException { if (!fileMapping.isEmpty()) { return; } Resource[] resources = mapperXmlFiles; if (resources != null) { for (int i = 0; i < resources.length; i++) { String multi_key = getValue(resources[i]); String fileName = resources[i].getFilename(); fileMapping.put(fileName, multi_key); logger.info("monitor Mybatis mapper file:{}", resources[i].getFile().getAbsolutePath()); } } } private String getValue(Resource resource) throws IOException { String contentLength = String.valueOf((resource.contentLength())); String lastModified = String.valueOf((resource.lastModified())); return new StringBuilder(contentLength).append(lastModified).toString(); } public boolean isChanged() throws IOException { boolean isChanged = false; Resource[] resources = mapperXmlFiles; if (resources != null) { for (int i = 0; i < resources.length; i++) { String name = resources[i].getFilename(); String value = fileMapping.get(name); String multi_key = getValue(resources[i]); if (!multi_key.equals(value)) { isChanged = true; fileMapping.put(name, multi_key); isChangedMapper.add(name); logger.info("reload Mybatis mapper file:{}", resources[i].getFile().getAbsolutePath()); } } } return isChanged; } } } 希望园友多多提意见...

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册