通过Arthas来动态打印日志

需求线上一个Spring boot应用 通过MyBatis来操作数据库 定位一个线上问题 想动态打印sql日志 解决通过Arthas工具来实现上述的需求 $ ognl '@org.slf4j.LoggerFactory@getLogger("com.foo.cpts.mapper")'@Logger[ serialVersionUID=@Long[5454405123156820674], FQCN=@String[ch.qos.logback.classic.Logger], name=@String[com.foo.cpts.mapper], level=null, effectiveLevelInt=@Integer[20000], parent=@Logger[Logger[com.foo.cpts]], childrenList=@CopyOnWriteArrayList[isEmpty=false;size=2], aai=null, additive=@Boolean[true], loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],]刚开始Mapper包对应的日志级别是null 将其改成DEBUG $ ognl '@org.slf4j.LoggerFactory@getLogger("com.foo.cpts.mapper").setLevel(@ch.qos.logback.classic.Level@DEBUG)'null$ ognl '@org.slf4j.LoggerFactory@getLogger("com.foo.cpts.mapper")'@Logger[ serialVersionUID=@Long[5454405123156820674], FQCN=@String[ch.qos.logback.classic.Logger], name=@String[com.foo.cpts.mapper], level=@Level[DEBUG], effectiveLevelInt=@Integer[10000], parent=@Logger[Logger[com.foo.cpts]], childrenList=@CopyOnWriteArrayList[isEmpty=false;size=2], aai=null, additive=@Boolean[true], loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],]此时当调用Mapper方法时 能打印出sql日志了 同样关闭sql日志 改成info即可 $ ognl '@org.slf4j.LoggerFactory@getLogger("com.foo.cpts.mapper").setLevel(@ch.qos.logback.classic.Level@INFO)'null补充因为打印sql日志对应的配置是 logging.level.com.foo.cpts.mapper=DEBUG刚开始想着通过下面的方式来实现 ognl '@java.lang.System@setProperty("logging.level.com.foo.cpts.mapper","DEBUG")'但是该方式并未起作用

June 26, 2019 · 1 min · jiezi

SpringCloud微服务架构升级总结

一、背景1.1 应用系统的架构历史 1.2 什么是微服务?起源:微服务的概念源于 2014 年 3 月 Martin Fowler 所写的一篇文章“Microservices”。文中内容提到:微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。 通信方式:每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于 HTTP 的 RESTful API)。 微服务的常规定义:微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务。把原来的一个完整的进程服务,拆分成两个或两个以上的进程服务,且互相之间存在调用关系,与原先单一的进程服务相比,就是“微服务”。(微服务是一个比较级的概念,而不是单一的概念) 1.3 微服务架构的优势可扩展性:在增加业务功能时,单一应用架构需要在原先架构的代码基础上做比较大的调整,而微服务架构只需要增加新的微服务节点,并调整与之有关联的微服务节点即可。在增加业务响应能力时,单一架构需要进行整体扩容,而微服务架构仅需要扩容响应能力不足的微服务节点。容错性:在系统发生故障时,单一应用架构需要进行整个系统的修复,涉及到代码的变更和应用的启停,而微服务架构仅仅需要针对有问题的服务进行代码的变更和服务的启停。其他服务可通过重试、熔断等机制实现应用层面的容错。技术选型灵活:微服务架构下,每个微服务节点可以根据完成需求功能的不同,自由选择最适合的技术栈,即使对单一的微服务节点进行重构,成本也非常低。开发运维效率更高:每个微服务节点都是一个单一进程,都专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模团队或者个人完全掌控,易于保持高可维护性和开发效率。Spring Cloud作为目前最流行的微服务开发框架,不是采用了Spring Cloud框架就实现了微服务架构,具备了微服务架构的优势。正确的理解是使用Spring Cloud框架开发微服务架构的系统,使系统具备微服务架构的优势(Spring Cloud就像工具,还需要“做”的过程)。 1.4 什么是Spring Boot?什么是Spring Cloud?Spring Boot框架是由Pivotal团队提供的全新框架,其设计目的是用来简化基于Spring应用的初始搭建以及开发过程。SpringBoot框架使用了特定的方式来进行应用系统的配置,从而使开发人员不再需要耗费大量精力去定义模板化的配置文件。 Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理、服务注册,服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。 1.5 微服务、Spring Boot、Spring Cloud三者之间的关系思想:微服务是一种架构的理念,提出了微服务的设计原则,从理论为具体的技术落地提供了指导思想。脚手架:Spring Boot是一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务。多个组件的集合:Spring Cloud是一个基于Spring Boot实现的服务治理工具包;Spring Boot专注于快速、方便集成的单个微服务个体;Spring Cloud关注全局的服务治理框架。二、技术解析2.1 Everything is jar, Everything is httpSpring Boot通过@SpringBootApplication注解标识为Spring Boot应用程序。所有的应用都通过jar包方式编译,部署和运行。 @SpringBootApplication public class Application { private static final Logger LOGGER = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { SpringApplication.run(Application.class, args); LOGGER.info(”启动成功!"); } }每个Spring Boot的应用都可以通过内嵌web容器的方式提供http服务,仅仅需要在pom文件中依赖spring-boot-start-web即可,原则上微服务架构希望每个独立节点都提供http服务。 ...

June 26, 2019 · 3 min · jiezi

我的第一本书被选作大学教材了

这真是意料之外! <!--more--> 去年年初,接受清华大学出版社夏老师的邀请,下定决心开始写我的第一本书,战战兢兢早起晚睡,好几个月都没有陪女票出去玩,辛辛苦苦终于在年底的时候新书顺利上市! 销量出乎意料新书上市之后,刚开始想的是首印的能卖完就不错了,千万别砸到出版社手里了。松哥的公众号当时也只有 4000 多人,而且一直断断续续更新,没啥影响力,新书出来后,宣传了一波之后就偃旗息鼓没再管了,结果后来发生的事情证明我的担心多余了。 2 月份回家过年,3 月份的时候收到出版社的消息第二次加印,4 月份第三次加印,5 月份第四次加印,下个月第五次加印!这一波操作真是亮瞎了。 这个销量真是出乎我的意料!从写书的第一天开始,从来不敢想会有这一天。 不停的加印,也让出版社的编辑老师又找我约稿了,希望能再来一本,可是最近几个月,每天敲键盘时间从早九点到晚十一点,有点快扛不住了,没敢答应,但是这让我看到了未来更多的可能性。 选作大学教材前天,收到了出版社编辑老师的消息,说是有高校老师想把这本书选作下学期的教材,希望我能提供一份配套的 PPT 课件,我立马就答应了,熬夜赶制了一份出来。 其实新书出版第一周就有一个老师加我微信,后来又陆陆续续有好几位高校老师加我微信,一起探讨书中的内容,我自己也获益良多。这次是一个山东的一本大学将这本书选作教材,我还是有些意外,具体是哪个学校我就不透露了,我看了下,这个高校去年在陕西招生分数高一本线 50 分左右。 其实对于经常写博客的人来说,写书真是一个信手拈来的机会,遍地都是。但是并不是每一本书都有机会进入大学,成为学生的课本,甚至最后成为学生考试的噩梦(松哥的书估计不会成为考试的噩梦)! 现在国内技术图书市场,很多书生命力很短,当然原因是多方面的。但是据出版社那边的反馈,如果这本书被选作高校教材,老师讲熟练了,后面还会继续使用,也就是说后面还会继续印刷,我觉得这是最有价值的地方,相信没有作者希望自己的书昙花一现吧! 为什么写书书卖了这么多,有人要问了,松哥一定赚的盆满钵满了吧?老实说,目前拿到手的稿费还没有一个月的薪水多。当然也因为有一部分稿费还没结算,不过写书不挣钱,这个倒是真的。如果奔着赚钱,我去年应该不会写这本书,因为写书是一个非常花费时间精力的事情,写书的时间要是拿去做其他事,会有更大的收益。 那为什么要写呢? 首先,我觉得这个世界不能白白走一遭,总得留下一点东西,几年之后,和孩子吹牛,咱也得有个话题啊! 第二,我觉得这是对自己的一个证明,跨行做计算机,其实我一直都不是很自信,虽然我觉得自己的代码写的还不赖,但是总觉得差点什么!通过这本书我想告诉别人,我不是计算机科班,我也不是一流名校,但是我能在这个行业中做的很好,能够得到多数同行的认可,这就够了。 第三,作为一个农村出来的孩子,能写一本书,也能让逐渐老去的父母脸上有光,这算是当时的写作的一点私心吧。 结语写书很辛苦,但是获得大家的认可还是很开心。因为《Spring Boot + Vue 全栈开发实战》这本书,认识了很多优秀的人,半年里,自己也提高了很多。因此我强烈建议每一个技术人都应该有自己的博客,记录自己技术成长的点点滴滴,说不定博客哪天给你带来一个意料之外的机会,这谁说的准呢? 关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

June 26, 2019 · 1 min · jiezi

安卓新闻API

安卓课设——新闻阅读器实现了:登录、注册、新闻详情要求数据从云端拉取,于是顺便写了新闻的接口。Spring Boot编写挺快的。开放下面的接口给需要的朋友练习文档原先是用语雀写的(语雀的体验真的非常好),现在有空就顺便粘贴过来的。注意:当你看到这篇文章时,请点击这里测试是否可用,如果看到一串JSON,说明可用。永远不知道,明天和停止维护,还有多远。新闻API文档 约定Content-Type为application/x-www-form-urlencoded ,通过urlencode格式化请求体。响应报文为 JSON 注册接口描述:用户注册,必要的参数:用户名,密码 HTTP 方法:POST 请求URL:https://challage.cn/guet/api/user/register 注册请求说明参数是否必需长度范围说明userName是0-50用户名userPassword是0-50密码userNikeName-0-50昵称userImg-0-50头像URL(需要存储接口配合,暂缺省)userMore-0-50预留字段,可用作自我介绍等 注册返回说明参数是否必需值说明registerMsg是注册结果信息userId是成功时返回userId,失败时返回-1 注册成功示例(userId>0):请求参数: userName:张三userPassword:407响应: { "registerMsg": "注册成功", "userId": 62} 注册失败示例(userId == -1):请求参数: userName:张三userPassword:407响应: { "registerMsg": "用户名已存在", "userId": -1} 注册参数不合法示例(userId == -1):请求参数: userName:张三userPassword:null响应: { "registerMsg": "用户名和密码不能为空", "userId": -1} 登录接口描述:用户登录,必要的参数:用户名,密码 HTTP 方法:POST 请求URL:https://challage.cn/guet/api/user/login 返回说明参数是否必需值说明loginMsg是登录结果信息userInfo是成功时返回用户POJO,登录失败时为null 登录成功示例(注意,因为返回用户POJO映射,考虑安全而将密码置空):请求参数: userName:panuserPassword:123456响应: { "loginMsg": "登录成功", "userInfo": { "userId": 1, "userName": "pan", "userPassword": null, "userNikeName": "潘裤衩", "userImg": "https://upload.jianshu.io/users/upload_avatars/2027280/6f5e986b-1eec-45c7-a512-4b51854595d9.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96", "userRegisterTime": "2019-06-12 22:45", "userMore": "自我介绍~" }} ...

June 25, 2019 · 1 min · jiezi

年轻人的第一个-Spring-Boot-应用太爽了

Spring Boot 大家都知道是啥吧? 还有不知道的来看这篇扫下盲:告诉你,Spring Boot 真是个牛逼货!。 顺便再往下看,栈长给你带来年轻人的第一个 Spring Boot 应用,撸码史无前例的轻松,那就一个字:爽! 第一步打开这个网站,快速生成一个 Spring Boot 项目。 https://start.spring.io/废话不说,看下图,几秒搞定! 第二步解压生成的 demo 项目,导入到 IDE 中。 来看下 pom.xml 文件内容: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.javastack</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </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> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>以及 DemoApplication 入口类内容: ...

June 24, 2019 · 1 min · jiezi

深入理解SpringApplication

SpringApplication类用于引导和启动一个Spring应用程序(即SpringBoot开发的应用)。通常用SpringBoot开发一个应用程序时,在主类的main函数中可以通过如下代码启动一个Spring应用: @SpringBootApplicationpublic class App { public static void main(String[] args) { SpringApplication.run(App.class, args); }}SpringApplication的静态方法run(Class<?> primarySource, String... args))的第一个参数接受一个Spring容器配置类(用Java代码对Spring容器进行配置)。第二个参数是命令行参数。将命令行参数转发给SpringApplication类,就可以在用java命令启动应用时,通过命令行参数对Spring应用做一些配置。SpringApplication类会做如下事情启动应用: 为应用创建一个合适的ApplicationContext注册一个CommandLinePropertySource,通过CommandLinePropertySource可以对外暴露命令行参数,并将命令行参数与spring应用中用到的properties关联起来启动ApplicationContext执行所有的CommandLineRunner类型bean下面我们通过SpringApplication的源码详细描述上述这些过程。 构建SpringApplication实例下面是SpringApplication类静态run方法的源码。可以看到,当我们调用这个静态run方法时,实际上会构造一个SpringApplication实例,然后再调用实例的run方法完成spring应用的启动。 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}下面是SpringApplication的构造函数,它主要完成下面初始化工作: 初始化Spring容器的配置类primarySources推断应用程序的类型,进而根据应用程序的类型创建恰当的ApplicationContext初始化指定的ApplicationContextInitializer列表初始化指定的ApplicationListener列表推断main class的类名称public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass();}下面对这些初始化过程进行一一说明: spring容器配置SpringApplication能够从各种不同的配置源读取bean的定义。Spring Boot建议采用Java注解配置的方式提供一个全局唯一的配置类。但是,你可以同时使用多种不同的配置源。如果是Java注解的配置方式,会使用AnnotatedBeanDefinitionReader加载配置(通过全类名)。如果是XML的配置方式,则会使用XmlBeanDefinitionReader加载配置(通过XML文件地址)。如果除了primarySources配置类以外,还需要其它的ApplicationContext配置源,则可以调用SpringApplication#setSources(Set<String> sources)方法进行设置,该方法的参数既可以接受一个配置类的全类名,也可以是一个XML配置文件的地址。 推断应用程序类型SpringApplication默认的应用类型只有三种: public enum WebApplicationType { /** * 非web类应用,无需内嵌web server */ NONE, /** * servlet类型的web应用,需要启动内嵌的web server */ SERVLET, /** * reactive类型的web应用,需要启动内嵌的reactive web server * 啥是reactive类型的web应用?目前还不知道^_^ */ REACTIVE;判断的逻辑也非常简单,就是检查classpath下是否存在对应的类。 ...

June 23, 2019 · 1 min · jiezi

SpringBoot系列教程JPA之update使用姿势

原文: 190623-SpringBoot系列教程JPA之update使用姿势上面两篇博文拉开了jpa使用姿势的面纱一角,接下来我们继续往下扯,数据插入db之后,并不是说就一层不变了,就好比我在银行开了户,当然是准备往里面存钱了,有存就有取(特别是当下银行利率这么低还不如买比特币屯着,截止19年6月22日,btc已经突破1.1w$,可惜没钱买????)这就是我们今天的主题,数据更新--update的使用姿势 <!-- more --> 通过本篇博文,您至少可以选到 save() 直接根据id来修改记录利用jpl 实现查询修改的使用姿势初识事物的神秘面纱I. 环境准备在开始之前,当然得先准备好基础环境,如安装测试使用mysql,创建SpringBoot项目工程,设置好配置信息等,关于搭建项目的详情可以参考前一篇文章 190612-SpringBoot系列教程JPA之基础环境搭建190614-SpringBoot系列教程JPA之新增记录使用姿势下面简单的看一下演示添加记录的过程中,需要的配置 1. 表准备沿用前一篇的表,结构如下 CREATE TABLE `money` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名', `money` int(26) NOT NULL DEFAULT '0' COMMENT '钱', `is_deleted` tinyint(1) NOT NULL DEFAULT '0', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), KEY `name` (`name`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;2. 项目配置配置信息,与之前有一点点区别,我们新增了更详细的日志打印;本篇主要目标集中在添加记录的使用姿势,对于配置说明,后面单独进行说明 ...

June 23, 2019 · 2 min · jiezi

微信小程序授权登录演示代码

登录效果: 整体设计: github地址:链接描述

June 22, 2019 · 1 min · jiezi

SpringBoot集成Thymeleaf模版引擎

一、前言Thymeleaf 是一个优秀的、面向Java 的XML、HTML/HTML5 页面模板,具有丰富的标签语言和函数。因此,在使用Spring Boot 框架进行页面设计时, 一般都会选择Thymeleaf 模板。类似thymeleaf的模版还有freemarker,推荐使用thymeleaf,前后端分离。二、springboot集成Thymeleaf模版引擎pom.xml引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>application.properties中配置: ##去除thymeleaf的html严格校验spring.thymeleaf.mode=LEGACYHTML5#设定thymeleaf文件路径 默认为src/main/resources/templatesspring.freemarker.template-loader-path=classpath:/templates在controller中书写相关代码,注意controller层中注解使用@controller,不要是用@RestController,否则就会出现页面返回字符串而不是正常的html页面。模版html页面中,也是需要引入thymeleaf: <html xmlns:th="http://www.thymeleaf.org">最后启动项目即可三、需要注意的问题通过以上配置后,我们发现,有时候自己写的html页面会无法解析,这种情况就有可能是因为使用springboot的thymeleaf模板时,默认会对HTML进行严格的检查,导致当标签没有闭合时就会通不过。nekohtml这个依赖可以解决这一问题。为了解决html严格校验报错的问题,可以在pom.xml增添依赖nekohtml <dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.15</version> </dependency>

June 21, 2019 · 1 min · jiezi

springboot通过jar包方式引入bootstrap

一、springboot引入bootstrap的两种方式SpringBoot结合前端有主要有两种方法,一种是在static里面直接加入下载的bootstrap中的css或js;另一种是引入webjars,以jar包的形式加入项目。手动在static中引入bootstrap需要自己去手动下载bootstrap,而引入webjars通过jar包方式就需要配置pom.xml即可。webjars方式引入bootstrap,实际上就是通过webjars方式管理前端静态资源的方式,具体的可以参考:https://www.jianshu.com/p/66d...WebJars官网找到项目所需的依赖,例如在pom.xml引入 jQuery、BootStrap前端组件等。二、webjars方式引入新建一个SpringBoot Web项目。然后在pom文件引入webjars的jar,pom文件代码如下: <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.5</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.1.1</version> </dependency>然后我们观察下项目的依赖包:然后在src/main/resources/static文件下新建index.html,代码如下: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Dalaoyang</title> <link rel="stylesheet" href="/webjars/bootstrap/3.3.5/css/bootstrap.min.css" /> <script src="/webjars/jquery/3.1.1/jquery.min.js"></script> <script src="/webjars/bootstrap/3.3.5/js/bootstrap.min.js"></script></head><body><div class="container"><br/> <div class="alert alert-success"> <a href="#" class="close" data-dismiss="alert" aria-label="close">×</a> <h3>index首页</h3>Hello, <strong>springboot and bootstrap!!!</strong> </div></div></body></html>配置结束,启动项目,访问http://localhost:8888/三、参考链接https://www.cnblogs.com/dalao...[https://www.cnblogs.com/dalao...][7]

June 20, 2019 · 1 min · jiezi

解决SpringBoot多模块发布时99的问题SpringBoot发布的8个原则和4个问题的解决方案

如果使用的是 SpringBoot 多模块的项目,在发布的时候可能遇到各种各样的问题。本文归纳了以下 8 个原则和发布时经常出现的 4 个问题的解决方案,掌握了这些原则和解决方案,几乎可以解决绝大数 SpringBoot 发布问题。 SpringBoot 多模块发布的 8 大原则 1 在发布模块打包,而不是父模块上打包比如,以下项目目录:如果要发布 api 就直接在它的模块上打包,而不是在父模块上打包。 2 公共调用模块,打包类型设置为 jar 格式公共模块,比如 common 和 model 需要设置 packaging 为 jar 格式,在 pom.xml 配置: <packaging>jar</packaging> 3 发布模块打包类型设置为 war 格式在发布的模块 pom.xml 中设置: <packaging>war</packaging> 4 排除内置 tomcat在发布的模块 pom.xml 中设置: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope></dependency>当设置 scope=provided 时,此 jar 包不会出现在发布的项目中,从而就排除了内置的 tomcat。 5 设置启动类此步骤相当于告诉 tomcat 启动的入口在哪。需要在启动类添加如下代码: @SpringBootApplicationpublic class ApiApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(ApiApplication.class); } public static void main(String[] args) { SpringApplication.run(ApiApplication.class, args); }} ...

June 20, 2019 · 1 min · jiezi

Spring-Boot的学习之路02和你一起阅读Spring-Boot官网

官网是我们学习的第一手资料,我们不能忽视它。却往往因为是英文版的,我们选择了逃避它,打开了又关闭。我们平常开发学习中,很少去官网上看。也许学完以后,我们连官网长什么样子,都不是很清楚。所以,我们在开始去学习之前,我们先拜读一下Spring Boot官网,对其有一个大体上的了解。我们在后续的讲解中, 有可能会引用到官网上的知识。 如果要建立完整的知识体系,我的个人看法是了解官网这个环节是少不了的。我在写《Spring Boot的学习之路》这个系列时,增加了这样一篇文章,来体现其重要性。 那接下来,我们一起来阅读一下Spring Boot官网。我们从中可以得到哪些有价值的知识。 一. 访问Spring Boot网站,看看网站有哪些板块Spring Boot地址:https://spring.io/projects/spring-boot通过上面地址,我们可以看到如下图显示页面。 ① Projects 项目左边展示的所有的项目列表,Spring Boot就排在第一个,说明还是很重要的呢。不流行都不行 ② Overview 概述Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".中文意思:Spring Boot使创建独立的、生产级的、基于Spring的应用程序变得容易,您可以“只运行”。 We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration.中文意思:我们对Spring平台和第三方库有一个独到的观点,这样你就可以以最少的麻烦开始了。大多数Spring Boot应用程序只需要很少的Spring配置。 ...

June 20, 2019 · 1 min · jiezi

spring-boot开发soap-webservice

介绍spring boot web模块提供了RestController实现restful,第一次看到这个名字的时候以为还有SoapController,很可惜没有,对于soap webservice提供了另外一个模块spring-boot-starter-web-services支持。本文介绍如何在spring boot中开发soap webservice接口,以及接口如何同时支持soap和restful两种协议。 soap webservice Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,既可以是soap webservice也可以是restwebservice,在rest还没出来之前,我们说webservice一般是指基于soap协议进行通信的web应用程序。  在开始之前,我觉得有必要了解下soap webservice,具体的概念网上可以找到很多资料,但网上资料概念性较强,而且soap协议使用的是xml进行通信,相信xml里面一个namespace就能吓跑一大堆人,所以这里不讨论具体的soap协议细节,我想通过一个例子来说明什么是soap webservice,通过该例子,你能了解soap webservice其运作原理,当然如果你觉得你对这个已经很了解了,大可跳过本章节,本章节跟后面的内容没有任何关系。假设我们开发了一个web接口,想给别人用,我们要怎么办     1.部署接口到服务器         2.编写接口文档,写清楚接口是通过什么方法调的,输入参数是什么,输出参数是什么,错误时返回什么。     那问题来了,我们能不能只把接口部署到服务器上,然后接口不单能提供具体的服务,而且还能自动生成一份标准的接口文档,把接口信息都记录在该文档里,如果能做到,是不是能做到"接口即文档"的目的。      那么一个接口的信息包括哪些呢?     1.接口地址          2.接口调用方法          3.接口输入参数          4.接口输出参数          5.接口出错返回信息          6..... soap webservice里wsdl文件就是接口描述信息。核心的信息就是以上几个。第二个问题,由于Web service是一个平台独立,也就是说,使用接口的人不知道这个service是用什么技术开发的,可能是php可能是java等,但接口的参数和返回的数据都是一样的,要达到这种目的,就需要两个东西,一个是跟平台无关的数据格式,soap使用的是xml,一个是通信协议,也就是soap协议。 下面就介绍如何不使用任何框架,仅通过servlet实现一个webservice。该webservice功能很简单,就是通过一个人的姓名查询这个人的详细信息。 ps:servlet是java web的基础,理解servlet对理解整个java web非常重要,没写过servlet就开始用各种框架写接口就是在胡闹。  1. wsdl文件  准备以下wsdl文件,不要管这个文件是怎么来的,是怎么生成的,我们这次只讲原理,不谈细节,总之,你根据需求写出了这个wsdl文件。 soap:address location里面端口号需要修改为servlet运行的端口号。  从以下xml片段可以看出    接口名称是EmployeeDetail(wsdl:operation)     接口输入参数是EmployeeDetailRequest(wsdl:input)     接口输出参数是EmployeeDetailResponse(wsdl:output)     接口地址是http://localhost:8081/ws-servlet/ws/employee-detail(soap:address)是不是很简单,是的,为了简单,我直接将wsdl文件用变量存储,我们还需要配置下web.xmlweb.xml这样我们访问http://localhost:8080/ws/employee就能返回一个wsdl文件,也就是接口描述文件。在wsdl文件里,我们定义接口地址为http://localhost:8080/ws/employee-detail,接下来我们就要实现这个接口。   业务servlet这里不做任何业务处理,不做xml转bean,不做bean转xml,就是这么暴力,直接返回xml,但他仍是一个soap服务,支持所有soap工具调用。  将servlet配置到web.xml里 web.xml 这个地址必须和wsdl文件里定义的保持一致,不然服务无法被找到。  测试 使用soapui测试我们的webservice,通过地址http://localhost:8081/ws-servlet/ws/employee导入wsdl文件,测试接口,返回我们在业务servlet里面写死的内容。恭喜你,你已经不依赖任何第三方包完成了一个soap webservice。  ...

June 19, 2019 · 1 min · jiezi

松哥整理了-15-道-Spring-Boot-高频面试题看完当面霸

什么是面霸?就是在面试中,神挡杀神佛挡杀佛,见招拆招,面到面试官自惭形秽自叹不如!松哥希望本文能成为你面霸路上的垫脚石! <!--more--> 做 Java 开发,没有人敢小觑 Spring Boot 的重要性,现在出去面试,无论多小的公司 or 项目,都要跟你扯一扯 Spring Boot,扯一扯微服务,不会?没用过? Sorry ,我们不合适! 今天松哥就给大家整理了 15 道高频 Spring Boot 面试题,希望能够帮助到刚刚走出校门的小伙伴以及准备寻找新的工作机会的小伙伴。 1.什么是 Spring Boot ?传统的 SSM/SSH 框架组合配置繁琐臃肿,不同项目有很多重复、模板化的配置,严重降低了 Java 工程师的开发效率,而 Spring Boot 可以轻松创建基于 Spring 的、可以独立运行的、生产级的应用程序。通过对 Spring 家族和一些第三方库提供一系列自动化配置的 Starter,来使得开发快速搭建一个基于 Spring 的应用程序。 Spring Boot 让日益臃肿的 Java 代码又重回简洁。在配合 Spring Cloud 使用时,还可以发挥更大的威力。 2.Spring Boot 有哪些特点 ?Spring Boot 主要有如下特点: 为 Spring 开发提供一个更快、更广泛的入门体验。开箱即用,远离繁琐的配置。提供了一系列大型项目通用的非业务性功能,例如:内嵌服务器、安全管理、运行数据监控、运行状况检查和外部化配置等。绝对没有代码生成,也不需要XML配置。3.Spring Boot 中的 starter 到底是什么 ?首先,这个 Starter 并非什么新的技术点,基本上还是基于 Spring 已有功能来实现的。首先它提供了一个自动化配置类,一般命名为 XXXAutoConfiguration ,在这个配置类中通过条件注解来决定一个配置是否生效(条件注解就是 Spring 中原本就有的),然后它还会提供一系列的默认配置,也允许开发者根据实际情况自定义相关配置,然后通过类型安全的属性注入将这些配置属性注入进来,新注入的属性会代替掉默认属性。正因为如此,很多第三方框架,我们只需要引入依赖就可以直接使用了。 ...

June 19, 2019 · 3 min · jiezi

『-效率工具-』Spring-Boot版的轻量级代码生成器减少70以上的开发任务

一. 前言之前很着迷于代码自动生成,减少写重复代码的工作量。网络上也搜索了很久,有基于插件的,有GUI的。但其配置和学习成本都比较高,都不是很如我意。本想自己用SpringBoot写一个,在收集相关的资料的时候, 偶然找到了人人开源实现的一个renren-generator项目,也正好是Spring Boot搭建的,也就下载下来,自己玩起来了。结合自己项目修改模板使用过后, 还是很方便的。我也不重复造轮子了。那么接下来就说说使用心得。 字段类型与实体类型可以自由配置支持表前缀可以设置是否取消数据库支持Mysql、Sqlserver、Oracle、Postgresql可在线生成entity、xml、dao、service、vue、sql代码2.本地部署我fork了一个renren-generator项目地址:https://gitee.com/rayson517/r... 通过git下载源码修改application.yml,更新MySQL账号和密码、数据库名称Eclipse、IDEA运行RenrenApplication.java,则可启动项目项目访问路径:http://localhost演示效果图: 3. 根据实际项目情况DIY代码生成模板模板路径在resources/template文件夹下面, 然后根据已有项目的代码结构,进行修改就好。 4. 一键生成sql数据库说明文档,解放双手这个功能是我自己DIY增加的功能,目前还没提交上去。需要的可以留言呢。 5. 扩展支持DB2扩展数据库DB2的支持,因为我们公司有在用。所以准备增加一个支持。

June 18, 2019 · 1 min · jiezi

宜信开源微服务任务调度平台SIATASK入手实践

引言最近宜信开源微服务任务调度平台SIA-TASK,SIA-TASK属于分布式的任务调度平台,使用起来简单方便,非常容易入手,部署搭建好SIA-TASK任务调度平台之后,编写TASK后配置JOB进行调度,进而实现整个调度流程。本文新建了JOB示例,该JOB关联了前后级联的两个TASK,TASKONE(前置TASK)和TASKTWO(后置TASK),主要阐述一个JOB怎样关联配置两个级联TASK,以及该JOB是如何通过SIA-TASK实现任务调度,最终实现对两个TASK执行器的调用。 拓展阅读:宜信开源|宜信开源微服务任务调度平台SIA—TASK宜信开源|分布式任务调度平台SIA-TASK的架构设计与运行流程 首先,根据部署文档来搭建任务调度平台。源码地址:https://github.com/siaorg/sia... 官方文档:https://github.com/siaorg/sia... 任务调度平台主要由任务编排中心、任务调度中心以及ZK和DB等第三方服务构成,搭建SIA-TASK任务调度平台需要的主要工作包括: 1.MySQL的搭建及根据建表语句建表 2.zookeeper安装 3.SIA-TASK前端项目打包及部署 4.任务编排中心(sia-task-config)部署 5.任务调度中心(sia-task-scheduler)部署 从github上clone代码仓库并下载源码后,可根据SIA-TASK部署指南,搭建SIA-TASK任务调度平台并启动,详见SIA-TASK部署指南 搭建好SIA-TASK任务调度平台后,下一步就是TASK执行器实例的编写啦。 其次,根据开发文档来编写TASK执行器实例并启动。根据SIA-TASK开发指南,编写了两个TASK示例,TASKONE(前置TASK)和TASKTWO(后置TASK),具体开发规则见SIA-TASK开发指南,TASK示例关键配置即代码在下文有详细展示与介绍。 该示例为springboot项目,并且需要通过POM文件引入SIA-TASK的执行器关键依赖包sia-task-hunter来实现task执行器的自动抓取,首先需要将SIA-TASK源码中的sia-task-hunter包用mvn install命令打包为jar包安装至本地仓库,SIA-TASK源码中的sia-task-hunter包如下图示: 然后就可以进行示例的编写,示例主要包括以下几部分: 配置POM文件关键依赖 <!-- 此处添加个性化依赖(sia-task-hunter) --> <dependency> <groupId>com.sia</groupId> <artifactId>sia-task-hunter</artifactId> <version>1.0.0</version> </dependency>配置文件主要配置项 # 项目名称(必须) spring.application.name: onlinetask-demo # 应用端口号(必须) server.port: 10086 # zookeeper地址(必须) zooKeeperHosts: *.*.*.*:2181,*.*.*.*:2181,*.*.*.*:2181 # 是否开启 AOP 切面功能(默认为true) spring.aop.auto: true # 是否开启 @OnlineTask 串行控制(如果使用则必须开启AOP功能)(默认为true)(可选) spring.onlinetask.serial: true编写TASK执行器主要代码@Controllerpublic class OpenTestController { @OnlineTask(description = "success,无入参",enableSerial=true) @RequestMapping(value = "/success-noparam", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8") @CrossOrigin(methods = { RequestMethod.POST }, origins = "*") @ResponseBody public String taskOne() { Map<String, String> info = new HashMap<String, String>(); info.put("result", "success-noparam"); info.put("status", "success"); System.out.println("调用taskOne任务成功"); return JSONHelper.toString(info); } @OnlineTask(description = "success,有入参",enableSerial=true) @RequestMapping(value = "/success-param", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8") @CrossOrigin(methods = { RequestMethod.POST }, origins = "*") @ResponseBody public String taskTwo(@RequestBody String json) { Map<String, String> info = new HashMap<String, String>(); info.put("result", "success-param"+"入参是:"+json); info.put("status", "success"); System.out.println("调用taskTwo任务成功"); return JSONHelper.toString(info); }}当编写完TASK执行器实例后,启动该执行器所在进程启动日志如下图: ...

June 18, 2019 · 1 min · jiezi

从0手写springCloud项目组件搭建

写在前面一直在写springCloud项目,每次都是新建项目然后从零开始写配置,现在写一个尽量通用的项目,方便后续搭建框架的时候直接拿过去使用。 需要搭建的组件(模块)有: eureka(认证),zuul(网关),auth(认证),config(配置中心),user(用户),order(订单),pay(支付),feign...这边主要想涉及到的框架技术有:springcloud,springboot2,oauth2,springSecurity,liquibase,lcn(5.0.2),mybatisplus,logback,redis,mysql,swagger2,poi需要搭建、支持的技术 github,jenkins(自动发布),maven私服,nginx,redis,mysql5.7,jdk1.8,swagger2,rabbitmq一 需要搭建的组件需要搭建的组件主要有7个模块(feign会集成到具体模块),这边我回详细记录eureka,zuul,auth,config,user.因为前四者是springCloud的配置。需要详细介绍,而具体的业务逻辑代码会在具体模块,这里我将以user模块为例子详细介绍. eureka我们知道,在为服务里面,所有模块需要被注册到一个注册中心,持续的向注册中心发送心跳以保证连接的存活。而springcloud的注册中心有consul和eureka,这里我选用的是eureka.eureka的代码很简单,只需要在配置文件里面配置好注册地址与密码(可不设置,生产上强烈建议设置),并标识好自己不向自己注册,不被自己发现即可。 maven坐标: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--我是用的springboot2.1.3如果是springboot1.5.x请不用这个--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency></dependencies>主类,不用做任何配置 @SpringBootApplication@EnableEurekaServerpublic class CrawlerEurekaApplication { public static void main(String[] args) { SpringApplication.run(CrawlerEurekaApplication.class, args); }}yml配置文件: spring: application: name: crawler-eurekaserver: host: http://localhost port: 9990eureka: client: fetch-registry: false register-with-eureka: false service-url: defaultZone: ${server.host}:${server.port}/eureka/ instance: prefer-ip-address: truezuul上面我们把注册中心搭建好了,访问localhost:9990就可以看到eureka的控制台。但是我们看不到一个服务注册上去了。现在我们搭建一个网关,因为在实际项目中,我们会有很多个微服务模块,而服务器只会向外暴露一个端口,其他的通过相对路径转发。这样也是为了安全和方便管理,有点nginx的感觉。网关的配置也不复杂:pom坐标: <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> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>主类除了标识为eureka-client,还标识是网关 ...

June 18, 2019 · 1 min · jiezi

SpringBoot系列教程之基础篇一白话我的学习经历

有人说,Spring Boot的出现,让Java迎来了又一春,它是Java应用开发的颠覆者,彻底改变了Java应用开发的模式。2017年,SpringBoot闯入我的生活, 也让我迎来了又一春我开始接触SpringBoot的时候,是在2017年,是公司同事在开始学。我也网上查找了些资料,发现SpringBoot相比传统SpringMVC在xml配置上有很大的一部分优势:无繁琐的xml配置,各个组件依赖配置都自动加入等。我便也跟着疯狂地学起来。不得不发表一下心得体会:用起来很爽,很舒服。 学习过程,痛并快乐着我是一个天生爱折腾的主儿。喜欢学习新的技术和实现方案,挑战难点。然后把学习到的知识转化为自己的,并记录下来,整理输出。有很多也跟我一样吧。 学习springboot的时间虽过了很久,但我仍旧感觉得到当初学习SpringBoot的那股劲。 现在学习技术的途径有很多,可以看视屏教程,看博客,看码云和Github都可以呢。 学习过程中,我也总是会遇到各种问题,或者不明白的知识点,也就是知识盲区,我会怎么做呢?根据这个知识点,提炼出“关键字”去百度或者谷歌搜索,对知识点先有一个大体的了解。但是要从浩瀚的资料中,筛选出有用的资料,那还真得有一双敏感的慧眼吧。我在搜索资料的时候,心里面就会去评估这份资料: 第一层:看到不符合的,直接pass掉。第二层:比较符合的。收集下来,再寻找相似的,方便后面做对比。第三层:直接符合的。那就是实践。看是否能正确解决。并做好记录。不断试错是一种态度。也正是这样的思考方式,解决了我遇到的很多问题。 在学习SpringBoot的过程中,除了基础知识点的积累,我少不了去找许多开源项目案例研究学习,折腾各种环境部署,并从中找到我需要的那部分,然后运用到自己的项目中。我很感谢有前辈们的探索和分享。 我当时的目标很简单,就一个----学好SpringBoot,然后能成功整合各个项目,并简单的用起来。 只是学会用的话,如果有SpringMVC的使用经验的话,上手是分分钟的。 当时,我的目的也很单纯,就是学会用,其他也没多想。我开始简单地搭建了三层架构,然后慢慢开始整合相关组件,实现功能需求。 就这么简单的目的,我什么都不想去实现它就可以了。然而会有很多人,还没开始去做,就开始打退堂鼓。从心里面就已经告知自己:“我不会,我不行,没有大佬带。”,就这样,每天活在痛苦和焦虑中。 有些路必须得自己去走,才能知道沿途的风景是多么的迷人。我曾经也很想会有大佬带,学什么会什么。但对于我们这样的无名小卒,菜鸟小白,谁又会去关心呢。只能啃书,啃视频。有问题也不知道该如何解决。 痛苦在所难免,但如果有我陪着,你是否不会感觉到孤单。我是一天不写代码就剁手的程序猿。遇到的问题,也尽可能的去一起解决,减轻学习上的痛苦。 我当时大概学习了一两周,就开始上手,整合项目,直接开干。在项目中去夯实基础。 学习完了,就得实战。不说了,直接干它一个商城!其过程可谓是艰辛痛苦,那可谓是网上搜罗各种资料博客,github上找Demo项目学习,也算是很艰辛的一段学习历程。 我在的公司是个小公司,但我当时主要负责聚合支付类项目的开发,一想到互联网的项目,应该使用的是比较新的技术开发,终于可以涨姿势了。当时就我一个人接手,我还很高兴,终于能挺起腰杆,撸起袖子,大干一场。但当我真正去接手的时候,我不敢相信自己的眼睛,项目是用servlet+jsp实现,还是几个研究生实习开发的。现在都二十一世纪了好吧,还是互联网项目。咋就没看出一点互联网项目的气息。 收了,吐槽结束。 也许正因为是在这样的环境下,让我有机会去把所学的给施展开来。当时,我一边用原来的技术开发着原有的功能,一边在谋划用SpringBoot新框架的搭建和实现。 我很喜欢当时带我项目的老师说的一句话:用你最熟悉的语言开发。 我深信不疑。 后来,整个的搭建思路,前后端的整合过程,百分之90是我独立完成的。我也很成功的将原有的旧项目V1.0,迁移到我新搭建的SpringBoot项目中,并按照规范开发,就基本上完成SpringBoot单体应用V2.0整合。后面,由于有其他需求,又进行了SpringBoot+Dubbo的微服务搭建V3.0。 正因为有了这样的经历,我知道这样的经验很宝贵,也很来之不易。当然也有我很多没有考虑到的,还需继续学习。 遗憾的是,当初没有做好笔记,光顾着自己爽了。现在也只能靠着自己残缺的记忆。 不遗憾的是,我依然还有心去做一件我值得去做的事----那就是将零散的知识点,躺过的坑,能总结分享,有机地形成一个个系列。 这也是我现在准备去做的事情。 曾经的我也开始过,但后来没有写下去,因为自己的口才和知识面不够,没有多少的落地经验,自然也写不出,即使写得出,也写不好,写不清楚。 相比之前的我,现在的我,年岁长了,经验长了,学到的和看到的多了。也写了几万的文字。也更有底气去做这件事情,相信可以写得更好。 于你,可以跟我一起,学习SpringBoot,并能真正的从基础入门到独自搭建属于自己的框架,为自己增添技术实力, 而且掌握大小公司里的开发技巧,工作习惯。 于我,可以在写教程中,反思自我,争取做得更好。也可能会有更好,更有趣的想法在其中产生。

June 18, 2019 · 1 min · jiezi

注册中心配置了spring-security后客户端启动报错

注册中心配置了security后, 报了 registration failed Cannot execute request on any known server 的错误, 原因是 2.1版本的security默认加上了 csrf 拦截, 所以需要通过重写方法, 把csrf拦截禁用 在启动类上加上以下代码(禁用csrf)即解决问题 @EnableWebSecuritystatic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); }}完整代码如下: /** * @author 毛宇鹏 */@EnableEurekaServer@SpringBootApplication(exclude={ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})public class RegisterApplication { public static void main(String[] args) { SpringApplication.run(RegisterApplication.class, args); } /** * 2.1版本的security默认加上了 csrf 拦截, 所以需要通过重写方法, 把csrf拦截禁用 * 参考: https://github.com/spring-cloud/spring-cloud-netflix/issues/2754 * <pre> * This is because @EnableWebSecurity is now added by default when Spring Security is on the classpath. * This enable CSRF protection by default. You will have the same problem in 1.5.10 if you add @EnableWebSecurity. * One work around, which is not the most secure workaround if you have browsers using the Eureka dashboard, is to disable CSRF protection. * This can be done by adding the following configuration to your app. * </pre> */ @EnableWebSecurity static class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); } }}

June 17, 2019 · 1 min · jiezi

想让自己的项目6666可是-Chrome-不答应

读万卷书,行万里路!有的技能可以从书里学会,有的技能却需要在实战中打怪升级慢慢掌握,今天就来和大家聊一个很多小伙伴经常遇到的问题。 <!--more--> 缘起有人向松哥反映,在搭建微服务分布式配置中心 Spring Cloud Config 时,如果将端口设置为 6000,总是访问不成功,像下面这样: 如果换成 Tomcat 默认的 8080 就可以访问了。 其实不止 6000,如果你配置成 6666 ,也是无法访问成功的! 分析刚入行或者经验欠缺的小伙伴应该很容易遇到这个问题,松哥就来和大家稍微说一说这个问题。 首先,当我们将项目的端口设置为 6000 之后,并非仅仅只有 Chrome 无法访问,Firefox、Safari 也是无法访问的,反而是经常被大家忽略的坐在角落的 IE/Edge 这对难兄难弟可以访问!看看 Safari 访问 6000 端口怎么说: 再看看 Firefox 访问 6000 端口怎么说: 但是 Edge 就可以访问,如下: 看到这里,大家首先可以确认出现这个问题,和你的代码没有关系!是不是可以松一口气了! 这个问题实际上是由 Chrome 默认的非安全端口限制导致的,除了上文说的 6000,还有其他端口也无法在 Chrome 、Firefox 以及 Safari 中访问(具体端口见文末列表)。 这些无法访问的端口大部分都是小于 1024,小于 1024 的端口大家应该会很少使用,基本上不会在这个上面栽跟头。大于 1024 的端口也并非每一个都可以使用,这才是容易犯错的地方。 解决那么问题要怎么解决呢?两个思路: 修改项目端口(推荐)修改浏览器配置,使之允许访问非安全端口推荐大家使用第一种方案,省事! 如果要使用第二种方案: Chrome 修改办法如下:右键单击Chrome快捷方式 -> 目标 -> 末尾添加参数:--explicitly-allowed-ports=6000 ...

June 17, 2019 · 1 min · jiezi

SpringBoot系列教程JPA之新增记录使用姿势

SpringBoot系列教程JPA之新增记录使用姿势上一篇文章介绍了如何快速的搭建一个JPA的项目环境,并给出了一个简单的演示demo,接下来我们开始业务教程,也就是我们常说的CURD,接下来进入第一篇,如何添加数据 通过本篇文章,你可以get到以下技能点 POJO对象如何与表关联如何向DB中添加单条记录如何批量向DB中添加记录save 与 saveAndFlush的区别<!-- more --> I. 环境准备实际开始之前,需要先走一些必要的操作,如安装测试使用mysql,创建SpringBoot项目工程,设置好配置信息等,关于搭建项目的详情可以参考前一篇文章 190612-SpringBoot系列教程JPA之基础环境搭建 下面简单的看一下演示添加记录的过程中,需要的配置 1. 表准备沿用前一篇的表,结构如下 CREATE TABLE `money` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名', `money` int(26) NOT NULL DEFAULT '0' COMMENT '钱', `is_deleted` tinyint(1) NOT NULL DEFAULT '0', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), KEY `name` (`name`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;2. 项目配置配置信息,与之前有一点点区别,我们新增了更详细的日志打印;本篇主要目标集中在添加记录的使用姿势,对于配置说明,后面单独进行说明 ...

June 16, 2019 · 3 min · jiezi

SpringBoot系列教程JPA之基础环境搭建

JPA(Java Persistence API)Java持久化API,是 Java 持久化的标准规范,Hibernate是持久化规范的技术实现,而Spring Data JPA是在 Hibernate 基础上封装的一款框架。JPA作为标准,实际上并没有说局限于某个固定的数据源,事实上mysql,mongo, solr都是ok的。接下来我们将介绍下springboot结合jpa 来实现mysql的curd以及更加复杂一点的sql支持 <!-- more --> jpa系列教程将包含以下几块 环境搭建基础的插入、修改、删除数据的使用姿势基础的单表查询,如(>, <, = , in, like, between),分页,排序等多表关联查询事物使用本篇为开始第一篇,先搭建一个可以愉快玩耍的jpa项目 I. 环境搭建我们选择的数据库为mysql,所以有必要先安装一下,这里跳过mysql的安装教程,直接进入springboot项目的搭建 1. pom依赖我们这里选择的是2.0.4.RELEASE版本进行演示 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> <!-- lookup parent from update --></parent><properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version></properties><dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.45</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency></dependencies><build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement></build><repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository></repositories>上面的pom依赖中,关键的是下面两个, 第一个引入的是jpa相关包,后面那个则是mysql的连接依赖,相当于指定操作mysql数据库 ...

June 16, 2019 · 2 min · jiezi

开发者说Sentinel-流控功能在-SpringMVCSpringBoot-上的实践

从用户的视角来感受一个开源项目的成长,是我们推出「开发者说」专栏的初衷,即在开发者进行开源项目选型时,提供更为立体的项目信息。专栏所有内容均来自作者原创/投稿,本文是「开发者说」的第6篇,作者 Jason Joo,@友乐活(北京),Sentinel Committer. 1st:《深度剖析开源分布式事务方案 Seata 的事务协调器》 2nd:《RocketMQ 消息发送的高可用设计》 3st:《消息队列 Kafka 和 RocketMQ 之我见》 4th:《如何参与定义一款 IDE 插件》 5th:《基于 Nacos 的网关灰度路由和服务权重灰度》 集成 Sentinel 前生流控在分布式系统中是较为基本的需求,其需要在系统负载、服务质量、流量甄别、安全⻛控等⽅⾯进⾏保障,并根据业务需求,进⾏动态调整或⼈工临时介入,尤其是在⼀些事件性的时期,以实现快速控制和恢复服务的效果。 流控手段一般挂载在流量网关和业务内的逻辑。 流量网关常见于 Nginx 这类代理层,通过扩展插件、Lua脚本进⾏针对 IP/Path/Query 等形式的流控。业务内则⼤多在局部或框架层进行信号量、线程池、超时时间或其它逻辑来实现流控。前者主要体现在运维的可操作性,不侵⼊业务线,而后者则针对性更强,但有侵⼊性或修改时需要部署,⾯向业务团队可控。 两种类型的流控往往⽐较割裂(由不同的团队在不共享的空间内进行控制),常出现指标的不协调性。 为了解决这⼀问题,我们开始汇总现有的需求,调研相关的系统,并准备实现⼀套可以同时面向业务和运维,进行应用级隔离和满足基本规则类型需求的流控实现,预期是在 Nginx 端利用LuaJIT实现一套更为强大的流控模块。 调研过程中,适逢 Sentinel 0.1/0.2的发布,⽀持servlet集成(URL限流),带有操作⾯板(Dashboard),支持基本的实时状况查看、实时的修改分发规则、全局负载和单点熔断,能基于QPS、信号量等形式进行流控。除了零侵入以外,基本满⾜我们的需求,所以准备基于 Sentinel 进行方案落地尝试。 集成 Sentinel 的实践我们的基本需求如下: 基于 URL 做流控基于 Dashboard 做动态修改规则业务端针对 SpringMVC/SpringBoot⽀持异步 Servlet (后续提出)sentinel-transport 监听端⼝可定制(涉及防⽕墙配置、同⼀节点多服务)集成适配基于 Sentinel 所提供的功能、适配方式,需要进行基本的配置和修改。 集成方式现有项⽬流量⼊口部分⼤多为基于 SpringMVC 的项目,少部分为 SpringBoot 项目,并且从运维部署的角度看,⽬前主要有普通运⾏方式(JVM/Tomcat)和容器化方式。 普通运行方式:尽量避免修改 JVM 启动参数,参数通过集中配置中心或 properties ⽂件来定义;容器化⽅式:参数⼤多是通过 ENV 环境变量进行定义。所以我们根据实际的需求,将 Sentinel 初始化⼯作进⾏了封装,基于 SpringMVC 提供了XML初始化方式,基于 SpringBoot 提供了注解初始化方式,例如: ...

June 14, 2019 · 1 min · jiezi

Spring-Boot配置Redis-Sentinel的例子

原文地址:https://chanjarster.github.io... 本文介绍Spring Boot连接Redis Sentinel的例子。 本文关联的源码:github 基本信息拓扑(M代表redis-master,S代表redis-sentinel,R代表redis-slave,C代表Spring Boot Application): +---------------------------+ | | +-----+ +-----+ +-----+ | M |-------| S |-------| R | +-----+ +-----+ +-----+ | | | +-----+ +----------| C | +-----+application.yaml配置: spring: redis:# host: redis-master# port: 6379 password: abc sentinel: master: springboot nodes: - redis-sentinel:26379注意这里不需要配置master的host和port,这些信息会从Redis Sentinel中得到。 演示步骤打包并构建镜像:mvn clean install dockerfile:build 进入docker目录,执行docker-compose up -d 观察Spring Boot Application的日志:docker logs -f docker_spring-boot_1,会发现每隔3秒执行INCR foo: 07:53:49.205 INFO hello.Application : INCR foo: 107:53:52.212 INFO hello.Application : INCR foo: 207:53:55.213 INFO hello.Application : INCR foo: 307:53:58.216 INFO hello.Application : INCR foo: 407:54:01.217 INFO hello.Application : INCR foo: 5停止redis-master:docker stop docker_redis-master_1,会看到Spring Boot Application的Redis链接出现了问题: ...

June 14, 2019 · 2 min · jiezi

干货最新版-Spring-Boot215-教程案例合集

最近发了一系列的 Spring Boot 教程,但是发的时候没有顺序,有小伙伴反映不知道该从哪篇文章开始看起,刚好最近工作告一个小小段落,松哥就把这些资料按照学习顺序重新整理了一遍,给大家做一个索引,大家照着索引就可以由浅入深的学习了。 <!--more--> 松哥刚开始写这个系列的时候最新版是 Spring Boot2.1.4 ,后来写着写着版本升级了变成 Spring Boot2.1.5 了,于是我又用 Spring Boot2.1.5 接着写,因此索引中的教程主要是这两个版本的教程。 可能有人觉得小版本的变化差异不大,事实上也确实如此,不过变化不大不意味着没有变化,给大家随便举两个例子: 在整合 Redis 时,Spring Boot2.1.4 不用引入 Spring Security,而 Spring Boot2.1.5 则需要引入 Spring Security。再比如 Spring Security 中的角色继承,在 Spring Boot2.0.8 之前和之后的写法完全不同,这些差异松哥也给大家细细剖析了。这一系列教程不是终点,而是一个起点,松哥后期还会不断完善这个教程,也会持续更新 Spring Boot 最新版本的教程,希望能帮到大家。教程索引如下: 创建一个 Spring Boot 项目,你会几种方法?这一次,我连 web.xml 都不要了,纯 Java 搭建 SSM 环境你真的理解 Spring Boot 项目中的 parent 吗?一文读懂 Spring Boot 配置文件 application.properties !Spring Boot中的yaml配置简介Spring Boot 中的静态资源到底要放在哪里?极简 Spring Boot 整合 Thymeleaf 页面模板Spring Boot 中关于自定义异常处理的套路!Spring Boot中通过CORS解决跨域问题SpringMVC 中 @ControllerAdvice 注解的三种使用场景!Spring Boot中,Redis缓存还能这么用!Spring Boot 操作 Redis,三种方案全解析!Spring Boot 一个依赖搞定 session 共享,没有比这更简单的方案了!另一种缓存,Spring Boot 整合 Ehcache徒手撸一个 Spring Boot 中的 Starter ,解密自动化配置黑魔法!Spring Boot 定义系统启动任务,你会几种方式?干货|一文读懂 Spring Data Jpa!Spring Boot数据持久化之JdbcTemplateSpring Boot多数据源配置之JdbcTemplate最简单的SpringBoot整合MyBatis教程极简Spring Boot整合MyBatis多数据源Spring Boot 中 10 行代码构建 RESTful 风格应用Spring Boot 整合 Shiro ,两种方式全总结!干货|一个案例学会Spring Security 中使用 JWT!Spring Security 中的角色继承问题Spring Security 登录添加验证码SpringSecurity登录使用JSON格式数据Spring Boot 中实现定时任务的两种方式!SpringBoot整合Swagger2,再也不用维护接口文档了!整理了八个开源的 Spring Boot 学习资源另外,还有一件重要的事,就是松哥把微信公众号中文章的案例,都整理到 GitHub 上了,每个案例都对应了一篇解读的文章,方便大家学习。松哥以前写博客没养成好习惯,有的案例丢失了,现在在慢慢整理补上。 ...

June 14, 2019 · 1 min · jiezi

阿里-EasyExcel-7-行代码优雅地实现-Excel-文件生成下载功能

欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 《Java 核心知识整理&面试.pdf》资源链接!!个人网站: https://www.exception.site/essay/how-to-create-excel-by-seven-line-code 目录一、前言二、Apache poi、jxl 的缺陷三、阿里出品的 EasyExcel,安利一波四、EasyExcel 解决了什么五、快速上手六、特殊场景支持七、Web 下载示例代码八、需要注意的点九、总结一、前言关于导出 Excel 文件,可以说是大多数服务中都需要集成的功能。那么,要如何优雅快速地(偷懒地)去实现这个功能呢? 你可能第一想法是:这还不简单?用 Apache 开源框架 poi, 或者 jxl 都可以实现啊。面向百度编程,把代码模板 copy 下来,根据自己的业务再改改,能有多难? 嗯.. 的确不难,但是你的代码可能是下面这个熊样子的: 上面这段代码看上去是不是又臭又长呢?今天,小哈将教您如何使用 7 行代码搞定 Excel 文件生成功能! 二、Apache poi、jxl 的缺陷在说如何实现之前,我们先来讨论一下传统 Excel 框架的不足!除了上面说的,Apache poi、jxl 都存在生成 excel 文件不够简单优雅快速外,它们都还存在一个严重的问题,那就是非常耗内存,严重时会导致内存溢出。 POI 虽然目前来说,是 excel 解析框架中被使用最广泛的,但这个框架并不完美。 为什么这么说呢? 开发者们大部分使用 POI,都是使用其 userModel 模式。而 userModel 的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,但是还是可控的。 然而 userModel 模式最大的问题是在于,对内存消耗非常大,一个几兆的文件解析甚至要用掉上百兆的内存。现实情况是,很多应用现在都在采用这种模式,之所以还正常在跑是因为并发不大,并发上来后,一定会OOM或者频繁的 full gc。 三、阿里出品的 EasyExcel,安利一波什么是 EasyExcel? 见名知意,就是让你操作 Excel 异常的酸爽。先来看下 EasyExcel GitHub 官方截图: ...

June 13, 2019 · 3 min · jiezi

Spring-boot-集成-ActiveMQ

安装ActiveMQ到Apache官方网站下载最新的ActiveMQ的安装包,并解压到本地目录下,下载链接如下:http://activemq.apache.org/do...。 进入bin 目录,如果我们是32位的机器,就双击 win32 目录下的 activemq.bat,如果是64位机器,则双击 win64 目录下的 activemq.bat ,运行结果如下:成功之后在浏览器输入 http://127.0.0.1:8161/ 地址,可以看到 ActiveMQ 的管理页面,用户名和密码默认都是 admin Spring Boot 整合 ActiveMQ工程结构 添加 pom 依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId></dependency>config 配置 # activemqspring.activemq.broker-url=tcp://localhost:61616spring.activemq.user=adminspring.activemq.password=admin#默认为true表示使用内存的activeMQ,不需要安装activeMQ serverspring.activemq.in-memory=true #如果此处设置为true,需要加如下的依赖包# <groupId>org.apache.activemq</groupId># <artifactId>activemq-pool</artifactId># 否则会自动配置失败,报JmsMessagingTemplate注入失败spring.activemq.pool.enabled=false队列模式创建 消息提供者:Producer.java import javax.jms.Destination; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jms.core.JmsMessagingTemplate;import org.springframework.stereotype.Service; @Servicepublic class Producer { @Autowired // 也可以注入JmsTemplate,JmsMessagingTemplate对JmsTemplate进行了封装 private JmsMessagingTemplate jmsTemplate; // 发送消息,destination是发送到的队列,message是待发送的消息 public void sendMessage(Destination destination, final String message){ jmsTemplate.convertAndSend(destination, message); }}创建消费者一: Consumer.java import org.springframework.jms.annotation.JmsListener;import org.springframework.stereotype.Component; @Componentpublic class Consumer { // 使用JmsListener配置消费者监听的队列,其中text是接收到的消息 @JmsListener(destination = "mytest.queue") public void receiveQueue(String text) { System.out.println("Consumer收到的报文为:"+text); }}创建消费者二:Consumer1 .java ...

June 13, 2019 · 2 min · jiezi

spring-statemachine的企业可用级开发指南3多个状态机共存

1、多个状态机的搞法在实际的企业应用中,基本不可能只有一个状态机流程在跑,比如订单,肯定是很多个订单在运行,每个订单都有自己的订单状态机流程,但上一章的例子,大家可以试一下,当执行到一个状态时,再次刷新页面,不会有任何日志出现,当一个状态流程执行到某个状态,再次执行这个状态,是不会有任何输出的,因为状态机的机制是只有在状态切换的时候才会事件(event)触发,所以我们这一章讲多个状态机的并行执行。 首先,靠上一章例子里面的手打定制一个StateMachineConfig的做法,就只能是有一个状态机流程制霸整个项目,这种霸道的做法肯定是不行啦,要想多个状态机流程并行,那么就要请builder出场了,看代码: private final static String MACHINEID = "orderMachine";public StateMachine<OrderStates, OrderEvents> build(BeanFactory beanFactory) throws Exception { StateMachineBuilder.Builder<OrderStates, OrderEvents> builder = StateMachineBuilder.builder(); System.out.println("构建订单状态机"); builder.configureConfiguration() .withConfiguration() .machineId(MACHINEID) .beanFactory(beanFactory); builder.configureStates() .withStates() .initial(OrderStates.UNPAID) .states(EnumSet.allOf(OrderStates.class)); builder.configureTransitions() .withExternal() .source(OrderStates.UNPAID).target(OrderStates.WAITING_FOR_RECEIVE) .event(OrderEvents.PAY).action(action()) .and() .withExternal() .source(OrderStates.WAITING_FOR_RECEIVE).target(OrderStates.DONE) .event(OrderEvents.RECEIVE); return builder.build(); }有没有似曾相识的感觉,里面描述订单状态机的初始状态,状态机的流程代码和StateMachineConfig几乎是一样的,但是都配置在StateMachineBuilder里面 StateMachineBuilder.Builder<OrderStates, OrderEvents> builder = StateMachineBuilder.builder();这是完整的builder类代码: import java.util.EnumSet;import org.springframework.beans.factory.BeanFactory;import org.springframework.context.annotation.Bean;import org.springframework.statemachine.StateContext;import org.springframework.statemachine.StateMachine;import org.springframework.statemachine.action.Action;import org.springframework.statemachine.config.StateMachineBuilder;import org.springframework.stereotype.Component;@Componentpublic class OrderStateMachineBuilder { private final static String MACHINEID = "orderMachine"; /** * 构建状态机 * * @param beanFactory * @return * @throws Exception */ public StateMachine<OrderStates, OrderEvents> build(BeanFactory beanFactory) throws Exception { StateMachineBuilder.Builder<OrderStates, OrderEvents> builder = StateMachineBuilder.builder(); System.out.println("构建订单状态机"); builder.configureConfiguration() .withConfiguration() .machineId(MACHINEID) .beanFactory(beanFactory); builder.configureStates() .withStates() .initial(OrderStates.UNPAID) .states(EnumSet.allOf(OrderStates.class)); builder.configureTransitions() .withExternal() .source(OrderStates.UNPAID).target(OrderStates.WAITING_FOR_RECEIVE) .event(OrderEvents.PAY).action(action()) .and() .withExternal() .source(OrderStates.WAITING_FOR_RECEIVE).target(OrderStates.DONE) .event(OrderEvents.RECEIVE); return builder.build(); } @Bean public Action<OrderStates, OrderEvents> action() { return new Action<OrderStates, OrderEvents>() { @Override public void execute(StateContext<OrderStates, OrderEvents> context) { System.out.println(context); } }; } }在完整的代码里面我们看到有个东西没讲,那就是MACHINEID,在builder的配置代码里面,有这么一段 ...

June 13, 2019 · 2 min · jiezi

极简-Spring-Boot-整合-Thymeleaf-页面模板

虽然现在慢慢在流行前后端分离开发,但是据松哥所了解到的,还是有一些公司在做前后端不分的开发,而在前后端不分的开发中,我们就会需要后端页面模板(实际上,即使前后端分离,也会在一些场景下需要使用页面模板,例如邮件发送模板)。 <!--more--> 早期的 Spring Boot 中还支持使用 Velocity 作为页面模板,现在的 Spring Boot 中已经不支持 Velocity 了,页面模板主要支持 Thymeleaf 和 Freemarker ,当然,作为 Java 最最基本的页面模板 Jsp ,Spring Boot 也是支持的,只是使用比较麻烦。 松哥打算用三篇文章分别向大家介绍一下这三种页面模板技术。 今天我们主要来看看 Thymeleaf 在 Spring Boot 中的整合! Thymeleaf 简介Thymeleaf 是新一代 Java 模板引擎,它类似于 Velocity、FreeMarker 等传统 Java 模板引擎,但是与传统 Java 模板引擎不同的是,Thymeleaf 支持 HTML 原型。 它既可以让前端工程师在浏览器中直接打开查看样式,也可以让后端工程师结合真实数据查看显示效果,同时,SpringBoot 提供了 Thymeleaf 自动化配置解决方案,因此在 SpringBoot 中使用 Thymeleaf 非常方便。 事实上, Thymeleaf 除了展示基本的 HTML ,进行页面渲染之外,也可以作为一个 HTML 片段进行渲染,例如我们在做邮件发送时,可以使用 Thymeleaf 作为邮件发送模板。 另外,由于 Thymeleaf 模板后缀为 .html,可以直接被浏览器打开,因此,预览时非常方便。 整合创建项目Spring Boot 中整合 Thymeleaf 非常容易,只需要创建项目时添加 Thymeleaf 即可: ...

June 13, 2019 · 3 min · jiezi

Spring-Boot-配置文件中的花样看这一篇足矣

在快速入门一节中,我们轻松的实现了一个简单的RESTful API应用,体验了一下Spring Boot给我们带来的诸多优点,我们用非常少的代码量就成功的实现了一个Web应用,这是传统的Spring应用无法办到的,虽然我们在实现Controller时用到的代码是一样的,但是在配置方面,相信大家也注意到了,在上面的例子中,除了Maven的配置之后,就没有引入任何的配置。 这就是之前我们所提到的,Spring Boot针对我们常用的开发场景提供了一系列自动化配置来减少原本复杂而又几乎很少改动的模板化配置内容。但是,我们还是需要去了解如何在Spring Boot中修改这些自动化的配置内容,以应对一些特殊的场景需求,比如:我们在同一台主机上需要启动多个基于Spring Boot的web应用,若我们不为每个应用指定特别的端口号,那么默认的8080端口必将导致冲突。 如果您还有在读我的Spring Cloud系列教程,其实有大量的工作都会是针对配置文件的。所以我们有必要深入的了解一些关于Spring Boot中的配置文件的知识,比如:它的配置方式、如何实现多环境配置,配置信息的加载顺序等。 配置基础在快速入门示例中,我们介绍Spring Boot的工程结构时,有提到过 src/main/resources目录是Spring Boot的配置目录,所以我们要为应用创建配置个性化配置时,就是在该目录之下。 Spring Boot的默认配置文件位置为: src/main/resources/application.properties。关于Spring Boot应用的配置内容都可以集中在该文件中了,根据我们引入的不同Starter模块,可以在这里定义诸如:容器端口名、数据库链接信息、日志级别等各种配置信息。比如,我们需要自定义web模块的服务端口号,可以在application.properties中添加server.port=8888来指定服务端口为8888,也可以通过spring.application.name=hello来指定应用名(该名字在Spring Cloud应用中会被注册为服务名)。 Spring Boot的配置文件除了可以使用传统的properties文件之外,还支持现在被广泛推荐使用的YAML文件。 YAML(英语发音:/jæml/,尾音类似camel骆驼)是一个可读性高,用来表达资料序列的格式。YAML参考了其他多种语言,包括:C语言、Python、Perl,并从XML、电子邮件的数据格式(RFC 2822)中获得灵感。Clark Evans在2001年首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者。目前已经有数种编程语言或脚本语言支援(或者说解析)这种语言。YAML是"YAML Ain't a Markup Language"(YAML不是一种标记语言)的递回缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重新命名。AML的语法和其他高阶语言类似,并且可以简单表达清单、散列表,标量等资料形态。它使用空白符号缩排和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种设定档、倾印除错内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。尽管它比较适合用来表达阶层式(hierarchical model)的数据结构,不过也有精致的语法可以表示关联性(relational model)的资料。由于YAML使用空白字元和分行来分隔资料,使得它特别适合用grep/Python/Perl/Ruby操作。其让人最容易上手的特色是巧妙避开各种封闭符号,如:引号、各种括号等,这些符号在巢状结构时会变得复杂而难以辨认。 —— 维基百科YAML采用的配置格式不像properties的配置那样以单纯的键值对形式来表示,而是以类似大纲的缩进形式来表示。比如:下面的一段YAML配置信息 environments: dev: url: http://dev.bar.com name: Developer Setup prod: url: http://foo.bar.com name: My Cool App与其等价的properties配置如下。 environments.dev.url=http://dev.bar.comenvironments.dev.name=Developer Setupenvironments.prod.url=http://foo.bar.comenvironments.prod.name=My Cool App通过YAML的配置方式,我们可以看到配置信息利用阶梯化缩进的方式,其结构显得更为清晰易读,同时配置内容的字符量也得到显著的减少。除此之外,YAML还可以在一个单个文件中通过使用spring.profiles属性来定义多个不同的环境配置。例如下面的内容,在指定为test环境时,server.port将使用8882端口;而在prod环境,server.port将使用8883端口;如果没有指定环境,server.port将使用8881端口。 server: port: 8881---spring: profiles: testserver: port: 8882---spring: profiles: prodserver: port: 8883注意:YAML目前还有一些不足,它无法通过@PropertySource注解来加载配置。但是,YAML加载属性到内存中保存的时候是有序的,所以当配置文件中的信息需要具备顺序含义时,YAML的配置方式比起properties配置文件更有优势。 ...

June 13, 2019 · 2 min · jiezi

spring-statemachine的企业可用级开发指南2先跑起来

上一篇说了很多废话,这一篇就不唠叨,先跑起来 1、来个spring boot去start.spring.io新建一个springboot的项目,虽然我对spirngboot也有不少的牢骚,但作为demo的开始,还是一个很好用的脚手架,记得选spring statemachine,为了方便,我还选了web 模块 点击generate project 下载到本地,用IDE打开,顺便说一句,我用的是java IDE界逼格很低的eclipse,因为我一直用它,还不要钱。 2、跑起来一个废物例子 在本地打开后我们首先看pom.xml文件,里面和我们相关的有这几段<properties> <spring-statemachine.version>2.0.1.RELEASE</spring-statemachine.version></properties><dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-starter</artifactId></dependency><dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-bom</artifactId> <version>${spring-statemachine.version}</version> <type>pom</type> <scope>import</scope></dependency> 现在就可以在springboot里面用statemachine了,然后我们就开始想办法跑起来。 先来一个StateMachineConfig,它的主要作用就告诉状态机的初始状态应该啥样,然后把整个状态流程都用代码配置出来。@Configuration是springboot的注解,表示这个类负责配置,@EnableStateMachine表示这个配置类是用在spring statemachine上面的。package com.skyblue.statemachine.config;import java.util.EnumSet;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.annotation.Configuration;import org.springframework.statemachine.config.EnableStateMachine;import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;@Configuration@EnableStateMachinepublic class StateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception { states.withStates().initial(OrderStates.UNPAID).states(EnumSet.allOf(OrderStates.class)); } @Override public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception { transitions.withExternal().source(OrderStates.UNPAID).target(OrderStates.WAITING_FOR_RECEIVE).event(OrderEvents.PAY).and() .withExternal().source(OrderStates.WAITING_FOR_RECEIVE).target(OrderStates.DONE).event(OrderEvents.RECEIVE); }}它配套需要OrderStates和OrderEvents,代码如下: ...

June 12, 2019 · 2 min · jiezi

另一种缓存Spring-Boot-整合-Ehcache

用惯了 Redis ,很多人已经忘记了还有另一个缓存方案 Ehcache ,是的,在 Redis 一统江湖的时代,Ehcache 渐渐有点没落了,不过,我们还是有必要了解下 Ehcache ,在有的场景下,我们还是会用到 Ehcache。 <!--more--> 今天松哥就来和大家聊聊 Spring Boot 中使用 Ehcache 的情况。相信看完本文,大家对于[Spring Boot 操作 Redis,三种方案全解析!]一文中的第二种方案会有更加深刻的理解。 Ehcache 也是 Java 领域比较优秀的缓存方案之一,Ehcache 这个缓存的名字很有意思,正着念反着念,都是 Ehcache,Spring Boot 中对此也提供了很好的支持,这个支持主要是通过 Spring Cache 来实现的。 Spring Cache 可以整合 Redis,当然也可以整合 Ehcache,两种缓存方案的整合还是比较相似,主要是配置的差异,具体的用法是一模一样的,就类似于 JDBC 和 数据库驱动的关系一样。前面配置完成后,后面具体使用的 API 都是一样的。 和 Spring Cache + Redis 相比,Spring Cache + Ehcache 主要是配置有所差异,具体的用法是一模一样的。我们来看下使用步骤。 项目创建首先,来创建一个 Spring Boot 项目,引入 Cache 依赖: 工程创建完成后,引入 Ehcache 的依赖,Ehcache 目前有两个版本: 这里采用第二个,在 pom.xml 文件中,引入 Ehcache 依赖: ...

June 12, 2019 · 2 min · jiezi

SpringBoot系列WebMvcConfigurer介绍

在上篇文章中,我们遇到了接口WebMvcConfigurer。今天就来大概看一下里面的方法都有什么吧。为什么要使用WebMvcConfigurer?WebMvcConfigurer是一个接口,提供很多自定义的拦截器,例如跨域设置、类型转化器等等。可以说此接口为开发者提前想到了很多拦截层面的需求,方便开发者自由选择使用。由于Spring5.0废弃了WebMvcConfigurerAdapter,所以WebMvcConfigurer继承了WebMvcConfigurerAdapter大部分内容。 WebMvcConfigurer方法介绍由于内容太多,只展示3个关键的接口,用的比较少的,只是阐述下含义,不再详解,用的更少的,就不看了,毕竟十多个方法呢...1.configurePathMatch(PathMatchConfigurer configurer)这个用到的比较少,这个是和访问路径有关的。举个例子,比如说PathMatchConfigurer 有个配置是setUseTrailingSlashMatch(),如果设置为true的话(默认为true),后面加个斜杠并不影响路径访问,例如“/user”等同于“/user/"。我们在开发中很少在访问路径上搞事情,所以这个方法如果有需要的请自行研究吧。 2.configureContentNegotiation(ContentNegotiationConfigurer configurer)这个东西直译叫做内容协商机制,主要是方便一个请求路径返回多个数据格式。ContentNegotiationConfigurer这个配置里面你会看到MediaType,里面有众多的格式。此方法不在多赘述。 3.configureAsyncSupport(AsyncSupportConfigurer configurer)顾名思义,这是处理异步请求的。只能设置两个值,一个超时时间(毫秒,Tomcat下默认是10000毫秒,即10秒),还有一个是AsyncTaskExecutor,异步任务执行器。 4.configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)这个接口可以实现静态文件可以像Servlet一样被访问。 5.addFormatters(FormatterRegistry registry)增加转化器或者格式化器。这边不仅可以把时间转化成你需要时区或者样式。还可以自定义转化器和你数据库做交互,比如传进来userId,经过转化可以拿到user对象。 6.addInterceptors(InterceptorRegistry registry)盼望着,盼望着,你一个常用的方法来了。这个方法可以自定义写拦截器,并指定拦截路径。来,咱们写一个拦截器。 public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle,ok,假设给你一个true,运行去吧"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle,ok,看看我什么时候运行的。"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion,ok,看完不点个赞再走吗?"); }}然后配置一下: ...

June 11, 2019 · 1 min · jiezi

Online开发初体验JeecgBoot-在线配置图表

Online开发——初体验(在线配置图表) 01 JSON数据格式演示.gif02 SQL-统计用户登录数量.gif03 图表合并展示.gif04 展示模板风格演示.gif

June 11, 2019 · 1 min · jiezi

Spring-Boot-配置文件-bootstrap-vs-application-到底有什么区别

用过 Spring Boot 的都知道在 Spring Boot 中有以下两种配置文件 bootstrap (.yml 或者 .properties)application (.yml 或者 .properties)为什么会有这两种配置文件呢?大家都清楚它们的区别和具体使用场景吗? bootstrap/ application 的区别特意去翻了下 Spring Boot 的官方文档,没有找到关于这两种文件的具体定义,然后再翻了下 Spring Cloud 的官方文档找到了它们的区别。 https://cloud.spring.io/sprin...认真阅读了下文档,原文大概意思是这样。 Spring Cloud 构建于 Spring Boot 之上,在 Spring Boot 中有两种上下文,一种是 bootstrap, 另外一种是 application, bootstrap 是应用程序的父上下文,也就是说 bootstrap 加载优先于 applicaton。bootstrap 主要用于从额外的资源来加载配置信息,还可以在本地外部配置文件中解密属性。这两个上下文共用一个环境,它是任何Spring应用程序的外部属性的来源。bootstrap 里面的属性会优先加载,它们默认也不能被本地相同配置覆盖。因此,对比 application 配置文件,bootstrap 配置文件具有以下几个特性。 boostrap 由父 ApplicationContext 加载,比 applicaton 优先加载boostrap 里面的属性不能被覆盖bootstrap/ application 的应用场景application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。 bootstrap 配置文件有以下几个应用场景。 使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;一些固定的不能被覆盖的属性一些加密/解密的场景;以下这个截图是一个国外网友问了一个 Spring Cloud 工程师得到的回答。 ...

June 11, 2019 · 1 min · jiezi

Spring-Boot-整合-Shiro-两种方式全总结

在 Spring Boot 中做权限管理,一般来说,主流的方案是 Spring Security ,但是,仅仅从技术角度来说,也可以使用 Shiro。 <!--more--> 今天松哥就来和大家聊聊 Spring Boot 整合 Shiro 的话题! 一般来说,Spring Security 和 Shiro 的比较如下: Spring Security 是一个重量级的安全管理框架;Shiro 则是一个轻量级的安全管理框架Spring Security 概念复杂,配置繁琐;Shiro 概念简单、配置简单Spring Security 功能强大;Shiro 功能简单...虽然 Shiro 功能简单,但是也能满足大部分的业务场景。所以在传统的 SSM 项目中,一般来说,可以整合 Shiro。 在 Spring Boot 中,由于 Spring Boot 官方提供了大量的非常方便的开箱即用的 Starter ,当然也提供了 Spring Security 的 Starter ,使得在 Spring Boot 中使用 Spring Security 变得更加容易,甚至只需要添加一个依赖就可以保护所有的接口,所以,如果是 Spring Boot 项目,一般选择 Spring Security 。 这只是一个建议的组合,单纯从技术上来说,无论怎么组合,都是没有问题的。 在 Spring Boot 中整合 Shiro ,有两种不同的方案: ...

June 11, 2019 · 2 min · jiezi

跨域问题与SpringBoot解决方案

什么是跨域?定义:浏览器从一个域名的网页取请求另一个域名下的东西。通俗点说,浏览器直接从A域访问B域中的资源是不被允许的,如果想要访问,就需要进行一步操作,这操作就叫“跨域”。例如,你从百度的页面,点击一个按钮,请求了新浪的一个接口,这就进行了跨域。不单单只有域名不同就是跨域,域名、端口、协议其一不同就是不同的域,请求资源需要跨域。 为什么要跨域?为什么需要跨域,而不直接访问其他域下的资源呢?这是浏览器的限制,专业点说叫浏览器同源策略限制。主要是为了安全考虑。现在的安全框架,一般请求的时候header中不是都存个token嘛,你要是用这个token去正常访问A域下的东西是没问题的,然后又去访问了B域,结果阴差阳错的还带着这个token,那么B域,或者说B网站是不是就可以拿着你的token去A域下做点什么呢,这就相当危险了。所以浏览器加上了所谓的浏览器同源策略限制。但是为了我们真的需要从A域下访问B的资源(正常访问),就需要用到跨域,跨越这个限制了。 SpringBoot解决跨域问题SpringBoot可以基于Cors解决跨域问题,Cors是一种机制,告诉我们的后台,哪边(origin )来的请求可以访问服务器的数据。全局配置配置实例如下: @Configurationpublic class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowCredentials(true) .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .maxAge(3600); }}首先实现了WebMvcConfigurer 接口,WebMvcConfigurer 这个接口十分强大,里面还有很多可用的方法,在SpringBoot2.0里面可以解决WebMvcConfigurerAdapter曾经的部分任务。其中一个方法就是addCorsMappings(),是专门为开发人员解决跨域而诞生的接口。其中构造参数为CorsRegistry。 看下CorsRegistry源码,十分简单: public class CorsRegistry { private final List<CorsRegistration> registrations = new ArrayList<>(); public CorsRegistration addMapping(String pathPattern) { CorsRegistration registration = new CorsRegistration(pathPattern); this.registrations.add(registration); return registration; } protected Map<String, CorsConfiguration> getCorsConfigurations() { Map<String, CorsConfiguration> configs = new LinkedHashMap<>(this.registrations.size()); for (CorsRegistration registration : this.registrations) { configs.put(registration.getPathPattern(), registration.getCorsConfiguration()); } return configs; }} 可以看出CorsRegistry 有个属性registrations ,按道理可以根据不同的项目路径进行定制访问行为,但是我们示例直接将pathPattern 设置为 /**,也就是说已覆盖项目所有路径,只需要创建一个CorsRegistration就好。getCorsConfigurations(),这个方法是获取所有CorsConfiguration的Map集合,key值为传入路径pathPattern。 ...

June 10, 2019 · 2 min · jiezi

WebSocket不再轮询

1.前言本文先讲解WebSocket的应用场景和特点,然后通过前后端示例代码讲解,展示在实际的开发中的应用。1.1. 应用场景WebSocket是一种在单个TCP连接上进行全双工通信的协议, 是为了满足基于 Web 的日益增长的实时通信需求而产生的。我们平时接触的大多数是HTTP的接口,但是在有些业务场景中满足不了我们的需求,这时候就需要用到WebSocket。简单举两个例子: (1) 页面地图上要实时显示在线人员坐标:传统基于HTTP接口的处理方式是轮询,每次轮询更新最新的坐标信息。 (2)手机的付款码页面,在外界设备扫描付款码支付成功后,手机付款码页面提示“支付成功”并自动关闭:传统方式还是轮询,付款码页面一直调用接口,直到从服务器获取成功支付的状态后,手机提示“支付成功”并关闭付款码页面。 HTTP 协议有一个缺陷:通信只能由客户端发起。这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。但这种方式即浪费带宽(HTTP HEAD 是比较大的),又消耗服务器 CPU 占用(没有信息也要接受请求)。 在WebSocket API尚未被众多浏览器实现和发布的时期,开发者在开发需要接收来自服务器的实时通知应用程序时,不得不求助于一些“hacks”来模拟实时连接以实现实时通信,最流行的一种方式是长轮询 。 长轮询主要是发出一个HTTP请求到服务器,然后保持连接打开以允许服务器在稍后的时间响应(由服务器确定)。为了这个连接有效地工作,许多技术需要被用于确保消息不错过,如需要在服务器端缓存和记录多个的连接信息(每个客户)。虽然长轮询是可以解决这一问题的,但它会耗费更多的资源,如CPU、内存和带宽等,要想很好的解决实时通信问题就需要设计和发布一种新的协议 1.2. WebSocket定义WebSocket是一种协议,是一种与HTTP 同等的网络协议,两者都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。 相比于传统HTTP 的每次“请求-应答”都要client 与 server 建立连接的模式,WebSocket 是一种长连接的模式。就是一旦WebSocket 连接建立后,除非client 或者 server 中有一端主动断开连接,否则每次数据传输之前都不需要HTTP 那样请求数据。 WebSocket 对象提供了一组 API,用于创建和管理 WebSocket 连接,以及通过连接发送和接收数据。浏览器提供的WebSocket API很简洁,调用示例如下:HTTP、WebSocket 等应用层协议,都是基于 TCP 协议来传输数据的。我们可以把这些高级协议理解成对 TCP 的封装。既然大家都使用 TCP 协议,那么大家的连接和断开,都要遵循 TCP 协议中的三次握手和四次握手 ,只是在连接之后发送的内容不同,或者是断开的时间不同。对于 WebSocket 来说,它必须依赖 HTTP 协议进行一次握手 ,握手成功后,数据就直接从 TCP 通道传输,与 HTTP 无关了。 ...

June 10, 2019 · 1 min · jiezi

Spring-Boot自动装配详解

在《Spring Boot Hello World》中介绍了一个简单的spring boot例子,体验了spring boot中的诸多特性,其中的自动配置特性极大的简化了程序开发中的工作(不用写一行XML)。本文我们就来看一下spring boot是如何做到自动配置的。首先阐明,spring boot的自动配置是基于spring framework提供的特性实现的,所以在本文中,我们先介绍spring framework的相关特性,在了解了这些基础知识后,我们再来看spring boot的自动配置是如何实现的。 基于Java代码对Spring进行配置在以往使用spring framework进行程序开发时,相信大家也只是使用XML搭配注解的方式对spring容器进行配置,例如在XML文件中使用<context:component-scan base-package="**"/>指定spring需要扫描package的根路径。除了使用XML对spring进行配置,还可以使用Java代码执行完全相同的配置。下面我们详细看一下如何使用Java代码对spring容器进行配置,详细内容可参考这里。使用Java代码进行spring配置,有两个核心注解@Configuration和@Bean: @Configurationpublic class AppConfig { @Bean public SampleService sampleService() { return new SampleServiceImpl(); }}@Bean注解用于修饰方法,方法的返回值会作为一个bean装载到spring容器中。bean的id就是方法的名字。@Configuration注解用于修饰一个类,它表明这个类的作用是用来对spring容器进行配置的。上面的Java代码相当于下面的XML配置: <beans> <bean id="sampleService" class="com.**.SampleServiceImpl"/></beans>使用AnnotationConfigApplicationContext类构建一个spring容器,从容器中取出对应的bean的测试代码如下: public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); SampleService myService = ctx.getBean("sampleService" ,SampleService.class); myService.doService();}Java代码配置ComponentScan使用@ComponentScan注解指定需要扫描package的根路径: @Configuration@ComponentScan(basePackages = "com.**.service.impl")public class AppConfig {}上面的Java代码相当于下面的XML配置: <beans> <context:component-scan base-package="com.**.service.impl"/></beans>此外,AnnotationConfigApplicationContext类还提供了scan方法用于指定要扫描的包路径。我们可以删除AppConfig类上的@ComponentScan注解,在构造spring容器时使用下面代码: public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("com.**.service.impl"); ctx.refresh(); SampleService myService = ctx.getBean("sampleService" ,SampleService.class); myService.doService();}使用@Import组合多个配置将所有的spring配置全部放在同一个类中肯定是不合适的,这会导致那个配置类非常复杂。通常会创建多个配置类,再借助@Import将多个配置类组合成一个。@Import的功能类似于XML中的<import/>。 ...

June 9, 2019 · 2 min · jiezi

源码分析SpringBoot启动

遇到一个问题,需要从yml文件中读取数据初始化到static的类中。搜索需要实现ApplicationRunner,并在其实现类中把值读出来再set进去。于是乎就想探究一下SpringBoot启动中都干了什么。引子就像引用中说的,用到了ApplicationRunner类给静态class赋yml中的值。代码先量一下,是这样: @Data@Component@EnableConfigurationProperties(MyApplicationRunner.class)@ConfigurationProperties(prefix = "flow")public class MyApplicationRunner implements ApplicationRunner { private String name; private int age; @Override public void run(ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner...start..."); MyProperties.setAge(age); MyProperties.setName(name); System.out.println("ApplicationRunner...end..."); }}public class MyProperties { private static String name; private static int age; public static String getName() { return name; } public static void setName(String name) { MyProperties.name = name; } public static int getAge() { return age; } public static void setAge(int age) { MyProperties.age = age; }}从SpringApplication开始@SpringBootApplicationpublic class FlowApplication { public static void main(String[] args) { SpringApplication.run(FlowApplication.class, args); }}这是一个SpringBoot启动入口,整个项目环境搭建和启动都是从这里开始的。我们就从SpringApplication.run()点进去看一下,Spring Boot启动的时候都做了什么。点进去run看一下。 ...

June 8, 2019 · 3 min · jiezi

开源项目几点心得Java架构必会几大技术点

Java架构必会几大技术点 关于学习架构,必须会的几点技术 列表项目java反射技术xml文件处理properties属性文件处理线程安全机制annocation注解设计模式代理机制(aop)serlvet基础(过滤器等等)几样比较实用的技术:  - 1. 模板语言freemarker  - 2. ognl  - 3. gson json工具类   大家对于几大框架望而生畏,实际上只要明白他的原理,就会触类旁通,在这里我说说自己的几点拙见!架构QQ交流群: 39596137MVC层(Struts,Springmvc):非常简单就是采用一个servlet实现统一跳转,配置文件保存了请求、处理类、转发请求的关系(通过xml文件或者注解)操作流程:页面触发请求,框架通过读取配置文件获取到处理类,然后通过反射实例化该类,进行对应的业务处理(现在流行的零配置,更是简化了mvc层的编写)持久层(Mybatis/Hibernate):     mybatis其实就是sql+mybatis逻辑标签     逻辑标签的理解如何大家用过模板语言freemarer或者velocity应该会很容易理解,就是自定义的规则标签     Hibernate略显高级,也很简单,他是采用注解或者配置的方式将实体字段和表字段进行一个配置,在启动的时候动态生成sql; 大家可以看下开源项目minidao,就会知道持久层原来写很简单;所以架构可以简单理解为:     配置+反射+设计模式+脚本+AOP     配置常用做法:       1.xml文件       2.annocation注解       3.properties属性文件       4.零配置思想 脚本处理方法:      可以自己写,也可以采用模板语言freemarker等等

June 8, 2019 · 1 min · jiezi

JeecgBoot-极简部署方案

基于 spring boot 特性 1、首先修改配置,去掉项目名 jeecg-boot ant-design-jeecg-vue/src/utils/request.jsant-design-jeecg-vue/public/index.html 2、修改路由History 模式为“hash” src/router/index.js 然后 ant-design-jeecg-vue 运行 build npm run build3、然后将编译之后dist下的文件复制到 jeecg-boot-module-system 项目的 /src/main/resources/static 目录下。4、修改springboot项目的启动名字,去掉项目名 jeecg-boot jeecg-boot-module-system/src/main/resources/application-dev.yml 5、重新启动项目,访问 http://localhost:8080/ 就可以看到效果

June 8, 2019 · 1 min · jiezi

JAVA前后端分离开发环境搭建详细教程JeecgBoot快速开发平台

目录索引: 后端开发工具前端开发工具Nodejs镜像WebStorm入门配置 JeecgBoot采用前后端分离的架构,官方推荐开发工具 前端开发: Webstrom 或者 IDEA 后端开发: Eclipse安装lombok插件 或者 IDEA 开发工具下载:https://pan.baidu.com/s/1tZmFuViGz5IwHhzmA-FN6A 提取码:frya 后端开发工具序号工具参考1eclipse安装lombok插件https://blog.csdn.net/qq_2564...2Eclipse自定义皮肤主题https://blog.csdn.net/StillOn...3Eclipse常用快捷键https://blog.csdn.net/zhangda...前端开发工具序号工具描述参考1Nodejs/Npm安装JavaScript运行环境,此处使用到它的包管理器npmhttp://www.jianshu.com/p/03a7...2Yarn安装下载包工具https://yarnpkg.com/zh-Hans/d...3WebStorm安装与使用WEB前端开发工具https://blog.csdn.net/u011781...配置Nodejs镜像npm config set registry https://registry.npm.taobao.org --globalnpm config set disturl https://npm.taobao.org/dist --globalyarn config set registry https://registry.npm.taobao.org --globalyarn config set disturl https://npm.taobao.org/dist --globalWebStorm-2018.1.3 开发工具入门配置序号标题链接1WebStorm安装与使用https://blog.csdn.net/u011781...2webstorm 2018 激活破解https://blog.csdn.net/q358591...3修改webstorm主题https://blog.csdn.net/master_...4Webstorm切换快捷键风格(Webstorm快捷键与eclipse对比介绍)https://blog.csdn.net/gsying1...5WebStorm SVN用法https://blog.csdn.net/hysh_ke...6‘svn’不是内部或外部命令问题解决https://blog.csdn.net/mitea90...7设置webstorm的vue新建文件模板(后面篇章)https://blog.csdn.net/diligen...8WebStorm卡顿拉取svn慢解决https://blog.csdn.net/WYA1993...前端Webstorm开发界面:后端Eclipse开发界面:

June 8, 2019 · 1 min · jiezi

JeecgBoot-201-版本发布企业级快速开发平台

Jeecg-Boot 2.0.1 版本发布,前后端分离快速开发平台Jeecg-Boot项目简介Jeecg-boot 是一款基于代码生成器的快速开发平台!采用前后端分离技术:SpringBoot,Mybatis,Shiro,JWT,Vue & AntDesign。提供强大的代码生成器, 前端页面和后台代码一键生成,不需要写任何代码,保持jeecg一贯的强大,绝对是全栈开发者福音!!JeecgBoot的宗旨是降低前后端分离的开发成本,提高UI能力的同时提高开发效率,追求更高的能力,No代码概念,一系列智能化在线开发。源码下载源码下载:https://github.com/zhangdaisc...在线文档:http://jeecg-boot.mydoc.io在线演示:http://boot.jeecg.org入门视频:http://t.cn/E9mFjet常见问题:http://t.cn/EITSuH8升级日志本版本主要录制全新教学视频和修复2.0.0版本的一系列bug,提供稳定版本供大家使用录制新版入门教程视频用户列表无编辑按钮,编辑提示无权限非admin用户,角色授权、部门、用户等操作提示无权限提示 Parameter useId not found (SysAnnouncementMapper的userid找不到)Type definition error: [simple type, class org.jeecg.modules.system.model.SysUserDepartsVO]无构造器问题oracle下部门数据列表是空问题系统公告提示资源找不到问题类别统计报表菜单删除,此功能是测试未完全实现部门管理怎么添加子部门: 选中部门,鼠标右键,添加子部门或删除子部门老菜单访问404,因为V2.0版本,菜单增加了一个类型“是否路由”,请设置“是”项目打可执行jar包,缺少maven plugin插件的问题添加新菜单访问404问题解决,改造程序,默认设置菜单路由类型默认值“是”系统展示

June 7, 2019 · 1 min · jiezi

JeecgBoot-20-入门视频教程

视频大纲 视频地址: https://pan.baidu.com/s/1Il0T... 提取码:hok5 源码下载: https://github.com/zhangdaisc... (喜欢请Star)

June 6, 2019 · 1 min · jiezi

聊聊Spring-MVC中-handlerMapping和handlerAdapter设计思想

先上一段Spring MVC核心类DispatcherServlet中最重要的方法doDispatch源码 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 根据当前请求获取对应的处理器映射器 mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 根据handler类型获取对应的处理器适配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }}关注代码中打中文注释的两个地方,一个获取对应handler的处理器映射器,一个获取对应handler的处理器适配器。那为什么需要这两个东西,我们直接在handler中写映射逻辑,直接通过handler来执行处理器方法难道不行吗?答案是否定的,但Spring为什么要这样做?有以下几个好处 ...

June 6, 2019 · 2 min · jiezi

基于WebSocket的web端IM即时通讯应用的开发

基于WebSocket的web端IM即时通讯应用的开发功能列表:1、Web端的IM应用2、支持上线、下线、实时在线提醒3、单聊、群聊的建立4、普通文字、表情、图片的传输(子定义富文本)5、单人的顶级提醒,多对话的窗口的提醒6、调用图灵机器人的自动回复演示核心技术列表1、websocket、sockjs、stomp2、前端展示涉及的jquery、vue、elementUI、jquerybase64js3、后端springboot、jsoup、spring-security、spring-websocket成果展示:技术实现说明:Websocket部分 web端的IM应用,要想实现两个客户端的通信,必然要通过服务器进行信息的转发。例如A要和B通信,则应该是A先把信息发送给IM应用服务器,服务器根据A信息中携带的接收者将它再转发给B,同样B到A也是这种模式。而要实现web端的实时通讯,websocket也是其中最好的方式,其他的协议如长轮询、短轮询、iframe数据、htmlfile等。 在实际开发中,我们通常使用的是一些别人写好的实时通讯的库,比如socket.io、sockjs(我们本次使用了他,类似jquery,对其他即时通讯技术做了封装),他们的原理就是将上面(还有一些其他的如基于Flash的push)的一些技术进行了在客户端和服务端的封装,然后给开发者一个统一调用的接口。这个接口在支持websocket的环境下使用websocket,在不支持它的时候启用上面所讲的一些hack技术。 WebSocket是HTML5的一种新通信协议(ws协议),是一个消息架构,不强制使用任何特定的消息协议,它依赖于应用层解释消息的含义;与处在应用层的HTTP不同,WebSocket处在TCP上非常薄的一层,会将字节流转换为文本/二进制消息,因此,对于实际应用来说,WebSocket的通信形式层级过低,因此,可以在 WebSocket 之上使用 STOMP协议,来为浏览器 和 server间的 通信增加适当的消息语义。 STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的简单文本协议。 同 HTTP 在 TCP 套接字上添加请求-响应模型层一样,STOMP 在 WebSocket 之上提供了一个基于帧的线路格式层,用来定义消息语义; STOMP 源码http://cdn.bootcss.com/stomp.js/2.3.3/stomp.js,有兴趣的可以看一下能大致了解其原理和用法。本例程序核心代码: <!--TO 创建socket连接 并订阅相关频道-->var socket = new SockJS('/im-websocket');stompClient = Stomp.over(socket);//设置stomp 控制台日志为不输出stompClient.debug=null;stompClient.connect({}, function (frame) { // 相当于连接 ws://localhost:8080/gs-guide-websocket/041/hk5tax0r/websocket hk5tax0r就是sessionid console.log("正在连接",socket._transport.url); //订阅通用私聊频道 群组也通过这里实现 stompClient.subscribe('/user/topic/private', function (greeting) { } ); //订阅用户上线下线的公共频道 stompClient.subscribe('/topic/userlist', function (greeting) { });},function errorCallBack (error) { // 连接失败时(服务器响应 ERROR 帧)的回调方法 });数据发送如下://第一个参数对应controller的 @MessageMapping注解 /app为后台定义的通用前缀//第三个参数为内容字符串stompClient.send("/app/private", {}, JSON.stringify(message));//发送服务器 ...

June 6, 2019 · 2 min · jiezi

编码-技术图谱-一份属于Java开发者的思维导图

一份Java开发者的技术图谱 , 近期主要侧重点在 Java | Docker | 并发与分布式.

June 6, 2019 · 1 min · jiezi

Spring-Boot-中-10-行代码构建-RESTful-风格应用

RESTful ,到现在相信已经没人不知道这个东西了吧!关于 RESTful 的概念,我这里就不做过多介绍了,传统的 Struts 对 RESTful 支持不够友好 ,但是 SpringMVC 对于 RESTful 提供了很好的支持,常见的相关注解有: <!--more--> @RestController@GetMapping@PutMapping@PostMapping@DeleteMapping@ResponseBody...这些注解都是和 RESTful 相关的,在移动互联网中,RESTful 得到了非常广泛的使用。RESTful 这个概念提出来很早,但是以前没有移动互联网时,我们做的大部分应用都是前后端不分的,在这种架构的应用中,数据基本上都是在后端渲染好返回给前端展示的,此时 RESTful 在 Web 应用中基本就没用武之地,移动互联网的兴起,让我们一套后台对应多个前端项目,因此前后端分离,RESTful 顺利走上前台。 Spring Boot 继承自 Spring + SpringMVC, SpringMVC 中对于 RESTful 支持的特性在 Spring Boot 中全盘接收,同时,结合 Jpa 和 自动化配置,对于 RESTful 还提供了更多的支持,使得开发者几乎不需要写代码(很少几行),就能快速实现一个 RESTful 风格的增删改查。 接下来,松哥通过一个简单的案例,来向大家展示 Spring Boot 对于 RESTful 的支持。 实战创建工程首先创建一个 Spring Boot 工程,引入 Web 、 Jpa 、 MySQL 、Rest Repositories 依赖: 创建完成后,还需要锁定 MySQL 驱动的版本以及加入 Druid 数据库连接池,完整依赖如下: ...

June 6, 2019 · 2 min · jiezi

springboot整合solr8一

环境:ubuntu18 + springboot2.0 + solr-8.0.0 第一部分 安装solr先去官网下载solr:Index of /apache/lucene/solr/8.0.0我这里下载的是zip的包,下载之后unzip命令解压得到下列文件: 进入bin目录,执行sudo ./solr -start 命令,这里可能遇到的错误有两个: 会提示找不到JAVA_HOME,解决方法是在solr中加入 export JAVA_HOME='你的JDK路径'即可;root启动需要添加-force参数,错误信息如下: 好吧,再次运行sudo ./solr start -force,运行成功。 solr的默认端口是8983,打开浏览器输入localhost:8983,可以看到solr已经可以访问了~ 第二部分 创建核心(core),并导入mysql数据选择左侧CoreAdmin,再选择Add Core,输入核心的名称和实例路径,后边三个默认就好。 点击Add Core提交信息,这里我遇到了一个错误信息,如下: 意思是在核心目录下找不到相应的配置文件,解决办法:将下图的conf文件夹cope到新创建的core目录下 /usr/local/solr-8.0.0/server/solr/new_core2。 重新点击Add Core,创建成功~ 接下来导入MySQL中的数据,我们会用到Solr自带的DIH(DataImportHandler)去进行数据导入。这一步需要导入三个jar包(solr-dataimporthandler-8.0.0.jar、olr-dataimporthandler-extras-8.0.0.jar和mysql-connector-java-8.0.15.jar),编辑三个文件(db-data-confg.xml 、solrconfig.xml以及managed-schema)。 导入jar包进入到/usr/local/solr-8.0.0/dist目录,将solr-dataimporthandler-8.0.0.jar和solr-dataimporthandler-extras-8.0.0.jar复制到/usr/local/solr-8.0.0/server/solr-webapp/webapp/WEB-INF/lib,再去mvn仓库下载一个mysql-connector-java的包,也复制到这里。 编辑配置文件首先需要我们进入到/usr/local/solr-8.0.0/example/example-DIH/solr/db/conf 目录,拷贝db-data-config.xml 到我们刚才复制的conf文件夹内/usr/local/solr-8.0.0/server/solr/new_core2/conf,可以随意修改一个文件名,也可以用他默认的,如下,我改成了mysql-db.xml: 编辑该文件vim mysql-db.xml,将原本的内容修改成我们自己的需求:修改dateSource: <dataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/dbname" user="root" password="" />加入entity: 接着退出,编辑另一个文件solrconfig.xml,添加如下信息: <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">mysql-db.xml</str> </lst> </requestHandler>mysql-db.xml就是刚才copy的db-data-config.xml的文件名。 保存退出,编辑managed-schema,加入<!-- custom fields--><field name="title" type="string" indexed="true" stored="true"/><field name="content" type="string" indexed="true" stored="true"/> <field name="labels" type="string" indexed="true" stored="true"/> 保存 --> 退出 --> 重启solr, sudo ./solr restart -force 再次打开localhost:8983管理页面,选择新创建的核心 --> 选择 DateImport --> 选择 full-import 全量导入 --> 提交 --> 点击Refresh刷新,可以看到导入成功的提示: ...

June 4, 2019 · 1 min · jiezi

Spring-Boot-一个依赖搞定-session-共享没有比这更简单的方案了

有的人可能会觉得题目有点夸张,其实不夸张,题目没有使用任何修辞手法!认真读完本文,你就知道松哥说的是对的了! <!--more--> 在传统的单服务架构中,一般来说,只有一个服务器,那么不存在 Session 共享问题,但是在分布式/集群项目中,Session 共享则是一个必须面对的问题,先看一个简单的架构图: 在这样的架构中,会出现一些单服务中不存在的问题,例如客户端发起一个请求,这个请求到达 Nginx 上之后,被 Nginx 转发到 Tomcat A 上,然后在 Tomcat A 上往 session 中保存了一份数据,下次又来一个请求,这个请求被转发到 Tomcat B 上,此时再去 Session 中获取数据,发现没有之前的数据。对于这一类问题的解决,思路很简单,就是将各个服务之间需要共享的数据,保存到一个公共的地方(主流方案就是 Redis): 当所有 Tomcat 需要往 Session 中写数据时,都往 Redis 中写,当所有 Tomcat 需要读数据时,都从 Redis 中读。这样,不同的服务就可以使用相同的 Session 数据了。 这样的方案,可以由开发者手动实现,即手动往 Redis 中存储数据,手动从 Redis 中读取数据,相当于使用一些 Redis 客户端工具来实现这样的功能,毫无疑问,手动实现工作量还是蛮大的。 一个简化的方案就是使用 Spring Session 来实现这一功能,Spring Session 就是使用 Spring 中的代理过滤器,将所有的 Session 操作拦截下来,自动的将数据 同步到 Redis 中,或者自动的从 Redis 中读取数据。 对于开发者来说,所有关于 Session 同步的操作都是透明的,开发者使用 Spring Session,一旦配置完成后,具体的用法就像使用一个普通的 Session 一样。 ...

June 4, 2019 · 2 min · jiezi

Resolved-Springboot-Maven-configration-problem

以maven的形式创建Springboot会遇到pom.xml第一行报 maven Configuration problem 的问题, 而且没有任何的错误description. 网上有很多的解决办法, 比如L:https://blog.csdn.net/wangyj1...但它并没有解决我的问题, 一番实验之后, 发现是在<parent>标签中的<version>2.1.5.RELEASE</version>的问题, 将2.1.5 改成2.1.4即可解决问题. maven中的parent标签声明了子项目继承的dependency, 并且是全继承, 即让子项目既继承parent中的package, 又继承parent中package的版本. 与之对应的是DependencyManagement, DependencyManagement只控制子项目的版本, 如果用Dependencymanagement取代Parent的话, 则还需要写Dependency, 不需要写版本. 在这个case里, 将Parent的2.1.5版本改成2.1.4即可使用, 说明2.1.5的Parent中声明的Dependency有缺失, 细查之后修复即可

June 3, 2019 · 1 min · jiezi

SpringBoot操作Redis三种方案全解析

在 Redis 出现之前,我们的缓存框架各种各样,有了 Redis ,缓存方案基本上都统一了,关于 Redis,松哥之前有一个系列教程,尚不了解 Redis 的小伙伴可以参考这个教程: Redis 教程合集使用 Java 操作 Redis 的方案很多,Jedis 是目前较为流行的一种方案,除了 Jedis ,还有很多其他解决方案,如下: 除了这些方案之外,还有一个使用也相当多的方案,就是 Spring Data Redis。 在传统的 SSM 中,需要开发者自己来配置 Spring Data Redis ,这个配置比较繁琐,主要配置 3 个东西:连接池、连接器信息以及 key 和 value 的序列化方案。 在 Spring Boot 中,默认集成的 Redis 就是 Spring Data Redis,默认底层的连接池使用了 lettuce ,开发者可以自行修改为自己的熟悉的,例如 Jedis。 Spring Data Redis 针对 Redis 提供了非常方便的操作模板 RedisTemplate 。这是 Spring Data 擅长的事情,那么接下来我们就来看看 Spring Boot 中 Spring Data Redis 的具体用法。 方案一:Spring Data Redis创建工程创建工程,引入 Redis 依赖: ...

June 3, 2019 · 2 min · jiezi

spring-boot-应用程序在-linux-下的-部署及运行

SpringBoot 应用程序在 linux 下的部署及运行将 spring boot 应用程序打包成 jar 包我们使用 spring boot 的 maven 插件来构建管理整个应用程序,使用 mvn package 将应用程序打包成一个 jar 包将 该 jar 包上传到 服务器上传到服务器大致有两种方式(常见的):1)通过 xftp 这种方式;2)本文将要介绍的这种,我不太建议使用 xftp,因为它太常见了,不新奇,说说第二种方式吧:首先我们在 linux 服务器上,下载 lrzsz 插件,命令为:yum -y install lrzsz,然后上传文件就输入命令:rz -y ,-y 表示强制覆盖原有文件(建议使用),rz 表示上传,当然了,sz 就表示下载喽。之后就会打开一个 windows 的文件资源管理器窗口,你选择目标 jar 包即可实现上传(前提,选择好你的上传目录)运行上传到服务器的指定位置了,接下来就是如何运行了!我们都知道,java 程序在你本地运行时就是选择好入口 main,然后运行即可。但是在 linux 上就不是那么简单了。大致呢有两种:一种是直接手动启动;一种是通过写一个脚本文件来启动。直接启动: java -jar 目标.jar >> catalina.out 2>&1 &,什么意思呢?就是将 tomcat(spring-boot-starter-web 自带 tomcat) 的启动内容 标准错误流重定向到标准输出流(2 >&1),并且以在后台运行的形式去运行(&)。脚本启动编写启动脚本 \#!/bin/bashPROJECTNAME=目标jar名称(不需要带.jar)pid=`ps -ef |grep $PROJECTNAME |grep -v "grep" |awk '{print $2}'`if [ $pid ]; then echo "$PROJECTNAME is running and pid=$pid"else echo "Start success to start $PROJECTNAME ...." nohup java -jar 目标.jar >> catalina.out 2>&1 &fi在该 .sh(脚本文件)中,使用到了命令 nohup java -jar ... nohup 就是 no hangup(不挂起),即 即使用户登出,关闭终端后,该进程还会继续运行;采用 nohup 命令后,那么就会在当前脚本所在的同级目录下生成一个 nohup.out 的文件,该文件就记录了整个应用启动过程以及之后运行中的所有日志内容(因为我们是将 2 标准错误 作为输出内容的,而标准错误默认是包括所有的输出内容+错误内容)。之后你只需要运行这个脚本即可启动应用程序啦:./start.sh,如果你想查看一下日志内容,你可以输入:vi nohup.out,或者你只想查看最后几行内容:tail -f nohup.out即可。 ...

May 31, 2019 · 1 min · jiezi

activiti-工作流关于acitiviti-和-spring-boot-集成遇到的一些问题总结

最近在调查activiti工作流的事。项目上打算做一个工作流服务,给微服务中的一些需要流程定义的服务用。所以我尝试用spring boot集成activiti。以下是我调查中遇到的一些问题和一些调查到的结果。 1. activity7 与 activity6 与 activiti cloudactivity cloud 不知道怎么使用,但是看官方解释比较适合云服务(spring cloud)便于扩展,但是activity cloud 貌似是云平台。因为不会搭建,所以就放弃了。activity7 与 activity6 相比,7 多了processruntime与taskruntime 两个类。这两个类需要与security 一起使用,官方里对于TaskRuntime API 有这样一段话: Something important to notice here, is that in order to interact with the TaskRuntime API as a user, you need to have the role: ACTIVITI_USER (Granted Authority: ROLE_ACTIVITI_USER) .因为我项目还没和spring security 集成起来,所以就只好不用这两个类了。我的做法是用activity7 的core,但是还是用 6.0使用方式。 继承方式很简单,在pom里引入就OK了 <activiti-dependencies.version>7.0.0.SR1</activiti-dependencies.version> ... ... <!--activiti starter--> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> </dependency> <!-- Activiti生成流程图 --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-image-generator</artifactId> </dependency>2. activity 数据表遇到的问题首先是基本25张表 ...

May 31, 2019 · 2 min · jiezi

是时候搞清楚-Spring-Boot-的配置文件-applicationproperties-了

在 Spring Boot 中,配置文件有两种不同的格式,一个是 properties ,另一个是 yaml 。 虽然 properties 文件比较常见,但是相对于 properties 而言,yaml 更加简洁明了,而且使用的场景也更多,很多开源项目都是使用 yaml 进行配置(例如 Hexo)。除了简洁,yaml 还有另外一个特点,就是 yaml 中的数据是有序的,properties 中的数据是无序的,在一些需要路径匹配的配置中,顺序就显得尤为重要(例如我们在 Spring Cloud Zuul 中的配置),此时我们一般采用 yaml。关于 yaml ,松哥之前写过一篇文章:Spring Boot 中的 yaml 配置简介。 本文主要来看看 properties 的问题。 位置问题首先,当我们创建一个 Spring Boot 工程时,默认 resources 目录下就有一个 application.properties 文件,可以在 application.properties 文件中进行项目配置,但是这个文件并非唯一的配置文件,在 Spring Boot 中,一共有 4 个地方可以存放 application.properties 文件。 当前项目根目录下的 config 目录下当前项目的根目录下resources 目录下的 config 目录下resources 目录下按如上顺序,四个配置文件的优先级依次降低。如下: 这四个位置是默认位置,即 Spring Boot 启动,默认会从这四个位置按顺序去查找相关属性并加载。但是,这也不是绝对的,我们也可以在项目启动时自定义配置文件位置。 例如,现在在 resources 目录下创建一个 javaboy 目录,目录中存放一个 application.properties 文件,那么正常情况下,当我们启动 Spring Boot 项目时,这个配置文件是不会被自动加载的。我们可以通过 spring.config.location 属性来手动的指定配置文件位置,指定完成后,系统就会自动去指定目录下查找 application.properties 文件。 ...

May 30, 2019 · 2 min · jiezi

SpringBoot高级篇搜索Solr之文档新增与修改使用姿势

大多涉及到数据的处理,无非CURD四种操作,对于搜索SOLR而言,基本操作也可以说就这么几种,在实际应用中,搜索条件的多样性才是重点,我们在进入复杂的搜索之前,先来看一下如何新增和修改文档 <!-- more --> I. 环境准备solr的基础环境需要准备好,如果对这一块有疑问的童鞋,可以参考下上一篇博文: 《190510-SpringBoot高级篇搜索之Solr环境搭建与简单测试》 1. 环境配置在pom文件中,设置好对应的依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> <!-- lookup parent from update --></parent><properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version></properties><build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement></build><repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository></repositories><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-solr</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency></dependencies>因为我们采用默认的solr访问姿势,所以配置文件中可以不加对应的参数,当然也可以加上 打开 application.yml 配置文件 spring: data: solr: host: http://127.0.0.1:8983/solr如果我们的solr加上了用户名密码访问条件,参数中并没有地方设置username和password,那应该怎么办? spring: data: solr: host: http://admin:admin@127.0.0.1:8983/solr如上写法,将用户名和密码写入http的连接中 2. 自动装配我们主要使用SolrTemplate来和Solr打交到,因此我们需要先注册这个bean,可以怎么办? package com.git.hui.boot.solr.config;import org.apache.solr.client.solrj.SolrClient;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.solr.core.SolrTemplate;/** * Created by @author yihui in 19:49 19/5/10. */@Configurationpublic class SearchAutoConfig { @Bean @ConditionalOnMissingBean(SolrTemplate.class) public SolrTemplate solrTemplate(SolrClient solrClient) { return new SolrTemplate(solrClient); }}上面的配置是条件注入,只有当SolrTemplate对应的bean没有被自动加载时,才会加载,为什么要怎么干? ...

May 29, 2019 · 3 min · jiezi

这一次我连-webxml-都不要了纯-Java-搭建-SSM-环境

在 Spring Boot 项目中,正常来说是不存在 XML 配置,这是因为 Spring Boot 不推荐使用 XML ,注意,并非不支持,Spring Boot 推荐开发者使用 Java 配置来搭建框架,Spring Boot 中,大量的自动化配置都是通过 Java 配置来实现的,这一套实现方案,我们也可以自己做,即自己也可以使用纯 Java 来搭建一个 SSM 环境,即在项目中,不存在任何 XML 配置,包括 web.xml 。 环境要求: 使用纯 Java 来搭建 SSM 环境,要求 Tomcat 的版本必须在 7 以上。快速体验1 创建工程创建一个普通的 Maven 工程(注意,这里可以不必创建 Web 工程),并添加 SpringMVC 的依赖,同时,这里环境的搭建需要用到 Servlet ,所以我们还需要引入 Servlet 的依赖(一定不能使用低版本的 Servlet),最终的 pom.xml 文件如下: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.6.RELEASE</version></dependency><dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope></dependency>2 添加 Spring 配置工程创建成功之后,首先添加 Spring 的配置文件,如下: @Configuration@ComponentScan(basePackages = "org.javaboy", useDefaultFilters = true, excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})public class SpringConfig {}关于这个配置,我说如下几点: ...

May 28, 2019 · 3 min · jiezi

Spring系列实战篇4你有多了解MyBatis

1. 概念在我上大学的时候,最流行的JavaEE框架是 SSH (Struts+Spring+Hibernate),现在同学们应该都在学 SSM(Spring+SpringMVC+MyBatis)了。从历史演变来看,Spring是越来越强大,而MyBatis则是顶替了Hibernate的地位。今天的“主角”就是MyBatis。 1.1. ORM的历史演变我们先聊一聊ORM(Object Relational Mapping),翻译为“对象关系映射”,就是通过实例对象的语法,完成关系型数据库的操作的技术。ORM用于实现面向对象编程语言里不同类型系统的数据之间的转换,其实是创建了一个可在编程语言里使用的"虚拟对象数据库"。 ORM 把数据库映射成对象: 数据库的表(table) --> 类(class)记录(record,行数据)--> 对象(object)字段(field)--> 对象的属性(attribute)基于传统ORM框架的产品有很多,其中就有耳熟能详的Hibernate。ORM通过配置文件,使数据库表和JavaBean类对应起来,提供简便的操作方法,增、删、改、查记录,不再拼写字符串生成sql,编程效率大大提高,同时减少程序出错机率,增强数据库的移植性,方便测试。 但是有些时候我还是喜欢原生的JDBC,因为在某些特殊的应用场景中,对于sql的应用复杂性比较高,或者需要对sql的性能进行优化,这些ORM框架就显得很笨重。Hibernate这类“全自动化”框架,对数据库结构封装的较为完整,这种一站式的解决方案未必适用于所有的业务场景。 幸运的是,不只我一个人有这种感受,很久之前大家开始关注一个叫 iBATIS 的开源项目,它相对传统ORM框架而言更加的灵活,被定义为“半自动化”的ORM框架。2010年,谷歌接管了iBATIS,MyBatis就随之诞生了。虽然2010年我都还没上大学,但很可惜,MyBatis在国内的大火的比较晚,我在校园期间都没有接触过。 1.2. 开启MyBatisMyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。 MyBatis为半自动化,需要自己书写sql语句,需要自己定义映射。增加了程序员的一些操作,但是带来了设计上的灵活。并且也是支持Hibernate的一些特性,如延迟加载,缓存和映射等,而且随之SSM架构的成熟,MyBatis肯定会被授予有越来越多新的特性。那么接下来就开始 MyBatis 的实战演练吧! 2. MyBatis 基本使用下面讲解在SpringBoot 中,使用MyBatis的基本操作。 2.1. 基础配置在SpringBoot中集成 MyBatis 的方式很简单,只需要引用 MyBatis的starter包即可,不过针对不同的数据源,需要导入所依赖的驱动jar包(如:mysql(mysql-connector-java-x.jar)/oracle(ojdbcx.jar)/sql server(sqljdbcx.jar)等) pom.xml(示例) <!--mybatis--><dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version></dependency><!--oracle jdbc--><dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>6</version></dependency><!--druid 数据源--><dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version></dependency>对于相关数据源的连接信息,需要在application.properties中配置,同样提供示例 # Oracle数据库的连接信息spring.datasource.url=jdbc:oracle:thin:@ip:port/instancespring.datasource.username=usernamespring.datasource.password=passwordspring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver#mybatis 驼峰式命名映射,可将ResultMap返回值通过驼峰式映射给pojomybatis.configuration.map-underscore-to-camel-case=true#mybatis xml文件路径mybatis.mapper-locations=classpath:mapper/*Mapper.xml#开启mybatis dao层的日志logging.level.com.df.stage.tasktimer.mapper=debug2.2. 使用MyBatis方式一:xml配置MyBatis3 之前,需要手动获取SqlSession,并通过命名空间来调用MyBatis方法,比较麻烦。而MyBatis3 就开始支持接口的方式来调用方法,这也成为当前即为普遍的用法,本文就以此为例。 ...

May 27, 2019 · 2 min · jiezi

最简单的springboot2整合redis-10行代码-完成订阅和发布

pom: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>代码: import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.MessageListener;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.listener.ChannelTopic;import org.springframework.data.redis.listener.RedisMessageListenerContainer;import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;@Configurationpublic class RedisSubListenerConfig { @Bean MessageListenerAdapter messageListener() { //abstract methods overwrite return new MessageListenerAdapter((MessageListener) (message, pattern) -> { System.out.println("Message received: " + message.toString()); }); } @Bean RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.addMessageListener(messageListener(), topic()); return container; } @Bean ChannelTopic topic() { return new ChannelTopic("messageQueue"); }}测试类: import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;/** * @date 2019-05-25 10:59 */@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTestpublic class RedisTest { @Resource private StringRedisTemplate stringRedisTemplate; @Test public void test(){ stringRedisTemplate.convertAndSend("messageQueue","hello world"); }}

May 25, 2019 · 1 min · jiezi

验证码

目标在后台写一个进行验证码验证,防止请求过快。 实现方法:在后台生产验证码并保存到session当中,将生成的验证码图片返回给前台,接收前台回传的数据时,对验证码进行验证,如果正确则进行下一步操作并清除session里存储的验证码,如果错误返回失败信息。 参考链接 配置kaptchakaptcha 是一个非常实用的验证码生成工具。有了它,你可以生成各种样式的验证码,因为它是可配置的。kaptcha工作的原理是调用 com.google.code.kaptcha.servlet.KaptchaServlet,生成一个图片导入kaptcha依赖://pom.xml<dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version></dependency>配置默认的kaptcha属性和获取: @Componentpublic class VerificationCodeConfig { @Bean public DefaultKaptcha getDefaultKaptcha() { DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); Properties properties = new Properties(); properties.setProperty("kaptcha.textproducer.char.string", "123456789");//验证码字符范围 properties.setProperty("kaptcha.border.color", "245,248,249");//图片边框颜色 properties.setProperty("kaptcha.textproducer.font.color", "black");//字体颜色 properties.setProperty("kaptcha.textproducer.char.space", "2");//文字间隔 properties.setProperty("kaptcha.image.width", "125");//图片宽度 properties.setProperty("kaptcha.image.height", "45");//图片高度 properties.setProperty("kaptcha.textproducer.char.length", "4");//长度 properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");//字体 Config config = new Config(properties); defaultKaptcha.setConfig(config); return defaultKaptcha; }}生成验证码用配置的defaultKaptcha对象生成验证码和验证码图片,将验证码写入session中,将验证码图片转化为字节流写入response中,前台就能获取到这个图片了. @AutowiredDefaultKaptcha defaultKaptcha;@AutowiredHttpSession httpSession; @Override public void createVerificationCode(HttpServletResponse httpServletResponse,HandleVerificationCode handleVerificationCode) throws IOException { byte[] captchaChallengeAsJpeg = null; ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); // 生产验证码字符串并保存到session中 String createText = defaultKaptcha.createText(); // 使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中 BufferedImage challenge = defaultKaptcha.createImage(createText); ImageIO.write(challenge, "jpg", jpegOutputStream); captchaChallengeAsJpeg = jpegOutputStream.toByteArray(); httpSession.setAttribute(VERIFICATION_CODE_SESSION_KEY, codeText); httpServletResponse.setHeader("Cache-Control", "no-store"); httpServletResponse.setHeader("Pragma", "no-cache"); httpServletResponse.setDateHeader("Expires", 0); httpServletResponse.setContentType("image/jpeg"); ServletOutputStream responseOutputStream = null; try { responseOutputStream = httpServletResponse.getOutputStream(); responseOutputStream.write(captchaChallengeAsJpeg); responseOutputStream.flush(); responseOutputStream.close(); } catch (IOException e) { httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND); } }获取验证码用img标签获取图片就行了,每一次获取的图片都不一样. ...

May 25, 2019 · 1 min · jiezi

SpringBoot-读取文件代码模版

引言Alice上线后,学生导入模版文件下载失败。 未打包是没问题的,打完包后就发生了错误。开始逐步定位问题。 原方法File file = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "static/导入学生模板.xlsx");InputStream inputStream = new FileInputStream(file);Workbook workbook = new XSSFWorkbook(inputStream);经测试,问题出在ResourceUtils.getFile这个方法上。 ResourceUtils.CLASSPATH_URL_PREFIX + "static/导入学生模板.xlsx"字符串拼接后就是classpath:static/导入学生模板.xlsx,没打包时,该方法能正常获取到该Excel文件。 ResourceUtils.getFile是获取磁盘上的文件,而打完包之后,resources目录了下的所有资源都被打进了jar包,所以不存在相应的Excel文件,获取的时候就报错了。找不到文件。 测试将打好的jar包解压,主要的东西都在BOOT-INF里。META-INF里面有pom.xml,应该是存储项目信息的。org目录内是springframework的一些class字节码。 BOOT-INF内就是本项目的核心代码与第三方库。 classes目录下就是本项目的核心代码,我们的Excel就在这里面。 lib目录下是项目中引用的第三方jar包,里面有我们常用的slf4j,spring-boot等jar包。 所以,Spring Boot的项目一般都较大,有一部分原因是把许多第三方包都打包进去了。 解决方案@Autowired一个ResourceLoader,然后调用getResource方法,加载资源。 想想也不难理解,SpringBoot启动时读取application.yml配置,Spring的源码中就是使用ResourceLoader实现的。 只不过源码中直接用的上下文对象,一个上下文对象不只是一个IOC容器,还是一个资源加载器。(上下文其实有很多的功能,可以点开Spring源码看一看,很有意思。) ResourcePatternResolver派生自ResourceLoader接口。 代码模版,以后读取文件就这样写,啥毛病没有: Resource resource = this.resourceLoader.getResource(ResourceUtils.CLASSPATH_URL_PREFIX + "static/导入学生模板.xlsx");总结千淘万漉虽辛苦,吹尽狂沙始到金。——刘禹锡《浪淘沙词九首·其九》一路走来,一路总结。最后留下的,是小本本中记录的代码模版与最佳实践。

May 24, 2019 · 1 min · jiezi

IPress-让你更流畅的书写-Spring-Boot-Vue-VueCli3-markdown

导语很久没有更新文章了, 最近参考showdoc.cc 写了一个 笔记(知识管理/博客)工具 IPress. 技术栈Spring Boot 2.x Mysql 8.x Vue 2.x Vue Router 2.x iview ui 3.x 前后端分离 通过 jwtToken 跨域认证演示地址:Ipress 部署在了码云的Pages上 项目源码获取:已上传 github 项目模块划分:IPress│├─ipress-core 用户权限登录模块 (80%)│├─ipress-pocket 业务功能模块(已完成)│├─ipress-run run 入口│└─ipress-ui 用户界面 (已完成)界面首页: pocket: pocket: 文件夹管理: 编辑器:

May 23, 2019 · 1 min · jiezi

Spring-Boot-Vue-前后端分离开发权限管理的一点思路

在传统的前后端不分的开发中,权限管理主要通过过滤器或者拦截器来进行(权限管理框架本身也是通过过滤器来实现功能),如果用户不具备某一个角色或者某一个权限,则无法访问某一个页面。 但是在前后端分离中,页面的跳转统统交给前端去做,后端只提供数据,这种时候,权限管理不能再按照之前的思路来。 首先要明确一点,前端是展示给用户看的,所有的菜单显示或者隐藏目的不是为了实现权限管理,而是为了给用户一个良好的体验,不能依靠前端隐藏控件来实现权限管理,即数据安全不能依靠前端。 这点就像普通的表单提交一样,前端做数据校验是为了提高效率,提高用户体验,后端才是真正的确保数据完整性。 所以,真正的数据安全管理是在后端实现的,后端在接口设计的过程中,就要确保每一个接口都是在满足某种权限的基础上才能访问,也就是说,不怕将后端数据接口地址暴露出来,即使暴露出来,只要你没有相应的角色,也是访问不了的。 前端为了良好的用户体验,需要将用户不能访问的接口或者菜单隐藏起来。 有人说,如果用户直接在地址拦输入某一个页面的路径,怎么办?此时,如果没有做任何额外的处理的话,用户确实可以通过直接输入某一个路径进入到系统中的某一个页面中,但是,不用担心数据泄露问题,因为没有相关的角色,就无法访问相关的接口。 但是,如果用户非这样操作,进入到一个空白的页面,用户体验不好,此时,我们可以使用 Vue 中的前置路由导航守卫,来监听页面跳转,如果用户想要去一个未获授权的页面,则直接在前置路由导航守卫中将之拦截下来,重定向到登录页,或者直接就停留在当前页,不让用户跳转,也可以顺手再给用户一点点未获授权的提示信息。 总而言之一句话,前端的所有操作,都是为了提高用户体验,不是为了数据安全,真正的权限校验要在后端来做,后端如果是 SSM 架构,建议使用 Shiro ,如果是 Spring Boot + 微服务,建议使用 Spring Security 。 关注公众号牧码小子,专注于 Spring Boot+微服务,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

May 23, 2019 · 1 min · jiezi

springboot结合MyBatis中使用foreach

废话少说,直接建项目上代码 先建一个springboot web项目或者模块,目录结构如下 在pom.xml中自行加入需要的依赖,application.properties的配置如下 server.port=8888mybatis.mapper-locations=classpath*:mapper/*Mapper.xmlspring.datasource.url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8spring.datasource.username=rootspring.datasource.password=Panbing936@spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver实体类User.java @Data@NoArgsConstructor@AllArgsConstructorpublic class User { private int id; private String name;}UserMapper.xml中使用foreach接收list参数 <!-- 使用foreach接收list参数 --> <select id="getUsersByListParam" resultType="cn.niit.mybatisforeach.pojo.User"> select * from t_user <foreach collection="list" item="item" open="where id in(" close=")" separator=","> #{item} </foreach> </select>使用foreach批量插入用户 <!-- 使用foreach批量插入用户--> <insert id="insertMultiUsers"> insert into t_user(id,name) values <foreach collection="users" item="user" separator=","> (#{user.id},#{user.name}) </foreach> </insert>使用foreach接收array数组 <!--使用foreach接收array数组--> <select id="getUserByArrayList" parameterType="java.util.ArrayList" resultType="cn.niit.mybatisforeach.pojo.User"> select * from t_user where id in <foreach collection="array" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>使用foreach接收Map <!--使用foreach接收Map--> <select id="getUserByMap" parameterType="java.util.HashMap" resultType="cn.niit.mybatisforeach.pojo.User"> select * from t_user where id in <foreach collection="ids" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>UserMapper.java@Mapper@Componentpublic interface UserMapper { /** * 多个参数查询user集合 */ List<User> getUsersByListParam(@Param("list") List list); /** * 使用foreach批量插入 */ int insertMultiUsers(@Param("users") List<User> users); /** * 使用foreach接收数组参数 */ List getUserByArrayList(int[] ids); /** * 使用foreach接收map */ List getUserByMap(Map params);}测试代码MybatisForeachApplicationTests.java 中的代码如下 ...

May 22, 2019 · 2 min · jiezi

Springboot-2x-整合druid1110-数据库密码加密-超简单配置

整合druid1、工程的pom.xml中加入druid starter:<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version></dependency>2、工程的application.yml中加入相关配置:public-key: 【your public key】spring: application: name: flow-effects-system datasource: type: com.alibaba.druid.pool.DruidDataSource druid: dbType: 【your db type】 driverClassName: 【your db driver】 url: 【your db url】 username: 【your db user name】 password: 【your encrypted Password】 #配置初始化大小 initialSize: 5 #最小空闲连接 minIdle: 5 #最大连接数量 maxActive: 20 #配置获取连接等待超时的时间 maxWait: 60000 #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 2000 #配置一个连接在池中最小、最大生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 600000 maxEvictableIdleTimeMillis: 900000 #是否自动回收超时连接 removeAbandoned: true #指定连接应该被废弃的时间 秒 removeAbandonedTimeout: 1800 #测试sql validationQuery: SELECT 1 FROM DUAL #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效 testWhileIdle: true #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 testOnBorrow: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 testOnReturn: false #通过connectProperties属性来打开mergeSql功能;慢SQL记录 connectionProperties: config.decrypt=true;config.decrypt.key=${public-key};druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 poolPreparedStatements: true #要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 maxPoolPreparedStatementPerConnectionSize: 50 #属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall filters: stat,wall,log4j,config web-stat-filter: enabled: true exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' stat-view-servlet: enabled: true reset-enable: false url-pattern: /druid/* login-username: admin login-password: admin filter: slf4j: enabled: true statement-create-after-log-enabled: false statement-close-after-log-enabled: false result-set-open-after-log-enabled: false result-set-close-after-log-enabled: false以上两部就完成了springboot 整合druid,不需要其他的任何配偶类。下面就是用alibaba druid官方提供的工具对铭文密码进行加密或者任何其他第三方的加密方法都行。记得保留公钥私钥匙 ...

May 22, 2019 · 1 min · jiezi

Spring-Boot-Vue-前后端分离开发前端网络请求封装与配置

前端网络访问,主流方案就是 Ajax,Vue 也不例外,在 Vue2.0 之前,网络访问较多的采用 vue-resources,Vue2.0 之后,官方不再建议使用 vue-resources ,这个项目本身也停止维护,目前建议使用的方案是 axios。今天松哥就带大家来看看 axios 的使用。 axios 引入axios 使用步骤很简单,首先在前端项目中,引入 axios: npm install axios -S装好之后,按理说可以直接使用了,但是,一般在生产环境中,我们都需要对网络请求进行封装。 因为网络请求可能会出错,这些错误有的是代码错误导致的,也有的是业务错误,不管是哪一种错误,都需要开发者去处理,而我们不可能在每一次发送请求时都去枚举各种错误情况。 因此我们需要对前端请求进行封装,封装完成后,将前端错误统一处理,这样,开发者只需要在每一次发送请求的地方处理请求成功的情况即可。 请求封装在 axios 中,我们可以使用 axios 自带的拦截器来实现对错误的统一处理。 在 axios 中,有请求拦截器,也有响应拦截器。 请求拦截器中可以统一添加公共的请求参数,例如单点登录中前端统一添加 token 参数。 响应拦截器则可以实现对错误的统一处理。 另外一个需要注意的地方则是错误的展示需要使用一种通用的方式,而不可以和页面绑定(例如,登录失败,在用户名/密码输入框后面展示错误信息,不支持这种错误显示方式),这里推荐使用 ElementUI 中的 Massage 来展示错误信息,这是一个页面无关的组件。 封装后的 axios 如下: import axios from 'axios'import {Message} from 'element-ui'axios.interceptors.request.use(config => { return config;}, err => { Message.error({message: '请求超时!'});})axios.interceptors.response.use(data => { if (data.status && data.status == 200 && data.data.status == 500) { Message.error({message: data.data.msg}); return; } if (data.data.msg) { Message.success({message: data.data.msg}); } return data.data;}, err => { if (err.response.status == 504 || err.response.status == 404) { Message.error({message: '服务器被吃了⊙﹏⊙∥'}); } else if (err.response.status == 403) { Message.error({message: '权限不足,请联系管理员!'}); } else if (err.response.status == 401) { Message.error({message: err.response.data.msg}); } else { if (err.response.data.msg) { Message.error({message: err.response.data.msg}); }else{ Message.error({message: '未知错误!'}); } }})代码解释: ...

May 21, 2019 · 2 min · jiezi

从零入门系列4Sprint-Boot-之-WEB接口设计实现

文章系列【从零入门系列-0】Sprint Boot 之 Hello World【从零入门系列-1】Sprint Boot 之 程序结构设计说明【从零入门系列-2】Sprint Boot 之 数据库实体类【从零入门系列-3】Sprint Boot 之 数据库操作类)前言前一章简述了已经实现了对数据库的增删改查以及复杂查询的功能,这一步将对相应的功能方法封装成WEB接口,对外提供WEB接口服务。 控制层类设计及测试控制层的角色是负责对访问路由到处理过程的关联映射和封装,在这里我们队Book新建一个控制类即可,在文件夹Controller上右键,New->Java Class新建BookController类。作为控制类,该类需要使用@Controller注解使之能够被识别为控制类对象,在这里我们使用@RestController,该注解包含了@Controller,相当于@Controller+@ResponseBody两个注解的结合,适合返回Json格式的控制器使用。 类定义@RestController@RequestMapping(path = "/library")public class BookController { @Autowired private BookJpaRepository bookJpaRepository; @Autowired private BookService bookService;}考虑到数据库的操作需要用到BookJpaRepository和BookService,这里首先声明这两个属性,并使用@Autowired注解自动装配。 在类上使用@RequestMapping(path = "/library")注解后,定义了该类的路径都是/library开始,可以统一接口路径,避免重复书写。 新增接口/*** 新增书籍* @param name* @param author* @param image* @return*/@PostMapping("/save")public Map<String, Object> save(@RequestParam String name, @RequestParam String author, @RequestParam String image){ Book book = new Book(); Map<String, Object> rsp = new HashMap<>(); book.setName(name); book.setAuthor(author); book.setImage(image); bookJpaRepository.save(book); rsp.put("data", book); rsp.put("code", "0"); rsp.put("info", "成功"); return rsp;}使用@PostMapping表示接口只接受POST请求,WEB接口路径为/library/save,该接口返回的是一个Map类型对象,但是由于类使用@RestController注解后,使得返回结果会自动转换成Json字符串格式。 ...

May 15, 2019 · 4 min · jiezi

19051501记录一次日常犯的错

Parameter 'array' not found. Available parameters are [collection, list]莫名其妙,今天写代码遇到个低级错误,困扰了好久,测试突然给提了个缺陷,说业务逻辑有问题于是,就启动了缺陷排查的流程1.问题复现   根据问题复现步骤,确实发现业务逻辑不对2.代码排查   根据代码排查,业务逻辑确实写了,对表的更新3.日志排查   根据日志排查,发现新增的代码并没有执行,而且,也没有报错。随后就进行了纠结(现在都想敲死自己,应该不用纠结,在编辑器debug跑一遍,问题就暴露出来了)。4.解决问题   先使用单测,跑了一遍对应的方法,发现确实没有问题,所以怀疑,是因为MOCK掉的DAO方法,抛了一个异常,然后没有显式的抛出来,所以就手动debug启动了下应用,就是POSTMAN测试,果然,报错如下: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'array' not found. Available parameters are [collection, list]这里是因为,在mybaits传集合参数,进行循环时,一定要指定集合类型,目前mybaits对List集合和Array集合,是不同,需要在循环时指定对应的集合,如果使用类似于Long[] 等进行传参时,一定要指定collection="array",如果使用List进行传参时,需要指定collection="list",否则就会抛异常。至于为什么在服务器上没有抛异常出来,很可能是被框架给吃掉了,需要进一步排查。

May 15, 2019 · 1 min · jiezi

从零入门系列3Sprint-Boot-之-数据库操作类

文章系列【从零入门系列-0】Sprint Boot 之 Hello World【从零入门系列-1】Sprint Boot 之 程序结构设计说明【从零入门系列-2】Sprint Boot 之 数据库实体类前言前一章简述了如何设计实现数据库实体类,本篇文章在此基础上进行开发,完成对该数据库表的常用操作,主要包括使用Spring Data JPA进行简单的增删改查和复杂查询操作。 Spring Data JPA是Spring提供的一套简化JPA开发的框架,按照约定好的【方法命名规则】写dao层接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作,同时提供了很多除了CRUD之外的功能,如分页、排序、复杂查询等等,Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现。通过引入Spring Data JPA后,我们可以基本不用写代码就能实现对数据库的增删改查操作。 此外,由于Spring Data JPA自带实现了很多内置的后台操作方法,因此在调用方法时必须根据其规范使用,深刻理解规范和约定。 表的基本操作实现(CRUD)在这里,先介绍一下JpaRepository,这是类型为interface的一组接口规范,是基于JPA的Repository接口,能够极大地减少访问数据库的代码编写,是实现Spring Data JPA技术访问数据库的关键接口。 编写数据操作接口在使用时,我们只需要定义一个继承该接口类型的接口即可实现对表的基本操作方法,在此我们需要对实体类Book进行操作,因此在Dao目录上右键New->Java Class,然后设置名称为BookJpaRepository,kind类型选Interface即可,然后添加注解及继承自JpaRepository,文件BookJpaRepository.java内容如下所示: package com.arbboter.demolibrary.Dao;import com.arbboter.demolibrary.Domain.Book;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;@Repositorypublic interface BookJpaRepository extends JpaRepository<Book, Integer> {}@Repository持久层组件,用于标注数据访问组件,即DAO组件,此时配合上上一篇文章中的JPA配置,我们就可以进行增删改查啦,不用添加任何其他代码,因为JpaRepository已经帮我们实现好了。 编写测试用例代码打开框架自动生成的测试代码文件DemoLibraryApplicationTests.java编写测试用例,测试增删改查效果,测试代码如下: @RunWith(SpringRunner.class)@SpringBootTestpublic class DemoLibraryApplicationTests { /** * @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 * 通过 @Autowired的使用来消除 set ,get方法,简化程序代码 * 此处自动装配我们实现的BookJpaRepository接口,然后可以直接使用bookJpaRepository操作数据库 * 如果不加@Autowired,直接使用bookJpaRepository,程序运行会抛出异常 */ @Autowired private BookJpaRepository bookJpaRepository; @Test public void contextLoads() { Book book = new Book(); // 增 book.setName("Spring Boot 入门学习实践"); book.setAuthor("arbboter"); book.setImage("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2656353677,2997395625&fm=26&gp=0.jpg"); bookJpaRepository.save(book); System.out.println("保存数据成功:" + book); // 查 book = bookJpaRepository.findById(book.getId()).get(); System.out.println("新增后根据ID查询结果:" + book); // 修改 book.setName("Spring Boot 入门学习实践(修改版)"); bookJpaRepository.save(book); System.out.println("修改后根据ID查询结果:" + book); // 删除 bookJpaRepository.deleteById(book.getId()); }}注意在测试代码用需要对属性bookJpaRepository使用@Autowired 自动注入实现初始化,@Autowired 注解,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 ...

May 14, 2019 · 5 min · jiezi

从零入门系列2Sprint-Boot-之-数据库实体类

文章系列【从零入门系列-0】Sprint Boot 之 Hello World【从零入门系列-1】Sprint Boot 之 程序结构设计说明前言本篇文章开始代码实践,系统设计从底向上展开,因此本篇先介绍如何实现数据库表实体类的设计实现。 SpringBoot数据库的持久层框架主要分为两种架构模式,即以JDBC Template为代表的SQL类和以Spring Data JPA为代表的ORM对象类。其中: Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!spring data jpa让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,自己写个仓储接口后继承JpaRepository即可实现最基本的增删改查功能!在使用@Entity进行实体类的持久化操作,当JPA检测到我们的实体类当中有@Entity 注解的时候,会在数据库中生成关联映射对应的表结构信息,因此针对本项目情况最底层的设计实现一个@Entity注解的书籍对象定义即可。 项目开始前,先按上一篇文章【从零入门系列-1】Sprint Boot 之 程序结构设计说明后台程序结构建立相对应的目录: 控制层:前端路由和后端处理关系处理,目录:Controller数据服务层:自定义对数据库的访问操作方法,目录:Service数据访问层:实现通用的数据库访问功能,SpringData的JPA方案,目录:Dao数据实体层:定义数据库表的属性方法,目录:Domain根据结构,我们需要在Domain目录下编写项目表实体类,右键Domain文件夹,New->Java Class。 编写实体类1.新建Book类 package com.arbboter.demolibrary.Domain; import javax.persistence.Entity; import javax.persistence.Table; /** * @Entity 注解该类为数据库表实体类,JPA可自动扫描识别到 * @Table 注解数据表信息,其中name指定表名 */ @Entity @Table(name = "library_book") public class Book{ }注意添加的类需要使用@Entity注解,其中@Table是可选的,默认不配置生成的表名和类名相同,如果上述类中不使用@Table,那么本类对应的表名为book。 2.添加表字段信息 public class Book{ /** * ID,唯一主键,按Alt+Enter可以快速导入引入 */ @Id @GeneratedValue private Integer id; /** * 书名 */ private String name; /** * 作者 */ private String author; /** * 封面 */ private String image; }@Id注解用于声明一个实体类的属性映射为数据库的主键列,该属性通常置于属性声明语句之前,可与声明语句同行,也可写在单独行上。 ...

May 14, 2019 · 2 min · jiezi

从零开始上线网站的日常二-第一次上线

上一章:从零开始上线网站的日常(一)— 前后端分离网站本地调通本章目标:在云服务器上线上一章完成的网站 时间:2019.05.13 零. 步骤项目打包云服务器环境部署(JDK & Nginx)项目部署部署脚本编写一. 项目打包1. 前端项目打包前端项目打包比较简单,进入前端项目目录,运行yarn build即可完成打包 打包结果即当前目录下的 dist 文件夹,其内容如图所示: 将 dist 文件夹压缩为dist.zip包即可 2. 后端项目打包1) 使用 IDEA 打包后端项目A. 配置 IDEA a. 点击IDEA 顶部栏 File - Project Structure b. 在 Project Structure 窗口中点击 Artifacts - JAR - From modules with dependencies c. 在 Create JAR from Modules 中点击 Main Class 右边的文件夹图标,在弹出的 Select Main Class 框中选中 SpringBoot 项目的启动文件(一般会自动显示) d. 在 JAR files from libraries 部分选中第二项,下方目录默认即可,点击确认. ...

May 13, 2019 · 3 min · jiezi

从零入门系列1Sprint-Boot-程序结构设计说明

【从零入门系列-1】Sprint Boot 程序结构设计说明文章系列【从零入门系列】Sprint Boot 之 Hello World设计效果图页面展示 增 删 改 查 搜索 页面程序结构图书馆结构分布图 1-WEB bootstrapbootstrap-table2-后台程序 控制层:前端路由和后端处理关系处理数据服务层:自定义对数据库的访问操作方法数据访问层:实现通用的数据库访问功能,SpringData的JPA方案数据实体层:定义数据库表的属性方法3-数据库 Book表:名字,作者,封面 说明程序的主要结构比较简单,主要难点在于初次接触很多东西理解不到位,各个层次间的交互链路以及Java基础薄弱,使得中间会有磕磕碰碰,且即使完成了整个项目,也不确定其中实现方案或者编码是否不合理。 但,先做出来再说,其他问题等以后熟练了再慢慢改进。 结束语本章预先提供了项目实际效果图以及项目的整体结构设计,后续文章会根据本篇章设计依次实现各个模块,请持续关注。

May 13, 2019 · 1 min · jiezi

来认识一下venusinit一个让你仅需一个命令开始Java开发的命令行工具

源代码地址: Github仓库地址 个人网站:个人网站地址 前言不知道你是否有过这样的经历。不管你是什么岗位,前端也好,后端也罢,想去了解一下Java开发到底是什么样的,它是不是真的跟传说中的一样。 于是你拿起键盘,用触控板 ? '' : 抄起鼠标',开始了Java淌坑之旅。在一把梭的操作之后,面对你搭建的环境,你陷入了深深的沉思,开始了吾日三省吾身的自我质疑中。这tm到底哪儿错了?到底哪儿在报错?这报错啥意思? 经历了吾日三省吾身的深思之后,你把鼠标移动到了MacOS ? 左上角' : '右上角',然后双手离开了键盘放弃了操作。嗯,刚刚什么都没有发生。 如果你有类似的经历(如果没有请给Java一个面子假装你有过),那么就来认识一下venus-init吧。 venus-init它是什么venus-init是一个基于Node.js的命令行工具。使用venus-init,只需要一个命令,然后输入两个参数,就可以创建一个简单的Spring Boot应用。并且初始化的demo项目中还会有一个名叫Hello的模块来帮助你更好的理解这个项目。 在我还没有更新文档的情况下,npm上周下载量就突破了1000,不管是因为什么原因,下错了也好,觉得好用也好。感谢大佬们的支持。 如何使用怎么做呢?"He can do that with a snap of his fing..." 走错了片场不好意思。首先你需要使用npm install venus-init来安装它。然后只需要在命令行输入如下的命令。 venus init# you can also do it like thisvenus i只要输入这样一个命令,然后跟随命令行工具的指导,完成创建就ok了。 创建项目之后完成上述步骤之后,就会发现在键入命令行的目录下,多个一个具有这样结构的目录。(这是我自己创建的demo项目,group和artifact都可以自己指定) .├── LICENSE├── README.md├── api│   ├── pom.xml│   └── src│   └── main│   ├── java│   │   └── com│   │   └── detectivehlh│   │   └── demo│   │   └── api│   │   ├── Application.java│   │   ├── config│   │   │   ├── DbConfig.java│   │   │   └── SwaggerConfig.java│   │   ├── controller│   │   │   └── HelloController.java│   │   ├── dao│   │   │   └── HelloMapper.java│   │   ├── dto│   │   │   └── HelloDTO.java│   │   ├── entity│   │   │   └── Hello.java│   │   └── service│   │   ├── HelloService.java│   │   └── impl│   │   └── HelloServiceImpl.java│   └── resources│   ├── application.yml│   └── mapper│   └── HelloMapper.xml└── pom.xml其中我内置了一个Hello的模块供大家参考,通过这个模块相信大家可以更加快的熟悉整个项目。整个项目的结构非常的清晰明了。 ...

May 13, 2019 · 1 min · jiezi

Spring-boot-redis-cache-的-key

在数据库查询中我们往往会使用增加缓存来提高程序的性能,@Cacheable 可以方便的对数据库查询方法加缓存。本文主要来探究一下缓存使用的key。 搭建项目数据库 mysql> select * from t_student;+----+--------+-------------+| id | name | grade_class |+----+--------+-------------+| 1 | Simone | 3-2 |+----+--------+-------------+1 row in set (0.01 sec)spring boot 配置 #jpaspring.jpa.hibernate.ddl-auto=nonespring.datasource.url=jdbc:mysql://127.0.0.1:3306/praticespring.datasource.username=rootspring.datasource.password=123456#redisspring.redis.host=localhostspring.redis.lettuce.pool.maxActive=5spring.redis.lettuce.pool.maxIdle=5#cachespring.cache.cache-names=Cachespring.cache.redis.time-to-live=300000数据访问层 public interface StudentDao extends CrudRepository<Student, Long> { @Cacheable(value = "Cache") @Override Optional<Student> findById(Long aLong); @Cacheable(value = "Cache") Optional<Student> findByName(String name);}启动调用数据访问层方法观察 @Overridepublic void run(ApplicationArguments args) throws Exception { Optional<Student> optional = studentDao.findById(1L); System.out.println(optional);}当默认使用 @Cacheable(value = "Cache") 的时候查看 redis 中缓存的 key ...

May 12, 2019 · 2 min · jiezi

第四讲Spring-Boot-自动化配置原理解析

本讲着重介绍Spring Boot的核心技术原理—自动化配置(AutoConfiguration)。通过对Spring Boot自动化配置相关源码的梳理,一步步揭开Spring Boot自动化配置的神秘面纱。1. 主要内容通过本讲的内容,你可以找到下面几个问题的具体答案: 什么是自动化配置?为什么需要自动化配置?自动化配置是如何实现的?如何调试自动化配置?2. 为什么需要自动化配置在常规的Spring应用程序中,充斥着大量的配置文件,我们需要手动去配置这些文件,如配置组件扫描、配置servlet、配置视图解析器,配置http编码等。这里以配置http编码为例,我们需要在web.xml文件中配置类似的一个filter: 下面的这段代码向我们展示了配置一个Servlet的经典写法: 如果我们的项目中使用Hibernate/JPA时 ,我们还需要配置诸如数据源、Bean工厂和事务管理器等。 上述的配置可以称得上是Spring应用程序的经典案例,但现在看起来,你是否觉得心累?常规的配置让开发人员将更多的经历耗费在了配置文件上。而这些配置都是一些固定模式的配置方式,甚至很多都是模板代码,那既然是这样一种情况,有没有一种可能性,让Spring自动完成这些模板配置工作呢?答案是肯定的,这就是Spring Boot AutoConfiguration产生的初衷。将开发人员从繁重的配置工作中解放出来,而这些繁琐的配置细节交由Spring Boot去完成,如果我们需要提供自己的配置参数,只需要覆盖自动配置的参数即可。现在是否觉得有点意思了? 3. Spring Boot自动化配置的核心原理Spring Boot有关自动化配置的源码可以在spring-boot-autoconfigure-2.x.x.x.jar/spring-boot-autoconfigure-1.x.x.x.jar包中找到。在org.springframework.boot.autoconfigure包下,提供了如下的一些自动化配置源码: 如何你想了解更多关于Spring Boot自动配置的知识,可以在这里查看具体的源码。 在spring-boot-autoconfigure.jar包中还有一份重要的文件,它是Spring Boot实施自动配置的关键所在。该文件是/META-INFO/spring.factories。此文件列出了所有可以被Spring Boot实施自动配置的模块清单,下面列举其中的一部分以供参考: 4. 调试自动配置有三种方式可以调试并查看有关自动配置的相关信息,如当前项目中有哪些组件被自动配置,有哪些组件未被自动配置以及产生的原因等。 通过java -jar [jarfile] --debug的方式运行项目,可以观察自动化配置的相关信息。打开项目的调试日志记录。在application.properties文件中为org.springframework包添加日志记录级别为DEBUG : logging.level.org.springframework:DEBUG为项目添加spring-boot-starter-actuator和spring-data-rest-hal-browser两个依赖: 使用方式一和方式二时,当我们重新启动应用程序时,你可以在控制台找到一份自动配置的报告,这份报告包含了被自动配置的组件信息和未被自动配置的组件信息以及相关的说明。 如果采用第三种方式,重新启动项目后,在浏览器地址栏输入http://localhost:8080/actuator/#http://localhost:8080/autoconfig ,你将收到如下的信息: 5 Spring Boot自动配置的核心原理在Spring Boot应用程序中,所有的运作都起源于@SpringBootApplication注解,@SpringBootApplication打开了运行程序的大门。该注解是一个组合注解,其核心功能是由@EnableAutoConfiguration注解提供的。现在先让我们看一下@SpringBootApplication和@EnableAutoConfiguration注解的源码。 在SpringBootApplication注解类中,最核心的地方是上图1处的@EnableAutoConfiguration注解,它为@SpringBootApplication注解类贡献了大部分的功能。我们再看看@Enableautoconfiguration注解的源码: 这里最关键的地方是由@Import注解导入的自动配置功能,EnableConfigurationImportSelector通过SpringFactoriesLoader.loadFactoryNames()方法来扫描spring-boot-autoconfigure.jar文件下/META-INF/spring.factories文件中配置的jar包信息,这就是一开始提到的那一份spring.factories文件的重要性。我们可以看一下EnableConfigurationImportSelector类中对应的代码: 6. 一个自动配置的示例分析org.springframework.boot.autoconfigure包下已经提供了很多的自动配置类,接下来我们以DataSourceAutoConfiguration为例子,看看其中配置的细节。通常,所有自动配置类都会查看类路径中可用的其他类。如果类路径中有特定的类,则通过自动配置启用该功能的配置。判断类路径上是否存在特定的东西,这是通过org.springframework.boot.autoconfigure.condition包下的条件注解来完成的,这里列举几个常用的条件注解解: @ConditionalOnBean:当容器中有指定的Bean的条件下@ConditionalOnClass:当类路径下有指定的类的条件下@ConditionalOnMissingBean: 当容器中没有指定Bean的情况下@ConditionalOnMissingClass: 当类路径下没有指定的类的条件下 @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }):仅当类路径中有这些类时,才会启用此配置。@ConditionalOnMissingBean :仅当没有其他bean配置相同名称时才配置此Bean。 7.结束语通过以上内容,简单的梳理了Spring Boot 自动配置的大致原理,同时回答了一开始提出的4个问题。Spring Boot的出现,是为了提高我们的开发效率,我们需要去了解其核心的基本原理,但任何时候都不应该被一个框架或者技术所绑架,更为重要的是学习其基本的原理,而不是框架本身。基于这个出发点,在下一讲中,将实现一个自定义的Spring Boot Starter,并提供自动化配置的能力。那本次内容到这里就结束了,谢谢~~ ...

May 12, 2019 · 1 min · jiezi

springboot结合全局异常处理之登录注册验证

在学校做一个校企合作项目,注册登录这一块需要对注册登录进行输入合法的服务器端验证,因为是前后端分离开发,所以要求返回JSON数据。方法有很多,这觉得用全局异常处理比较容易上手 全局异常处理首先来创建一个sprIngboot的web项目或模块,目录结构如下 实体类User.java @Datapublic class User { private String userName; private String passwold;}实体类UserResult.java 把数据封装到这里返回到客户端 @Data@NoArgsConstructor@AllArgsConstructorpublic class UserResult { private int code; private String msg;}接下来自定义异常,都继承自ExceptionUserNullException.java 当用户名为空抛出这个异常 public class UserNullException extends Exception{ public UserNullException() { super("用户名不能为空"); }}PasswoldNullException.java 当密码为空抛出这个异常 public class PasswoldNullException extends Exception { public PasswoldNullException() { super("密码不能为空"); }}UserNamePasswordNullException.java 当用户名和密码都为空抛出这个异常 public class UserNamePasswordNullException extends Exception { public UserNamePasswordNullException() { super("请输入用户名和密码"); }}UserNameValidationException.jva 当输入不符合要求的用户名时抛出此异常 public class UserNameValidationException extends Exception{ public UserNameValidationException() { super("请输入6到16位的数字或字母组合"); }}UserNamePasswordNullException.java 当输入的密码不符合要求时抛出这个异常 ...

May 11, 2019 · 2 min · jiezi

第三讲使用JUnit对Spring-Boot中的Rest-Controller进行单元测试

(第三讲)使用JUnit对Spring Boot中的Rest Controller进行单元测试本次教程主要讲解如何对Spring Boot中的Rest Service进行单元测试。以往我们主要是使用JUnit对业务层进行单元测试,本次课程将使用一个简单的案例来说明如何使用JUnit对Spring Boot的Rest Service进行单元测试。1. 主要类容快速搭建Restfull Service 环境创建GET请求以检索用户信息创建GET请求检索用户角色信息创建POST请求新增用户角色信息如何使用PostMan请求Restfull Service使用JUnit对GET请求进行单元测试使用JUnit对POST请求进行单元测试2. 你将需要准备的工具JDK 1.8及以上版本Maven 3.0及以上版本的项目构建工具IDEA代码编辑器3. 你可以通过以下的地址获取本次课程的所有示例代码项目代码已经上传到GitHub仓库中,你可以通过以下的地址获取示例源码:https://github.com/ramostear/Spring_Boot_2.X_Tutorial/tree/master/spring-boot-junit-rest-service 4. 项目结构下面通过一张截图来了解以下本次课程中我们使用到的项目结构。 首先我们需要位单元测试提供一个可用的Rest Controller。UserController文件为我们提供了一个可用于测试的Rest Controller。在UserController类中,我们提供两种请求类型的方法,一种是GET请求,另一种是POST请求。然后我们为这两种请求方式的方法编写单元测试用例。 在接下来的测试过程中,我们将使用Mockito来模拟请求UserService的过程,使用MockMvc来模拟请求UserController。单元测试的目的是将测试范围尽可能的缩小。在本次案例中,我们仅对UserController中的方法进行测试。 5. 初始化项目我们依然使用Spring Initializr来初始化本次课程的项目,你需要配置如下图中的参数: 现在我们需要提供两个实体类:User和Role: User.java Role.java 6. 提供可用的业务服务所有的应用都需要有数据的存储,本次课程主要的重点是为了Rest Controller的单元测试,因此使用ArrayList来充当数据库的角色。在案例中,一个用户可以有多个角色,一个角色也可以被赋予给多个用户。用户有ID,名字,别名和角色列表,角色具有ID,名称和描述。在UserService类中,将提供如图所示的公共方法。 7. 提供GET请求方法在UserController类中,我们将提供如下几个公开的GET请求方法: @GetMapping(value="/users") : 获取所有的用户信息@GetMapping(value="/users/{id}/roles") : 根据用户ID获取该用户的所有角色信息UserController.java类中的详细代码如下: package com.ramostear.spring.boot.test.restservice.controller;import com.ramostear.spring.boot.test.restservice.model.Role;import com.ramostear.spring.boot.test.restservice.model.User;import com.ramostear.spring.boot.test.restservice.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import java.util.List;@RestControllerpublic class UserController { private final UserService userService; @Autowired public UserController(UserService userService){ this.userService = userService; } @GetMapping(value = "/users") public List<User> findAllStudents(){ return userService.findAllUsers(); } @GetMapping(value = "/users/{id}/roles") public List<Role> findUserRoles(@PathVariable(value = "id")String id){ return userService.findUserAllRoles(id); }}8. 使用Postman对RestController进行测试我们将使用Postman工具对上述两个Rest API进行请求,首先向Postman地址栏输入http://localhost:8080/users 进行测试,获得的响应信息如下: ...

May 10, 2019 · 2 min · jiezi

Alice-上线小记

引言Alice学生管理系统昨日正式上线测试,上线遇到的问题不少,但最后都完美解决了。 特此分享,一起爬坑。 项目优化登录页美化原来的登录页采用的是黑背景,经过大家的充分讨论,我们需要换一个登录页,黑背景看着压抑。 然后就在晨澍和潘佳琦的帮助下开始找各种登录的模板,发现都特别丑,后来发现当前系统的登录风格和微信的登录风格很像,顺手就抄过来了,感觉效果还不错。 上线问题打包问题前台ng build --prod之后,发现样式不一致。 期待: 实际结果: 查看元素发现是Bootstrap的样式在打包之后没有了。 后来发现:黄庭祥在style.less中引用了一个在线的Bootstrap,打包肯定打不进去啊。 引用的所有包,都不能引在线的,需要使用npm安装。npm install之后的包才能被打包进去。 远程文件拷贝本地打完包,需要将文件上传到服务器,查了一下,需要使用scp命令。 scp:secure copy,安全拷贝,将文件加密传输,安全的远程文件拷贝命令。 scp -r /Users/panjie/github/yunzhiclub/alice/web/webApp/dist/webApp root@xxx.xxx.xxx.xxx:/root/将本地打包的webApp目录上传到服务器的/root目录下。 -r代表目录,xxx.xxx.xxx.xxx请替换成相应的服务器IP地址。 nginx 403前台上线,浏览器端访问服务器却得到了403,查看相关日志后发现是nginx访问文件时遭到了拒绝。 在nginx的配置文件中,有一行配置用户的。 原配置是user nginx;,所以启动时nginx进程的用户是nginx,但是webApp文件的用户所有者是root,所以就403了。 解决方案是把用户配置改成root(有权限的用户),然后重新加载配置文件即可。 刷新404问题当用户访问127.0.0.1:8100时,根据路由的重定向,''重定向为'setup'登录界面。 但是用户刷新或直接访问127.0.0.1:8100/setup时,报404错误。 猜想,nginx转发出现了问题,应该是/转给了Angular,但是把/setup当成了文件夹。 这个只是我的猜测,如果您有什么意见,欢迎在评论区中指出我的错误,感激不尽。 华软就没有该类问题,对比两个项目,发现华软中默认配置了hash路由。 hash路由特意去官网学习了一下hash路由,感觉应该能给大家讲明白。 两个路由: 127.0.0.1:8100/setup127.0.0.1:8100/#/setup普通的路由是不带#的,hash路由是带#的。 #号,我们是不是在哪里见过?大家还记得Spring的官方文档吗? 用a标签实现页面内跳转。hash路由与之类似。 #之后的路由变化不会被发送给服务器,也就是说:127.0.0.1:8100/setup,后台nginx获取到的路径是/setup,而使用hash路由,对于路由127.0.0.1:8100/#/setup,后台获取到的路径就是/。 注入hash路由策略,即可启用hash路由。 再访问,后台获取到的就是/,然后把angular应用返回回来,然后angular应用再去处理#之后的路由,不会出现404。 以上的论述,是我结合官方文档和我的经验得出的解决,如果有不正确之处,欢迎您批评指正。 不足当时忙着上线,ng alain中默认也启用了hash路由,就以为hash路由是正统的解决方案。 但是今天看官方文档,却看到了这样的描述: 几乎所有的Angular项目都会使用默认的HTML 5风格。它生成的URL更易于被用户理解,它也为将来做服务端渲染预留了空间。 在服务器端渲染指定的页面,是一项可以在该应用首次加载时大幅提升响应速度的技术。那些原本需要十秒甚至更长时间加载的应用,可以预先在服务端渲染好,并在少于一秒的时间内完整呈现在用户的设备上。 默认的路由(不带#)的,支持服务器端渲染,而hash路由则不支持。除非你有强烈的理由不得不使用hash路由,否则就应该坚决使用默认的HTML 5路由风格。 hash路由不推荐,不支持SSR。另外,我觉得应该是当前nginx的转发配置写得不好,以后再研究研究。 总结对技术怀着一颗敬畏之心,努力地寻找着最佳实践。

May 10, 2019 · 1 min · jiezi

SpringBoot整合shiro从初恋到失恋

建个项目或者模块,目录结构如下 在pom.xml中加入shiro依赖,其他依赖自行添加(lombok,jpa,mybatis,web,thymeleaf等) <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>application.properties中的配置##端口号server.port=8888##数据库配置##数据库地址spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=utf8&useSSL=false\ &serverTimezone=GMT%2B8##数据库用户名spring.datasource.username=root##数据库密码spring.datasource.password=Panbing936@##数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver##validate 加载hibernate时,验证创建数据库表结构##create 每次加载hibernate,重新创建数据库表结构,这就是导致数据库表数据丢失的原因。##create-drop 加载hibernate时创建,退出是删除表结构##update 加载hibernate自动更新数据库结构##validate 启动时验证表的结构,不会创建表##none 启动时不做任何操作spring.jpa.hibernate.ddl-auto=update##控制台打印sqlspring.jpa.show-sql=true# 建议在开发时关闭缓存,不然没法看到实时页面spring.thymeleaf.cache=false##去除thymeleaf的html严格校验spring.thymeleaf.mode=LEGACYHTML5#没下面这行配置就会报这个错误#Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect实体类SysMenu.java中的代码@Entity@Datapublic class SysMenu implements Serializable { @Id @GeneratedValue private Integer menuId; private String menuName; @ManyToMany @JoinTable(name = "SysRoleMenu", joinColumns = {@JoinColumn(name = "menuId")}, inverseJoinColumns = {@JoinColumn(name = "roleId")}) private List<SysRole> roleList;}实体类SysRole.java中的代码@Entity@Datapublic class SysRole implements Serializable { @Id @GeneratedValue private Integer roleId; private String roleName; //多对多关系 @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "SysRoleMenu", joinColumns = {@JoinColumn(name = "roleId")}, inverseJoinColumns = {@JoinColumn(name = "menuId")}) private List<SysMenu> menuList; //多对多关系 @ManyToMany @JoinTable(name = "SysUserRole", joinColumns = {@JoinColumn(name = "roleId")}, inverseJoinColumns = {@JoinColumn(name = "userId")}) private List<SysUser> userList;// 一个角色对应多个用户}实体类SysUser.java中的代码@Entity@Datapublic class SysUser implements Serializable { @Id @GeneratedValue private Integer userId; @NotEmpty private String userName; @NotEmpty private String passWord; //多对多关系 @ManyToMany(fetch = FetchType.EAGER) //急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载 //FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载 @JoinTable(name = "SysUserRole", joinColumns = {@JoinColumn(name = "userId")}, inverseJoinColumns = {@JoinColumn(name = "roleId")}) private List<SysRole> roleList;// 一个用户具有多个角色}接口UserRepository.java中的代码public interface UserRepository extends CrudRepository<SysUser,Long> { SysUser findByUserName(String username);}下面的代码才是shiro相关的MyshiroRealm.javapublic class MyShiroRealm extends AuthorizingRealm { @Resource private UserRepository userRepository; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); SysUser userInfo = (SysUser)principals.getPrimaryPrincipal(); for(SysRole role:userInfo.getRoleList()){ authorizationInfo.addRole(role.getRoleName()); for(SysMenu menu:role.getMenuList()){ authorizationInfo.addStringPermission(menu.getMenuName()); } } return authorizationInfo; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //获得当前用户的用户名 String username = (String)token.getPrincipal(); System.out.println(token.getCredentials()); //根据用户名找到对象 //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法 SysUser userInfo = userRepository.findByUserName(username); if(userInfo == null){ return null; } //这里会去校验密码是否正确 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( userInfo, //用户名 userInfo.getPassWord(),//密码 getName() ); return authenticationInfo; }}ShiroConfig.java@Configurationpublic class ShiroConfig { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { logger.info("启动shiroFilter--时间是:" + new Date()); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //shiro拦截器 Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 --> // 配置不被拦截的资源及链接 filterChainDefinitionMap.put("/static/**", "anon"); // 退出过滤器 filterChainDefinitionMap.put("/logout", "logout"); //配置需要认证权限的 filterChainDefinitionMap.put("/**", "authc"); // 如果不设置默认会自动寻找Web工程根目录下的"/login"页面,即本文使用的login.html shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } //自定义身份认证Realm(包含用户名密码校验,权限校验等) @Bean public MyShiroRealm myShiroRealm(){ MyShiroRealm myShiroRealm = new MyShiroRealm(); return myShiroRealm; } @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); return securityManager; } //开启shiro aop注解支持,不开启的话权限验证就会失效 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } //配置异常处理,不配置的话没有权限后台报错,前台不会跳转到403页面 @Bean(name="simpleMappingExceptionResolver") public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() { SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver(); Properties mappings = new Properties(); mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理 mappings.setProperty("UnauthorizedException","403"); simpleMappingExceptionResolver.setExceptionMappings(mappings); // None by default simpleMappingExceptionResolver.setDefaultErrorView("error"); // No default simpleMappingExceptionResolver.setExceptionAttribute("ex"); // Default is "exception" return simpleMappingExceptionResolver; }}thymeleaf的页面代码index.html<!DOCTYPE html><html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"><head> <meta charset="UTF-8"> <title>Title</title></head><body>index<br/><form th:action="@{/logout}" method="post"> <p><input type="submit" value="重新登录"/></p></form><form th:action="@{/select}" method="get"> <p><input type="submit" value="查看"/></p></form><form th:action="@{/delete}" method="get"> <p><input type="submit" value="删除"/></p></form></body></html>login.html<!DOCTYPE html><html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"><head> <meta charset="UTF-8"> <title>Login</title></head><body>错误信息:<h4 th:text="${msg}"></h4><form action="" method="post"> <p>账号:<input type="text" name="username" value="dalaoyang"/></p> <p>密码:<input type="text" name="password" value="123"/></p> <p><input type="submit" value="登录"/></p></form></body></html>另外三个跳转页面就不贴出来了,panpan账号登录可以查看和删除,用xiaoli账号登录则只有查看而没有删除的权限,代码见下面,sql文件在resources包下 ...

May 7, 2019 · 2 min · jiezi

SpringBoot-20-响应式编程

SpringBoot 2.0 已经发布多时,一直不知道它有什么用,只是知道它有个webflux。今天就来学习一下,看一下是否有必要升级到新版本?1 2.0与1.0版本的区别? 我们可以看出来,增加了些新的特性,主要是对响应式编程的支持,底层多了Netty,这样就可以进行非阻塞io的编程,这也是响应式编程的基础。还有就是2.0对应的java版本必须最低java8,支持java9.如果你们公司使用的还是1.6,1.7那就不适合升级版本,可能会带来一堆麻烦。使用webflux并不会提高应用的响应速度,官网也明确指出了。所以不要跟风去使用2.0的版本。使用webflux可以在有限的资源下提高系统的吞吐量和伸缩性。2 搭建简单的webflux项目如果你是使用得STS来创建项目的话将会很简单,直接选择web flux模块就好。SpringBoot选择最新稳定的2.1.4.RELEASE。 完整pom文件: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.mike</groupId> <artifactId>flux</artifactId> <version>0.0.1-SNAPSHOT</version> <name>mike-flux</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>创建启动类:(使用STS会自动创建) package com.mike;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class MikeFluxApplication { public static void main(String[] args) { SpringApplication.run(MikeFluxApplication.class, args); }}3 编写路由和处理类有过前端工作经验的小伙伴,对路由肯定不陌生,Vue react中都有统一的路有管理。现在SpringBoot也可以这样来写了。后端的小伙伴可以把它理解为你之前写的controller。先定义一个处理类: ...

May 7, 2019 · 1 min · jiezi

学习笔记关于consul-的理解

首先的首先,按照我最近一段时间的学习,推荐入门的几篇文章。然后这里记录下我自己在此基础上的理解0. 入门学习第一个 :初学通过这个先了解下 什么是 consul,知道一些基本的概念官网 :consul官网中文翻译知道一个全面的大概consul 可以用来做什么基于Docker + Consul + Nginx + Consul-Template的服务负载均衡实现应用到 spring boot 项目中注册中心 Consul 使用详解5. 实际项目介绍可以自己公司的一个实际项目。 todo

May 7, 2019 · 1 min · jiezi

Provisional-headers-are-shown错误

使用chrome谷歌浏览器前后端接口调试的时候遇到了这个问题:network Provisional headers are shownconsole Cross-Origin Read Blocking (CORB) blocked cross-origin response http://xxx:180/test?id=1035 with MIME type application/json. See https://www.chromestatus.com/feature/5629709824032768 for more details.如果使用其它浏览器或者postman等测试工具测试的话又可以调用。 错误原因其实是跨域的问题,后端需要设置允许跨域就可以了。 我的项目是spring boot项目,配置跨域如下: @Configurationpublic class CorsConfig { private CorsConfiguration buildConfig() { org.springframework.web.cors.CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("http://127.0.0.1:800"); // 1这就是前端调用放的origin,可以在network中查看 corsConfiguration.addAllowedHeader("*"); // 2允许任何头 corsConfiguration.addAllowedMethod("*"); // 3允许任何方法(post、get等) return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); // 4 return new CorsFilter(source); }}

May 7, 2019 · 1 min · jiezi

Spring-Boot-注册-Servlet-的三种方法真是太有用了

本文栈长教你如何在 Spring Boot 注册 Servlet、Filter、Listener。 你所需具备的基础什么是 Spring Boot?Spring Boot 核心配置文件详解Spring Boot 开启的 2 种方式Spring Boot 自动配置原理、实战Spring Boot 2.x 启动全过程源码分析更多请在Java技术栈微信公众号后台回复关键字:boot。 一、Spring Boot 注册Spring Boot 提供了 ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean 三个类分别用来注册 Servlet, Filter, Listener,下面是 Servlet 的示例代码。 import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/** * @author Java技术栈 */public class RegisterServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { String name = getServletConfig().getInitParameter("name"); String sex = getServletConfig().getInitParameter("sex"); resp.getOutputStream().println("name is " + name); resp.getOutputStream().println("sex is " + sex); }}@Beanpublic ServletRegistrationBean registerServlet() { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean( new RegisterServlet(), "/registerServlet"); servletRegistrationBean.addInitParameter("name", "javastack"); servletRegistrationBean.addInitParameter("sex", "man"); return servletRegistrationBean;}二、组件扫描注册Servlet 3.0 之前,Servlet、Filter、Listener 这些组件都需要在 web.xml 中进行配置,3.0 之后开始不再需要 web.xml 这个配置文件了,所有的组件都可以通过代码配置或者注解来达到目的。 ...

May 7, 2019 · 3 min · jiezi

基于Spring-Security-Oauth2的SSO单点登录JWT权限控制实践

概 述在前文《基于Spring Security和 JWT的权限系统设计》之中已经讨论过基于 Spring Security和 JWT的权限系统用法和实践,本文则进一步实践一下基于 Spring Security Oauth2实现的多系统单点登录(SSO)和 JWT权限控制功能,毕竟这个需求也还是蛮普遍的。 代码已开源,放在文尾,需要自取理论知识在此之前需要学习和了解一些前置知识包括: Spring Security:基于 Spring实现的 Web系统的认证和权限模块OAuth2:一个关于授权(authorization)的开放网络标准单点登录 (SSO):在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统JWT:在网络应用间传递信息的一种基于 JSON的开放标准((RFC 7519),用于作为JSON对象在不同系统之间进行安全地信息传输。主要使用场景一般是用来在 身份提供者和服务提供者间传递被认证的用户身份信息要完成的目标目标1:设计并实现一个第三方授权中心服务(Server),用于完成用户登录,认证和权限处理目标2:可以在授权中心下挂载任意多个客户端应用(Client)目标3:当用户访问客户端应用的安全页面时,会重定向到授权中心进行身份验证,认证完成后方可访问客户端应用的服务,且多个客户端应用只需要登录一次即可(谓之 “单点登录 SSO”)基于此目标驱动,本文设计三个独立服务,分别是: 一个授权服务中心(codesheep-server)客户端应用1(codesheep-client1)客户端应用2(codesheep-client2)多模块(Multi-Module)项目搭建三个应用通过一个多模块的 Maven项目进行组织,其中项目父 pom中需要加入相关依赖如下: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.8.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>io.spring.platform</groupId> <artifactId>platform-bom</artifactId> <version>Cairo-RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR2</version> <type>pom</type> <scope>import</scope> </dependency></dependencies>项目结构如下: 授权认证中心搭建授权认证中心本质就是一个 Spring Boot应用,因此需要完成几个大步骤: pom中添加依赖<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency></dependencies>项目 yml配置文件:server: port: 8085 servlet: context-path: /uac即让授权中心服务启动在本地的 8085端口之上 创建一个带指定权限的模拟用户@Componentpublic class SheepUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { if( !"codesheep".equals(s) ) throw new UsernameNotFoundException("用户" + s + "不存在" ); return new User( s, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_NORMAL,ROLE_MEDIUM")); }}这里创建了一个用户名为codesheep,密码 123456的模拟用户,并且赋予了 普通权限(ROLE_NORMAL)和 中等权限(ROLE_MEDIUM) ...

May 7, 2019 · 2 min · jiezi

SpringBoot启动报错Failed-to-determine-a-suitable-driver-class

SpringBoot启动报错如下 Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.2019-05-06 21:27:18.275 ERROR 10968 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : ***************************APPLICATION FAILED TO START***************************Description:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.Reason: Failed to determine a suitable driver classAction:Consider the following: If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).Process finished with exit code 1把这个依赖注释掉就好了 ...

May 6, 2019 · 1 min · jiezi

SpringMVC方法四种类型返回值总结你用过几种

SpringMVC 现在算是 Java 领域的一个基础性框架了,很多人天天用,可是对于 SpringMVC 方法的返回值,你又是否完全清楚呢?今天松哥就来和大家聊一聊 SpringMVC 中四种不同类型的返回值,看看有没有 get 到你的知识盲点? 1. ModelAndView以前前后端不分的情况下,ModelAndView 应该是最最常见的返回值类型了,现在前后端分离后,后端都是以返回 JSON 数据为主了。后端返回 ModelAndView 这个比较容易理解,开发者可以在 ModelAndView 对象中指定视图名称,然后也可以绑定数据,像下面这样: @RequestMapping("/book")public ModelAndView getAllBook() { ModelAndView mv = new ModelAndView(); List<Book> books = new ArrayList<>(); Book b1 = new Book(); b1.setId(1); b1.setName("三国演义"); b1.setAuthor("罗贯中"); books.add(b1); Book b2 = new Book(); b2.setId(2); b2.setName("红楼梦"); b2.setAuthor("曹雪芹"); books.add(b2); //指定数据模型 mv.addObject("bs", books); mv.setViewName("book");//指定视图名 return mv;}返回 ModelAndView ,最常见的两个操作就是指定数据模型+指定视图名 。 2. Void返回值为 void 时,可能是你真的没有值要返回,也可能是你有其他办法,松哥将之归为如下四类,大伙来看下。 2.1 没有值如果确实没有返回值,那就返回 void ,但是一定要注意,此时,方法上需要添加 @ResponseBody 注解,像下面这样: ...

May 6, 2019 · 2 min · jiezi

SpringBoot结合swagger2快速生成简单的接口文档

1. pom.xml中加入依赖 <dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.8.0.RELEASE</version> </dependency>2. 在启动类(即带@SpringBootApplication这个注解的类)上添加@EnableSwagger2Doc注解3. 在application.properties中配置如下swagger.enabled=trueswagger.title=spring-boot-mybatis module apiswagger.description=Starter for swagger 2.xswagger.license=Apache License, Version 2.0swagger.licenseUrl=https://www.apache.org/licenses/LICENSE-2.0.htmlswagger.termsOfServiceUrl=https://github.com/pbw123/spring-boot-demoswagger.contact.name=mqXuswagger.contact.url=https://www.jianshu.com/u/2f60beddf923swagger.contact.email=2635225112@qq.comswagger.base-package=com.springboot.mybatis.controllerswagger.base-path=/**swagger.exclude-path=/error, /ops/**经我多次尝试application.properties中不加任何swagger配置也可生成文档进行正常测试 另一种用swagger自动生成文档的方式请前往个人网站

May 5, 2019 · 1 min · jiezi

SpringBootVue前后端分离开发首秀

需求:读取数据库的数据展现到前端页面技术栈:后端有主要有SpringBoot,lombok,SpringData JPA,Swagger,跨域,前端有Vue和axios不了解这些技术的可以去入门一下lombok入门swagger入门[SpringData JPA入门]()配置:mysql 8.0.11,IntelliJ IDEA 2017.1.2,HBuilderX 1.9.3首先创建一个Spring Boot项目,目录结构如下:在pom.xml中加入如下依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.1.4.RELEASE</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency> </dependencies>application.properties配置#端口server.port=8888#连接数据库的配置spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.password=Panbing936@spring.datasource.username=rootspring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 #SpringData JPA的配置spring.jpa.hibernate.ddl-auto=updatespring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect实体类User.java@Entity@Datapublic class User{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column(length = 55) private String name; private String avatarURL;}接口UserMapper.java继承JpaRepositorypublic interface UserMapper extends JpaRepository<User,Integer> {}Controller.java@RestController@RequestMapping(value = "/api",produces = APPLICATION_JSON_VALUE)@Api(description = "用户管理")public class UserController { @Autowired private UserMapper userMapper; @ApiOperation(value = "用户列表",notes = "查寻所有已注册过的用户信息") @RequestMapping(value = "getAll",method = RequestMethod.GET) public List<User> getAll() { return userMapper.findAll(); }}SwaggerConfig.java@Configuration@EnableSwagger2public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("cn.niit.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("Spring Boot中使用Swagger2实现前后端分离开发") .description("此项目只是练习如何实现前后端分离开发的小Demo") .termsOfServiceUrl("https://www.jianshu.com/u/2f60beddf923") .contact("WEN") .version("1.0") .build(); }}WebConfig.java是实现跨域的配置,务必记得@Configurationclass WebMvcConfigurer extends WebMvcConfigurerAdapter { //跨域配置 @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override //重写父类提供的跨域请求处理的接口 public void addCorsMappings(CorsRegistry registry) { //添加映射路径 registry.addMapping("/**") //放行哪些原始域 .allowedOrigins("*") //是否发送Cookie信息 .allowCredentials(true) //放行哪些原始域(请求方式) .allowedMethods("GET", "POST", "PUT", "DELETE") //放行哪些原始域(头部信息) .allowedHeaders("*") //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息) .exposedHeaders("Header1", "Header2"); } }; }}点击localhost:8888/swagger-ui.html查看生成的接口文档,测试一下返回数据没有问题,接着可以根据文档开发前端代码了用HBuilderX新建一个test.html页面,具体代码如下 ...

May 5, 2019 · 2 min · jiezi

由表生成代码mybatisgenerator入门

application.properties## mapper xml 文件地址mybatis.mapper-locations=classpath*:mapper/*Mapper.xml##数据库urlspring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=utf8&useSSL=false##数据库用户名spring.datasource.username=root##数据库密码spring.datasource.password=1234##数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver#Mybatis Generator configuration#dao类和实体类的位置mybatis.project =src/main/java#mapper文件的位置mybatis.resources=src/main/resourcesgeneratorConfig.xml<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><!-- 配置生成器 --><generatorConfiguration> <!--执行generator插件生成文件的命令: call mvn mybatis-generator:generate -e --> <!-- 引入配置文件 --> <properties resource="application.properties"/> <!--classPathEntry:数据库的JDBC驱动,换成你自己的驱动位置 可选 --> <!--<classPathEntry location="D:\generator_mybatis\mysql-connector-java-5.1.24-bin.jar" /> --> <!-- 一个数据库一个context --> <!--defaultModelType="flat" 大数据字段,不分表 --> <context id="MysqlTables" targetRuntime="MyBatis3Simple" defaultModelType="flat"> <!-- 自动识别数据库关键字,默认false,如果设置为true,根据SqlReservedWords中定义的关键字列表; 一般保留默认值,遇到数据库关键字(Java关键字),使用columnOverride覆盖 --> <property name="autoDelimitKeywords" value="true" /> <!-- 生成的Java文件的编码 --> <property name="javaFileEncoding" value="utf-8" /> <!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; --> <property name="beginningDelimiter" value="`" /> <property name="endingDelimiter" value="`" /> <!-- 格式化java代码 --> <property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/> <!-- 格式化XML代码 --> <property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/> <plugin type="org.mybatis.generator.plugins.SerializablePlugin" /> <plugin type="org.mybatis.generator.plugins.ToStringPlugin" /> <!-- 注释 --> <commentGenerator> <property name="suppressAllComments" value="true"/><!-- 是否取消注释 --> <property name="suppressDate" value="true" /> <!-- 是否生成注释代时间戳--> <!--<property name="addRemarkComments" value="true"/>--> </commentGenerator> <!-- jdbc连接 --> <jdbcConnection driverClass="${spring.datasource.driver-class-name}" connectionURL="${spring.datasource.url}" userId="${spring.datasource.username}" password="${spring.datasource.password}" /> <!-- 类型转换 --> <javaTypeResolver> <!-- 是否使用bigDecimal, false可自动转化以下类型(Long, Integer, Short, etc.) --> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- 生成实体类地址 --> <javaModelGenerator targetPackage="cn.niit.entity" targetProject="${mybatis.project}" > <property name="enableSubPackages" value="false"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- 生成mapxml文件 --> <sqlMapGenerator targetPackage="mapper" targetProject="${mybatis.resources}" > <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- 生成mapxml对应client,也就是接口dao --> <javaClientGenerator targetPackage="cn.niit.dao" targetProject="${mybatis.project}" type="XMLMAPPER" > <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- table可以有多个,每个数据库中的表都可以写一个table,tableName表示要匹配的数据库表,也可以在tableName属性中通过使用%通配符来匹配所有数据库表,只有匹配的表才会自动生成文件 --> <table tableName="%" enableCountByExample="true" enableUpdateByExample="true" enableDeleteByExample="true" enableSelectByExample="true" selectByExampleQueryId="true"> <property name="useActualColumnNames" value="false" /> <!-- 数据库表主键 --> <generatedKey column="id" sqlStatement="Mysql" identity="true" /> </table> </context></generatorConfiguration>pom.xml加入插件 <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <executions> <execution> <id>mybatis-generator</id> <phase>deploy</phase> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <!-- Mybatis-Generator 工具配置文件的位置 --> <configurationFile>src/main/resources/mybatis-generator/generatorConfig.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.2</version> </dependency> </dependencies> </plugin>点击插件即可生成代码个人网站 ...

May 5, 2019 · 2 min · jiezi

springboot六springboot与webflux结合初探

spring-cloud-gateway 的ReactorHttpHandlerAdapter这几天看了看spring-cloud-gateway的请求处理流程,因为之前一直用的springboot1.x和spring4,一开始对spring-cloud-gateway的处理流程有点懵逼,找不到入口,后来跟了代码,在网上找了点资料,发现spring-cloud-gateway的入口在ReactorHttpHandlerAdapter的apply方法 public class ReactorHttpHandlerAdapter implements BiFunction<HttpServerRequest, HttpServerResponse, Mono<Void>> { private static final Log logger = HttpLogging.forLogName(ReactorHttpHandlerAdapter.class); private final HttpHandler httpHandler; public ReactorHttpHandlerAdapter(HttpHandler httpHandler) { Assert.notNull(httpHandler, "HttpHandler must not be null"); this.httpHandler = httpHandler; } @Override public Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) { NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(reactorResponse.alloc()); try { ReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory); ServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory); if (request.getMethod() == HttpMethod.HEAD) { response = new HttpHeadResponseDecorator(response); } return this.httpHandler.handle(request, response) .doOnError(ex -> logger.trace(request.getLogPrefix() + "Failed to complete: " + ex.getMessage())) .doOnSuccess(aVoid -> logger.trace(request.getLogPrefix() + "Handling completed")); } catch (URISyntaxException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to get request URI: " + ex.getMessage()); } reactorResponse.status(HttpResponseStatus.BAD_REQUEST); return Mono.empty(); } }}该方法的作用就是把接收到的HttpServerRequest或者最终需要返回的HttpServerResponse,包装转换为ReactorServerHttpRequest和ReactorServerHttpResponse。 ...

May 5, 2019 · 2 min · jiezi

Spring-Boot-编程思想-核心篇勘误汇总

如果您在阅读《Spring Boot 编程思想 - 核心篇》或示例练习的过程中发现了其中错误或提出建议,请将内容提交至【勘误汇】,小马哥将勘误或建议内容汇总到此,修正后的内容将在后续的书籍发行中体现,并刊登勘误贡献者 Github ID。 笔者水平有限,行文的过程中错误无法避免,为此深表歉意。基本信息书名:《Spring Boot 编程思想 - 核心篇》 ISBN:978-7-121-36039-8 版次:2019 年 3 月第 1 版勘误列表更新时间:2019-05-05位置错误描述修正描述类型贡献者来源修正版次XIII页表达约定“本书的讨论内容可能对相同事务出现不同的表述方式”应该采用【事物】而非【事务】编写landy8530#3 XVIII页“igApplicationContext();”部分换行了,这一行不应该注释掉,但是被“//”注释了。”移除 “//” 注释符号编写cabbageXun#3 12页正文第1句“控制台输入内容”应调整为“控制台输出内容”编写aaa448579123#3 15页倒数第5行“tree-Dincludes=”应该增加空格“tree -Dincludes”排版verichenn#16 21页第1行“8080/HTTP”,缺少空格“8080/ HTTP”排版Cyric-Cao#7 28页正文最后1行“\json _pp”多出空格应修改为“\json_pp”排版darkness463#3 36页正文第9行“java-jar”应该增加空格“java -jar”排版verichenn#16 36页正文第11行“java-jar”应该增加空格“java -jar”排版verichenn#16 38页正文6行“页面文件位于...”原稿中并无此句排版verichenn#16 38页正文7行“页面文件位于...”原稿中并无此句排版verichenn#16 39页第2行“org.springframe work.boot” 多出空格移除空格排版stackfing#3 40页第1行“org.springframework.boot.Spring Application”org.springframework.boot.SpringApplication排版Yuhuiyang-Dev#3 43页第7行“故符合xxxx.startWith(BOOT_INF_LIB)的判断”“故符合xxxx.startWith(BOOT_INF_CLASSES)的判断”描述nosqlcoco#3 42页第4行“在IDEA中同时按下command+O”command+O 调整为 command+n描述Yuhuiyang-Dev#3 50页正文第3行“sun.net.www.proto col.jar.Handler”多出空格应该调整为“sun.net.www.protocol.jar.Handler”排版wallfacers#3 58页正文第一行“Stater”应调整为“Starter”拼写verichenn#16 58页倒数第2行“烦琐”应调整为“繁琐”描述caixingjava#17 68页正文10行“Spring IO Platorm”应调整为“Spring IO Platform”拼写JamesDragon#13 73页第6行“类似于Spring Boot FAR JAR”应调整为“修正Spring Boot FAT JAR”拼写xkcoding#3 79页最后1行“spring-boot- starter-undertow”多出空格应调整为““spring-boot- starter-undertow””排版verichenn#16 89页正文第6行“Application Context 实现”“Application Context 实现”调整为“ApplicationContext 实现”排版miaoo92#3 91页第1行“当前 WebServer 实现类为 org.springframework.boot.web.embedded.undertow.UndertowWebServer”应纳入控制台输出部分,而非正文排版xkcoding#8 92页倒数第2段“当前 WebServer 实现类为 org.springframework.boot.web.embedded.undertow.UndertowWebServer”不应出现在正文部分应纳入控制台输出部分,而非正文排版xkcoding#8 93页第2行“替换默认的 Jetty Web Server”此处应为“默认的 Netty Web Server”描述liaozan#3 93页引文出现“spring-boot-star ter-tomcat”的单词starter分开和“spring-boot-starter-undertow” 中的unde rtow单词分开移除空格排版zhengjiangming#3 94页正文第1段“当前 WebServer 实现类为 org.springframework.boot.web.embedded.jetty.JettyWebServer”不应出现在正文部分应纳入控制台输出部分,而非正文排版xkcoding#8 95页正文第2段“当前 WebServer 实现类为 org.springframework.boot.web.embedded.tomcat.TomcatWebServer”不应出现在正文部分应纳入控制台输出部分,而非正文排版xkcoding#8 98页第2行“A single @Springboot Application” 一个注解被分开2个词了移除中间的空格排版porscheYong#3 103页正文第2段“尽管 @CompoentScan 仅关注于 @Component”,@CompoentScan 拼写错误“@CompoentScan” 修改为“@ComponentScan”拼写xkcoding#9 106页正文第2段“当前 WebServer 实现类为 org.springframework.boot.web.embedded.tomcat.TomcatWebServer”不应出现在正文部分应纳入控制台输出部分,而非正文排版xkcoding#8 106页倒数正文第4行“组件转配”应该是“组件装配”“组件转配”调整为“组件装配”编写liaozan#3 110页倒数第3行“其Bean对象的行文”描述错误调整为“其Bean对象的行为”描述alonecong#3 112页正文11行“所谓GLIB提升并非是@bean对象提供的”“GLIB”调整为“CGLIB”拼写bilaisheng#5 112页倒数2行“@Confiugraion”拼写错误应调整为“@Configuration”拼写verichenn#16 113页正文第2行“这些“starer”提供自动装配”“starer”调整为“starter”拼写bilaisheng#6 113正文第6行“@Condtinal注解写错”“@Condtinal”应调整为“@Conditional”拼写redaness#3 117页正文第3行“src/main/resource”应调整为“src/main/resources”拼写bilaisheng#14 130页英文第1行“by using the @Valueannotation”没有空格“@Valueannotation” 调整为 “@Value annotation”排版331829683#3 131页引文第二段“application. properties”多了一个空格应调整“application.properties”排版MoPei#3 133页正文第6行“ 注解(如@Server)”应调整为“ 注解(如@Service)”编写porscheYong#3 133页正文第10行“Annotation @Confiugraion”应调整为“Annotation @Configuration”拼写porscheYong#3 133页正文第12行“Annotation @Confiugraion”应调整为“Annotation @Configuration”拼写porscheYong#3 145页正文第9行“其中原委在将Web篇中展开讨论”应调整为“其中原委将在Web篇中展开讨论””编写loupXing#3 151页正文第5行“@CompoentScan”单词拼写有误应调整为“@ComponentScan”拼写runyan#3 151页倒数第4行“@CompoentScan”单词拼写有误应调整为“@ComponentScan”拼写runyan#3 151页倒数第2行“@CompoentScan”单词拼写有误应调整为“@ComponentScan”拼写runyan#3 152页正文第3行“@CompoentScan”单词拼写有误应调整为“@ComponentScan”拼写runyan#3 153页倒数第二个表格“配属属性注解如下表示”应调整为“配置属性注解如下表示”编写porscheYong#3 159页“context左边缺少一个‘<’”context左边增加‘<’排版yxzjerryfish#3 163页正文第3行“API 为 BeanDefinition Parser”应调整为“API 为 BeanDefinitionParser”排版porscheYong#3 166页正文第6行“classpath:thinking/in/spring/boot/**/.class”应调整为“classpath:thinking/in/spring/boot//.class”排版redaness#3 178页“左边的内容”将“2.5.6.SEC03” 与 “3.0.0.RELEASE” 对调编写alonecong#3 180页对比图片顺序错误图片顺序对调图片顺序redaness#3 226页表格“EnableWebFlux缺少@”“EnableWebFlux”前增加 @编写wqyblue3316#3 239页正文第6行“实现类 AnnotationConfigBeanDefini tionParser”多一个空格“AnnotationConfigBeanDefini tionParser” 调整为 “AnnotationConfigBeanDefinitionParser”排版RockFishChina#3 240页正文第4行“Ordered.HIGHT ST_PRECEDENCE”多一个空格应调整为“Ordered.HIGHEST_PRECEDENCE”排版porscheYong#3 259页正文第7行“ServletContextListener#onStartup”应调整为“ServletContainerInitializer#onStartup”编写zhongqinzhen#3 270页正文倒数第5行“表示语法环境”应调整为“表示预发环境”编写MoPei#3 283页正文第1行“@ConditionalOn Bean ”多一个空格应调整为“@ConditionalOnBean ”排版nosqlcoco#3 283页倒数第1行“ConditionOnSystem Property”应调整为“ConditionOnSystemProperty”排版nosqlcoco#3 286页正文第3行“作为 @conditional 条件装配的元注解”应调整为“作为条件装配的元注解”编写porscheYong#3 294页第2行“它可能会造成默写Spring Boot应用的错误”“默写”修改成“某些”编写RockFishChina 296页正文第3行“作为Spring Boot最核心注解的@SpringBootApp lication”将“@SpringBootApp lication” 调整为“@SpringBootApplication”排版old-king#3 296页正文第6行“不过也妨”应调整为“不过也无妨”编写RockFishChina#3 307页第1行“移除排名类名单”“移除排除类名单”编写liaozan#3 326页倒数第2行“将制定的自动装配”应调整为“将指定的自动装配”编写liaozan#3 406页正文第3行“newSpringApplication”应调整为“new SpringApplication”排版LeeDamon#3 517页正文倒数第5行“#createEnvironm ent()”应调整为“#createEnvironment()”排版codingma#3 593页正文第2行“#initia lValue() 方法”应调整为“#initialValue() 方法”排版codingma#3 优化列表更新时间:2019-05-05位置缺陷/不足修正类型贡献者来源修正版次155页最后一段“当任何组件标注它时”“所标注的任何组件”表述RockFishChina#3 432页“11.1.4 这一行 上下篇幅,“事件/监听” ,“事件/监听器模式”,"事件监听器接口",这个字段,有些 带“/” ,有些没带“/” ”添加到在前言 XIII 页中的“表达约定” 中表述codingma#3 36页“class文件不是存放在WEB-INF/classes目录”“class文件不正是存放在WEB-INF/classes目录”表达zhongqinzhen#3 贡献者名单更新时间:2019-05-05参与勘误贡献者名单包括未收录的人员,将以 GitHub 为 ID 的方式记录(排名不分先后): ...

May 5, 2019 · 1 min · jiezi

spring-boot介绍及使用详解

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。 Spring boot的特点 创建独立的Spring应用程序嵌入的Tomcat,无需部署WAR文件简化Maven配置自动配置Spring提供生产就绪型功能,如指标,健康检查和外部配置绝对没有代码生成和对XML没有要求配置Spring boot的优点 spring boot 可以支持你快速的开发出 restful 风格的微服务架构 自动化确实方便,做微服务再合适不过了,单一jar包部署和管理都非常方便。只要系统架构设计合理,大型项目也能用,加上nginx负载均衡,轻松实现横向扩展 spring boot 要解决的问题, 精简配置是一方面, 另外一方面是如何方便的让spring生态圈和其他工具链整合(比如redis, email, elasticsearch) Spring boot的使用 1、maven配置文件 <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springboot.sample</groupId> <artifactId>spring-boot-sample</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>spring-boot-sample</name> <description>Spring Boot Sample Web Application</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.2.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MYSQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- Spring Boot JDBC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>2、application类 ...

May 4, 2019 · 2 min · jiezi

测试类中的静态内部类识别不到NoSuchBeanDefinitionException解决

问题 测试类里面的静态内部类识别不到 @RunWith(SpringRunner.class)@SpringBootTestpublic class FooTest { @Autowired private Bar bar; @Test public void bar() { } @Component public static class Bar { }}但执行bar测试方法的时候 报错: Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.my.app.dao.FooTest$Bar' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1654) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE] 解决 添加两个额外的注解 @RunWith(SpringRunner.class)@SpringBootTest@ComponentScan@ImportAutoConfigurationpublic class FooTest

May 4, 2019 · 1 min · jiezi

如何优雅地在-Spring-Boot-中使用自定义注解AOP-切面统一打印出入参日志

欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 《Java 核心知识整理&面试.pdf》资源链接!!个人网站: https://www.exception.site/springboot/spring-boot-aop-web-request 其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 Controller 方法中的方法为切面,也就是说切死了,如果说我们不想让某个接口打印出入参日志,就办不到了;Controller 包层级过深时,导致很多包下的接口切不到;今天主要说说如何通过自定义注解的方式,在 Spring Boot 中来实现 AOP 切面统一打印出入参日志。小伙伴们可以收藏一波。 废话不多说,进入正题 ! 目录一、先看看切面日志输出效果 二、添加 AOP Maven 依赖 三、自定义日志注解 四、配置 AOP 切面 五、怎么使用呢? 六、对于文件上传好使不? 七、只想在开发环境和测试环境中使用? 八、多切面如何指定优先级? 一、先看看切面日志输出效果在看看实现方法之前,我们先看下切面日志输出效果咋样: 从上图中可以看到,每个对于每个请求,开始与结束一目了然,并且打印了以下参数: URL: 请求接口地址;Description: 接口的中文说明信息;HTTP Method: 请求的方法,是 POST, GET, 还是 DELETE 等;Class Method: 被请求的方法路径 : 包名 + 方法名;IP: 请求方的 IP 地址;Request Args: 请求入参,以 JSON 格式输出;Response Args: 响应出参,以 JSON 格式输出;Time-Consuming: 请求耗时,以此估算每个接口的性能指数;怎么样?看上去效果还不错呢?接下来看看,我们要如何一步一步实现它呢? ...

May 3, 2019 · 3 min · jiezi

那些年我们用过的定时调度

定时调度作为后端开发人员,我们总会遇到这样的业务场景:每周同步一批数据;每半个小时检查一遍服务器运行状况;每天早上八点给用户发送一份包含今日待办事项的邮件,等等。 这些场景中都离不开“定时器”,就像一个定好时间规则的闹钟,它会在指定时间触发,执行我们想要定义的调度任务。那么我们今天就来数一下,那些年我们用过的“定时调度”。 1. job (oracle)从刚工作就一直使用oracle数据库,最早接触的定时任务就是oracle数据库的job。job有定时执行的功能,可以在指定的时间点或每天的某个时间点自行执行任务。 而且oracle重新启动后,job会继续运行,不用重新启动。 而且job的机制非常完备,可以查询相关的表或视图,查询job的定时规则和执行情况。缺点是作为oracle数据库层面的工具,自定义功能扩展,二次开发的难度比较大。 1.1 创建job DECLARE job NUMBER;BEGIN sys.dbms_job.submit(job => job, what => 'prc_name;', --执行的存储过程的名字 next_date => to_date('22-11-2013 09:09:41', 'dd-mm-yyyy hh24:mi:ss'), --下一次执行时间 interval =>'sysdate+1/24'); --每天24小时,即每小时运行prc_name过程一次END;-- job参数是输出参数,由submit()过程返回的binary_ineger,这个值用来唯一标识一个工作。一般定义一个变量接收,可以去user_jobs视图查询job值。-- what参数是将被执行的PL/SQL代码块,存储过程名称等。-- next_date参数指识何时将运行这个工作。-- interval参数何时这个工作将被重执行1.2 删除job DECLAREBEGIN dbms_job.remove(1093); -- 1093为当前需要删除的 job 值 COMMIT;END;1.3 查询job -- 查询当前用户的jobselect * from user_jobs;-- 查询所有jobselect * from dba_jobs;-- 查询所有运行中的jobselect * from dba_jobs_running;2. crontab (linux)crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务 工具,并且会自动启动crond进程,crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。 cron是服务名称,crond是后台进程,crontab则是定制好的计划任务表。大部分linux系统默认都安装了cron,可以检查一下。 -- 检查Crontab工具是否安装crontab -l-- 检查crond服务是否启动service crond status-- centos安装yum install vixie-cronyum install crontabscrontab基本操作命令 ...

May 3, 2019 · 2 min · jiezi

如何在低版本的-Spring-中快速实现类似自动配置的功能

感谢您的阅读,本文由 杨斌的博客 版权所有。如若转载,请注明出处:杨斌的博客(https://y0ngb1n.github.io/a/c...) 在 Spring 4 后才引入了 @Conditional 等条件注解,它是 Spring Boot 中实现自动配置的最大功臣!那么问题来了:如果我们还在使用 Spring 3.x 的老版本,这时候要怎么实现一个自动配置呢?代码托管于 GitHub,欢迎 Star 需求和问题核心的诉求现存系统,不打算重构Spring 版本为 3.x,也不打算升级版本和引入 Spring Boot期望能够在少改代码的前提下实现功能增强比如说: 希望能够给全站统一添加上日志记录(如:RPC 框架 Web 调用的摘要信息、数据库访问层的摘要信息),这个其实是个通用的功能。我们引用了一些基础设施,并想对这些基础设施的功能作进一步的增强,这时候就应该从框架的层面来解决这个问题。面临的问题3.x 的 Spring 没有条件注解因为没有条件注解,所以我们不清楚在什么时候 需要/不需要 配置这些东西 无法自动定位需要加载的自动配置此时我们没有办法像 Spring Boot 的自动配置那样让框架自动加载我们的配置,我们要使用一些别的手段让 Spring 可以加载到我们定制的这些功能。 核心解决思路条件判断通过 BeanFactoryPostProcessor 进行判断Spring 为我们提供了一个扩展点,我们可以通过 BeanFactoryPostProcessor 来解决条件判断的问题,它可以让我们在 BeanFactory 定义完之后、Bean 的初始化之前对我们这些 Bean 的定义做一些后置的处理。可以在这个时候对我们的 Bean 定义做判断,看看当前 存在/缺少 哪些 Bean 的定义,还可以增加一些 Bean 的定义 —— 加入一些自己定制的 Bean。 配置加载编写 Java Config 类引入配置类 通过 component-scan通过 XML 文件 import可以考虑编写自己的 Java Config 类,并把它加到 component-scan 里面,然后想办法让现在系统的 component-scan 包含我们编写的 Java Config 类;也可以编写 XML 文件,如果当前系统使用 XML 的方式,那么它加载的路径上是否可以加载我们的 XML 文件,如果不行就可以使用手动 import 这个文件。 ...

May 3, 2019 · 3 min · jiezi

如何定制自己的起步依赖-Spring-Boot-Starter

感谢您的阅读,本文由 杨斌的博客 版权所有。如若转载,请注明出处:杨斌的博客(https://y0ngb1n.github.io/a/c...) 在这里我们一起动手实现一个属于自己的起步依赖代码托管于 GitHub,欢迎 Star 主要内容主要加入两个模块,一个是与自动配置相关的模块,如果你的依赖需要做自动配置,那么我们可以在里面写上自动配置。另一个是 starter 模块,它里面就是一些依赖项,首先就是指向我们的 autoconfigure 模块的一个依赖,另外就是当前这个 Starter 所自己需要的依赖项。 autoconfigure 模块,包含自动配置代码starter 模块,包含指向自动配置模块的依赖及其他相关依赖这里说明一下,autoconfigure 并不是必须的,如果当前这个模块并不需要什么自动配置,就可以把它去掉。而 Spring Boot 相关的那些自动配置很多都是集中在 spring-boot-autoconfigure 里面的,所以它只要依赖了 spring-boot-starter,那么就会自动地加入这些 Autoconfigure。 命名方式一般是建议在前面加上一个前缀,主要是与 Spring Boot 官方的那些依赖做区分,如下所示: xxx-spring-boot-autoconfigurexxx-spring-boot-starter这样就可以定义一个你自己的 Spring Boot Starter 了。 一些注意事项不要使用 spring-boot 作为依赖的前缀如果你这样做了,会和 Spring Boot 官方的那些依赖混在一起,从而导致不好辨认。 不要使用 spring-boot 的配置命名空间另外如果你有一些配置,这里也是建议不要使用 Spring Boot 已经在使用的配置命名空间。比方说它里面有 server、management 相关的这些配置项,那么你就不要再使用以 server、management 命名前缀的配置了。 starter 中仅添加必要的依赖在当前这个 Starter 中只加入必要的依赖就可以了。也许这个要求有点苛克,但这里的建议是希望你的依赖不多不少、正正好好,你要使用到哪些依赖就只加这些依赖就好;如果没有必要加进去的就可以去掉它,这样的好处是可以减少最终打出来的包里面的依赖。 声明对 spring-boot-starter 的依赖如果有需要的可以在这个 Starter 当中加入 spring-boot-starter 这个依赖。这个并不是必须的,因为我们现在很多的工程本身就是 spring-boot 的一个项目,所以它本身就添加了对 spring-boot-starter 的依赖。这个要看你的需要来决定一下是否要添加。 撸起袖子加油干下面我们来看看都有哪些方式可以实现自动配置 ...

May 3, 2019 · 2 min · jiezi

配置-spring-boot-2X-支持-prometheus-metrics

前言实际项目中,提供metrics接口,对接公司的监控系统,增加服务的可观察性,是一个基本的要求。在spring boot 1.X 中集成prometheus metrics,非常简单。但是spring boot 2.X 颇费周折。因为prometheus官方提供的prometheus-client-java不兼容spring boot 2.X 。需要借助micrometer。 步骤1:引入所需的包 在pom.xml文件中增加如下: <!-- Spring boot actuator to expose metrics endpoint --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- Micormeter core dependecy --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-core</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>2: 增加相关配置 在 application.yml中增加如下设置: management: endpoints: web: exposure: include: ["metrics","prometheus"] endpoint: metrics: enabled: true prometheus: enabled: true metrics: export: prometheus: enabled: truePS: 如果想获取其他的metrics,可以设置include: ["*"] 3:运行查看metrics 运行项目,访问 http://localhost:8090/actuator,可看到如下: ...

April 30, 2019 · 4 min · jiezi

SpringBoot集成RabbitMQ死信队列

介绍死信队列:没有被及时消费的消息存放的队列,消息没有被及时消费有以下几点原因:1.有消息被拒绝(basic.reject/ basic.nack)并且requeue=false2.队列达到最大长度3.消息TTL过期 场景 1.小时进入初始队列,等待30分钟后进入5分钟队列2.消息等待5分钟后进入执行队列3.执行失败后重新回到5分钟队列4.失败5次后,消息进入2小时队列5.消息等待2小时进入执行队列6.失败5次后,将消息丢弃或做其他处理 使用安装MQ使用docker方式安装,选择带mangement的版本 docker pull rabbitmq:managementdocker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:management访问 localhost: 15672,默认账号密码guest/guest 项目配置(1)创建springboot项目(2)在application.properties配置文件中配置mq连接信息 spring.rabbitmq.host=localhostspring.rabbitmq.port=5672spring.rabbitmq.username=guestspring.rabbitmq.password=guest(3)队列配置 package com.df.ps.mq;import org.springframework.amqp.core.Binding;import org.springframework.amqp.core.BindingBuilder;import org.springframework.amqp.core.Queue;import org.springframework.amqp.core.TopicExchange;import org.springframework.amqp.rabbit.connection.ConnectionFactory;import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;import org.springframework.beans.factory.annotation.Autowire;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.HashMap;import java.util.Map;@Configurationpublic class MqConfig { //time @Value("${spring.df.buffered.min:120}") private int springdfBufferedTime; @Value("${spring.df.high-buffered.min:5}") private int springdfHighBufferedTime; @Value("${spring.df.low-buffered.min:120}") private int springdfLowBufferedTime; // 30min Buffered Queue @Value("${spring.df.queue:spring-df-buffered-queue}") private String springdfBufferedQueue; @Value("${spring.df.topic:spring-df-buffered-topic}") private String springdfBufferedTopic; @Value("${spring.df.route:spring-df-buffered-route}") private String springdfBufferedRouteKey; // 5M Buffered Queue @Value("${spring.df.high-buffered.queue:spring-df-high-buffered-queue}") private String springdfHighBufferedQueue; @Value("${spring.df.high-buffered.topic:spring-df-high-buffered-topic}") private String springdfHighBufferedTopic; @Value("${spring.df.high-buffered.route:spring-df-high-buffered-route}") private String springdfHighBufferedRouteKey; // High Queue @Value("${spring.df.high.queue:spring-df-high-queue}") private String springdfHighQueue; @Value("${spring.df.high.topic:spring-df-high-topic}") private String springdfHighTopic; @Value("${spring.df.high.route:spring-df-high-route}") private String springdfHighRouteKey; // 2H Low Buffered Queue @Value("${spring.df.low-buffered.queue:spring-df-low-buffered-queue}") private String springdfLowBufferedQueue; @Value("${spring.df.low-buffered.topic:spring-df-low-buffered-topic}") private String springdfLowBufferedTopic; @Value("${spring.df.low-buffered.route:spring-df-low-buffered-route}") private String springdfLowBufferedRouteKey; // Low Queue @Value("${spring.df.low.queue:spring-df-low-queue}") private String springdfLowQueue; @Value("${spring.df.low.topic:spring-df-low-topic}") private String springdfLowTopic; @Value("${spring.df.low.route:spring-df-low-route}") private String springdfLowRouteKey; @Bean(autowire = Autowire.BY_NAME, value = "springdfBufferedQueue") Queue springdfBufferedQueue() { int bufferedTime = 1000 * 60 * springdfBufferedTime; return createBufferedQueue(springdfBufferedQueue, springdfHighBufferedTopic, springdfHighBufferedRouteKey, bufferedTime); } @Bean(autowire = Autowire.BY_NAME, value = "springdfHighBufferedQueue") Queue springdfHighBufferedQueue() { int highBufferedTime = 1000 * 60 * springdfHighBufferedTime; return createBufferedQueue(springdfHighBufferedQueue, springdfHighTopic, springdfHighRouteKey, highBufferedTime); } @Bean(autowire = Autowire.BY_NAME, value = "springdfHighQueue") Queue springdfHighQueue() { return new Queue(springdfHighQueue, true); } @Bean(autowire = Autowire.BY_NAME, value = "springdfLowBufferedQueue") Queue springdfLowBufferedQueue() { int lowBufferedTime = 1000 * 60 * springdfLowBufferedTime; return createBufferedQueue(springdfLowBufferedQueue, springdfLowTopic, springdfLowRouteKey, lowBufferedTime); } @Bean(autowire = Autowire.BY_NAME, value = "springdfLowQueue") Queue springdfLowQueue() { return new Queue(springdfLowQueue, true); } @Bean(autowire = Autowire.BY_NAME, value = "springdfBufferedTopic") TopicExchange springdfBufferedTopic() { return new TopicExchange(springdfBufferedTopic); } @Bean Binding springBuffereddf(Queue springdfBufferedQueue, TopicExchange springdfBufferedTopic) { return BindingBuilder.bind(springdfBufferedQueue).to(springdfBufferedTopic).with(springdfBufferedRouteKey); } @Bean(autowire = Autowire.BY_NAME, value = "springdfHighBufferedTopic") TopicExchange springdfHighBufferedTopic() { return new TopicExchange(springdfHighBufferedTopic); } @Bean Binding springHighBuffereddf(Queue springdfHighBufferedQueue, TopicExchange springdfHighBufferedTopic) { return BindingBuilder.bind(springdfHighBufferedQueue).to(springdfHighBufferedTopic).with(springdfHighBufferedRouteKey); } @Bean(autowire = Autowire.BY_NAME, value = "springdfHighTopic") TopicExchange springdfHighTopic() { return new TopicExchange(springdfHighTopic); } @Bean Binding springHighdf(Queue springdfHighQueue, TopicExchange springdfHighTopic) { return BindingBuilder.bind(springdfHighQueue).to(springdfHighTopic).with(springdfHighRouteKey); } @Bean(autowire = Autowire.BY_NAME, value = "springdfLowBufferedTopic") TopicExchange springdfLowBufferedTopic() { return new TopicExchange(springdfLowBufferedTopic); } @Bean Binding springLowBuffereddf(Queue springdfLowBufferedQueue, TopicExchange springdfLowBufferedTopic) { return BindingBuilder.bind(springdfLowBufferedQueue).to(springdfLowBufferedTopic).with(springdfLowBufferedRouteKey); } @Bean(autowire = Autowire.BY_NAME, value = "springdfLowTopic") TopicExchange springdfLowTopic() { return new TopicExchange(springdfLowTopic); } @Bean Binding springLowdf(Queue springdfLowQueue, TopicExchange springdfLowTopic) { return BindingBuilder.bind(springdfLowQueue).to(springdfLowTopic).with(springdfLowRouteKey); } @Bean SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setQueueNames(springdfHighQueue, springdfLowQueue); container.setMessageListener(listenerAdapter); return container; } @Bean MessageListenerAdapter listenerAdapter(IntegrationReceiver receiver) { MessageListenerAdapter adapter = new MessageListenerAdapter(receiver); adapter.setDefaultListenerMethod("receive"); Map<String, String> queueOrTagToMethodName = new HashMap<>(); queueOrTagToMethodName.put(springdfHighQueue, "springdfHighReceive"); queueOrTagToMethodName.put(springdfLowQueue, "springdfLowReceive"); adapter.setQueueOrTagToMethodName(queueOrTagToMethodName); return adapter; } private Queue createBufferedQueue(String queueName, String topic, String routeKey, int bufferedTime) { Map<String, Object> args = new HashMap<>(); args.put("x-dead-letter-exchange", topic); args.put("x-dead-letter-routing-key", routeKey); args.put("x-message-ttl", bufferedTime); // 是否持久化 boolean durable = true; // 仅创建者可以使用的私有队列,断开后自动删除 boolean exclusive = false; // 当所有消费客户端连接断开后,是否自动删除队列 boolean autoDelete = false; return new Queue(queueName, durable, exclusive, autoDelete, args); }}消费者配置package com.df.ps.mq;import com.fasterxml.jackson.databind.ObjectMapper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.amqp.rabbit.core.RabbitTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import java.util.Map;public class MqReceiver { private static Logger logger = LoggerFactory.getLogger(MqReceiver.class); @Value("${high-retry:5}") private int highRetry; @Value("${low-retry:5}") private int lowRetry; @Value("${spring.df.high-buffered.topic:spring-df-high-buffered-topic}") private String springdfHighBufferedTopic; @Value("${spring.df.high-buffered.route:spring-df-high-buffered-route}") private String springdfHighBufferedRouteKey; @Value("${spring.df.low-buffered.topic:spring-df-low-buffered-topic}") private String springdfLowBufferedTopic; @Value("${spring.df.low-buffered.route:spring-df-low-buffered-route}") private String springdfLowBufferedRouteKey; private final RabbitTemplate rabbitTemplate; @Autowired public MqReceiver(RabbitTemplate rabbitTemplate) { this.rabbitTemplate = rabbitTemplate; } public void receive(Object message) { if (logger.isInfoEnabled()) { logger.info("default receiver: " + message); } } /** * 消息从初始队列进入5分钟的高速缓冲队列 * @param message */ public void highReceiver(Object message){ ObjectMapper mapper = new ObjectMapper(); Map msg = mapper.convertValue(message, Map.class); try{ logger.info("这里做消息处理..."); }catch (Exception e){ int times = msg.get("times") == null ? 0 : (int) msg.get("times"); if (times < highRetry) { msg.put("times", times + 1); rabbitTemplate.convertAndSend(springdfHighBufferedTopic,springdfHighBufferedRouteKey,message); } else { msg.put("times", 0); rabbitTemplate.convertAndSend(springdfLowBufferedTopic,springdfLowBufferedRouteKey,message); } } } /** * 消息从5分钟缓冲队列进入2小时缓冲队列 * @param message */ public void lowReceiver(Object message){ ObjectMapper mapper = new ObjectMapper(); Map msg = mapper.convertValue(message, Map.class); try { logger.info("这里做消息处理..."); }catch (Exception e){ int times = msg.get("times") == null ? 0 : (int) msg.get("times"); if (times < lowRetry) { rabbitTemplate.convertAndSend(springdfLowBufferedTopic,springdfLowBufferedRouteKey,message); }else{ logger.info("消息无法被消费..."); } } }}

April 30, 2019 · 3 min · jiezi