diff --git a/README.md b/README.md index e1ea1fa..5424e7a 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ - github | Spring Boot 系列 :[https://github.com/gf-huanchupk/SpringBootLearning](https://github.com/gf-huanchupk/SpringBootLearning),记得star哦! - github | Spring Cloud 系列 :[https://github.com/gf-huanchupk/SpringCloudLearning](https://github.com/gf-huanchupk/SpringCloudLearning),记得star哦! +### RocketMQ 系列 +- [RocketMQ 教程 | RocketMQ 简介](http://blog.itwolfed.com/blog/98) +- [RocketMQ 教程 | RocketMQ 安装](http://blog.itwolfed.com/blog/99) +- [RocketMQ 教程 | RocketMQ 常用消息类型](http://blog.itwolfed.com/blog/101) ### Spring Boot 系列 @@ -25,6 +29,7 @@ - [Spring Boot 教程 | Spring Boot Security OAuth2 实现支持JWT令牌的授权服务器](http://blog.itwolfed.com/blog/17) - [Spring Boot 教程 | Spring Boot Actuator 整合 Prometheus](http://blog.itwolfed.com/blog/18) - [Spring Boot 教程 | Spring Boot 集成 Seata 解决分布式事务问题](http://blog.itwolfed.com/blog/19) +- [Spring Boot 教程 | Spring Boot 使用 JSR303 实现参数验证](http://blog.itwolfed.com/blog/97) ### Spring Cloud 系列 @@ -83,7 +88,7 @@

- +
欢迎扫码或微信搜索公众号《程序员果果》关注我,更多精彩内容不能错过 ~~

diff --git a/springboot-jsr303/.gitignore b/springboot-jsr303/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/springboot-jsr303/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/springboot-jsr303/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-jsr303/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..24c6680 --- /dev/null +++ b/springboot-jsr303/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println( "- Downloader started" ); + File baseDirectory = new File( args[0] ); + System.out.println( "- Using base directory: " + baseDirectory.getAbsolutePath() ); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File( baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH ); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream( mavenWrapperPropertyFile ); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load( mavenWrapperPropertyFileInputStream ); + url = mavenWrapperProperties.getProperty( PROPERTY_NAME_WRAPPER_URL, url ); + } catch (IOException e) { + System.out.println( "- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'" ); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println( "- Downloading from: " + url ); + + File outputFile = new File( baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH ); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'" ); + } + } + System.out.println( "- Downloading to: " + outputFile.getAbsolutePath() ); + try { + downloadFileFromURL( url, outputFile ); + System.out.println( "Done" ); + System.exit( 0 ); + } catch (Throwable e) { + System.out.println( "- Error downloading" ); + e.printStackTrace(); + System.exit( 1 ); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null) { + String username = System.getenv( "MVNW_USERNAME" ); + char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray(); + Authenticator.setDefault( new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication( username, password ); + } + } ); + } + URL website = new URL( urlString ); + ReadableByteChannel rbc; + rbc = Channels.newChannel( website.openStream() ); + FileOutputStream fos = new FileOutputStream( destination ); + fos.getChannel().transferFrom( rbc, 0, Long.MAX_VALUE ); + fos.close(); + rbc.close(); + } + +} diff --git a/springboot-jsr303/.mvn/wrapper/maven-wrapper.jar b/springboot-jsr303/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/springboot-jsr303/.mvn/wrapper/maven-wrapper.jar differ diff --git a/springboot-jsr303/.mvn/wrapper/maven-wrapper.properties b/springboot-jsr303/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/springboot-jsr303/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/springboot-jsr303/pom.xml b/springboot-jsr303/pom.xml new file mode 100644 index 0000000..e42f567 --- /dev/null +++ b/springboot-jsr303/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.0.RELEASE + + + com.itwolfed + springboot-jsr303 + 0.0.1-SNAPSHOT + springboot-jsr303 + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/springboot-jsr303/src/main/java/com/itwolfed/SpringbootJsr303Application.java b/springboot-jsr303/src/main/java/com/itwolfed/SpringbootJsr303Application.java new file mode 100644 index 0000000..c45a832 --- /dev/null +++ b/springboot-jsr303/src/main/java/com/itwolfed/SpringbootJsr303Application.java @@ -0,0 +1,13 @@ +package com.itwolfed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringbootJsr303Application { + + public static void main(String[] args) { + SpringApplication.run( SpringbootJsr303Application.class, args ); + } + +} diff --git a/springboot-jsr303/src/main/java/com/itwolfed/controller/UserController.java b/springboot-jsr303/src/main/java/com/itwolfed/controller/UserController.java new file mode 100644 index 0000000..a3832ab --- /dev/null +++ b/springboot-jsr303/src/main/java/com/itwolfed/controller/UserController.java @@ -0,0 +1,21 @@ +package com.itwolfed.controller; + +import com.itwolfed.entity.User; +import com.itwolfed.utils.Result; +import com.itwolfed.valid.Groups; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("/user") +public class UserController { + + @PostMapping("") + public Result save (@Validated(Groups.Add.class) User user) { + return Result.ok(); + } + +} diff --git a/springboot-jsr303/src/main/java/com/itwolfed/entity/User.java b/springboot-jsr303/src/main/java/com/itwolfed/entity/User.java new file mode 100644 index 0000000..3de6ffe --- /dev/null +++ b/springboot-jsr303/src/main/java/com/itwolfed/entity/User.java @@ -0,0 +1,30 @@ +package com.itwolfed.entity; + +import com.itwolfed.valid.Groups; +import com.itwolfed.valid.ListValue; +import lombok.Data; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import javax.validation.constraints.Pattern; + + +@Data +public class User { + + @Null(message = "新增不需要指定id" , groups = Groups.Add.class) + @NotNull(message = "修改需要指定id" , groups = Groups.Update.class) + private Integer id; + @NotBlank(message = "用户名不能为空") + @NotNull + private String username; + @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合") + private String password; + @Email + private String email; + @ListValue( message = "性别应指定相应的值" , vals = {1,2} , groups = {Groups.Add.class , Groups.Update.class}) + private Integer gender; + +} diff --git a/springboot-jsr303/src/main/java/com/itwolfed/exception/GlobalExceptionControllerAdvice.java b/springboot-jsr303/src/main/java/com/itwolfed/exception/GlobalExceptionControllerAdvice.java new file mode 100644 index 0000000..03abdd3 --- /dev/null +++ b/springboot-jsr303/src/main/java/com/itwolfed/exception/GlobalExceptionControllerAdvice.java @@ -0,0 +1,36 @@ +package com.itwolfed.exception; + +import com.itwolfed.utils.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.HashMap; +import java.util.Map; + +/** + * 统一异常处理 + */ +@Slf4j +@RestControllerAdvice(basePackages = "com.itwolfed.controller") +public class GlobalExceptionControllerAdvice { + + @ExceptionHandler(value= {MethodArgumentNotValidException.class , BindException.class}) + public Result handleVaildException(Exception e){ + BindingResult bindingResult = null; + if (e instanceof MethodArgumentNotValidException) { + bindingResult = ((MethodArgumentNotValidException)e).getBindingResult(); + } else if (e instanceof BindException) { + bindingResult = ((BindException)e).getBindingResult(); + } + Map errorMap = new HashMap<>(16); + bindingResult.getFieldErrors().forEach((fieldError)-> + errorMap.put(fieldError.getField(),fieldError.getDefaultMessage()) + ); + return Result.build(400 , "非法参数 !" , errorMap); + } + +} diff --git a/springboot-jsr303/src/main/java/com/itwolfed/utils/Result.java b/springboot-jsr303/src/main/java/com/itwolfed/utils/Result.java new file mode 100644 index 0000000..fb0600f --- /dev/null +++ b/springboot-jsr303/src/main/java/com/itwolfed/utils/Result.java @@ -0,0 +1,63 @@ +package com.itwolfed.utils; + + +import lombok.Getter; +import lombok.Setter; + +/** + * 自定义响应结构 + */ +@Getter +@Setter +public class Result { + + // 响应业务状态 + private Integer status; + + // 响应消息 + private String msg; + + // 响应中的数据 + private Object data; + + + public Result() { + + } + + public static Result ok(Object data) { + + return new Result(200 , data); + } + + public static Result ok() { + return new Result(200 , null); + } + + public static Result build(Integer status, String msg) { + return new Result(status, msg, null); + } + + + public static Result build(Integer status, String msg , Object data) { + return new Result(status,msg,data); + } + + public Result(Integer status,String msg) { + this.status = status; + this.msg = msg; + } + + + public Result(Integer status,Object data) { + this.status = status; + this.data = data; + } + + public Result(Integer status, String msg, Object data) { + this.status = status; + this.msg = msg; + this.data = data; + } + +} \ No newline at end of file diff --git a/springboot-jsr303/src/main/java/com/itwolfed/valid/Groups.java b/springboot-jsr303/src/main/java/com/itwolfed/valid/Groups.java new file mode 100644 index 0000000..621049d --- /dev/null +++ b/springboot-jsr303/src/main/java/com/itwolfed/valid/Groups.java @@ -0,0 +1,7 @@ +package com.itwolfed.valid; + + +public class Groups { + public interface Add{} + public interface Update{} +} diff --git a/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValue.java b/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValue.java new file mode 100644 index 0000000..f3effce --- /dev/null +++ b/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValue.java @@ -0,0 +1,25 @@ +package com.itwolfed.valid; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Documented +@Constraint(validatedBy = { ListValueConstraintValidator.class }) +@Target({ METHOD, FIELD, ANNOTATION_TYPE }) +@Retention(RUNTIME) +public @interface ListValue { + String message() default ""; + + Class[] groups() default { }; + + Class[] payload() default { }; + + int[] vals() ; +} \ No newline at end of file diff --git a/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValueConstraintValidator.java b/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValueConstraintValidator.java new file mode 100644 index 0000000..1b99a94 --- /dev/null +++ b/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValueConstraintValidator.java @@ -0,0 +1,37 @@ +package com.itwolfed.valid; + + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.HashSet; +import java.util.Set; + +public class ListValueConstraintValidator implements ConstraintValidator { + + private Set set = new HashSet<>(); + /** + * 初始化方法 + */ + @Override + public void initialize(ListValue constraintAnnotation) { + + int[] vals = constraintAnnotation.vals(); + for (int val : vals) { + set.add(val); + } + + } + + /** + * 判断是否校验成功 + * + * @param value 需要校验的值 + * @param context + * @return + */ + @Override + public boolean isValid(Integer value, ConstraintValidatorContext context) { + + return set.contains(value); + } +} diff --git a/springboot-jsr303/src/main/resources/application.yml b/springboot-jsr303/src/main/resources/application.yml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/springboot-jsr303/src/main/resources/application.yml @@ -0,0 +1 @@ + diff --git a/springboot-jsr303/src/test/java/com/itwolfed/SpringbootJsr303ApplicationTests.java b/springboot-jsr303/src/test/java/com/itwolfed/SpringbootJsr303ApplicationTests.java new file mode 100644 index 0000000..c6cfbc9 --- /dev/null +++ b/springboot-jsr303/src/test/java/com/itwolfed/SpringbootJsr303ApplicationTests.java @@ -0,0 +1,13 @@ +package com.itwolfed; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringbootJsr303ApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/springboot-rocketmq-message/.gitignore b/springboot-rocketmq-message/.gitignore new file mode 100644 index 0000000..1e4385b --- /dev/null +++ b/springboot-rocketmq-message/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +mvnw.cmd +mvnw +.mvn + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/springboot-rocketmq-message/pom.xml b/springboot-rocketmq-message/pom.xml new file mode 100644 index 0000000..310f5b8 --- /dev/null +++ b/springboot-rocketmq-message/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.10.RELEASE + + + com.itwolfed + springboot-rocketmq-message + 0.0.1-SNAPSHOT + springboot-rocketmq-message + Demo project for Spring Boot + + 1.8 + + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.rocketmq + rocketmq-spring-boot-starter + 2.1.1 + + + org.projectlombok + lombok + 1.18.16 + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/RocketmqMessageNormalApplication.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/RocketmqMessageNormalApplication.java new file mode 100644 index 0000000..43e68eb --- /dev/null +++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/RocketmqMessageNormalApplication.java @@ -0,0 +1,13 @@ +package com.itwolfed; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class RocketmqMessageNormalApplication { + + public static void main(String[] args) { + SpringApplication.run( RocketmqMessageNormalApplication.class, args ); + } + +} diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Consumer.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Consumer.java new file mode 100644 index 0000000..090ac08 --- /dev/null +++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Consumer.java @@ -0,0 +1,18 @@ +package com.itwolfed.msg; + +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RocketMQMessageListener(topic = "Topic-Normal",consumerGroup = "consumer-msg-group") +public class Consumer implements RocketMQListener { + + @Override + public void onMessage(String message) { + log.info("Receive message:{}" , message); + } + +} diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/DelayConsumer.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/DelayConsumer.java new file mode 100644 index 0000000..f85a4c7 --- /dev/null +++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/DelayConsumer.java @@ -0,0 +1,18 @@ +package com.itwolfed.msg; + +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RocketMQMessageListener(topic = "Topic-Delay",consumerGroup = "consumer-delay-group") +public class DelayConsumer implements RocketMQListener { + + @Override + public void onMessage(String message) { + log.info("Receive message:{}" , message); + } + +} diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/OrderConsumer.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/OrderConsumer.java new file mode 100644 index 0000000..994aa25 --- /dev/null +++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/OrderConsumer.java @@ -0,0 +1,18 @@ +package com.itwolfed.msg; + +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RocketMQMessageListener(topic = "Topic-Order",consumerGroup = "consumer-order-group") +public class OrderConsumer implements RocketMQListener { + + @Override + public void onMessage(String message) { + log.info("Receive message:{}" , message); + } + +} diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Producer.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Producer.java new file mode 100644 index 0000000..71b9709 --- /dev/null +++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Producer.java @@ -0,0 +1,128 @@ +package com.itwolfed.msg; + +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.LocalTransactionState; +import org.apache.rocketmq.client.producer.SendCallback; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.client.producer.TransactionSendResult; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +@Component +public class Producer { + + + @Autowired + private RocketMQTemplate rocketMQTemplate; + + /** + * 普通消息 + */ + public void sendMsg() throws Exception { + + Message message = new Message( + // 普通消息所属的Topic + "Topic-Normal", + // Message Tag可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列 RocketMQ 版的服务器过滤。 + "TagA", + // Message Body可以是任何二进制形式的数据。 + "Hello MQ".getBytes() + ); + rocketMQTemplate.getProducer().send( message ); + // 等同于上面的方式(常用) + //rocketMQTemplate.convertAndSend("Topic-Normal:TagA","Hello MQ".getBytes()); + } + + /** + * 异步发送普通消息 + */ + public void sendAsyncMsg() { + Map map = new HashMap<>(); + map.put( "name", "zs" ); + map.put( "age", 20 ); + + rocketMQTemplate.asyncSend( "Topic-Normal", map, new SendCallback() { + @Override + public void onSuccess(SendResult sendResult) { + // 消息发送成功。 + log.info( "async send success" ); + } + + @Override + public void onException(Throwable throwable) { + // 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。 + log.info( "async send fail" ); + } + } ); + } + + /** + * 顺序消息 + */ + public void sendOrderlyMsg() { + //根据指定的hashKey按顺序发送 + for (int i = 0; i < 1000; i++) { + String orderId = "biz_" + i % 10; + // 分区顺序消息中区分不同分区的关键字段,Sharding Key与普通消息的key是完全不同的概念。 + // 全局顺序消息,该字段可以设置为任意非空字符串。 + String shardingKey = String.valueOf( orderId ); + try { + SendResult sendResult = rocketMQTemplate.syncSendOrderly( "Topic-Order", "send order msg".getBytes(), shardingKey ); + // 发送消息,只要不抛异常就是成功。 + if (sendResult != null) { + System.out.println( new Date() + " Send mq message success . msgId is:" + sendResult.getMsgId() ); + } + } catch (Exception e) { + // 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。 + System.out.println( new Date() + " Send mq message failed" ); + e.printStackTrace(); + } + } + } + + /** + * 延时消息 + */ + public void sendDelayMsg() { + rocketMQTemplate.syncSend( "Topic-Delay", + MessageBuilder.withPayload( "Hello MQ".getBytes() ).build(), + 3000, + //设置延时等级3,这个消息将在10s之后发送(现在只支持固定的几个时间,详看delayTimeLevel) + //messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h"; + 3 ); + } + + /** + * 批量消息 + */ + public void sendBatchMsg(List messages) { + rocketMQTemplate.syncSend( "springboot-rocketmq", messages ); + + } + + /** + * 事务消息 + */ + public void sendTransactionMsg(){ + TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction( + "Topic-Tx:TagA", + MessageBuilder.withPayload( "Hello MQ transaction===".getBytes() ).build(), + null ); + SendStatus sendStatus = transactionSendResult.getSendStatus(); + LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState(); + + System.out.println( new Date() + " Send mq message status "+ sendStatus +" , localTransactionState "+ localTransactionState ); + } + + +} diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxConsumerListener.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxConsumerListener.java new file mode 100644 index 0000000..84975de --- /dev/null +++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxConsumerListener.java @@ -0,0 +1,22 @@ +package com.itwolfed.msg; + +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + + +@Component +@Slf4j +@RocketMQMessageListener(topic = "Topic-Tx",consumerGroup = "consumer-tx-group") +public class TxConsumerListener implements RocketMQListener { + + + + @Override + public void onMessage(String message) { + + log.info("Receive message:{}" , message); + } + +} diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxProducerListener.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxProducerListener.java new file mode 100644 index 0000000..3eb5c8a --- /dev/null +++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxProducerListener.java @@ -0,0 +1,45 @@ +package com.itwolfed.msg; + +import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; +import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; +import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; +import org.springframework.messaging.Message; +import org.springframework.stereotype.Component; + + +@Component +@RocketMQTransactionListener +public class TxProducerListener implements RocketMQLocalTransactionListener { + + @Override + public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { + // 执行本地事务 + System.out.println("TX message listener execute local transaction"); + RocketMQLocalTransactionState result; + try { + // 业务代码( 例如下订单 ) + result = RocketMQLocalTransactionState.COMMIT; + } catch (Exception e) { + System.out.println("execute local transaction error"); + result = RocketMQLocalTransactionState.UNKNOWN; + } + return result; + } + + @Override + public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { + // 检查本地事务( 例如检查下订单是否成功 ) + System.out.println("TX message listener check local transaction"); + RocketMQLocalTransactionState result; + try { + //业务代码( 根据检查结果,决定是COMMIT或ROLLBACK ) + result = RocketMQLocalTransactionState.COMMIT; + } catch (Exception e) { + // 异常就回滚 + System.out.println("check local transaction error"); + result = RocketMQLocalTransactionState.ROLLBACK; + } + return result; + } + +} diff --git a/springboot-rocketmq-message/src/main/resources/application.yml b/springboot-rocketmq-message/src/main/resources/application.yml new file mode 100644 index 0000000..fa3511d --- /dev/null +++ b/springboot-rocketmq-message/src/main/resources/application.yml @@ -0,0 +1,7 @@ +server: + port: 8080 + +rocketmq: + name-server: 172.16.250.129:9876 + producer: + group: myGroup \ No newline at end of file diff --git a/springboot-rocketmq-message/src/test/java/com/itwolfed/RocketmqMessageNormalApplicationTests.java b/springboot-rocketmq-message/src/test/java/com/itwolfed/RocketmqMessageNormalApplicationTests.java new file mode 100644 index 0000000..29f75c4 --- /dev/null +++ b/springboot-rocketmq-message/src/test/java/com/itwolfed/RocketmqMessageNormalApplicationTests.java @@ -0,0 +1,62 @@ +package com.itwolfed; + + +import com.itwolfed.msg.Producer; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + + +@RunWith(SpringRunner.class) +@SpringBootTest +public class RocketmqMessageNormalApplicationTests { + + @Autowired + private Producer producer; + + + + /** + * 测试普通消息 + */ + @Test + public void sendMsg() throws Exception { + producer.sendMsg(); + } + + /** + * 异步发送普通消息 + */ + @Test + public void sendAsyncMsg() { + producer.sendAsyncMsg(); + } + + /** + * 顺序消息 + */ + @Test + public void sendOrderlyMsg() { + producer.sendOrderlyMsg(); + } + + /** + * 延时消息 + */ + @Test + public void sendDelayMsg() { + producer.sendDelayMsg(); + } + + /** + * 事务消息 + */ + @Test + public void sendTransactionMsg(){ + producer.sendTransactionMsg(); + } + + +}