增加README.md文章导航

This commit is contained in:
罗祥 2019-02-01 09:50:53 +08:00
parent 1569c0ad61
commit 4fc9b96f2a
34 changed files with 7953 additions and 7308 deletions

View File

@ -0,0 +1,2 @@
@Profile({"dev","test"})
@ConditionalOnProperty(name = "swagger.enable", havingValue = "true")

View File

@ -0,0 +1,61 @@
<?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.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.heibaiying.springboot</groupId>
<artifactId>spring-boot-swagger2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-swagger2</name>
<description>swagger2 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>
<!--swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,14 @@
package com.heibaiying.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootSwagger2Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootSwagger2Application.class, args);
}
}

View File

@ -0,0 +1,25 @@
package com.heibaiying.springboot.bean;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Product {
private long id;
private String name;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date date;
}

View File

@ -0,0 +1,67 @@
package com.heibaiying.springboot.config;
import com.google.common.base.Predicate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import static com.google.common.base.Predicates.not;
import static springfox.documentation.builders.PathSelectors.regex;
/**
* @author : heibaiying
* @description : Swagger 配置类
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${swagger.enable}")
private boolean swaggerEnable;
/***
* 配置swagger
* 开发和测试环境下可以开启swagger辅助进行调试,而生产环境下可以关闭或者进行相应的权限控制防止接口信息泄露
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(swaggerEnable)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.heibaiying.springboot.controller"))
.paths(PathSelectors.any())
.paths(doFilteringRules())
.build();
}
/***
* 接口文档的描述信息
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("spring boot swagger2 用例")
.description("描述")
.licenseUrl("https://mit-license.org/")
.version("1.0")
.build();
}
/**
* 可以使用正则定义url过滤规则
*/
private Predicate<String> doFilteringRules() {
return not(
regex("/ignore/*")
);
}
}

View File

@ -0,0 +1,84 @@
package com.heibaiying.springboot.controller;
import com.heibaiying.springboot.bean.Product;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import java.util.Date;
/**
* @author : heibaiying
* @description : 产品查询接口
*/
@Slf4j
@Api(value = "产品接口", description = "产品信息接口")
@RestController
public class ProductController {
/***
* 一个标准的swagger注解
*/
@ApiOperation(notes = "查询所有产品", value = "产品查询接口")
@ApiImplicitParams(
@ApiImplicitParam(name = "id", value = "产品编号", paramType = "path", defaultValue = "1")
)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "请求成功"),
@ApiResponse(code = 400, message = "无效的请求"),
@ApiResponse(code = 401, message = "未经过授权认证"),
@ApiResponse(code = 403, message = "已经过授权认证,但是没有该资源对应的访问权限"),
@ApiResponse(code = 404, message = "服务器找不到给定的资源,商品不存在"),
@ApiResponse(code = 500, message = "服务器错误")
})
@GetMapping(value = "/product/{id}", produces = "application/json")
public ResponseEntity<Product> getProduct(@PathVariable long id) {
Product product = new Product(id, "product" + id, new Date());
return ResponseEntity.ok(product);
}
/***
* 如果用实体类接收参数,则用实体类对应的属性名称指定参数
*/
@ApiOperation(notes = "保存产品", value = "产品保存接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "产品编号", paramType = "body", defaultValue = "1"),
@ApiImplicitParam(name = "name", value = "产品名称", paramType = "body"),
@ApiImplicitParam(name = "date", value = "产品生产日期", paramType = "body")
}
)
@PostMapping(value = "/product")
public ResponseEntity<Void> saveProduct(@RequestBody Product product) {
System.out.println(product);
return ResponseEntity.ok().build();
}
/***
* 在配置类中指明了该接口不被扫描到,可以在配置类中使用正则指定某一类符合规则的接口不被扫描到
*/
@ApiOperation(notes = "该接口会被忽略", value = "产品保存接口")
@PostMapping(value = "/ignore")
public ResponseEntity<Product> ignore() {
return ResponseEntity.ok().build();
}
/**
* 不加上任何swagger相关的注解也会被扫描到 如果不希望被扫描到需要用 @ApiIgnore 修饰
*/
@PostMapping(value = "/normal")
public ResponseEntity<Void> normal() {
return ResponseEntity.ok().build();
}
@ApiIgnore
@PostMapping(value = "/apiIgnore")
public ResponseEntity<Void> apiIgnore() {
return ResponseEntity.ok().build();
}
}

View File

@ -0,0 +1 @@
swagger.enable = true

View File

@ -0,0 +1,17 @@
package com.heibaiying.springboot;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootSwagger2ApplicationTests {
@Test
public void contextLoads() {
}
}

View File

@ -1,236 +1,249 @@
# spring AOP注解方式 # spring AOP注解方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-aop">二、spring aop</a><br/>
1. 切面配置位于com.heibaiying.config下AopConfig.java文件 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建待切入接口及其实现类">2.1 创建待切入接口及其实现类</a><br/>
2. 自定义切面位于advice下其中CustomAdvice是标准的自定义切面FirstAdvice和SecondAdvice用于测试多切面共同作用于同一个被切入点时的执行顺序 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-创建自定义切面类">2.2 创建自定义切面类</a><br/>
3. OrderService是待切入方法。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-配置切面">2.3 配置切面</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-测试切面">2.4 测试切面</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-aop-annotation.png"/> </div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25--切面执行顺序">2.5 切面执行顺序</a><br/>
<a href="#附-关于切面表达式的说明">附: 关于切面表达式的说明</a><br/>
## 正文<br/>
### 1.2 依赖说明
## 一、说明
除了spring的基本依赖外需要导入aop依赖包
### 1.1 项目结构说明
```xml
<!--aop 相关依赖--> 1. 切面配置位于com.heibaiying.config下AopConfig.java文件
<dependency> 2. 自定义切面位于advice下其中CustomAdvice是标准的自定义切面FirstAdvice和SecondAdvice用于测试多切面共同作用于同一个被切入点时的执行顺序
<groupId>org.springframework</groupId> 3. OrderService是待切入方法。
<artifactId>spring-aop</artifactId>
<version>${spring-base-version}</version> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-aop-annotation.png"/> </div>
</dependency>
```
### 1.2 依赖说明
## 二、spring aop 除了spring的基本依赖外需要导入aop依赖包
#### 2.1 创建待切入接口及其实现类 ```xml
<!--aop 相关依赖-->
```java <dependency>
public interface OrderService { <groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
Order queryOrder(Long id); <version>${spring-base-version}</version>
</dependency>
Order createOrder(Long id, String productName); ```
}
```
```java ## 二、spring aop
public class OrderServiceImpl implements OrderService {
#### 2.1 创建待切入接口及其实现类
public Order queryOrder(Long id) {
return new Order(id, "product", new Date()); ```java
} public interface OrderService {
public Order createOrder(Long id, String productName) { Order queryOrder(Long id);
// 模拟抛出异常
// int j = 1 / 0; Order createOrder(Long id, String productName);
return new Order(id, "new Product", new Date()); }
} ```
}
```java
``` public class OrderServiceImpl implements OrderService {
#### 2.2 创建自定义切面类 public Order queryOrder(Long id) {
return new Order(id, "product", new Date());
注:@Pointcut的值可以是多个切面表达式的组合 }
```java public Order createOrder(Long id, String productName) {
/** // 模拟抛出异常
* @author : heibaiying // int j = 1 / 0;
* @description : 自定义切面 return new Order(id, "new Product", new Date());
*/ }
@Aspect }
@Component //除了加上@Aspect外 还需要声明为spring的组件 @Aspect只是一个切面声明
public class CustomAdvice { ```
#### 2.2 创建自定义切面类
/**
* 使用 || , or 表示或 注:@Pointcut的值可以是多个切面表达式的组合
* 使用 && , and 表示与
* ! 表示非 ```java
*/ /**
@Pointcut("execution(* com.heibaiying.service.OrderService.*(..)) && !execution(* com.heibaiying.service.OrderService.deleteOrder(..))") * @author : heibaiying
private void pointCut() { * @description : 自定义切面
*/
} @Aspect
@Component //除了加上@Aspect外 还需要声明为spring的组件 @Aspect只是一个切面声明
@Before("pointCut()") public class CustomAdvice {
public void before(JoinPoint joinPoint) {
//获取节点名称
String name = joinPoint.getSignature().getName(); /**
Object[] args = joinPoint.getArgs(); * 使用 || , or 表示或
System.out.println(name + "方法调用前:获取调用参数" + Arrays.toString(args)); * 使用 && , and 表示与
} * ! 表示非
*/
// returning 参数用于指定返回结果与哪一个参数绑定 @Pointcut("execution(* com.heibaiying.service.OrderService.*(..)) && !execution(* com.heibaiying.service.OrderService.deleteOrder(..))")
@AfterReturning(pointcut = "pointCut()", returning = "result") private void pointCut() {
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("后置返回通知结果" + result); }
}
@Before("pointCut()")
@Around("pointCut()") public void before(JoinPoint joinPoint) {
public Object around(ProceedingJoinPoint joinPoint) throws Throwable { //获取节点名称
System.out.println("环绕通知-前"); String name = joinPoint.getSignature().getName();
//调用目标方法 Object[] args = joinPoint.getArgs();
Object proceed = joinPoint.proceed(); System.out.println(name + "方法调用前:获取调用参数" + Arrays.toString(args));
System.out.println("环绕通知-后"); }
return proceed;
} // returning 参数用于指定返回结果与哪一个参数绑定
@AfterReturning(pointcut = "pointCut()", returning = "result")
// throwing 参数用于指定抛出的异常与哪一个参数绑定 public void afterReturning(JoinPoint joinPoint, Object result) {
@AfterThrowing(pointcut = "pointCut()", throwing = "exception") System.out.println("后置返回通知结果" + result);
public void afterThrowing(JoinPoint joinPoint, Exception exception) { }
System.err.println("后置异常通知:" + exception);
} @Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知-前");
@After("pointCut()") //调用目标方法
public void after(JoinPoint joinPoint) { Object proceed = joinPoint.proceed();
System.out.println("后置通知"); System.out.println("环绕通知-后");
} return proceed;
} }
``` // throwing 参数用于指定抛出的异常与哪一个参数绑定
@AfterThrowing(pointcut = "pointCut()", throwing = "exception")
#### 2.3 配置切面 public void afterThrowing(JoinPoint joinPoint, Exception exception) {
System.err.println("后置异常通知:" + exception);
```java }
/**
* @author : heibaiying
* @description : 开启切面配置 @After("pointCut()")
*/ public void after(JoinPoint joinPoint) {
@Configuration System.out.println("后置通知");
@ComponentScan("com.heibaiying.*") }
@EnableAspectJAutoProxy // 开启@Aspect注解支持 等价于<aop:aspectj-autoproxy> }
public class AopConfig {
} ```
```
#### 2.3 配置切面
#### 2.4 测试切面
```java
```java /**
@RunWith(SpringRunner.class) * @author : heibaiying
@ContextConfiguration(classes = AopConfig.class) * @description : 开启切面配置
public class AopTest { */
@Configuration
@ComponentScan("com.heibaiying.*")
@Autowired @EnableAspectJAutoProxy // 开启@Aspect注解支持 等价于<aop:aspectj-autoproxy>
private OrderService orderService; public class AopConfig {
}
@Test ```
public void saveAndQuery() {
orderService.createOrder(1283929319L, "手机"); #### 2.4 测试切面
orderService.queryOrder(4891894129L);
} ```java
@RunWith(SpringRunner.class)
/** @ContextConfiguration(classes = AopConfig.class)
* 多个切面作用于同一个切入点时,可以用@Order指定切面的执行顺序 public class AopTest {
* 优先级高的切面在切入方法前执行的通知(before)会优先执行,但是位于方法后执行的通知(after,afterReturning)反而会延后执行
*/
@Test @Autowired
public void delete() { private OrderService orderService;
orderService.deleteOrder(12793179L);
} @Test
} public void saveAndQuery() {
``` orderService.createOrder(1283929319L, "手机");
orderService.queryOrder(4891894129L);
#### 2.5 切面执行顺序 }
- 多个切面作用于同一个切入点时,可以用@Order指定切面的执行顺序 /**
* 多个切面作用于同一个切入点时,可以用@Order指定切面的执行顺序
- 优先级高的切面在切入方法前执行的通知(before)会优先执行,但是位于方法后执行的通知(after,afterReturning)反而会延后执行,类似于同心圆原理。 * 优先级高的切面在切入方法前执行的通知(before)会优先执行,但是位于方法后执行的通知(after,afterReturning)反而会延后执行
*/
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/aop执行顺序.png"/> </div> @Test
public void delete() {
orderService.deleteOrder(12793179L);
}
## 附: 关于切面表达式的说明 }
```
切面表达式遵循以下格式:
#### 2.5 切面执行顺序
```shell
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) - 多个切面作用于同一个切入点时,可以用@Order指定切面的执行顺序
throws-pattern?)
``` - 优先级高的切面在切入方法前执行的通知(before)会优先执行,但是位于方法后执行的通知(after,afterReturning)反而会延后执行,类似于同心圆原理。
- 除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的; <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/aop执行顺序.png"/> </div>
- `*`,它代表了匹配任意的返回类型;
- `()` 匹配了一个不接受任何参数的方法, 而 `(..)` 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 `(*)` 匹配了一个接受一个任何类型的参数的方法。 模式 `(*,String)` 匹配了一个接受两个参数的方法第一个可以是任意类型第二个则必须是String类型。
下面给出一些常见切入点表达式的例子。 ## 附: 关于切面表达式的说明
- 任意公共方法的执行: 切面表达式遵循以下格式:
```java ```shell
execution(public * *(..)) execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
``` throws-pattern?)
```
- 任何一个以“set”开始的方法的执行
- 除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的;
```java - `*`,它代表了匹配任意的返回类型;
execution(* set*(..)) - `()` 匹配了一个不接受任何参数的方法, 而 `(..)` 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 `(*)` 匹配了一个接受一个任何类型的参数的方法。 模式 `(*,String)` 匹配了一个接受两个参数的方法第一个可以是任意类型第二个则必须是String类型。
```
下面给出一些常见切入点表达式的例子。
- `AccountService` 接口的任意方法的执行:
- 任意公共方法的执行:
```java
execution(* com.xyz.service.AccountService.*(..)) ```java
``` execution(public * *(..))
```
- 定义在service包里的任意方法的执行
- 任何一个以“set”开始的方法的执行
```java
execution(* com.xyz.service.*.*(..)) ```java
``` execution(* set*(..))
```
- 定义在service包或者子包里的任意方法的执行
- `AccountService` 接口的任意方法的执行:
```java
execution(* com.xyz.service..*.*(..)) ```java
``` execution(* com.xyz.service.AccountService.*(..))
```
- 在service包里的任意连接点在Spring AOP中只是方法执行
- 定义在service包里的任意方法的执行
```java
within(com.xyz.service.*) ```java
``` execution(* com.xyz.service.*.*(..))
```
- 在service包或者子包里的任意连接点在Spring AOP中只是方法执行
- 定义在service包或者子包里的任意方法的执行
```
within(com.xyz.service..*) ```java
``` execution(* com.xyz.service..*.*(..))
```
- 实现了 `AccountService` 接口的代理对象的任意连接点在Spring AOP中只是方法执行
- 在service包里的任意连接点在Spring AOP中只是方法执行
```
this(com.xyz.service.AccountService) ```java
``` within(com.xyz.service.*)
```
- 在service包或者子包里的任意连接点在Spring AOP中只是方法执行
```
within(com.xyz.service..*)
```
- 实现了 `AccountService` 接口的代理对象的任意连接点在Spring AOP中只是方法执行
```
this(com.xyz.service.AccountService)
```
更多表达式可以参考官方文档:[Declaring a Pointcut](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop-pointcuts) 更多表达式可以参考官方文档:[Declaring a Pointcut](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop-pointcuts)

View File

@ -1,227 +1,239 @@
# spring AOPxml配置方式 # spring AOPxml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-aop">二、spring aop</a><br/>
切面配置位于resources下的aop.xml文件其中CustomAdvice是自定义切面类OrderService是待切入的方法。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建待切入接口及其实现类">2.1 创建待切入接口及其实现类</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-创建自定义切面类">2.2 创建自定义切面类</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-aop.png"/> </div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-配置切面">2.3 配置切面</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-测试切面">2.4 测试切面</a><br/>
<a href="#附-关于切面表达式的说明">附: 关于切面表达式的说明</a><br/>
## 正文<br/>
## 一、说明
### 1.2 依赖说明
### 1.1 项目结构说明
除了spring的基本依赖外需要导入aop依赖包
切面配置位于resources下的aop.xml文件其中CustomAdvice是自定义切面类OrderService是待切入的方法。
```xml
<!--aop 相关依赖--> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-aop.png"/> </div>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-base-version}</version> ### 1.2 依赖说明
</dependency>
``` 除了spring的基本依赖外需要导入aop依赖包
```xml
<!--aop 相关依赖-->
## 二、spring aop <dependency>
<groupId>org.springframework</groupId>
#### 2.1 创建待切入接口及其实现类 <artifactId>spring-aop</artifactId>
<version>${spring-base-version}</version>
```java </dependency>
public interface OrderService { ```
Order queryOrder(Long id);
Order createOrder(Long id, String productName); ## 二、spring aop
}
``` #### 2.1 创建待切入接口及其实现类
```java ```java
public class OrderServiceImpl implements OrderService { public interface OrderService {
public Order queryOrder(Long id) { Order queryOrder(Long id);
return new Order(id, "product", new Date());
} Order createOrder(Long id, String productName);
}
public Order createOrder(Long id, String productName) { ```
// 模拟抛出异常
// int j = 1 / 0; ```java
return new Order(id, "new Product", new Date()); public class OrderServiceImpl implements OrderService {
}
} public Order queryOrder(Long id) {
return new Order(id, "product", new Date());
``` }
#### 2.2 创建自定义切面类 public Order createOrder(Long id, String productName) {
// 模拟抛出异常
```java // int j = 1 / 0;
/** return new Order(id, "new Product", new Date());
* @author : heibaiying }
* @description : 自定义切面 }
*/
public class CustomAdvice { ```
#### 2.2 创建自定义切面类
//前置通知
public void before(JoinPoint joinPoint) { ```java
//获取节点名称 /**
String name = joinPoint.getSignature().getName(); * @author : heibaiying
Object[] args = joinPoint.getArgs(); * @description : 自定义切面
System.out.println(name + "方法调用前:获取调用参数" + Arrays.toString(args)); */
} public class CustomAdvice {
//后置通知(抛出异常后不会被执行)
public void afterReturning(JoinPoint joinPoint, Object result) { //前置通知
System.out.println("后置返回通知结果" + result); public void before(JoinPoint joinPoint) {
} //获取节点名称
String name = joinPoint.getSignature().getName();
//环绕通知 Object[] args = joinPoint.getArgs();
public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println(name + "方法调用前:获取调用参数" + Arrays.toString(args));
System.out.println("环绕通知-前"); }
//调用目标方法
Object proceed = joinPoint.proceed(); //后置通知(抛出异常后不会被执行)
System.out.println("环绕通知-后"); public void afterReturning(JoinPoint joinPoint, Object result) {
return proceed; System.out.println("后置返回通知结果" + result);
} }
//异常通知 //环绕通知
public void afterException(JoinPoint joinPoint, Exception exception) { public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.err.println("后置异常通知:" + exception); System.out.println("环绕通知-前");
} //调用目标方法
Object proceed = joinPoint.proceed();
; System.out.println("环绕通知-后");
return proceed;
// 后置通知 总会执行 但是不能访问到返回值 }
public void after(JoinPoint joinPoint) {
System.out.println("后置通知"); //异常通知
} public void afterException(JoinPoint joinPoint, Exception exception) {
} System.err.println("后置异常通知:" + exception);
}
```
;
#### 2.3 配置切面
// 后置通知 总会执行 但是不能访问到返回值
```xml public void after(JoinPoint joinPoint) {
<?xml version="1.0" encoding="UTF-8"?> System.out.println("后置通知");
<beans xmlns="http://www.springframework.org/schema/beans" }
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" }
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans ```
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop #### 2.3 配置切面
http://www.springframework.org/schema/aop/spring-aop.xsd">
```xml
<!--开启后允许使用Spring AOP的@AspectJ注解 如果是纯xml配置 可以不用开启这个声明--> <?xml version="1.0" encoding="UTF-8"?>
<aop:aspectj-autoproxy/> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!-- 1.配置目标对象 --> xmlns:aop="http://www.springframework.org/schema/aop"
<bean name="orderService" class="com.heibaiying.service.OrderServiceImpl"/> xsi:schemaLocation="http://www.springframework.org/schema/beans
<!-- 2.声明切面 --> http://www.springframework.org/schema/beans/spring-beans.xsd
<bean name="myAdvice" class="com.heibaiying.advice.CustomAdvice"/> http://www.springframework.org/schema/aop
<!-- 3.配置将通知织入目标对象 --> http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<!--命名切入点 关于切入点更多表达式写法可以参见README.md--> <!--开启后允许使用Spring AOP的@AspectJ注解 如果是纯xml配置 可以不用开启这个声明-->
<aop:pointcut expression="execution(* com.heibaiying.service.OrderService.*(..))" id="cutPoint"/> <aop:aspectj-autoproxy/>
<aop:aspect ref="myAdvice">
<!-- 前置通知 --> <!-- 1.配置目标对象 -->
<aop:before method="before" pointcut-ref="cutPoint"/> <bean name="orderService" class="com.heibaiying.service.OrderServiceImpl"/>
<!-- 后置通知 如果需要拿到返回值 则要指明返回值对应的参数名称--> <!-- 2.声明切面 -->
<aop:after-returning method="afterReturning" pointcut-ref="cutPoint" returning="result"/> <bean name="myAdvice" class="com.heibaiying.advice.CustomAdvice"/>
<!-- 环绕通知 --> <!-- 3.配置将通知织入目标对象 -->
<aop:around method="around" pointcut-ref="cutPoint"/> <aop:config>
<!-- 后置异常 如果需要拿到异常 则要指明异常对应的参数名称 --> <!--命名切入点 关于切入点更多表达式写法可以参见README.md-->
<aop:after-throwing method="afterException" pointcut-ref="cutPoint" throwing="exception"/> <aop:pointcut expression="execution(* com.heibaiying.service.OrderService.*(..))" id="cutPoint"/>
<!-- 最终通知 --> <aop:aspect ref="myAdvice">
<aop:after method="after" pointcut-ref="cutPoint"/> <!-- 前置通知 -->
</aop:aspect> <aop:before method="before" pointcut-ref="cutPoint"/>
</aop:config> <!-- 后置通知 如果需要拿到返回值 则要指明返回值对应的参数名称-->
<aop:after-returning method="afterReturning" pointcut-ref="cutPoint" returning="result"/>
</beans> <!-- 环绕通知 -->
``` <aop:around method="around" pointcut-ref="cutPoint"/>
<!-- 后置异常 如果需要拿到异常 则要指明异常对应的参数名称 -->
#### 2.4 测试切面 <aop:after-throwing method="afterException" pointcut-ref="cutPoint" throwing="exception"/>
<!-- 最终通知 -->
```java <aop:after method="after" pointcut-ref="cutPoint"/>
@RunWith(SpringRunner.class) </aop:aspect>
@ContextConfiguration("classpath:aop.xml") </aop:config>
public class AopTest {
</beans>
@Autowired ```
private OrderService orderService;
#### 2.4 测试切面
@Test
public void save() { ```java
orderService.createOrder(1283929319L, "手机"); @RunWith(SpringRunner.class)
orderService.queryOrder(4891894129L); @ContextConfiguration("classpath:aop.xml")
} public class AopTest {
}
``` @Autowired
private OrderService orderService;
@Test
## 附: 关于切面表达式的说明 public void save() {
orderService.createOrder(1283929319L, "手机");
切面表达式遵循以下格式: orderService.queryOrder(4891894129L);
}
```shell }
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) ```
throws-pattern?)
```
- 除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的; ## 附: 关于切面表达式的说明
- `*`,它代表了匹配任意的返回类型;
- `()` 匹配了一个不接受任何参数的方法, 而 `(..)` 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 `(*)` 匹配了一个接受一个任何类型的参数的方法。 模式 `(*,String)` 匹配了一个接受两个参数的方法第一个可以是任意类型第二个则必须是String类型。 切面表达式遵循以下格式:
下面给出一些常见切入点表达式的例子。 ```shell
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
- 任意公共方法的执行: throws-pattern?)
```
```java
execution(public * *(..)) - 除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的;
``` - `*`,它代表了匹配任意的返回类型;
- `()` 匹配了一个不接受任何参数的方法, 而 `(..)` 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 `(*)` 匹配了一个接受一个任何类型的参数的方法。 模式 `(*,String)` 匹配了一个接受两个参数的方法第一个可以是任意类型第二个则必须是String类型。
- 任何一个以“set”开始的方法的执行
下面给出一些常见切入点表达式的例子。
```java
execution(* set*(..)) - 任意公共方法的执行:
```
```java
- `AccountService` 接口的任意方法的执行: execution(public * *(..))
```
```java
execution(* com.xyz.service.AccountService.*(..)) - 任何一个以“set”开始的方法的执行
```
```java
- 定义在service包里的任意方法的执行 execution(* set*(..))
```
```java
execution(* com.xyz.service.*.*(..)) - `AccountService` 接口的任意方法的执行:
```
```java
- 定义在service包或者子包里的任意方法的执行 execution(* com.xyz.service.AccountService.*(..))
```
```java
execution(* com.xyz.service..*.*(..)) - 定义在service包里的任意方法的执行
```
```java
- 在service包里的任意连接点在Spring AOP中只是方法执行 execution(* com.xyz.service.*.*(..))
```
```java
within(com.xyz.service.*) - 定义在service包或者子包里的任意方法的执行
```
```java
- 在service包或者子包里的任意连接点在Spring AOP中只是方法执行 execution(* com.xyz.service..*.*(..))
```
```
within(com.xyz.service..*) - 在service包里的任意连接点在Spring AOP中只是方法执行
```
```java
- 实现了 `AccountService` 接口的代理对象的任意连接点在Spring AOP中只是方法执行 within(com.xyz.service.*)
```
```
this(com.xyz.service.AccountService) - 在service包或者子包里的任意连接点在Spring AOP中只是方法执行
```
```
within(com.xyz.service..*)
```
- 实现了 `AccountService` 接口的代理对象的任意连接点在Spring AOP中只是方法执行
```
this(com.xyz.service.AccountService)
```
更多表达式可以参考官方文档:[Declaring a Pointcut](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop-pointcuts) 更多表达式可以参考官方文档:[Declaring a Pointcut](https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aop-pointcuts)

View File

@ -1,5 +1,18 @@
# spring +druid+ mybatis注解方式 # spring +druid+ mybatis注解方式
## 目录<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#1创建maven工程除了Spring基本依赖外还需要导入mybatis和druid的相关依赖">1、创建maven工程除了Spring基本依赖外还需要导入mybatis和druid的相关依赖</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#2新建-DispatcherServletInitializerjava继承自AbstractAnnotationConfigDispatcherServletInitializer等价于我们在webxml中配置的前端控制器">2、新建 DispatcherServletInitializer.java继承自AbstractAnnotationConfigDispatcherServletInitializer,等价于我们在web.xml中配置的前端控制器</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#3基于servlet-30的支持可以采用注解的方式注册druid的servlet和filter">3、基于servlet 3.0的支持可以采用注解的方式注册druid的servlet和filter </a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#4在resources文件夹下新建数据库配置文件mysqlpropertiesoracleproperties">4、在resources文件夹下新建数据库配置文件mysql.properties、oracle.properties</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#5在新建数据库配置映射类DataSourceConfigjava">5、在新建数据库配置映射类DataSourceConfig.java</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#6新建ServletConfigjava进行数据库相关配置">6、新建ServletConfig.java进行数据库相关配置</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#7新建mybtais-配置文件按需要进行额外参数配置-更多settings配置项可以参考[官方文档]http//wwwmybatisorg/mybatis-3/zh/configurationhtml">7、新建mybtais 配置文件,按需要进行额外参数配置, 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html)</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#8新建查询接口及其对应的mapper文件">8、新建查询接口及其对应的mapper文件</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#9新建测试controller进行测试">9、新建测试controller进行测试</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#10druid-监控页面访问地址http//localhost8080/druid/indexhtml">10、druid 监控页面访问地址http://localhost:8080/druid/index.html</a><br/>
## 正文<br/>
### 项目目录结构 ### 项目目录结构
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-druid-mybatis-annotation.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-druid-mybatis-annotation.png"/> </div>
@ -61,7 +74,7 @@ public class DispatcherServletInitializer extends AbstractAnnotationConfigDispat
} }
``` ```
#### 3、基servlet 3.0的支持可以采用注解的方式注册druid的servlet和filter #### 3、基servlet 3.0的支持可以采用注解的方式注册druid的servlet和filter
关于servlet 更多注解支持可以查看[Servlet 规范文档](https://github.com/heibaiying/spring-samples-for-all/blob/master/referenced%20documents/Servlet3.1%E8%A7%84%E8%8C%83%EF%BC%88%E6%9C%80%E7%BB%88%E7%89%88%EF%BC%89.pdf)中**8.1小节 注解和可插拔性** 关于servlet 更多注解支持可以查看[Servlet 规范文档](https://github.com/heibaiying/spring-samples-for-all/blob/master/referenced%20documents/Servlet3.1%E8%A7%84%E8%8C%83%EF%BC%88%E6%9C%80%E7%BB%88%E7%89%88%EF%BC%89.pdf)中**8.1小节 注解和可插拔性**
@ -311,7 +324,7 @@ public interface OracleDao {
</mapper> </mapper>
``` ```
#### 9.新建测试controller进行测试 #### 9新建测试controller进行测试
```java ```java
@RestController @RestController

View File

@ -1,5 +1,16 @@
# spring +druid+ mybatisxml配置方式 # spring +druid+ mybatisxml配置方式
## 目录<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#1创建maven工程除了Spring基本依赖外还需要导入mybatis和druid的相关依赖">1、创建maven工程除了Spring基本依赖外还需要导入mybatis和druid的相关依赖</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#2在webxml-中配置spring前端控制器druid监控台servlet和filter">2、在web.xml 中配置spring前端控制器、druid监控台servlet和filter</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#3在resources文件夹下新建数据库配置文件jdbcproperties">3、在resources文件夹下新建数据库配置文件jdbc.properties</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#4在resources文件夹下创建springApplicationxml-配置文件和druidxml配置文件">4、在resources文件夹下创建springApplication.xml 配置文件和druid.xml配置文件 </a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#5新建mybtais-配置文件按需要进行额外配置更多settings配置项可以参考[官方文档]http//wwwmybatisorg/mybatis-3/zh/configurationhtml">5、新建mybtais 配置文件按需要进行额外配置更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html)</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#6新建查询接口及其对应的mapper文件">6、新建查询接口及其对应的mapper文件</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#7新建测试controller进行测试">7、新建测试controller进行测试</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#8druid-监控页面访问地址http//localhost8080/druid/indexhtml">8、druid 监控页面访问地址http://localhost:8080/druid/index.html</a><br/>
## 正文<br/>
### 项目目录结构 ### 项目目录结构
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-druid-mybatis.png"/> </div> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-druid-mybatis.png"/> </div>
@ -308,7 +319,7 @@ public interface OracleDao {
</mapper> </mapper>
``` ```
#### 7.新建测试controller进行测试 #### 7新建测试controller进行测试
```java ```java
package com.heibaiying.controller; package com.heibaiying.controller;

View File

@ -1,327 +1,341 @@
# spring 整合 dubbo注解方式 # spring 整合 dubbo注解方式
## 目录<br/>
## 一、 项目结构说明 <a href="#一-项目结构说明">一、 项目结构说明</a><br/>
<a href="#二项目依赖">二、项目依赖</a><br/>
1.1 按照dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中所以项目采用maven多模块的构建方式在spring-dubbo-annotation下构建三个子模块 <a href="#三公共模块dubbo-ano-common">三、公共模块dubbo-ano-common</a><br/>
<a href="#四-服务提供者dubbo-ano-provider">四、 服务提供者dubbo-ano-provider</a><br/>
1. dubbo-ano-common 是公共模块用于存放公共的接口和bean,被dubbo-ano-provider和dubbo-ano-provider在pom.xml中引用 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#41-提供方配置">4.1 提供方配置</a><br/>
2. dubbo-ano-provider 是服务的提供者,提供商品的查询服务; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#42--使用注解@Service暴露服务">4.2 使用注解@Service暴露服务</a><br/>
3. dubbo-ano-provider 是服务的消费者调用provider提供的查询服务。 <a href="#五服务消费者dubbo-ano-consumer">五、服务消费者dubbo-ano-consumer</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#1消费方的配置">1.消费方的配置</a><br/>
1.2 本项目dubbo的搭建采用zookeeper作为注册中心 关于zookeeper的安装和基本操作可以参见我的手记[Zookeeper 基础命令与Java客户端](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/ZooKeeper/ZooKeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA%E4%B8%8EJava%E5%AE%A2%E6%88%B7%E7%AB%AF.md) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#2使用注解@Reference引用远程服务">2.使用注解@Reference引用远程服务</a><br/>
<a href="#六项目构建的说明">六、项目构建的说明</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-dubbo.png"/> </div> <a href="#七关于dubbo新版本管理控制台的安装说明">七、关于dubbo新版本管理控制台的安装说明</a><br/>
## 正文<br/>
## 二、项目依赖 ## 一、 项目结构说明
**在父工程的项目中统一导入依赖dubbo依赖的的jar包** 1.1 按照dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中所以项目采用maven多模块的构建方式在spring-dubbo-annotation下构建三个子模块
这里需要注意的是ZooKeeper 3.5.x 和 ZooKeeper 3.4.x 是存在不兼容的情况 详见官网解释[ZooKeeper Version Compatibility](https://curator.apache.org/zk-compatibility.html), zookeeper 3.5 目前是beta版本所以zookeeper 我选择的版本是 zookeeper-3.4.9 作为服务端。但默认情况下 curator-framework自动引用的最新的3.5的版本客户端,会出现 KeeperException$UnimplementedException 异常 1. dubbo-ano-common 是公共模块用于存放公共的接口和bean,被dubbo-ano-provider和dubbo-ano-provider在pom.xml中引用
2. dubbo-ano-provider 是服务的提供者,提供商品的查询服务;
```xml 3. dubbo-ano-provider 是服务的消费者调用provider提供的查询服务。
<!--dubbo 依赖-->
<groupId>com.alibaba</groupId> 1.2 本项目dubbo的搭建采用zookeeper作为注册中心 关于zookeeper的安装和基本操作可以参见我的手记[Zookeeper 基础命令与Java客户端](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/ZooKeeper/ZooKeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA%E4%B8%8EJava%E5%AE%A2%E6%88%B7%E7%AB%AF.md)
<artifactId>dubbo</artifactId>
<version>2.6.2</version> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-dubbo.png"/> </div>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId> ## 二、项目依赖
<version>4.0.0</version>
<exclusions> **在父工程的项目中统一导入依赖dubbo依赖的的jar包**
<exclusion>
<groupId>org.apache.zookeeper</groupId> 这里需要注意的是ZooKeeper 3.5.x 和 ZooKeeper 3.4.x 是存在不兼容的情况 详见官网解释[ZooKeeper Version Compatibility](https://curator.apache.org/zk-compatibility.html), zookeeper 3.5 目前是beta版本所以zookeeper 我选择的版本是 zookeeper-3.4.9 作为服务端。但默认情况下 curator-framework自动引用的最新的3.5的版本客户端,会出现 KeeperException$UnimplementedException 异常
<artifactId>zookeeper</artifactId>
</exclusion> ```xml
</exclusions> <!--dubbo 依赖-->
</dependency> <groupId>com.alibaba</groupId>
<dependency> <artifactId>dubbo</artifactId>
<groupId>org.apache.zookeeper</groupId> <version>2.6.2</version>
<artifactId>zookeeper</artifactId> </dependency>
<version>3.4.13</version> <dependency>
</dependency> <groupId>org.apache.curator</groupId>
``` <artifactId>curator-framework</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
## 三、公共模块dubbo-ano-common <groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
- api 下为公共的调用接口; </exclusion>
- bean 下为公共的实体类。 </exclusions>
</dependency>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-ano-common.png"/> </div> <dependency>
<groupId>org.apache.zookeeper</groupId>
## 四、 服务提供者dubbo-ano-provider <artifactId>zookeeper</artifactId>
<version>3.4.13</version>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-ano-provider.png"/> </div> </dependency>
```
#### 4.1 提供方配置
```java
@Configuration ## 三、公共模块dubbo-ano-common
public class DubboConfiguration {
- api 下为公共的调用接口;
/** - bean 下为公共的实体类。
* 提供方应用信息,用于计算依赖关系
*/ <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-ano-common.png"/> </div>
@Bean
public ApplicationConfig applicationConfig() { ## 四、 服务提供者dubbo-ano-provider
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-ano-provider"); <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-ano-provider.png"/> </div>
return applicationConfig;
} #### 4.1 提供方配置
/** ```java
* 使用zookeeper注册中心暴露服务地址 @Configuration
*/ public class DubboConfiguration {
@Bean
public RegistryConfig registryConfig() { /**
RegistryConfig registryConfig = new RegistryConfig(); * 提供方应用信息,用于计算依赖关系
registryConfig.setAddress("zookeeper://127.0.0.1:2181"); */
registryConfig.setClient("curator"); @Bean
return registryConfig; public ApplicationConfig applicationConfig() {
} ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-ano-provider");
/** return applicationConfig;
* 用dubbo协议在20880端口暴露服务 }
*/
@Bean /**
public ProtocolConfig protocolConfig() { * 使用zookeeper注册中心暴露服务地址
ProtocolConfig protocolConfig = new ProtocolConfig(); */
protocolConfig.setName("dubbo"); @Bean
protocolConfig.setPort(20880); public RegistryConfig registryConfig() {
return protocolConfig; RegistryConfig registryConfig = new RegistryConfig();
} registryConfig.setAddress("zookeeper://127.0.0.1:2181");
} registryConfig.setClient("curator");
``` return registryConfig;
}
#### 4.2 使用注解@Service暴露服务
/**
需要注意的是这里的@Service注解不是spring的注解而是dubbo的注解 com.alibaba.dubbo.config.annotation.Service * 用dubbo协议在20880端口暴露服务
*/
```java @Bean
package com.heibaiying.service; public ProtocolConfig protocolConfig() {
ProtocolConfig protocolConfig = new ProtocolConfig();
import com.alibaba.dubbo.config.annotation.Service; protocolConfig.setName("dubbo");
import com.heibaiying.api.IProductService; protocolConfig.setPort(20880);
import com.heibaiying.bean.Product; return protocolConfig;
}
}
import java.util.ArrayList; ```
import java.util.Date;
import java.util.List; #### 4.2 使用注解@Service暴露服务
/** 需要注意的是这里的@Service注解不是spring的注解而是dubbo的注解 com.alibaba.dubbo.config.annotation.Service
* @author : heibaiying
* @description : 产品提供接口实现类 ```java
*/ package com.heibaiying.service;
@Service(timeout = 5000)
public class ProductService implements IProductService { import com.alibaba.dubbo.config.annotation.Service;
import com.heibaiying.api.IProductService;
private static List<Product> productList = new ArrayList<>(); import com.heibaiying.bean.Product;
static {
for (int i = 0; i < 20; i++) { import java.util.ArrayList;
productList.add(new Product(i, "产品" + i, i / 2 == 0, new Date(), 66.66f * i)); import java.util.Date;
} import java.util.List;
}
/**
public Product queryProductById(int id) { * @author : heibaiying
for (Product product : productList) { * @description : 产品提供接口实现类
if (product.getId() == id) { */
return product; @Service(timeout = 5000)
} public class ProductService implements IProductService {
}
return null; private static List<Product> productList = new ArrayList<>();
}
static {
public List<Product> queryAllProducts() { for (int i = 0; i < 20; i++) {
return productList; productList.add(new Product(i, "产品" + i, i / 2 == 0, new Date(), 66.66f * i));
} }
} }
```
public Product queryProductById(int id) {
for (Product product : productList) {
if (product.getId() == id) {
## 五、服务消费者dubbo-ano-consumer return product;
}
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-ano-consumer.png"/> </div> }
return null;
#### 1.消费方的配置 }
```java public List<Product> queryAllProducts() {
@Configuration return productList;
public class DubboConfiguration { }
}
/** ```
* 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样
*/
@Bean
public ApplicationConfig applicationConfig() { ## 五、服务消费者dubbo-ano-consumer
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-ano-consumer"); <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-ano-consumer.png"/> </div>
return applicationConfig;
} #### 1.消费方的配置
/** ```java
* 设置调用服务超时时间 @Configuration
* 关闭所有服务的启动时检查 public class DubboConfiguration {
*/
@Bean /**
public ConsumerConfig consumerConfig() { * 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样
ConsumerConfig consumerConfig = new ConsumerConfig(); */
consumerConfig.setTimeout(3000); @Bean
consumerConfig.setCheck(false); public ApplicationConfig applicationConfig() {
return consumerConfig; ApplicationConfig applicationConfig = new ApplicationConfig();
} applicationConfig.setName("dubbo-ano-consumer");
return applicationConfig;
/** }
* 使用zookeeper注册中心暴露发现服务地址
*/ /**
@Bean * 设置调用服务超时时间
public RegistryConfig registryConfig() { * 关闭所有服务的启动时检查
RegistryConfig registryConfig = new RegistryConfig(); */
registryConfig.setAddress("zookeeper://127.0.0.1:2181"); @Bean
registryConfig.setClient("curator"); public ConsumerConfig consumerConfig() {
return registryConfig; ConsumerConfig consumerConfig = new ConsumerConfig();
} consumerConfig.setTimeout(3000);
consumerConfig.setCheck(false);
} return consumerConfig;
``` }
#### 2.使用注解@Reference引用远程服务 /**
* 使用zookeeper注册中心暴露发现服务地址
```java */
package com.heibaiying.controller; @Bean
public RegistryConfig registryConfig() {
import com.alibaba.dubbo.config.annotation.Reference; RegistryConfig registryConfig = new RegistryConfig();
import com.heibaiying.api.IProductService; registryConfig.setAddress("zookeeper://127.0.0.1:2181");
import com.heibaiying.bean.Product; registryConfig.setClient("curator");
import org.springframework.stereotype.Controller; return registryConfig;
import org.springframework.ui.Model; }
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; }
```
import java.util.List;
#### 2.使用注解@Reference引用远程服务
@Controller
@RequestMapping("sell") ```java
public class SellController { package com.heibaiying.controller;
// dubbo远程引用注解 import com.alibaba.dubbo.config.annotation.Reference;
@Reference import com.heibaiying.api.IProductService;
private IProductService productService; import com.heibaiying.bean.Product;
import org.springframework.stereotype.Controller;
@RequestMapping import org.springframework.ui.Model;
public String productList(Model model) { import org.springframework.web.bind.annotation.PathVariable;
List<Product> products = productService.queryAllProducts(); import org.springframework.web.bind.annotation.RequestMapping;
model.addAttribute("products", products);
return "products"; import java.util.List;
}
@Controller
@RequestMapping("product/{id}") @RequestMapping("sell")
public String productDetail(@PathVariable int id, Model model) { public class SellController {
Product product = productService.queryProductById(id);
model.addAttribute("product", product); // dubbo远程引用注解
return "product"; @Reference
} private IProductService productService;
}
@RequestMapping
``` public String productList(Model model) {
List<Product> products = productService.queryAllProducts();
## 六、项目构建的说明 model.addAttribute("products", products);
return "products";
因为在项目中consumer和provider模块均依赖公共模块,所以在构建consumer和provider项目前需要将common 模块安装到本地仓库,**依次**对**父工程**和**common模块**执行: }
```shell @RequestMapping("product/{id}")
mvn install -Dmaven.test.skip = true public String productDetail(@PathVariable int id, Model model) {
``` Product product = productService.queryProductById(id);
model.addAttribute("product", product);
consumer中 pom.xml如下 return "product";
}
```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"> ## 六、项目构建的说明
<parent>
<artifactId>spring-dubbo-annotation</artifactId> 因为在项目中consumer和provider模块均依赖公共模块,所以在构建consumer和provider项目前需要将common 模块安装到本地仓库,**依次**对**父工程**和**common模块**执行:
<groupId>com.heibaiying</groupId>
<version>1.0-SNAPSHOT</version> ```shell
</parent> mvn install -Dmaven.test.skip = true
<modelVersion>4.0.0</modelVersion> ```
<artifactId>dubbo-ano-consumer</artifactId> consumer中 pom.xml如下
<dependencies> ```xml
<dependency> <?xml version="1.0" encoding="UTF-8"?>
<groupId>com.heibaiying</groupId> <project xmlns="http://maven.apache.org/POM/4.0.0"
<artifactId>dubbo-ano-common</artifactId> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<version>1.0-SNAPSHOT</version> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<scope>compile</scope> <parent>
</dependency> <artifactId>spring-dubbo-annotation</artifactId>
</dependencies> <groupId>com.heibaiying</groupId>
<version>1.0-SNAPSHOT</version>
</project> </parent>
``` <modelVersion>4.0.0</modelVersion>
provider中 pom.xml如下 <artifactId>dubbo-ano-consumer</artifactId>
```xml <dependencies>
<?xml version="1.0" encoding="UTF-8"?> <dependency>
<project xmlns="http://maven.apache.org/POM/4.0.0" <groupId>com.heibaiying</groupId>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <artifactId>dubbo-ano-common</artifactId>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <version>1.0-SNAPSHOT</version>
<parent> <scope>compile</scope>
<artifactId>spring-dubbo-annotation</artifactId> </dependency>
<groupId>com.heibaiying</groupId> </dependencies>
<version>1.0-SNAPSHOT</version>
</parent> </project>
<modelVersion>4.0.0</modelVersion> ```
<artifactId>dubbo-ano-provider</artifactId> provider中 pom.xml如下
<dependencies> ```xml
<dependency> <?xml version="1.0" encoding="UTF-8"?>
<groupId>com.heibaiying</groupId> <project xmlns="http://maven.apache.org/POM/4.0.0"
<artifactId>dubbo-ano-common</artifactId> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<version>1.0-SNAPSHOT</version> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<scope>compile</scope> <parent>
</dependency> <artifactId>spring-dubbo-annotation</artifactId>
</dependencies> <groupId>com.heibaiying</groupId>
<version>1.0-SNAPSHOT</version>
</project> </parent>
``` <modelVersion>4.0.0</modelVersion>
## 七、关于dubbo新版本管理控制台的安装说明 <artifactId>dubbo-ano-provider</artifactId>
安装: <dependencies>
<dependency>
```sh <groupId>com.heibaiying</groupId>
git clone https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops <artifactId>dubbo-ano-common</artifactId>
cd /var/tmp/dubbo-ops <version>1.0-SNAPSHOT</version>
mvn clean package <scope>compile</scope>
``` </dependency>
</dependencies>
配置:
</project>
```sh ```
配置文件为:
dubbo-admin-backend/src/main/resources/application.properties ## 七、关于dubbo新版本管理控制台的安装说明
主要的配置有 默认的配置就是127.0.0.1:2181
dubbo.registry.address=zookeeper://127.0.0.1:2181 安装:
```
```sh
启动: git clone https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops
cd /var/tmp/dubbo-ops
```sh mvn clean package
mvn --projects dubbo-admin-backend spring-boot:run ```
```
配置:
访问:
```sh
``` 配置文件为:
http://127.0.0.1:8080 dubbo-admin-backend/src/main/resources/application.properties
主要的配置有 默认的配置就是127.0.0.1:2181
dubbo.registry.address=zookeeper://127.0.0.1:2181
```
启动:
```sh
mvn --projects dubbo-admin-backend spring-boot:run
```
访问:
```
http://127.0.0.1:8080
``` ```

View File

@ -1,279 +1,293 @@
# spring 整合 dubboxml配置方式 # spring 整合 dubboxml配置方式
## 目录<br/>
## 一、 项目结构说明 <a href="#一-项目结构说明">一、 项目结构说明</a><br/>
<a href="#二项目依赖">二、项目依赖</a><br/>
1.1 按照dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中所以项目采用maven多模块的构建方式在spring-dubbo下构建三个子模块 <a href="#三公共模块dubbo-common">三、公共模块dubbo-common</a><br/>
<a href="#四-服务提供者dubbo-provider">四、 服务提供者dubbo-provider</a><br/>
1. dubbo-common 是公共模块用于存放公共的接口和bean,被dubbo-provider和dubbo-provider在pom.xml中引用 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#41--productService是服务的提供者-商品数据用模拟数据展示">4.1 productService是服务的提供者 商品数据用模拟数据展示)</a><br/>
2. dubbo-provider 是服务的提供者,提供商品的查询服务; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#42-在dubboxml暴露服务">4.2 在dubbo.xml暴露服务</a><br/>
3. dubbo-provider 是服务的消费者调用provider提供的查询服务。 <a href="#五服务消费者dubbo-consumer">五、服务消费者dubbo-consumer</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#1在dubboxml调用远程的服务">1.在dubbo.xml调用远程的服务</a><br/>
1.2 本项目dubbo的搭建采用zookeeper作为注册中心 关于zookeeper的安装和基本操作可以参见我的手记[Zookeeper 基础命令与Java客户端](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/ZooKeeper/ZooKeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA%E4%B8%8EJava%E5%AE%A2%E6%88%B7%E7%AB%AF.md) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#2消费服务">2.消费服务</a><br/>
<a href="#六项目构建的说明">六、项目构建的说明</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-dubbo.png"/> </div> <a href="#七关于dubbo新版本管理控制台的安装说明">七、关于dubbo新版本管理控制台的安装说明</a><br/>
## 正文<br/>
## 二、项目依赖 ## 一、 项目结构说明
**在父工程的项目中统一导入依赖dubbo依赖的的jar包** 1.1 按照dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中所以项目采用maven多模块的构建方式在spring-dubbo下构建三个子模块
这里需要注意的是ZooKeeper 3.5.x 和 ZooKeeper 3.4.x 是存在不兼容的情况 详见官网解释[ZooKeeper Version Compatibility](https://curator.apache.org/zk-compatibility.html), zookeeper 3.5 目前是beta版本所以zookeeper 我选择的版本是 zookeeper-3.4.9 作为服务端。但默认情况下 curator-framework自动引用的最新的3.5的版本客户端,会出现 KeeperException$UnimplementedException 异常 1. dubbo-common 是公共模块用于存放公共的接口和bean,被dubbo-provider和dubbo-provider在pom.xml中引用
2. dubbo-provider 是服务的提供者,提供商品的查询服务;
```xml 3. dubbo-provider 是服务的消费者调用provider提供的查询服务。
<!--dubbo 依赖-->
<groupId>com.alibaba</groupId> 1.2 本项目dubbo的搭建采用zookeeper作为注册中心 关于zookeeper的安装和基本操作可以参见我的手记[Zookeeper 基础命令与Java客户端](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/ZooKeeper/ZooKeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA%E4%B8%8EJava%E5%AE%A2%E6%88%B7%E7%AB%AF.md)
<artifactId>dubbo</artifactId>
<version>2.6.2</version> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-dubbo.png"/> </div>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId> ## 二、项目依赖
<version>4.0.0</version>
<exclusions> **在父工程的项目中统一导入依赖dubbo依赖的的jar包**
<exclusion>
<groupId>org.apache.zookeeper</groupId> 这里需要注意的是ZooKeeper 3.5.x 和 ZooKeeper 3.4.x 是存在不兼容的情况 详见官网解释[ZooKeeper Version Compatibility](https://curator.apache.org/zk-compatibility.html), zookeeper 3.5 目前是beta版本所以zookeeper 我选择的版本是 zookeeper-3.4.9 作为服务端。但默认情况下 curator-framework自动引用的最新的3.5的版本客户端,会出现 KeeperException$UnimplementedException 异常
<artifactId>zookeeper</artifactId>
</exclusion> ```xml
</exclusions> <!--dubbo 依赖-->
</dependency> <groupId>com.alibaba</groupId>
<dependency> <artifactId>dubbo</artifactId>
<groupId>org.apache.zookeeper</groupId> <version>2.6.2</version>
<artifactId>zookeeper</artifactId> </dependency>
<version>3.4.13</version> <dependency>
</dependency> <groupId>org.apache.curator</groupId>
``` <artifactId>curator-framework</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
## 三、公共模块dubbo-common <groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
- api 下为公共的调用接口; </exclusion>
- bean 下为公共的实体类。 </exclusions>
</dependency>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-common.png"/> </div> <dependency>
<groupId>org.apache.zookeeper</groupId>
## 四、 服务提供者dubbo-provider <artifactId>zookeeper</artifactId>
<version>3.4.13</version>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-provider.png"/> </div> </dependency>
```
#### 4.1 productService是服务的提供者 商品数据用模拟数据展示)
这里实现的接口IProductService来源于公共模块
## 三、公共模块dubbo-common
```java
/** - api 下为公共的调用接口;
* @author : heibaiying - bean 下为公共的实体类。
* @description : 产品提供接口实现类
*/ <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-common.png"/> </div>
@Service
public class ProductService implements IProductService { ## 四、 服务提供者dubbo-provider
private static List<Product> productList = new ArrayList<>(); <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-provider.png"/> </div>
static { #### 4.1 productService是服务的提供者 商品数据用模拟数据展示)
for (int i = 0; i < 20; i++) {
productList.add(new Product(i, "产品" + i, i / 2 == 0, new Date(), 66.66f * i)); 这里实现的接口IProductService来源于公共模块
}
} ```java
/**
public Product queryProductById(int id) { * @author : heibaiying
for (Product product : productList) { * @description : 产品提供接口实现类
if (product.getId() == id) { */
return product; @Service
} public class ProductService implements IProductService {
}
return null; private static List<Product> productList = new ArrayList<>();
}
static {
for (int i = 0; i < 20; i++) {
public List<Product> queryAllProducts() { productList.add(new Product(i, "产品" + i, i / 2 == 0, new Date(), 66.66f * i));
return productList; }
} }
}
``` public Product queryProductById(int id) {
for (Product product : productList) {
#### 4.2 在dubbo.xml暴露服务 if (product.getId() == id) {
return product;
```xml }
<?xml version="1.0" encoding="UTF-8"?> }
<beans xmlns="http://www.springframework.org/schema/beans" return null;
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" }
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd public List<Product> queryAllProducts() {
http://dubbo.apache.org/schema/dubbo return productList;
http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> }
}
<!-- 提供方应用信息,用于计算依赖关系 --> ```
<dubbo:application name="dubbo-provider"/>
#### 4.2 在dubbo.xml暴露服务
<!-- 使用zookeeper注册中心暴露服务地址 如果是集群配置 用, 分隔地址 -->
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/> ```xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 用dubbo协议在20880端口暴露服务 --> <beans xmlns="http://www.springframework.org/schema/beans"
<dubbo:protocol name="dubbo" port="20880"/> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
<!-- 声明需要暴露的服务接口 --> xsi:schemaLocation="http://www.springframework.org/schema/beans
<dubbo:service interface="com.heibaiying.api.IProductService" ref="productService"/> http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
<!-- 和本地bean一样实现服务 --> http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<bean id="productService" class="com.heibaiying.service.ProductService"/>
</beans> <!-- 提供方应用信息,用于计算依赖关系 -->
``` <dubbo:application name="dubbo-provider"/>
## 五、服务消费者dubbo-consumer <!-- 使用zookeeper注册中心暴露服务地址 如果是集群配置 用, 分隔地址 -->
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-consumer.png"/> </div>
<!-- 用dubbo协议在20880端口暴露服务 -->
#### 1.在dubbo.xml调用远程的服务 <dubbo:protocol name="dubbo" port="20880"/>
```xml <!-- 声明需要暴露的服务接口 -->
<?xml version="1.0" encoding="UTF-8"?> <dubbo:service interface="com.heibaiying.api.IProductService" ref="productService"/>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- 和本地bean一样实现服务 -->
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" <bean id="productService" class="com.heibaiying.service.ProductService"/>
xsi:schemaLocation="http://www.springframework.org/schema/beans </beans>
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd ```
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> ## 五、服务消费者dubbo-consumer
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dubbo-consumer.png"/> </div>
<dubbo:application name="dubbo-consumer">
<dubbo:parameter key="shutdown.timeout" value="60000"/> <!-- 单位毫秒 --> #### 1.在dubbo.xml调用远程的服务
</dubbo:application>
```xml
<!--Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"。--> <?xml version="1.0" encoding="UTF-8"?>
<!--可以关闭所有服务的启动时检查 --> <beans xmlns="http://www.springframework.org/schema/beans"
<dubbo:consumer check="false" /> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
<!-- 使用zookeeper注册中心暴露发现服务地址 --> xsi:schemaLocation="http://www.springframework.org/schema/beans
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/> http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
<!-- 生成远程服务代理可以和本地bean一样使用demoService --> http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:reference id="sellService" interface="com.heibaiying.api.IProductService"/>
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="dubbo-consumer">
</beans> <dubbo:parameter key="shutdown.timeout" value="60000"/> <!-- 单位毫秒 -->
``` </dubbo:application>
#### 2.消费服务 <!--Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"。-->
<!--可以关闭所有服务的启动时检查 -->
```java <dubbo:consumer check="false" />
@Controller
@RequestMapping("sell") <!-- 使用zookeeper注册中心暴露发现服务地址 -->
public class SellController { <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
@Autowired <!-- 生成远程服务代理可以和本地bean一样使用demoService -->
private IProductService productService; <dubbo:reference id="sellService" interface="com.heibaiying.api.IProductService"/>
@RequestMapping
public String productList(Model model) { </beans>
List<Product> products = productService.queryAllProducts(); ```
model.addAttribute("products", products);
return "products"; #### 2.消费服务
}
```java
@RequestMapping("product/{id}") @Controller
public String productDetail(@PathVariable int id, Model model) { @RequestMapping("sell")
Product product = productService.queryProductById(id); public class SellController {
model.addAttribute("product", product);
return "product"; @Autowired
} private IProductService productService;
}
``` @RequestMapping
public String productList(Model model) {
## 六、项目构建的说明 List<Product> products = productService.queryAllProducts();
model.addAttribute("products", products);
因为在项目中consumer和provider模块均依赖公共模块,所以在构建consumer和provider项目前需要将common 模块安装到本地仓库,**依次**对**父工程**和**common模块**执行: return "products";
}
```shell
mvn install -Dmaven.test.skip = true @RequestMapping("product/{id}")
``` public String productDetail(@PathVariable int id, Model model) {
Product product = productService.queryProductById(id);
consumer中 pom.xml如下 model.addAttribute("product", product);
return "product";
```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"> ## 六、项目构建的说明
<parent>
<artifactId>spring-dubbo</artifactId> 因为在项目中consumer和provider模块均依赖公共模块,所以在构建consumer和provider项目前需要将common 模块安装到本地仓库,**依次**对**父工程**和**common模块**执行:
<groupId>com.heibaiying</groupId>
<version>1.0-SNAPSHOT</version> ```shell
</parent> mvn install -Dmaven.test.skip = true
<modelVersion>4.0.0</modelVersion> ```
<artifactId>dubbo-consumer</artifactId> consumer中 pom.xml如下
<dependencies> ```xml
<dependency> <?xml version="1.0" encoding="UTF-8"?>
<groupId>com.heibaiying</groupId> <project xmlns="http://maven.apache.org/POM/4.0.0"
<artifactId>dubbo-common</artifactId> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<version>1.0-SNAPSHOT</version> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<scope>compile</scope> <parent>
</dependency> <artifactId>spring-dubbo</artifactId>
</dependencies> <groupId>com.heibaiying</groupId>
<version>1.0-SNAPSHOT</version>
</project> </parent>
``` <modelVersion>4.0.0</modelVersion>
provider中 pom.xml如下 <artifactId>dubbo-consumer</artifactId>
```xml <dependencies>
<?xml version="1.0" encoding="UTF-8"?> <dependency>
<project xmlns="http://maven.apache.org/POM/4.0.0" <groupId>com.heibaiying</groupId>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <artifactId>dubbo-common</artifactId>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <version>1.0-SNAPSHOT</version>
<parent> <scope>compile</scope>
<artifactId>spring-dubbo</artifactId> </dependency>
<groupId>com.heibaiying</groupId> </dependencies>
<version>1.0-SNAPSHOT</version>
</parent> </project>
<modelVersion>4.0.0</modelVersion> ```
<artifactId>dubbo-provider</artifactId> provider中 pom.xml如下
<dependencies> ```xml
<dependency> <?xml version="1.0" encoding="UTF-8"?>
<groupId>com.heibaiying</groupId> <project xmlns="http://maven.apache.org/POM/4.0.0"
<artifactId>dubbo-common</artifactId> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<version>1.0-SNAPSHOT</version> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<scope>compile</scope> <parent>
</dependency> <artifactId>spring-dubbo</artifactId>
</dependencies> <groupId>com.heibaiying</groupId>
<version>1.0-SNAPSHOT</version>
</project> </parent>
``` <modelVersion>4.0.0</modelVersion>
## 七、关于dubbo新版本管理控制台的安装说明 <artifactId>dubbo-provider</artifactId>
安装: <dependencies>
<dependency>
```sh <groupId>com.heibaiying</groupId>
git clone https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops <artifactId>dubbo-common</artifactId>
cd /var/tmp/dubbo-ops <version>1.0-SNAPSHOT</version>
mvn clean package <scope>compile</scope>
``` </dependency>
</dependencies>
配置:
</project>
```sh ```
配置文件为:
dubbo-admin-backend/src/main/resources/application.properties ## 七、关于dubbo新版本管理控制台的安装说明
主要的配置有 默认的配置就是127.0.0.1:2181
dubbo.registry.address=zookeeper://127.0.0.1:2181 安装:
```
```sh
启动: git clone https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops
cd /var/tmp/dubbo-ops
```sh mvn clean package
mvn --projects dubbo-admin-backend spring-boot:run ```
```
配置:
访问:
```sh
``` 配置文件为:
http://127.0.0.1:8080 dubbo-admin-backend/src/main/resources/application.properties
主要的配置有 默认的配置就是127.0.0.1:2181
dubbo.registry.address=zookeeper://127.0.0.1:2181
```
启动:
```sh
mvn --projects dubbo-admin-backend spring-boot:run
```
访问:
```
http://127.0.0.1:8080
``` ```

View File

@ -1,287 +1,297 @@
# spring 邮件发送xml配置方式 # spring 邮件发送xml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-email">二、spring email</a><br/>
1. 邮件发送配置类为com.heibaiying.config下EmailConfig.java; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-邮件发送配置">2.1 邮件发送配置</a><br/>
2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-新建邮件发送基本类">2.2 新建邮件发送基本类</a><br/>
3. 项目以单元测试的方法进行测试测试类为SendEmail。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-邮件发送的测试">2.3 邮件发送的测试</a><br/>
## 正文<br/>
## 一、说明
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-email-annotation.png"/> </div>
### 1.1 项目结构说明
1. 邮件发送配置类为com.heibaiying.config下EmailConfig.java;
### 1.2 依赖说明 2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中
3. 项目以单元测试的方法进行测试测试类为SendEmail。
除了spring的基本依赖外需要导入邮件发送的支持包spring-context-support
```xml
<!--邮件发送依赖包--> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-email-annotation.png"/> </div>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring-base-version}</version> ### 1.2 依赖说明
</dependency>
<!--模板引擎--> 除了spring的基本依赖外需要导入邮件发送的支持包spring-context-support
<!--这里采用的是beetl,beetl性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">-->
<dependency> ```xml
<groupId>com.ibeetl</groupId> <!--邮件发送依赖包-->
<artifactId>beetl</artifactId> <dependency>
<version>2.9.7</version> <groupId>org.springframework</groupId>
</dependency> <artifactId>spring-context-support</artifactId>
``` <version>${spring-base-version}</version>
</dependency>
<!--模板引擎-->
<!--这里采用的是beetl,beetl性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">-->
## 二、spring email <dependency>
<groupId>com.ibeetl</groupId>
#### 2.1 邮件发送配置 <artifactId>beetl</artifactId>
<version>2.9.7</version>
```java </dependency>
/** ```
* @author : heibaiying
* @description : 邮件发送配置类
*/
## 二、spring email
@Configuration
@ComponentScan(value = "com.heibaiying.email") #### 2.1 邮件发送配置
public class EmailConfig {
```java
/*** /**
* 在这里可以声明不同的邮件服务器主机通常是SMTP主机,而具体的用户名和时授权码则建议在业务中从数据库查询 * @author : heibaiying
*/ * @description : 邮件发送配置类
@Bean(name = "qqMailSender") */
JavaMailSenderImpl javaMailSender() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); @Configuration
javaMailSender.setHost("smtp.qq.com"); @ComponentScan(value = "com.heibaiying.email")
javaMailSender.setPassword("587"); public class EmailConfig {
return javaMailSender;
} /***
* 在这里可以声明不同的邮件服务器主机通常是SMTP主机,而具体的用户名和时授权码则建议在业务中从数据库查询
/*** */
* 配置模板引擎 @Bean(name = "qqMailSender")
*/ JavaMailSenderImpl javaMailSender() {
@Bean JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
GroupTemplate groupTemplate() throws IOException { javaMailSender.setHost("smtp.qq.com");
//指定加载模板资源的位置 指定在classpath:beetl下- javaMailSender.setPassword("587");
ClasspathResourceLoader loader = new ClasspathResourceLoader("beetl"); return javaMailSender;
//beetl配置 这里采用默认的配置- }
org.beetl.core.Configuration configuration = org.beetl.core.Configuration.defaultConfiguration();
return new GroupTemplate(loader, configuration); /***
} * 配置模板引擎
} */
@Bean
``` GroupTemplate groupTemplate() throws IOException {
//指定加载模板资源的位置 指定在classpath:beetl下-
#### 2.2 新建邮件发送基本类 ClasspathResourceLoader loader = new ClasspathResourceLoader("beetl");
//beetl配置 这里采用默认的配置-
```java org.beetl.core.Configuration configuration = org.beetl.core.Configuration.defaultConfiguration();
/** return new GroupTemplate(loader, configuration);
* @author : heibaiying }
* @description : 邮件发送基本类 }
*/
@Component ```
public class SpringMail {
#### 2.2 新建邮件发送基本类
@Autowired
private JavaMailSenderImpl qqMailSender; ```java
@Autowired /**
private GroupTemplate groupTemplate; * @author : heibaiying
* @description : 邮件发送基本类
/** */
* 发送简单邮件 @Component
* 在qq邮件发送的测试中测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户 public class SpringMail {
* 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user
* qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 中开启服务后获取 @Autowired
*/ private JavaMailSenderImpl qqMailSender;
public void sendTextMessage(String from, String authWord, String to, String subject, String content) { @Autowired
// 设置发送人邮箱和授权码 private GroupTemplate groupTemplate;
qqMailSender.setUsername(from);
qqMailSender.setPassword(authWord); /**
// 实例化消息对象 * 发送简单邮件
SimpleMailMessage msg = new SimpleMailMessage(); * 在qq邮件发送的测试中测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户
msg.setFrom(from); * 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user
msg.setTo(to); * qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 中开启服务后获取
msg.setSubject(subject); */
msg.setText(content); public void sendTextMessage(String from, String authWord, String to, String subject, String content) {
try { // 设置发送人邮箱和授权码
// 发送消息 qqMailSender.setUsername(from);
this.qqMailSender.send(msg); qqMailSender.setPassword(authWord);
System.out.println("发送邮件成功"); // 实例化消息对象
} catch (MailException ex) { SimpleMailMessage msg = new SimpleMailMessage();
// 消息发送失败可以做对应的处理 msg.setFrom(from);
System.err.println("发送邮件失败" + ex.getMessage()); msg.setTo(to);
} msg.setSubject(subject);
} msg.setText(content);
try {
/** // 发送消息
* 发送带附件的邮件 this.qqMailSender.send(msg);
*/ System.out.println("发送邮件成功");
public void sendEmailWithAttachments(String from, String authWord, String to, } catch (MailException ex) {
String subject, String content, Map<String, File> files) { // 消息发送失败可以做对应的处理
try { System.err.println("发送邮件失败" + ex.getMessage());
// 设置发送人邮箱和授权码 }
qqMailSender.setUsername(from); }
qqMailSender.setPassword(authWord);
// 实例化消息对象 /**
MimeMessage message = qqMailSender.createMimeMessage(); * 发送带附件的邮件
// 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息 */
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); public void sendEmailWithAttachments(String from, String authWord, String to,
helper.setFrom(from); String subject, String content, Map<String, File> files) {
helper.setTo(to); try {
helper.setSubject(subject); // 设置发送人邮箱和授权码
helper.setText(content); qqMailSender.setUsername(from);
// 传入附件 qqMailSender.setPassword(authWord);
for (Map.Entry<String, File> entry : files.entrySet()) { // 实例化消息对象
helper.addAttachment(entry.getKey(), entry.getValue()); MimeMessage message = qqMailSender.createMimeMessage();
} // 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息
// 发送消息 MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
this.qqMailSender.send(message); helper.setFrom(from);
System.out.println("发送邮件成功"); helper.setTo(to);
} catch (MessagingException ex) { helper.setSubject(subject);
// 消息发送失败可以做对应的处理 helper.setText(content);
System.err.println("发送邮件失败" + ex.getMessage()); // 传入附件
} for (Map.Entry<String, File> entry : files.entrySet()) {
} helper.addAttachment(entry.getKey(), entry.getValue());
}
// 发送消息
/** this.qqMailSender.send(message);
* 发送带内嵌资源的邮件 System.out.println("发送邮件成功");
*/ } catch (MessagingException ex) {
public void sendEmailWithInline(String from, String authWord, String to, // 消息发送失败可以做对应的处理
String subject, String content, File file) { System.err.println("发送邮件失败" + ex.getMessage());
try { }
// 设置发送人邮箱和授权码 }
qqMailSender.setUsername(from);
qqMailSender.setPassword(authWord);
// 实例化消息对象 /**
MimeMessage message = qqMailSender.createMimeMessage(); * 发送带内嵌资源的邮件
// 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息 */
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); public void sendEmailWithInline(String from, String authWord, String to,
helper.setFrom(from); String subject, String content, File file) {
helper.setTo(to); try {
helper.setSubject(subject); // 设置发送人邮箱和授权码
// 使用true标志来指示包含的文本是HTML 固定格式资源前缀 cid: qqMailSender.setUsername(from);
helper.setText("<html><body><img src='cid:image'></body></html>", true); qqMailSender.setPassword(authWord);
// 需要先指定文本 再指定资源文件 // 实例化消息对象
FileSystemResource res = new FileSystemResource(file); MimeMessage message = qqMailSender.createMimeMessage();
helper.addInline("image", res); // 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息
// 发送消息 MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
this.qqMailSender.send(message); helper.setFrom(from);
System.out.println("发送邮件成功"); helper.setTo(to);
} catch (MessagingException ex) { helper.setSubject(subject);
// 消息发送失败可以做对应的处理 // 使用true标志来指示包含的文本是HTML 固定格式资源前缀 cid:
System.err.println("发送邮件失败" + ex.getMessage()); helper.setText("<html><body><img src='cid:image'></body></html>", true);
} // 需要先指定文本 再指定资源文件
} FileSystemResource res = new FileSystemResource(file);
helper.addInline("image", res);
/** // 发送消息
* 使用模板邮件 this.qqMailSender.send(message);
*/ System.out.println("发送邮件成功");
public void sendEmailByTemplate(String from, String authWord, String to, } catch (MessagingException ex) {
String subject, String content) { // 消息发送失败可以做对应的处理
try { System.err.println("发送邮件失败" + ex.getMessage());
Template t = groupTemplate.getTemplate("template.html"); }
t.binding("subject", subject); }
t.binding("content", content);
String text = t.render(); /**
// 设置发送人邮箱和授权码 * 使用模板邮件
qqMailSender.setUsername(from); */
qqMailSender.setPassword(authWord); public void sendEmailByTemplate(String from, String authWord, String to,
// 实例化消息对象 String subject, String content) {
MimeMessage message = qqMailSender.createMimeMessage(); try {
// 指定 utf-8 防止乱码 Template t = groupTemplate.getTemplate("template.html");
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); t.binding("subject", subject);
helper.setFrom(from); t.binding("content", content);
helper.setTo(to); String text = t.render();
helper.setSubject(subject); // 设置发送人邮箱和授权码
// 为true 时候 表示文本内容以 html 渲染 qqMailSender.setUsername(from);
helper.setText(text, true); qqMailSender.setPassword(authWord);
this.qqMailSender.send(message); // 实例化消息对象
System.out.println("发送邮件成功"); MimeMessage message = qqMailSender.createMimeMessage();
} catch (MessagingException ex) { // 指定 utf-8 防止乱码
// 消息发送失败可以做对应的处理 MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
System.err.println("发送邮件失败" + ex.getMessage()); helper.setFrom(from);
} helper.setTo(to);
} helper.setSubject(subject);
// 为true 时候 表示文本内容以 html 渲染
} helper.setText(text, true);
this.qqMailSender.send(message);
``` System.out.println("发送邮件成功");
} catch (MessagingException ex) {
**关于模板邮件的说明:** // 消息发送失败可以做对应的处理
System.err.println("发送邮件失败" + ex.getMessage());
- 模板引擎最主要的作用是,在对邮件格式有要求的时候,采用拼接字符串不够直观,所以采用模板引擎; }
}
- 这里我们使用的beetl模板引擎原因是其性能优异官网是介绍其性能6倍与freemaker,并有完善的文档支持。当然大家也可以换成任何其他的模板引擎freemarker,thymeleaf
}
一个简单的模板template.html如下
```
```html
<!doctype html> **关于模板邮件的说明:**
<html lang="en">
<head> - 模板引擎最主要的作用是,在对邮件格式有要求的时候,采用拼接字符串不够直观,所以采用模板引擎;
<meta charset="UTF-8">
</head> - 这里我们使用的beetl模板引擎原因是其性能优异官网是介绍其性能6倍与freemaker,并有完善的文档支持。当然大家也可以换成任何其他的模板引擎freemarker,thymeleaf
<body>
<h1>邮件主题:<span style="color: chartreuse"> ${subject}</span></h1> 一个简单的模板template.html如下
<h4 style="color: blueviolet">${content}</h4>
</body> ```html
</html> <!doctype html>
``` <html lang="en">
<head>
<meta charset="UTF-8">
</head>
#### 2.3 邮件发送的测试 <body>
<h1>邮件主题:<span style="color: chartreuse"> ${subject}</span></h1>
```java <h4 style="color: blueviolet">${content}</h4>
/** </body>
* @author : heibaiying </html>
* @description : 发送邮件测试类 ```
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = EmailConfig.class)
public class SendEmail { #### 2.3 邮件发送的测试
@Autowired ```java
private SpringMail springMail; /**
* @author : heibaiying
// 发送方邮箱地址 * @description : 发送邮件测试类
private static final String from = "发送方邮箱地址@qq.com"; */
// 发送方邮箱地址对应的授权码 @RunWith(SpringRunner.class)
private static final String authWord = "授权码"; @ContextConfiguration(classes = EmailConfig.class)
// 接收方邮箱地址 public class SendEmail {
private static final String to = "接收方邮箱地址@qq.com";
@Autowired
@Test private SpringMail springMail;
public void sendMessage() {
// 发送方邮箱地址
springMail.sendTextMessage(from, authWord, to, "spring简单邮件", "Hello Spring Email!"); private static final String from = "发送方邮箱地址@qq.com";
} // 发送方邮箱地址对应的授权码
private static final String authWord = "授权码";
// 接收方邮箱地址
@Test private static final String to = "接收方邮箱地址@qq.com";
public void sendComplexMessage() {
Map<String, File> fileMap = new HashMap<>(); @Test
fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png")); public void sendMessage() {
fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png"));
springMail.sendEmailWithAttachments(from, authWord, to, "spring多附件邮件" springMail.sendTextMessage(from, authWord, to, "spring简单邮件", "Hello Spring Email!");
, "Hello Spring Email!", fileMap); }
}
@Test @Test
public void sendEmailWithInline() { public void sendComplexMessage() {
springMail.sendEmailWithInline(from, authWord, to, "spring内嵌资源邮件" Map<String, File> fileMap = new HashMap<>();
, "Hello Spring Email!", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png")); fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png"));
} fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png"));
springMail.sendEmailWithAttachments(from, authWord, to, "spring多附件邮件"
@Test , "Hello Spring Email!", fileMap);
public void sendEmailByTemplate() { }
springMail.sendEmailByTemplate(from, authWord, to,
"spring模板邮件", "Hello Spring Email!"); @Test
} public void sendEmailWithInline() {
} springMail.sendEmailWithInline(from, authWord, to, "spring内嵌资源邮件"
``` , "Hello Spring Email!", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png"));
}
@Test
public void sendEmailByTemplate() {
springMail.sendEmailByTemplate(from, authWord, to,
"spring模板邮件", "Hello Spring Email!");
}
}
```

View File

@ -1,298 +1,308 @@
# spring 邮件发送xml配置方式 # spring 邮件发送xml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-email">二、spring email</a><br/>
1. 邮件发送配置文件为springApplication.xml; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-邮件发送配置">2.1 邮件发送配置</a><br/>
2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-新建邮件发送基本类">2.2 新建邮件发送基本类</a><br/>
3. 项目以单元测试的方法进行测试测试类为SendEmail。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-邮件发送的测试">2.3 邮件发送的测试</a><br/>
## 正文<br/>
## 一、说明
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-email.png"/> </div>
### 1.1 项目结构说明
1. 邮件发送配置文件为springApplication.xml;
### 1.2 依赖说明 2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中
3. 项目以单元测试的方法进行测试测试类为SendEmail。
除了spring的基本依赖外需要导入邮件发送的支持包spring-context-support
```xml
<!--邮件发送依赖包--> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-email.png"/> </div>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring-base-version}</version> ### 1.2 依赖说明
</dependency>
<!--模板引擎--> 除了spring的基本依赖外需要导入邮件发送的支持包spring-context-support
<!--这里采用的是beetl,beetl性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">-->
<dependency> ```xml
<groupId>com.ibeetl</groupId> <!--邮件发送依赖包-->
<artifactId>beetl</artifactId> <dependency>
<version>2.9.7</version> <groupId>org.springframework</groupId>
</dependency> <artifactId>spring-context-support</artifactId>
``` <version>${spring-base-version}</version>
</dependency>
<!--模板引擎-->
<!--这里采用的是beetl,beetl性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">-->
## 二、spring email <dependency>
<groupId>com.ibeetl</groupId>
#### 2.1 邮件发送配置 <artifactId>beetl</artifactId>
<version>2.9.7</version>
```xml </dependency>
<?xml version="1.0" encoding="UTF-8"?> ```
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans ## 二、spring email
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context #### 2.1 邮件发送配置
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
```xml
<!-- 开启注解包扫描--> <?xml version="1.0" encoding="UTF-8"?>
<context:component-scan base-package="com.heibaiying.email"/> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!--在这里可以声明不同的邮件服务器主机通常是SMTP主机,而具体的用户名和时授权码则建议在业务中从数据库查询--> xmlns:context="http://www.springframework.org/schema/context"
<bean id="qqMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> xsi:schemaLocation="http://www.springframework.org/schema/beans
<!--qq 邮箱配置 <a href="https://service.mail.qq.com/cgi-bin/help?subtype=1&no=167&id=28"> --> http://www.springframework.org/schema/beans/spring-beans.xsd
<property name="host" value="smtp.qq.com"/> http://www.springframework.org/schema/context
<property name="port" value="587"/> http://www.springframework.org/schema/context/spring-context-4.1.xsd">
</bean>
<!-- 开启注解包扫描-->
<!--配置beetle模板引擎 如果不使用模板引擎,以下的配置不是必须的--> <context:component-scan base-package="com.heibaiying.email"/>
<bean id="resourceLoader" class="org.beetl.core.resource.ClasspathResourceLoader">
<!--指定加载模板资源的位置 指定在classpath:beetl下--> <!--在这里可以声明不同的邮件服务器主机通常是SMTP主机,而具体的用户名和时授权码则建议在业务中从数据库查询-->
<constructor-arg name="root" value="beetl"/> <bean id="qqMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
</bean> <!--qq 邮箱配置 <a href="https://service.mail.qq.com/cgi-bin/help?subtype=1&no=167&id=28"> -->
<!--beetl 配置 这里采用默认的配置--> <property name="host" value="smtp.qq.com"/>
<bean id="configuration" class="org.beetl.core.Configuration" init-method="defaultConfiguration"/> <property name="port" value="587"/>
<bean id="groupTemplate" class="org.beetl.core.GroupTemplate"> </bean>
<constructor-arg name="loader" ref="resourceLoader"/>
<constructor-arg name="conf" ref="configuration"/> <!--配置beetle模板引擎 如果不使用模板引擎,以下的配置不是必须的-->
</bean> <bean id="resourceLoader" class="org.beetl.core.resource.ClasspathResourceLoader">
<!--指定加载模板资源的位置 指定在classpath:beetl下-->
</beans> <constructor-arg name="root" value="beetl"/>
``` </bean>
<!--beetl 配置 这里采用默认的配置-->
#### 2.2 新建邮件发送基本类 <bean id="configuration" class="org.beetl.core.Configuration" init-method="defaultConfiguration"/>
<bean id="groupTemplate" class="org.beetl.core.GroupTemplate">
```java <constructor-arg name="loader" ref="resourceLoader"/>
/** <constructor-arg name="conf" ref="configuration"/>
* @author : heibaiying </bean>
* @description : 邮件发送基本类
*/ </beans>
@Component ```
public class SpringMail {
#### 2.2 新建邮件发送基本类
@Autowired
private JavaMailSenderImpl qqMailSender; ```java
@Autowired /**
private GroupTemplate groupTemplate; * @author : heibaiying
* @description : 邮件发送基本类
/** */
* 发送简单邮件 @Component
* 在qq邮件发送的测试中测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户 public class SpringMail {
* 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user
* qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 中开启服务后获取 @Autowired
*/ private JavaMailSenderImpl qqMailSender;
public void sendTextMessage(String from, String authWord, String to, String subject, String content) { @Autowired
// 设置发送人邮箱和授权码 private GroupTemplate groupTemplate;
qqMailSender.setUsername(from);
qqMailSender.setPassword(authWord); /**
// 实例化消息对象 * 发送简单邮件
SimpleMailMessage msg = new SimpleMailMessage(); * 在qq邮件发送的测试中测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户
msg.setFrom(from); * 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user
msg.setTo(to); * qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 中开启服务后获取
msg.setSubject(subject); */
msg.setText(content); public void sendTextMessage(String from, String authWord, String to, String subject, String content) {
try { // 设置发送人邮箱和授权码
// 发送消息 qqMailSender.setUsername(from);
this.qqMailSender.send(msg); qqMailSender.setPassword(authWord);
System.out.println("发送邮件成功"); // 实例化消息对象
} catch (MailException ex) { SimpleMailMessage msg = new SimpleMailMessage();
// 消息发送失败可以做对应的处理 msg.setFrom(from);
System.err.println("发送邮件失败" + ex.getMessage()); msg.setTo(to);
} msg.setSubject(subject);
} msg.setText(content);
try {
/** // 发送消息
* 发送带附件的邮件 this.qqMailSender.send(msg);
*/ System.out.println("发送邮件成功");
public void sendEmailWithAttachments(String from, String authWord, String to, } catch (MailException ex) {
String subject, String content, Map<String, File> files) { // 消息发送失败可以做对应的处理
try { System.err.println("发送邮件失败" + ex.getMessage());
// 设置发送人邮箱和授权码 }
qqMailSender.setUsername(from); }
qqMailSender.setPassword(authWord);
// 实例化消息对象 /**
MimeMessage message = qqMailSender.createMimeMessage(); * 发送带附件的邮件
// 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息 */
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); public void sendEmailWithAttachments(String from, String authWord, String to,
helper.setFrom(from); String subject, String content, Map<String, File> files) {
helper.setTo(to); try {
helper.setSubject(subject); // 设置发送人邮箱和授权码
helper.setText(content); qqMailSender.setUsername(from);
// 传入附件 qqMailSender.setPassword(authWord);
for (Map.Entry<String, File> entry : files.entrySet()) { // 实例化消息对象
helper.addAttachment(entry.getKey(), entry.getValue()); MimeMessage message = qqMailSender.createMimeMessage();
} // 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息
// 发送消息 MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
this.qqMailSender.send(message); helper.setFrom(from);
System.out.println("发送邮件成功"); helper.setTo(to);
} catch (MessagingException ex) { helper.setSubject(subject);
// 消息发送失败可以做对应的处理 helper.setText(content);
System.err.println("发送邮件失败" + ex.getMessage()); // 传入附件
} for (Map.Entry<String, File> entry : files.entrySet()) {
} helper.addAttachment(entry.getKey(), entry.getValue());
}
// 发送消息
/** this.qqMailSender.send(message);
* 发送带内嵌资源的邮件 System.out.println("发送邮件成功");
*/ } catch (MessagingException ex) {
public void sendEmailWithInline(String from, String authWord, String to, // 消息发送失败可以做对应的处理
String subject, String content, File file) { System.err.println("发送邮件失败" + ex.getMessage());
try { }
// 设置发送人邮箱和授权码 }
qqMailSender.setUsername(from);
qqMailSender.setPassword(authWord);
// 实例化消息对象 /**
MimeMessage message = qqMailSender.createMimeMessage(); * 发送带内嵌资源的邮件
// 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息 */
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); public void sendEmailWithInline(String from, String authWord, String to,
helper.setFrom(from); String subject, String content, File file) {
helper.setTo(to); try {
helper.setSubject(subject); // 设置发送人邮箱和授权码
// 使用true标志来指示包含的文本是HTML 固定格式资源前缀 cid: qqMailSender.setUsername(from);
helper.setText("<html><body><img src='cid:image'></body></html>", true); qqMailSender.setPassword(authWord);
// 需要先指定文本 再指定资源文件 // 实例化消息对象
FileSystemResource res = new FileSystemResource(file); MimeMessage message = qqMailSender.createMimeMessage();
helper.addInline("image", res); // 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息
// 发送消息 MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
this.qqMailSender.send(message); helper.setFrom(from);
System.out.println("发送邮件成功"); helper.setTo(to);
} catch (MessagingException ex) { helper.setSubject(subject);
// 消息发送失败可以做对应的处理 // 使用true标志来指示包含的文本是HTML 固定格式资源前缀 cid:
System.err.println("发送邮件失败" + ex.getMessage()); helper.setText("<html><body><img src='cid:image'></body></html>", true);
} // 需要先指定文本 再指定资源文件
} FileSystemResource res = new FileSystemResource(file);
helper.addInline("image", res);
/** // 发送消息
* 使用模板邮件 this.qqMailSender.send(message);
*/ System.out.println("发送邮件成功");
public void sendEmailByTemplate(String from, String authWord, String to, } catch (MessagingException ex) {
String subject, String content) { // 消息发送失败可以做对应的处理
try { System.err.println("发送邮件失败" + ex.getMessage());
Template t = groupTemplate.getTemplate("template.html"); }
t.binding("subject", subject); }
t.binding("content", content);
String text = t.render(); /**
// 设置发送人邮箱和授权码 * 使用模板邮件
qqMailSender.setUsername(from); */
qqMailSender.setPassword(authWord); public void sendEmailByTemplate(String from, String authWord, String to,
// 实例化消息对象 String subject, String content) {
MimeMessage message = qqMailSender.createMimeMessage(); try {
// 指定 utf-8 防止乱码 Template t = groupTemplate.getTemplate("template.html");
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); t.binding("subject", subject);
helper.setFrom(from); t.binding("content", content);
helper.setTo(to); String text = t.render();
helper.setSubject(subject); // 设置发送人邮箱和授权码
// 为true 时候 表示文本内容以 html 渲染 qqMailSender.setUsername(from);
helper.setText(text, true); qqMailSender.setPassword(authWord);
this.qqMailSender.send(message); // 实例化消息对象
System.out.println("发送邮件成功"); MimeMessage message = qqMailSender.createMimeMessage();
} catch (MessagingException ex) { // 指定 utf-8 防止乱码
// 消息发送失败可以做对应的处理 MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
System.err.println("发送邮件失败" + ex.getMessage()); helper.setFrom(from);
} helper.setTo(to);
} helper.setSubject(subject);
// 为true 时候 表示文本内容以 html 渲染
} helper.setText(text, true);
this.qqMailSender.send(message);
``` System.out.println("发送邮件成功");
} catch (MessagingException ex) {
**关于模板邮件的说明:** // 消息发送失败可以做对应的处理
System.err.println("发送邮件失败" + ex.getMessage());
- 模板引擎最主要的作用是,在对邮件格式有要求的时候,采用拼接字符串不够直观,所以采用模板引擎; }
}
- 这里我们使用的beetl模板引擎原因是其性能优异官网是介绍其性能6倍与freemaker,并有完善的文档支持。当然大家也可以换成任何其他的模板引擎freemarker,thymeleaf
}
一个简单的模板template.html如下
```
```html
<!doctype html> **关于模板邮件的说明:**
<html lang="en">
<head> - 模板引擎最主要的作用是,在对邮件格式有要求的时候,采用拼接字符串不够直观,所以采用模板引擎;
<meta charset="UTF-8">
</head> - 这里我们使用的beetl模板引擎原因是其性能优异官网是介绍其性能6倍与freemaker,并有完善的文档支持。当然大家也可以换成任何其他的模板引擎freemarker,thymeleaf
<body>
<h1>邮件主题:<span style="color: chartreuse"> ${subject}</span></h1> 一个简单的模板template.html如下
<h4 style="color: blueviolet">${content}</h4>
</body> ```html
</html> <!doctype html>
``` <html lang="en">
<head>
<meta charset="UTF-8">
</head>
#### 2.3 邮件发送的测试 <body>
<h1>邮件主题:<span style="color: chartreuse"> ${subject}</span></h1>
```java <h4 style="color: blueviolet">${content}</h4>
/** </body>
* @author : heibaiying </html>
* @description : 发送邮件测试类 ```
*/
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class SendEmail { #### 2.3 邮件发送的测试
@Autowired ```java
private SpringMail springMail; /**
* @author : heibaiying
// 发送方邮箱地址 * @description : 发送邮件测试类
private static final String from = "发送方邮箱地址@qq.com"; */
// 发送方邮箱地址对应的授权码 @RunWith(SpringRunner.class)
private static final String authWord = "授权码"; @ContextConfiguration({"classpath:springApplication.xml"})
// 接收方邮箱地址 public class SendEmail {
private static final String to = "接收方邮箱地址@qq.com";
@Autowired
private SpringMail springMail;
/**
* 简单邮件测试 // 发送方邮箱地址
*/ private static final String from = "发送方邮箱地址@qq.com";
@Test // 发送方邮箱地址对应的授权码
public void sendMessage() { private static final String authWord = "授权码";
springMail.sendTextMessage(from, authWord, to, "spring简单邮件", "Hello Spring Email!"); // 接收方邮箱地址
} private static final String to = "接收方邮箱地址@qq.com";
/**
* 发送带附件的邮件 /**
*/ * 简单邮件测试
@Test */
public void sendComplexMessage() { @Test
Map<String, File> fileMap = new HashMap<>(); public void sendMessage() {
fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png")); springMail.sendTextMessage(from, authWord, to, "spring简单邮件", "Hello Spring Email!");
fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png")); }
springMail.sendEmailWithAttachments(from, authWord, to, "spring多附件邮件"
, "Hello Spring Email!", fileMap); /**
} * 发送带附件的邮件
*/
/** @Test
* 发送内嵌资源的邮件 public void sendComplexMessage() {
*/ Map<String, File> fileMap = new HashMap<>();
@Test fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png"));
public void sendEmailWithInline() { fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png"));
springMail.sendEmailWithInline(from, authWord, to, "spring内嵌资源邮件" springMail.sendEmailWithAttachments(from, authWord, to, "spring多附件邮件"
, "Hello Spring Email!", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png")); , "Hello Spring Email!", fileMap);
} }
/** /**
* 发送模板邮件 * 发送内嵌资源的邮件
*/ */
@Test @Test
public void sendEmailByTemplate() { public void sendEmailWithInline() {
springMail.sendEmailByTemplate(from, authWord, to, springMail.sendEmailWithInline(from, authWord, to, "spring内嵌资源邮件"
"spring模板邮件", "Hello Spring Email!"); , "Hello Spring Email!", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png"));
} }
}
/**
``` * 发送模板邮件
*/
@Test
public void sendEmailByTemplate() {
springMail.sendEmailByTemplate(from, authWord, to,
"spring模板邮件", "Hello Spring Email!");
}
}
```

View File

@ -1,5 +1,16 @@
# spring 整合 jdbc template注解方式 # spring 整合 jdbc template注解方式
## 目录<br/>
<a href="#1说明">1.说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12--项目依赖">1.2 项目依赖</a><br/>
<a href="#二spring-整合-jdbc-template">二、spring 整合 jdbc template</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-在resources文件夹下新建数据库配置文件mysqlpropertiesoracleproperties及其映射类">2.1 在resources文件夹下新建数据库配置文件mysql.properties、oracle.properties及其映射类</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-新建数据库配置类DatabaseConfigjava">2.2 新建数据库配置类DatabaseConfig.java</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-新建查询接口及其实现类这里我查询的表是mysql和oracle中的字典表">2.3 新建查询接口及其实现类</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-新建测试类进行测试">2.4 新建测试类进行测试</a><br/>
## 正文<br/>
## 1.说明 ## 1.说明
#### 1.1 项目目录结构 #### 1.1 项目目录结构

View File

@ -1,5 +1,17 @@
# spring 整合 jdbc templatexml配置方式 # spring 整合 jdbc templatexml配置方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12--项目依赖">1.2 项目依赖</a><br/>
<a href="#二-spring-整合-jdbc-template">二、 spring 整合 jdbc template</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#1在resources文件夹下新建数据库配置文件jdbcproperties">1、在resources文件夹下新建数据库配置文件jdbc.properties</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#2配置Jdbc数据源并定义事务管理器">2、配置Jdbc数据源并定义事务管理器</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#3新建查询接口及其实现类这里我查询的表是mysql和oracle中的字典表">3、新建查询接口及其实现类</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#4新建测试类进行测试">4.新建测试类进行测试</a><br/>
## 正文<br/>
## 一、说明 ## 一、说明
#### 1.1 项目结构 #### 1.1 项目结构

View File

@ -1,150 +1,163 @@
# spring 整合 mecached注解方式 # spring 整合 mecached注解方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/>
### 1.1 XMemcached客户端说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#13-依赖说明">1.3 依赖说明</a><br/>
XMemcached是基于java nio的memcached高性能客户端支持完整的memcached协议支持客户端分布并且提供了一致性哈希(consistent hash)算法的实现。 <a href="#二spring-整合-memcached">二、spring 整合 memcached</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-单机配置">2.1 单机配置</a><br/>
### 1.2 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-集群配置">2.2 集群配置</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-存储基本类型测试用例">2.3 存储基本类型测试用例</a><br/>
1. memcached的整合配置位于com.heibaiying.config文件夹下。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25-存储实体对象测试用例">2.5 存储实体对象测试用例</a><br/>
<a href="#附memcached-基本命令">memcached 基本命令</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached-annotation.png"/> </div> ## 正文<br/>
### 1.3 依赖说明
除了spring的基本依赖外需要导入xmemcached依赖包
```xml
<!--memcached java 客户端-->
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.5</version>
</dependency>
```
## 一、说明
## 二、spring 整合 memcached
### 1.1 XMemcached客户端说明
#### 2.1 单机配置
XMemcached是基于java nio的memcached高性能客户端支持完整的memcached协议支持客户端分布并且提供了一致性哈希(consistent hash)算法的实现。
```java
@Bean ### 1.2 项目结构说明
public MemcachedClient memcachedClient() {
XMemcachedClientBuilder builder = new XMemcachedClientBuilder("192.168.200.201:11211"); 1. memcached的整合配置位于com.heibaiying.config文件夹下。
MemcachedClient memcachedClient = null;
try { <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached-annotation.png"/> </div>
memcachedClient = builder.build();
} catch (IOException e) { ### 1.3 依赖说明
e.printStackTrace();
} 除了spring的基本依赖外需要导入xmemcached依赖包
return memcachedClient;
} ```xml
``` <!--memcached java 客户端-->
<dependency>
#### 2.2 集群配置 <groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
```java <version>2.4.5</version>
@Bean </dependency>
public MemcachedClient memcachedClientForCluster() { ```
List<InetSocketAddress> addressList = new ArrayList<InetSocketAddress>();
addressList.add(new InetSocketAddress("192.168.200.201", 11211));
addressList.add(new InetSocketAddress("192.168.200.201", 11212)); ## 二、spring 整合 memcached
// 赋予权重
int[] weights = {1, 2}; #### 2.1 单机配置
XMemcachedClientBuilder builder = new XMemcachedClientBuilder(addressList, weights);
// 设置连接池大小 ```java
builder.setConnectionPoolSize(10); @Bean
// 协议工厂 public MemcachedClient memcachedClient() {
builder.setCommandFactory(new TextCommandFactory()); XMemcachedClientBuilder builder = new XMemcachedClientBuilder("192.168.200.201:11211");
// 分布策略一致性哈希KetamaMemcachedSessionLocator或者ArraySessionLocator(默认) MemcachedClient memcachedClient = null;
builder.setSessionLocator(new KetamaMemcachedSessionLocator()); try {
// 设置序列化器 memcachedClient = builder.build();
builder.setTranscoder(new SerializingTranscoder()); } catch (IOException e) {
MemcachedClient memcachedClient = null; e.printStackTrace();
try { }
memcachedClient = builder.build(); return memcachedClient;
} catch (IOException e) { }
e.printStackTrace(); ```
}
return memcachedClient; #### 2.2 集群配置
}
``` ```java
@Bean
#### 2.3 存储基本类型测试用例 public MemcachedClient memcachedClientForCluster() {
xmemcached单机版本和集群版本注入的实例是相同的 List<InetSocketAddress> addressList = new ArrayList<InetSocketAddress>();
addressList.add(new InetSocketAddress("192.168.200.201", 11211));
```java addressList.add(new InetSocketAddress("192.168.200.201", 11212));
/** // 赋予权重
* @author : heibaiying int[] weights = {1, 2};
* @description : Memcached 操作基本对象 XMemcachedClientBuilder builder = new XMemcachedClientBuilder(addressList, weights);
*/ // 设置连接池大小
@RunWith(SpringRunner.class) builder.setConnectionPoolSize(10);
@ContextConfiguration(classes = {MemcacheConfig.class}) // 协议工厂
public class MemSamples { builder.setCommandFactory(new TextCommandFactory());
// 分布策略一致性哈希KetamaMemcachedSessionLocator或者ArraySessionLocator(默认)
@Autowired builder.setSessionLocator(new KetamaMemcachedSessionLocator());
private MemcachedClient memcachedClient; // 设置序列化器
builder.setTranscoder(new SerializingTranscoder());
@Test MemcachedClient memcachedClient = null;
public void operate() throws InterruptedException, MemcachedException, TimeoutException { try {
memcachedClient.set("hello", 0, "Hello,cluster xmemcached"); memcachedClient = builder.build();
String value = memcachedClient.get("hello"); } catch (IOException e) {
System.out.println("hello=" + value); e.printStackTrace();
memcachedClient.delete("hello"); }
value = memcachedClient.get("hello"); return memcachedClient;
System.out.println("hello=" + value); }
} ```
}
``` #### 2.3 存储基本类型测试用例
#### 2.5 存储实体对象测试用例 xmemcached单机版本和集群版本注入的实例是相同的
```java ```java
/** /**
* @author : heibaiying * @author : heibaiying
* @description :Memcached 序列化与反序列化 * @description : Memcached 操作基本对象
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ContextConfiguration(classes = {MemcacheConfig.class}) @ContextConfiguration(classes = {MemcacheConfig.class})
public class MemObjectSamples { public class MemSamples {
@Autowired @Autowired
private MemcachedClient memcachedClient; private MemcachedClient memcachedClient;
@Test @Test
public void operate() throws InterruptedException, MemcachedException, TimeoutException { public void operate() throws InterruptedException, MemcachedException, TimeoutException {
memcachedClient.set("programmer", 0, new Programmer("xiaoming", 12, 5000.21f, new Date())); memcachedClient.set("hello", 0, "Hello,cluster xmemcached");
Programmer programmer = memcachedClient.get("programmer"); String value = memcachedClient.get("hello");
System.out.println("hello ," + programmer.getName()); System.out.println("hello=" + value);
memcachedClient.delete("programmer"); memcachedClient.delete("hello");
programmer = memcachedClient.get("programmer"); value = memcachedClient.get("hello");
Assert.assertNull(programmer); System.out.println("hello=" + value);
} }
} }
```
```
#### 2.5 存储实体对象测试用例
```java
## 附memcached 基本命令 /**
* @author : heibaiying
| 命令 | 格式 | 说明 | * @description :Memcached 序列化与反序列化
| --------------- | -------------------------------------------------- | ------------------------------------- | */
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | @RunWith(SpringRunner.class)
| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下才可以插入 | @ContextConfiguration(classes = {MemcacheConfig.class})
| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 | public class MemObjectSamples {
| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 |
| 前面追加prepend | prepend key flags exTime length -> value | length表示追加的长度而不是总长度 | @Autowired
| 查询操作 get | get key | | private MemcachedClient memcachedClient;
| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 |
| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 | @Test
| 删除 delete | delete key | 将数据打一个删除标记 | public void operate() throws InterruptedException, MemcachedException, TimeoutException {
| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value | memcachedClient.set("programmer", 0, new Programmer("xiaoming", 12, 5000.21f, new Date()));
| 自减 decr | decr key 减少偏移量 | desr不能将数字减少至0以下 | Programmer programmer = memcachedClient.get("programmer");
System.out.println("hello ," + programmer.getName());
memcachedClient.delete("programmer");
programmer = memcachedClient.get("programmer");
Assert.assertNull(programmer);
}
}
```
## 附memcached 基本命令
| 命令 | 格式 | 说明 |
| --------------- | -------------------------------------------------- | ------------------------------------- |
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 |
| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下才可以插入 |
| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 |
| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 |
| 前面追加prepend | prepend key flags exTime length -> value | length表示追加的长度而不是总长度 |
| 查询操作 get | get key | |
| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 |
| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 |
| 删除 delete | delete key | 将数据打一个删除标记 |
| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value |
| 自减 decr | decr key 减少偏移量 | desr不能将数字减少至0以下 |
| 清库 | flush_all | | | 清库 | flush_all | |

View File

@ -1,191 +1,204 @@
# spring 整合 mecachedxml配置方式 # spring 整合 mecachedxml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/>
### 1.1 XMemcached客户端说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#13-依赖说明">1.3 依赖说明</a><br/>
XMemcached是基于java nio的memcached高性能客户端支持完整的memcached协议支持客户端分布并且提供了一致性哈希(consistent hash)算法的实现。 <a href="#二spring-整合-memcached">二、spring 整合 memcached</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-单机配置">2.1 单机配置</a><br/>
### 1.2 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-集群配置">2.2 集群配置</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-存储基本类型测试用例">2.3 存储基本类型测试用例</a><br/>
1. memcached的整合配置位于resources下的memcached文件夹下其中集群配置用cluster开头。所有配置按照需要在springApplication.xml用import导入。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25-存储实体对象测试用例">2.5 存储实体对象测试用例</a><br/>
2. 实体类Programmer.java用于测试memcached序列化与反序列化 <a href="#附memcached-基本命令">memcached 基本命令</a><br/>
## 正文<br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached.png"/> </div>
**springapplication.xml文件**
```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--memcached 单机版配置-->
<!--<import resource="classpath*:memcached/singleConfig.xml"/>-->
<!--memcached 集群配置-->
<import resource="classpath*:memcached/clusterConfig.xml"/>
</beans>
```
### 1.3 依赖说明
除了spring的基本依赖外需要导入xmemcached依赖包
```xml
<!--memcached java 客户端-->
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.5</version>
</dependency>
```
## 一、说明
## 二、spring 整合 memcached
### 1.1 XMemcached客户端说明
#### 2.1 单机配置
XMemcached是基于java nio的memcached高性能客户端支持完整的memcached协议支持客户端分布并且提供了一致性哈希(consistent hash)算法的实现。
```xml
<?xml version="1.0" encoding="UTF-8"?> ### 1.2 项目结构说明
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 1. memcached的整合配置位于resources下的memcached文件夹下其中集群配置用cluster开头。所有配置按照需要在springApplication.xml用import导入。
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 2. 实体类Programmer.java用于测试memcached序列化与反序列化
<bean id="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder"> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached.png"/> </div>
<constructor-arg name="addressList" value="192.168.200.201:11211"/>
</bean> **springapplication.xml文件**
<bean id="memcachedClient" factory-bean="memcachedClientBuilder" factory-method="build" ```xml
destroy-method="shutdown"/> <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
</beans> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
``` xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
#### 2.2 集群配置 <!--memcached 单机版配置-->
<!--<import resource="classpath*:memcached/singleConfig.xml"/>-->
```xml
<?xml version="1.0" encoding="UTF-8"?> <!--memcached 集群配置-->
<beans xmlns="http://www.springframework.org/schema/beans" <import resource="classpath*:memcached/clusterConfig.xml"/>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
```
<bean name="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder">
<!--memcached servers 节点列表--> ### 1.3 依赖说明
<constructor-arg name="addressList">
<list> 除了spring的基本依赖外需要导入xmemcached依赖包
<bean class="java.net.InetSocketAddress">
<constructor-arg value="192.168.200.201"/> ```xml
<constructor-arg value="11211"/> <!--memcached java 客户端-->
</bean> <dependency>
<bean class="java.net.InetSocketAddress"> <groupId>com.googlecode.xmemcached</groupId>
<constructor-arg value="192.168.200.201"/> <artifactId>xmemcached</artifactId>
<constructor-arg value="11212"/> <version>2.4.5</version>
</bean> </dependency>
</list> ```
</constructor-arg>
<!--与servers对应的节点的权重-->
<constructor-arg name="weights">
<list> ## 二、spring 整合 memcached
<value>1</value>
<value>2</value> #### 2.1 单机配置
</list>
</constructor-arg> ```xml
<!--连接池大小--> <?xml version="1.0" encoding="UTF-8"?>
<property name="connectionPoolSize" value="10"/> <beans xmlns="http://www.springframework.org/schema/beans"
<!--协议工厂--> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<property name="commandFactory"> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="net.rubyeye.xmemcached.command.TextCommandFactory"/>
</property> <bean id="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder">
<!--分布策略一致性哈希net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator或者ArraySessionLocator(默认)--> <constructor-arg name="addressList" value="192.168.200.201:11211"/>
<property name="sessionLocator"> </bean>
<bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"/>
</property> <bean id="memcachedClient" factory-bean="memcachedClientBuilder" factory-method="build"
<!--序列化转换器默认使用net.rubyeye.xmemcached.transcoders.SerializingTranscoder--> destroy-method="shutdown"/>
<property name="transcoder">
<bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder"/> </beans>
</property> ```
</bean>
<!-- 集群配置 实例化bean --> #### 2.2 集群配置
<bean name="memcachedClientForCulster" factory-bean="memcachedClientBuilder"
factory-method="build" destroy-method="shutdown"/> ```xml
</beans> <?xml version="1.0" encoding="UTF-8"?>
``` <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
#### 2.3 存储基本类型测试用例 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
xmemcached单机版本和集群版本注入的实例是相同的 <bean name="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder">
<!--memcached servers 节点列表-->
```java <constructor-arg name="addressList">
/** <list>
* @author : heibaiying <bean class="java.net.InetSocketAddress">
* @description : Memcached 操作基本对象 <constructor-arg value="192.168.200.201"/>
*/ <constructor-arg value="11211"/>
@RunWith(SpringRunner.class) </bean>
@ContextConfiguration({"classpath:springApplication.xml"}) <bean class="java.net.InetSocketAddress">
public class MemSamples { <constructor-arg value="192.168.200.201"/>
<constructor-arg value="11212"/>
@Autowired </bean>
private MemcachedClient memcachedClient; </list>
</constructor-arg>
@Test <!--与servers对应的节点的权重-->
public void operate() throws InterruptedException, MemcachedException, TimeoutException { <constructor-arg name="weights">
memcachedClient.set("hello", 0, "Hello,cluster xmemcached"); <list>
String value = memcachedClient.get("hello"); <value>1</value>
System.out.println("hello=" + value); <value>2</value>
memcachedClient.delete("hello"); </list>
value = memcachedClient.get("hello"); </constructor-arg>
System.out.println("hello=" + value); <!--连接池大小-->
} <property name="connectionPoolSize" value="10"/>
} <!--协议工厂-->
<property name="commandFactory">
``` <bean class="net.rubyeye.xmemcached.command.TextCommandFactory"/>
</property>
#### 2.5 存储实体对象测试用例 <!--分布策略一致性哈希net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator或者ArraySessionLocator(默认)-->
<property name="sessionLocator">
```java <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"/>
/** </property>
* @author : heibaiying <!--序列化转换器默认使用net.rubyeye.xmemcached.transcoders.SerializingTranscoder-->
* @description :Memcached 序列化与反序列化 <property name="transcoder">
*/ <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder"/>
@RunWith(SpringRunner.class) </property>
@ContextConfiguration({"classpath:springApplication.xml"}) </bean>
public class MemObjectSamples { <!-- 集群配置 实例化bean -->
<bean name="memcachedClientForCulster" factory-bean="memcachedClientBuilder"
@Autowired factory-method="build" destroy-method="shutdown"/>
private MemcachedClient memcachedClient; </beans>
```
@Test
public void operate() throws InterruptedException, MemcachedException, TimeoutException { #### 2.3 存储基本类型测试用例
memcachedClient.set("programmer", 0, new Programmer("xiaoming", 12, 5000.21f, new Date()));
Programmer programmer = memcachedClient.get("programmer"); xmemcached单机版本和集群版本注入的实例是相同的
System.out.println("hello ," + programmer.getName());
memcachedClient.delete("programmer"); ```java
programmer = memcachedClient.get("programmer"); /**
Assert.assertNull(programmer); * @author : heibaiying
} * @description : Memcached 操作基本对象
} */
@RunWith(SpringRunner.class)
``` @ContextConfiguration({"classpath:springApplication.xml"})
public class MemSamples {
@Autowired
## 附memcached 基本命令 private MemcachedClient memcachedClient;
| 命令 | 格式 | 说明 | @Test
| --------------- | -------------------------------------------------- | ------------------------------------- | public void operate() throws InterruptedException, MemcachedException, TimeoutException {
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | memcachedClient.set("hello", 0, "Hello,cluster xmemcached");
| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下才可以插入 | String value = memcachedClient.get("hello");
| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 | System.out.println("hello=" + value);
| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 | memcachedClient.delete("hello");
| 前面追加prepend | prepend key flags exTime length -> value | length表示追加的长度而不是总长度 | value = memcachedClient.get("hello");
| 查询操作 get | get key | | System.out.println("hello=" + value);
| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 | }
| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 | }
| 删除 delete | delete key | 将数据打一个删除标记 |
| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value | ```
| 自减 decr | decr key 减少偏移量 | desr不能将数字减少至0以下 |
#### 2.5 存储实体对象测试用例
```java
/**
* @author : heibaiying
* @description :Memcached 序列化与反序列化
*/
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class MemObjectSamples {
@Autowired
private MemcachedClient memcachedClient;
@Test
public void operate() throws InterruptedException, MemcachedException, TimeoutException {
memcachedClient.set("programmer", 0, new Programmer("xiaoming", 12, 5000.21f, new Date()));
Programmer programmer = memcachedClient.get("programmer");
System.out.println("hello ," + programmer.getName());
memcachedClient.delete("programmer");
programmer = memcachedClient.get("programmer");
Assert.assertNull(programmer);
}
}
```
## 附memcached 基本命令
| 命令 | 格式 | 说明 |
| --------------- | -------------------------------------------------- | ------------------------------------- |
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 |
| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下才可以插入 |
| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 |
| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 |
| 前面追加prepend | prepend key flags exTime length -> value | length表示追加的长度而不是总长度 |
| 查询操作 get | get key | |
| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 |
| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 |
| 删除 delete | delete key | 将数据打一个删除标记 |
| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value |
| 自减 decr | decr key 减少偏移量 | desr不能将数字减少至0以下 |
| 清库 | flush_all | | | 清库 | flush_all | |

View File

@ -1,161 +1,171 @@
# spring 整合 mongodb注解方式 # spring 整合 mongodb注解方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-mongodb">二、spring mongodb</a><br/>
配置文件位于com.heibaiying.config下,项目以单元测试的方式进行测试。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-新建配置文件及其映射类">2.1 新建配置文件及其映射类</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-整合配置">2.2 整合配置</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb-annotation.png"/> </div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-测试整合">2.3 测试整合</a><br/>
## 正文<br/>
## 一、说明
### 1.2 依赖说明
### 1.1 项目结构说明
除了spring的基本依赖外需要导入mongodb整合依赖包
配置文件位于com.heibaiying.config下,项目以单元测试的方式进行测试。
```xml
<!--spring mongodb 整合依赖--> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb-annotation.png"/> </div>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.1.3.RELEASE</version> ### 1.2 依赖说明
</dependency>
``` 除了spring的基本依赖外需要导入mongodb整合依赖包
```xml
<!--spring mongodb 整合依赖-->
## 二、spring mongodb <dependency>
<groupId>org.springframework.data</groupId>
#### 2.1 新建配置文件及其映射类 <artifactId>spring-data-mongodb</artifactId>
<version>2.1.3.RELEASE</version>
```properties </dependency>
mongo.host=192.168.200.228 ```
mongo.port=27017
# 数据库名称. 默认是'db'.
mongo.dbname=database
# 每个主机允许的连接数 ## 二、spring mongodb
mongo.connectionsPerHost=10
# 线程队列数它和上面connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出异常 #### 2.1 新建配置文件及其映射类
mongo.threadsAllowedToBlockForConnectionMultiplier=5
# 连接超时的毫秒 0是默认值且无限大。 ```properties
mongo.connectTimeout=1000 mongo.host=192.168.200.228
# 最大等待连接的线程阻塞时间 默认是120000 ms (2 minutes). mongo.port=27017
mongo.maxWaitTime=1500 # 数据库名称. 默认是'db'.
# 保持活动标志,控制是否有套接字保持活动超时 官方默认为true 且不建议禁用 mongo.dbname=database
mongo.socketKeepAlive=true # 每个主机允许的连接数
# 用于群集心跳的连接的套接字超时。 mongo.connectionsPerHost=10
mongo.socketTimeout=1500 # 线程队列数它和上面connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出异常
``` mongo.threadsAllowedToBlockForConnectionMultiplier=5
# 连接超时的毫秒 0是默认值且无限大。
```java mongo.connectTimeout=1000
/** # 最大等待连接的线程阻塞时间 默认是120000 ms (2 minutes).
* @author : heibaiying mongo.maxWaitTime=1500
* @description : Mongo 配置属性 # 保持活动标志,控制是否有套接字保持活动超时 官方默认为true 且不建议禁用
*/ mongo.socketKeepAlive=true
@Data # 用于群集心跳的连接的套接字超时。
@Configuration mongo.socketTimeout=1500
@PropertySource(value = "classpath:mongodb.properties") ```
public class MongoProperty {
```java
@Value("${mongo.host}") /**
private String host; * @author : heibaiying
@Value("${mongo.port}") * @description : Mongo 配置属性
private int port; */
@Value("${mongo.dbname}") @Data
private String dbname; @Configuration
@Value("${mongo.connectionsPerHost}") @PropertySource(value = "classpath:mongodb.properties")
private int connectionsPerHost; public class MongoProperty {
@Value("${mongo.threadsAllowedToBlockForConnectionMultiplier}")
private int multiplier; @Value("${mongo.host}")
@Value("${mongo.connectTimeout}") private String host;
private int connectTimeout; @Value("${mongo.port}")
@Value("${mongo.maxWaitTime}") private int port;
private int maxWaitTime; @Value("${mongo.dbname}")
@Value("${mongo.socketKeepAlive}") private String dbname;
private boolean socketKeepAlive; @Value("${mongo.connectionsPerHost}")
@Value("${mongo.socketTimeout}") private int connectionsPerHost;
private int socketTimeout; @Value("${mongo.threadsAllowedToBlockForConnectionMultiplier}")
} private int multiplier;
``` @Value("${mongo.connectTimeout}")
private int connectTimeout;
#### 2.2 整合配置 @Value("${mongo.maxWaitTime}")
private int maxWaitTime;
```java @Value("${mongo.socketKeepAlive}")
/** private boolean socketKeepAlive;
* @author : heibaiying @Value("${mongo.socketTimeout}")
* @description : Mongo 配置类 private int socketTimeout;
*/ }
```
@Configuration
@ComponentScan(value = "com.heibaiying.*") #### 2.2 整合配置
public class MongoConfig {
```java
@Bean /**
public MongoDbFactory mongoDbFactory(MongoProperty mongo) { * @author : heibaiying
MongoClientOptions options = MongoClientOptions.builder() * @description : Mongo 配置类
.threadsAllowedToBlockForConnectionMultiplier(mongo.getMultiplier()) */
.connectionsPerHost(mongo.getConnectionsPerHost())
.connectTimeout(mongo.getConnectTimeout()) @Configuration
.maxWaitTime(mongo.getMaxWaitTime()) @ComponentScan(value = "com.heibaiying.*")
.socketTimeout(mongo.getSocketTimeout()) public class MongoConfig {
.build();
MongoClient client = new MongoClient(new ServerAddress(mongo.getHost(), mongo.getPort()), options); @Bean
return new SimpleMongoDbFactory(client, mongo.getDbname()); public MongoDbFactory mongoDbFactory(MongoProperty mongo) {
} MongoClientOptions options = MongoClientOptions.builder()
.threadsAllowedToBlockForConnectionMultiplier(mongo.getMultiplier())
@Bean .connectionsPerHost(mongo.getConnectionsPerHost())
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory) { .connectTimeout(mongo.getConnectTimeout())
return new MongoTemplate(mongoDbFactory); .maxWaitTime(mongo.getMaxWaitTime())
} .socketTimeout(mongo.getSocketTimeout())
} .build();
``` MongoClient client = new MongoClient(new ServerAddress(mongo.getHost(), mongo.getPort()), options);
return new SimpleMongoDbFactory(client, mongo.getDbname());
#### 2.3 测试整合 }
```java @Bean
@RunWith(SpringRunner.class) public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory) {
@ContextConfiguration(classes = MongoConfig.class) return new MongoTemplate(mongoDbFactory);
public class MongoDBTest { }
}
@Autowired ```
private MongoTemplate mongoTemplate;
#### 2.3 测试整合
@Test
public void insert() { ```java
// 单条插入 @RunWith(SpringRunner.class)
mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date())); @ContextConfiguration(classes = MongoConfig.class)
List<Programmer> programmers = new ArrayList<Programmer>(); public class MongoDBTest {
// 批量插入
programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date())); @Autowired
programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date())); private MongoTemplate mongoTemplate;
mongoTemplate.insert(programmers, Programmer.class);
} @Test
public void insert() {
// 条件查询 // 单条插入
@Test mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date()));
public void select() { List<Programmer> programmers = new ArrayList<Programmer>();
Criteria criteria = new Criteria(); // 批量插入
criteria.andOperator(where("name").is("xiaohong"), where("age").is(21)); programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date()));
Query query = new Query(criteria); programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date()));
Programmer one = mongoTemplate.findOne(query, Programmer.class); mongoTemplate.insert(programmers, Programmer.class);
System.out.println(one); }
}
// 条件查询
@Test
// 更新数据 public void select() {
@Test Criteria criteria = new Criteria();
public void MUpdate() { criteria.andOperator(where("name").is("xiaohong"), where("age").is(21));
UpdateResult updateResult = mongoTemplate.updateMulti(query(where("name").is("xiaoming")), update("age", 35), Programmer.class); Query query = new Query(criteria);
System.out.println("更新记录数:" + updateResult.getModifiedCount()); Programmer one = mongoTemplate.findOne(query, Programmer.class);
} System.out.println(one);
}
// 删除指定数据
@Test
public void delete() { // 更新数据
DeleteResult result = mongoTemplate.remove(query(where("name").is("xiaolan")), Programmer.class); @Test
System.out.println("影响记录数:" + result.getDeletedCount()); public void MUpdate() {
System.out.println("是否成功:" + result.wasAcknowledged()); UpdateResult updateResult = mongoTemplate.updateMulti(query(where("name").is("xiaoming")), update("age", 35), Programmer.class);
} System.out.println("更新记录数:" + updateResult.getModifiedCount());
} }
```
// 删除指定数据
@Test
public void delete() {
DeleteResult result = mongoTemplate.remove(query(where("name").is("xiaolan")), Programmer.class);
System.out.println("影响记录数:" + result.getDeletedCount());
System.out.println("是否成功:" + result.wasAcknowledged());
}
}
```

View File

@ -1,144 +1,154 @@
# spring 整合 mongodbxml配置方式 # spring 整合 mongodbxml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-mongodb">二、spring mongodb</a><br/>
配置文件位于resources下,项目以单元测试的方式进行测试。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-新建配置文件">2.1 新建配置文件</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-整合配置">2.2 整合配置</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb.png"/> </div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-测试整合">2.3 测试整合</a><br/>
## 正文<br/>
## 一、说明
### 1.2 依赖说明
### 1.1 项目结构说明
除了spring的基本依赖外需要导入mongodb整合依赖包
配置文件位于resources下,项目以单元测试的方式进行测试。
```xml
<!--spring mongodb 整合依赖--> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb.png"/> </div>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.1.3.RELEASE</version> ### 1.2 依赖说明
</dependency>
``` 除了spring的基本依赖外需要导入mongodb整合依赖包
```xml
<!--spring mongodb 整合依赖-->
## 二、spring mongodb <dependency>
<groupId>org.springframework.data</groupId>
#### 2.1 新建配置文件 <artifactId>spring-data-mongodb</artifactId>
<version>2.1.3.RELEASE</version>
```properties </dependency>
mongo.host=192.168.200.228 ```
mongo.port=27017
# 数据库名称. 默认是'db'.
mongo.dbname=database
# 每个主机允许的连接数 ## 二、spring mongodb
mongo.connectionsPerHost=10
# 线程队列数它和上面connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出异常 #### 2.1 新建配置文件
mongo.threadsAllowedToBlockForConnectionMultiplier=5
# 连接超时的毫秒 0是默认值且无限大。 ```properties
mongo.connectTimeout=1000 mongo.host=192.168.200.228
# 最大等待连接的线程阻塞时间 默认是120000 ms (2 minutes). mongo.port=27017
mongo.maxWaitTime=1500 # 数据库名称. 默认是'db'.
# 保持活动标志,控制是否有套接字保持活动超时 官方默认为true 且不建议禁用 mongo.dbname=database
mongo.socketKeepAlive=true # 每个主机允许的连接数
# 用于群集心跳的连接的套接字超时。 mongo.connectionsPerHost=10
mongo.socketTimeout=1500 # 线程队列数它和上面connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出异常
``` mongo.threadsAllowedToBlockForConnectionMultiplier=5
# 连接超时的毫秒 0是默认值且无限大。
#### 2.2 整合配置 mongo.connectTimeout=1000
# 最大等待连接的线程阻塞时间 默认是120000 ms (2 minutes).
```xml mongo.maxWaitTime=1500
<?xml version="1.0" encoding="UTF-8"?> # 保持活动标志,控制是否有套接字保持活动超时 官方默认为true 且不建议禁用
<beans xmlns="http://www.springframework.org/schema/beans" mongo.socketKeepAlive=true
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" # 用于群集心跳的连接的套接字超时。
xmlns:context="http://www.springframework.org/schema/context" mongo.socketTimeout=1500
xmlns:mongo="http://www.springframework.org/schema/data/mongo" ```
xsi:schemaLocation=
"http://www.springframework.org/schema/context #### 2.2 整合配置
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd ```xml
http://www.springframework.org/schema/beans <?xml version="1.0" encoding="UTF-8"?>
http://www.springframework.org/schema/beans/spring-beans.xsd"> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!--扫描配置文件--> xmlns:context="http://www.springframework.org/schema/context"
<context:property-placeholder location="classpath:mongodb.properties"/> xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation=
<!--定义用于访问MongoDB的MongoClient实例--> "http://www.springframework.org/schema/context
<mongo:mongo-client host="${mongo.host}" port="${mongo.port}"> http://www.springframework.org/schema/context/spring-context.xsd
<mongo:client-options http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
connections-per-host="${mongo.connectionsPerHost}" http://www.springframework.org/schema/beans
threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}" http://www.springframework.org/schema/beans/spring-beans.xsd">
connect-timeout="${mongo.connectTimeout}"
max-wait-time="${mongo.maxWaitTime}" <!--扫描配置文件-->
socket-keep-alive="${mongo.socketKeepAlive}" <context:property-placeholder location="classpath:mongodb.properties"/>
socket-timeout="${mongo.socketTimeout}"
/> <!--定义用于访问MongoDB的MongoClient实例-->
</mongo:mongo-client> <mongo:mongo-client host="${mongo.host}" port="${mongo.port}">
<mongo:client-options
<!--定义用于连接到数据库的连接工厂--> connections-per-host="${mongo.connectionsPerHost}"
<mongo:db-factory dbname="${mongo.dbname}" mongo-ref="mongoClient"/> threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}"
connect-timeout="${mongo.connectTimeout}"
<!--实际操作mongodb的template,在代码中注入--> max-wait-time="${mongo.maxWaitTime}"
<bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> socket-keep-alive="${mongo.socketKeepAlive}"
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/> socket-timeout="${mongo.socketTimeout}"
</bean> />
</mongo:mongo-client>
</beans>
``` <!--定义用于连接到数据库的连接工厂-->
<mongo:db-factory dbname="${mongo.dbname}" mongo-ref="mongoClient"/>
#### 2.3 测试整合
<!--实际操作mongodb的template,在代码中注入-->
```java <bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
/** <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
* @author : heibaiying </bean>
* @description : MongoDB 查询
*/ </beans>
@RunWith(SpringRunner.class) ```
@ContextConfiguration(locations = "classpath:mongodb.xml")
public class MongoDBTest { #### 2.3 测试整合
@Autowired ```java
private MongoTemplate mongoTemplate; /**
* @author : heibaiying
@Test * @description : MongoDB 查询
public void insert() { */
// 单条插入 @RunWith(SpringRunner.class)
mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date())); @ContextConfiguration(locations = "classpath:mongodb.xml")
List<Programmer> programmers = new ArrayList<Programmer>(); public class MongoDBTest {
// 批量插入
programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date())); @Autowired
programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date())); private MongoTemplate mongoTemplate;
mongoTemplate.insert(programmers, Programmer.class);
} @Test
public void insert() {
// 条件查询 // 单条插入
@Test mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date()));
public void select() { List<Programmer> programmers = new ArrayList<Programmer>();
Criteria criteria = new Criteria(); // 批量插入
criteria.andOperator(where("name").is("xiaohong"), where("age").is(21)); programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date()));
Query query = new Query(criteria); programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date()));
Programmer one = mongoTemplate.findOne(query, Programmer.class); mongoTemplate.insert(programmers, Programmer.class);
System.out.println(one); }
}
// 条件查询
@Test
// 更新数据 public void select() {
@Test Criteria criteria = new Criteria();
public void MUpdate() { criteria.andOperator(where("name").is("xiaohong"), where("age").is(21));
UpdateResult updateResult = mongoTemplate.updateMulti(query(where("name").is("xiaoming")), update("age", 35), Programmer.class); Query query = new Query(criteria);
System.out.println("更新记录数:" + updateResult.getModifiedCount()); Programmer one = mongoTemplate.findOne(query, Programmer.class);
} System.out.println(one);
}
// 删除指定数据
@Test
public void delete() { // 更新数据
DeleteResult result = mongoTemplate.remove(query(where("name").is("xiaolan")), Programmer.class); @Test
System.out.println("影响记录数:" + result.getDeletedCount()); public void MUpdate() {
System.out.println("是否成功:" + result.wasAcknowledged()); UpdateResult updateResult = mongoTemplate.updateMulti(query(where("name").is("xiaoming")), update("age", 35), Programmer.class);
} System.out.println("更新记录数:" + updateResult.getModifiedCount());
}
}
``` // 删除指定数据
@Test
public void delete() {
DeleteResult result = mongoTemplate.remove(query(where("name").is("xiaolan")), Programmer.class);
System.out.println("影响记录数:" + result.getDeletedCount());
System.out.println("是否成功:" + result.wasAcknowledged());
}
}
```

View File

@ -1,262 +1,274 @@
# spring 整合 mybatis注解方式 # spring 整合 mybatis注解方式
## 目录<br/>
<a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/>
<a href="#二spring-整合-mybatis">二、spring 整合 mybatis</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21--在resources文件夹下新建数据库配置文件jdbcproperties及其映射类">2.1 在resources文件夹下新建数据库配置文件jdbc.properties及其映射类</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22--配置数据源和mybatis会话工厂定义事务管理器">2.2 配置数据源和mybatis会话工厂、定义事务管理器</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-新建mybtais配置文件按照需求配置额外参数-更多settings配置项可以参考[官方文档]http//wwwmybatisorg/mybatis-3/zh/configurationhtml">2.3 新建mybtais配置文件按照需求配置额外参数 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html)</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-新建查询接口及其对应的mapper文件">2.4 新建查询接口及其对应的mapper文件</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25-新建测试类进行测试">2.5 新建测试类进行测试</a><br/>
## 正文<br/>
## 一、说明
#### 1.1 项目结构
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mybatis-annotation.png"/> </div>
#### 1.2 项目依赖
除了spring相关依赖外还需要导入数据库驱动和对应的mybatis依赖包
```xml
<!--jdbc 相关依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-base-version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3.0</version>
</dependency>
<!--mybatis 依赖包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
```
## 二、spring 整合 mybatis
#### 2.1 在resources文件夹下新建数据库配置文件jdbc.properties及其映射类
```properties
# mysql 数据库配置
mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mysql
mysql.username=root
mysql.password=root
# oracle 数据库配置
oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名
oracle.username=用户名
oracle.password=密码
```
```java
@Configuration
@PropertySource(value = "classpath:mysql.properties")
@Data
public class DataSourceConfig {
@Value("${mysql.driverClassName}")
private String driverClassName;
@Value("${mysql.url}")
private String url;
@Value("${mysql.username}")
private String username;
@Value("${mysql.password}")
private String password;
}
```
#### 2.2 配置数据源和mybatis会话工厂、定义事务管理器
```java
/**
* @author : heibaiying
*/
@Configuration
@EnableTransactionManagement // 开启声明式事务处理 等价于xml中<tx:annotation-driven/>
@ComponentScan(basePackages = {"com.heibaiying.*"})
public class DatabaseConfig {
/* @Autowired
* private DataSourceConfig sourceConfig;
* 不要采用这种方式注入DataSourceConfig,由于类的加载顺序影响会报空指针异常
* 最好的方式是在DriverManagerDataSource构造中采用参数注入
*/
/**
* 配置数据源
*/
@Bean
public DriverManagerDataSource dataSource(DataSourceConfig sourceConfig) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(sourceConfig.getDriverClassName());
dataSource.setUrl(sourceConfig.getUrl());
dataSource.setUsername(sourceConfig.getUsername());
dataSource.setPassword(sourceConfig.getPassword());
return dataSource;
}
/**
* 配置mybatis 会话工厂
*
* @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为
* 采用@Bean注解生成的bean 默认采用方法名为名称,当然也可以在使用@Bean时指定name属性
*/
@Bean
public SqlSessionFactoryBean sessionFactoryBean(DriverManagerDataSource dataSource) throws IOException {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:/mappers/**/*.xml"));
sessionFactoryBean.setConfigLocation(resolver.getResource("classpath:mybatisConfig.xml"));
return sessionFactoryBean;
}
/**
* 配置mybatis 会话工厂
*/
@Bean
public MapperScannerConfigurer MapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setSqlSessionFactoryBeanName("sessionFactoryBean");
configurer.setBasePackage("com.heibaiying.dao");
return configurer;
}
/**
* 定义事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DriverManagerDataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
```
#### 2.3 新建mybtais配置文件按照需求配置额外参数 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html)
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis 配置文件 -->
<configuration>
<settings>
<!-- 开启驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印查询sql -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
<!--更多settings配置项可以参考官方文档: <a href="http://www.mybatis.org/mybatis-3/zh/configuration.html"/>-->
```
#### 2.4 新建查询接口及其对应的mapper文件
```java
public interface MysqlDao {
List<Relation> get();
}
```
```xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heibaiying.dao.MysqlDao">
<select id="queryById" resultType="com.heibaiying.bean.Relation">
SELECT help_keyword_id AS id,name
FROM HELP_KEYWORD
WHERE HELP_KEYWORD_ID = #{id}
</select>
</mapper>
```
```mysql
public interface OracleDao {
List<Flow> queryById(long id);
}
```
```xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heibaiying.dao.OracleDao">
<select id="queryById" resultType="com.heibaiying.bean.Flow">
select * from APEX_030200.WWV_FLOW_CALS where ID = #{id}
</select>
</mapper>
```
#### 2.5 新建测试类进行测试
```java
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {DatabaseConfig.class})
public class MysqlDaoTest {
@Autowired
private MysqlDao mysqlDao;
@Test
public void get() {
List<Relation> relations = mysqlDao.queryById("691");
if (relations != null) {
for (Relation relation : relations) {
System.out.println(relation.getId() + " " + relation.getName());
}
}
}
}
```
```java
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {DatabaseConfig.class})
public class OracleDaoTest {
@Autowired
private OracleDao oracleDao;
@Test
public void get() {
List<Flow> flows = oracleDao.queryById(217584603977429772L);
if (flows != null) {
for (Flow flow : flows) {
System.out.println(flow.getId() + " " + flow.getPlugId());
}
}
}
}
```
## 一、说明
#### 1.1 项目结构
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mybatis-annotation.png"/> </div>
#### 1.2 项目依赖
除了spring相关依赖外还需要导入数据库驱动和对应的mybatis依赖包
```xml
<!--jdbc 相关依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-base-version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3.0</version>
</dependency>
<!--mybatis 依赖包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
```
## 二、spring 整合 mybatis
#### 2.1 在resources文件夹下新建数据库配置文件jdbc.properties及其映射类
```properties
# mysql 数据库配置
mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mysql
mysql.username=root
mysql.password=root
# oracle 数据库配置
oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名
oracle.username=用户名
oracle.password=密码
```
```java
@Configuration
@PropertySource(value = "classpath:mysql.properties")
@Data
public class DataSourceConfig {
@Value("${mysql.driverClassName}")
private String driverClassName;
@Value("${mysql.url}")
private String url;
@Value("${mysql.username}")
private String username;
@Value("${mysql.password}")
private String password;
}
```
#### 2.2 配置数据源和mybatis会话工厂、定义事务管理器
```java
/**
* @author : heibaiying
*/
@Configuration
@EnableTransactionManagement // 开启声明式事务处理 等价于xml中<tx:annotation-driven/>
@ComponentScan(basePackages = {"com.heibaiying.*"})
public class DatabaseConfig {
/* @Autowired
* private DataSourceConfig sourceConfig;
* 不要采用这种方式注入DataSourceConfig,由于类的加载顺序影响会报空指针异常
* 最好的方式是在DriverManagerDataSource构造中采用参数注入
*/
/**
* 配置数据源
*/
@Bean
public DriverManagerDataSource dataSource(DataSourceConfig sourceConfig) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(sourceConfig.getDriverClassName());
dataSource.setUrl(sourceConfig.getUrl());
dataSource.setUsername(sourceConfig.getUsername());
dataSource.setPassword(sourceConfig.getPassword());
return dataSource;
}
/**
* 配置mybatis 会话工厂
*
* @param dataSource 这个参数的名称需要保持和上面方法名一致 才能自动注入,因为
* 采用@Bean注解生成的bean 默认采用方法名为名称,当然也可以在使用@Bean时指定name属性
*/
@Bean
public SqlSessionFactoryBean sessionFactoryBean(DriverManagerDataSource dataSource) throws IOException {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:/mappers/**/*.xml"));
sessionFactoryBean.setConfigLocation(resolver.getResource("classpath:mybatisConfig.xml"));
return sessionFactoryBean;
}
/**
* 配置mybatis 会话工厂
*/
@Bean
public MapperScannerConfigurer MapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setSqlSessionFactoryBeanName("sessionFactoryBean");
configurer.setBasePackage("com.heibaiying.dao");
return configurer;
}
/**
* 定义事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DriverManagerDataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
```
#### 2.3 新建mybtais配置文件按照需求配置额外参数 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html)
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis 配置文件 -->
<configuration>
<settings>
<!-- 开启驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印查询sql -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
<!--更多settings配置项可以参考官方文档: <a href="http://www.mybatis.org/mybatis-3/zh/configuration.html"/>-->
```
#### 2.4 新建查询接口及其对应的mapper文件
```java
public interface MysqlDao {
List<Relation> get();
}
```
```xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heibaiying.dao.MysqlDao">
<select id="queryById" resultType="com.heibaiying.bean.Relation">
SELECT help_keyword_id AS id,name
FROM HELP_KEYWORD
WHERE HELP_KEYWORD_ID = #{id}
</select>
</mapper>
```
```mysql
public interface OracleDao {
List<Flow> queryById(long id);
}
```
```xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heibaiying.dao.OracleDao">
<select id="queryById" resultType="com.heibaiying.bean.Flow">
select * from APEX_030200.WWV_FLOW_CALS where ID = #{id}
</select>
</mapper>
```
#### 2.5 新建测试类进行测试
```java
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {DatabaseConfig.class})
public class MysqlDaoTest {
@Autowired
private MysqlDao mysqlDao;
@Test
public void get() {
List<Relation> relations = mysqlDao.queryById("691");
if (relations != null) {
for (Relation relation : relations) {
System.out.println(relation.getId() + " " + relation.getName());
}
}
}
}
```
```java
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {DatabaseConfig.class})
public class OracleDaoTest {
@Autowired
private OracleDao oracleDao;
@Test
public void get() {
List<Flow> flows = oracleDao.queryById(217584603977429772L);
if (flows != null) {
for (Flow flow : flows) {
System.out.println(flow.getId() + " " + flow.getPlugId());
}
}
}
}
```

View File

@ -1,236 +1,248 @@
# spring 整合 mybatisxml配置方式 # spring 整合 mybatisxml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构">1.1 项目结构</a><br/>
#### 1.1 项目结构 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目依赖">1.2 项目依赖</a><br/>
<a href="#二spring-整合-mybatis">二、spring 整合 mybatis</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mybatis.png"/> </div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21--在resources文件夹下新建数据库配置文件jdbcproperties">2.1 在resources文件夹下新建数据库配置文件jdbc.properties</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22--配置数据源和mybatis会话工厂定义事务管理器">2.2 配置数据源和mybatis会话工厂、定义事务管理器</a><br/>
#### 1.2 项目依赖 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-新建mybtais配置文件按照需求配置额外参数-更多settings配置项可以参考[官方文档]http//wwwmybatisorg/mybatis-3/zh/configurationhtml">2.3 新建mybtais配置文件按照需求配置额外参数 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html)</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-新建查询接口及其对应的mapper文件">2.4 新建查询接口及其对应的mapper文件</a><br/>
除了spring相关依赖外还需要导入数据库驱动和对应的mybatis依赖包 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25-新建测试类进行测试">2.5 新建测试类进行测试</a><br/>
## 正文<br/>
```xml
<!--jdbc 相关依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-base-version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3.0</version>
</dependency>
<!--mybatis 依赖包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
```
## 二、spring 整合 mybatis
#### 2.1 在resources文件夹下新建数据库配置文件jdbc.properties
```properties
# mysql 数据库配置
mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mysql
mysql.username=root
mysql.password=root
# oracle 数据库配置
oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名
oracle.username=用户名
oracle.password=密码
```
#### 2.2 配置数据源和mybatis会话工厂、定义事务管理器
```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启注解包扫描-->
<context:component-scan base-package="com.heibaiying.*"/>
<!--指定配置文件的位置-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--Mysql 配置-->
<property name="driverClassName" value="${mysql.driverClassName}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
<!--Oracle 配置-->
<!--<property name="driverClassName" value="${oracle.driverClassName}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.username}"/>
<property name="password" value="${oracle.password}"/>-->
</bean>
<!--配置 mybatis 会话工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--指定mapper文件所在的位置-->
<property name="mapperLocations" value="classpath*:/mappers/**/*.xml"/>
<property name="configLocation" value="classpath:mybatisConfig.xml"/>
</bean>
<!--扫描注册接口 -->
<!--作用:从接口的基础包开始递归搜索,并将它们注册为 MapperFactoryBean(只有至少一种方法的接口才会被注册;, 具体类将被忽略)-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定会话工厂 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 指定mybatis接口所在的包 -->
<property name="basePackage" value="com.heibaiying.dao"/>
</bean>
<!--定义事务管理器--> ## 一、说明
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> #### 1.1 项目结构
<property name="dataSource" ref="dataSource"/>
</bean> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mybatis.png"/> </div>
<!-- 开启事务注解@Transactional支持 --> #### 1.2 项目依赖
<tx:annotation-driven/>
除了spring相关依赖外还需要导入数据库驱动和对应的mybatis依赖包
</beans>
``` ```xml
<!--jdbc 相关依赖包-->
#### 2.3 新建mybtais配置文件按照需求配置额外参数 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html) <dependency>
<groupId>org.springframework</groupId>
```xml <artifactId>spring-jdbc</artifactId>
<?xml version="1.0" encoding="UTF-8" ?> <version>${spring-base-version}</version>
<!DOCTYPE configuration </dependency>
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" <dependency>
"http://mybatis.org/dtd/mybatis-3-config.dtd"> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- mybatis 配置文件 --> <version>8.0.13</version>
<configuration> </dependency>
<settings> <dependency>
<!-- 开启驼峰命名 --> <groupId>com.oracle</groupId>
<setting name="mapUnderscoreToCamelCase" value="true"/> <artifactId>ojdbc6</artifactId>
<!-- 打印查询sql --> <version>11.2.0.3.0</version>
<setting name="logImpl" value="STDOUT_LOGGING"/> </dependency>
</settings> <!--mybatis 依赖包-->
<dependency>
</configuration> <groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<!--更多settings配置项可以参考官方文档: <a href="http://www.mybatis.org/mybatis-3/zh/configuration.html"/>--> <version>1.3.2</version>
</dependency>
``` <dependency>
<groupId>org.mybatis</groupId>
#### 2.4 新建查询接口及其对应的mapper文件 <artifactId>mybatis</artifactId>
<version>3.4.6</version>
```java </dependency>
public interface MysqlDao { ```
List<Relation> get(); ## 二、spring 整合 mybatis
}
``` #### 2.1 在resources文件夹下新建数据库配置文件jdbc.properties
```xml ```properties
<!DOCTYPE mapper # mysql 数据库配置
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" mysql.driverClassName=com.mysql.jdbc.Driver
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> mysql.url=jdbc:mysql://localhost:3306/mysql
mysql.username=root
<mapper namespace="com.heibaiying.dao.MysqlDao"> mysql.password=root
<select id="queryById" resultType="com.heibaiying.bean.Relation"> # oracle 数据库配置
SELECT help_keyword_id AS id,name oracle.driverClassName=oracle.jdbc.driver.OracleDriver
FROM HELP_KEYWORD oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名
WHERE HELP_KEYWORD_ID = #{id} oracle.username=用户名
</select> oracle.password=密码
```
</mapper>
``` #### 2.2 配置数据源和mybatis会话工厂、定义事务管理器
```mysql ```xml
public interface OracleDao { <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
List<Flow> queryById(long id); xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
} xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
``` http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
```xml <!-- 开启注解包扫描-->
<!DOCTYPE mapper <context:component-scan base-package="com.heibaiying.*"/>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--指定配置文件的位置-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<mapper namespace="com.heibaiying.dao.OracleDao">
<!--配置数据源-->
<select id="queryById" resultType="com.heibaiying.bean.Flow"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
select * from APEX_030200.WWV_FLOW_CALS where ID = #{id} <!--Mysql 配置-->
</select> <property name="driverClassName" value="${mysql.driverClassName}"/>
<property name="url" value="${mysql.url}"/>
</mapper> <property name="username" value="${mysql.username}"/>
``` <property name="password" value="${mysql.password}"/>
<!--Oracle 配置-->
#### 2.5 新建测试类进行测试 <!--<property name="driverClassName" value="${oracle.driverClassName}"/>
<property name="url" value="${oracle.url}"/>
```java <property name="username" value="${oracle.username}"/>
@RunWith(SpringRunner.class) <property name="password" value="${oracle.password}"/>-->
@ContextConfiguration({"classpath:springApplication.xml"}) </bean>
public class MysqlDaoTest {
<!--配置 mybatis 会话工厂 -->
@Autowired <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
private MysqlDao mysqlDao; <property name="dataSource" ref="dataSource"/>
<!--指定mapper文件所在的位置-->
@Test <property name="mapperLocations" value="classpath*:/mappers/**/*.xml"/>
public void get() { <property name="configLocation" value="classpath:mybatisConfig.xml"/>
List<Relation> relations = mysqlDao.get(); </bean>
if (relations != null) {
for (Relation relation : relations) { <!--扫描注册接口 -->
System.out.println(relation.getId() + " " + relation.getName()); <!--作用:从接口的基础包开始递归搜索,并将它们注册为 MapperFactoryBean(只有至少一种方法的接口才会被注册;, 具体类将被忽略)-->
} <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
} <!--指定会话工厂 -->
} <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
} <!-- 指定mybatis接口所在的包 -->
<property name="basePackage" value="com.heibaiying.dao"/>
``` </bean>
```java
@RunWith(SpringRunner.class) <!--定义事务管理器-->
@ContextConfiguration({"classpath:springApplication.xml"}) <bean id="transactionManager"
public class OracleDaoTest { class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
/*注入接口时: 如果接口有多个实现类 可以用这个注解指定具体的实现类*/ </bean>
@Qualifier("oracleDaoImpl")
@Autowired <!-- 开启事务注解@Transactional支持 -->
private OracleDao oracleDao; <tx:annotation-driven/>
@Test </beans>
public void get() { ```
List<Flow> flows = oracleDao.get();
if (flows != null) { #### 2.3 新建mybtais配置文件按照需求配置额外参数 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html)
for (Flow flow : flows) {
System.out.println(flow.getId() + " " + flow.getPlugId()); ```xml
} <?xml version="1.0" encoding="UTF-8" ?>
} <!DOCTYPE configuration
} PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
} "http://mybatis.org/dtd/mybatis-3-config.dtd">
``` <!-- mybatis 配置文件 -->
<configuration>
<settings>
<!-- 开启驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印查询sql -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
<!--更多settings配置项可以参考官方文档: <a href="http://www.mybatis.org/mybatis-3/zh/configuration.html"/>-->
```
#### 2.4 新建查询接口及其对应的mapper文件
```java
public interface MysqlDao {
List<Relation> get();
}
```
```xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heibaiying.dao.MysqlDao">
<select id="queryById" resultType="com.heibaiying.bean.Relation">
SELECT help_keyword_id AS id,name
FROM HELP_KEYWORD
WHERE HELP_KEYWORD_ID = #{id}
</select>
</mapper>
```
```mysql
public interface OracleDao {
List<Flow> queryById(long id);
}
```
```xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heibaiying.dao.OracleDao">
<select id="queryById" resultType="com.heibaiying.bean.Flow">
select * from APEX_030200.WWV_FLOW_CALS where ID = #{id}
</select>
</mapper>
```
#### 2.5 新建测试类进行测试
```java
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class MysqlDaoTest {
@Autowired
private MysqlDao mysqlDao;
@Test
public void get() {
List<Relation> relations = mysqlDao.get();
if (relations != null) {
for (Relation relation : relations) {
System.out.println(relation.getId() + " " + relation.getName());
}
}
}
}
```
```java
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class OracleDaoTest {
/*注入接口时: 如果接口有多个实现类 可以用这个注解指定具体的实现类*/
@Qualifier("oracleDaoImpl")
@Autowired
private OracleDao oracleDao;
@Test
public void get() {
List<Flow> flows = oracleDao.get();
if (flows != null) {
for (Flow flow : flows) {
System.out.println(flow.getId() + " " + flow.getPlugId());
}
}
}
}
```

View File

@ -1,403 +1,419 @@
# spring 整合 rabbitmq注解方式 # spring 整合 rabbitmq注解方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-rabbit-基本配置">二、spring rabbit 基本配置</a><br/>
1. 本用例关于rabbitmq的整合提供**简单消息发送**和**对象消费发送**两种情况下的sample。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-基本配置属性及其映射类">2.1 基本配置属性及其映射类</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-创建连接工厂管理器">2.2 创建连接工厂、管理器</a><br/>
2. rabbitBaseAnnotation.java中声明了topic类型的交换机、持久化队列、及其绑定关系用于测试说明topic交换机路由键的绑定规则。 <a href="#三简单消费的发送">三、简单消费的发送</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#31-声明交换机队列绑定关系和消费者监听器">3.1 声明交换机、队列、绑定关系和消费者监听器</a><br/>
3. rabbitObjectAnnotation.java中声明了direct类型的交换机持久化队列及其绑定关系用于示例对象消息的传输。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#32-测试简单消息的发送">3.2 测试简单消息的发送</a><br/>
<a href="#四传输对象">四、传输对象</a><br/>
关于rabbitmq安装、交换机、队列、死信队列等基本概念可以参考我的手记[《RabbitMQ实战指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ/%E3%80%8ARabbitMQ%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md),里面有详细的配图说明。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#41-创建消息的委托处理器">4.1 创建消息的委托处理器</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#42-声明交换机队列绑定关系和消费者监听器">4.2 声明交换机、队列、绑定关系和消费者监听器</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#43-测试对象消息的发送">4.3 测试对象消息的发送</a><br/>
## 正文<br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-rabbitmq-annotation.png"/> </div>
## 一、说明
### 1.2 依赖说明 ### 1.1 项目结构说明
除了spring的基本依赖外需要导入spring rabbitmq 整合依赖 1. 本用例关于rabbitmq的整合提供**简单消息发送**和**对象消费发送**两种情况下的sample。
```xml 2. rabbitBaseAnnotation.java中声明了topic类型的交换机、持久化队列、及其绑定关系用于测试说明topic交换机路由键的绑定规则。
<!--spring rabbitmq 整合依赖-->
<dependency> 3. rabbitObjectAnnotation.java中声明了direct类型的交换机持久化队列及其绑定关系用于示例对象消息的传输。
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId> 关于rabbitmq安装、交换机、队列、死信队列等基本概念可以参考我的手记[《RabbitMQ实战指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ/%E3%80%8ARabbitMQ%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md),里面有详细的配图说明。
<version>2.1.2.RELEASE</version>
</dependency>
<!--rabbitmq 传输对象序列化依赖了这个包-->
<dependency> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-rabbitmq-annotation.png"/> </div>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency> ### 1.2 依赖说明
```
除了spring的基本依赖外需要导入spring rabbitmq 整合依赖
```xml
## 二、spring rabbit 基本配置 <!--spring rabbitmq 整合依赖-->
<dependency>
#### 2.1 基本配置属性及其映射类 <groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
```properties <version>2.1.2.RELEASE</version>
rabbitmq.addresses=localhost:5672 </dependency>
rabbitmq.username=guest <!--rabbitmq 传输对象序列化依赖了这个包-->
rabbitmq.password=guest <dependency>
# 虚拟主机,可以类比为命名空间 默认为/ 必须先用图形界面或者管控台添加 程序不会自动创建且会抛出异常 <groupId>com.fasterxml.jackson.core</groupId>
rabbitmq.virtualhost=/ <artifactId>jackson-databind</artifactId>
``` <version>2.9.8</version>
</dependency>
```java ```
/**
* @author : heibaiying
* @description : rabbit 属性配置
*/ ## 二、spring rabbit 基本配置
@Data
@PropertySource(value = "classpath:rabbitmq.properties") #### 2.1 基本配置属性及其映射类
@Configuration
public class RabbitProperty { ```properties
rabbitmq.addresses=localhost:5672
rabbitmq.username=guest
@Value("${rabbitmq.addresses}") rabbitmq.password=guest
private String addresses; # 虚拟主机,可以类比为命名空间 默认为/ 必须先用图形界面或者管控台添加 程序不会自动创建且会抛出异常
rabbitmq.virtualhost=/
@Value("${rabbitmq.username}") ```
private String username;
```java
@Value("${rabbitmq.password}") /**
private String password; * @author : heibaiying
* @description : rabbit 属性配置
@Value("${rabbitmq.virtualhost}") */
private String virtualhost; @Data
} @PropertySource(value = "classpath:rabbitmq.properties")
``` @Configuration
public class RabbitProperty {
#### 2.2 创建连接工厂、管理器
```java @Value("${rabbitmq.addresses}")
private String addresses;
@Configuration
@ComponentScan("com.heibaiying.rabbit.config") @Value("${rabbitmq.username}")
public class RabbitBaseConfig { private String username;
/** @Value("${rabbitmq.password}")
* 声明连接工厂 private String password;
*/
@Bean @Value("${rabbitmq.virtualhost}")
public ConnectionFactory connectionFactory(RabbitProperty property) { private String virtualhost;
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); }
connectionFactory.setAddresses(property.getAddresses()); ```
connectionFactory.setUsername(property.getUsername());
connectionFactory.setPassword(property.getPassword()); #### 2.2 创建连接工厂、管理器
connectionFactory.setVirtualHost(property.getVirtualhost());
return connectionFactory; ```java
}
@Configuration
/** @ComponentScan("com.heibaiying.rabbit.config")
* 创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。 public class RabbitBaseConfig {
* auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为true。
*/ /**
@Bean * 声明连接工厂
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) { */
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); @Bean
rabbitAdmin.setAutoStartup(true); public ConnectionFactory connectionFactory(RabbitProperty property) {
return rabbitAdmin; CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
} connectionFactory.setAddresses(property.getAddresses());
connectionFactory.setUsername(property.getUsername());
connectionFactory.setPassword(property.getPassword());
@Bean connectionFactory.setVirtualHost(property.getVirtualhost());
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { return connectionFactory;
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); }
return rabbitTemplate;
} /**
} * 创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。
``` * auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为true。
*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
## 三、简单消费的发送 RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
#### 3.1 声明交换机、队列、绑定关系和消费者监听器 return rabbitAdmin;
}
```java
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.*; @Bean
import org.springframework.amqp.rabbit.connection.ConnectionFactory; public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; return rabbitTemplate;
import org.springframework.context.annotation.Bean; }
import org.springframework.context.annotation.Configuration; }
```
/**
* @author : heibaiying
* @description : 声明队列、交换机、绑定关系、和队列消息监听
*/ ## 三、简单消费的发送
@Configuration #### 3.1 声明交换机、队列、绑定关系和消费者监听器
public class RabbitBaseAnnotation {
```java
@Bean import com.rabbitmq.client.Channel;
public TopicExchange exchange() { import org.springframework.amqp.core.*;
// 创建一个持久化的交换机 import org.springframework.amqp.rabbit.connection.ConnectionFactory;
return new TopicExchange("topic01", true, false); import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
} import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.context.annotation.Bean;
@Bean import org.springframework.context.annotation.Configuration;
public Queue firstQueue() {
// 创建一个持久化的队列1 /**
return new Queue("FirstQueue", true); * @author : heibaiying
} * @description : 声明队列、交换机、绑定关系、和队列消息监听
*/
@Bean
public Queue secondQueue() { @Configuration
// 创建一个持久化的队列2 public class RabbitBaseAnnotation {
return new Queue("SecondQueue", true);
} @Bean
public TopicExchange exchange() {
/** // 创建一个持久化的交换机
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词 return new TopicExchange("topic01", true, false);
* 这里我们声明三个绑定关系用于测试topic这种类型交换器 }
*/
@Bean @Bean
public Binding orange() { public Queue firstQueue() {
return BindingBuilder.bind(firstQueue()).to(exchange()).with("*.orange.*"); // 创建一个持久化的队列1
} return new Queue("FirstQueue", true);
}
@Bean
public Binding rabbit() { @Bean
return BindingBuilder.bind(secondQueue()).to(exchange()).with("*.*.rabbit"); public Queue secondQueue() {
} // 创建一个持久化的队列2
return new Queue("SecondQueue", true);
@Bean }
public Binding lazy() {
return BindingBuilder.bind(secondQueue()).to(exchange()).with("lazy.#"); /**
} * BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词
* 这里我们声明三个绑定关系用于测试topic这种类型交换器
*/
/*创建队列1消费者监听*/ @Bean
@Bean public Binding orange() {
public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) { return BindingBuilder.bind(firstQueue()).to(exchange()).with("*.orange.*");
}
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
// 设置监听的队列 @Bean
container.setQueues(firstQueue()); public Binding rabbit() {
// 指定要创建的并发使用者数。 return BindingBuilder.bind(secondQueue()).to(exchange()).with("*.*.rabbit");
container.setConcurrentConsumers(1); }
// 设置消费者数量的上限
container.setMaxConcurrentConsumers(5); @Bean
// 设置是否自动签收消费 为保证消费被成功消费,建议手工签收 public Binding lazy() {
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); return BindingBuilder.bind(secondQueue()).to(exchange()).with("lazy.#");
container.setMessageListener(new ChannelAwareMessageListener() { }
@Override
public void onMessage(Message message, Channel channel) throws Exception {
// 可以在这个地方得到消息额外属性 /*创建队列1消费者监听*/
MessageProperties properties = message.getMessageProperties(); @Bean
//得到消息体内容 public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) {
byte[] body = message.getBody();
System.out.println(firstQueue().getName() + "收到消息:" + new String(body)); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
/* // 设置监听的队列
* DeliveryTag 是一个单调递增的整数 container.setQueues(firstQueue());
* 第二个参数 代表是否一次签收多条如果设置为true,则所有DeliveryTag小于该DeliveryTag的消息都会被签收 // 指定要创建的并发使用者数。
*/ container.setConcurrentConsumers(1);
channel.basicAck(properties.getDeliveryTag(), false); // 设置消费者数量的上限
} container.setMaxConcurrentConsumers(5);
}); // 设置是否自动签收消费 为保证消费被成功消费,建议手工签收
return container; container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
} container.setMessageListener(new ChannelAwareMessageListener() {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
/*创建队列2消费者监听*/ // 可以在这个地方得到消息额外属性
@Bean MessageProperties properties = message.getMessageProperties();
public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) { //得到消息体内容
byte[] body = message.getBody();
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); System.out.println(firstQueue().getName() + "收到消息:" + new String(body));
container.setQueues(secondQueue()); /*
container.setMessageListener(new ChannelAwareMessageListener() { * DeliveryTag 是一个单调递增的整数
@Override * 第二个参数 代表是否一次签收多条如果设置为true,则所有DeliveryTag小于该DeliveryTag的消息都会被签收
public void onMessage(Message message, Channel channel) throws Exception { */
byte[] body = message.getBody(); channel.basicAck(properties.getDeliveryTag(), false);
System.out.println(secondQueue().getName() + "收到消息:" + new String(body)); }
} });
}); return container;
return container; }
}
} /*创建队列2消费者监听*/
``` @Bean
public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) {
#### 3.2 测试简单消息的发送
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
```java container.setQueues(secondQueue());
/** container.setMessageListener(new ChannelAwareMessageListener() {
* @author : heibaiying @Override
* @description : 传输简单字符串 public void onMessage(Message message, Channel channel) throws Exception {
*/ byte[] body = message.getBody();
System.out.println(secondQueue().getName() + "收到消息:" + new String(body));
@RunWith(SpringRunner.class) }
@ContextConfiguration(classes = RabbitBaseConfig.class) });
public class RabbitTest { return container;
}
@Autowired
private RabbitTemplate rabbitTemplate; }
```
@Test
public void sendMessage() { #### 3.2 测试简单消息的发送
MessageProperties properties = new MessageProperties();
```java
String allReceived = "我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到"; /**
Message message1 = new Message(allReceived.getBytes(), properties); * @author : heibaiying
rabbitTemplate.send("topic01", "quick.orange.rabbit", message1); * @description : 传输简单字符串
*/
String firstReceived = "我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到";
Message message2 = new Message(firstReceived.getBytes(), properties); @RunWith(SpringRunner.class)
rabbitTemplate.send("topic01", "quick.orange.fox", message2); @ContextConfiguration(classes = RabbitBaseConfig.class)
public class RabbitTest {
String secondReceived = "我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到";
Message message3 = new Message(secondReceived.getBytes(), properties); @Autowired
rabbitTemplate.send("topic01", "lazy.brown.fox", message3); private RabbitTemplate rabbitTemplate;
String notReceived = "我的路由键 quick.brown.fox 不符合 topic1 任何绑定队列的要求,你将看不到我"; @Test
Message message4 = new Message(notReceived.getBytes(), properties); public void sendMessage() {
rabbitTemplate.send("topic01", "quick.brown.fox", message4); MessageProperties properties = new MessageProperties();
}
} String allReceived = "我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到";
``` Message message1 = new Message(allReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.orange.rabbit", message1);
```java
结果: String firstReceived = "我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到";
SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到 Message message2 = new Message(firstReceived.getBytes(), properties);
FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到 rabbitTemplate.send("topic01", "quick.orange.fox", message2);
FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到
SecondQueue收到消息:我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到 String secondReceived = "我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到";
``` Message message3 = new Message(secondReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "lazy.brown.fox", message3);
String notReceived = "我的路由键 quick.brown.fox 不符合 topic1 任何绑定队列的要求,你将看不到我";
## 四、传输对象 Message message4 = new Message(notReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.brown.fox", message4);
#### 4.1 创建消息的委托处理器 }
}
这里为了增强用例的实用性我们创建的处理器的handleMessage方法是一个重载方法对于同一个队列的监听不仅可以传输对象消息同时针对不同的对象类型调用不同的处理方法。 ```
```java ```java
/** 结果:
* @author : heibaiying SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到
* @description :消息委派处理类 FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到
*/ FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到
public class MessageDelegate { SecondQueue收到消息:我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到
```
public void handleMessage(ProductManager manager) {
System.out.println("收到一个产品经理" + manager);
}
## 四、传输对象
public void handleMessage(Programmer programmer) {
System.out.println("收到一个程序员" + programmer); #### 4.1 创建消息的委托处理器
}
这里为了增强用例的实用性我们创建的处理器的handleMessage方法是一个重载方法对于同一个队列的监听不仅可以传输对象消息同时针对不同的对象类型调用不同的处理方法。
}
``` ```java
/**
#### 4.2 声明交换机、队列、绑定关系和消费者监听器 * @author : heibaiying
* @description :消息委派处理类
```java */
/** public class MessageDelegate {
* @author : heibaiying
* @description : 声明队列、交换机、绑定关系、用于测试对象的消息传递 public void handleMessage(ProductManager manager) {
*/ System.out.println("收到一个产品经理" + manager);
}
@Configuration
public class RabbitObjectAnnotation { public void handleMessage(Programmer programmer) {
System.out.println("收到一个程序员" + programmer);
@Bean }
public DirectExchange objectTopic() {
// 创建一个持久化的交换机 }
return new DirectExchange("objectTopic", true, false); ```
}
#### 4.2 声明交换机、队列、绑定关系和消费者监听器
@Bean
public Queue objectQueue() { ```java
// 创建一个持久化的队列 /**
return new Queue("objectQueue", true); * @author : heibaiying
} * @description : 声明队列、交换机、绑定关系、用于测试对象的消息传递
*/
@Bean
public Binding binding() { @Configuration
return BindingBuilder.bind(objectQueue()).to(objectTopic()).with("object"); public class RabbitObjectAnnotation {
}
@Bean
public DirectExchange objectTopic() {
/*创建队列消费者监听*/ // 创建一个持久化的交换机
@Bean return new DirectExchange("objectTopic", true, false);
public SimpleMessageListenerContainer objectQueueLister(ConnectionFactory connectionFactory) { }
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); @Bean
// 设置监听的队列 public Queue objectQueue() {
container.setQueues(objectQueue()); // 创建一个持久化的队列
// 将监听到的消息委派给实际的处理类 return new Queue("objectQueue", true);
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate()); }
// 指定由哪个方法来处理消息 默认就是handleMessage
adapter.setDefaultListenerMethod("handleMessage"); @Bean
public Binding binding() {
// 消息转换 return BindingBuilder.bind(objectQueue()).to(objectTopic()).with("object");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(); }
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
Map<String, Class<?>> idClassMapping = new HashMap<>(); /*创建队列消费者监听*/
// 针对不同的消息体调用不同的重载方法 @Bean
idClassMapping.put(Type.MANAGER, com.heibaiying.bean.ProductManager.class); public SimpleMessageListenerContainer objectQueueLister(ConnectionFactory connectionFactory) {
idClassMapping.put(Type.PROGRAMMER, com.heibaiying.bean.Programmer.class);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
javaTypeMapper.setIdClassMapping(idClassMapping); // 设置监听的队列
container.setQueues(objectQueue());
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper); // 将监听到的消息委派给实际的处理类
adapter.setMessageConverter(jackson2JsonMessageConverter); MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
container.setMessageListener(adapter); // 指定由哪个方法来处理消息 默认就是handleMessage
return container; adapter.setDefaultListenerMethod("handleMessage");
}
// 消息转换
} Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
``` DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
#### 4.3 测试对象消息的发送 Map<String, Class<?>> idClassMapping = new HashMap<>();
// 针对不同的消息体调用不同的重载方法
```java idClassMapping.put(Type.MANAGER, com.heibaiying.bean.ProductManager.class);
@RunWith(SpringRunner.class) idClassMapping.put(Type.PROGRAMMER, com.heibaiying.bean.Programmer.class);
@ContextConfiguration(classes = RabbitBaseConfig.class)
public class RabbitSendObjectTest { javaTypeMapper.setIdClassMapping(idClassMapping);
@Autowired jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
private RabbitTemplate rabbitTemplate; adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
@Test return container;
public void sendProgrammer() throws JsonProcessingException { }
MessageProperties messageProperties = new MessageProperties();
//必须设置 contentType为 application/json }
messageProperties.setContentType("application/json"); ```
// 必须指定类型
messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER); #### 4.3 测试对象消息的发送
Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date());
// 序列化与反序列化都使用的Jackson ```java
ObjectMapper mapper = new ObjectMapper(); @RunWith(SpringRunner.class)
String programmerJson = mapper.writeValueAsString(programmer); @ContextConfiguration(classes = RabbitBaseConfig.class)
Message message = new Message(programmerJson.getBytes(), messageProperties); public class RabbitSendObjectTest {
rabbitTemplate.send("objectTopic", "object", message);
} @Autowired
private RabbitTemplate rabbitTemplate;
@Test @Test
public void sendProductManager() throws JsonProcessingException { public void sendProgrammer() throws JsonProcessingException {
MessageProperties messageProperties = new MessageProperties(); MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/json"); //必须设置 contentType为 application/json
messageProperties.getHeaders().put("__TypeId__", Type.MANAGER); messageProperties.setContentType("application/json");
ProductManager manager = new ProductManager("xiaohong", 21, new Date()); // 必须指定类型
ObjectMapper mapper = new ObjectMapper(); messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER);
String managerJson = mapper.writeValueAsString(manager); Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date());
Message message = new Message(managerJson.getBytes(), messageProperties); // 序列化与反序列化都使用的Jackson
rabbitTemplate.send("objectTopic", "object", message); ObjectMapper mapper = new ObjectMapper();
} String programmerJson = mapper.writeValueAsString(programmer);
} Message message = new Message(programmerJson.getBytes(), messageProperties);
rabbitTemplate.send("objectTopic", "object", message);
}
@Test
public void sendProductManager() throws JsonProcessingException {
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/json");
messageProperties.getHeaders().put("__TypeId__", Type.MANAGER);
ProductManager manager = new ProductManager("xiaohong", 21, new Date());
ObjectMapper mapper = new ObjectMapper();
String managerJson = mapper.writeValueAsString(manager);
Message message = new Message(managerJson.getBytes(), messageProperties);
rabbitTemplate.send("objectTopic", "object", message);
}
}
``` ```

View File

@ -1,381 +1,395 @@
# spring 整合 rabbitmqxml配置方式 # spring 整合 rabbitmqxml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-rabbit-基本配置">二、spring rabbit 基本配置</a><br/>
1. 本用例关于rabbitmq的整合提供**简单消息发送**和**对象消费发送**两种情况下的sample。 <a href="#三简单消费的发送">三、简单消费的发送</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#31-声明交换机队列绑定关系和消费者监听器">3.1 声明交换机、队列、绑定关系和消费者监听器</a><br/>
2. rabbitBaseAnnotation.java中声明了topic类型的交换机、持久化队列、及其绑定关系用于测试说明topic交换机路由键的绑定规则。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#32-测试简单消息的发送">3.2 测试简单消息的发送</a><br/>
<a href="#四传输对象">四、传输对象</a><br/>
3. rabbitObjectAnnotation.java中声明了direct类型的交换机持久化队列及其绑定关系用于示例对象消息的传输。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#41-创建消息的委托处理器">4.1 创建消息的委托处理器</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#42-声明交换机队列绑定关系和消费者监听器">4.2 声明交换机、队列、绑定关系和消费者监听器</a><br/>
关于rabbitmq安装、交换机、队列、死信队列等基本概念可以参考我的手记[《RabbitMQ实战指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ/%E3%80%8ARabbitMQ%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md),里面有详细的配图说明。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#43-测试对象消息的发送">4.3 测试对象消息的发送</a><br/>
## 正文<br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-rabbitmq.png"/> </div> ## 一、说明
### 1.1 项目结构说明
### 1.2 依赖说明 1. 本用例关于rabbitmq的整合提供**简单消息发送**和**对象消费发送**两种情况下的sample。
除了spring的基本依赖外需要导入spring rabbitmq 整合依赖 2. rabbitBaseAnnotation.java中声明了topic类型的交换机、持久化队列、及其绑定关系用于测试说明topic交换机路由键的绑定规则。
```xml 3. rabbitObjectAnnotation.java中声明了direct类型的交换机持久化队列及其绑定关系用于示例对象消息的传输。
<!--spring rabbitmq 整合依赖-->
<dependency> 关于rabbitmq安装、交换机、队列、死信队列等基本概念可以参考我的手记[《RabbitMQ实战指南》读书笔记](https://github.com/heibaiying/LearningNotes/blob/master/notes/%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ/%E3%80%8ARabbitMQ%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.md),里面有详细的配图说明。
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.1.2.RELEASE</version>
</dependency> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-rabbitmq.png"/> </div>
<!--rabbitmq 传输对象序列化依赖了这个包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> ### 1.2 依赖说明
<version>2.9.8</version>
</dependency> 除了spring的基本依赖外需要导入spring rabbitmq 整合依赖
```
```xml
<!--spring rabbitmq 整合依赖-->
<dependency>
## 二、spring rabbit 基本配置 <groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
```properties <version>2.1.2.RELEASE</version>
rabbitmq.addresses=localhost:5672 </dependency>
rabbitmq.username=guest <!--rabbitmq 传输对象序列化依赖了这个包-->
rabbitmq.password=guest <dependency>
# 虚拟主机,可以类比为命名空间 默认为/ 必须先用图形界面或者管控台添加 程序不会自动创建且会抛出异常 <groupId>com.fasterxml.jackson.core</groupId>
rabbitmq.virtualhost=/ <artifactId>jackson-databind</artifactId>
``` <version>2.9.8</version>
</dependency>
```xml ```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" ## 二、spring rabbit 基本配置
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation= ```properties
"http://www.springframework.org/schema/context rabbitmq.addresses=localhost:5672
http://www.springframework.org/schema/context/spring-context.xsd rabbitmq.username=guest
http://www.springframework.org/schema/beans rabbitmq.password=guest
http://www.springframework.org/schema/beans/spring-beans.xsd # 虚拟主机,可以类比为命名空间 默认为/ 必须先用图形界面或者管控台添加 程序不会自动创建且会抛出异常
http://www.springframework.org/schema/rabbit rabbitmq.virtualhost=/
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd"> ```
<context:property-placeholder location="rabbitmq.properties"/> ```xml
<?xml version="1.0" encoding="UTF-8"?>
<!--声明连接工厂--> <beans xmlns="http://www.springframework.org/schema/beans"
<rabbit:connection-factory id="connectionFactory" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
addresses="${rabbitmq.addresses}" xmlns:context="http://www.springframework.org/schema/context"
username="${rabbitmq.username}" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
password="${rabbitmq.password}" xsi:schemaLocation=
virtual-host="${rabbitmq.virtualhost}"/> "http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!--创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。 http://www.springframework.org/schema/beans
auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为true。--> http://www.springframework.org/schema/beans/spring-beans.xsd
<rabbit:admin connection-factory="connectionFactory" auto-startup="true"/> http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!--声明 template 的时候需要声明id 不然会抛出异常-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/> <context:property-placeholder location="rabbitmq.properties"/>
<!--声明连接工厂-->
<!--可以在xml采用如下方式声明交换机、队列、绑定管理 但是建议使用代码方式声明 方法更加灵活且可以采用链调用--> <rabbit:connection-factory id="connectionFactory"
<rabbit:queue name="remoting.queue"/> addresses="${rabbitmq.addresses}"
username="${rabbitmq.username}"
<rabbit:direct-exchange name="remoting.exchange"> password="${rabbitmq.password}"
<rabbit:bindings> virtual-host="${rabbitmq.virtualhost}"/>
<rabbit:binding queue="remoting.queue" key="remoting.binding"/>
</rabbit:bindings> <!--创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。
</rabbit:direct-exchange> auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为true。-->
<rabbit:admin connection-factory="connectionFactory" auto-startup="true"/>
<!--扫描rabbit包 自动声明交换器、队列、绑定关系-->
<context:component-scan base-package="com.heibaiying.rabbit"/> <!--声明 template 的时候需要声明id 不然会抛出异常-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>
```
<!--可以在xml采用如下方式声明交换机、队列、绑定管理 但是建议使用代码方式声明 方法更加灵活且可以采用链调用-->
<rabbit:queue name="remoting.queue"/>
## 三、简单消费的发送 <rabbit:direct-exchange name="remoting.exchange">
<rabbit:bindings>
#### 3.1 声明交换机、队列、绑定关系和消费者监听器 <rabbit:binding queue="remoting.queue" key="remoting.binding"/>
</rabbit:bindings>
```java </rabbit:direct-exchange>
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.*; <!--扫描rabbit包 自动声明交换器、队列、绑定关系-->
import org.springframework.amqp.rabbit.connection.ConnectionFactory; <context:component-scan base-package="com.heibaiying.rabbit"/>
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; </beans>
import org.springframework.context.annotation.Bean; ```
import org.springframework.context.annotation.Configuration;
/**
* @author : heibaiying ## 三、简单消费的发送
* @description : 声明队列、交换机、绑定关系、和队列消息监听
*/ #### 3.1 声明交换机、队列、绑定关系和消费者监听器
@Configuration ```java
public class RabbitBaseAnnotation { import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.*;
@Bean import org.springframework.amqp.rabbit.connection.ConnectionFactory;
public TopicExchange exchange() { import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
// 创建一个持久化的交换机 import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
return new TopicExchange("topic01", true, false); import org.springframework.context.annotation.Bean;
} import org.springframework.context.annotation.Configuration;
@Bean /**
public Queue firstQueue() { * @author : heibaiying
// 创建一个持久化的队列1 * @description : 声明队列、交换机、绑定关系、和队列消息监听
return new Queue("FirstQueue", true); */
}
@Configuration
@Bean public class RabbitBaseAnnotation {
public Queue secondQueue() {
// 创建一个持久化的队列2 @Bean
return new Queue("SecondQueue", true); public TopicExchange exchange() {
} // 创建一个持久化的交换机
return new TopicExchange("topic01", true, false);
/** }
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词
* 这里我们声明三个绑定关系用于测试topic这种类型交换器 @Bean
*/ public Queue firstQueue() {
@Bean // 创建一个持久化的队列1
public Binding orange() { return new Queue("FirstQueue", true);
return BindingBuilder.bind(firstQueue()).to(exchange()).with("*.orange.*"); }
}
@Bean
@Bean public Queue secondQueue() {
public Binding rabbit() { // 创建一个持久化的队列2
return BindingBuilder.bind(secondQueue()).to(exchange()).with("*.*.rabbit"); return new Queue("SecondQueue", true);
} }
@Bean /**
public Binding lazy() { * BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词
return BindingBuilder.bind(secondQueue()).to(exchange()).with("lazy.#"); * 这里我们声明三个绑定关系用于测试topic这种类型交换器
} */
@Bean
public Binding orange() {
/*创建队列1消费者监听*/ return BindingBuilder.bind(firstQueue()).to(exchange()).with("*.orange.*");
@Bean }
public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) {
@Bean
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); public Binding rabbit() {
// 设置监听的队列 return BindingBuilder.bind(secondQueue()).to(exchange()).with("*.*.rabbit");
container.setQueues(firstQueue()); }
// 指定要创建的并发使用者数。
container.setConcurrentConsumers(1); @Bean
// 设置消费者数量的上限 public Binding lazy() {
container.setMaxConcurrentConsumers(5); return BindingBuilder.bind(secondQueue()).to(exchange()).with("lazy.#");
// 设置是否自动签收消费 为保证消费被成功消费,建议手工签收 }
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setMessageListener(new ChannelAwareMessageListener() {
@Override /*创建队列1消费者监听*/
public void onMessage(Message message, Channel channel) throws Exception { @Bean
// 可以在这个地方得到消息额外属性 public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) {
MessageProperties properties = message.getMessageProperties();
//得到消息体内容 SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
byte[] body = message.getBody(); // 设置监听的队列
System.out.println(firstQueue().getName() + "收到消息:" + new String(body)); container.setQueues(firstQueue());
/* // 指定要创建的并发使用者数。
* DeliveryTag 是一个单调递增的整数 container.setConcurrentConsumers(1);
* 第二个参数 代表是否一次签收多条如果设置为true,则所有DeliveryTag小于该DeliveryTag的消息都会被签收 // 设置消费者数量的上限
*/ container.setMaxConcurrentConsumers(5);
channel.basicAck(properties.getDeliveryTag(), false); // 设置是否自动签收消费 为保证消费被成功消费,建议手工签收
} container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
}); container.setMessageListener(new ChannelAwareMessageListener() {
return container; @Override
} public void onMessage(Message message, Channel channel) throws Exception {
// 可以在这个地方得到消息额外属性
MessageProperties properties = message.getMessageProperties();
/*创建队列2消费者监听*/ //得到消息体内容
@Bean byte[] body = message.getBody();
public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) { System.out.println(firstQueue().getName() + "收到消息:" + new String(body));
/*
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); * DeliveryTag 是一个单调递增的整数
container.setQueues(secondQueue()); * 第二个参数 代表是否一次签收多条如果设置为true,则所有DeliveryTag小于该DeliveryTag的消息都会被签收
container.setMessageListener(new ChannelAwareMessageListener() { */
@Override channel.basicAck(properties.getDeliveryTag(), false);
public void onMessage(Message message, Channel channel) throws Exception { }
byte[] body = message.getBody(); });
System.out.println(secondQueue().getName() + "收到消息:" + new String(body)); return container;
} }
});
return container;
} /*创建队列2消费者监听*/
@Bean
} public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) {
```
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
#### 3.2 测试简单消息的发送 container.setQueues(secondQueue());
container.setMessageListener(new ChannelAwareMessageListener() {
```java @Override
/** public void onMessage(Message message, Channel channel) throws Exception {
* @author : heibaiying byte[] body = message.getBody();
* @description : 传输简单字符串 System.out.println(secondQueue().getName() + "收到消息:" + new String(body));
*/ }
});
@RunWith(SpringRunner.class) return container;
@ContextConfiguration(locations = "classpath:rabbitmq.xml") }
public class RabbitTest {
}
@Autowired ```
private RabbitTemplate rabbitTemplate;
#### 3.2 测试简单消息的发送
@Test
public void sendMessage() { ```java
MessageProperties properties = new MessageProperties(); /**
* @author : heibaiying
String allReceived = "我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到"; * @description : 传输简单字符串
Message message1 = new Message(allReceived.getBytes(), properties); */
rabbitTemplate.send("topic01", "quick.orange.rabbit", message1);
@RunWith(SpringRunner.class)
String firstReceived = "我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到"; @ContextConfiguration(locations = "classpath:rabbitmq.xml")
Message message2 = new Message(firstReceived.getBytes(), properties); public class RabbitTest {
rabbitTemplate.send("topic01", "quick.orange.fox", message2);
@Autowired
String secondReceived = "我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到"; private RabbitTemplate rabbitTemplate;
Message message3 = new Message(secondReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "lazy.brown.fox", message3); @Test
public void sendMessage() {
String notReceived = "我的路由键 quick.brown.fox 不符合 topic1 任何绑定队列的要求,你将看不到我"; MessageProperties properties = new MessageProperties();
Message message4 = new Message(notReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.brown.fox", message4); String allReceived = "我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到";
} Message message1 = new Message(allReceived.getBytes(), properties);
} rabbitTemplate.send("topic01", "quick.orange.rabbit", message1);
```
String firstReceived = "我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到";
```java Message message2 = new Message(firstReceived.getBytes(), properties);
结果: rabbitTemplate.send("topic01", "quick.orange.fox", message2);
SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到
FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到 String secondReceived = "我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到";
FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到 Message message3 = new Message(secondReceived.getBytes(), properties);
SecondQueue收到消息:我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到 rabbitTemplate.send("topic01", "lazy.brown.fox", message3);
```
String notReceived = "我的路由键 quick.brown.fox 不符合 topic1 任何绑定队列的要求,你将看不到我";
Message message4 = new Message(notReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.brown.fox", message4);
## 四、传输对象 }
}
#### 4.1 创建消息的委托处理器 ```
这里为了增强用例的实用性我们创建的处理器的handleMessage方法是一个重载方法对于同一个队列的监听不仅可以传输对象消息同时针对不同的对象类型调用不同的处理方法。 ```java
结果:
```java SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到
/** FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到
* @author : heibaiying FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到
* @description :消息委派处理类 SecondQueue收到消息:我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到
*/ ```
public class MessageDelegate {
public void handleMessage(ProductManager manager) {
System.out.println("收到一个产品经理" + manager); ## 四、传输对象
}
#### 4.1 创建消息的委托处理器
public void handleMessage(Programmer programmer) {
System.out.println("收到一个程序员" + programmer); 这里为了增强用例的实用性我们创建的处理器的handleMessage方法是一个重载方法对于同一个队列的监听不仅可以传输对象消息同时针对不同的对象类型调用不同的处理方法。
}
```java
} /**
``` * @author : heibaiying
* @description :消息委派处理类
#### 4.2 声明交换机、队列、绑定关系和消费者监听器 */
public class MessageDelegate {
```java
/** public void handleMessage(ProductManager manager) {
* @author : heibaiying System.out.println("收到一个产品经理" + manager);
* @description : 声明队列、交换机、绑定关系、用于测试对象的消息传递 }
*/
public void handleMessage(Programmer programmer) {
@Configuration System.out.println("收到一个程序员" + programmer);
public class RabbitObjectAnnotation { }
@Bean }
public DirectExchange objectTopic() { ```
// 创建一个持久化的交换机
return new DirectExchange("objectTopic", true, false); #### 4.2 声明交换机、队列、绑定关系和消费者监听器
}
```java
@Bean /**
public Queue objectQueue() { * @author : heibaiying
// 创建一个持久化的队列 * @description : 声明队列、交换机、绑定关系、用于测试对象的消息传递
return new Queue("objectQueue", true); */
}
@Configuration
@Bean public class RabbitObjectAnnotation {
public Binding binding() {
return BindingBuilder.bind(objectQueue()).to(objectTopic()).with("object"); @Bean
} public DirectExchange objectTopic() {
// 创建一个持久化的交换机
return new DirectExchange("objectTopic", true, false);
/*创建队列消费者监听*/ }
@Bean
public SimpleMessageListenerContainer objectQueueLister(ConnectionFactory connectionFactory) { @Bean
public Queue objectQueue() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); // 创建一个持久化的队列
// 设置监听的队列 return new Queue("objectQueue", true);
container.setQueues(objectQueue()); }
// 将监听到的消息委派给实际的处理类
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate()); @Bean
// 指定由哪个方法来处理消息 默认就是handleMessage public Binding binding() {
adapter.setDefaultListenerMethod("handleMessage"); return BindingBuilder.bind(objectQueue()).to(objectTopic()).with("object");
}
// 消息转换
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); /*创建队列消费者监听*/
@Bean
Map<String, Class<?>> idClassMapping = new HashMap<>(); public SimpleMessageListenerContainer objectQueueLister(ConnectionFactory connectionFactory) {
// 针对不同的消息体调用不同的重载方法
idClassMapping.put(Type.MANAGER, com.heibaiying.bean.ProductManager.class); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
idClassMapping.put(Type.PROGRAMMER, com.heibaiying.bean.Programmer.class); // 设置监听的队列
container.setQueues(objectQueue());
javaTypeMapper.setIdClassMapping(idClassMapping); // 将监听到的消息委派给实际的处理类
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper); // 指定由哪个方法来处理消息 默认就是handleMessage
adapter.setMessageConverter(jackson2JsonMessageConverter); adapter.setDefaultListenerMethod("handleMessage");
container.setMessageListener(adapter);
return container; // 消息转换
} Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
}
``` Map<String, Class<?>> idClassMapping = new HashMap<>();
// 针对不同的消息体调用不同的重载方法
#### 4.3 测试对象消息的发送 idClassMapping.put(Type.MANAGER, com.heibaiying.bean.ProductManager.class);
idClassMapping.put(Type.PROGRAMMER, com.heibaiying.bean.Programmer.class);
```java
@RunWith(SpringRunner.class) javaTypeMapper.setIdClassMapping(idClassMapping);
@ContextConfiguration(locations = "classpath:rabbitmq.xml")
public class RabbitSendObjectTest { jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
@Autowired container.setMessageListener(adapter);
private RabbitTemplate rabbitTemplate; return container;
}
@Test
public void sendProgrammer() throws JsonProcessingException { }
MessageProperties messageProperties = new MessageProperties(); ```
//必须设置 contentType为 application/json
messageProperties.setContentType("application/json"); #### 4.3 测试对象消息的发送
// 必须指定类型
messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER); ```java
Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date()); @RunWith(SpringRunner.class)
// 序列化与反序列化都使用的Jackson @ContextConfiguration(locations = "classpath:rabbitmq.xml")
ObjectMapper mapper = new ObjectMapper(); public class RabbitSendObjectTest {
String programmerJson = mapper.writeValueAsString(programmer);
Message message = new Message(programmerJson.getBytes(), messageProperties); @Autowired
rabbitTemplate.send("objectTopic", "object", message); private RabbitTemplate rabbitTemplate;
}
@Test
public void sendProgrammer() throws JsonProcessingException {
@Test MessageProperties messageProperties = new MessageProperties();
public void sendProductManager() throws JsonProcessingException { //必须设置 contentType为 application/json
MessageProperties messageProperties = new MessageProperties(); messageProperties.setContentType("application/json");
messageProperties.setContentType("application/json"); // 必须指定类型
messageProperties.getHeaders().put("__TypeId__", Type.MANAGER); messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER);
ProductManager manager = new ProductManager("xiaohong", 21, new Date()); Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date());
ObjectMapper mapper = new ObjectMapper(); // 序列化与反序列化都使用的Jackson
String managerJson = mapper.writeValueAsString(manager); ObjectMapper mapper = new ObjectMapper();
Message message = new Message(managerJson.getBytes(), messageProperties); String programmerJson = mapper.writeValueAsString(programmer);
rabbitTemplate.send("objectTopic", "object", message); Message message = new Message(programmerJson.getBytes(), messageProperties);
} rabbitTemplate.send("objectTopic", "object", message);
} }
@Test
public void sendProductManager() throws JsonProcessingException {
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/json");
messageProperties.getHeaders().put("__TypeId__", Type.MANAGER);
ProductManager manager = new ProductManager("xiaohong", 21, new Date());
ObjectMapper mapper = new ObjectMapper();
String managerJson = mapper.writeValueAsString(manager);
Message message = new Message(managerJson.getBytes(), messageProperties);
rabbitTemplate.send("objectTopic", "object", message);
}
}
``` ```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,192 +1,201 @@
# spring 定时任务(注解方式) # spring 定时任务(注解方式)
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-scheduling">二、spring scheduling</a><br/>
关于任务的调度配置定义在ServletConfig.java中为方便观察项目定时执行的情况项目以web的方式构建。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建定时任务">2.1 创建定时任务</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-配置定时任务">2.2 配置定时任务</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling-annotation.png"/> </div> ## 正文<br/>
## 一、说明
### 1.2 依赖说明
### 1.1 项目结构说明
导入基本依赖
关于任务的调度配置定义在ServletConfig.java中为方便观察项目定时执行的情况项目以web的方式构建。
```xml
<?xml version="1.0" encoding="UTF-8"?> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling-annotation.png"/> </div>
<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> ### 1.2 依赖说明
<groupId>com.heibaiying</groupId> 导入基本依赖
<artifactId>spring-scheduling</artifactId>
<version>1.0-SNAPSHOT</version> ```xml
<build> <?xml version="1.0" encoding="UTF-8"?>
<plugins> <project xmlns="http://maven.apache.org/POM/4.0.0"
<plugin> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<groupId>org.apache.maven.plugins</groupId> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>maven-compiler-plugin</artifactId> <modelVersion>4.0.0</modelVersion>
<configuration>
<source>8</source> <groupId>com.heibaiying</groupId>
<target>8</target> <artifactId>spring-scheduling</artifactId>
</configuration> <version>1.0-SNAPSHOT</version>
</plugin> <build>
</plugins> <plugins>
</build> <plugin>
<properties> <groupId>org.apache.maven.plugins</groupId>
<spring-base-version>5.1.3.RELEASE</spring-base-version> <artifactId>maven-compiler-plugin</artifactId>
</properties> <configuration>
<source>8</source>
<dependencies> <target>8</target>
<dependency> </configuration>
<groupId>org.springframework</groupId> </plugin>
<artifactId>spring-context</artifactId> </plugins>
<version>${spring-base-version}</version> </build>
</dependency> <properties>
<dependency> <spring-base-version>5.1.3.RELEASE</spring-base-version>
<groupId>org.springframework</groupId> </properties>
<artifactId>spring-beans</artifactId>
<version>${spring-base-version}</version> <dependencies>
</dependency> <dependency>
<dependency> <groupId>org.springframework</groupId>
<groupId>org.springframework</groupId> <artifactId>spring-context</artifactId>
<artifactId>spring-core</artifactId> <version>${spring-base-version}</version>
<version>${spring-base-version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.springframework</groupId>
<groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId>
<artifactId>spring-web</artifactId> <version>${spring-base-version}</version>
<version>${spring-base-version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.springframework</groupId>
<groupId>org.springframework</groupId> <artifactId>spring-core</artifactId>
<artifactId>spring-webmvc</artifactId> <version>${spring-base-version}</version>
<version>${spring-base-version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.springframework</groupId>
<groupId>javax.servlet</groupId> <artifactId>spring-web</artifactId>
<artifactId>javax.servlet-api</artifactId> <version>${spring-base-version}</version>
<version>4.0.1</version> </dependency>
<scope>provided</scope> <dependency>
</dependency> <groupId>org.springframework</groupId>
</dependencies> <artifactId>spring-webmvc</artifactId>
<version>${spring-base-version}</version>
</project> </dependency>
``` <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
## 二、spring scheduling <scope>provided</scope>
</dependency>
#### 2.1 创建定时任务 </dependencies>
```java </project>
/** ```
* @author : heibaiying
*/
@Component
public class Task { ## 二、spring scheduling
/** #### 2.1 创建定时任务
* 基于间隔的触发器,其中间隔是从上一个任务的 完成时间 开始计算, 时间单位值以毫秒为单位。
*/ ```java
@Scheduled(fixedDelay = 5000, initialDelay = 1000) /**
public void methodA() { * @author : heibaiying
Thread thread = Thread.currentThread(); */
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", @Component
thread.getName(), thread.getId(), "methodA方法执行", LocalDateTime.now())); public class Task {
}
/**
/** * 基于间隔的触发器,其中间隔是从上一个任务的 完成时间 开始计算, 时间单位值以毫秒为单位。
* 基于间隔的触发器,其中间隔是从上一个任务的 开始时间 开始测量的。 */
*/ @Scheduled(fixedDelay = 5000, initialDelay = 1000)
@Scheduled(fixedRate = 5000) public void methodA() {
@Async Thread thread = Thread.currentThread();
public void methodB() throws InterruptedException { System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
Thread thread = Thread.currentThread(); thread.getName(), thread.getId(), "methodA方法执行", LocalDateTime.now()));
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", }
thread.getName(), thread.getId(), "methodB方法执行", LocalDateTime.now()));
Thread.sleep(10 * 1000); /**
} * 基于间隔的触发器,其中间隔是从上一个任务的 开始时间 开始测量的。
*/
@Scheduled(cron = "0/10 * * * * ?") @Scheduled(fixedRate = 5000)
public void methodC() { @Async
Thread thread = Thread.currentThread(); public void methodB() throws InterruptedException {
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", Thread thread = Thread.currentThread();
thread.getName(), thread.getId(), "methodC方法执行", LocalDateTime.now())); System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
} thread.getName(), thread.getId(), "methodB方法执行", LocalDateTime.now()));
} Thread.sleep(10 * 1000);
}
```
@Scheduled(cron = "0/10 * * * * ?")
#### 2.2 配置定时任务 public void methodC() {
Thread thread = Thread.currentThread();
```java System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
/** thread.getName(), thread.getId(), "methodC方法执行", LocalDateTime.now()));
* @author : heibaiying }
* spring 主配置类 }
*/
@Configuration ```
@EnableWebMvc
@EnableScheduling //启用Spring的计划任务执行功能 #### 2.2 配置定时任务
@EnableAsync //启用Spring的异步方法执行功能
@ComponentScan(basePackages = {"com.heibaiying.task"}) ```java
public class ServletConfig implements WebMvcConfigurer, AsyncConfigurer, SchedulingConfigurer { /**
* @author : heibaiying
private ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); * spring 主配置类
*/
// 任务执行器线程池配置 @Configuration
@Override @EnableWebMvc
public Executor getAsyncExecutor() { @EnableScheduling //启用Spring的计划任务执行功能
executor.setCorePoolSize(5); @EnableAsync //启用Spring的异步方法执行功能
executor.setMaxPoolSize(10); @ComponentScan(basePackages = {"com.heibaiying.task"})
executor.setQueueCapacity(100); public class ServletConfig implements WebMvcConfigurer, AsyncConfigurer, SchedulingConfigurer {
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize(); private ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
return executor;
} // 任务执行器线程池配置
@Override
// 这个方法可以监听到异步程序发生的错误 public Executor getAsyncExecutor() {
@Override executor.setCorePoolSize(5);
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { executor.setMaxPoolSize(10);
return new AsyncUncaughtExceptionHandler() { executor.setQueueCapacity(100);
@Override executor.setThreadNamePrefix("MyExecutor-");
public void handleUncaughtException(Throwable ex, Method method, Object... params) { executor.initialize();
System.out.println(method.getName() + "发生错误:" + ex.getMessage()); return executor;
} }
};
} // 这个方法可以监听到异步程序发生的错误
@Override
// 如果程序结束,需要关闭线程池 不然程序无法完全退出 只能kill才能完全退出 public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
@PreDestroy return new AsyncUncaughtExceptionHandler() {
public void destroy() { @Override
if (executor != null) { public void handleUncaughtException(Throwable ex, Method method, Object... params) {
executor.shutdown(); System.out.println(method.getName() + "发生错误:" + ex.getMessage());
} }
} };
}
// 调度程序线程池配置
@Override // 如果程序结束,需要关闭线程池 不然程序无法完全退出 只能kill才能完全退出
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { @PreDestroy
taskRegistrar.setScheduler(taskExecutor()); public void destroy() {
} if (executor != null) {
executor.shutdown();
// 如果程序结束,需要关闭线程池 }
@Bean(destroyMethod = "shutdown") }
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(50); // 调度程序线程池配置
} @Override
} public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
``` taskRegistrar.setScheduler(taskExecutor());
}
**关于调度程序线程池作用说明**
// 如果程序结束,需要关闭线程池
按照例子 我们有methodA 、 methodB 、methodC 三个方法 其中 methodB 是耗时的方法如果不声明调度程序线程池 则methodB 会阻塞 methodA 、methodC 方法的执行 因为调度程序是单线程的 @Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
**关于任务执行线程池作用说明** return Executors.newScheduledThreadPool(50);
}
按照例子 如果我们声明 methodB 是按照 fixedRate=5000 方法执行的 理论上不管任务耗时多久任务都应该是每5秒执行一次但是实际上任务是被加入执行队列也不会立即被执行因为默认执行任务是单线程的这个时候需要开启@EnableAsync 并指定方法是 @Async 异步的,并且配置执行任务线程池(如果不配置就使用默认的线程池配置) }
```
**关于调度程序线程池作用说明**
按照例子 我们有methodA 、 methodB 、methodC 三个方法 其中 methodB 是耗时的方法如果不声明调度程序线程池 则methodB 会阻塞 methodA 、methodC 方法的执行 因为调度程序是单线程的
**关于任务执行线程池作用说明**
按照例子 如果我们声明 methodB 是按照 fixedRate=5000 方法执行的 理论上不管任务耗时多久任务都应该是每5秒执行一次但是实际上任务是被加入执行队列也不会立即被执行因为默认执行任务是单线程的这个时候需要开启@EnableAsync 并指定方法是 @Async 异步的,并且配置执行任务线程池(如果不配置就使用默认的线程池配置)

View File

@ -1,183 +1,192 @@
# spring 定时任务xml配置方式 # spring 定时任务xml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-scheduling">二、spring scheduling</a><br/>
关于任务的调度配置定义在springApplication.xml中为方便观察项目定时执行的情况项目以web的方式构建。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建定时任务">2.1 创建定时任务</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-配置定时任务">2.2 配置定时任务</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling.png"/> </div> ## 正文<br/>
## 一、说明
### 1.2 依赖说明
### 1.1 项目结构说明
导入基本依赖
关于任务的调度配置定义在springApplication.xml中为方便观察项目定时执行的情况项目以web的方式构建。
```xml
<?xml version="1.0" encoding="UTF-8"?> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling.png"/> </div>
<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> ### 1.2 依赖说明
<groupId>com.heibaiying</groupId> 导入基本依赖
<artifactId>spring-scheduling</artifactId>
<version>1.0-SNAPSHOT</version> ```xml
<build> <?xml version="1.0" encoding="UTF-8"?>
<plugins> <project xmlns="http://maven.apache.org/POM/4.0.0"
<plugin> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<groupId>org.apache.maven.plugins</groupId> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>maven-compiler-plugin</artifactId> <modelVersion>4.0.0</modelVersion>
<configuration>
<source>8</source> <groupId>com.heibaiying</groupId>
<target>8</target> <artifactId>spring-scheduling</artifactId>
</configuration> <version>1.0-SNAPSHOT</version>
</plugin> <build>
</plugins> <plugins>
</build> <plugin>
<properties> <groupId>org.apache.maven.plugins</groupId>
<spring-base-version>5.1.3.RELEASE</spring-base-version> <artifactId>maven-compiler-plugin</artifactId>
</properties> <configuration>
<source>8</source>
<dependencies> <target>8</target>
<dependency> </configuration>
<groupId>org.springframework</groupId> </plugin>
<artifactId>spring-context</artifactId> </plugins>
<version>${spring-base-version}</version> </build>
</dependency> <properties>
<dependency> <spring-base-version>5.1.3.RELEASE</spring-base-version>
<groupId>org.springframework</groupId> </properties>
<artifactId>spring-beans</artifactId>
<version>${spring-base-version}</version> <dependencies>
</dependency> <dependency>
<dependency> <groupId>org.springframework</groupId>
<groupId>org.springframework</groupId> <artifactId>spring-context</artifactId>
<artifactId>spring-core</artifactId> <version>${spring-base-version}</version>
<version>${spring-base-version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.springframework</groupId>
<groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId>
<artifactId>spring-web</artifactId> <version>${spring-base-version}</version>
<version>${spring-base-version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.springframework</groupId>
<groupId>org.springframework</groupId> <artifactId>spring-core</artifactId>
<artifactId>spring-webmvc</artifactId> <version>${spring-base-version}</version>
<version>${spring-base-version}</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.springframework</groupId>
<groupId>javax.servlet</groupId> <artifactId>spring-web</artifactId>
<artifactId>javax.servlet-api</artifactId> <version>${spring-base-version}</version>
<version>4.0.1</version> </dependency>
<scope>provided</scope> <dependency>
</dependency> <groupId>org.springframework</groupId>
</dependencies> <artifactId>spring-webmvc</artifactId>
<version>${spring-base-version}</version>
</project> </dependency>
``` <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
## 二、spring scheduling <scope>provided</scope>
</dependency>
#### 2.1 创建定时任务 </dependencies>
```java </project>
package com.heibaiying.task; ```
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
## 二、spring scheduling
import java.time.LocalDateTime;
#### 2.1 创建定时任务
/**
* @author : heibaiying ```java
*/ package com.heibaiying.task;
public class Task {
import org.springframework.scheduling.annotation.Async;
public void methodA() { import org.springframework.scheduling.annotation.Scheduled;
Thread thread = Thread.currentThread();
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", import java.time.LocalDateTime;
thread.getName(), thread.getId(), "methodA方法执行", LocalDateTime.now()));
} /**
* @author : heibaiying
@Async */
public void methodB() throws InterruptedException { public class Task {
Thread thread = Thread.currentThread();
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", public void methodA() {
thread.getName(), thread.getId(), "methodB方法执行", LocalDateTime.now())); Thread thread = Thread.currentThread();
Thread.sleep(10 * 1000); System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
} thread.getName(), thread.getId(), "methodA方法执行", LocalDateTime.now()));
}
public void methodC() {
Thread thread = Thread.currentThread(); @Async
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", public void methodB() throws InterruptedException {
thread.getName(), thread.getId(), "methodC方法执行", LocalDateTime.now())); Thread thread = Thread.currentThread();
} System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
} thread.getName(), thread.getId(), "methodB方法执行", LocalDateTime.now()));
Thread.sleep(10 * 1000);
``` }
#### 2.2 配置定时任务 public void methodC() {
Thread thread = Thread.currentThread();
```xml System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
<?xml version="1.0" encoding="UTF-8"?> thread.getName(), thread.getId(), "methodC方法执行", LocalDateTime.now()));
<beans xmlns="http://www.springframework.org/schema/beans" }
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" }
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task" ```
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd #### 2.2 配置定时任务
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd ```xml
http://www.springframework.org/schema/mvc <?xml version="1.0" encoding="UTF-8"?>
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd <beans xmlns="http://www.springframework.org/schema/beans"
http://www.springframework.org/schema/task xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
http://www.springframework.org/schema/task/spring-task.xsd"> xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task"
<!-- 开启注解包扫描--> xsi:schemaLocation="http://www.springframework.org/schema/beans
<context:component-scan base-package="com.heibaiying.*"/> http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
<!-- 开启注解驱动 --> http://www.springframework.org/schema/context/spring-context-4.1.xsd
<mvc:annotation-driven/> http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
<!--配置定时任务--> http://www.springframework.org/schema/task
<bean id="task" class="com.heibaiying.task.Task"/> http://www.springframework.org/schema/task/spring-task.xsd">
<task:scheduled-tasks scheduler="myScheduler">
<!--基于间隔的触发器,其中间隔是从上一个任务的 完成时间 开始计算, 时间单位值以毫秒为单位。--> <!-- 开启注解包扫描-->
<task:scheduled ref="task" method="methodA" fixed-delay="5000" initial-delay="1000"/> <context:component-scan base-package="com.heibaiying.*"/>
<!--基于间隔的触发器,其中间隔是从上一个任务的 开始时间 开始测量的。-->
<task:scheduled ref="task" method="methodB" fixed-rate="5000"/> <!-- 开启注解驱动 -->
<!-- cron 表达式--> <mvc:annotation-driven/>
<task:scheduled ref="task" method="methodC" cron="0/10 * * * * ?"/>
</task:scheduled-tasks> <!--配置定时任务-->
<bean id="task" class="com.heibaiying.task.Task"/>
<!--定义任务调度器线程池的大小--> <task:scheduled-tasks scheduler="myScheduler">
<task:scheduler id="myScheduler" pool-size="10"/> <!--基于间隔的触发器,其中间隔是从上一个任务的 完成时间 开始计算, 时间单位值以毫秒为单位。-->
<task:scheduled ref="task" method="methodA" fixed-delay="5000" initial-delay="1000"/>
<!--定义任务执行器的线程池大小、等待队列的容量、和拒绝策略--> <!--基于间隔的触发器,其中间隔是从上一个任务的 开始时间 开始测量的。-->
<task:executor <task:scheduled ref="task" method="methodB" fixed-rate="5000"/>
id="executorWithPoolSizeRange" <!-- cron 表达式-->
pool-size="5-25" <task:scheduled ref="task" method="methodC" cron="0/10 * * * * ?"/>
queue-capacity="100" </task:scheduled-tasks>
rejection-policy="CALLER_RUNS"/>
<!--拒绝策略默认值为ABORT <!--定义任务调度器线程池的大小-->
CALLER_RUNS来限制入栈任务 <task:scheduler id="myScheduler" pool-size="10"/>
DISCARD删除当前任务
DISCARD_OLDEST将任务放在队列的头部。--> <!--定义任务执行器的线程池大小、等待队列的容量、和拒绝策略-->
<task:executor
<!--允许在任何Spring管理的对象上检测@Async和@Scheduled注释, 如果存在,将生成用于异步执行带注释的方法的代理。--> id="executorWithPoolSizeRange"
<task:annotation-driven/> pool-size="5-25"
queue-capacity="100"
</beans> rejection-policy="CALLER_RUNS"/>
``` <!--拒绝策略默认值为ABORT
CALLER_RUNS来限制入栈任务
**关于调度程序线程池作用说明** DISCARD删除当前任务
DISCARD_OLDEST将任务放在队列的头部。-->
按照例子 我们有methodA 、 methodB 、methodC 三个方法 其中 methodB 是耗时的方法如果不声明调度程序线程池 则methodB 会阻塞 methodA 、methodC 方法的执行 因为调度程序是单线程的
<!--允许在任何Spring管理的对象上检测@Async和@Scheduled注释, 如果存在,将生成用于异步执行带注释的方法的代理。-->
**关于任务执行线程池作用说明** <task:annotation-driven/>
按照例子 如果我们声明 methodB 是按照 fixedRate=5000 方法执行的 理论上不管任务耗时多久任务都应该是每5秒执行一次但是实际上任务是被加入执行队列也不会立即被执行因为默认执行任务是单线程的这个时候需要开启@EnableAsync 并指定方法是 @Async 异步的,并且配置执行任务线程池(如果不配置就使用默认的线程池配置) </beans>
```
**关于调度程序线程池作用说明**
按照例子 我们有methodA 、 methodB 、methodC 三个方法 其中 methodB 是耗时的方法如果不声明调度程序线程池 则methodB 会阻塞 methodA 、methodC 方法的执行 因为调度程序是单线程的
**关于任务执行线程池作用说明**
按照例子 如果我们声明 methodB 是按照 fixedRate=5000 方法执行的 理论上不管任务耗时多久任务都应该是每5秒执行一次但是实际上任务是被加入执行队列也不会立即被执行因为默认执行任务是单线程的这个时候需要开启@EnableAsync 并指定方法是 @Async 异步的,并且配置执行任务线程池(如果不配置就使用默认的线程池配置)

View File

@ -1,203 +1,215 @@
# spring websocket注解方式 # spring websocket注解方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-websocket">二、spring websocket</a><br/>
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建消息处理类继承自TextWebSocketHandler">2.1 创建消息处理类继承自TextWebSocketHandler</a><br/>
2. webconfig 包是基础注解的方式配置web在spring-base-annotation项目中已经讲解过每个类作用 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-创建websocket-握手拦截器如果没有权限拦截等需求这一步不是必须的">2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的)</a><br/>
3. CustomHander为消息的自定义处理器 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-创建websocket的配置类">2.3 创建websocket的配置类</a><br/>
4. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-前端-websocket-的实现">2.4 前端 websocket 的实现</a><br/>
5. webSocketConfig 是websocket 的主要配置类; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25-简单登录的实现">2.5 简单登录的实现</a><br/>
6. 项目以web的方式构建。 ## 正文<br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket-annotation.png"/> </div>
## 一、说明
### 1.2 依赖说明
### 1.1 项目结构说明
除了基本的spring 依赖外还需要导入webSocket的依赖包
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中
```xml 2. webconfig 包是基础注解的方式配置web在spring-base-annotation项目中已经讲解过每个类作用
<!--spring webSocket 的依赖包 --> 3. CustomHander为消息的自定义处理器
<dependency> 4. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器;
<groupId>org.springframework</groupId> 5. webSocketConfig 是websocket 的主要配置类;
<artifactId>spring-websocket</artifactId> 6. 项目以web的方式构建。
<version>5.1.3.RELEASE</version>
</dependency> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket-annotation.png"/> </div>
```
### 1.2 依赖说明
## 二、spring websocket
除了基本的spring 依赖外还需要导入webSocket的依赖包
#### 2.1 创建消息处理类继承自TextWebSocketHandler
```xml
```java <!--spring webSocket 的依赖包 -->
/** <dependency>
* @author : heibaiying <groupId>org.springframework</groupId>
* @description : 自定义消息处理类 <artifactId>spring-websocket</artifactId>
*/ <version>5.1.3.RELEASE</version>
public class CustomHandler extends TextWebSocketHandler { </dependency>
```
private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>();
// 建立连接时候触发
@Override ## 二、spring websocket
public void afterConnectionEstablished(WebSocketSession session) {
String username = getNameFromSession(session); #### 2.1 创建消息处理类继承自TextWebSocketHandler
nameAndSession.putIfAbsent(username, session);
} ```java
/**
* @author : heibaiying
// 关闭连接时候触发 * @description : 自定义消息处理类
@Override */
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { public class CustomHandler extends TextWebSocketHandler {
String username = getNameFromSession(session);
nameAndSession.remove(username); private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>();
}
// 建立连接时候触发
// 处理消息 @Override
@Override public void afterConnectionEstablished(WebSocketSession session) {
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String username = getNameFromSession(session);
// 防止中文乱码 nameAndSession.putIfAbsent(username, session);
String msg = URLDecoder.decode(message.getPayload(), "utf-8"); }
String username = getNameFromSession(session);
// 简单模拟群发消息
TextMessage reply = new TextMessage(username + " : " + msg); // 关闭连接时候触发
nameAndSession.forEach((s, webSocketSession) @Override
-> { public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
try { String username = getNameFromSession(session);
webSocketSession.sendMessage(reply); nameAndSession.remove(username);
} catch (IOException e) { }
e.printStackTrace();
} // 处理消息
}); @Override
} protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 防止中文乱码
String msg = URLDecoder.decode(message.getPayload(), "utf-8");
private String getNameFromSession(WebSocketSession session) { String username = getNameFromSession(session);
Map<String, Object> attributes = session.getAttributes(); // 简单模拟群发消息
return (String) attributes.get(Constant.USER_NAME); TextMessage reply = new TextMessage(username + " : " + msg);
} nameAndSession.forEach((s, webSocketSession)
} -> {
try {
``` webSocketSession.sendMessage(reply);
} catch (IOException e) {
#### 2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的) e.printStackTrace();
}
```java });
/** }
* @author : heibaiying
* @description : 可以按照需求实现权限拦截等功能
*/ private String getNameFromSession(WebSocketSession session) {
public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor { Map<String, Object> attributes = session.getAttributes();
return (String) attributes.get(Constant.USER_NAME);
@Override }
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { }
InetSocketAddress remoteAddress = request.getRemoteAddress();
InetAddress address = remoteAddress.getAddress(); ```
System.out.println(address);
/* #### 2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的)
* 最后需要要显示调用父类方法父类的beforeHandshake方法
* 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。 ```java
* 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值 /**
* 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空 * @author : heibaiying
*/ * @description : 可以按照需求实现权限拦截等功能
return super.beforeHandshake(request, response, wsHandler, attributes); */
} public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
}
``` @Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
#### 2.3 创建websocket的配置类 InetSocketAddress remoteAddress = request.getRemoteAddress();
InetAddress address = remoteAddress.getAddress();
```java System.out.println(address);
/** /*
* @author : heibaiying * 最后需要要显示调用父类方法父类的beforeHandshake方法
* @description :websocket 配置 * 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。
*/ * 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值
@Configuration * 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空
@EnableWebSocket */
public class WebSocketConfig implements WebSocketConfigurer { return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override }
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { ```
registry.addHandler(new CustomHandler(), "/socket").addInterceptors(new CustomHandshakeInterceptor());
} #### 2.3 创建websocket的配置类
}
``` ```java
/**
#### 2.4 前端 websocket 的实现 * @author : heibaiying
* @description :websocket 配置
```jsp */
<%@ page contentType="text/html;charset=UTF-8" language="java" %> @Configuration
<html> @EnableWebSocket
<head> public class WebSocketConfig implements WebSocketConfigurer {
<title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title>
</head> @Override
<body> public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
<input id="message" type="text"> registry.addHandler(new CustomHandler(), "/socket").addInterceptors(new CustomHandshakeInterceptor());
<button id="btn">发送消息</button> }
<div id="show"> }
```
</div>
<script> #### 2.4 前端 websocket 的实现
let btn = document.getElementById("btn");
let message = document.getElementById("message"); ```jsp
let show = document.getElementById("show"); <%@ page contentType="text/html;charset=UTF-8" language="java" %>
let ws = new WebSocket("ws://localhost:8080/socket"); <html>
ws.onmessage = function (evt) { <head>
let node = document.createElement("div"); <title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title>
node.innerHTML = "<h5>" + evt.data + "</h5>"; </head>
show.appendChild(node); <body>
}; <input id="message" type="text">
btn.addEventListener("click", function () { <button id="btn">发送消息</button>
let data = message.value; <div id="show">
console.log(data);
if (data) { </div>
ws.send(encodeURI(data)); <script>
} else { let btn = document.getElementById("btn");
alert("请输入消息后发送"); let message = document.getElementById("message");
} let show = document.getElementById("show");
message.value = ""; let ws = new WebSocket("ws://localhost:8080/socket");
}); ws.onmessage = function (evt) {
// 关闭页面时候关闭ws let node = document.createElement("div");
window.addEventListener("beforeunload", function(event) { node.innerHTML = "<h5>" + evt.data + "</h5>";
ws.close(); show.appendChild(node);
}); };
</script> btn.addEventListener("click", function () {
</body> let data = message.value;
</html> console.log(data);
if (data) {
``` ws.send(encodeURI(data));
} else {
#### 2.5 简单登录的实现 alert("请输入消息后发送");
}
```java message.value = "";
<%@ page contentType="text/html;charset=UTF-8" language="java" %> });
<html> // 关闭页面时候关闭ws
<head> window.addEventListener("beforeunload", function(event) {
<title>Title</title> ws.close();
</head> });
<body> </script>
<form action="${pageContext.request.contextPath}/login" method="post"> </body>
<input name="username" type="text"> </html>
<button id="btn">输入临时用户名后登录!</button>
</form> ```
</body>
</html> #### 2.5 简单登录的实现
```
```java
```java <%@ page contentType="text/html;charset=UTF-8" language="java" %>
@Controller <html>
public class LoginController { <head>
<title>Title</title>
@PostMapping("login") </head>
public String login(String username, HttpSession session){ <body>
session.setAttribute(Constant.USER_NAME,username); <form action="${pageContext.request.contextPath}/login" method="post">
return "chat"; <input name="username" type="text">
} <button id="btn">输入临时用户名后登录!</button>
} </form>
``` </body>
</html>
```
```java
@Controller
public class LoginController {
@PostMapping("login")
public String login(String username, HttpSession session){
session.setAttribute(Constant.USER_NAME,username);
return "chat";
}
}
```

View File

@ -1,233 +1,245 @@
# spring websocketxml配置方式 # spring websocketxml配置方式
## 目录<br/>
## 一、说明 <a href="#一说明">一、说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
<a href="#二spring-websocket">二、spring websocket</a><br/>
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建消息处理类继承自TextWebSocketHandler">2.1 创建消息处理类继承自TextWebSocketHandler</a><br/>
2. CustomHander为消息的自定义处理器 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-创建websocket-握手拦截器如果没有权限拦截等需求这一步不是必须的">2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的)</a><br/>
3. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-配置websocket">2.3 配置websocket</a><br/>
4. 项目以web的方式构建。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-前端-websocket-的实现">2.4 前端 websocket 的实现</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25-简单登录的实现">2.5 简单登录的实现</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket.png"/> </div> ## 正文<br/>
## 一、说明
### 1.2 依赖说明
### 1.1 项目结构说明
除了基本的spring 依赖外还需要导入webSocket的依赖包
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中
```xml 2. CustomHander为消息的自定义处理器
<!--spring webSocket 的依赖包 --> 3. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器;
<dependency> 4. 项目以web的方式构建。
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket.png"/> </div>
<version>5.1.3.RELEASE</version>
</dependency>
```
### 1.2 依赖说明
除了基本的spring 依赖外还需要导入webSocket的依赖包
## 二、spring websocket
```xml
#### 2.1 创建消息处理类继承自TextWebSocketHandler <!--spring webSocket 的依赖包 -->
<dependency>
```java <groupId>org.springframework</groupId>
/** <artifactId>spring-websocket</artifactId>
* @author : heibaiying <version>5.1.3.RELEASE</version>
* @description : 自定义消息处理类 </dependency>
*/ ```
public class CustomHandler extends TextWebSocketHandler {
private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>();
## 二、spring websocket
// 建立连接时候触发
@Override #### 2.1 创建消息处理类继承自TextWebSocketHandler
public void afterConnectionEstablished(WebSocketSession session) {
String username = getNameFromSession(session); ```java
nameAndSession.putIfAbsent(username, session); /**
} * @author : heibaiying
* @description : 自定义消息处理类
*/
// 关闭连接时候触发 public class CustomHandler extends TextWebSocketHandler {
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>();
String username = getNameFromSession(session);
nameAndSession.remove(username); // 建立连接时候触发
} @Override
public void afterConnectionEstablished(WebSocketSession session) {
// 处理消息 String username = getNameFromSession(session);
@Override nameAndSession.putIfAbsent(username, session);
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { }
// 防止中文乱码
String msg = URLDecoder.decode(message.getPayload(), "utf-8");
String username = getNameFromSession(session); // 关闭连接时候触发
// 简单模拟群发消息 @Override
TextMessage reply = new TextMessage(username + " : " + msg); public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
nameAndSession.forEach((s, webSocketSession) String username = getNameFromSession(session);
-> { nameAndSession.remove(username);
try { }
webSocketSession.sendMessage(reply);
} catch (IOException e) { // 处理消息
e.printStackTrace(); @Override
} protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
}); // 防止中文乱码
} String msg = URLDecoder.decode(message.getPayload(), "utf-8");
String username = getNameFromSession(session);
// 简单模拟群发消息
private String getNameFromSession(WebSocketSession session) { TextMessage reply = new TextMessage(username + " : " + msg);
Map<String, Object> attributes = session.getAttributes(); nameAndSession.forEach((s, webSocketSession)
return (String) attributes.get(Constant.USER_NAME); -> {
} try {
} webSocketSession.sendMessage(reply);
} catch (IOException e) {
``` e.printStackTrace();
}
#### 2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的) });
}
```java
/**
* @author : heibaiying private String getNameFromSession(WebSocketSession session) {
* @description : 可以按照需求实现权限拦截等功能 Map<String, Object> attributes = session.getAttributes();
*/ return (String) attributes.get(Constant.USER_NAME);
public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor { }
}
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { ```
InetSocketAddress remoteAddress = request.getRemoteAddress();
InetAddress address = remoteAddress.getAddress(); #### 2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的)
System.out.println(address);
/* ```java
* 最后需要要显示调用父类方法父类的beforeHandshake方法 /**
* 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。 * @author : heibaiying
* 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值 * @description : 可以按照需求实现权限拦截等功能
* 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空 */
*/ public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
return super.beforeHandshake(request, response, wsHandler, attributes);
} @Override
} public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
``` InetSocketAddress remoteAddress = request.getRemoteAddress();
InetAddress address = remoteAddress.getAddress();
#### 2.3 配置websocket System.out.println(address);
/*
```xml * 最后需要要显示调用父类方法父类的beforeHandshake方法
<?xml version="1.0" encoding="UTF-8"?> * 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。
<beans xmlns="http://www.springframework.org/schema/beans" * 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" * 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空
xmlns:context="http://www.springframework.org/schema/context" */
xmlns:mvc="http://www.springframework.org/schema/mvc" return super.beforeHandshake(request, response, wsHandler, attributes);
xmlns:websocket="http://www.springframework.org/schema/websocket" }
xsi:schemaLocation="http://www.springframework.org/schema/beans }
http://www.springframework.org/schema/beans/spring-beans.xsd ```
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd #### 2.3 配置websocket
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd ```xml
http://www.springframework.org/schema/websocket <?xml version="1.0" encoding="UTF-8"?>
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!-- 开启注解包扫描--> xmlns:context="http://www.springframework.org/schema/context"
<context:component-scan base-package="com.heibaiying.*"/> xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:websocket="http://www.springframework.org/schema/websocket"
<!--使用默认的Servlet来响应静态文件 --> xsi:schemaLocation="http://www.springframework.org/schema/beans
<mvc:default-servlet-handler/> http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
<!-- 开启注解驱动 --> http://www.springframework.org/schema/context/spring-context-4.1.xsd
<mvc:annotation-driven/> http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
<!-- 配置视图解析器 --> http://www.springframework.org/schema/websocket
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" http://www.springframework.org/schema/websocket/spring-websocket.xsd">
id="internalResourceViewResolver">
<!-- 前缀 --> <!-- 开启注解包扫描-->
<property name="prefix" value="/WEB-INF/jsp/"/> <context:component-scan base-package="com.heibaiying.*"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/> <!--使用默认的Servlet来响应静态文件 -->
</bean> <mvc:default-servlet-handler/>
<!--配置webSocket--> <!-- 开启注解驱动 -->
<bean id="customHandler" class="com.heibaiying.websocket.CustomHandler"/> <mvc:annotation-driven/>
<websocket:handlers>
<!--指定webSocket 地址--> <!-- 配置视图解析器 -->
<websocket:mapping path="/socket" handler="customHandler"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
<!--webSocket握手--> id="internalResourceViewResolver">
<websocket:handshake-interceptors> <!-- 前缀 -->
<bean class="com.heibaiying.websocket.CustomHandshakeInterceptor"/> <property name="prefix" value="/WEB-INF/jsp/"/>
</websocket:handshake-interceptors> <!-- 后缀 -->
</websocket:handlers> <property name="suffix" value=".jsp"/>
</bean>
</beans>
``` <!--配置webSocket-->
<bean id="customHandler" class="com.heibaiying.websocket.CustomHandler"/>
#### 2.4 前端 websocket 的实现 <websocket:handlers>
<!--指定webSocket 地址-->
```jsp <websocket:mapping path="/socket" handler="customHandler"/>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!--webSocket握手-->
<html> <websocket:handshake-interceptors>
<head> <bean class="com.heibaiying.websocket.CustomHandshakeInterceptor"/>
<title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title> </websocket:handshake-interceptors>
</head> </websocket:handlers>
<body>
<input id="message" type="text"> </beans>
<button id="btn">发送消息</button> ```
<div id="show">
#### 2.4 前端 websocket 的实现
</div>
<script> ```jsp
let btn = document.getElementById("btn"); <%@ page contentType="text/html;charset=UTF-8" language="java" %>
let message = document.getElementById("message"); <html>
let show = document.getElementById("show"); <head>
let ws = new WebSocket("ws://localhost:8080/socket"); <title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title>
ws.onmessage = function (evt) { </head>
let node = document.createElement("div"); <body>
node.innerHTML = "<h5>" + evt.data + "</h5>"; <input id="message" type="text">
show.appendChild(node); <button id="btn">发送消息</button>
}; <div id="show">
btn.addEventListener("click", function () {
let data = message.value; </div>
console.log(data); <script>
if (data) { let btn = document.getElementById("btn");
ws.send(encodeURI(data)); let message = document.getElementById("message");
} else { let show = document.getElementById("show");
alert("请输入消息后发送"); let ws = new WebSocket("ws://localhost:8080/socket");
} ws.onmessage = function (evt) {
message.value = ""; let node = document.createElement("div");
}); node.innerHTML = "<h5>" + evt.data + "</h5>";
// 关闭页面时候关闭ws show.appendChild(node);
window.addEventListener("beforeunload", function(event) { };
ws.close(); btn.addEventListener("click", function () {
}); let data = message.value;
</script> console.log(data);
</body> if (data) {
</html> ws.send(encodeURI(data));
} else {
``` alert("请输入消息后发送");
}
#### 2.5 简单登录的实现 message.value = "";
});
```java // 关闭页面时候关闭ws
<%@ page contentType="text/html;charset=UTF-8" language="java" %> window.addEventListener("beforeunload", function(event) {
<html> ws.close();
<head> });
<title>Title</title> </script>
</head> </body>
<body> </html>
<form action="${pageContext.request.contextPath}/login" method="post">
<input name="username" type="text"> ```
<button id="btn">输入临时用户名后登录!</button>
</form> #### 2.5 简单登录的实现
</body>
</html> ```java
``` <%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
```java <head>
@Controller <title>Title</title>
public class LoginController { </head>
<body>
@PostMapping("login") <form action="${pageContext.request.contextPath}/login" method="post">
public String login(String username, HttpSession session){ <input name="username" type="text">
session.setAttribute(Constant.USER_NAME,username); <button id="btn">输入临时用户名后登录!</button>
return "chat"; </form>
} </body>
} </html>
``` ```
```java
@Controller
public class LoginController {
@PostMapping("login")
public String login(String username, HttpSession session){
session.setAttribute(Constant.USER_NAME,username);
return "chat";
}
}
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff