首页 文章 精选 留言 我的

精选列表

搜索[springboot],共4364篇文章
优秀的个人博客,低调大师

springboot统一表单数据校验

统一表单数据校验 在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有两个麻烦: 验证代码繁琐,重复劳动 方法内代码显得冗长 每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码 hibernate validator(官方文档)提供了一套比较完善、便捷的验证实现方式。 spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。 于是编写了一套校验工具类,并配置了切面做统一的校验,无需在每次手动调用校验方法。 校验工具类 ValidatorUtil import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.groups.Default; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * @author lism * @date 2018年8月29日10:25:34 * bean 校验工具类 */ public class ValidatorUtil { private static Validator validator = Validation.buildDefaultValidatorFactory() .getValidator(); /** * 校验bean * @param bean * @param <T> * @return */ public static <T> List<ValidateBean> validate(T bean) { Set<ConstraintViolation<T>> constraintViolations = validator.validate(bean, Default.class); return errors2ValidateBeanList(constraintViolations); } /** * 校验属性 * @param bean * @param property * @param <T> * @return */ public static <T> List<ValidateBean> validateProperty(T bean, String property) { Set<ConstraintViolation<T>> constraintViolations = validator.validateProperty(bean, property, Default.class); return errors2ValidateBeanList(constraintViolations); } /** * 校验属性值 * @param bean * @param property * @param propertyValue * @param <T> * @return */ public static <T> List<ValidateBean> validateValue(T bean, String property, Object propertyValue) { Set<? extends ConstraintViolation<?>> constraintViolations = validator.validateValue(bean.getClass(), property, propertyValue, Default.class); return errors2ValidateBeanList(constraintViolations); } private static <T> List<ValidateBean> errors2ValidateBeanList(Set<? extends ConstraintViolation<?>> errors) { List<ValidateBean> validateBeans = new ArrayList<>(); if (errors != null && errors.size() > 0) { for (ConstraintViolation<?> cv : errors) { //这里循环获取错误信息,可以自定义格式 String property = cv.getPropertyPath().toString(); String message = cv.getMessage(); validateBeans.add(new ValidateBean(property, message)); } } return validateBeans; } } 校验结果辅助类 ValidateBean import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class ValidateBean { private String property; private String message; @Override public String toString() { final StringBuilder sb = new StringBuilder("{"); sb.append("property='").append(property).append('\''); sb.append(", message='").append(message).append('\''); sb.append('}'); return sb.toString(); } } 统一数据校验和异常处理切面 import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import java.util.List; import java.util.Optional; /** * 统一数据校验和异常处理 * 控制层无需再进行数据校验和异常捕获 * @author lism */ @Aspect @Component @Slf4j @Order(1) public class ValidateBeanAspect { /** * 定义一个切入点 */ @Pointcut("execution(* org.lism..controller..*.*(..))") private void anyMethod() { } @Around("anyMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); if (args.length > 0) { Optional<List<ValidateBean>> optional = Arrays.stream(args).filter(arg -> { return !(arg == null ||arg instanceof HttpServletRequest || arg instanceof HttpServletResponse); }).map(arg -> { return ValidatorUtil.validate(arg); }).filter(validateBeans -> { return validateBeans.size() > 0; }).findFirst(); if (optional.isPresent()) { return new ResponseBean(RTCodeEnum.CODE_FAIL.getCode(), optional.get().toString()); } } try { Object proceed = pjp.proceed(args); if (proceed instanceof ResponseBean) { return proceed; } else { return new ResponseBean(proceed, RTCodeEnum.CODE_200); } } catch (BaseException e) { RequestContextUtil.writeToResponse(new ResponseBean<>(e.getCode(), e.getMessage()).toString()); log.error(e.getMessage()); // return new ResponseBean<>(e.getCode(), e.getMessage()); return null; } catch (Exception e) { log.error(e.getMessage()); RequestContextUtil.writeToResponse(new ResponseBean<>(RTCodeEnum.CODE_FAIL.getCode(), e.getMessage())); // return new ResponseBean<>(RTCodeEnum.CODE_FAIL.getCode(), e.getMessage()); return null; } } @Before("anyMethod()") public void doBefore(JoinPoint pjp) throws Throwable { } @AfterReturning("anyMethod()") public void doAfterReturning(JoinPoint pjp) throws Throwable { } } RequestContextUtil工具类 import com.alibaba.fastjson.JSON; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * RequestContext工具类 * @Author lism * @Date 2018/8/29 17:04 */ public class RequestContextUtil { public static ServletRequestAttributes getRequestAttributes() { return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); } /** * 获取Request * @return */ public static HttpServletRequest getRequest() { //TODO:单元测试的时候,还是会得到requestAttributes,不等于null ServletRequestAttributes requestAttributes = getRequestAttributes(); if (requestAttributes != null) { return requestAttributes.getRequest(); } else { return null; } } /** * 获取Response * @return */ public static HttpServletResponse getResponse() { //TODO:单元测试的时候,还是会得到requestAttributes,不等于null ServletRequestAttributes requestAttributes = getRequestAttributes(); if (requestAttributes != null) { return requestAttributes.getResponse(); } else { return null; } } /** * 获取SessionId * @return */ public static String getSessionId() { //TODO:单元测试的时候,还是会得到requestAttributes,不等于null ServletRequestAttributes requestAttributes = getRequestAttributes(); if (requestAttributes != null) { return requestAttributes.getSessionId(); } else { return null; } } /** * 往前端写数据 * @param object */ public static void writeToResponse(Object object) { PrintWriter writer = null; try { HttpServletResponse response = RequestContextUtil.getResponse(); response.setCharacterEncoding("utf-8"); response.setHeader("Content-type", "text/html;charset=utf-8"); writer = response.getWriter(); writer.write(JSON.toJSONString(object)); writer.flush(); } catch (IOException e) { e.printStackTrace(); }finally { writer.close(); } } } 返回code 枚举类 import com.alibaba.fastjson.JSONObject; /** */ public enum RTCodeEnum { CODE_OK(0, "OK"), // CODE_DONE(1, "Done"), // CODE_FAIL(-1, "Failed"), CODE_PARAM_ERROR(300, "Input Param Error"), CODE_TOKEN_ERROR(301, "Token Validation Error"), CODE_CAPTCHA_ERROR(302, "验证码错误,请重试"), CODE_DATA_VALIDATE_FAILED(303, "数据校验未通过"), CODE_STATE_EXIST(305, "请勿重复请求"), // Data Issue: 4** CODE_400(400, "服务404"), CODE_DATA_ERROR_PAGETIME_EXPIRE(401, "页面超时不可用,请刷新重试"), // System Service Issue: 5** CODE_SERVICE_NOT_AVAILABLE(500, "系统服务不可用,请联系管理员"), CODE_200(200,"成功"), CODE_401(401,"未登录,需要登录"), CODE_405(405, "权限不足"), CODE_406(406,"客户端请求接口参数不正确或缺少参数"), CODE_501(501,"服务器接口错误"), CODE_999(999,"保留码"); private int code; private String desc; RTCodeEnum(int code, String desc) { this.code = code; this.desc = desc; } public JSONObject toJSON() { JSONObject jsonObject = new JSONObject(); jsonObject.put("code", code); jsonObject.put("desc", desc); return jsonObject; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } ResponseBean工具类 import com.alibaba.fastjson.annotation.JSONField; import com.google.common.base.MoreObjects; import java.util.Date; /** * Created by lism 2018/8/23. */ public class ResponseBean<T> { private int code = 200; private String msg; private T data; @JSONField(format="yyyy-MM-dd HH:mm:ss") private Date date = new Date(); public static ResponseBean me(Object data){ return new ResponseBean(data); } public ResponseBean(T data) { this.data = data; } public ResponseBean(T data, RTCodeEnum rtCodeEnum) { this.data = data; this.msg = rtCodeEnum.getDesc(); this.code = rtCodeEnum.getCode(); } public ResponseBean(RTCodeEnum rtCodeEnum) { this.msg = rtCodeEnum.getDesc(); this.code = rtCodeEnum.getCode(); } public ResponseBean(T data, int code, String msg) { this.data = data; this.code = code; this.msg = msg; } public ResponseBean(int code, String msg) { this.code = code; this.msg = msg; } public ResponseBean() { } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public T getData() { return data; } public void setData(T data) { this.data = data; } public Date getDate() { return date; } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("code", code) .add("msg", msg) .add("data", data) .toString(); } } 1.校验实体类型参数 编写测试实体 import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.persistence.Entity; import javax.persistence.Table; import javax.validation.constraints.NotEmpty; @Entity @Table(name = "t_subject") @Data public class Subject extends BaseEntityModel { @NotEmpty(message = "专题名不能为空") @Length(min = 1, max = 20, message = "专题名1-20个字符之间") private String name; private String keyword; private String[] docType; private String startTime; private String endTime; } 测试控制器 @RestController @RequestMapping(value = "/subject") public class SubjectController { @RequestMapping(value = "/", method = RequestMethod.POST, produces = "application/json", consumes = "application/json") @ResponseBody @Override public ResponseBean create(@RequestBody Subject model) { return super.create(model); } 输入一个空的名子进行测试,可以看到返回结果 v2.png 2.校验RequestParam 类型请求参数 ValidatorUtil方式无法校验RequestParam 请求方法,在BaseController 加上@Validated注解 @RestController @Validated public class BaseController { } 在子控制器写测试方法 @RestController @RequestMapping(value = "/subject") public class SubjectController extends BaseController { /** * test4 * @return */ @RequestMapping(value = "/test3/", method = RequestMethod.GET, produces = "application/json") @ResponseBody public void test3(HttpServletRequest request, HttpServletResponse response, @Length(min = 1, max = 20, message = "专题名1-20个字符之间") @RequestParam String name) { log.info("test4"); } 通过测试看到:验证不通过时,抛出了ConstraintViolationException异常,所以我们在ValidateBeanAspect中使用统一的捕获异常是可以捕获到异常,并调用RequestContextUtil.writeToResponse方法将异常写到前台。 测试结果: v1.png 参考 https://www.cnblogs.com/mr-yang-localhost/p/7812038.htmlhttps://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/

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

SpringBoot|第四章:日志管理

Spring Boot在所有内部日志中使用Commons Logging,但是默认配置也提供了对常用日志的支持,如:Java Util Logging,Log4J,Log4J2和Logback。每种Logger都可以通过配置使用控制台或者文件输出日志内容。 格式化日志 默认的日志输出如下: 12016-04-1308:23:50.120INFO 37397--- [ main] org.hibernate.Version : HHH000412: Hibernate Core {4.3.11.Final} 输出内容元素具体如下: 时间日期 — 精确到毫秒 日志级别 — ERROR, WARN, INFO, DEBUG or TRACE 进程ID 分隔符 —---标识实际日志的开始 线程名 — 方括号括起来(可能会截断控制台输出) Logger名 — 通常使用源代码的类名 日志内容 控制台输出 在Spring Boot中默认配置了ERROR、WARN和INFO级别的日志输出到控制台。 我们可以通过两种方式切换至DEBUG级别: 在运行命令后加入--debug标志,如:$ java -jar myapp.jar --debug 在application.properties中配置debug=true,该属性置为true的时候,核心Logger(包含嵌入式容器、hibernate、spring)会输出更多内容,但是你自己应用的日志并不会输出为DEBUG级别。 多彩输出 如果你的终端支持ANSI,设置彩色输出会让日志更具可读性。通过在application.properties中设置spring.output.ansi.enabled参数来支持。 NEVER:禁用ANSI-colored输出(默认项) DETECT:会检查终端是否支持ANSI,是的话就采用彩色输出(推荐项) ALWAYS:总是使用ANSI-colored格式输出,若终端不支持的时候,会有很多干扰信息,不推荐使用 文件输出 Spring Boot默认配置只会输出到控制台,并不会记录到文件中,但是我们通常生产环境使用时都需要以文件方式记录。 若要增加文件输出,需要在application.properties中配置logging.file或logging.path属性。 logging.file,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.log logging.path,设置目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log 日志文件会在10Mb大小的时候被截断,产生新的日志文件,默认级别为:ERROR、WARN、INFO 级别控制 在Spring Boot中只需要在application.properties中进行配置完成日志记录的级别控制。 配置格式:logging.level.*=LEVEL logging.level:日志级别控制前缀,*为包名或Logger名 LEVEL:选项TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF 举例: logging.level.com.didispace=DEBUG:com.didispace包下所有class以DEBUG级别输出 logging.level.root=WARN:root日志以WARN级别输出 自定义日志配置 由于日志服务一般都在ApplicationContext创建前就初始化了,它并不是必须通过Spring的配置文件控制。因此通过系统属性和传统的Spring Boot外部配置文件依然可以很好的支持日志控制和管理。 根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载: Logback:logback-spring.xml,logback-spring.groovy,logback.xml,logback.groovy Log4j:log4j-spring.properties,log4j-spring.xml,log4j.properties,log4j.xml Log4j2:log4j2-spring.xml,log4j2.xml JDK (Java Util Logging):logging.properties Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml) 自定义输出格式 在Spring Boot中可以通过在application.properties配置如下参数控制输出格式: logging.pattern.console:定义输出到控制台的样式(不支持JDK Logger) logging.pattern.file:定义输出到文件的样式(不支持JDK Logger) 如果你现在在JAVA这条路上挣扎,也想在IT行业拿高薪,可以参加我们的训练营课程,选择最适合自己的课程学习,技术大牛亲授,7个月后,进入名企拿高薪。我们的课程内容有:Java工程化、高性能及分布式、高性能、高架构、性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点。如果你想拿高薪的,想学习的,想就业前景好的,想跟别人竞争能取得优势的,想进阿里面试但担心面试不过的,你都可以来,q群号为:835638062 注:加群要求 1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加。 2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加。 3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加。 4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。 5.阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!

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

SpringBoot实战(三)之使用RestFul Web服务

一、导入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.springframework</groupId> <artifactId>gs-consuming-rest</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.8.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 二、构建实体 Quote.java package hello; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class Quote { private String type; private Value value; public Quote() { } public String getType() { return type; } public void setType(String type) { this.type = type; } public Value getValue() { return value; } public void setValue(Value value) { this.value = value; } @Override public String toString() { return "Quote{" + "type='" + type + '\'' + ", value=" + value + '}'; } } Value.java package hello; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class Value { private Long id; private String quote; public Value() { } public Long getId() { return this.id; } public String getQuote() { return this.quote; } public void setId(Long id) { this.id = id; } public void setQuote(String quote) { this.quote = quote; } @Override public String toString() { return "Value{" + "id=" + id + ", quote='" + quote + '\'' + '}'; } } @JsonIgnore注解用来忽略某些字段,可以用在Field或者Getter方法上,用在Setter方法时,和Filed效果一样。这个注解只能用在POJO存在的字段要忽略的情况,不能满足现在需要的情况。 @JsonIgnoreProperties(ignoreUnknown = true),将这个注解写在类上之后,就会忽略类中不存在的字段,可以满足当前的需要。这个注解还可以指定要忽略的字段。使用方法如下: @JsonIgnoreProperties({ "internalId", "secretKey" })指定的字段不会被序列化和反序列化。 三、编写启动类 package hello; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.client.RestTemplate; public class Application { private static final Logger log = LoggerFactory.getLogger(Application.class); public static void main(String args[]) { RestTemplate restTemplate = new RestTemplate(); Quote quote = restTemplate.getForObject("http://gturnquist-quoters.cfapps.io/api/random", Quote.class); log.info(quote.toString()); } } 运行后控制台显示结果为: package hello; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class Application { private static final Logger log = LoggerFactory.getLogger(Application.class); public static void main(String args[]) { SpringApplication.run(Application.class); } @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder.build(); } @Bean public CommandLineRunner run(RestTemplate restTemplate) throws Exception { return args -> { Quote quote = restTemplate.getForObject( "http://gturnquist-quoters.cfapps.io/api/random", Quote.class); log.info(quote.toString()); }; } } 它RestTemplateBuilder是由Spring注入的,如果你使用它来创建一个,RestTemplate那么你将受益于Spring Boot中带有消息转换器和请求工厂的所有自动配置。我们还将其提取RestTemplate为a@Bean以使其更容易测试(可以通过这种方式更容易地进行模拟)。 最后执行mvn clean install或者mvn install,生成jar包,通过cmd命令执行java -jar gs-consume-rest-0.1.0.jar 出现如红色标记处,表示成功开发一个RestFul客户端。

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

SpringBoot-11-之从本地到线上

一、本地:[注]现在对于已经有了服务器和域名(已备案)的你,可以继续阅读 1.打包项目为jar 打包项目 2.备份数据库 备份数据库:mysqldump -uroot -p密码 toly>toly.sql 二、服务器: [1]将数据库拷贝到服务器 [2]进入mysql后创建同名数据库:mysql> create database toly [3]使用数据库:mysql> use toly [4]导入数据库:mysql> source 路径名\toly.sql 数据库恢复 运行:需要java环境 java -jar 文件路径 三、访问:把一直以来的localhost换成域名就行了

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

初窥RabbitMQ消息中间及SpringBoot整合

一:RabbitMQ简介 RabbitMQ介绍 RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。 消息中间件最主要的作用是解耦,中间件最标准的用法是生产者生产消息传送到队列,消费者从队列中拿取消息并处理,生产者不用关心是谁来消费,消费者不用关心谁在生产消息,从而达到解耦的目的。在分布式的系统中,消息队列也会被用在很多其它的方面,比如:分布式事务的支持,RPC的调用等等。 RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。RabbitMQ主要是为了实现系统之间的双向解耦而实现的。当生产者大量产生数据时,消费者无法快速消费,那么需要一个中间层。保存这个数据。 AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。 运行机制 通常我们谈到队列服务, 会有三个概念: 发消息者、队列、收消息者,RabbitMQ 在这个基本概念之上, 多做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。 左侧 P 代表 生产者,也就是往 RabbitMQ 发消息的程序。 中间即是 RabbitMQ,其中包括了 交换机 和 队列。 右侧 C 代表 消费者,也就是往 RabbitMQ 拿消息的程序。 那么,其中比较重要的概念有 4 个,分别为:虚拟主机,交换机,队列,和绑定。 虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢?很简单,RabbitMQ当中,用户只能在虚拟主机的粒度进行权限控制。 因此,如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。 交换机:Exchange 用于转发消息,但是它不会做存储 ,如果没有 Queue bind 到 Exchange 的话,它会直接丢弃掉 Producer 发送过来的消息。 这里有一个比较重要的概念:路由键(routing_key) 。消息到交换机的时候,交互机会转发到对应的队列中,那么究竟转发到哪个队列,就要根据该路由键。 绑定:也就是交换机需要和队列相绑定,这其中如上图所示,是多对多的关系。 交换机 交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。交换机有四种类型:Direct, topic, Headers and Fanout Direct:direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去. Topic:按规则转发消息(最灵活) Headers:设置header attribute参数类型的交换机 Fanout:转发消息到所有绑定队列(广播:忽略routing_key ) 一句话总结 RabbitMQ简单来说,就是生产者发送消息到虚拟主机,虚拟主机把消息交给指定的交换机,交换机按照规则扔给消息队列进行存储,消息队列等待消费者来消费。 由此我想到了商品买卖:厂家生产商品卖给批发部,批发部交给指定的超市出售,超市按照售价摆放在门店,并等待顾客上门购买。 完美~~ 二:RabbitMQ安装 因为RabbitMQ是由erlang语言写的,就像Java程序需要jdk环境一样,运行RabbitMQ也需要erlang环境。 1.安装erlang 环境:Centos7.4 下载地址:http://erlang.org/download/ 在Linux终端运行命令行 下载: wget http://erlang.org/download/otp_src_18.3.tar.gz 下载一定要认准otp_src_字样。 解压: tar -zxvf otp_src_18.3.tar.gz 进入解压后的根目录: ./configure --prefix=/usr/local/erlang --enable-hipe --enable-threads --enable-smp-support --enable-kernel-poll --without-javac make && make install 上面有点慢。 把erlang加入环境变量: vi /etc/profile export ERLANG=/usr/local/erlang/erlang export PATH=$ERLANG/bin:$PATH 使环境变量生效 source /etc/profile 然后,我们测试下是否安装成功: [root@yueshutong ~]# erl Erlang/OTP 18 [erts-7.3] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false] Eshell V7.3 (abort with ^G) 1> 2.安装RabbitMQ 下载地址:http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.1/ 下载: wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.1/rabbitmq-server-generic-unix-3.6.1.tar.xz 对于下载xz包进行解压,首先先下载xz压缩工具: yum install xz 对rabbitmq包进行解压: xz -d xz -d rabbitmq-server-generic-unix-3.6.1.tar.xz tar -xvf rabbitmq-server-generic-unix-3.6.1.tar 随后在sbin目录启用MQ管理方式: ./rabbitmq-plugins enable rabbitmq_management #启动后台管理 ./rabbitmq-server -detached #后台运行rabbitmq 添加用户和权限 默认网页guest用户是不允许访问的,需要增加一个用户修改一下权限,代码如下: 添加用户: ./rabbitmqctl add_user admin admin 添加权限: ./rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*" 修改用户角色: ./rabbitmqctl set_user_tags admin administrator 然后就可以远程访问了,然后可直接配置用户权限等信息。 验证 访问http://localhost:15672/,输入admin用户密码,登录成功! 三:整合Spring Boot 新建一个1.5版本的Spring boot项目,选择rabbitmq+web模块。 1.导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> 2.yml配置rabbitmq virtual-host: / 默认就是斜杠,具体查看rabbitmq后台admin栏。如果默认这行不用写。 spring: rabbitmq: host: 127.0.0.1 username: admin password: admin virtual-host: / #可以不用写 3.发送direct消息 direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去. 1.我们在RabbitMQ后台新建一个交换机,demo-direct交换机名,direct交换机类型,Durable持久化: 2.再新建一个消息队列,取名为demo: 3.点击demo-direct交换机进入绑定消息队列demo: 如果没有指定routingkey,消息队列的名称就是routingkey 4.绑定成功后查看: 你可以直接在交换机页面下方的Publish message发送消息,在消息队列的Get message查看消息,不过我们实际生产比较多使用编程: 在Spring Boot创建单元测试 demo-direct交换机名 demo是该交换机绑定的消息队列名 发送消息 @Autowired RabbitTemplate rabbitTemplate; @Test public void contextLoads() { //message需要自己构造一个,定义消息体内容和消息体 //rabbitTemplate.send(exchange,routingkey,message); Map map = new HashMap(); map.put("key","值"); map.put("msg",true); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-direct","demo",map); } 此时发送消息我们在rabbitmq网页发现消息是经序列化后的,我们如果想改变序列化机制为JSON,也很简单,只需要注入一个人Bean: @Configuration public class MyAMQPConfig { @Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } } 然后我们再启动测试,发送。 接收消息 demo是消息队列名,也就是消费者只需要得到消息队列的名字就可以接收队列中的消息。 @Test public void receive(){ Object o = rabbitTemplate.receiveAndConvert("demo"); System.out.println(o.getClass()); System.out.println(o); } 查看打印 class java.util.HashMap {msg=true, key=值} 4.广播fanout消息 转发消息到所有绑定队列(广播:忽略routing_key ) 1.首先我们需要在RabbitMQ后台创建一个广播交换机: 2.再创建一些(demo1,demo2)消息队列,以一个作为演示: 3.进入创建的交换机页面: 4.交换机与消息队列(demo1,demo2)进行绑定: 如果没有指定routingkey,消息队列的名称就是routingkey 在Spring Boot创建单元测试 注意在广播模式下会忽略忽略routing_key 发送消息 @Autowired RabbitTemplate rabbitTemplate; @Test public void send() { Book book = new Book(); book.setName("<西游记>"); book.setAnthony("吴承恩"); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-fanout","",book); } 接收消息 @Test public void receive(){ Object o = rabbitTemplate.receiveAndConvert("demo"); System.out.println(o.getClass()); System.out.println(o); } 打印输出: class cn.zyzpp.rabbitmq.entity.Book Book{name='<西游记>', anthony='吴承恩'} 5.发送topic消息 按规则转发消息(使用通配符) 1.在rabbitmq后台新建一个topic交换机 2.新建一个消息队列demo 3.交换机绑定消息队列,注意此处的路由键Routing key使用了通配符 *表示一个词. #表示零个或多个词. 那我们如何区分几个字母为一个单词呢? 答案是通过”点分”的 routing_key 形式,比如两个单词是 *.demo hello.demo, 如果路由键为demo.#,那可以匹配demo.开头的所有路由键。 4.查看此时的交换机 在Spring Boot创建单元测试 发送消息 @Test public void contextLoads() { //message需要自己构造一个,定义消息体内容和消息体 //rabbitTemplate.send(exchange,routingkey,message); Map map = new HashMap(); map.put("key","topic交换机"); map.put("msg",true); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-topic","demo.hello",map); } 接收消息 @Test public void receive(){ Object o = rabbitTemplate.receiveAndConvert("demo"); System.out.println(o.getClass()); System.out.println(o); } 打印输出 class java.util.HashMap {msg=true, key=topic交换机} 6.代码创建交换机与消息队列 1.上面演示的是通过RabbitMQ网页后台创建,通过编程的方式也非常简单: @Autowired AmqpAdmin amqpAdmin; /** * 代码创建交换机与消息队列并绑定 */ @Test public void createExChange(){ // new TopicExchange("topic.exChange"); // new FanoutExchange("fanout.exChange"); amqpAdmin.declareExchange(new DirectExchange("amqp.exChange"));//创建交换机(remove为删除交换机) System.out.println("单播交换机创建完成"); amqpAdmin.declareQueue(new Queue("amqp.queue",true)); //创建消息队列 amqpAdmin.declareBinding(new Binding("amqp.queue", Binding.DestinationType.QUEUE,"amqp.exChange","amqp.exChange",null));//绑定 } 2.登录后台查看,创建成功! 7.监听消息队列 监听消息队列,当有消息发到消息队列,立马获取并操作。方法也很简单。 1.开启基于注解的Rabbit模式 @EnableRabbit //开启基于注解的Rabbit模式 @SpringBootApplication public class RabbitmqApplication { public static void main(String[] args) { SpringApplication.run(RabbitmqApplication.class, args); } } 2.@RabbitListener注解实现监听 注意此时你的消息队列已经有了demo和demo.news @Service public class BookService { @RabbitListener(queues = "demo") public void receive(Book book){ System.out.println("收到消息:"+ book); } @RabbitListener(queues = "demo.news") public void receiveMess(Message message){ System.out.println("收到消息:"+ message); System.out.println(message.getBody()); //getBody返回的byte[]字节数组 System.out.println(message.getMessageProperties()); } } 然后我们在开启主程序时再运行测试用例: 测试用例 @Test public void send() { Book book = new Book(); book.setName("<西游记>"); book.setAnthony("吴承恩"); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-fanout","",book); } 查看主控制台打印 收到消息:Book{name='<西游记>', anthony='吴承恩'} 测试用例 @Test public void contextLoads() { //message需要自己构造一个,定义消息体内容和消息体 //rabbitTemplate.send(exchange,routingkey,message); Map map = new HashMap(); map.put("key","topic交换机"); map.put("msg",true); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-topic","demo.hello",map); } 查看主控制台打印 收到消息:(Body:'{"msg":true,"key":"topic交换机"}' MessageProperties [headers={__ContentTypeId__=java.lang.Object, __KeyTypeId__=java.lang.Object, __TypeId__=java.util.HashMap}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/json, contentEncoding=UTF-8, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=demo-topic, receivedRoutingKey=demo.hello, receivedDelay=null, deliveryTag=1, messageCount=0, consumerTag=amq.ctag-KRZh2DyNETjEaGSH0JZ2dA, consumerQueue=demo.news]) [B@5332f99e MessageProperties [headers={__ContentTypeId__=java.lang.Object, __KeyTypeId__=java.lang.Object, __TypeId__=java.util.HashMap}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/json, contentEncoding=UTF-8, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=demo-topic, receivedRoutingKey=demo.hello, receivedDelay=null, deliveryTag=1, messageCount=0, consumerTag=amq.ctag-KRZh2DyNETjEaGSH0JZ2dA, consumerQueue=demo.news]

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

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

Rocky Linux

Rocky Linux

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

Sublime Text

Sublime Text

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

用户登录
用户注册