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

View File

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

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

View File

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

View File

@@ -1,287 +1,297 @@
# spring 邮件发送xml配置方式 # spring 邮件发送xml配置方式
## 一、说明 ## 目录<br/>
<a href="#一说明">一、说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
1. 邮件发送配置类为com.heibaiying.config下EmailConfig.java; <a href="#二spring-email">二、spring email</a><br/>
2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-邮件发送配置">2.1 邮件发送配置</a><br/>
3. 项目以单元测试的方法进行测试测试类为SendEmail。 &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/>
## 正文<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.2 依赖说明 1. 邮件发送配置类为com.heibaiying.config下EmailConfig.java;
2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中
除了spring的基本依赖外需要导入邮件发送的支持包spring-context-support 3. 项目以单元测试的方法进行测试测试类为SendEmail。
```xml
<!--邮件发送依赖包-->
<dependency> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-email-annotation.png"/> </div>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring-base-version}</version>
</dependency> ### 1.2 依赖说明
<!--模板引擎-->
<!--这里采用的是beetl,beetl性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">--> 除了spring的基本依赖外需要导入邮件发送的支持包spring-context-support
<dependency>
<groupId>com.ibeetl</groupId> ```xml
<artifactId>beetl</artifactId> <!--邮件发送依赖包-->
<version>2.9.7</version> <dependency>
</dependency> <groupId>org.springframework</groupId>
``` <artifactId>spring-context-support</artifactId>
<version>${spring-base-version}</version>
</dependency>
<!--模板引擎-->
## 二、spring email <!--这里采用的是beetl,beetl性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">-->
<dependency>
#### 2.1 邮件发送配置 <groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
```java <version>2.9.7</version>
/** </dependency>
* @author : heibaiying ```
* @description : 邮件发送配置类
*/
@Configuration ## 二、spring email
@ComponentScan(value = "com.heibaiying.email")
public class EmailConfig { #### 2.1 邮件发送配置
/*** ```java
* 在这里可以声明不同的邮件服务器主机通常是SMTP主机,而具体的用户名和时授权码则建议在业务中从数据库查询 /**
*/ * @author : heibaiying
@Bean(name = "qqMailSender") * @description : 邮件发送配置类
JavaMailSenderImpl javaMailSender() { */
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost("smtp.qq.com"); @Configuration
javaMailSender.setPassword("587"); @ComponentScan(value = "com.heibaiying.email")
return javaMailSender; public class EmailConfig {
}
/***
/*** * 在这里可以声明不同的邮件服务器主机通常是SMTP主机,而具体的用户名和时授权码则建议在业务中从数据库查询
* 配置模板引擎 */
*/ @Bean(name = "qqMailSender")
@Bean JavaMailSenderImpl javaMailSender() {
GroupTemplate groupTemplate() throws IOException { JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
//指定加载模板资源的位置 指定在classpath:beetl下- javaMailSender.setHost("smtp.qq.com");
ClasspathResourceLoader loader = new ClasspathResourceLoader("beetl"); javaMailSender.setPassword("587");
//beetl配置 这里采用默认的配置- return javaMailSender;
org.beetl.core.Configuration configuration = org.beetl.core.Configuration.defaultConfiguration(); }
return new GroupTemplate(loader, configuration);
} /***
} * 配置模板引擎
*/
``` @Bean
GroupTemplate groupTemplate() throws IOException {
#### 2.2 新建邮件发送基本类 //指定加载模板资源的位置 指定在classpath:beetl下-
ClasspathResourceLoader loader = new ClasspathResourceLoader("beetl");
```java //beetl配置 这里采用默认的配置-
/** org.beetl.core.Configuration configuration = org.beetl.core.Configuration.defaultConfiguration();
* @author : heibaiying return new GroupTemplate(loader, configuration);
* @description : 邮件发送基本类 }
*/ }
@Component
public class SpringMail { ```
@Autowired #### 2.2 新建邮件发送基本类
private JavaMailSenderImpl qqMailSender;
@Autowired ```java
private GroupTemplate groupTemplate; /**
* @author : heibaiying
/** * @description : 邮件发送基本类
* 发送简单邮件 */
* 在qq邮件发送的测试中测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户 @Component
* 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user public class SpringMail {
* qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 中开启服务后获取
*/ @Autowired
public void sendTextMessage(String from, String authWord, String to, String subject, String content) { private JavaMailSenderImpl qqMailSender;
// 设置发送人邮箱和授权码 @Autowired
qqMailSender.setUsername(from); private GroupTemplate groupTemplate;
qqMailSender.setPassword(authWord);
// 实例化消息对象 /**
SimpleMailMessage msg = new SimpleMailMessage(); * 发送简单邮件
msg.setFrom(from); * 在qq邮件发送的测试中测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户
msg.setTo(to); * 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user
msg.setSubject(subject); * qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 中开启服务后获取
msg.setText(content); */
try { public void sendTextMessage(String from, String authWord, String to, String subject, String content) {
// 发送消息 // 设置发送人邮箱和授权码
this.qqMailSender.send(msg); qqMailSender.setUsername(from);
System.out.println("发送邮件成功"); qqMailSender.setPassword(authWord);
} catch (MailException ex) { // 实例化消息对象
// 消息发送失败可以做对应的处理 SimpleMailMessage msg = new SimpleMailMessage();
System.err.println("发送邮件失败" + ex.getMessage()); msg.setFrom(from);
} msg.setTo(to);
} msg.setSubject(subject);
msg.setText(content);
/** try {
* 发送带附件的邮件 // 发送消息
*/ this.qqMailSender.send(msg);
public void sendEmailWithAttachments(String from, String authWord, String to, System.out.println("发送邮件成功");
String subject, String content, Map<String, File> files) { } catch (MailException ex) {
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"); */
helper.setFrom(from); public void sendEmailWithAttachments(String from, String authWord, String to,
helper.setTo(to); String subject, String content, Map<String, File> files) {
helper.setSubject(subject); try {
helper.setText(content); // 设置发送人邮箱和授权码
// 传入附件 qqMailSender.setUsername(from);
for (Map.Entry<String, File> entry : files.entrySet()) { qqMailSender.setPassword(authWord);
helper.addAttachment(entry.getKey(), entry.getValue()); // 实例化消息对象
} MimeMessage message = qqMailSender.createMimeMessage();
// 发送消息 // 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息
this.qqMailSender.send(message); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
System.out.println("发送邮件成功"); helper.setFrom(from);
} catch (MessagingException ex) { helper.setTo(to);
// 消息发送失败可以做对应的处理 helper.setSubject(subject);
System.err.println("发送邮件失败" + ex.getMessage()); helper.setText(content);
} // 传入附件
} for (Map.Entry<String, File> entry : files.entrySet()) {
helper.addAttachment(entry.getKey(), entry.getValue());
}
/** // 发送消息
* 发送带内嵌资源的邮件 this.qqMailSender.send(message);
*/ System.out.println("发送邮件成功");
public void sendEmailWithInline(String from, String authWord, String to, } catch (MessagingException ex) {
String subject, String content, File file) { // 消息发送失败可以做对应的处理
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"); */
helper.setFrom(from); public void sendEmailWithInline(String from, String authWord, String to,
helper.setTo(to); String subject, String content, File file) {
helper.setSubject(subject); try {
// 使用true标志来指示包含的文本是HTML 固定格式资源前缀 cid: // 设置发送人邮箱和授权码
helper.setText("<html><body><img src='cid:image'></body></html>", true); qqMailSender.setUsername(from);
// 需要先指定文本 再指定资源文件 qqMailSender.setPassword(authWord);
FileSystemResource res = new FileSystemResource(file); // 实例化消息对象
helper.addInline("image", res); MimeMessage message = qqMailSender.createMimeMessage();
// 发送消息 // 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息
this.qqMailSender.send(message); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
System.out.println("发送邮件成功"); helper.setFrom(from);
} catch (MessagingException ex) { helper.setTo(to);
// 消息发送失败可以做对应的处理 helper.setSubject(subject);
System.err.println("发送邮件失败" + ex.getMessage()); // 使用true标志来指示包含的文本是HTML 固定格式资源前缀 cid:
} helper.setText("<html><body><img src='cid:image'></body></html>", true);
} // 需要先指定文本 再指定资源文件
FileSystemResource res = new FileSystemResource(file);
/** helper.addInline("image", res);
* 使用模板邮件 // 发送消息
*/ this.qqMailSender.send(message);
public void sendEmailByTemplate(String from, String authWord, String to, System.out.println("发送邮件成功");
String subject, String content) { } catch (MessagingException ex) {
try { // 消息发送失败可以做对应的处理
Template t = groupTemplate.getTemplate("template.html"); System.err.println("发送邮件失败" + ex.getMessage());
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,
MimeMessage message = qqMailSender.createMimeMessage(); String subject, String content) {
// 指定 utf-8 防止乱码 try {
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); Template t = groupTemplate.getTemplate("template.html");
helper.setFrom(from); t.binding("subject", subject);
helper.setTo(to); t.binding("content", content);
helper.setSubject(subject); String text = t.render();
// 为true 时候 表示文本内容以 html 渲染 // 设置发送人邮箱和授权码
helper.setText(text, true); qqMailSender.setUsername(from);
this.qqMailSender.send(message); qqMailSender.setPassword(authWord);
System.out.println("发送邮件成功"); // 实例化消息对象
} catch (MessagingException ex) { MimeMessage message = qqMailSender.createMimeMessage();
// 消息发送失败可以做对应的处理 // 指定 utf-8 防止乱码
System.err.println("发送邮件失败" + ex.getMessage()); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
} 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>
<body> - 这里我们使用的beetl模板引擎原因是其性能优异官网是介绍其性能6倍与freemaker,并有完善的文档支持。当然大家也可以换成任何其他的模板引擎freemarker,thymeleaf
<h1>邮件主题:<span style="color: chartreuse"> ${subject}</span></h1>
<h4 style="color: blueviolet">${content}</h4> 一个简单的模板template.html如下
</body>
</html> ```html
``` <!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
#### 2.3 邮件发送的测试 </head>
<body>
```java <h1>邮件主题:<span style="color: chartreuse"> ${subject}</span></h1>
/** <h4 style="color: blueviolet">${content}</h4>
* @author : heibaiying </body>
* @description : 发送邮件测试类 </html>
*/ ```
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = EmailConfig.class)
public class SendEmail {
#### 2.3 邮件发送的测试
@Autowired
private SpringMail springMail; ```java
/**
// 发送方邮箱地址 * @author : heibaiying
private static final String from = "发送方邮箱地址@qq.com"; * @description : 发送邮件测试类
// 发送方邮箱地址对应的授权码 */
private static final String authWord = "授权码"; @RunWith(SpringRunner.class)
// 接收方邮箱地址 @ContextConfiguration(classes = EmailConfig.class)
private static final String to = "接收方邮箱地址@qq.com"; public class SendEmail {
@Test @Autowired
public void sendMessage() { private SpringMail springMail;
springMail.sendTextMessage(from, authWord, to, "spring简单邮件", "Hello Spring Email!"); // 发送方邮箱地址
} private static final String from = "发送方邮箱地址@qq.com";
// 发送方邮箱地址对应的授权码
private static final String authWord = "授权码";
@Test // 接收方邮箱地址
public void sendComplexMessage() { private static final String to = "接收方邮箱地址@qq.com";
Map<String, File> fileMap = new HashMap<>();
fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png")); @Test
fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png")); public void sendMessage() {
springMail.sendEmailWithAttachments(from, authWord, to, "spring多附件邮件"
, "Hello Spring Email!", fileMap); springMail.sendTextMessage(from, authWord, to, "spring简单邮件", "Hello Spring Email!");
} }
@Test
public void sendEmailWithInline() { @Test
springMail.sendEmailWithInline(from, authWord, to, "spring内嵌资源邮件" public void sendComplexMessage() {
, "Hello Spring Email!", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png")); Map<String, File> fileMap = new HashMap<>();
} fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png"));
fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png"));
@Test springMail.sendEmailWithAttachments(from, authWord, to, "spring多附件邮件"
public void sendEmailByTemplate() { , "Hello Spring Email!", fileMap);
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/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
1. 邮件发送配置文件为springApplication.xml; <a href="#二spring-email">二、spring email</a><br/>
2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-邮件发送配置">2.1 邮件发送配置</a><br/>
3. 项目以单元测试的方法进行测试测试类为SendEmail。 &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/>
## 正文<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.2 依赖说明 1. 邮件发送配置文件为springApplication.xml;
2. 简单邮件发送、附件邮件发送、内嵌资源邮件发送、模板邮件发送的方法封装在SpringMail类中
除了spring的基本依赖外需要导入邮件发送的支持包spring-context-support 3. 项目以单元测试的方法进行测试测试类为SendEmail。
```xml
<!--邮件发送依赖包-->
<dependency> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-email.png"/> </div>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring-base-version}</version>
</dependency> ### 1.2 依赖说明
<!--模板引擎-->
<!--这里采用的是beetl,beetl性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">--> 除了spring的基本依赖外需要导入邮件发送的支持包spring-context-support
<dependency>
<groupId>com.ibeetl</groupId> ```xml
<artifactId>beetl</artifactId> <!--邮件发送依赖包-->
<version>2.9.7</version> <dependency>
</dependency> <groupId>org.springframework</groupId>
``` <artifactId>spring-context-support</artifactId>
<version>${spring-base-version}</version>
</dependency>
<!--模板引擎-->
## 二、spring email <!--这里采用的是beetl,beetl性能很卓越并且功能也很全面 官方文档地址 <a href="http://ibeetl.com/guide/#beetl">-->
<dependency>
#### 2.1 邮件发送配置 <groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
```xml <version>2.9.7</version>
<?xml version="1.0" encoding="UTF-8"?> </dependency>
<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
http://www.springframework.org/schema/beans/spring-beans.xsd ## 二、spring email
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd"> #### 2.1 邮件发送配置
<!-- 开启注解包扫描--> ```xml
<context:component-scan base-package="com.heibaiying.email"/> <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
<!--在这里可以声明不同的邮件服务器主机通常是SMTP主机,而具体的用户名和时授权码则建议在业务中从数据库查询--> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<bean id="qqMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> xmlns:context="http://www.springframework.org/schema/context"
<!--qq 邮箱配置 <a href="https://service.mail.qq.com/cgi-bin/help?subtype=1&no=167&id=28"> --> xsi:schemaLocation="http://www.springframework.org/schema/beans
<property name="host" value="smtp.qq.com"/> http://www.springframework.org/schema/beans/spring-beans.xsd
<property name="port" value="587"/> http://www.springframework.org/schema/context
</bean> http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--配置beetle模板引擎 如果不使用模板引擎,以下的配置不是必须的--> <!-- 开启注解包扫描-->
<bean id="resourceLoader" class="org.beetl.core.resource.ClasspathResourceLoader"> <context:component-scan base-package="com.heibaiying.email"/>
<!--指定加载模板资源的位置 指定在classpath:beetl下-->
<constructor-arg name="root" value="beetl"/> <!--在这里可以声明不同的邮件服务器主机通常是SMTP主机,而具体的用户名和时授权码则建议在业务中从数据库查询-->
</bean> <bean id="qqMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<!--beetl 配置 这里采用默认的配置--> <!--qq 邮箱配置 <a href="https://service.mail.qq.com/cgi-bin/help?subtype=1&no=167&id=28"> -->
<bean id="configuration" class="org.beetl.core.Configuration" init-method="defaultConfiguration"/> <property name="host" value="smtp.qq.com"/>
<bean id="groupTemplate" class="org.beetl.core.GroupTemplate"> <property name="port" value="587"/>
<constructor-arg name="loader" ref="resourceLoader"/> </bean>
<constructor-arg name="conf" ref="configuration"/>
</bean> <!--配置beetle模板引擎 如果不使用模板引擎,以下的配置不是必须的-->
<bean id="resourceLoader" class="org.beetl.core.resource.ClasspathResourceLoader">
</beans> <!--指定加载模板资源的位置 指定在classpath:beetl下-->
``` <constructor-arg name="root" value="beetl"/>
</bean>
#### 2.2 新建邮件发送基本类 <!--beetl 配置 这里采用默认的配置-->
<bean id="configuration" class="org.beetl.core.Configuration" init-method="defaultConfiguration"/>
```java <bean id="groupTemplate" class="org.beetl.core.GroupTemplate">
/** <constructor-arg name="loader" ref="resourceLoader"/>
* @author : heibaiying <constructor-arg name="conf" ref="configuration"/>
* @description : 邮件发送基本类 </bean>
*/
@Component </beans>
public class SpringMail { ```
@Autowired #### 2.2 新建邮件发送基本类
private JavaMailSenderImpl qqMailSender;
@Autowired ```java
private GroupTemplate groupTemplate; /**
* @author : heibaiying
/** * @description : 邮件发送基本类
* 发送简单邮件 */
* 在qq邮件发送的测试中测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户 @Component
* 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user public class SpringMail {
* qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 中开启服务后获取
*/ @Autowired
public void sendTextMessage(String from, String authWord, String to, String subject, String content) { private JavaMailSenderImpl qqMailSender;
// 设置发送人邮箱和授权码 @Autowired
qqMailSender.setUsername(from); private GroupTemplate groupTemplate;
qqMailSender.setPassword(authWord);
// 实例化消息对象 /**
SimpleMailMessage msg = new SimpleMailMessage(); * 发送简单邮件
msg.setFrom(from); * 在qq邮件发送的测试中测试结果表明不管是简单邮件还是复杂邮件都必须指定发送用户
msg.setTo(to); * 且发送用户已经授权不然都会抛出异常: SMTPSendFailedException 501 mail from address must be same as authorization user
msg.setSubject(subject); * qq 的授权码 可以在 设置/账户/POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 中开启服务后获取
msg.setText(content); */
try { public void sendTextMessage(String from, String authWord, String to, String subject, String content) {
// 发送消息 // 设置发送人邮箱和授权码
this.qqMailSender.send(msg); qqMailSender.setUsername(from);
System.out.println("发送邮件成功"); qqMailSender.setPassword(authWord);
} catch (MailException ex) { // 实例化消息对象
// 消息发送失败可以做对应的处理 SimpleMailMessage msg = new SimpleMailMessage();
System.err.println("发送邮件失败" + ex.getMessage()); msg.setFrom(from);
} msg.setTo(to);
} msg.setSubject(subject);
msg.setText(content);
/** try {
* 发送带附件的邮件 // 发送消息
*/ this.qqMailSender.send(msg);
public void sendEmailWithAttachments(String from, String authWord, String to, System.out.println("发送邮件成功");
String subject, String content, Map<String, File> files) { } catch (MailException ex) {
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"); */
helper.setFrom(from); public void sendEmailWithAttachments(String from, String authWord, String to,
helper.setTo(to); String subject, String content, Map<String, File> files) {
helper.setSubject(subject); try {
helper.setText(content); // 设置发送人邮箱和授权码
// 传入附件 qqMailSender.setUsername(from);
for (Map.Entry<String, File> entry : files.entrySet()) { qqMailSender.setPassword(authWord);
helper.addAttachment(entry.getKey(), entry.getValue()); // 实例化消息对象
} MimeMessage message = qqMailSender.createMimeMessage();
// 发送消息 // 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息
this.qqMailSender.send(message); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
System.out.println("发送邮件成功"); helper.setFrom(from);
} catch (MessagingException ex) { helper.setTo(to);
// 消息发送失败可以做对应的处理 helper.setSubject(subject);
System.err.println("发送邮件失败" + ex.getMessage()); helper.setText(content);
} // 传入附件
} for (Map.Entry<String, File> entry : files.entrySet()) {
helper.addAttachment(entry.getKey(), entry.getValue());
}
/** // 发送消息
* 发送带内嵌资源的邮件 this.qqMailSender.send(message);
*/ System.out.println("发送邮件成功");
public void sendEmailWithInline(String from, String authWord, String to, } catch (MessagingException ex) {
String subject, String content, File file) { // 消息发送失败可以做对应的处理
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"); */
helper.setFrom(from); public void sendEmailWithInline(String from, String authWord, String to,
helper.setTo(to); String subject, String content, File file) {
helper.setSubject(subject); try {
// 使用true标志来指示包含的文本是HTML 固定格式资源前缀 cid: // 设置发送人邮箱和授权码
helper.setText("<html><body><img src='cid:image'></body></html>", true); qqMailSender.setUsername(from);
// 需要先指定文本 再指定资源文件 qqMailSender.setPassword(authWord);
FileSystemResource res = new FileSystemResource(file); // 实例化消息对象
helper.addInline("image", res); MimeMessage message = qqMailSender.createMimeMessage();
// 发送消息 // 需要指定第二个参数为true 代表创建支持可选文本,内联元素和附件的多部分消息
this.qqMailSender.send(message); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
System.out.println("发送邮件成功"); helper.setFrom(from);
} catch (MessagingException ex) { helper.setTo(to);
// 消息发送失败可以做对应的处理 helper.setSubject(subject);
System.err.println("发送邮件失败" + ex.getMessage()); // 使用true标志来指示包含的文本是HTML 固定格式资源前缀 cid:
} helper.setText("<html><body><img src='cid:image'></body></html>", true);
} // 需要先指定文本 再指定资源文件
FileSystemResource res = new FileSystemResource(file);
/** helper.addInline("image", res);
* 使用模板邮件 // 发送消息
*/ this.qqMailSender.send(message);
public void sendEmailByTemplate(String from, String authWord, String to, System.out.println("发送邮件成功");
String subject, String content) { } catch (MessagingException ex) {
try { // 消息发送失败可以做对应的处理
Template t = groupTemplate.getTemplate("template.html"); System.err.println("发送邮件失败" + ex.getMessage());
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,
MimeMessage message = qqMailSender.createMimeMessage(); String subject, String content) {
// 指定 utf-8 防止乱码 try {
MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); Template t = groupTemplate.getTemplate("template.html");
helper.setFrom(from); t.binding("subject", subject);
helper.setTo(to); t.binding("content", content);
helper.setSubject(subject); String text = t.render();
// 为true 时候 表示文本内容以 html 渲染 // 设置发送人邮箱和授权码
helper.setText(text, true); qqMailSender.setUsername(from);
this.qqMailSender.send(message); qqMailSender.setPassword(authWord);
System.out.println("发送邮件成功"); // 实例化消息对象
} catch (MessagingException ex) { MimeMessage message = qqMailSender.createMimeMessage();
// 消息发送失败可以做对应的处理 // 指定 utf-8 防止乱码
System.err.println("发送邮件失败" + ex.getMessage()); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8");
} 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>
<body> - 这里我们使用的beetl模板引擎原因是其性能优异官网是介绍其性能6倍与freemaker,并有完善的文档支持。当然大家也可以换成任何其他的模板引擎freemarker,thymeleaf
<h1>邮件主题:<span style="color: chartreuse"> ${subject}</span></h1>
<h4 style="color: blueviolet">${content}</h4> 一个简单的模板template.html如下
</body>
</html> ```html
``` <!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
#### 2.3 邮件发送的测试 </head>
<body>
```java <h1>邮件主题:<span style="color: chartreuse"> ${subject}</span></h1>
/** <h4 style="color: blueviolet">${content}</h4>
* @author : heibaiying </body>
* @description : 发送邮件测试类 </html>
*/ ```
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class SendEmail {
#### 2.3 邮件发送的测试
@Autowired
private SpringMail springMail; ```java
/**
// 发送方邮箱地址 * @author : heibaiying
private static final String from = "发送方邮箱地址@qq.com"; * @description : 发送邮件测试类
// 发送方邮箱地址对应的授权码 */
private static final String authWord = "授权码"; @RunWith(SpringRunner.class)
// 接收方邮箱地址 @ContextConfiguration({"classpath:springApplication.xml"})
private static final String to = "接收方邮箱地址@qq.com"; public class SendEmail {
@Autowired
/** private SpringMail springMail;
* 简单邮件测试
*/ // 发送方邮箱地址
@Test private static final String from = "发送方邮箱地址@qq.com";
public void sendMessage() { // 发送方邮箱地址对应的授权码
springMail.sendTextMessage(from, authWord, to, "spring简单邮件", "Hello Spring Email!"); private static final String authWord = "授权码";
} // 接收方邮箱地址
private static final String to = "接收方邮箱地址@qq.com";
/**
* 发送带附件的邮件
*/ /**
@Test * 简单邮件测试
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.sendTextMessage(from, authWord, to, "spring简单邮件", "Hello Spring Email!");
springMail.sendEmailWithAttachments(from, authWord, to, "spring多附件邮件" }
, "Hello Spring Email!", fileMap);
} /**
* 发送带附件的邮件
/** */
* 发送内嵌资源的邮件 @Test
*/ public void sendComplexMessage() {
@Test Map<String, File> fileMap = new HashMap<>();
public void sendEmailWithInline() { fileMap.put("image1.jpg", new File("D:\\LearningNotes\\picture\\msm相关依赖.png"));
springMail.sendEmailWithInline(from, authWord, to, "spring内嵌资源邮件" fileMap.put("image2.jpg", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png"));
, "Hello Spring Email!", new File("D:\\LearningNotes\\picture\\RabbitMQ模型架构.png")); springMail.sendEmailWithAttachments(from, authWord, to, "spring多附件邮件"
} , "Hello Spring Email!", fileMap);
}
/**
* 发送模板邮件 /**
*/ * 发送内嵌资源的邮件
@Test */
public void sendEmailByTemplate() { @Test
springMail.sendEmailByTemplate(from, authWord, to, public void sendEmailWithInline() {
"spring模板邮件", "Hello Spring Email!"); 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,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/>
### 1.1 XMemcached客户端说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11--XMemcached客户端说明">1.1 XMemcached客户端说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-项目结构说明">1.2 项目结构说明</a><br/>
XMemcached是基于java nio的memcached高性能客户端支持完整的memcached协议支持客户端分布并且提供了一致性哈希(consistent hash)算法的实现。 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#13-依赖说明">1.3 依赖说明</a><br/>
<a href="#二spring-整合-memcached">二、spring 整合 memcached</a><br/>
### 1.2 项目结构说明 &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/>
1. memcached的整合配置位于com.heibaiying.config文件夹下。 &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="#25-存储实体对象测试用例">2.5 存储实体对象测试用例</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached-annotation.png"/> </div> <a href="#附memcached-基本命令">memcached 基本命令</a><br/>
### 1.3 依赖说明
除了spring的基本依赖外需要导入xmemcached依赖包
```xml
<!--memcached java 客户端-->
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.5</version>
</dependency>
```
## 正文<br/> ## 正文<br/>
## 二、spring 整合 memcached ## 一、说明
#### 2.1 单机配置 ### 1.1 XMemcached客户端说明
```java XMemcached是基于java nio的memcached高性能客户端支持完整的memcached协议支持客户端分布并且提供了一致性哈希(consistent hash)算法的实现。
@Bean
public MemcachedClient memcachedClient() { ### 1.2 项目结构说明
XMemcachedClientBuilder builder = new XMemcachedClientBuilder("192.168.200.201:11211");
MemcachedClient memcachedClient = null; 1. memcached的整合配置位于com.heibaiying.config文件夹下。
try {
memcachedClient = builder.build(); <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-memcached-annotation.png"/> </div>
} catch (IOException e) {
e.printStackTrace(); ### 1.3 依赖说明
}
return memcachedClient; 除了spring的基本依赖外需要导入xmemcached依赖包
}
``` ```xml
<!--memcached java 客户端-->
#### 2.2 集群配置 <dependency>
<groupId>com.googlecode.xmemcached</groupId>
```java <artifactId>xmemcached</artifactId>
@Bean <version>2.4.5</version>
public MemcachedClient memcachedClientForCluster() { </dependency>
```
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};
XMemcachedClientBuilder builder = new XMemcachedClientBuilder(addressList, weights); #### 2.1 单机配置
// 设置连接池大小
builder.setConnectionPoolSize(10); ```java
// 协议工厂 @Bean
builder.setCommandFactory(new TextCommandFactory()); public MemcachedClient memcachedClient() {
// 分布策略一致性哈希KetamaMemcachedSessionLocator或者ArraySessionLocator(默认) XMemcachedClientBuilder builder = new XMemcachedClientBuilder("192.168.200.201:11211");
builder.setSessionLocator(new KetamaMemcachedSessionLocator()); MemcachedClient memcachedClient = null;
// 设置序列化器 try {
builder.setTranscoder(new SerializingTranscoder()); memcachedClient = builder.build();
MemcachedClient memcachedClient = null; } catch (IOException e) {
try { e.printStackTrace();
memcachedClient = builder.build(); }
} catch (IOException e) { return memcachedClient;
e.printStackTrace(); }
} ```
return memcachedClient;
} #### 2.2 集群配置
```
```java
#### 2.3 存储基本类型测试用例 @Bean
public MemcachedClient memcachedClientForCluster() {
xmemcached单机版本和集群版本注入的实例是相同的
List<InetSocketAddress> addressList = new ArrayList<InetSocketAddress>();
```java addressList.add(new InetSocketAddress("192.168.200.201", 11211));
/** addressList.add(new InetSocketAddress("192.168.200.201", 11212));
* @author : heibaiying // 赋予权重
* @description : Memcached 操作基本对象 int[] weights = {1, 2};
*/ XMemcachedClientBuilder builder = new XMemcachedClientBuilder(addressList, weights);
@RunWith(SpringRunner.class) // 设置连接池大小
@ContextConfiguration(classes = {MemcacheConfig.class}) builder.setConnectionPoolSize(10);
public class MemSamples { // 协议工厂
builder.setCommandFactory(new TextCommandFactory());
@Autowired // 分布策略一致性哈希KetamaMemcachedSessionLocator或者ArraySessionLocator(默认)
private MemcachedClient memcachedClient; builder.setSessionLocator(new KetamaMemcachedSessionLocator());
// 设置序列化器
@Test builder.setTranscoder(new SerializingTranscoder());
public void operate() throws InterruptedException, MemcachedException, TimeoutException { MemcachedClient memcachedClient = null;
memcachedClient.set("hello", 0, "Hello,cluster xmemcached"); try {
String value = memcachedClient.get("hello"); memcachedClient = builder.build();
System.out.println("hello=" + value); } catch (IOException e) {
memcachedClient.delete("hello"); e.printStackTrace();
value = memcachedClient.get("hello"); }
System.out.println("hello=" + value); return memcachedClient;
} }
} ```
```
#### 2.3 存储基本类型测试用例
#### 2.5 存储实体对象测试用例
xmemcached单机版本和集群版本注入的实例是相同的
```java
/** ```java
* @author : heibaiying /**
* @description :Memcached 序列化与反序列化 * @author : heibaiying
*/ * @description : Memcached 操作基本对象
@RunWith(SpringRunner.class) */
@ContextConfiguration(classes = {MemcacheConfig.class}) @RunWith(SpringRunner.class)
public class MemObjectSamples { @ContextConfiguration(classes = {MemcacheConfig.class})
public class MemSamples {
@Autowired
private MemcachedClient memcachedClient; @Autowired
private MemcachedClient memcachedClient;
@Test
public void operate() throws InterruptedException, MemcachedException, TimeoutException { @Test
memcachedClient.set("programmer", 0, new Programmer("xiaoming", 12, 5000.21f, new Date())); public void operate() throws InterruptedException, MemcachedException, TimeoutException {
Programmer programmer = memcachedClient.get("programmer"); memcachedClient.set("hello", 0, "Hello,cluster xmemcached");
System.out.println("hello ," + programmer.getName()); String value = memcachedClient.get("hello");
memcachedClient.delete("programmer"); System.out.println("hello=" + value);
programmer = memcachedClient.get("programmer"); memcachedClient.delete("hello");
Assert.assertNull(programmer); value = memcachedClient.get("hello");
} System.out.println("hello=" + value);
} }
}
``` ```
#### 2.5 存储实体对象测试用例
## 附memcached 基本命令 ```java
/**
| 命令 | 格式 | 说明 | * @author : heibaiying
| --------------- | -------------------------------------------------- | ------------------------------------- | * @description :Memcached 序列化与反序列化
| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | */
| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下才可以插入 | @RunWith(SpringRunner.class)
| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 | @ContextConfiguration(classes = {MemcacheConfig.class})
| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 | public class MemObjectSamples {
| 前面追加prepend | prepend key flags exTime length -> value | length表示追加的长度而不是总长度 |
| 查询操作 get | get key | | @Autowired
| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 | private MemcachedClient memcachedClient;
| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 |
| 删除 delete | delete key | 将数据打一个删除标记 | @Test
| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value | public void operate() throws InterruptedException, MemcachedException, TimeoutException {
| 自减 decr | decr key 减少偏移量 | desr不能将数字减少至0以下 | 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以下 | | 自减 decr | decr key 减少偏移量 | desr不能将数字减少至0以下 |

View File

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

View File

@@ -1,161 +1,171 @@
# spring 整合 mongodb注解方式 # spring 整合 mongodb注解方式
## 一、说明 ## 目录<br/>
<a href="#一说明">一、说明</a><br/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
配置文件位于com.heibaiying.config下,项目以单元测试的方式进行测试。 <a href="#二spring-mongodb">二、spring mongodb</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-新建配置文件及其映射类">2.1 新建配置文件及其映射类</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="#22-整合配置">2.2 整合配置</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-测试整合">2.3 测试整合</a><br/>
## 正文<br/> ## 正文<br/>
### 1.2 依赖说明 ## 一、说明
除了spring的基本依赖外需要导入mongodb整合依赖包 ### 1.1 项目结构说明
```xml 配置文件位于com.heibaiying.config下,项目以单元测试的方式进行测试。
<!--spring mongodb 整合依赖-->
<dependency> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb-annotation.png"/> </div>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.1.3.RELEASE</version>
</dependency> ### 1.2 依赖说明
```
除了spring的基本依赖外需要导入mongodb整合依赖包
```xml
## 二、spring mongodb <!--spring mongodb 整合依赖-->
<dependency>
#### 2.1 新建配置文件及其映射类 <groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
```properties <version>2.1.3.RELEASE</version>
mongo.host=192.168.200.228 </dependency>
mongo.port=27017 ```
# 数据库名称. 默认是'db'.
mongo.dbname=database
# 每个主机允许的连接数
mongo.connectionsPerHost=10 ## 二、spring mongodb
# 线程队列数它和上面connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出异常
mongo.threadsAllowedToBlockForConnectionMultiplier=5 #### 2.1 新建配置文件及其映射类
# 连接超时的毫秒 0是默认值且无限大。
mongo.connectTimeout=1000 ```properties
# 最大等待连接的线程阻塞时间 默认是120000 ms (2 minutes). mongo.host=192.168.200.228
mongo.maxWaitTime=1500 mongo.port=27017
# 保持活动标志,控制是否有套接字保持活动超时 官方默认为true 且不建议禁用 # 数据库名称. 默认是'db'.
mongo.socketKeepAlive=true mongo.dbname=database
# 用于群集心跳的连接的套接字超时。 # 每个主机允许的连接数
mongo.socketTimeout=1500 mongo.connectionsPerHost=10
``` # 线程队列数它和上面connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出异常
mongo.threadsAllowedToBlockForConnectionMultiplier=5
```java # 连接超时的毫秒 0是默认值且无限大。
/** mongo.connectTimeout=1000
* @author : heibaiying # 最大等待连接的线程阻塞时间 默认是120000 ms (2 minutes).
* @description : Mongo 配置属性 mongo.maxWaitTime=1500
*/ # 保持活动标志,控制是否有套接字保持活动超时 官方默认为true 且不建议禁用
@Data mongo.socketKeepAlive=true
@Configuration # 用于群集心跳的连接的套接字超时。
@PropertySource(value = "classpath:mongodb.properties") mongo.socketTimeout=1500
public class MongoProperty { ```
@Value("${mongo.host}") ```java
private String host; /**
@Value("${mongo.port}") * @author : heibaiying
private int port; * @description : Mongo 配置属性
@Value("${mongo.dbname}") */
private String dbname; @Data
@Value("${mongo.connectionsPerHost}") @Configuration
private int connectionsPerHost; @PropertySource(value = "classpath:mongodb.properties")
@Value("${mongo.threadsAllowedToBlockForConnectionMultiplier}") public class MongoProperty {
private int multiplier;
@Value("${mongo.connectTimeout}") @Value("${mongo.host}")
private int connectTimeout; private String host;
@Value("${mongo.maxWaitTime}") @Value("${mongo.port}")
private int maxWaitTime; private int port;
@Value("${mongo.socketKeepAlive}") @Value("${mongo.dbname}")
private boolean socketKeepAlive; private String dbname;
@Value("${mongo.socketTimeout}") @Value("${mongo.connectionsPerHost}")
private int socketTimeout; private int connectionsPerHost;
} @Value("${mongo.threadsAllowedToBlockForConnectionMultiplier}")
``` private int multiplier;
@Value("${mongo.connectTimeout}")
#### 2.2 整合配置 private int connectTimeout;
@Value("${mongo.maxWaitTime}")
```java private int maxWaitTime;
/** @Value("${mongo.socketKeepAlive}")
* @author : heibaiying private boolean socketKeepAlive;
* @description : Mongo 配置类 @Value("${mongo.socketTimeout}")
*/ private int socketTimeout;
}
@Configuration ```
@ComponentScan(value = "com.heibaiying.*")
public class MongoConfig { #### 2.2 整合配置
@Bean ```java
public MongoDbFactory mongoDbFactory(MongoProperty mongo) { /**
MongoClientOptions options = MongoClientOptions.builder() * @author : heibaiying
.threadsAllowedToBlockForConnectionMultiplier(mongo.getMultiplier()) * @description : Mongo 配置类
.connectionsPerHost(mongo.getConnectionsPerHost()) */
.connectTimeout(mongo.getConnectTimeout())
.maxWaitTime(mongo.getMaxWaitTime()) @Configuration
.socketTimeout(mongo.getSocketTimeout()) @ComponentScan(value = "com.heibaiying.*")
.build(); public class MongoConfig {
MongoClient client = new MongoClient(new ServerAddress(mongo.getHost(), mongo.getPort()), options);
return new SimpleMongoDbFactory(client, mongo.getDbname()); @Bean
} public MongoDbFactory mongoDbFactory(MongoProperty mongo) {
MongoClientOptions options = MongoClientOptions.builder()
@Bean .threadsAllowedToBlockForConnectionMultiplier(mongo.getMultiplier())
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory) { .connectionsPerHost(mongo.getConnectionsPerHost())
return new MongoTemplate(mongoDbFactory); .connectTimeout(mongo.getConnectTimeout())
} .maxWaitTime(mongo.getMaxWaitTime())
} .socketTimeout(mongo.getSocketTimeout())
``` .build();
MongoClient client = new MongoClient(new ServerAddress(mongo.getHost(), mongo.getPort()), options);
#### 2.3 测试整合 return new SimpleMongoDbFactory(client, mongo.getDbname());
}
```java
@RunWith(SpringRunner.class) @Bean
@ContextConfiguration(classes = MongoConfig.class) public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory) {
public class MongoDBTest { return new MongoTemplate(mongoDbFactory);
}
@Autowired }
private MongoTemplate mongoTemplate; ```
@Test #### 2.3 测试整合
public void insert() {
// 单条插入 ```java
mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date())); @RunWith(SpringRunner.class)
List<Programmer> programmers = new ArrayList<Programmer>(); @ContextConfiguration(classes = MongoConfig.class)
// 批量插入 public class MongoDBTest {
programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date()));
programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date())); @Autowired
mongoTemplate.insert(programmers, Programmer.class); private MongoTemplate mongoTemplate;
}
@Test
// 条件查询 public void insert() {
@Test // 单条插入
public void select() { mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date()));
Criteria criteria = new Criteria(); List<Programmer> programmers = new ArrayList<Programmer>();
criteria.andOperator(where("name").is("xiaohong"), where("age").is(21)); // 批量插入
Query query = new Query(criteria); programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date()));
Programmer one = mongoTemplate.findOne(query, Programmer.class); programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date()));
System.out.println(one); mongoTemplate.insert(programmers, Programmer.class);
} }
// 条件查询
// 更新数据 @Test
@Test public void select() {
public void MUpdate() { Criteria criteria = new Criteria();
UpdateResult updateResult = mongoTemplate.updateMulti(query(where("name").is("xiaoming")), update("age", 35), Programmer.class); criteria.andOperator(where("name").is("xiaohong"), where("age").is(21));
System.out.println("更新记录数:" + updateResult.getModifiedCount()); Query query = new Query(criteria);
} 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); // 更新数据
System.out.println("影响记录数:" + result.getDeletedCount()); @Test
System.out.println("是否成功:" + result.wasAcknowledged()); public void MUpdate() {
} 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/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
配置文件位于resources下,项目以单元测试的方式进行测试。 <a href="#二spring-mongodb">二、spring mongodb</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-新建配置文件">2.1 新建配置文件</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="#22-整合配置">2.2 整合配置</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-测试整合">2.3 测试整合</a><br/>
## 正文<br/> ## 正文<br/>
### 1.2 依赖说明 ## 一、说明
除了spring的基本依赖外需要导入mongodb整合依赖包 ### 1.1 项目结构说明
```xml 配置文件位于resources下,项目以单元测试的方式进行测试。
<!--spring mongodb 整合依赖-->
<dependency> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mongodb.png"/> </div>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.1.3.RELEASE</version>
</dependency> ### 1.2 依赖说明
```
除了spring的基本依赖外需要导入mongodb整合依赖包
```xml
## 二、spring mongodb <!--spring mongodb 整合依赖-->
<dependency>
#### 2.1 新建配置文件 <groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
```properties <version>2.1.3.RELEASE</version>
mongo.host=192.168.200.228 </dependency>
mongo.port=27017 ```
# 数据库名称. 默认是'db'.
mongo.dbname=database
# 每个主机允许的连接数
mongo.connectionsPerHost=10 ## 二、spring mongodb
# 线程队列数它和上面connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出异常
mongo.threadsAllowedToBlockForConnectionMultiplier=5 #### 2.1 新建配置文件
# 连接超时的毫秒 0是默认值且无限大。
mongo.connectTimeout=1000 ```properties
# 最大等待连接的线程阻塞时间 默认是120000 ms (2 minutes). mongo.host=192.168.200.228
mongo.maxWaitTime=1500 mongo.port=27017
# 保持活动标志,控制是否有套接字保持活动超时 官方默认为true 且不建议禁用 # 数据库名称. 默认是'db'.
mongo.socketKeepAlive=true mongo.dbname=database
# 用于群集心跳的连接的套接字超时。 # 每个主机允许的连接数
mongo.socketTimeout=1500 mongo.connectionsPerHost=10
``` # 线程队列数它和上面connectionsPerHost值相乘的结果就是线程队列最大值。如果连接线程排满了队列就会抛出异常
mongo.threadsAllowedToBlockForConnectionMultiplier=5
#### 2.2 整合配置 # 连接超时的毫秒 0是默认值且无限大。
mongo.connectTimeout=1000
```xml # 最大等待连接的线程阻塞时间 默认是120000 ms (2 minutes).
<?xml version="1.0" encoding="UTF-8"?> mongo.maxWaitTime=1500
<beans xmlns="http://www.springframework.org/schema/beans" # 保持活动标志,控制是否有套接字保持活动超时 官方默认为true 且不建议禁用
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" mongo.socketKeepAlive=true
xmlns:context="http://www.springframework.org/schema/context" # 用于群集心跳的连接的套接字超时。
xmlns:mongo="http://www.springframework.org/schema/data/mongo" mongo.socketTimeout=1500
xsi:schemaLocation= ```
"http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd #### 2.2 整合配置
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
http://www.springframework.org/schema/beans ```xml
http://www.springframework.org/schema/beans/spring-beans.xsd"> <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
<!--扫描配置文件--> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<context:property-placeholder location="classpath:mongodb.properties"/> xmlns:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
<!--定义用于访问MongoDB的MongoClient实例--> xsi:schemaLocation=
<mongo:mongo-client host="${mongo.host}" port="${mongo.port}"> "http://www.springframework.org/schema/context
<mongo:client-options http://www.springframework.org/schema/context/spring-context.xsd
connections-per-host="${mongo.connectionsPerHost}" http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}" http://www.springframework.org/schema/beans
connect-timeout="${mongo.connectTimeout}" http://www.springframework.org/schema/beans/spring-beans.xsd">
max-wait-time="${mongo.maxWaitTime}"
socket-keep-alive="${mongo.socketKeepAlive}" <!--扫描配置文件-->
socket-timeout="${mongo.socketTimeout}" <context:property-placeholder location="classpath:mongodb.properties"/>
/>
</mongo:mongo-client> <!--定义用于访问MongoDB的MongoClient实例-->
<mongo:mongo-client host="${mongo.host}" port="${mongo.port}">
<!--定义用于连接到数据库的连接工厂--> <mongo:client-options
<mongo:db-factory dbname="${mongo.dbname}" mongo-ref="mongoClient"/> connections-per-host="${mongo.connectionsPerHost}"
threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}"
<!--实际操作mongodb的template,在代码中注入--> connect-timeout="${mongo.connectTimeout}"
<bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> max-wait-time="${mongo.maxWaitTime}"
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/> socket-keep-alive="${mongo.socketKeepAlive}"
</bean> socket-timeout="${mongo.socketTimeout}"
/>
</beans> </mongo:mongo-client>
```
<!--定义用于连接到数据库的连接工厂-->
#### 2.3 测试整合 <mongo:db-factory dbname="${mongo.dbname}" mongo-ref="mongoClient"/>
```java <!--实际操作mongodb的template,在代码中注入-->
/** <bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
* @author : heibaiying <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
* @description : MongoDB 查询 </bean>
*/
@RunWith(SpringRunner.class) </beans>
@ContextConfiguration(locations = "classpath:mongodb.xml") ```
public class MongoDBTest {
#### 2.3 测试整合
@Autowired
private MongoTemplate mongoTemplate; ```java
/**
@Test * @author : heibaiying
public void insert() { * @description : MongoDB 查询
// 单条插入 */
mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date())); @RunWith(SpringRunner.class)
List<Programmer> programmers = new ArrayList<Programmer>(); @ContextConfiguration(locations = "classpath:mongodb.xml")
// 批量插入 public class MongoDBTest {
programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date()));
programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date())); @Autowired
mongoTemplate.insert(programmers, Programmer.class); private MongoTemplate mongoTemplate;
}
@Test
// 条件查询 public void insert() {
@Test // 单条插入
public void select() { mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date()));
Criteria criteria = new Criteria(); List<Programmer> programmers = new ArrayList<Programmer>();
criteria.andOperator(where("name").is("xiaohong"), where("age").is(21)); // 批量插入
Query query = new Query(criteria); programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date()));
Programmer one = mongoTemplate.findOne(query, Programmer.class); programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date()));
System.out.println(one); mongoTemplate.insert(programmers, Programmer.class);
} }
// 条件查询
// 更新数据 @Test
@Test public void select() {
public void MUpdate() { Criteria criteria = new Criteria();
UpdateResult updateResult = mongoTemplate.updateMulti(query(where("name").is("xiaoming")), update("age", 35), Programmer.class); criteria.andOperator(where("name").is("xiaohong"), where("age").is(21));
System.out.println("更新记录数:" + updateResult.getModifiedCount()); Query query = new Query(criteria);
} 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); // 更新数据
System.out.println("影响记录数:" + result.getDeletedCount()); @Test
System.out.println("是否成功:" + result.wasAcknowledged()); public void MUpdate() {
} 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/> ## 正文<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/>
#### 1.1 项目结构 &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/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mybatis.png"/> </div> <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/>
#### 1.2 项目依赖 &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/>
除了spring相关依赖外还需要导入数据库驱动和对应的mybatis依赖包 &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/>
```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>
## 正文<br/> ## 正文<br/>
<!--定义事务管理器-->
<bean id="transactionManager" ## 一、说明
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/> #### 1.1 项目结构
</bean>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-mybatis.png"/> </div>
<!-- 开启事务注解@Transactional支持 -->
<tx:annotation-driven/> #### 1.2 项目依赖
</beans> 除了spring相关依赖外还需要导入数据库驱动和对应的mybatis依赖包
```
```xml
#### 2.3 新建mybtais配置文件按照需求配置额外参数 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html) <!--jdbc 相关依赖包-->
<dependency>
```xml <groupId>org.springframework</groupId>
<?xml version="1.0" encoding="UTF-8" ?> <artifactId>spring-jdbc</artifactId>
<!DOCTYPE configuration <version>${spring-base-version}</version>
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" </dependency>
"http://mybatis.org/dtd/mybatis-3-config.dtd"> <dependency>
<groupId>mysql</groupId>
<!-- mybatis 配置文件 --> <artifactId>mysql-connector-java</artifactId>
<configuration> <version>8.0.13</version>
<settings> </dependency>
<!-- 开启驼峰命名 --> <dependency>
<setting name="mapUnderscoreToCamelCase" value="true"/> <groupId>com.oracle</groupId>
<!-- 打印查询sql --> <artifactId>ojdbc6</artifactId>
<setting name="logImpl" value="STDOUT_LOGGING"/> <version>11.2.0.3.0</version>
</settings> </dependency>
<!--mybatis 依赖包-->
</configuration> <dependency>
<groupId>org.mybatis</groupId>
<!--更多settings配置项可以参考官方文档: <a href="http://www.mybatis.org/mybatis-3/zh/configuration.html"/>--> <artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
``` </dependency>
<dependency>
#### 2.4 新建查询接口及其对应的mapper文件 <groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
```java <version>3.4.6</version>
public interface MysqlDao { </dependency>
```
List<Relation> get();
} ## 二、spring 整合 mybatis
```
#### 2.1 在resources文件夹下新建数据库配置文件jdbc.properties
```xml
<!DOCTYPE mapper ```properties
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" # mysql 数据库配置
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mysql
<mapper namespace="com.heibaiying.dao.MysqlDao"> mysql.username=root
mysql.password=root
<select id="queryById" resultType="com.heibaiying.bean.Relation">
SELECT help_keyword_id AS id,name # oracle 数据库配置
FROM HELP_KEYWORD oracle.driverClassName=oracle.jdbc.driver.OracleDriver
WHERE HELP_KEYWORD_ID = #{id} oracle.url=jdbc:oracle:thin:@//IP地址:端口号/数据库实例名
</select> oracle.username=用户名
oracle.password=密码
</mapper> ```
```
#### 2.2 配置数据源和mybatis会话工厂、定义事务管理器
```mysql
public interface OracleDao { ```xml
<?xml version="1.0" encoding="UTF-8"?>
List<Flow> queryById(long id); <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">
```xml
<!DOCTYPE mapper <!-- 开启注解包扫描-->
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" <context:component-scan base-package="com.heibaiying.*"/>
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--指定配置文件的位置-->
<mapper namespace="com.heibaiying.dao.OracleDao"> <context:property-placeholder location="classpath:jdbc.properties"/>
<select id="queryById" resultType="com.heibaiying.bean.Flow"> <!--配置数据源-->
select * from APEX_030200.WWV_FLOW_CALS where ID = #{id} <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
</select> <!--Mysql 配置-->
<property name="driverClassName" value="${mysql.driverClassName}"/>
</mapper> <property name="url" value="${mysql.url}"/>
``` <property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
#### 2.5 新建测试类进行测试 <!--Oracle 配置-->
<!--<property name="driverClassName" value="${oracle.driverClassName}"/>
```java <property name="url" value="${oracle.url}"/>
@RunWith(SpringRunner.class) <property name="username" value="${oracle.username}"/>
@ContextConfiguration({"classpath:springApplication.xml"}) <property name="password" value="${oracle.password}"/>-->
public class MysqlDaoTest { </bean>
@Autowired <!--配置 mybatis 会话工厂 -->
private MysqlDao mysqlDao; <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
@Test <!--指定mapper文件所在的位置-->
public void get() { <property name="mapperLocations" value="classpath*:/mappers/**/*.xml"/>
List<Relation> relations = mysqlDao.get(); <property name="configLocation" value="classpath:mybatisConfig.xml"/>
if (relations != null) { </bean>
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"}) <!--定义事务管理器-->
public class OracleDaoTest { <bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
/*注入接口时: 如果接口有多个实现类 可以用这个注解指定具体的实现类*/ <property name="dataSource" ref="dataSource"/>
@Qualifier("oracleDaoImpl") </bean>
@Autowired
private OracleDao oracleDao; <!-- 开启事务注解@Transactional支持 -->
<tx:annotation-driven/>
@Test
public void get() { </beans>
List<Flow> flows = oracleDao.get(); ```
if (flows != null) {
for (Flow flow : flows) { #### 2.3 新建mybtais配置文件按照需求配置额外参数 更多settings配置项可以参考[官方文档](http://www.mybatis.org/mybatis-3/zh/configuration.html)
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/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
1. 本用例关于rabbitmq的整合提供**简单消息发送**和**对象消费发送**两种情况下的sample。 <a href="#二spring-rabbit-基本配置">二、spring rabbit 基本配置</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-基本配置属性及其映射类">2.1 基本配置属性及其映射类</a><br/>
2. rabbitBaseAnnotation.java中声明了topic类型的交换机、持久化队列、及其绑定关系用于测试说明topic交换机路由键的绑定规则。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-创建连接工厂管理器">2.2 创建连接工厂、管理器</a><br/>
<a href="#三简单消费的发送">三、简单消费的发送</a><br/>
3. rabbitObjectAnnotation.java中声明了direct类型的交换机持久化队列及其绑定关系用于示例对象消息的传输。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#31-声明交换机队列绑定关系和消费者监听器">3.1 声明交换机、队列、绑定关系和消费者监听器</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#32-测试简单消息的发送">3.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),里面有详细的配图说明。 <a href="#四传输对象">四、传输对象</a><br/>
&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/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-rabbitmq-annotation.png"/> </div> ## 正文<br/>
## 一、说明
### 1.2 依赖说明
### 1.1 项目结构说明
除了spring的基本依赖外需要导入spring rabbitmq 整合依赖
1. 本用例关于rabbitmq的整合提供**简单消息发送**和**对象消费发送**两种情况下的sample。
```xml
<!--spring rabbitmq 整合依赖--> 2. rabbitBaseAnnotation.java中声明了topic类型的交换机、持久化队列、及其绑定关系用于测试说明topic交换机路由键的绑定规则。
<dependency>
<groupId>org.springframework.amqp</groupId> 3. rabbitObjectAnnotation.java中声明了direct类型的交换机持久化队列及其绑定关系用于示例对象消息的传输。
<artifactId>spring-rabbit</artifactId>
<version>2.1.2.RELEASE</version> 关于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),里面有详细的配图说明。
</dependency>
<!--rabbitmq 传输对象序列化依赖了这个包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-rabbitmq-annotation.png"/> </div>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
``` ### 1.2 依赖说明
除了spring的基本依赖外需要导入spring rabbitmq 整合依赖
## 二、spring rabbit 基本配置 ```xml
<!--spring rabbitmq 整合依赖-->
#### 2.1 基本配置属性及其映射类 <dependency>
<groupId>org.springframework.amqp</groupId>
```properties <artifactId>spring-rabbit</artifactId>
rabbitmq.addresses=localhost:5672 <version>2.1.2.RELEASE</version>
rabbitmq.username=guest </dependency>
rabbitmq.password=guest <!--rabbitmq 传输对象序列化依赖了这个包-->
# 虚拟主机,可以类比为命名空间 默认为/ 必须先用图形界面或者管控台添加 程序不会自动创建且会抛出异常 <dependency>
rabbitmq.virtualhost=/ <groupId>com.fasterxml.jackson.core</groupId>
``` <artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
```java </dependency>
/** ```
* @author : heibaiying
* @description : rabbit 属性配置
*/
@Data ## 二、spring rabbit 基本配置
@PropertySource(value = "classpath:rabbitmq.properties")
@Configuration #### 2.1 基本配置属性及其映射类
public class RabbitProperty {
```properties
rabbitmq.addresses=localhost:5672
@Value("${rabbitmq.addresses}") rabbitmq.username=guest
private String addresses; rabbitmq.password=guest
# 虚拟主机,可以类比为命名空间 默认为/ 必须先用图形界面或者管控台添加 程序不会自动创建且会抛出异常
@Value("${rabbitmq.username}") rabbitmq.virtualhost=/
private String username; ```
@Value("${rabbitmq.password}") ```java
private String password; /**
* @author : heibaiying
@Value("${rabbitmq.virtualhost}") * @description : rabbit 属性配置
private String virtualhost; */
} @Data
``` @PropertySource(value = "classpath:rabbitmq.properties")
@Configuration
#### 2.2 创建连接工厂、管理器 public class RabbitProperty {
```java
@Value("${rabbitmq.addresses}")
@Configuration private String addresses;
@ComponentScan("com.heibaiying.rabbit.config")
public class RabbitBaseConfig { @Value("${rabbitmq.username}")
private String username;
/**
* 声明连接工厂 @Value("${rabbitmq.password}")
*/ private String password;
@Bean
public ConnectionFactory connectionFactory(RabbitProperty property) { @Value("${rabbitmq.virtualhost}")
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); private String virtualhost;
connectionFactory.setAddresses(property.getAddresses()); }
connectionFactory.setUsername(property.getUsername()); ```
connectionFactory.setPassword(property.getPassword());
connectionFactory.setVirtualHost(property.getVirtualhost()); #### 2.2 创建连接工厂、管理器
return connectionFactory;
} ```java
/** @Configuration
* 创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。 @ComponentScan("com.heibaiying.rabbit.config")
* auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为true。 public class RabbitBaseConfig {
*/
@Bean /**
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) { * 声明连接工厂
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); */
rabbitAdmin.setAutoStartup(true); @Bean
return rabbitAdmin; public ConnectionFactory connectionFactory(RabbitProperty property) {
} CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(property.getAddresses());
connectionFactory.setUsername(property.getUsername());
@Bean connectionFactory.setPassword(property.getPassword());
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { connectionFactory.setVirtualHost(property.getVirtualhost());
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); return connectionFactory;
return rabbitTemplate; }
}
} /**
``` * 创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。
* auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为true。
*/
@Bean
## 三、简单消费的发送 public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
#### 3.1 声明交换机、队列、绑定关系和消费者监听器 rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
```java }
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory; @Bean
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
import org.springframework.context.annotation.Bean; return rabbitTemplate;
import org.springframework.context.annotation.Configuration; }
}
/** ```
* @author : heibaiying
* @description : 声明队列、交换机、绑定关系、和队列消息监听
*/
## 三、简单消费的发送
@Configuration
public class RabbitBaseAnnotation { #### 3.1 声明交换机、队列、绑定关系和消费者监听器
@Bean ```java
public TopicExchange exchange() { import com.rabbitmq.client.Channel;
// 创建一个持久化的交换机 import org.springframework.amqp.core.*;
return new TopicExchange("topic01", true, false); import org.springframework.amqp.rabbit.connection.ConnectionFactory;
} import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
@Bean import org.springframework.context.annotation.Bean;
public Queue firstQueue() { import org.springframework.context.annotation.Configuration;
// 创建一个持久化的队列1
return new Queue("FirstQueue", true); /**
} * @author : heibaiying
* @description : 声明队列、交换机、绑定关系、和队列消息监听
@Bean */
public Queue secondQueue() {
// 创建一个持久化的队列2 @Configuration
return new Queue("SecondQueue", true); public class RabbitBaseAnnotation {
}
@Bean
/** public TopicExchange exchange() {
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词 // 创建一个持久化的交换机
* 这里我们声明三个绑定关系用于测试topic这种类型交换器 return new TopicExchange("topic01", true, false);
*/ }
@Bean
public Binding orange() { @Bean
return BindingBuilder.bind(firstQueue()).to(exchange()).with("*.orange.*"); public Queue firstQueue() {
} // 创建一个持久化的队列1
return new Queue("FirstQueue", true);
@Bean }
public Binding rabbit() {
return BindingBuilder.bind(secondQueue()).to(exchange()).with("*.*.rabbit"); @Bean
} public Queue secondQueue() {
// 创建一个持久化的队列2
@Bean return new Queue("SecondQueue", true);
public Binding lazy() { }
return BindingBuilder.bind(secondQueue()).to(exchange()).with("lazy.#");
} /**
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词
* 这里我们声明三个绑定关系用于测试topic这种类型交换器
/*创建队列1消费者监听*/ */
@Bean @Bean
public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) { public Binding orange() {
return BindingBuilder.bind(firstQueue()).to(exchange()).with("*.orange.*");
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); }
// 设置监听的队列
container.setQueues(firstQueue()); @Bean
// 指定要创建的并发使用者数。 public Binding rabbit() {
container.setConcurrentConsumers(1); return BindingBuilder.bind(secondQueue()).to(exchange()).with("*.*.rabbit");
// 设置消费者数量的上限 }
container.setMaxConcurrentConsumers(5);
// 设置是否自动签收消费 为保证消费被成功消费,建议手工签收 @Bean
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); public Binding lazy() {
container.setMessageListener(new ChannelAwareMessageListener() { return BindingBuilder.bind(secondQueue()).to(exchange()).with("lazy.#");
@Override }
public void onMessage(Message message, Channel channel) throws Exception {
// 可以在这个地方得到消息额外属性
MessageProperties properties = message.getMessageProperties(); /*创建队列1消费者监听*/
//得到消息体内容 @Bean
byte[] body = message.getBody(); public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) {
System.out.println(firstQueue().getName() + "收到消息:" + new String(body));
/* SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
* DeliveryTag 是一个单调递增的整数 // 设置监听的队列
* 第二个参数 代表是否一次签收多条如果设置为true,则所有DeliveryTag小于该DeliveryTag的消息都会被签收 container.setQueues(firstQueue());
*/ // 指定要创建的并发使用者数。
channel.basicAck(properties.getDeliveryTag(), false); container.setConcurrentConsumers(1);
} // 设置消费者数量的上限
}); container.setMaxConcurrentConsumers(5);
return container; // 设置是否自动签收消费 为保证消费被成功消费,建议手工签收
} container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setMessageListener(new ChannelAwareMessageListener() {
@Override
/*创建队列2消费者监听*/ public void onMessage(Message message, Channel channel) throws Exception {
@Bean // 可以在这个地方得到消息额外属性
public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) { MessageProperties properties = message.getMessageProperties();
//得到消息体内容
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); byte[] body = message.getBody();
container.setQueues(secondQueue()); System.out.println(firstQueue().getName() + "收到消息:" + new String(body));
container.setMessageListener(new ChannelAwareMessageListener() { /*
@Override * DeliveryTag 是一个单调递增的整数
public void onMessage(Message message, Channel channel) throws Exception { * 第二个参数 代表是否一次签收多条如果设置为true,则所有DeliveryTag小于该DeliveryTag的消息都会被签收
byte[] body = message.getBody(); */
System.out.println(secondQueue().getName() + "收到消息:" + new String(body)); channel.basicAck(properties.getDeliveryTag(), false);
} }
}); });
return container; return container;
} }
}
``` /*创建队列2消费者监听*/
@Bean
#### 3.2 测试简单消息的发送 public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) {
```java SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
/** container.setQueues(secondQueue());
* @author : heibaiying container.setMessageListener(new ChannelAwareMessageListener() {
* @description : 传输简单字符串 @Override
*/ public void onMessage(Message message, Channel channel) throws Exception {
byte[] body = message.getBody();
@RunWith(SpringRunner.class) System.out.println(secondQueue().getName() + "收到消息:" + new String(body));
@ContextConfiguration(classes = RabbitBaseConfig.class) }
public class RabbitTest { });
return container;
@Autowired }
private RabbitTemplate rabbitTemplate;
}
@Test ```
public void sendMessage() {
MessageProperties properties = new MessageProperties(); #### 3.2 测试简单消息的发送
String allReceived = "我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到"; ```java
Message message1 = new Message(allReceived.getBytes(), properties); /**
rabbitTemplate.send("topic01", "quick.orange.rabbit", message1); * @author : heibaiying
* @description : 传输简单字符串
String firstReceived = "我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到"; */
Message message2 = new Message(firstReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "quick.orange.fox", message2); @RunWith(SpringRunner.class)
@ContextConfiguration(classes = RabbitBaseConfig.class)
String secondReceived = "我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到"; public class RabbitTest {
Message message3 = new Message(secondReceived.getBytes(), properties);
rabbitTemplate.send("topic01", "lazy.brown.fox", message3); @Autowired
private RabbitTemplate rabbitTemplate;
String notReceived = "我的路由键 quick.brown.fox 不符合 topic1 任何绑定队列的要求,你将看不到我";
Message message4 = new Message(notReceived.getBytes(), properties); @Test
rabbitTemplate.send("topic01", "quick.brown.fox", message4); public void sendMessage() {
} MessageProperties properties = new MessageProperties();
}
``` String allReceived = "我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到";
Message message1 = new Message(allReceived.getBytes(), properties);
```java rabbitTemplate.send("topic01", "quick.orange.rabbit", message1);
结果:
SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到 String firstReceived = "我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到";
FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到 Message message2 = new Message(firstReceived.getBytes(), properties);
FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到 rabbitTemplate.send("topic01", "quick.orange.fox", message2);
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);
#### 4.1 创建消息的委托处理器 rabbitTemplate.send("topic01", "quick.brown.fox", message4);
}
这里为了增强用例的实用性我们创建的处理器的handleMessage方法是一个重载方法对于同一个队列的监听不仅可以传输对象消息同时针对不同的对象类型调用不同的处理方法。 }
```
```java
/** ```java
* @author : heibaiying 结果:
* @description :消息委派处理类 SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到
*/ FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到
public class MessageDelegate { FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到
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
```java * @description :消息委派处理类
/** */
* @author : heibaiying public class MessageDelegate {
* @description : 声明队列、交换机、绑定关系、用于测试对象的消息传递
*/ public void handleMessage(ProductManager manager) {
System.out.println("收到一个产品经理" + manager);
@Configuration }
public class RabbitObjectAnnotation {
public void handleMessage(Programmer programmer) {
@Bean System.out.println("收到一个程序员" + programmer);
public DirectExchange objectTopic() { }
// 创建一个持久化的交换机
return new DirectExchange("objectTopic", true, false); }
} ```
@Bean #### 4.2 声明交换机、队列、绑定关系和消费者监听器
public Queue objectQueue() {
// 创建一个持久化的队列 ```java
return new Queue("objectQueue", true); /**
} * @author : heibaiying
* @description : 声明队列、交换机、绑定关系、用于测试对象的消息传递
@Bean */
public Binding binding() {
return BindingBuilder.bind(objectQueue()).to(objectTopic()).with("object"); @Configuration
} public class RabbitObjectAnnotation {
@Bean
/*创建队列消费者监听*/ public DirectExchange objectTopic() {
@Bean // 创建一个持久化的交换机
public SimpleMessageListenerContainer objectQueueLister(ConnectionFactory connectionFactory) { return new DirectExchange("objectTopic", true, false);
}
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
// 设置监听的队列 @Bean
container.setQueues(objectQueue()); public Queue objectQueue() {
// 将监听到的消息委派给实际的处理类 // 创建一个持久化的队列
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate()); return new Queue("objectQueue", true);
// 指定由哪个方法来处理消息 默认就是handleMessage }
adapter.setDefaultListenerMethod("handleMessage");
@Bean
// 消息转换 public Binding binding() {
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(); return BindingBuilder.bind(objectQueue()).to(objectTopic()).with("object");
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); }
Map<String, Class<?>> idClassMapping = new HashMap<>();
// 针对不同的消息体调用不同的重载方法 /*创建队列消费者监听*/
idClassMapping.put(Type.MANAGER, com.heibaiying.bean.ProductManager.class); @Bean
idClassMapping.put(Type.PROGRAMMER, com.heibaiying.bean.Programmer.class); public SimpleMessageListenerContainer objectQueueLister(ConnectionFactory connectionFactory) {
javaTypeMapper.setIdClassMapping(idClassMapping); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
// 设置监听的队列
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper); container.setQueues(objectQueue());
adapter.setMessageConverter(jackson2JsonMessageConverter); // 将监听到的消息委派给实际的处理类
container.setMessageListener(adapter); MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
return container; // 指定由哪个方法来处理消息 默认就是handleMessage
} adapter.setDefaultListenerMethod("handleMessage");
} // 消息转换
``` Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
#### 4.3 测试对象消息的发送
Map<String, Class<?>> idClassMapping = new HashMap<>();
```java // 针对不同的消息体调用不同的重载方法
@RunWith(SpringRunner.class) idClassMapping.put(Type.MANAGER, com.heibaiying.bean.ProductManager.class);
@ContextConfiguration(classes = RabbitBaseConfig.class) idClassMapping.put(Type.PROGRAMMER, com.heibaiying.bean.Programmer.class);
public class RabbitSendObjectTest {
javaTypeMapper.setIdClassMapping(idClassMapping);
@Autowired
private RabbitTemplate rabbitTemplate; jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
@Test container.setMessageListener(adapter);
public void sendProgrammer() throws JsonProcessingException { return container;
MessageProperties messageProperties = new MessageProperties(); }
//必须设置 contentType为 application/json
messageProperties.setContentType("application/json"); }
// 必须指定类型 ```
messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER);
Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date()); #### 4.3 测试对象消息的发送
// 序列化与反序列化都使用的Jackson
ObjectMapper mapper = new ObjectMapper(); ```java
String programmerJson = mapper.writeValueAsString(programmer); @RunWith(SpringRunner.class)
Message message = new Message(programmerJson.getBytes(), messageProperties); @ContextConfiguration(classes = RabbitBaseConfig.class)
rabbitTemplate.send("objectTopic", "object", message); public class RabbitSendObjectTest {
}
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void sendProductManager() throws JsonProcessingException { @Test
MessageProperties messageProperties = new MessageProperties(); public void sendProgrammer() throws JsonProcessingException {
messageProperties.setContentType("application/json"); MessageProperties messageProperties = new MessageProperties();
messageProperties.getHeaders().put("__TypeId__", Type.MANAGER); //必须设置 contentType为 application/json
ProductManager manager = new ProductManager("xiaohong", 21, new Date()); messageProperties.setContentType("application/json");
ObjectMapper mapper = new ObjectMapper(); // 必须指定类型
String managerJson = mapper.writeValueAsString(manager); messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER);
Message message = new Message(managerJson.getBytes(), messageProperties); Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date());
rabbitTemplate.send("objectTopic", "object", message); // 序列化与反序列化都使用的Jackson
} 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/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
1. 本用例关于rabbitmq的整合提供**简单消息发送**和**对象消费发送**两种情况下的sample。 <a href="#二spring-rabbit-基本配置">二、spring rabbit 基本配置</a><br/>
<a href="#三简单消费的发送">三、简单消费的发送</a><br/>
2. rabbitBaseAnnotation.java中声明了topic类型的交换机、持久化队列、及其绑定关系用于测试说明topic交换机路由键的绑定规则。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#31-声明交换机队列绑定关系和消费者监听器">3.1 声明交换机、队列、绑定关系和消费者监听器</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#32-测试简单消息的发送">3.2 测试简单消息的发送</a><br/>
3. rabbitObjectAnnotation.java中声明了direct类型的交换机持久化队列及其绑定关系用于示例对象消息的传输。 <a href="#四传输对象">四、传输对象</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#41-创建消息的委托处理器">4.1 创建消息的委托处理器</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="#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.png"/> </div>
## 一、说明
### 1.1 项目结构说明
### 1.2 依赖说明
1. 本用例关于rabbitmq的整合提供**简单消息发送**和**对象消费发送**两种情况下的sample。
除了spring的基本依赖外需要导入spring rabbitmq 整合依赖
2. rabbitBaseAnnotation.java中声明了topic类型的交换机、持久化队列、及其绑定关系用于测试说明topic交换机路由键的绑定规则。
```xml
<!--spring rabbitmq 整合依赖--> 3. rabbitObjectAnnotation.java中声明了direct类型的交换机持久化队列及其绑定关系用于示例对象消息的传输。
<dependency>
<groupId>org.springframework.amqp</groupId> 关于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),里面有详细的配图说明。
<artifactId>spring-rabbit</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<!--rabbitmq 传输对象序列化依赖了这个包--> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-rabbitmq.png"/> </div>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version> ### 1.2 依赖说明
</dependency>
``` 除了spring的基本依赖外需要导入spring rabbitmq 整合依赖
```xml
<!--spring rabbitmq 整合依赖-->
## 二、spring rabbit 基本配置 <dependency>
<groupId>org.springframework.amqp</groupId>
```properties <artifactId>spring-rabbit</artifactId>
rabbitmq.addresses=localhost:5672 <version>2.1.2.RELEASE</version>
rabbitmq.username=guest </dependency>
rabbitmq.password=guest <!--rabbitmq 传输对象序列化依赖了这个包-->
# 虚拟主机,可以类比为命名空间 默认为/ 必须先用图形界面或者管控台添加 程序不会自动创建且会抛出异常 <dependency>
rabbitmq.virtualhost=/ <groupId>com.fasterxml.jackson.core</groupId>
``` <artifactId>jackson-databind</artifactId>
<version>2.9.8</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"
xmlns:rabbit="http://www.springframework.org/schema/rabbit" ## 二、spring rabbit 基本配置
xsi:schemaLocation=
"http://www.springframework.org/schema/context ```properties
http://www.springframework.org/schema/context/spring-context.xsd rabbitmq.addresses=localhost:5672
http://www.springframework.org/schema/beans rabbitmq.username=guest
http://www.springframework.org/schema/beans/spring-beans.xsd rabbitmq.password=guest
http://www.springframework.org/schema/rabbit # 虚拟主机,可以类比为命名空间 默认为/ 必须先用图形界面或者管控台添加 程序不会自动创建且会抛出异常
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd"> rabbitmq.virtualhost=/
```
<context:property-placeholder location="rabbitmq.properties"/>
```xml
<!--声明连接工厂--> <?xml version="1.0" encoding="UTF-8"?>
<rabbit:connection-factory id="connectionFactory" <beans xmlns="http://www.springframework.org/schema/beans"
addresses="${rabbitmq.addresses}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
username="${rabbitmq.username}" xmlns:context="http://www.springframework.org/schema/context"
password="${rabbitmq.password}" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
virtual-host="${rabbitmq.virtualhost}"/> xsi:schemaLocation=
"http://www.springframework.org/schema/context
<!--创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。 http://www.springframework.org/schema/context/spring-context.xsd
auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为true。--> http://www.springframework.org/schema/beans
<rabbit:admin connection-factory="connectionFactory" auto-startup="true"/> http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/rabbit
<!--声明 template 的时候需要声明id 不然会抛出异常--> http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
<context:property-placeholder location="rabbitmq.properties"/>
<!--可以在xml采用如下方式声明交换机、队列、绑定管理 但是建议使用代码方式声明 方法更加灵活且可以采用链调用--> <!--声明连接工厂-->
<rabbit:queue name="remoting.queue"/> <rabbit:connection-factory id="connectionFactory"
addresses="${rabbitmq.addresses}"
<rabbit:direct-exchange name="remoting.exchange"> username="${rabbitmq.username}"
<rabbit:bindings> password="${rabbitmq.password}"
<rabbit:binding queue="remoting.queue" key="remoting.binding"/> virtual-host="${rabbitmq.virtualhost}"/>
</rabbit:bindings>
</rabbit:direct-exchange> <!--创建一个管理器org.springframework.amqp.rabbit.core.RabbitAdmin用于管理交换队列和绑定。
auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为true。-->
<!--扫描rabbit包 自动声明交换器、队列、绑定关系--> <rabbit:admin connection-factory="connectionFactory" auto-startup="true"/>
<context:component-scan base-package="com.heibaiying.rabbit"/>
<!--声明 template 的时候需要声明id 不然会抛出异常-->
</beans> <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
```
<!--可以在xml采用如下方式声明交换机、队列、绑定管理 但是建议使用代码方式声明 方法更加灵活且可以采用链调用-->
<rabbit:queue name="remoting.queue"/>
## 三、简单消费的发送
<rabbit:direct-exchange name="remoting.exchange">
#### 3.1 声明交换机、队列、绑定关系和消费者监听器 <rabbit:bindings>
<rabbit:binding queue="remoting.queue" key="remoting.binding"/>
```java </rabbit:bindings>
import com.rabbitmq.client.Channel; </rabbit:direct-exchange>
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory; <!--扫描rabbit包 自动声明交换器、队列、绑定关系-->
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; <context:component-scan base-package="com.heibaiying.rabbit"/>
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.context.annotation.Bean; </beans>
import org.springframework.context.annotation.Configuration; ```
/**
* @author : heibaiying
* @description : 声明队列、交换机、绑定关系、和队列消息监听 ## 三、简单消费的发送
*/
#### 3.1 声明交换机、队列、绑定关系和消费者监听器
@Configuration
public class RabbitBaseAnnotation { ```java
import com.rabbitmq.client.Channel;
@Bean import org.springframework.amqp.core.*;
public TopicExchange exchange() { import org.springframework.amqp.rabbit.connection.ConnectionFactory;
// 创建一个持久化的交换机 import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
return new TopicExchange("topic01", true, false); import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
} import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Bean
public Queue firstQueue() { /**
// 创建一个持久化的队列1 * @author : heibaiying
return new Queue("FirstQueue", true); * @description : 声明队列、交换机、绑定关系、和队列消息监听
} */
@Bean @Configuration
public Queue secondQueue() { public class RabbitBaseAnnotation {
// 创建一个持久化的队列2
return new Queue("SecondQueue", true); @Bean
} public TopicExchange exchange() {
// 创建一个持久化的交换机
/** return new TopicExchange("topic01", true, false);
* BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词 }
* 这里我们声明三个绑定关系用于测试topic这种类型交换器
*/ @Bean
@Bean public Queue firstQueue() {
public Binding orange() { // 创建一个持久化的队列1
return BindingBuilder.bind(firstQueue()).to(exchange()).with("*.orange.*"); return new Queue("FirstQueue", true);
} }
@Bean @Bean
public Binding rabbit() { public Queue secondQueue() {
return BindingBuilder.bind(secondQueue()).to(exchange()).with("*.*.rabbit"); // 创建一个持久化的队列2
} return new Queue("SecondQueue", true);
}
@Bean
public Binding lazy() { /**
return BindingBuilder.bind(secondQueue()).to(exchange()).with("lazy.#"); * BindingKey 中可以存在两种特殊的字符串“#”和“*”,其中“*”用于匹配一个单词,“#”用于匹配零个或者多个单词
} * 这里我们声明三个绑定关系用于测试topic这种类型交换器
*/
@Bean
/*创建队列1消费者监听*/ public Binding orange() {
@Bean return BindingBuilder.bind(firstQueue()).to(exchange()).with("*.orange.*");
public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) { }
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); @Bean
// 设置监听的队列 public Binding rabbit() {
container.setQueues(firstQueue()); return BindingBuilder.bind(secondQueue()).to(exchange()).with("*.*.rabbit");
// 指定要创建的并发使用者数。 }
container.setConcurrentConsumers(1);
// 设置消费者数量的上限 @Bean
container.setMaxConcurrentConsumers(5); public Binding lazy() {
// 设置是否自动签收消费 为保证消费被成功消费,建议手工签收 return BindingBuilder.bind(secondQueue()).to(exchange()).with("lazy.#");
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); }
container.setMessageListener(new ChannelAwareMessageListener() {
@Override
public void onMessage(Message message, Channel channel) throws Exception { /*创建队列1消费者监听*/
// 可以在这个地方得到消息额外属性 @Bean
MessageProperties properties = message.getMessageProperties(); public SimpleMessageListenerContainer firstQueueLister(ConnectionFactory connectionFactory) {
//得到消息体内容
byte[] body = message.getBody(); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
System.out.println(firstQueue().getName() + "收到消息:" + new String(body)); // 设置监听的队列
/* container.setQueues(firstQueue());
* DeliveryTag 是一个单调递增的整数 // 指定要创建的并发使用者数。
* 第二个参数 代表是否一次签收多条如果设置为true,则所有DeliveryTag小于该DeliveryTag的消息都会被签收 container.setConcurrentConsumers(1);
*/ // 设置消费者数量的上限
channel.basicAck(properties.getDeliveryTag(), false); container.setMaxConcurrentConsumers(5);
} // 设置是否自动签收消费 为保证消费被成功消费,建议手工签收
}); container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return container; container.setMessageListener(new ChannelAwareMessageListener() {
} @Override
public void onMessage(Message message, Channel channel) throws Exception {
// 可以在这个地方得到消息额外属性
/*创建队列2消费者监听*/ MessageProperties properties = message.getMessageProperties();
@Bean //得到消息体内容
public SimpleMessageListenerContainer secondQueueLister(ConnectionFactory connectionFactory) { byte[] body = message.getBody();
System.out.println(firstQueue().getName() + "收到消息:" + new String(body));
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); /*
container.setQueues(secondQueue()); * DeliveryTag 是一个单调递增的整数
container.setMessageListener(new ChannelAwareMessageListener() { * 第二个参数 代表是否一次签收多条如果设置为true,则所有DeliveryTag小于该DeliveryTag的消息都会被签收
@Override */
public void onMessage(Message message, Channel channel) throws Exception { channel.basicAck(properties.getDeliveryTag(), false);
byte[] body = message.getBody(); }
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);
container.setQueues(secondQueue());
```java container.setMessageListener(new ChannelAwareMessageListener() {
/** @Override
* @author : heibaiying public void onMessage(Message message, Channel channel) throws Exception {
* @description : 传输简单字符串 byte[] body = message.getBody();
*/ System.out.println(secondQueue().getName() + "收到消息:" + new String(body));
}
@RunWith(SpringRunner.class) });
@ContextConfiguration(locations = "classpath:rabbitmq.xml") return container;
public class RabbitTest { }
@Autowired }
private RabbitTemplate rabbitTemplate; ```
@Test #### 3.2 测试简单消息的发送
public void sendMessage() {
MessageProperties properties = new MessageProperties(); ```java
/**
String allReceived = "我的路由键 quick.orange.rabbit 符合queue1 和 queue2 的要求,我应该被两个监听器接收到"; * @author : heibaiying
Message message1 = new Message(allReceived.getBytes(), properties); * @description : 传输简单字符串
rabbitTemplate.send("topic01", "quick.orange.rabbit", message1); */
String firstReceived = "我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到"; @RunWith(SpringRunner.class)
Message message2 = new Message(firstReceived.getBytes(), properties); @ContextConfiguration(locations = "classpath:rabbitmq.xml")
rabbitTemplate.send("topic01", "quick.orange.fox", message2); public class RabbitTest {
String secondReceived = "我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到"; @Autowired
Message message3 = new Message(secondReceived.getBytes(), properties); private RabbitTemplate rabbitTemplate;
rabbitTemplate.send("topic01", "lazy.brown.fox", message3);
@Test
String notReceived = "我的路由键 quick.brown.fox 不符合 topic1 任何绑定队列的要求,你将看不到我"; public void sendMessage() {
Message message4 = new Message(notReceived.getBytes(), properties); MessageProperties properties = new MessageProperties();
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);
```java String firstReceived = "我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到";
结果: Message message2 = new Message(firstReceived.getBytes(), properties);
SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到 rabbitTemplate.send("topic01", "quick.orange.fox", message2);
FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到
FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到 String secondReceived = "我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到";
SecondQueue收到消息:我的路由键 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 结果:
/** SecondQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到
* @author : heibaiying FirstQueue收到消息:我的路由键 quick.orange.rabbit 符合queue1 queue2 的要求我应该被两个监听器接收到
* @description :消息委派处理类 FirstQueue收到消息:我的路由键 quick.orange.fox 只符合queue1 的要求只能被queue 1 接收到
*/ SecondQueue收到消息:我的路由键 lazy.brown.fox 只符合queue2 的要求只能被queue 2 接收到
public class MessageDelegate { ```
public void handleMessage(ProductManager manager) {
System.out.println("收到一个产品经理" + manager);
} ## 四、传输对象
public void handleMessage(Programmer programmer) { #### 4.1 创建消息的委托处理器
System.out.println("收到一个程序员" + programmer);
} 这里为了增强用例的实用性我们创建的处理器的handleMessage方法是一个重载方法对于同一个队列的监听不仅可以传输对象消息同时针对不同的对象类型调用不同的处理方法。
} ```java
``` /**
* @author : heibaiying
#### 4.2 声明交换机、队列、绑定关系和消费者监听器 * @description :消息委派处理类
*/
```java public class MessageDelegate {
/**
* @author : heibaiying public void handleMessage(ProductManager manager) {
* @description : 声明队列、交换机、绑定关系、用于测试对象的消息传递 System.out.println("收到一个产品经理" + manager);
*/ }
@Configuration public void handleMessage(Programmer programmer) {
public class RabbitObjectAnnotation { System.out.println("收到一个程序员" + programmer);
}
@Bean
public DirectExchange objectTopic() { }
// 创建一个持久化的交换机 ```
return new DirectExchange("objectTopic", true, false);
} #### 4.2 声明交换机、队列、绑定关系和消费者监听器
@Bean ```java
public Queue objectQueue() { /**
// 创建一个持久化的队列 * @author : heibaiying
return new Queue("objectQueue", true); * @description : 声明队列、交换机、绑定关系、用于测试对象的消息传递
} */
@Bean @Configuration
public Binding binding() { public class RabbitObjectAnnotation {
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
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); public Queue objectQueue() {
// 设置监听的队列 // 创建一个持久化的队列
container.setQueues(objectQueue()); return new Queue("objectQueue", true);
// 将监听到的消息委派给实际的处理类 }
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
// 指定由哪个方法来处理消息 默认就是handleMessage @Bean
adapter.setDefaultListenerMethod("handleMessage"); 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
// 针对不同的消息体调用不同的重载方法 public SimpleMessageListenerContainer objectQueueLister(ConnectionFactory connectionFactory) {
idClassMapping.put(Type.MANAGER, com.heibaiying.bean.ProductManager.class);
idClassMapping.put(Type.PROGRAMMER, com.heibaiying.bean.Programmer.class); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
// 设置监听的队列
javaTypeMapper.setIdClassMapping(idClassMapping); container.setQueues(objectQueue());
// 将监听到的消息委派给实际的处理类
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper); MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setMessageConverter(jackson2JsonMessageConverter); // 指定由哪个方法来处理消息 默认就是handleMessage
container.setMessageListener(adapter); adapter.setDefaultListenerMethod("handleMessage");
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);
```java idClassMapping.put(Type.PROGRAMMER, com.heibaiying.bean.Programmer.class);
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:rabbitmq.xml") javaTypeMapper.setIdClassMapping(idClassMapping);
public class RabbitSendObjectTest {
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
@Autowired adapter.setMessageConverter(jackson2JsonMessageConverter);
private RabbitTemplate rabbitTemplate; container.setMessageListener(adapter);
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);
Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date()); ```java
// 序列化与反序列化都使用的Jackson @RunWith(SpringRunner.class)
ObjectMapper mapper = new ObjectMapper(); @ContextConfiguration(locations = "classpath:rabbitmq.xml")
String programmerJson = mapper.writeValueAsString(programmer); public class RabbitSendObjectTest {
Message message = new Message(programmerJson.getBytes(), messageProperties);
rabbitTemplate.send("objectTopic", "object", message); @Autowired
} private RabbitTemplate rabbitTemplate;
@Test
@Test public void sendProgrammer() throws JsonProcessingException {
public void sendProductManager() throws JsonProcessingException { MessageProperties messageProperties = new MessageProperties();
MessageProperties messageProperties = new MessageProperties(); //必须设置 contentType为 application/json
messageProperties.setContentType("application/json"); messageProperties.setContentType("application/json");
messageProperties.getHeaders().put("__TypeId__", Type.MANAGER); // 必须指定类型
ProductManager manager = new ProductManager("xiaohong", 21, new Date()); messageProperties.getHeaders().put("__TypeId__", Type.PROGRAMMER);
ObjectMapper mapper = new ObjectMapper(); Programmer programmer = new Programmer("xiaoming", 34, 52200.21f, new Date());
String managerJson = mapper.writeValueAsString(manager); // 序列化与反序列化都使用的Jackson
Message message = new Message(managerJson.getBytes(), messageProperties); ObjectMapper mapper = new ObjectMapper();
rabbitTemplate.send("objectTopic", "object", message); 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);
}
} }

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/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
关于任务的调度配置定义在ServletConfig.java中为方便观察项目定时执行的情况项目以web的方式构建。 <a href="#二spring-scheduling">二、spring scheduling</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建定时任务">2.1 创建定时任务</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling-annotation.png"/> </div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-配置定时任务">2.2 配置定时任务</a><br/>
## 正文<br/> ## 正文<br/>
### 1.2 依赖说明 ## 一、说明
导入基本依赖 ### 1.1 项目结构说明
```xml 关于任务的调度配置定义在ServletConfig.java中为方便观察项目定时执行的情况项目以web的方式构建。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling-annotation.png"/> </div>
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>
<build> ```xml
<plugins> <?xml version="1.0" encoding="UTF-8"?>
<plugin> <project xmlns="http://maven.apache.org/POM/4.0.0"
<groupId>org.apache.maven.plugins</groupId> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<artifactId>maven-compiler-plugin</artifactId> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<configuration> <modelVersion>4.0.0</modelVersion>
<source>8</source>
<target>8</target> <groupId>com.heibaiying</groupId>
</configuration> <artifactId>spring-scheduling</artifactId>
</plugin> <version>1.0-SNAPSHOT</version>
</plugins> <build>
</build> <plugins>
<properties> <plugin>
<spring-base-version>5.1.3.RELEASE</spring-base-version> <groupId>org.apache.maven.plugins</groupId>
</properties> <artifactId>maven-compiler-plugin</artifactId>
<configuration>
<dependencies> <source>8</source>
<dependency> <target>8</target>
<groupId>org.springframework</groupId> </configuration>
<artifactId>spring-context</artifactId> </plugin>
<version>${spring-base-version}</version> </plugins>
</dependency> </build>
<dependency> <properties>
<groupId>org.springframework</groupId> <spring-base-version>5.1.3.RELEASE</spring-base-version>
<artifactId>spring-beans</artifactId> </properties>
<version>${spring-base-version}</version>
</dependency> <dependencies>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-context</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-web</artifactId> <artifactId>spring-beans</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-webmvc</artifactId> <artifactId>spring-core</artifactId>
<version>${spring-base-version}</version> <version>${spring-base-version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>org.springframework</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>spring-web</artifactId>
<version>4.0.1</version> <version>${spring-base-version}</version>
<scope>provided</scope> </dependency>
</dependency> <dependency>
</dependencies> <groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</project> <version>${spring-base-version}</version>
``` </dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
## 二、spring scheduling <version>4.0.1</version>
<scope>provided</scope>
#### 2.1 创建定时任务 </dependency>
</dependencies>
```java
/** </project>
* @author : heibaiying ```
*/
@Component
public class Task {
## 二、spring scheduling
/**
* 基于间隔的触发器,其中间隔是从上一个任务的 完成时间 开始计算, 时间单位值以毫秒为单位。 #### 2.1 创建定时任务
*/
@Scheduled(fixedDelay = 5000, initialDelay = 1000) ```java
public void methodA() { /**
Thread thread = Thread.currentThread(); * @author : heibaiying
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", */
thread.getName(), thread.getId(), "methodA方法执行", LocalDateTime.now())); @Component
} public class Task {
/** /**
* 基于间隔的触发器,其中间隔是从上一个任务的 开始时间 开始测量的 * 基于间隔的触发器,其中间隔是从上一个任务的 完成时间 开始计算, 时间单位值以毫秒为单位
*/ */
@Scheduled(fixedRate = 5000) @Scheduled(fixedDelay = 5000, initialDelay = 1000)
@Async public void methodA() {
public void methodB() throws InterruptedException { Thread thread = Thread.currentThread();
Thread thread = Thread.currentThread(); System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", thread.getName(), thread.getId(), "methodA方法执行", LocalDateTime.now()));
thread.getName(), thread.getId(), "methodB方法执行", LocalDateTime.now())); }
Thread.sleep(10 * 1000);
} /**
* 基于间隔的触发器,其中间隔是从上一个任务的 开始时间 开始测量的。
@Scheduled(cron = "0/10 * * * * ?") */
public void methodC() { @Scheduled(fixedRate = 5000)
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 配置定时任务 @Scheduled(cron = "0/10 * * * * ?")
public void methodC() {
```java Thread thread = Thread.currentThread();
/** System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
* @author : heibaiying thread.getName(), thread.getId(), "methodC方法执行", LocalDateTime.now()));
* spring 主配置类 }
*/ }
@Configuration
@EnableWebMvc ```
@EnableScheduling //启用Spring的计划任务执行功能
@EnableAsync //启用Spring的异步方法执行功能 #### 2.2 配置定时任务
@ComponentScan(basePackages = {"com.heibaiying.task"})
public class ServletConfig implements WebMvcConfigurer, AsyncConfigurer, SchedulingConfigurer { ```java
/**
private ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); * @author : heibaiying
* spring 主配置类
// 任务执行器线程池配置 */
@Override @Configuration
public Executor getAsyncExecutor() { @EnableWebMvc
executor.setCorePoolSize(5); @EnableScheduling //启用Spring的计划任务执行功能
executor.setMaxPoolSize(10); @EnableAsync //启用Spring的异步方法执行功能
executor.setQueueCapacity(100); @ComponentScan(basePackages = {"com.heibaiying.task"})
executor.setThreadNamePrefix("MyExecutor-"); public class ServletConfig implements WebMvcConfigurer, AsyncConfigurer, SchedulingConfigurer {
executor.initialize();
return executor; private ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
}
// 任务执行器线程池配置
// 这个方法可以监听到异步程序发生的错误 @Override
@Override public Executor getAsyncExecutor() {
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { executor.setCorePoolSize(5);
return new AsyncUncaughtExceptionHandler() { executor.setMaxPoolSize(10);
@Override executor.setQueueCapacity(100);
public void handleUncaughtException(Throwable ex, Method method, Object... params) { executor.setThreadNamePrefix("MyExecutor-");
System.out.println(method.getName() + "发生错误:" + ex.getMessage()); executor.initialize();
} return executor;
}; }
}
// 这个方法可以监听到异步程序发生的错误
// 如果程序结束,需要关闭线程池 不然程序无法完全退出 只能kill才能完全退出 @Override
@PreDestroy public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
public void destroy() { return new AsyncUncaughtExceptionHandler() {
if (executor != null) { @Override
executor.shutdown(); public void handleUncaughtException(Throwable ex, Method method, Object... params) {
} System.out.println(method.getName() + "发生错误:" + ex.getMessage());
} }
};
// 调度程序线程池配置 }
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { // 如果程序结束,需要关闭线程池 不然程序无法完全退出 只能kill才能完全退出
taskRegistrar.setScheduler(taskExecutor()); @PreDestroy
} 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/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
关于任务的调度配置定义在springApplication.xml中为方便观察项目定时执行的情况项目以web的方式构建。 <a href="#二spring-scheduling">二、spring scheduling</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建定时任务">2.1 创建定时任务</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling.png"/> </div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-配置定时任务">2.2 配置定时任务</a><br/>
## 正文<br/> ## 正文<br/>
### 1.2 依赖说明 ## 一、说明
导入基本依赖 ### 1.1 项目结构说明
```xml 关于任务的调度配置定义在springApplication.xml中为方便观察项目定时执行的情况项目以web的方式构建。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-scheduling.png"/> </div>
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>
<build> ```xml
<plugins> <?xml version="1.0" encoding="UTF-8"?>
<plugin> <project xmlns="http://maven.apache.org/POM/4.0.0"
<groupId>org.apache.maven.plugins</groupId> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<artifactId>maven-compiler-plugin</artifactId> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<configuration> <modelVersion>4.0.0</modelVersion>
<source>8</source>
<target>8</target> <groupId>com.heibaiying</groupId>
</configuration> <artifactId>spring-scheduling</artifactId>
</plugin> <version>1.0-SNAPSHOT</version>
</plugins> <build>
</build> <plugins>
<properties> <plugin>
<spring-base-version>5.1.3.RELEASE</spring-base-version> <groupId>org.apache.maven.plugins</groupId>
</properties> <artifactId>maven-compiler-plugin</artifactId>
<configuration>
<dependencies> <source>8</source>
<dependency> <target>8</target>
<groupId>org.springframework</groupId> </configuration>
<artifactId>spring-context</artifactId> </plugin>
<version>${spring-base-version}</version> </plugins>
</dependency> </build>
<dependency> <properties>
<groupId>org.springframework</groupId> <spring-base-version>5.1.3.RELEASE</spring-base-version>
<artifactId>spring-beans</artifactId> </properties>
<version>${spring-base-version}</version>
</dependency> <dependencies>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-context</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-web</artifactId> <artifactId>spring-beans</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-webmvc</artifactId> <artifactId>spring-core</artifactId>
<version>${spring-base-version}</version> <version>${spring-base-version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>org.springframework</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>spring-web</artifactId>
<version>4.0.1</version> <version>${spring-base-version}</version>
<scope>provided</scope> </dependency>
</dependency> <dependency>
</dependencies> <groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</project> <version>${spring-base-version}</version>
``` </dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
## 二、spring scheduling <version>4.0.1</version>
<scope>provided</scope>
#### 2.1 创建定时任务 </dependency>
</dependencies>
```java
package com.heibaiying.task; </project>
```
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import java.time.LocalDateTime; ## 二、spring scheduling
/** #### 2.1 创建定时任务
* @author : heibaiying
*/ ```java
public class Task { package com.heibaiying.task;
public void methodA() { import org.springframework.scheduling.annotation.Async;
Thread thread = Thread.currentThread(); import org.springframework.scheduling.annotation.Scheduled;
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
thread.getName(), thread.getId(), "methodA方法执行", LocalDateTime.now())); import java.time.LocalDateTime;
}
/**
@Async * @author : heibaiying
public void methodB() throws InterruptedException { */
Thread thread = Thread.currentThread(); public class Task {
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
thread.getName(), thread.getId(), "methodB方法执行", LocalDateTime.now())); public void methodA() {
Thread.sleep(10 * 1000); Thread thread = Thread.currentThread();
} System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
thread.getName(), thread.getId(), "methodA方法执行", LocalDateTime.now()));
public void methodC() { }
Thread thread = Thread.currentThread();
System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s", @Async
thread.getName(), thread.getId(), "methodC方法执行", LocalDateTime.now())); public void methodB() throws InterruptedException {
} 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() {
```xml Thread thread = Thread.currentThread();
<?xml version="1.0" encoding="UTF-8"?> System.out.println(String.format("线程名称:%s ; 线程ID%s ; 调用方法:%s ; 调用时间:%s",
<beans xmlns="http://www.springframework.org/schema/beans" thread.getName(), thread.getId(), "methodC方法执行", LocalDateTime.now()));
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
http://www.springframework.org/schema/context #### 2.2 配置定时任务
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc ```xml
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd <?xml version="1.0" encoding="UTF-8"?>
http://www.springframework.org/schema/task <beans xmlns="http://www.springframework.org/schema/beans"
http://www.springframework.org/schema/task/spring-task.xsd"> 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"
<context:component-scan base-package="com.heibaiying.*"/> xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
<!-- 开启注解驱动 --> http://www.springframework.org/schema/context
<mvc:annotation-driven/> http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc
<!--配置定时任务--> http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
<bean id="task" class="com.heibaiying.task.Task"/> http://www.springframework.org/schema/task
<task:scheduled-tasks scheduler="myScheduler"> http://www.springframework.org/schema/task/spring-task.xsd">
<!--基于间隔的触发器,其中间隔是从上一个任务的 完成时间 开始计算, 时间单位值以毫秒为单位。-->
<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 表达式--> <!-- 开启注解驱动 -->
<task:scheduled ref="task" method="methodC" cron="0/10 * * * * ?"/> <mvc:annotation-driven/>
</task:scheduled-tasks>
<!--配置定时任务-->
<!--定义任务调度器线程池的大小--> <bean id="task" class="com.heibaiying.task.Task"/>
<task:scheduler id="myScheduler" pool-size="10"/> <task:scheduled-tasks scheduler="myScheduler">
<!--基于间隔的触发器,其中间隔是从上一个任务的 完成时间 开始计算, 时间单位值以毫秒为单位。-->
<!--定义任务执行器的线程池大小、等待队列的容量、和拒绝策略--> <task:scheduled ref="task" method="methodA" fixed-delay="5000" initial-delay="1000"/>
<task:executor <!--基于间隔的触发器,其中间隔是从上一个任务的 开始时间 开始测量的。-->
id="executorWithPoolSizeRange" <task:scheduled ref="task" method="methodB" fixed-rate="5000"/>
pool-size="5-25" <!-- cron 表达式-->
queue-capacity="100" <task:scheduled ref="task" method="methodC" cron="0/10 * * * * ?"/>
rejection-policy="CALLER_RUNS"/> </task:scheduled-tasks>
<!--拒绝策略默认值为ABORT
CALLER_RUNS来限制入栈任务 <!--定义任务调度器线程池的大小-->
DISCARD删除当前任务 <task:scheduler id="myScheduler" pool-size="10"/>
DISCARD_OLDEST将任务放在队列的头部。-->
<!--定义任务执行器的线程池大小、等待队列的容量、和拒绝策略-->
<!--允许在任何Spring管理的对象上检测@Async和@Scheduled注释, 如果存在,将生成用于异步执行带注释的方法的代理。--> <task:executor
<task:annotation-driven/> id="executorWithPoolSizeRange"
pool-size="5-25"
</beans> queue-capacity="100"
``` rejection-policy="CALLER_RUNS"/>
<!--拒绝策略默认值为ABORT
**关于调度程序线程池作用说明** CALLER_RUNS来限制入栈任务
DISCARD删除当前任务
按照例子 我们有methodA 、 methodB 、methodC 三个方法 其中 methodB 是耗时的方法如果不声明调度程序线程池 则methodB 会阻塞 methodA 、methodC 方法的执行 因为调度程序是单线程的 DISCARD_OLDEST将任务放在队列的头部。-->
**关于任务执行线程池作用说明** <!--允许在任何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/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中 <a href="#二spring-websocket">二、spring websocket</a><br/>
2. webconfig 包是基础注解的方式配置web在spring-base-annotation项目中已经讲解过每个类作用 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建消息处理类继承自TextWebSocketHandler">2.1 创建消息处理类继承自TextWebSocketHandler</a><br/>
3. CustomHander为消息的自定义处理器 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-创建websocket-握手拦截器如果没有权限拦截等需求这一步不是必须的">2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的)</a><br/>
4. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-创建websocket的配置类">2.3 创建websocket的配置类</a><br/>
5. webSocketConfig 是websocket 的主要配置类; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-前端-websocket-的实现">2.4 前端 websocket 的实现</a><br/>
6. 项目以web的方式构建。 &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-annotation.png"/> </div>
## 正文<br/> ## 正文<br/>
### 1.2 依赖说明 ## 一、说明
除了基本的spring 依赖外还需要导入webSocket的依赖包 ### 1.1 项目结构说明
```xml 1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中
<!--spring webSocket 的依赖包 --> 2. webconfig 包是基础注解的方式配置web在spring-base-annotation项目中已经讲解过每个类作用
<dependency> 3. CustomHander为消息的自定义处理器
<groupId>org.springframework</groupId> 4. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器;
<artifactId>spring-websocket</artifactId> 5. webSocketConfig 是websocket 的主要配置类;
<version>5.1.3.RELEASE</version> 6. 项目以web的方式构建。
</dependency>
``` <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket-annotation.png"/> </div>
## 二、spring websocket ### 1.2 依赖说明
#### 2.1 创建消息处理类继承自TextWebSocketHandler 除了基本的spring 依赖外还需要导入webSocket的依赖包
```java ```xml
/** <!--spring webSocket 的依赖包 -->
* @author : heibaiying <dependency>
* @description : 自定义消息处理类 <groupId>org.springframework</groupId>
*/ <artifactId>spring-websocket</artifactId>
public class CustomHandler extends TextWebSocketHandler { <version>5.1.3.RELEASE</version>
</dependency>
private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>(); ```
// 建立连接时候触发
@Override
public void afterConnectionEstablished(WebSocketSession session) { ## 二、spring websocket
String username = getNameFromSession(session);
nameAndSession.putIfAbsent(username, session); #### 2.1 创建消息处理类继承自TextWebSocketHandler
}
```java
/**
// 关闭连接时候触发 * @author : heibaiying
@Override * @description : 自定义消息处理类
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { */
String username = getNameFromSession(session); public class CustomHandler extends TextWebSocketHandler {
nameAndSession.remove(username);
} private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>();
// 处理消息 // 建立连接时候触发
@Override @Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { public void afterConnectionEstablished(WebSocketSession session) {
// 防止中文乱码 String username = getNameFromSession(session);
String msg = URLDecoder.decode(message.getPayload(), "utf-8"); nameAndSession.putIfAbsent(username, session);
String username = getNameFromSession(session); }
// 简单模拟群发消息
TextMessage reply = new TextMessage(username + " : " + msg);
nameAndSession.forEach((s, webSocketSession) // 关闭连接时候触发
-> { @Override
try { public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
webSocketSession.sendMessage(reply); String username = getNameFromSession(session);
} catch (IOException e) { nameAndSession.remove(username);
e.printStackTrace(); }
}
}); // 处理消息
} @Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 防止中文乱码
private String getNameFromSession(WebSocketSession session) { String msg = URLDecoder.decode(message.getPayload(), "utf-8");
Map<String, Object> attributes = session.getAttributes(); String username = getNameFromSession(session);
return (String) attributes.get(Constant.USER_NAME); // 简单模拟群发消息
} TextMessage reply = new TextMessage(username + " : " + msg);
} nameAndSession.forEach((s, webSocketSession)
-> {
``` try {
webSocketSession.sendMessage(reply);
#### 2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的) } catch (IOException e) {
e.printStackTrace();
```java }
/** });
* @author : heibaiying }
* @description : 可以按照需求实现权限拦截等功能
*/
public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor { private String getNameFromSession(WebSocketSession session) {
Map<String, Object> attributes = session.getAttributes();
@Override return (String) attributes.get(Constant.USER_NAME);
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); ```
/*
* 最后需要要显示调用父类方法父类的beforeHandshake方法 #### 2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的)
* 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。
* 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值 ```java
* 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空 /**
*/ * @author : heibaiying
return super.beforeHandshake(request, response, wsHandler, attributes); * @description : 可以按照需求实现权限拦截等功能
} */
} public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
```
@Override
#### 2.3 创建websocket的配置类 public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
InetSocketAddress remoteAddress = request.getRemoteAddress();
```java InetAddress address = remoteAddress.getAddress();
/** System.out.println(address);
* @author : heibaiying /*
* @description :websocket 配置 * 最后需要要显示调用父类方法父类的beforeHandshake方法
*/ * 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。
@Configuration * 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值
@EnableWebSocket * 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空
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
```jsp * @description :websocket 配置
<%@ page contentType="text/html;charset=UTF-8" language="java" %> */
<html> @Configuration
<head> @EnableWebSocket
<title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title> public class WebSocketConfig implements WebSocketConfigurer {
</head>
<body> @Override
<input id="message" type="text"> public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
<button id="btn">发送消息</button> registry.addHandler(new CustomHandler(), "/socket").addInterceptors(new CustomHandshakeInterceptor());
<div id="show"> }
}
</div> ```
<script>
let btn = document.getElementById("btn"); #### 2.4 前端 websocket 的实现
let message = document.getElementById("message");
let show = document.getElementById("show"); ```jsp
let ws = new WebSocket("ws://localhost:8080/socket"); <%@ page contentType="text/html;charset=UTF-8" language="java" %>
ws.onmessage = function (evt) { <html>
let node = document.createElement("div"); <head>
node.innerHTML = "<h5>" + evt.data + "</h5>"; <title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title>
show.appendChild(node); </head>
}; <body>
btn.addEventListener("click", function () { <input id="message" type="text">
let data = message.value; <button id="btn">发送消息</button>
console.log(data); <div id="show">
if (data) {
ws.send(encodeURI(data)); </div>
} else { <script>
alert("请输入消息后发送"); let btn = document.getElementById("btn");
} let message = document.getElementById("message");
message.value = ""; let show = document.getElementById("show");
}); let ws = new WebSocket("ws://localhost:8080/socket");
// 关闭页面时候关闭ws ws.onmessage = function (evt) {
window.addEventListener("beforeunload", function(event) { let node = document.createElement("div");
ws.close(); node.innerHTML = "<h5>" + evt.data + "</h5>";
}); show.appendChild(node);
</script> };
</body> btn.addEventListener("click", function () {
</html> let data = message.value;
console.log(data);
``` if (data) {
ws.send(encodeURI(data));
#### 2.5 简单登录的实现 } else {
alert("请输入消息后发送");
```java }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> message.value = "";
<html> });
<head> // 关闭页面时候关闭ws
<title>Title</title> window.addEventListener("beforeunload", function(event) {
</head> ws.close();
<body> });
<form action="${pageContext.request.contextPath}/login" method="post"> </script>
<input name="username" type="text"> </body>
<button id="btn">输入临时用户名后登录</button> </html>
</form>
</body> ```
</html>
``` #### 2.5 简单登录的实现
```java ```java
@Controller <%@ page contentType="text/html;charset=UTF-8" language="java" %>
public class LoginController { <html>
<head>
@PostMapping("login") <title>Title</title>
public String login(String username, HttpSession session){ </head>
session.setAttribute(Constant.USER_NAME,username); <body>
return "chat"; <form action="${pageContext.request.contextPath}/login" method="post">
} <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/>
### 1.1 项目结构说明 &nbsp;&nbsp;&nbsp;&nbsp;<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#12-依赖说明">1.2 依赖说明</a><br/>
1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中 <a href="#二spring-websocket">二、spring websocket</a><br/>
2. CustomHander为消息的自定义处理器; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#21-创建消息处理类继承自TextWebSocketHandler">2.1 创建消息处理类继承自TextWebSocketHandler</a><br/>
3. CustomHandershakerInterceptor为自定义的 websocket 握手拦截器 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#22-创建websocket-握手拦截器如果没有权限拦截等需求这一步不是必须的">2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的)</a><br/>
4. 项目以web的方式构建。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#23-配置websocket">2.3 配置websocket</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#24-前端-websocket-的实现">2.4 前端 websocket 的实现</a><br/>
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket.png"/> </div> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#25-简单登录的实现">2.5 简单登录的实现</a><br/>
## 正文<br/> ## 正文<br/>
### 1.2 依赖说明 ## 一、说明
除了基本的spring 依赖外还需要导入webSocket的依赖包 ### 1.1 项目结构说明
```xml 1. 项目模拟一个简单的群聊功能为区分不同的聊天客户端登录时候将临时用户名存储在session当中
<!--spring webSocket 的依赖包 --> 2. CustomHander为消息的自定义处理器
<dependency> 3. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器;
<groupId>org.springframework</groupId> 4. 项目以web的方式构建。
<artifactId>spring-websocket</artifactId>
<version>5.1.3.RELEASE</version> <div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-websocket.png"/> </div>
</dependency>
```
### 1.2 依赖说明
## 二、spring websocket 除了基本的spring 依赖外,还需要导入webSocket的依赖包
#### 2.1 创建消息处理类继承自TextWebSocketHandler ```xml
<!--spring webSocket 的依赖包 -->
```java <dependency>
/** <groupId>org.springframework</groupId>
* @author : heibaiying <artifactId>spring-websocket</artifactId>
* @description : 自定义消息处理类 <version>5.1.3.RELEASE</version>
*/ </dependency>
public class CustomHandler extends TextWebSocketHandler { ```
private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>();
// 建立连接时候触发 ## 二、spring websocket
@Override
public void afterConnectionEstablished(WebSocketSession session) { #### 2.1 创建消息处理类继承自TextWebSocketHandler
String username = getNameFromSession(session);
nameAndSession.putIfAbsent(username, session); ```java
} /**
* @author : heibaiying
* @description : 自定义消息处理类
// 关闭连接时候触发 */
@Override public class CustomHandler extends TextWebSocketHandler {
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
String username = getNameFromSession(session); private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>();
nameAndSession.remove(username);
} // 建立连接时候触发
@Override
// 处理消息 public void afterConnectionEstablished(WebSocketSession session) {
@Override String username = getNameFromSession(session);
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { nameAndSession.putIfAbsent(username, session);
// 防止中文乱码 }
String msg = URLDecoder.decode(message.getPayload(), "utf-8");
String username = getNameFromSession(session);
// 简单模拟群发消息 // 关闭连接时候触发
TextMessage reply = new TextMessage(username + " : " + msg); @Override
nameAndSession.forEach((s, webSocketSession) public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
-> { String username = getNameFromSession(session);
try { nameAndSession.remove(username);
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) { // 简单模拟群发消息
Map<String, Object> attributes = session.getAttributes(); TextMessage reply = new TextMessage(username + " : " + msg);
return (String) attributes.get(Constant.USER_NAME); nameAndSession.forEach((s, webSocketSession)
} -> {
} try {
webSocketSession.sendMessage(reply);
``` } catch (IOException e) {
e.printStackTrace();
#### 2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的) }
});
```java }
/**
* @author : heibaiying
* @description : 可以按照需求实现权限拦截等功能 private String getNameFromSession(WebSocketSession session) {
*/ Map<String, Object> attributes = session.getAttributes();
public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor { 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方法 ```java
* 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。 /**
* 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值 * @author : heibaiying
* 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空 * @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 {
InetSocketAddress remoteAddress = request.getRemoteAddress();
#### 2.3 配置websocket InetAddress address = remoteAddress.getAddress();
System.out.println(address);
```xml /*
<?xml version="1.0" encoding="UTF-8"?> * 最后需要要显示调用父类方法父类的beforeHandshake方法
<beans xmlns="http://www.springframework.org/schema/beans" * 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" * 如果我们没有实现这个方法我们在最后的handler处理中 是拿不到 session中的值
xmlns:context="http://www.springframework.org/schema/context" * 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空
xmlns:mvc="http://www.springframework.org/schema/mvc" */
xmlns:websocket="http://www.springframework.org/schema/websocket" return super.beforeHandshake(request, response, wsHandler, attributes);
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/mvc #### 2.3 配置websocket
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/websocket ```xml
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
<!-- 开启注解包扫描--> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<context:component-scan base-package="com.heibaiying.*"/> xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
<!--使用默认的Servlet来响应静态文件 --> xmlns:websocket="http://www.springframework.org/schema/websocket"
<mvc:default-servlet-handler/> xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
<!-- 开启注解驱动 --> http://www.springframework.org/schema/context
<mvc:annotation-driven/> http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc
<!-- 配置视图解析器 --> http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" http://www.springframework.org/schema/websocket
id="internalResourceViewResolver"> http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/> <!-- 开启注解包扫描-->
<!-- 后缀 --> <context:component-scan base-package="com.heibaiying.*"/>
<property name="suffix" value=".jsp"/>
</bean> <!--使用默认的Servlet来响应静态文件 -->
<mvc:default-servlet-handler/>
<!--配置webSocket-->
<bean id="customHandler" class="com.heibaiying.websocket.CustomHandler"/> <!-- 开启注解驱动 -->
<websocket:handlers> <mvc:annotation-driven/>
<!--指定webSocket 地址-->
<websocket:mapping path="/socket" handler="customHandler"/> <!-- 配置视图解析器 -->
<!--webSocket握手--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
<websocket:handshake-interceptors> id="internalResourceViewResolver">
<bean class="com.heibaiying.websocket.CustomHandshakeInterceptor"/> <!-- 前缀 -->
</websocket:handshake-interceptors> <property name="prefix" value="/WEB-INF/jsp/"/>
</websocket:handlers> <!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</beans> </bean>
```
<!--配置webSocket-->
#### 2.4 前端 websocket 的实现 <bean id="customHandler" class="com.heibaiying.websocket.CustomHandler"/>
<websocket:handlers>
```jsp <!--指定webSocket 地址-->
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <websocket:mapping path="/socket" handler="customHandler"/>
<html> <!--webSocket握手-->
<head> <websocket:handshake-interceptors>
<title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title> <bean class="com.heibaiying.websocket.CustomHandshakeInterceptor"/>
</head> </websocket:handshake-interceptors>
<body> </websocket:handlers>
<input id="message" type="text">
<button id="btn">发送消息</button> </beans>
<div id="show"> ```
</div> #### 2.4 前端 websocket 的实现
<script>
let btn = document.getElementById("btn"); ```jsp
let message = document.getElementById("message"); <%@ page contentType="text/html;charset=UTF-8" language="java" %>
let show = document.getElementById("show"); <html>
let ws = new WebSocket("ws://localhost:8080/socket"); <head>
ws.onmessage = function (evt) { <title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title>
let node = document.createElement("div"); </head>
node.innerHTML = "<h5>" + evt.data + "</h5>"; <body>
show.appendChild(node); <input id="message" type="text">
}; <button id="btn">发送消息</button>
btn.addEventListener("click", function () { <div id="show">
let data = message.value;
console.log(data); </div>
if (data) { <script>
ws.send(encodeURI(data)); let btn = document.getElementById("btn");
} else { let message = document.getElementById("message");
alert("请输入消息后发送"); let show = document.getElementById("show");
} let ws = new WebSocket("ws://localhost:8080/socket");
message.value = ""; ws.onmessage = function (evt) {
}); let node = document.createElement("div");
// 关闭页面时候关闭ws node.innerHTML = "<h5>" + evt.data + "</h5>";
window.addEventListener("beforeunload", function(event) { show.appendChild(node);
ws.close(); };
}); btn.addEventListener("click", function () {
</script> let data = message.value;
</body> console.log(data);
</html> if (data) {
ws.send(encodeURI(data));
``` } else {
alert("请输入消息后发送");
#### 2.5 简单登录的实现 }
message.value = "";
```java });
<%@ page contentType="text/html;charset=UTF-8" language="java" %> // 关闭页面时候关闭ws
<html> window.addEventListener("beforeunload", function(event) {
<head> ws.close();
<title>Title</title> });
</head> </script>
<body> </body>
<form action="${pageContext.request.contextPath}/login" method="post"> </html>
<input name="username" type="text">
<button id="btn">输入临时用户名后登录</button> ```
</form>
</body> #### 2.5 简单登录的实现
</html>
``` ```java
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
```java <html>
@Controller <head>
public class LoginController { <title>Title</title>
</head>
@PostMapping("login") <body>
public String login(String username, HttpSession session){ <form action="${pageContext.request.contextPath}/login" method="post">
session.setAttribute(Constant.USER_NAME,username); <input name="username" type="text">
return "chat"; <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";
}
}
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff