diff --git a/README.md b/README.md index 21a0576..7371704 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ spring-cloud:Finchley.SR2 | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | [spring-boot-base](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-base) | spring-boot 基础 | [spring boot 官方文档](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/)
[spring boot 中文官方文档](https://www.breakyizhan.com/springboot/3028.html) | | [spring-boot-yml-profile](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-yml-profile) | yml 语法和多配置切换 | [Using YAML Instead of Properties](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-external-config-yaml) | -| [spring-boot-servlet](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-servlet) | 整合servlet 3.0 | [Embedded Servlet Container Support](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-embedded-container) | | [spring-boot-tomcat](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-tomcat) | spring-boot 整合外部容器(tomcat) | [Use Another Web Server](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#howto-use-another-web-server) | -| [spring-boot-jsp](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp) | spring-boot 整合 jsp | [JSP Limitations](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-jsp-limitations) | +| [spring-boot-servlet](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-servlet) | 整合servlet 3.0 | [Embedded Servlet Container Support](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-embedded-container) | +| [spring-boot-jsp](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp) | spring-boot 内置容器整合 jsp | [JSP Limitations](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-jsp-limitations) | | [spring-boot-data-jpa](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp) | spring-boot data jpa 的使用 | [Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/) | | [spring-boot-mybatis](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mybatis) | spring-boot 整合 mybatis
boot 2.x 内置HikariDataSources数据源的配置 | [Mybatis-Spring](http://www.mybatis.org/spring/zh/index.html)
[Mybatis-Spring-Boot-Autoconfigure](http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/) | | [spring-boot-druid-mybtais](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-druid-mybatis) | spring-boot 整合druid、mybatis | [Alibaba druid](https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)
[druid-spring-boot-starter](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter) | diff --git a/pictures/boot-dubbo-common.png b/pictures/boot-dubbo-common.png new file mode 100644 index 0000000..6907238 Binary files /dev/null and b/pictures/boot-dubbo-common.png differ diff --git a/pictures/boot-dubbo-consumer1.png b/pictures/boot-dubbo-consumer1.png new file mode 100644 index 0000000..79513a9 Binary files /dev/null and b/pictures/boot-dubbo-consumer1.png differ diff --git a/pictures/boot-dubbo-provider.png b/pictures/boot-dubbo-provider.png new file mode 100644 index 0000000..30d5963 Binary files /dev/null and b/pictures/boot-dubbo-provider.png differ diff --git a/pictures/druid-status.png b/pictures/druid-status.png new file mode 100644 index 0000000..44bd7f9 Binary files /dev/null and b/pictures/druid-status.png differ diff --git a/pictures/rabbitmq-common.png b/pictures/rabbitmq-common.png new file mode 100644 index 0000000..7535c19 Binary files /dev/null and b/pictures/rabbitmq-common.png differ diff --git a/pictures/rabbitmq-consumer.png b/pictures/rabbitmq-consumer.png new file mode 100644 index 0000000..c7e2738 Binary files /dev/null and b/pictures/rabbitmq-consumer.png differ diff --git a/pictures/rabbitmq-producer.png b/pictures/rabbitmq-producer.png new file mode 100644 index 0000000..9d72d2a Binary files /dev/null and b/pictures/rabbitmq-producer.png differ diff --git a/pictures/spring-boot-base.png b/pictures/spring-boot-base.png new file mode 100644 index 0000000..a09ae93 Binary files /dev/null and b/pictures/spring-boot-base.png differ diff --git a/pictures/spring-boot-data-jpa.png b/pictures/spring-boot-data-jpa.png new file mode 100644 index 0000000..8d41d0a Binary files /dev/null and b/pictures/spring-boot-data-jpa.png differ diff --git a/pictures/spring-boot-dependencies.png b/pictures/spring-boot-dependencies.png new file mode 100644 index 0000000..b764548 Binary files /dev/null and b/pictures/spring-boot-dependencies.png differ diff --git a/pictures/spring-boot-druid 控制台.png b/pictures/spring-boot-druid 控制台.png new file mode 100644 index 0000000..ef5d83c Binary files /dev/null and b/pictures/spring-boot-druid 控制台.png differ diff --git a/pictures/spring-boot-druid-mybatis.png b/pictures/spring-boot-druid-mybatis.png new file mode 100644 index 0000000..5846607 Binary files /dev/null and b/pictures/spring-boot-druid-mybatis.png differ diff --git a/pictures/spring-boot-dubbo.png b/pictures/spring-boot-dubbo.png new file mode 100644 index 0000000..9992f1c Binary files /dev/null and b/pictures/spring-boot-dubbo.png differ diff --git a/pictures/spring-boot-jsp.png b/pictures/spring-boot-jsp.png new file mode 100644 index 0000000..93f6a67 Binary files /dev/null and b/pictures/spring-boot-jsp.png differ diff --git a/pictures/spring-boot-memcached.png b/pictures/spring-boot-memcached.png new file mode 100644 index 0000000..9838e54 Binary files /dev/null and b/pictures/spring-boot-memcached.png differ diff --git a/pictures/spring-boot-mongodb.png b/pictures/spring-boot-mongodb.png new file mode 100644 index 0000000..dcb91b5 Binary files /dev/null and b/pictures/spring-boot-mongodb.png differ diff --git a/pictures/spring-boot-mybatis.png b/pictures/spring-boot-mybatis.png new file mode 100644 index 0000000..2c20680 Binary files /dev/null and b/pictures/spring-boot-mybatis.png differ diff --git a/pictures/spring-boot-rabbitmq.png b/pictures/spring-boot-rabbitmq.png new file mode 100644 index 0000000..c5751a0 Binary files /dev/null and b/pictures/spring-boot-rabbitmq.png differ diff --git a/pictures/spring-boot-redis.png b/pictures/spring-boot-redis.png new file mode 100644 index 0000000..89621cb Binary files /dev/null and b/pictures/spring-boot-redis.png differ diff --git a/pictures/spring-boot-servlet.png b/pictures/spring-boot-servlet.png new file mode 100644 index 0000000..81f9c31 Binary files /dev/null and b/pictures/spring-boot-servlet.png differ diff --git a/pictures/spring-boot-tomcat.png b/pictures/spring-boot-tomcat.png new file mode 100644 index 0000000..0b867ed Binary files /dev/null and b/pictures/spring-boot-tomcat.png differ diff --git a/pictures/spring-boot-websocket.png b/pictures/spring-boot-websocket.png new file mode 100644 index 0000000..bea2c9d Binary files /dev/null and b/pictures/spring-boot-websocket.png differ diff --git a/spring-boot/spring-boot-base/README.md b/spring-boot/spring-boot-base/README.md new file mode 100644 index 0000000..840cd42 --- /dev/null +++ b/spring-boot/spring-boot-base/README.md @@ -0,0 +1,247 @@ +# spring-boot 基础 + +## 一、说明 + +#### 1.1 项目结构说明 + +1. 本项目搭建一个简单的hello spring 的 web工程,简单说明spring-boot 的开箱即用的特性; +2. 模板引擎采用freemaker 和 thymeleaf 作为示例,分别对应模板文件makershow.ftl 和 leafShow.html; +3. spring boot 2.x 默认是不支持jsp的,需要额外的配置,关于使用jsp的整合可以参考[spring-boot-jsp](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-jsp)项目。 + +![spring-boot-base](D:\spring-samples-for-all\pictures\spring-boot-base.png) + +#### 1.2 项目依赖 + +导入相关的starter(启动器) + +```xml + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + com.heibaiying + spring-boot-base + 0.0.1-SNAPSHOT + spring-boot-base + Demo project for Spring Boot + + + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-freemarker + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + +``` + +1. spring boot 项目默认继承自spring-boot-starter-parent,而spring-boot-starter-parent继承自spring-boot-dependencies, spring-boot-dependencies中定义了关于spring boot 依赖的各种jar包的版本,是spring boot 的版本管理中心。 + +![spring-boot-dependencies](D:\spring-samples-for-all\pictures\spring-boot-dependencies.png) + +2. 关于spring boot 2.x官方支持的所有starter 可以参见官方文档 [Table 13.1. Spring Boot application starters](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#using-boot-starter) + + + +## 二、spring boot 主启动类 + + 如果采用IDEA 或者 Spring Tool Suite (STS) 等开发工具创建的spring boot 工程,会默认创建启动类,如果没有创建,需要手动创建启动类 + +```java +package com.heibaiying.springbootbase; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootBaseApplication { + + // 启动类默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase + public static void main(String[] args) { + SpringApplication.run(SpringBootBaseApplication.class, args); + } + +} +``` + +@SpringBootApplication 注解是一个复合注解,里面包含了@ComponentScan注解,默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase + +```java +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@SpringBootConfiguration +@EnableAutoConfiguration +@ComponentScan(excludeFilters = { + @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), + @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) +public @interface SpringBootApplication { + ... +} +``` + + + +## 三、开箱即用的web工程 + +在springbootBaseApplication.java 的同级目录创建controller文件夹,并在其中创建RestfulController.java,启动项目访问localhost:8080/restful/programmers 即可看到项目搭建成功。 + +```java +/** + * @author : heibaiying + * @description : restful 控制器 + */ +@RestController +@RequestMapping("restful") +public class RestfulController { + + @GetMapping("programmers") + private List getProgrammers() { + List programmers = new ArrayList<>(); + programmers.add(new Programmer("xiaoming", 12, 100000.00f, LocalDate.of(2019, Month.AUGUST, 2))); + programmers.add(new Programmer("xiaohong", 23, 900000.00f, LocalDate.of(2013, Month.FEBRUARY, 2))); + return programmers; + } +} +``` + +这里之所以能够开箱即用,是因为我们在项目中导入spring-boot-starter-web启动器,而@SpringBootApplication 复合注解中默认开启了@EnableAutoConfiguration注解允许开启自动化配置,spring在检查导入starter-web的依赖后就会开启web的自动化配置。 + + + +## 四、模板引擎 + +这里我们在一个项目中同时导入了freemaker 和 thymeleaf的starter(虽然并不推荐,但是在同一个项目中是可以混用这两种模板引擎的)。 + +#### 4.1 freemarker + +```java +/** + * @author : heibaiying + * @description : 跳转渲染模板引擎 默认模板的存放位置为classpath:templates + */ +@Controller +@RequestMapping("freemarker") +public class FreeMarkerController { + + @RequestMapping("show") + private String programmerShow(ModelMap modelMap){ + List programmerList=new ArrayList<>(); + programmerList.add(new Programmer("xiaoming",12,100000.00f,LocalDate.of(2019,Month.AUGUST,2))); + programmerList.add(new Programmer("xiaohong",23,900000.00f,LocalDate.of(2013,Month.FEBRUARY,2))); + modelMap.addAttribute("programmers",programmerList); + return "markerShow"; + } +} + +``` + +```html + + + + + freemarker模板引擎 + + +
    + <#list programmers as programmer> +
  • 姓名: ${programmer.name} 年龄: ${programmer.age}
  • + +
+ + +``` + +#### 4.2 thymeleaf + +```java +/** + * @author : heibaiying + * @description : 跳转渲染模板引擎 默认模板的存放位置为classpath:templates + */ +@Controller +@RequestMapping("thymeleaf") +public class ThymeleafController { + + @RequestMapping("show") + private String programmerShow(ModelMap modelMap) { + List programmerList = new ArrayList<>(); + programmerList.add(new Programmer("xiaoming", 12, 100000.00f, LocalDate.of(2019, Month.AUGUST, 2))); + programmerList.add(new Programmer("xiaohong", 23, 900000.00f, LocalDate.of(2013, Month.FEBRUARY, 2))); + modelMap.addAttribute("programmers", programmerList); + return "leafShow"; + } +} + +``` + +```html + + + + + thymeleaf模板引擎 + + +
    +
  • + 姓名: + 薪水: +
  • +
+ + +``` + +#### 4.3 文档说明 + +freemarker:提供了完善的中文文档,地址 http://freemarker.foofun.cn/ + +thymeleaf:官方英文文档地址:[thymeleaf 3.0.11RELEASE](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf) + +注:我在本仓库中也上传了一份[thymeleaf中文文档(gangzi828(刘明刚 译)](https://github.com/heibaiying/spring-samples-for-all/tree/master/referenced%20documents),翻译的版本为3.0.5RELEASE \ No newline at end of file diff --git a/spring-boot/spring-boot-base/pom.xml b/spring-boot/spring-boot-base/pom.xml index 9c2aaa8..c8137e9 100644 --- a/spring-boot/spring-boot-base/pom.xml +++ b/spring-boot/spring-boot-base/pom.xml @@ -47,6 +47,7 @@ + diff --git a/spring-boot/spring-boot-base/src/main/java/com/heibaiying/springbootbase/SpringBootBaseApplication.java b/spring-boot/spring-boot-base/src/main/java/com/heibaiying/springbootbase/SpringBootBaseApplication.java index 547f498..ac395d7 100644 --- a/spring-boot/spring-boot-base/src/main/java/com/heibaiying/springbootbase/SpringBootBaseApplication.java +++ b/spring-boot/spring-boot-base/src/main/java/com/heibaiying/springbootbase/SpringBootBaseApplication.java @@ -6,7 +6,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootBaseApplication { - // 启动器默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase + /*@SpringBootApplication 注解是一个复合注解,里面包含了@ComponentScan注解 + 默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase*/ public static void main(String[] args) { SpringApplication.run(SpringBootBaseApplication.class, args); } diff --git a/spring-boot/spring-boot-data-jpa/README.md b/spring-boot/spring-boot-data-jpa/README.md new file mode 100644 index 0000000..2239e05 --- /dev/null +++ b/spring-boot/spring-boot-data-jpa/README.md @@ -0,0 +1,193 @@ +# spring boot data jpa + +## 一、说明 + +#### 1.1 项目结构 + +![spring-boot-servlet](D:\spring-samples-for-all\pictures\spring-boot-data-jpa.png) + +#### 1.2 项目主要依赖 + +```xml + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + mysql + mysql-connector-java + 8.0.13 + + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +## 二、data jpa 的使用 + +#### 2.1 在application.yml 中配置数据源 + +```yaml +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + hibernate: + ddl-auto: update + #Hibernate默认创建的表是myisam引擎,可以用以下方式指定为使用innodb创建表 + database-platform: org.hibernate.dialect.MySQL57Dialect + show-sql: true +``` + +#### 2.2 新建查询接口 + +```java +/** + * @author : heibaiying + * @description : 查询接口继承自CrudRepository,CrudRepository 默认定义了部分增删改查方法 + */ +public interface ProgRepository extends CrudRepository { + + /* + * 方法名遵循命名规范的查询 更多命名规范可以参考官方文档所列出的这张表格 + */ + List findAllByName(String name); + + /* + *分页排序查询 + */ + Page findAll(Pageable pageable); + + + /* + * 占位符查询 + */ + @Query(value = "select u from Programmer u where u.name = ?1 or u.salary = ?2") + List findByConditionAndOrder(String name, float salary, Sort.Order order); + + + /* + * 传入参数名称 + */ + @Query("select u from Programmer u where u.name = :name or u.age = :age") + Programmer findByParam(@Param("name") String name, + @Param("age") int age); +} + +``` + +关于查询方法遵循的命名规范和关键词见下表: + +| Keyword | Sample | JPQL snippet | +| ------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `And` | `findByLastnameAndFirstname` | `… where x.lastname = ?1 and x.firstname = ?2` | +| `Or` | `findByLastnameOrFirstname` | `… where x.lastname = ?1 or x.firstname = ?2` | +| `Is,Equals` | `findByFirstname`,`findByFirstnameIs`,
`findByFirstnameEquals` | `… where x.firstname = ?1` | +| `Between` | `findByStartDateBetween` | `… where x.startDate between ?1 and ?2` | +| `LessThan` | `findByAgeLessThan` | `… where x.age < ?1` | +| `LessThanEqual` | `findByAgeLessThanEqual` | `… where x.age <= ?1` | +| `GreaterThan` | `findByAgeGreaterThan` | `… where x.age > ?1` | +| `GreaterThanEqual` | `findByAgeGreaterThanEqual` | `… where x.age >= ?1` | +| `After` | `findByStartDateAfter` | `… where x.startDate > ?1` | +| `Before` | `findByStartDateBefore` | `… where x.startDate < ?1` | +| `IsNull` | `findByAgeIsNull` | `… where x.age is null` | +| `IsNotNull,NotNull` | `findByAge(Is)NotNull` | `… where x.age not null` | +| `Like` | `findByFirstnameLike` | `… where x.firstname like ?1` | +| `NotLike` | `findByFirstnameNotLike` | `… where x.firstname not like ?1` | +| `StartingWith` | `findByFirstnameStartingWith` | `… where x.firstname like ?1`(parameter bound with appended `%`) | +| `EndingWith` | `findByFirstnameEndingWith` | `… where x.firstname like ?1`(parameter bound with prepended `%`) | +| `Containing` | `findByFirstnameContaining` | `… where x.firstname like ?1`(parameter bound wrapped in `%`) | +| `OrderBy` | `findByAgeOrderByLastnameDesc` | `… where x.age = ?1 order by x.lastname desc` | +| `Not` | `findByLastnameNot` | `… where x.lastname <> ?1` | +| `In` | `findByAgeIn(Collection ages)` | `… where x.age in ?1` | +| `NotIn` | `findByAgeNotIn(Collection ages)` | `… where x.age not in ?1` | +| `True` | `findByActiveTrue()` | `… where x.active = true` | +| `False` | `findByActiveFalse()` | `… where x.active = false` | +| `IgnoreCase` | `findByFirstnameIgnoreCase` | `… where UPPER(x.firstame) = UPPER(?1)` | + +#### 2.3 测试类 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class DataJPATests { + + @Autowired + private ProgRepository repository; + + /** + * 保存数据测试 + */ + @Test + public void save() { + // 保存单条数据 + repository.save(new Programmer("pro01", 12, 2121.34f, new Date())); + // 保存多条数据 + List programmers = new ArrayList<>(); + programmers.add(new Programmer("pro02", 22, 3221.34f, new Date())); + programmers.add(new Programmer("pro03", 32, 3321.34f, new Date())); + programmers.add(new Programmer("pro04", 44, 4561.34f, new Date())); + programmers.add(new Programmer("pro01", 44, 4561.34f, new Date())); + repository.saveAll(programmers); + } + + + /** + * 查询数据测试 + */ + @Test + public void get() { + + // 遵循命名规范的查询 + List programmers = repository.findAllByName("pro01"); + programmers.forEach(System.out::println); + + // 传入参数名称 + Programmer param = repository.findByParam("pro02", 22); + System.out.println("findByParam:" + param); + + // 占位符查询 + List byCondition = repository.findByConditionAndOrder("pro03", 3321.34f, Sort.Order.asc("salary")); + System.out.println("byCondition:" + byCondition); + + //条件与分页查询 需要注意的是这里的页数是从第0页开始计算的 + Page page = repository.findAll(PageRequest.of(0, 10, Sort.Direction.DESC, "salary")); + page.get().forEach(System.out::println); + } + + + /** + * 更新数据测试 + */ + @Test + public void update() { + // 保存主键相同的数据就认为是更新操作 + repository.save(new Programmer(1, "updatePro01", 12, 2121.34f, new Date())); + Optional programmer = repository.findById(1); + Assert.assertEquals(programmer.get().getName(), "updatePro01"); + } + + /** + * 删除数据测试 + */ + @Test + public void delete() { + Optional programmer = repository.findById(2); + if (programmer.isPresent()) { + repository.deleteById(2); + } + Assert.assertFalse(programmer.isPresent()); + } +} +``` + diff --git a/spring-boot/spring-boot-data-jpa/src/main/resources/application.yml b/spring-boot/spring-boot-data-jpa/src/main/resources/application.yml index 504d2a7..e044e55 100644 --- a/spring-boot/spring-boot-data-jpa/src/main/resources/application.yml +++ b/spring-boot/spring-boot-data-jpa/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: datasource: - url: jdbc:mysql://127.0.0.1:3306/mysql?useUnicode=true&characterEncoding=utf-8 + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/spring-boot/spring-boot-druid-mybatis/README.md b/spring-boot/spring-boot-druid-mybatis/README.md new file mode 100644 index 0000000..3435f70 --- /dev/null +++ b/spring-boot/spring-boot-druid-mybatis/README.md @@ -0,0 +1,196 @@ +# spring boot 整合 druid+mybatis + +## 一、说明 + +#### 1.1 项目结构 + +1. 项目查询用的表对应的建表语句放置在resources的sql文件夹下; + +2. 为了使用druid控制台的功能,项目以web的方式构建。 + +![spring-boot-servlet](D:\spring-samples-for-all\pictures\spring-boot-druid-mybatis.png) + +#### 1.2 项目主要依赖 + +需要说明的是按照spring 官方对应自定义的starter 命名规范的推荐: + +- 官方的starter命名:spring-boot-starter-XXXX +- 其他第三方starter命名:XXXX-spring-boot-starte + +所以mybatis的starter命名为mybatis-spring-boot-starter,如果有自定义starter需求,也需要按照此命名规则进行命名。 + +```xml + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + + mysql + mysql-connector-java + 8.0.13 + + + + com.alibaba + druid-spring-boot-starter + 1.1.10 + +``` + +spring boot 与 mybatis 版本的对应关系: + +| MyBatis-Spring-Boot-Starter | [MyBatis-Spring](http://www.mybatis.org/spring/index.html#Requirements) | Spring Boot | +| --------------------------- | ------------------------------------------------------------ | ------------- | +| **1.3.x (1.3.1)** | 1.3 or higher | 1.5 or higher | +| **1.2.x (1.2.1)** | 1.3 or higher | 1.4 or higher | +| **1.1.x (1.1.1)** | 1.3 or higher | 1.3 or higher | +| **1.0.x (1.0.2)** | 1.2 or higher | 1.3 or higher | + + + +## 二、整合 druid + mybatis + +#### 2.1 在application.yml 中配置数据源 + +本用例采用druid作为数据库连接池,虽然druid性能略逊于Hikari,但是提供了更为全面的监控管理,可以按照实际需求选用druid或者Hikari。(关于Hikari数据源的配置可以参考[spring-boot-mbatis项目](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-mybatis)) + +```yaml +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + + # 使用 druid 作为连接池 更多配置的说明可以参见 druid starter 中文文档 https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter + type: com.alibaba.druid.pool.DruidDataSource + druid: + # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 + initialSize: 5 + # 最小连接池数量 + minIdle: 5 + # 最大连接池数量 + maxActive: 10 + # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 + maxWait: 60000 + # Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 + timeBetweenEvictionRunsMillis: 60000 + # 连接保持空闲而不被驱逐的最小时间 + minEvictableIdleTimeMillis: 300000 + # 用来检测连接是否有效的sql 因数据库方言而差, 例如 oracle 应该写成 SELECT 1 FROM DUAL + validationQuery: SELECT 1 + # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 + testWhileIdle: true + # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + testOnBorrow: false + # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + testOnReturn: false + # 是否自动回收超时连接 + removeAbandoned: true + # 超时时间(以秒数为单位) + remove-abandoned-timeout: 180 + + # druid 监控的配置 如果不使用 druid 的监控功能的话 以下配置就不是必须的 + # 本项目监控台访问地址: http://localhost:8080/druid/login.html + + # WebStatFilter用于采集web-jdbc关联监控的数据。 + # 更多配置可参见: https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter + web-stat-filter: + # 是否开启 WebStatFilter 默认是true + enabled: true + # 需要拦截的url + url-pattern: /* + # 排除静态资源的请求 + exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" + + # Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。 + # 更多配置可参见:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE + stat-view-servlet: + #是否启用StatViewServlet 默认值true + enabled: true + # 需要拦截的url + url-pattern: /druid/* + # 允许清空统计数据 + reset-enable: true + login-username: druid + login-password: druid + + + +# mybatis 相关配置 +mybatis: + configuration: + # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 + # oracle数据库建议配置为JdbcType.NULL, 默认是Other + jdbc-type-for-null: 'null' + # 是否打印sql语句 调试的时候可以开启 +log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +``` + +#### 2.2 新建查询接口和controller + +```java +@Mapper +public interface ProgrammerDao { + + + @Select("select * from programmer") + List selectAll(); + + @Insert("insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday})") + void save(Programmer programmer); + + @Select("select * from programmer where name = #{id}") + Programmer selectById(int id); + + @Update("update programmer set name=#{name},age=#{age},salary=#{salary},birthday=#{birthday} where id=#{id}") + int modify(Programmer programmer); + + @Delete(" delete from programmer where id = #{id}") + void delete(int id); +} +``` + +```xml +@RestController +public class ProgrammerController { + + @Autowired + private ProgrammerDao programmerDao; + + @GetMapping("/programmers") + public List get() { + return programmerDao.selectAll(); + } +} +``` + +#### 2.3 关于druid监控数据的外部化调用 + +```java +/** + * @author : heibaiying + * @description :在 Spring Boot 中可以通过 HTTP 接口将 Druid 监控数据以JSON 的形式暴露出去, + * 实际使用中你可以根据你的需要自由地对监控数据、暴露方式进行扩展。 + */ + +@RestController +public class DruidStatController { + + @GetMapping("/stat") + public Object druidStat() { + // DruidStatManagerFacade#getDataSourceStatDataList 该方法可以获取所有数据源的监控数据 + return DruidStatManagerFacade.getInstance().getDataSourceStatDataList(); + } +} +``` + +![druid-status](D:\spring-samples-for-all\pictures\druid-status.png) + + + +#### 2.4 druid 控制台的使用,默认访问地址 http://localhost:8080/druid/login.html + +![spring-boot-druid 控制台](D:\spring-samples-for-all\pictures\spring-boot-druid 控制台.png) \ No newline at end of file diff --git a/spring-boot/spring-boot-druid-mybatis/src/main/resources/application.yml b/spring-boot/spring-boot-druid-mybatis/src/main/resources/application.yml index 47d3129..4356fc8 100644 --- a/spring-boot/spring-boot-druid-mybatis/src/main/resources/application.yml +++ b/spring-boot/spring-boot-druid-mybatis/src/main/resources/application.yml @@ -1,71 +1,70 @@ spring: datasource: - url: jdbc:mysql://127.0.0.1:3306/mysql?useUnicode=true&characterEncoding=utf-8 + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver - # ʹ druid Ϊӳ õ˵Բμ druid starter ĵ https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter + # 使用 druid 作为连接池 更多配置的说明可以参见 druid starter 中文文档 https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter type: com.alibaba.druid.pool.DruidDataSource druid: - # ʼʱӵĸʼʾinitߵһgetConnectionʱ + # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 initialSize: 5 - # Сӳ + # 最小连接池数量 minIdle: 5 - # ӳ + # 最大连接池数量 maxActive: 10 - # ȡʱȴʱ䣬λ롣maxWait֮ȱʡùƽЧʻ½ҪͨuseUnfairLockΪtrueʹ÷ǹƽ + # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 maxWait: 60000 - # Destroy̻߳ӵļʱ䣬ӿʱڵminEvictableIdleTimeMillisرӡ + # Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 timeBetweenEvictionRunsMillis: 60000 - # ӱֿжСʱ + # 连接保持空闲而不被驱逐的最小时间 minEvictableIdleTimeMillis: 300000 - # ǷЧsql ݿⷽԶ, oracle Ӧд SELECT 1 FROM DUAL + # 用来检测连接是否有效的sql 因数据库方言而差, 例如 oracle 应该写成 SELECT 1 FROM DUAL validationQuery: SELECT 1 - # ΪtrueӰܣұ֤ȫԡӵʱ⣬ʱtimeBetweenEvictionRunsMillisִvalidationQueryǷЧ + # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 testWhileIdle: true - # ʱִvalidationQueryǷЧûήܡ + # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 testOnBorrow: false - # 黹ʱִvalidationQueryǷЧûήܡ + # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 testOnReturn: false - # ǷԶճʱ + # 是否自动回收超时连接 removeAbandoned: true - # ʱʱ(Ϊλ) + # 超时时间(以秒数为单位) remove-abandoned-timeout: 180 - # druid ص ʹ druid ļعܵĻ þͲDZ - # Ŀ̨ʵַ: http://localhost:8080/druid/login.html + # druid 监控的配置 如果不使用 druid 的监控功能的话 以下配置就不是必须的 + # 本项目监控台访问地址: http://localhost:8080/druid/login.html - # WebStatFilterڲɼweb-jdbcصݡ - # ÿɲμ: https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter + # WebStatFilter用于采集web-jdbc关联监控的数据。 + # 更多配置可参见: https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter web-stat-filter: - # Ƿ WebStatFilter Ĭtrue + # 是否开启 WebStatFilter 默认是true enabled: true - # Ҫصurl + # 需要拦截的url url-pattern: /* - # ų̬Դ + # 排除静态资源的请求 exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" - # DruidṩһStatViewServletչʾDruidͳϢ - # ÿɲμ:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE + # Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。 + # 更多配置可参见:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE stat-view-servlet: - #ǷStatViewServlet Ĭֵtrue + #是否启用StatViewServlet 默认值true enabled: true - # Ҫصurl + # 需要拦截的url url-pattern: /druid/* - # ͳ + # 允许清空统计数据 reset-enable: true login-username: druid login-password: druid -# mybatis +# mybatis 相关配置 mybatis: configuration: - # ûΪṩض JDBC ʱΪֵָ JDBC ͡ - # oracleݿ⽨ΪJdbcType.NULL, ĬOther + # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 + # oracle数据库建议配置为JdbcType.NULL, 默认是Other jdbc-type-for-null: 'null' - # Ƿӡsql ԵʱԿ - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl - + # 是否打印sql语句 调试的时候可以开启 +log-impl: org.apache.ibatis.logging.stdout.StdOutImpl \ No newline at end of file diff --git a/spring-boot/spring-boot-dubbo/README.md b/spring-boot/spring-boot-dubbo/README.md new file mode 100644 index 0000000..9116d9c --- /dev/null +++ b/spring-boot/spring-boot-dubbo/README.md @@ -0,0 +1,348 @@ +# spring boot 整合 dubbo + +## 一、 项目结构说明 + +1.1 按照dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中,所以项目采用maven多模块的构建方式,在spring-boot-dubbo下构建三个子模块: + +1. boot-dubbo-common 是公共模块,用于存放公共的接口和bean,被boot-dubbo-provider和boot-dubbo-consumer在pom.xml中引用; +2. boot-dubbo-provider 是服务的提供者,提供商品的查询服务; +3. boot-dubbo-consumer是服务的消费者,调用provider提供的查询服务。 + +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) + +![spring-scheduling](D:\spring-samples-for-all\pictures\spring-boot-dubbo.png) + + + +## 二、关键依赖 + +在父工程的项目中统一导入依赖dubbo的starter,父工程的pom.xml如下 + +```xml + + + 4.0.0 + pom + + + boot-dubbo-common + boot-dubbo-consumer + boot-dubbo-provider + + + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + com.heibaiying + spring-boot-dubbo + 0.0.1-SNAPSHOT + spring-boot-dubbo + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-freemarker + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.alibaba.boot + dubbo-spring-boot-starter + 0.2.0 + + + + +``` + + + +## 三、公共模块(boot-dubbo-common) + +- api 下为公共的调用接口; +- bean 下为公共的实体类。 + +![spring-scheduling](D:\spring-samples-for-all\pictures\boot-dubbo-common.png) + +## 四、 服务提供者(boot-dubbo-provider) + +![spring-scheduling](D:\spring-samples-for-all\pictures\boot-dubbo-provider.png) + +#### 4.1 提供方配置 + +```yaml +dubbo: + application: + name: boot-duboo-provider + # 指定注册协议和注册地址 dubbo推荐使用zookeeper作为注册中心,并且在start依赖中引入了zookeeper的java客户端Curator + registry: + protocol: zookeeper + address: 127.0.0.1:2181 + protocol.name: dubbo +``` + +#### 4.2 使用注解@Service暴露服务 + +需要注意的是这里的@Service注解不是spring的注解,而是dubbo的注解 com.alibaba.dubbo.config.annotation.Service + +```java +package com.heibaiying.dubboprovider.service; + +import com.alibaba.dubbo.config.annotation.Service; +import com.heibaiying.api.IProductService; +import com.heibaiying.bean.Product; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author : heibaiying + * @description : 产品提供接口实现类 + */ +@Service(timeout = 5000) +public class ProductService implements IProductService { + + private static List productList = new ArrayList<>(); + + static { + 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) { + if (product.getId() == id) { + return product; + } + } + return null; + } + + + public List queryAllProducts() { + return productList; + } +} + +``` + +## 五、服务消费者(boot-dubbo-consumer) + +![boot-dubbo-consumer](D:\spring-samples-for-all\pictures\boot-dubbo-consumer1.png) + +#### 1.消费方的配置 + +```yaml +dubbo: + application: + name: boot-duboo-provider + # 指定注册协议和注册地址 dubbo推荐使用zookeeper作为注册中心,并且在start依赖中引入了zookeeper的java客户端Curator + registry: + protocol: zookeeper + address: 127.0.0.1:2181 + protocol.name: dubbo + # 关闭所有服务的启动时检查 (没有提供者时报错)视实际情况设置 + consumer: + check: false +server: +port: 8090 +``` + +#### 2.使用注解@Reference引用远程服务 + +```java +package com.heibaiying.dubboconsumer.controller; + +import com.alibaba.dubbo.config.annotation.Reference; +import com.heibaiying.api.IProductService; +import com.heibaiying.bean.Product; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.List; + +@Controller +@RequestMapping("sell") +public class SellController { + + // dubbo远程引用注解 + @Reference + private IProductService productService; + + @RequestMapping + public String productList(Model model) { + List products = productService.queryAllProducts(); + model.addAttribute("products", products); + return "products"; + } + + @RequestMapping("product/{id}") + public String productDetail(@PathVariable int id, Model model) { + Product product = productService.queryProductById(id); + model.addAttribute("product", product); + return "product"; + } +} +``` + +## 六、项目构建的说明 + +因为在项目中,consumer和provider模块均依赖公共模块,所以在构建consumer和provider项目前需要将common 模块安装到本地仓库,**依次**对**父工程**和**common模块**执行: + +```shell +mvn install -Dmaven.test.skip = true +``` + +consumer中 pom.xml如下 + +```xml + + + 4.0.0 + + + spring-boot-dubbo + com.heibaiying + 0.0.1-SNAPSHOT + + + boot-dubbo-consumer + 0.0.1-SNAPSHOT + boot-dubbo-consumer + dubbo project for Spring Boot + + + 1.8 + + + + + + com.heibaiying + boot-dubbo-common + 0.0.1-SNAPSHOT + compile + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` + +provider中 pom.xml如下 + +```xml + + + 4.0.0 + + + spring-boot-dubbo + com.heibaiying + 0.0.1-SNAPSHOT + + + + boot-dubbo-provider + 0.0.1-SNAPSHOT + boot-dubbo-provider + dubbo project for Spring Boot + + + 1.8 + + + + + + com.heibaiying + boot-dubbo-common + 0.0.1-SNAPSHOT + compile + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` + +## 七、关于dubbo新版本管理控制台的安装说明 + +安装: + +```sh +git clone https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops +cd /var/tmp/dubbo-ops +mvn clean package +``` + +配置: + +```sh +配置文件为: +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 +``` \ No newline at end of file diff --git a/spring-boot/spring-boot-dubbo/boot-dubbo-consumer/src/main/resources/application.yml b/spring-boot/spring-boot-dubbo/boot-dubbo-consumer/src/main/resources/application.yml index 023f3f1..f1e417e 100644 --- a/spring-boot/spring-boot-dubbo/boot-dubbo-consumer/src/main/resources/application.yml +++ b/spring-boot/spring-boot-dubbo/boot-dubbo-consumer/src/main/resources/application.yml @@ -1,14 +1,13 @@ dubbo: application: name: boot-duboo-provider - # ָעЭעַ dubboƼʹzookeeperΪעģstartzookeeperjavaͻCurator + # 指定注册协议和注册地址 dubbo推荐使用zookeeper作为注册中心,并且在start依赖中引入了zookeeper的java客户端Curator registry: protocol: zookeeper address: 127.0.0.1:2181 protocol.name: dubbo - # رзʱ (ûṩʱʵ + # 关闭所有服务的启动时检查 (没有提供者时报错)视实际情况设置 consumer: check: false server: - port: 8090 - +port: 8090 \ No newline at end of file diff --git a/spring-boot/spring-boot-dubbo/boot-dubbo-provider/src/main/resources/application.yml b/spring-boot/spring-boot-dubbo/boot-dubbo-provider/src/main/resources/application.yml index 464875f..d4e5972 100644 --- a/spring-boot/spring-boot-dubbo/boot-dubbo-provider/src/main/resources/application.yml +++ b/spring-boot/spring-boot-dubbo/boot-dubbo-provider/src/main/resources/application.yml @@ -1,8 +1,8 @@ dubbo: application: name: boot-duboo-provider - # ָעЭעַ dubboƼʹzookeeperΪעģstartzookeeperjavaͻCurator + # 指定注册协议和注册地址 dubbo推荐使用zookeeper作为注册中心,并且在start依赖中引入了zookeeper的java客户端Curator registry: protocol: zookeeper address: 127.0.0.1:2181 - protocol.name: dubbo + protocol.name: dubbo \ No newline at end of file diff --git a/spring-boot/spring-boot-jsp/README.md b/spring-boot/spring-boot-jsp/README.md new file mode 100644 index 0000000..be18f2e --- /dev/null +++ b/spring-boot/spring-boot-jsp/README.md @@ -0,0 +1,84 @@ +# spring boot 内置容器 整合 jsp + +## 一、说明 + +#### 1.1 项目结构 + +![spring-boot-servlet](D:\spring-samples-for-all\pictures\spring-boot-jsp.png) + +#### 1.2 项目主要依赖 + +```xml + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + javax.servlet + jstl + +``` + +## 二、整合 jsp + +#### 2.1 导入整合的依赖 + +```xml + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + javax.servlet + jstl + +``` + +#### 2.2 在application.yml 中指定访问视图文件的前缀和后缀 + +```yml +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp +``` + +#### 2.3 新建controller和show.jsp 测试整合是否成功 + +```java +@Controller +@RequestMapping("index") +public class JspController { + + @RequestMapping + public String jsp(Model model){ + Programmer programmer = new Programmer("heibai", 21, 1298.31f, LocalDate.now()); + model.addAttribute("programmer",programmer); + return "show"; + } +} +``` + +```jsp +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + programmer + + + +
    +
  • 姓名: ${programmer.name}
  • +
  • 年龄: ${programmer.age}
  • +
+ + +``` + diff --git a/spring-boot/spring-boot-jsp/pom.xml b/spring-boot/spring-boot-jsp/pom.xml index 07b7ad7..615c274 100644 --- a/spring-boot/spring-boot-jsp/pom.xml +++ b/spring-boot/spring-boot-jsp/pom.xml @@ -40,6 +40,7 @@ tomcat-embed-jasper provided + javax.servlet jstl diff --git a/spring-boot/spring-boot-memcached/README.md b/spring-boot/spring-boot-memcached/README.md new file mode 100644 index 0000000..725a1a8 --- /dev/null +++ b/spring-boot/spring-boot-memcached/README.md @@ -0,0 +1,148 @@ +# spring boot 整合 mecached + +## 一、说明 + +### 1.1 XMemcached客户端说明 + +spring boot 官方并没有提供关于 memcached 的starter,所以我们这里还是采用XMemcached作为客户端进行整合。 XMemcached是基于java nio的memcached高性能客户端,支持完整的memcached协议,支持客户端分布并且提供了一致性哈希(consistent hash)算法的实现。 + +### 1.2 项目结构说明 + +memcached的整合配置位于config文件夹下。 + +![spring+redis项目目录结构](D:\spring-samples-for-all\pictures\spring-boot-memcached.png) + +### 1.3 主要依赖 + +```xml + + + com.googlecode.xmemcached + xmemcached + 2.4.5 + +``` + + + +## 二、spring boot 整合 memcached + +#### 2.1 单机配置 + +```java +@Bean +public MemcachedClient memcachedClient() { + XMemcachedClientBuilder builder = new XMemcachedClientBuilder("192.168.200.201:11211"); + MemcachedClient memcachedClient = null; +try { + memcachedClient = builder.build(); + } catch (IOException e) { + e.printStackTrace(); +} + return memcachedClient; +} +``` + +#### 2.2 集群配置 + +```java +@Bean +public MemcachedClient memcachedClientForCluster() { + + List addressList = new ArrayList(); + addressList.add(new InetSocketAddress("192.168.200.201", 11211)); + addressList.add(new InetSocketAddress("192.168.200.201", 11212)); + // 赋予权重 + int[] weights = {1, 2}; + XMemcachedClientBuilder builder = new XMemcachedClientBuilder(addressList, weights); + // 设置连接池大小 + builder.setConnectionPoolSize(10); + // 协议工厂 + builder.setCommandFactory(new TextCommandFactory()); + // 分布策略,一致性哈希KetamaMemcachedSessionLocator或者ArraySessionLocator(默认) + builder.setSessionLocator(new KetamaMemcachedSessionLocator()); + // 设置序列化器 + builder.setTranscoder(new SerializingTranscoder()); + MemcachedClient memcachedClient = null; + try { + memcachedClient = builder.build(); + } catch (IOException e) { + e.printStackTrace(); + } + return memcachedClient; +} +``` + +#### 2.3 存储基本类型测试用例 + +xmemcached单机版本和集群版本注入的实例是相同的。 + +```java +/** + * @author : heibaiying + * @description : Memcached 操作基本对象 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class MemSamples { + + @Autowired + private MemcachedClient memcachedClient; + + @Test + public void operate() throws InterruptedException, MemcachedException, TimeoutException { + memcachedClient.set("hello", 0, "Hello,cluster xmemcached"); + String value = memcachedClient.get("hello"); + System.out.println("hello=" + value); + memcachedClient.delete("hello"); + value = memcachedClient.get("hello"); + System.out.println("hello=" + value); + } +} +``` + +#### 2.5 存储实体对象测试用例 + +```java +/** + * @author : heibaiying + * @description :Memcached 序列化与反序列化 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class MemObjectSamples { + + @Autowired + private MemcachedClient memcachedClient; + + @Test + public void operate() throws InterruptedException, MemcachedException, TimeoutException { + memcachedClient.set("programmer", 0, new Programmer("xiaoming", 12, 5000.21f, new Date())); + Programmer programmer = memcachedClient.get("programmer"); + System.out.println("hello ," + programmer.getName()); + memcachedClient.delete("programmer"); + programmer = memcachedClient.get("programmer"); + Assert.assertNull(programmer); + } +} + +``` + + + +## 附:memcached 基本命令 + +| 命令 | 格式 | 说明 | +| --------------- | -------------------------------------------------- | ------------------------------------- | +| 新增 set | set key flags exTime length -> value | 无论什么情况,都可以插入 | +| 新增 add | add key flags exTime length -> value | 只有当key不存在的情况下,才可以插入 | +| 替换 replace | replace key flags exTime length -> value | 只修改已存在key的value值 | +| 追加内容append | append key flags exTime length -> value | length表示追加的长度而不是总长度 | +| 前面追加prepend | prepend key flags exTime length -> value | length表示追加的长度而不是总长度 | +| 查询操作 get | get key | | +| 检查更新 cas | cas key flags exTime length version -> value | 版本正确才更新 | +| 详细获取 gets | gets key | 返回的最后一个数代表 key 的 CAS 令牌 | +| 删除 delete | delete key | 将数据打一个删除标记 | +| 自增 incr | incr key 增加偏移量 | incr和decr只能操作能转换为数字的Value | +| 自减 decr | decr key 减少偏移量 | desr不能将数字减少至0以下 | +| 清库 | flush_all | | \ No newline at end of file diff --git a/spring-boot/spring-boot-memcached/pom.xml b/spring-boot/spring-boot-memcached/pom.xml index 8dff5fc..0535c5e 100644 --- a/spring-boot/spring-boot-memcached/pom.xml +++ b/spring-boot/spring-boot-memcached/pom.xml @@ -35,7 +35,7 @@ true - + com.googlecode.xmemcached xmemcached diff --git a/spring-boot/spring-boot-mongodb/README.md b/spring-boot/spring-boot-mongodb/README.md new file mode 100644 index 0000000..dfa0d09 --- /dev/null +++ b/spring-boot/spring-boot-mongodb/README.md @@ -0,0 +1,182 @@ +# spring boot 整合 mongodb + +## 一、说明 + +#### 1.1 用例结构 + +1. 本用例提供mongdb的简单整合用例; +2. 提供用MongoTemplate的方式操作mongdb,见测试用例MongoOriginalTests.java +3. 提供基于spring data jpa 的方式操作mongodb(推荐),见测试用例MongoJPATests.java + +![spring-boot-servlet](D:\spring-samples-for-all\pictures\spring-boot-mongodb.png) + +#### 1.2 项目主要依赖 + +```xml + + org.springframework.boot + spring-boot-starter-data-mongodb + +``` + + + +## 二、整合 mongodb + +#### 2.1 在application.yml 中配置mongodb数据源 + +```yaml +spring: + data: + mongodb: + database: spring + uri: mongodb://192.168.0.108:27017 +``` + +#### 2.2 基于MongoTemplate实现对mongodb的操作 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class MongoOriginalTests { + + @Autowired + private MongoTemplate mongoTemplate; + + @Test + public void insert() { + // 单条插入 + mongoTemplate.insert(new Programmer("xiaoming", 12, 5000.21f, new Date())); + List programmers = new ArrayList(); + // 批量插入 + programmers.add(new Programmer("xiaohong", 21, 52200.21f, new Date())); + programmers.add(new Programmer("xiaolan", 34, 500.21f, new Date())); + mongoTemplate.insert(programmers, Programmer.class); + } + + // 条件查询 + @Test + public void select() { + Criteria criteria = new Criteria(); + criteria.andOperator(where("name").is("xiaohong"), where("age").is(21)); + Query query = new Query(criteria); + Programmer one = mongoTemplate.findOne(query, Programmer.class); + System.out.println(one); + } + + + // 更新数据 + @Test + 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()); + } + +} +``` + +#### 2.3 使用 data jpa 方式操作mongodb (推荐使用) + +1.新建查询结构,查询方法按照支持的关键字命名 + +```java +public interface ProgrammerRepository extends MongoRepository { + + void deleteAllByName(String name); + + Programmer findAllByName(String names); + + Programmer findByNameAndAge(String name, int age); + +} +``` + +2.测试 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class MongoJPATests { + + @Autowired + private ProgrammerRepository repository; + + @Test + public void insert() { + // 单条插入 + repository.save(new Programmer("python", 23, 21832.34f, new Date())); + // 批量插入 + List programmers = new ArrayList(); + programmers.add(new Programmer("java", 21, 52200.21f, new Date())); + programmers.add(new Programmer("Go", 34, 500.21f, new Date())); + repository.saveAll(programmers); + } + + // 条件查询 + @Test + public void select() { + Programmer java = repository.findByNameAndAge("java", 21); + Assert.assertEquals(java.getSalary(), 52200.21f, 0.01); + } + + + // 更新数据 + @Test + public void MUpdate() { + repository.save(new Programmer("Go", 8, 500.21f, new Date())); + Programmer go = repository.findAllByName("Go"); + Assert.assertEquals(go.getAge(), 8); + } + + // 删除指定数据 + @Test + public void delete() { + repository.deleteAllByName("python"); + Optional python = repository.findById("python"); + Assert.assertFalse(python.isPresent()); + } + +} +``` + +查询方法支持的关键字如下,更多命名规范可以参见Spring Data MongoDB官方文档[Query Methods](https://docs.spring.io/spring-data/mongodb/docs/2.1.3.RELEASE/reference/html/#mongodb.repositories.queries): + +| Keyword | Sample | Logical result | +| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `After` | `findByBirthdateAfter(Date date)` | `{"birthdate" : {"$gt" : date}}` | +| `GreaterThan` | `findByAgeGreaterThan(int age)` | `{"age" : {"$gt" : age}}` | +| `GreaterThanEqual` | `findByAgeGreaterThanEqual(int age)` | `{"age" : {"$gte" : age}}` | +| `Before` | `findByBirthdateBefore(Date date)` | `{"birthdate" : {"$lt" : date}}` | +| `LessThan` | `findByAgeLessThan(int age)` | `{"age" : {"$lt" : age}}` | +| `LessThanEqual` | `findByAgeLessThanEqual(int age)` | `{"age" : {"$lte" : age}}` | +| `Between` | `findByAgeBetween(int from, int to)` | `{"age" : {"$gt" : from, "$lt" : to}}` | +| `In` | `findByAgeIn(Collection ages)` | `{"age" : {"$in" : [ages…]}}` | +| `NotIn` | `findByAgeNotIn(Collection ages)` | `{"age" : {"$nin" : [ages…]}}` | +| `IsNotNull`, `NotNull` | `findByFirstnameNotNull()` | `{"firstname" : {"$ne" : null}}` | +| `IsNull`, `Null` | `findByFirstnameNull()` | `{"firstname" : null}` | +| `Like`, `StartingWith`, `EndingWith` | `findByFirstnameLike(String name)` | `{"firstname" : name} (name as regex)` | +| `NotLike`, `IsNotLike` | `findByFirstnameNotLike(String name)` | `{"firstname" : { "$not" : name }} (name as regex)` | +| `Containing` on String | `findByFirstnameContaining(String name)` | `{"firstname" : name} (name as regex)` | +| `NotContaining` on String | `findByFirstnameNotContaining(String name)` | `{"firstname" : { "$not" : name}} (name as regex)` | +| `Containing` on Collection | `findByAddressesContaining(Address address)` | `{"addresses" : { "$in" : address}}` | +| `NotContaining` on Collection | `findByAddressesNotContaining(Address address)` | `{"addresses" : { "$not" : { "$in" : address}}}` | +| `Regex` | `findByFirstnameRegex(String firstname)` | `{"firstname" : {"$regex" : firstname }}` | +| `(No keyword)` | `findByFirstname(String name)` | `{"firstname" : name}` | +| `Not` | `findByFirstnameNot(String name)` | `{"firstname" : {"$ne" : name}}` | +| `Near` | `findByLocationNear(Point point)` | `{"location" : {"$near" : [x,y]}}` | +| `Near` | `findByLocationNear(Point point, Distance max)` | `{"location" : {"$near" : [x,y], "$maxDistance" : max}}` | +| `Near` | `findByLocationNear(Point point, Distance min, Distance max)` | `{"location" : {"$near" : [x,y], "$minDistance" : min, "$maxDistance" : max}}` | +| `Within` | `findByLocationWithin(Circle circle)` | `{"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}}` | +| `Within` | `findByLocationWithin(Box box)` | `{"location" : {"$geoWithin" : {"$box" : [ [x1, y1], x2, y2]}}}` | +| `IsTrue`, `True` | `findByActiveIsTrue()` | `{"active" : true}` | +| `IsFalse`, `False` | `findByActiveIsFalse()` | `{"active" : false}` | +| `Exists` | `findByLocationExists(boolean exists)` | `{"location" : {"$exists" : exists }}` | + diff --git a/spring-boot/spring-boot-mybatis/README.md b/spring-boot/spring-boot-mybatis/README.md new file mode 100644 index 0000000..0d287a6 --- /dev/null +++ b/spring-boot/spring-boot-mybatis/README.md @@ -0,0 +1,249 @@ +# spring boot 整合 mybatis + +## 一、说明 + +#### 1.1 项目结构 + +1. 项目查询用的表对应的建表语句放置在resources的sql文件夹下; + +2. 关于mybatis sql的写法提供两种方式: + + xml 写法:对应的类为ProgrammerMapper.java 和 programmerMapper.xml,用MybatisXmlTest进行测试; + + 注解写法:对应的类为Programmer.java ,用MybatisAnnotationTest进行测试。 + +![spring-boot-servlet](D:\spring-samples-for-all\pictures\spring-boot-mybatis.png) + +#### 1.2 项目主要依赖 + +需要说明的是按照spring 官方对应自定义的starter 命名规范的推荐: + +- 官方的starter命名:spring-boot-starter-XXXX +- 其他第三方starter命名:XXXX-spring-boot-starte + +所以mybatis的starter命名为mybatis-spring-boot-starter,如果有自定义starter需求,也需要按照此命名规则进行命名。 + +```xml + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + + mysql + mysql-connector-java + 8.0.13 + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + +``` + +spring boot 与 mybatis 版本的对应关系: + +| MyBatis-Spring-Boot-Starter | [MyBatis-Spring](http://www.mybatis.org/spring/index.html#Requirements) | Spring Boot | +| --------------------------- | ------------------------------------------------------------ | ------------- | +| **1.3.x (1.3.1)** | 1.3 or higher | 1.5 or higher | +| **1.2.x (1.2.1)** | 1.3 or higher | 1.4 or higher | +| **1.1.x (1.1.1)** | 1.3 or higher | 1.3 or higher | +| **1.0.x (1.0.2)** | 1.2 or higher | 1.3 or higher | + +## 二、整合 mybatis + +#### 2.1 在application.yml 中配置数据源 + +spring boot 2.x 版本默认采用Hikari作为数据库连接池,Hikari是目前java平台性能最好的连接池,性能好于druid。 + +```yaml +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + + # 如果不想配置对数据库连接池做特殊配置的话,以下关于连接池的配置就不是必须的 + # spring-boot 2 默认采用高性能的 Hikari 作为连接池 更多配置可以参考 https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby + type: com.zaxxer.hikari.HikariDataSource + hikari: + # 池中维护的最小空闲连接数 + minimum-idle: 10 + # 池中最大连接数,包括闲置和使用中的连接 + maximum-pool-size: 20 + # 此属性控制从池返回的连接的默认自动提交行为。默认为true + auto-commit: true + # 允许最长空闲时间 + idle-timeout: 30000 + # 此属性表示连接池的用户定义名称,主要显示在日志记录和JMX管理控制台中,以标识池和池配置。 默认值:自动生成 + pool-name: custom-hikari + #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟 + max-lifetime: 1800000 + # 数据库连接超时时间,默认30秒,即30000 + connection-timeout: 30000 + # 连接测试sql 这个地方需要根据数据库方言差异而配置 例如 oracle 就应该写成 select 1 from dual + connection-test-query: SELECT 1 + +# mybatis 相关配置 +mybatis: + # 指定 sql xml 文件的位置 + mapper-locations: classpath*:mappers/*.xml + configuration: + # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 + # oracle数据库建议配置为JdbcType.NULL, 默认是Other + jdbc-type-for-null: 'null' + # 是否打印sql语句 调试的时候可以开启 + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +``` + +#### 2.2 xml方式的sql语句 + +新建 ProgrammerMapper.java 和 programmerMapper.xml,及其测试类 + +```java +@Mapper +public interface ProgrammerMapper { + + void save(Programmer programmer); + + Programmer selectById(int id); + + int modify(Programmer programmer); + + void delete(int id); +} +``` + +```xml + + + + + + insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday}) + + + + + + update programmer set name=#{name},age=#{age},salary=#{salary},birthday=#{birthday} where id=#{id} + + + + delete from programmer where id = #{id} + + + +``` + +测试类 + +```java +/*** + * @description: xml Sql测试类 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class MybatisXmlTest { + + @Autowired + private ProgrammerMapper mapper; + + @Test + public void save() { + mapper.save(new Programmer("xiaominng", 12, 3467.34f, new Date())); + mapper.save(new Programmer("xiaominng", 12, 3467.34f, new Date())); + } + + @Test + public void modify() { + mapper.modify(new Programmer(1, "xiaohong", 112, 347.34f, new Date())); + } + + @Test + public void selectByCondition() { + Programmer programmers = mapper.selectById(1); + System.out.println(programmers); + } + + @Test + public void delete() { + mapper.delete(2); + Programmer programmers = mapper.selectById(2); + Assert.assertNull(programmers); + } +} +``` + +#### 2.3 注解方式的sql语句 + +```java +@Mapper +public interface ProgrammerDao { + + @Insert("insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday})") + void save(Programmer programmer); + + @Select("select * from programmer where name = #{id}") + Programmer selectById(int id); + + @Update("update programmer set name=#{name},age=#{age},salary=#{salary},birthday=#{birthday} where id=#{id}") + int modify(Programmer programmer); + + @Delete(" delete from programmer where id = #{id}") + void delete(int id); +} +``` + +测试类 + +```java +/*** + * @description: 注解Sql测试类 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class MybatisAnnotationTest { + + @Autowired + private ProgrammerDao programmerDao; + + @Test + public void save() { + programmerDao.save(new Programmer("xiaominng", 12, 3467.34f, new Date())); + programmerDao.save(new Programmer("xiaominng", 12, 3467.34f, new Date())); + } + + @Test + public void modify() { + programmerDao.modify(new Programmer(1, "xiaolan", 21, 347.34f, new Date())); + } + + @Test + public void selectByCondition() { + Programmer programmers = programmerDao.selectById(1); + System.out.println(programmers); + } + + @Test + public void delete() { + programmerDao.delete(3); + Programmer programmers = programmerDao.selectById(3); + Assert.assertNull(programmers); + } +} + +``` + diff --git a/spring-boot/spring-boot-mybatis/src/main/java/com/heibaiying/springboot/dao/ProgrammerDao.java b/spring-boot/spring-boot-mybatis/src/main/java/com/heibaiying/springboot/dao/ProgrammerDao.java index d8a35a3..61cd54e 100644 --- a/spring-boot/spring-boot-mybatis/src/main/java/com/heibaiying/springboot/dao/ProgrammerDao.java +++ b/spring-boot/spring-boot-mybatis/src/main/java/com/heibaiying/springboot/dao/ProgrammerDao.java @@ -3,8 +3,6 @@ package com.heibaiying.springboot.dao; import com.heibaiying.springboot.bean.Programmer; import org.apache.ibatis.annotations.*; -import java.util.List; - /** * @author : heibaiying */ diff --git a/spring-boot/spring-boot-mybatis/src/main/java/com/heibaiying/springboot/dao/ProgrammerMapper.java b/spring-boot/spring-boot-mybatis/src/main/java/com/heibaiying/springboot/dao/ProgrammerMapper.java index 6c769c8..9797c00 100644 --- a/spring-boot/spring-boot-mybatis/src/main/java/com/heibaiying/springboot/dao/ProgrammerMapper.java +++ b/spring-boot/spring-boot-mybatis/src/main/java/com/heibaiying/springboot/dao/ProgrammerMapper.java @@ -3,8 +3,6 @@ package com.heibaiying.springboot.dao; import com.heibaiying.springboot.bean.Programmer; import org.apache.ibatis.annotations.Mapper; -import java.util.List; - /** * @author : heibaiying */ diff --git a/spring-boot/spring-boot-mybatis/src/main/resources/application.yml b/spring-boot/spring-boot-mybatis/src/main/resources/application.yml index 06b8071..932dc69 100644 --- a/spring-boot/spring-boot-mybatis/src/main/resources/application.yml +++ b/spring-boot/spring-boot-mybatis/src/main/resources/application.yml @@ -1,55 +1,55 @@ spring: datasource: - url: jdbc:mysql://127.0.0.1:3306/mysql?useUnicode=true&characterEncoding=utf-8 + url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver - # ԴĻ,¹ӳصþͲDZ - # spring-boot 2 Ĭϲøܵ Hikari Ϊӳ ÿԲο https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby + # 如果不想配置对数据库连接池做特殊配置的话,以下关于连接池的配置就不是必须的 + # spring-boot 2 默认采用高性能的 Hikari 作为连接池 更多配置可以参考 https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby type: com.zaxxer.hikari.HikariDataSource hikari: - # άС + # 池中维护的最小空闲连接数 minimum-idle: 10 - # úʹе + # 池中最大连接数,包括闲置和使用中的连接 maximum-pool-size: 20 - # ԿƴӳطصӵĬԶύΪĬΪtrue + # 此属性控制从池返回的连接的默认自动提交行为。默认为true auto-commit: true - # ʱ + # 允许最长空闲时间 idle-timeout: 30000 - # ԱʾӳصûƣҪʾ־¼JMX̨УԱʶغͳá ĬֵԶ + # 此属性表示连接池的用户定义名称,主要显示在日志记录和JMX管理控制台中,以标识池和池配置。 默认值:自动生成 pool-name: custom-hikari - #ԿƳӵڣֵ0ʾڣĬ180000030 + #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟 max-lifetime: 1800000 - # ݿӳʱʱ,Ĭ30룬30000 + # 数据库连接超时时间,默认30秒,即30000 connection-timeout: 30000 - # Ӳsql طҪݿⷽԲ oracle Ӧд select 1 from dual + # 连接测试sql 这个地方需要根据数据库方言差异而配置 例如 oracle 就应该写成 select 1 from dual connection-test-query: SELECT 1 -# mybatis +# mybatis 相关配置 mybatis: - # ָ sql xml ļλ + # 指定 sql xml 文件的位置 mapper-locations: classpath*:mappers/*.xml configuration: - # ûΪṩض JDBC ʱΪֵָ JDBC ͡ - # oracleݿ⽨ΪJdbcType.NULL, ĬOther + # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 + # oracle数据库建议配置为JdbcType.NULL, 默认是Other jdbc-type-for-null: 'null' - # Ƿӡsql ԵʱԿ + # 是否打印sql语句 调试的时候可以开启 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl -# ע 1 +# 注 1 # oracle # driver="oracle.jdbc.driver.OracleDriver" - # url="jdbc:oracle:thin:@localhost:1521:ݿ" + # url="jdbc:oracle:thin:@localhost:1521:数据库名" # mysql - # driver="com.mysql.jdbc.Driver" - # url="jdbc:mysql://localhost/ݿ?[Ӳ]" + # driver="com.mysql.cj.jdbc.Driver" + # url="jdbc:mysql://localhost/数据库名?[后接参数]" -# ע 2 - # mybatis ø˵Բοsettings http://www.mybatis.org/mybatis-3/zh/configuration.html +# 注 2 + # mybatis 配置更多说明可以参考settings http://www.mybatis.org/mybatis-3/zh/configuration.html -# ע 3 - # spring boot 2.0 ĬϲHikari Ϊӳ githubַ https://github.com/brettwooldridge/HikariCP \ No newline at end of file +# 注 3 +# spring boot 2.0 默认采用Hikari 作为连接池 Hikari github地址 https://github.com/brettwooldridge/HikariCP \ No newline at end of file diff --git a/spring-boot/spring-boot-rabbitmq/README.md b/spring-boot/spring-boot-rabbitmq/README.md new file mode 100644 index 0000000..942df79 --- /dev/null +++ b/spring-boot/spring-boot-rabbitmq/README.md @@ -0,0 +1,390 @@ +# spring boot 整合 rabbitmq + +## 一、 项目结构说明 + +1.1 之前关于spring 整合 rabbitmq 我们采用的是单项目的方式,为了使得用例更具有实际意义,这里采用maven多模块的构建方式,在spring-boot-rabbitmq下构建三个子模块: + +1. rabbitmq-common 是公共模块,用于存放公共的接口、配置和bean,被rabbitmq-producer和rabbitmq-consumer在pom.xml中引用; +2. rabbitmq-producer 是消息的生产者模块; +3. rabbitmq-consumer是消息的消费者模块。 + +1.2 关于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),里面有详细的配图说明。 + +![spring-scheduling](D:\spring-samples-for-all\pictures\spring-boot-rabbitmq.png) + + + +## 二、关键依赖 + +在父工程的项目中统一导入依赖rabbitmq的starter(spring-boot-starter-amqp),父工程的pom.xml如下 + +```xml + + + + 4.0.0 + pom + + + rabbitmq-consumer + rabbitmq-producer + rabbitmq-common + + + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + com.heibaiying + spring-boot-rabbitmq + 0.0.1-SNAPSHOT + spring-boot-rabbitmq + RabbitMQ project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-amqp + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + +``` + + + +## 三、公共模块(rabbitmq-common) + +- bean 下为公共的实体类。 +- constant 下为公共配置,用静态常量引用。(这里我使用静态常量是为了方便引用,实际中也可以按照情况,抽取为公共配置文件) + +![spring-scheduling](D:\spring-samples-for-all\pictures\rabbitmq-common.png) + +```java +package com.heibaiying.constant; + +/** + * @author : heibaiying + * @description : rabbit 公用配置信息 + */ +public class RabbitInfo { + + // queue 配置 + public static final String QUEUE_NAME = "spring.boot.simple.queue"; + public static final String QUEUE_DURABLE = "true"; + + // exchange 配置 + public static final String EXCHANGE_NAME = "spring.boot.simple.exchange"; + public static final String EXCHANGE_TYPE = "topic"; + + // routing key + public static final String ROUTING_KEY = "springboot.simple.*"; +} + +``` + + + +## 四、服务消费者(rabbitmq-consumer) + +![boot-dubbo-consumer](D:\spring-samples-for-all\pictures\rabbitmq-consumer.png) + +#### 4.1 消息消费者配置 + +```yaml +spring: + rabbitmq: + addresses: 127.0.0.1:5672 + # RabbitMQ 默认的用户名和密码都是guest 而虚拟主机名称是 "/" + # 如果配置其他虚拟主机地址,需要预先用管控台或者图形界面创建 图形界面地址 http://主机地址:15672 + username: guest + password: guest + virtual-host: / + listener: + simple: + # 为了保证信息能够被正确消费,建议签收模式设置为手工签收,并在代码中实现手工签收 + acknowledge-mode: manual + # 侦听器调用者线程的最小数量 + concurrency: 10 + # 侦听器调用者线程的最大数量 + max-concurrency: 50 +``` + +#### 4.2 使用注解@RabbitListener和@RabbitHandler创建消息监听者 + +1. 使用注解创建的交换机、队列、和绑定关系会在项目初始化的时候自动创建,但是不会重复创建; +2. 这里我们创建两个消息监听器,分别演示消息是基本类型和消息是对象时的配置区别。 + +```java +/** + * @author : heibaiying + * @description : 消息是对象的消费者 + */ + +@Component +@Slf4j +public class RabbitmqBeanConsumer { + + @RabbitListener(bindings = @QueueBinding( + value = @Queue(value = RabbitBeanInfo.QUEUE_NAME, durable = RabbitBeanInfo.QUEUE_DURABLE), + exchange = @Exchange(value = RabbitBeanInfo.EXCHANGE_NAME, type = RabbitBeanInfo.EXCHANGE_TYPE), + key = RabbitBeanInfo.ROUTING_KEY) + ) + @RabbitHandler + public void onMessage(@Payload Programmer programmer, @Headers Map headers, Channel channel) throws Exception { + log.info("programmer:{} ", programmer); + Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG); + channel.basicAck(deliveryTag, false); + } +} +``` + +```java +@Component +@Slf4j +public class RabbitmqConsumer { + + @RabbitListener(bindings = @QueueBinding( + value = @Queue(value = RabbitInfo.QUEUE_NAME, durable = RabbitInfo.QUEUE_DURABLE), + exchange = @Exchange(value = RabbitInfo.EXCHANGE_NAME, type = RabbitInfo.EXCHANGE_TYPE), + key = RabbitInfo.ROUTING_KEY) + ) + @RabbitHandler + public void onMessage(Message message, Channel channel) throws Exception { + MessageHeaders headers = message.getHeaders(); + // 获取消息头信息和消息体 + log.info("msgInfo:{} ; payload:{} ", headers.get("msgInfo"), message.getPayload()); + // DELIVERY_TAG 代表 RabbitMQ 向该Channel投递的这条消息的唯一标识ID,是一个单调递增的正整数 + Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG); + // 第二个参数代表是否一次签收多条,当该参数为 true 时,则可以一次性确认 DELIVERY_TAG 小于等于传入值的所有消息 + channel.basicAck(deliveryTag, false); + } + +} +``` + + + +## 五、 消息生产者(rabbitmq-producer) + +![spring-scheduling](D:\spring-samples-for-all\pictures\rabbitmq-producer.png) + +#### 5.1 消息生产者配置 + +```yaml +spring: + rabbitmq: + addresses: 127.0.0.1:5672 + # RabbitMQ 默认的用户名和密码都是guest 而虚拟主机名称是 "/" + # 如果配置其他虚拟主机地址,需要预先用管控台或者图形界面创建 图形界面地址 http://主机地址:15672 + username: guest + password: guest + virtual-host: / + # 是否启用发布者确认 具体确认回调实现见代码 + publisher-confirms: true + # 是否启用发布者返回 具体返回回调实现见代码 + publisher-returns: true + # 是否启用强制消息 保证消息的有效监听 + template.mandatory: true + +server: + port: 8090 +``` + +#### 5.2 创建消息生产者 + +```java +/** + * @author : heibaiying + * @description : 消息生产者 + */ +@Component +@Slf4j +public class RabbitmqProducer { + + @Autowired + private RabbitTemplate rabbitTemplate; + + public void sendSimpleMessage(Map headers, Object message, + String messageId, String exchangeName, String key) { + // 自定义消息头 + MessageHeaders messageHeaders = new MessageHeaders(headers); + // 创建消息 + Message msg = MessageBuilder.createMessage(message, messageHeaders); + /* 确认的回调 确认消息是否到达 Broker 服务器 其实就是是否到达交换器 + 如果发送时候指定的交换器不存在 ack就是false 代表消息不可达 */ + rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { + log.info("correlationData:{} , ack:{}", correlationData.getId(), ack); + if (!ack) { + System.out.println("进行对应的消息补偿机制"); + } + }); + /* 消息失败的回调 + * 例如消息已经到达交换器上,但路由键匹配任何绑定到该交换器的队列,会触发这个回调,此时 replyText: NO_ROUTE + */ + rabbitTemplate.setReturnCallback((message1, replyCode, replyText, exchange, routingKey) -> { + log.info("message:{}; replyCode: {}; replyText: {} ; exchange:{} ; routingKey:{}", + message1, replyCode, replyText, exchange, routingKey); + }); + // 在实际中ID 应该是全局唯一 能够唯一标识消息 消息不可达的时候触发ConfirmCallback回调方法时可以获取该值,进行对应的错误处理 + CorrelationData correlationData = new CorrelationData(messageId); + rabbitTemplate.convertAndSend(exchangeName, key, msg, correlationData); + } +} +``` + +#### 5.3 以单元测试的方式发送消息 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class RabbitmqProducerTests { + + @Autowired + private RabbitmqProducer producer; + + /*** + * 发送消息体为简单数据类型的消息 + */ + @Test + public void send() { + Map heads = new HashMap<>(); + heads.put("msgInfo", "自定义消息头信息"); + // 模拟生成消息ID,在实际中应该是全局唯一的 消息不可达时候可以在setConfirmCallback回调中取得,可以进行对应的重发或错误处理 + String id = String.valueOf(Math.round(Math.random() * 10000)); + producer.sendSimpleMessage(heads, "hello Spring", id, RabbitInfo.EXCHANGE_NAME, "springboot.simple.abc"); + } + + + /*** + * 发送消息体为bean的消息 + */ + @Test + public void sendBean() { + String id = String.valueOf(Math.round(Math.random() * 10000)); + Programmer programmer = new Programmer("xiaoMing", 12, 12123.45f, new Date()); + producer.sendSimpleMessage(null, programmer, id, RabbitBeanInfo.EXCHANGE_NAME, RabbitBeanInfo.ROUTING_KEY); + } + +} +``` + + + +## 六、项目构建的说明 + +因为在项目中,consumer和producer模块均依赖公共模块,所以在构建consumer和producer项目前需要将common 模块安装到本地仓库,**依次**对**父工程**和**common模块**执行: + +```shell +mvn install -Dmaven.test.skip = true +``` + +consumer中 pom.xml如下 + +```xml + + + 4.0.0 + + + com.heibaiying + spring-boot-rabbitmq + 0.0.1-SNAPSHOT + + + rabbitmq-consumer + 0.0.1-SNAPSHOT + rabbitmq-consumer + RabbitMQ consumer project for Spring Boot + + + 1.8 + + + + + com.heibaiying + rabbitmq-common + 0.0.1-SNAPSHOT + compile + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` + +producer中 pom.xml如下 + +```xml + + + 4.0.0 + + + com.heibaiying + spring-boot-rabbitmq + 0.0.1-SNAPSHOT + + + rabbitmq-producer + 0.0.1-SNAPSHOT + rabbitmq-producer + RabbitMQ producer project for Spring Boot + + + 1.8 + + + + + com.heibaiying + rabbitmq-common + 0.0.1-SNAPSHOT + compile + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + +``` diff --git a/spring-boot/spring-boot-redis/README.md b/spring-boot/spring-boot-redis/README.md new file mode 100644 index 0000000..34d1a6d --- /dev/null +++ b/spring-boot/spring-boot-redis/README.md @@ -0,0 +1,444 @@ +# spring boot 整合 redis + +## 一、说明 + +#### 1.1 项目结构 + +1. RedisConfig.java实现了redisTemplate 序列化与反序列化的配置; +2. RedisOperation和RedisObjectOperation分别封装了对基本类型和对象的操作。 + +![spring-boot-servlet](D:\spring-samples-for-all\pictures\spring-boot-redis.png) + +#### 1.2 项目主要依赖 + +```xml + + + org.springframework.boot + spring-boot-starter-data-redis + + + + com.fasterxml.jackson.core + jackson-databind + 2.9.8 + +``` + + + +## 二、整合 Redis + +#### 2.1 在application.yml 中配置redis数据源 + +```yaml +spring: + redis: + host: 127.0.0.1 + port: 6379 + # 默认采用的也是 0 号数据库 redis官方在4.0之后版本就不推荐采用单节点多数据库(db1-db15)的方式存储数据,如果有需要应该采用集群方式构建 + database: 0 + +# 如果是集群节点 采用如下配置指定节点 +#spring.redis.cluster.nodes +``` + +#### 2.2 封装redis基本操作 + +需要说明的是spring boot 提供了两个template 用于操作redis: + +- StringRedisTemplate:由于redis在大多数使用情况下都是操作字符串类型的存储,所以spring boot 将对字符串的操作单独封装在StringRedisTemplate ; +- RedisTemplate :redis 用于操作任意类型的template。 + +```java +/** + * @author : heibaiying + * @description : redis 基本操作 + */ + +@Component +public class RedisOperation { + + @Autowired + private StringRedisTemplate redisTemplate; + + /*** + * 操作普通字符串 + */ + public void StringSet(String key, String value) { + ValueOperations valueOperations = redisTemplate.opsForValue(); + valueOperations.set(key, value); + } + + /*** + * 操作列表 + */ + public void ListSet(String key, List values) { + ListOperations listOperations = redisTemplate.opsForList(); + values.forEach(value -> listOperations.leftPush(key, value)); + } + + /*** + * 操作集合 + */ + public void SetSet(String key, Set values) { + SetOperations setOperations = redisTemplate.opsForSet(); + values.forEach(value -> setOperations.add(key, value)); + } + + /*** + * 获取字符串 + */ + public String StringGet(String key) { + ValueOperations valueOperations = redisTemplate.opsForValue(); + return valueOperations.get(key); + } + + /*** + * 列表弹出元素 + */ + public String ListLeftPop(String key) { + ListOperations listOperations = redisTemplate.opsForList(); + return listOperations.leftPop(key, 2, TimeUnit.SECONDS); + } + + /*** + * 集合弹出元素 + */ + public String SetPop(String key) { + SetOperations setOperations = redisTemplate.opsForSet(); + return setOperations.pop(key); + } + +} +``` + +```java +/** + * @author : heibaiying + * @description : redis 基本操作 + */ + +@Component +public class RedisObjectOperation { + + @Autowired + private RedisTemplate objectRedisTemplate; + + /*** + * 操作对象 + */ + public void ObjectSet(Object key, Object value) { + ValueOperations valueOperations = objectRedisTemplate.opsForValue(); + valueOperations.set(key, value); + } + + /*** + * 操作元素为对象列表 + */ + public void ListSet(Object key, List values) { + ListOperations listOperations = objectRedisTemplate.opsForList(); + values.forEach(value -> listOperations.leftPush(key, value)); + } + + /*** + * 操作元素为对象集合 + */ + public void SetSet(Object key, Set values) { + SetOperations setOperations = objectRedisTemplate.opsForSet(); + values.forEach(value -> setOperations.add(key, value)); + } + + /*** + * 获取对象 + */ + public Object ObjectGet(Object key) { + ValueOperations valueOperations = objectRedisTemplate.opsForValue(); + return valueOperations.get(key); + } + + /*** + * 列表弹出元素 + */ + public Object ListLeftPop(Object key) { + ListOperations listOperations = objectRedisTemplate.opsForList(); + return listOperations.leftPop(key, 2, TimeUnit.SECONDS); + } + + /*** + * 集合弹出元素 + */ + public Object SetPop(Object key) { + SetOperations setOperations = objectRedisTemplate.opsForSet(); + return setOperations.pop(key); + } + +} +``` + +#### 2.3 redisTemplate 序列化为json格式与反序列化 + +这里需要说明的spring boot 的 redisTemplate 本身是实现了对象的序列化与反序列化的,是支持直接存取对象的。但是这里的序列化默认采用的是JdkSerializationRedisSerializer.serialize()序列化为二进制码,这个本身是不影响redisTemplate 的操作的,因为redisTemplate在取出数据的时候会自动进行反序列化。 + +但是如果我们在命令行中使用get命令去获取数据时候,得到的是一串不直观的二进制码,所以我们尽量将其序列化为直观的json格式存储。 + +```java +/** + * @author : heibaiying + * @description : 自定义序列化器 + * 不定义的话默认采用的是serializer.JdkSerializationRedisSerializer.serialize()序列化为二进制字节码 存储在数据库中不直观 + */ +@Configuration +public class RedisConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + + // 使用Jackson2JsonRedisSerialize 需要导入依赖 com.fasterxml.jackson.core jackson-databind + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + + ObjectMapper objectMapper = new ObjectMapper(); + // 第一个参数表示: 表示所有访问者都受到影响 包括 字段, getter / isGetter,setter,creator + // 第二个参数表示: 所有类型的访问修饰符都是可接受的,不论是公有还有私有表变量都会被序列化 + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + + // 设置key,value 序列化规则 + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } +} +``` + +#### 2.4 测试 + +```java +@RunWith(SpringRunner.class) +@SpringBootTest +public class RedisTests { + + @Autowired + private RedisOperation redisOperation; + + @Test + public void StringOperation() { + redisOperation.StringSet("hello", "redis"); + String s = redisOperation.StringGet("hello"); + Assert.assertEquals(s, "redis"); + } + + @Test + public void ListOperation() { + redisOperation.ListSet("skill", Arrays.asList("java", "oracle", "vue")); + String s = redisOperation.ListLeftPop("skill"); + Assert.assertEquals(s, "vue"); + } + + /* + * 需要注意的是Redis的集合(set)不仅不允许有重复元素,并且集合中的元素是无序的, + * 不能通过索引下标获取元素。哪怕你在java中传入的集合是有序的newLinkedHashSet,但是实际在Redis存储的还是无序的集合 + */ + @Test + public void SetOperation() { + redisOperation.SetSet("skillSet", Sets.newLinkedHashSet("java", "oracle", "vue")); + String s = redisOperation.SetPop("skillSet"); + Assert.assertNotNull(s); + } + +} +``` + +
+ +## 附:Redis的数据结构和操作命令 + +### 1.1 预备 + +#### 1.1.1 全局命令 + +1. 查看所有键: **keys \*** + +2. 查看键总数:**dbsize** + +3. 检查键是否存在:**exists key** + +4. 删除键:**del key [key ...]** 支持删除多个键 + +5. 键过期:**expire key seconds** + + ttl命令会返回键的剩余过期时间, 它有3种返回值: + + - 大于等于0的整数: 键剩余的过期时间。 + - -1: 键没设置过期时间。 + - -2: 键不存在 + +6. 键的数据结构 **type key** + +#### 1.1.2 数据结构和内部编码 + +type命令实际返回的就是当前键的数据结构类型, 它们分别是:**string**(字符串) 、 **hash**(哈希) 、 **list**(列表) 、 **set**(集合) 、 **zset**(有序集合) + +#### 1.1.3 单线程架构 + +1. 纯内存访问, Redis将所有数据放在内存中, 内存的响应时长大约为100纳秒, 这是Redis达到每秒万级别访问的重要基础。 +2. 非阻塞I/O, Redis使用epoll作为I/O多路复用技术的实现, 再加上Redis自身的事件处理模型将epoll中的连接、 读写、 关闭都转换为事件, 不在网络I/O上浪费过多的时间, 如图2-6所示。 +3. 单线程避免了线程切换和竞态产生的消耗。 + +### 1.2 字符串 + +| 作用 | 格式 | 参数或示例 | +| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 设置值 | set key value \[ex seconds]\[px milliseconds][nx\|xx] setnx setex | ex seconds: 为键设置秒级过期时间。
px milliseconds: 为键设置毫秒级过期时间。
nx: 键必须不存在, 才可以设置成功, 用于添加。
xx: 与nx相反, 键必须存在, 才可以设置成功, 用于更新。 | +| 获取值 | get key | r如果获取的键不存在 ,则返回nil(空) | +| 批量设置 | mset key value [key value ...] | mset a 1 b 2 c 3 d 4 | +| 批量获取值 | mget key [key ...] | mget a b c d | +| 计数 | incr key decr key incrby key increment(指定数值自增)
decrby key decrement(指定数值自减)
incrbyfloat key increment (浮点数自增) | 值不是整数, 返回错误。 值是整数, 返回自增或自减后的结果。
键不存在,创建键,并按照值为0自增或自减, 返回结果为1。 | +| 追加值 | append key value | 向字符串的默认追加值 | +| 字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 | +| 设置并返回原值 | getset key value | | +| 设置指定位置的租字符串 | setrange key offeset value | | +| 获取部分字符串 | getrange key start end | | + +### 1.3 哈希 + +| 作用 | 格式 | 参数或示例 | +| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 设置值 | hset key field value | hset user:1 name tom
hset user:1 age 12 | +| 获取值 | hget key field | hget user:1 name | +| 删除field | hdel key field [field ...] | | +| 计算field个数 | hlen key | | +| 批量设置或获取field-value | hmget key field [field]
hmset key field value [field value...] | hmset user:1 name mike age 12 city tianjin
hmget user:1 name city | +| 判断field是否存在 | hexists key field | | +| 获取所有field | hkeys key | | +| 获取所有value | hvals key | | +| 获取所有的filed-value | hgetall key | 如果哈希元素个数比较多, 会存在阻塞Redis的可能。
获取全部 可以使用hscan命令, 该命令会渐进式遍历哈希类型 | +| 计数 | hincrby key field
hincrbyfloat key field | | + +### 1.4 列表 + +| 作用 | 格式 | 参数或示例 | +| -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 增 | 左侧插入:lpush key value [value ...] 右侧插入:rpush key value [value ...] 某个指定元素前后插入:linsert key before\|after pivot value | | +| 查 | 获取指定范围内的元素列表:lrange key start end 获取列表指定索引下标的元素:lindex key index 获取列表指定长度:llen key | lrange listkey 0 -1 | +| 删 | 从列表左侧弹出元素:lpop key 从列表右侧弹出元素:rpop key 删除指定元素:lrem key count value 截取列表:ltrim key start end | count>0, 从左到右, 删除最多count个元素。
count<0, 从右到左, 删除最多count绝对值个元素。
count=0, 删除所有 | +| 改 | 修改指定索引下标的元素:lset key index newValue | | +| 阻塞操作 | blpop key [key ...] timeout brpop key [key ...] timeout | key[key...]: 多个列表的键。 timeout: 阻塞时间\|等待时间(单位: 秒) | + + + +### 1.5 集合 + +集合(set) 类型也是用来保存多个的字符串元素, 但和列表类型不一样的是, **集合中不允许有重复元素**, 并且集合中的元素是无序的, **不能通过索引下标获取元素**。 + +**集合内操作**: + +| 作用 | 格式 | 参数或示例 | +| -------------------- | ------------------------------ | ----------------------------------------- | +| 添加元素 | sadd key element [element ...] | 返回结果为添加成功的元素个数 | +| 删除元素 | srem key element [element ...] | 返回结果为成功删除的元素个数 | +| 计算元素个数 | scard key | | +| 判断元素是否在集合中 | sismember key element | | +| 随机返回 | srandmember key [count] | 随机从集合返回指定个数元素,count 默认为1 | +| 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素,spop 会 | +| 获取集合中所有元素 | smembers key | 可用sscan 代替 | + +**集合间操作**: + +| 作用 | 格式 | +| ---------------------------- | ------------------------------------------------------------ | +| 求多个集合的交集 | sinter key [key ...] | +| 求多个集合的并集 | suinon key [key ...] | +| 求多个集合的差集 | sdiff key [key ...] | +| 将交集、并集、差集的结果保存 | sinterstore destination key [key ...]
suionstore destination key [key ...]
sdiffstore destination key [key ...] | + +### 1.6 有序集合 + +有序集合中的元素可以排序。 但是它和列表使用索引下标作为排序依据不同的是, 它给每个元素设置一个分数(score) 作为排序的依据。 + +**集合内操作**: + +| 作用 | 格式 | 参数或示例 | +| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 添加成员 | zadd key score member [score member ...] | nx: member必须不存在, 才可设置成功, 用于添加。
xx: member必须存在, 才可以设置成功, 用于更新。
ch: 返回此次操作后, 有序集合元素和分数发生变化的个数
incr: 对score做增加, 相当于后面介绍的zincrby。 | +| 计算成员个数 | zcard key | | +| 计算某个成员的分数 | zscore key member | | +| 计算某个成员的排名 | zrank key member zrevrank key member | zrank是从分数从低到高返回排名, zrevrank反之。 | +| 删除成员 | zrem key member [member ...] | | +| 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom | +| 返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange是从低到高返回, zrevrange反之。 | +| 返回指定分数范围内的成员 | zrangebyscore key min max \[withscores][limit offset count] zrevrangebyscore key max min \[withscores][limit offset count] | 其中zrangebyscore按照分数从低到高返回, zrevrangebyscore反之。 [limit offset count]选项可以限制输出的起始位置和个数: 同时min和max还支持开区间(小括号) 和闭区间(中括号) , -inf和+inf分别代表无限小和无限大 | +| 删除指定排名内的升序元素 | zremrangerank key start end | | +| 删除指定分数范围的成员 | zremrangebyscore key min max | | + +**集合间操作**: + +| 作用 | 格式 | +| ---- | ------------------------------------------------------------ | +| 交集 | zinterstore destination numkeys key \[key ...] [weights weight [weight ...]] \[aggregate sum\|min\|max] | +| 并集 | zunionstore destination numkeys key \[key ...] [weights weight [weight ...]] \[aggregate sum\|min\|max] | + +- destination: 交集计算结果保存到这个键。 +- numkeys: 需要做交集计算键的个数。 +- key[key...]: 需要做交集计算的键。 +- weights weight[weight...]: 每个键的权重, 在做交集计算时, 每个键中的每个member会将自己分数乘以这个权重, 每个键的权重默认是1。 +- aggregate sum|min|max: 计算成员交集后, 分值可以按照sum(和) 、min(最小值) 、 max(最大值) 做汇总, 默认值是sum。 + +### 1.7 键管理 + +#### 1.7.1 单个键管理 + +##### 1.键重命名 + +**rename key newkey** + + 为了防止被强行rename, Redis提供了renamenx命令, 确保只有newKey不存在时候才被覆盖。 + +##### 2. 随机返回键 + + **random key** + +##### 3.键过期 + +- expire key seconds: 键在seconds秒后过期。 +- expireat key timestamp: 键在秒级时间戳timestamp后过期。 +- pexpire key milliseconds: 键在milliseconds毫秒后过期。 +- pexpireat key milliseconds-timestamp键在毫秒级时间戳timestamp后过期 + +注意: + +1. 如果expire key的键不存在, 返回结果为0 +2. 如果设置过期时间为负值, 键会立即被删除, 犹如使用del命令一样 +3. persist key t命令可以将键的过期时间清除 +4. 对于字符串类型键, 执行set命令会去掉过期时间, 这个问题很容易在开发中被忽视 +5. Redis不支持二级数据结构(例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置 +6. setex命令作为set+expire的组合, 不但是原子执行, 同时减少了一次网络通讯的时间 + +#### 1.7.2 键遍历 + +##### 1. 全量键遍历 + +**keys pattern** + +##### 2. 渐进式遍历 + +scan cursor \[match pattern] \[count number] + +- cursor是必需参数, 实际上cursor是一个游标, 第一次遍历从0开始, 每次scan遍历完都会返回当前游标的值, 直到游标值为0, 表示遍历结束。 +- match pattern是可选参数, 它的作用的是做模式的匹配, 这点和keys的模式匹配很像。 +- count number是可选参数, 它的作用是表明每次要遍历的键个数, 默认值是10, 此参数可以适当增大。 + +#### 1.7.3 数据库管理 + +##### 1.切换数据库 + +**select dbIndex** + +##### 2.flushdb/flushall + +flushdb/flushall命令用于清除数据库, 两者的区别的是flushdb只清除当前数据库, flushall会清除所有数据库。 \ No newline at end of file diff --git a/spring-boot/spring-boot-redis/src/main/resources/application.yml b/spring-boot/spring-boot-redis/src/main/resources/application.yml index a69b90f..c914396 100644 --- a/spring-boot/spring-boot-redis/src/main/resources/application.yml +++ b/spring-boot/spring-boot-redis/src/main/resources/application.yml @@ -2,8 +2,8 @@ spring: redis: host: 127.0.0.1 port: 6379 - # ĬϲõҲ 0 ݿ redisٷ4.0֮汾ͲƼõڵݿ(db1-db15)ķʽ洢ݣҪӦòüȺʽ + # 默认采用的也是 0 号数据库 redis官方在4.0之后版本就不推荐采用单节点多数据库(db1-db15)的方式存储数据,如果有需要应该采用集群方式构建 database: 0 -# ǼȺڵ ָڵ +# 如果是集群节点 采用如下配置指定节点 #spring.redis.cluster.nodes \ No newline at end of file diff --git a/spring-boot/spring-boot-redis/src/test/java/com/heibaiying/springbootredis/RedisObjectTests.java b/spring-boot/spring-boot-redis/src/test/java/com/heibaiying/springbootredis/RedisObjectTests.java index 58be9ab..086449f 100644 --- a/spring-boot/spring-boot-redis/src/test/java/com/heibaiying/springbootredis/RedisObjectTests.java +++ b/spring-boot/spring-boot-redis/src/test/java/com/heibaiying/springbootredis/RedisObjectTests.java @@ -2,7 +2,6 @@ package com.heibaiying.springbootredis; import com.heibaiying.springbootredis.bean.Programmer; import com.heibaiying.springbootredis.redis.RedisObjectOperation; -import com.heibaiying.springbootredis.redis.RedisOperation; import org.assertj.core.util.Sets; import org.junit.Assert; import org.junit.Test; @@ -11,7 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; -import java.time.LocalDate; import java.util.Arrays; import java.util.Date; diff --git a/spring-boot/spring-boot-servlet/README.md b/spring-boot/spring-boot-servlet/README.md new file mode 100644 index 0000000..a772cb9 --- /dev/null +++ b/spring-boot/spring-boot-servlet/README.md @@ -0,0 +1,230 @@ +# spring boot 整合 servlet + +## 一、说明 + +#### 1.1 项目结构说明 + +1. 项目提供与servlet整合的两种方式,一种是servlet3.0 原生的注解方式,一种是采用spring 注册的方式; +2. servlet、过滤器、监听器分别位于servlet、filter、listen 下,其中以Annotation命名结尾的代表是servlet注解方式实现,采用spring注册方式则在ServletConfig中进行注册; +3. 为了说明外置容器对servlet注解的自动发现机制,项目采用外置容器构建,关于spring boot 整合外置容器的详细说明可以参考[spring-boot-tomcat](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-tomcat) + +![spring-boot-servlet](D:\spring-samples-for-all\pictures\spring-boot-servlet.png) + +#### 1.2 项目依赖 + +```xml + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + javax.servlet + servlet-api + 2.5 + provided + + + + javax.servlet + javax.servlet-api + + +``` + +## 二、采用spring 注册方式整合 servlet + +#### 2.1 新建过滤器、监听器和servlet + +```java +/** + * @author : heibaiying + * @description : 自定义过滤器 + */ + +public class CustomFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + request.setAttribute("filterParam","我是filter传递的参数"); + chain.doFilter(request,response); + response.getWriter().append(" CustomFilter "); + } + + @Override + public void destroy() { + + } +} +``` + +```java +/** + * @author : heibaiying + * @description : 自定义监听器 + */ +public class CustomListen implements ServletContextListener { + + //Web应用程序初始化过程正在启动的通知 + @Override + public void contextInitialized(ServletContextEvent sce) { + System.out.println("容器初始化启动"); + } + + + + /* 通知servlet上下文即将关闭 + * 这个地方如果我们使用的是spring boot 内置的容器 是监听不到销毁过程,所以我们使用了外置 tomcat 容器 + */ + @Override + public void contextDestroyed(ServletContextEvent sce) { + System.out.println("容器即将销毁"); + } +} +``` + +```java +/** + * @author : heibaiying + * @description : 自定义servlet + */ +public class CustomServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + System.out.println("doGet 执行:" + req.getAttribute("filterParam")); + resp.getWriter().append("CustomServlet"); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + doGet(req, resp); + } +} +``` + +#### 2.2 注册过滤器、监听器和servlet + +```java +/** + * @author : heibaiying + */ +@Configuration +public class ServletConfig { + + @Bean + public ServletRegistrationBean registrationBean() { + return new ServletRegistrationBean(new CustomServlet(), "/servlet"); + } + + @Bean + public FilterRegistrationBean filterRegistrationBean() { + FilterRegistrationBean bean = new FilterRegistrationBean(); + bean.setFilter(new CustomFilter()); + bean.addUrlPatterns("/servlet"); + return bean; + } + + @Bean + public ServletListenerRegistrationBean listenerRegistrationBean() { + return new ServletListenerRegistrationBean(new CustomListen()); + } + +} +``` + +## 三、采用注解方式整合 servlet + +#### 3.1 新建过滤器、监听器和servlet,分别使用@WebFilter、@WebListener、@WebServlet注解标注 + +```java +/** + * @author : heibaiying + * @description : 自定义过滤器 + */ + +@WebFilter(urlPatterns = "/servletAnn") +public class CustomFilterAnnotation implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + chain.doFilter(request,response); + response.getWriter().append(" CustomFilter Annotation"); + } + + @Override + public void destroy() { + + } +} +``` + +```java +/** + * @author : heibaiying + * @description :自定义监听器 + */ +@WebListener +public class CustomListenAnnotation implements ServletContextListener { + + //Web应用程序初始化过程正在启动的通知 + @Override + public void contextInitialized(ServletContextEvent sce) { + System.out.println("容器初始化启动 Annotation"); + } + + + + /* 通知servlet上下文即将关闭 + * 这个地方如果我们使用的是spring boot 内置的容器 是监听不到销毁过程,所以我们使用了外置 tomcat 容器 + */ + @Override + public void contextDestroyed(ServletContextEvent sce) { + System.out.println("容器即将销毁 Annotation"); + } +} +``` + +```java + +/** + * @author : heibaiying + * @description : 自定义servlet + */ +@WebServlet(urlPatterns = "/servletAnn") +public class CustomServletAnnotation extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.getWriter().append("CustomServlet Annotation"); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + doGet(req, resp); + } +} +``` + +#### 3.2 使注解生效 + +1. 如果是内置容器,需要在启动类上添加@ServletComponentScan("com.heibaiying.springbootservlet") ,指定扫描的包目录; +2. 如果是外置容器,不需要进行任何配置,依靠容器内建的discovery机制自动发现,需要说明的是这里的容器必须支持servlet3.0(tomcat从7.0版本开始支持Servlet3.0)。 \ No newline at end of file diff --git a/spring-boot/spring-boot-servlet/pom.xml b/spring-boot/spring-boot-servlet/pom.xml index 3cb5319..9ce8692 100644 --- a/spring-boot/spring-boot-servlet/pom.xml +++ b/spring-boot/spring-boot-servlet/pom.xml @@ -37,17 +37,16 @@ 2.5 provided - - - org.springframework.boot - spring-boot-starter-test - test - javax.servlet javax.servlet-api + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/spring-boot/spring-boot-servlet/src/main/java/com/heibaiying/springbootservlet/servlet/CustomServletAnnation.java b/spring-boot/spring-boot-servlet/src/main/java/com/heibaiying/springbootservlet/servlet/CustomServletAnnotation.java similarity index 92% rename from spring-boot/spring-boot-servlet/src/main/java/com/heibaiying/springbootservlet/servlet/CustomServletAnnation.java rename to spring-boot/spring-boot-servlet/src/main/java/com/heibaiying/springbootservlet/servlet/CustomServletAnnotation.java index bd4d69e..de6cf62 100644 --- a/spring-boot/spring-boot-servlet/src/main/java/com/heibaiying/springbootservlet/servlet/CustomServletAnnation.java +++ b/spring-boot/spring-boot-servlet/src/main/java/com/heibaiying/springbootservlet/servlet/CustomServletAnnotation.java @@ -12,7 +12,7 @@ import java.io.IOException; * @description : 自定义servlet */ @WebServlet(urlPatterns = "/servletAnn") -public class CustomServletAnnation extends HttpServlet { +public class CustomServletAnnotation extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { diff --git a/spring-boot/spring-boot-tomcat/README.md b/spring-boot/spring-boot-tomcat/README.md new file mode 100644 index 0000000..ba6597d --- /dev/null +++ b/spring-boot/spring-boot-tomcat/README.md @@ -0,0 +1,101 @@ +# spring boot 整合 tomcat + +## 一、说明 + +#### 1.1 项目结构说明 + +spring boot 整合 tomcat 后支持jsp 的使用(内置容器默认是不支持jsp),所以项目整合后采用jspController 跳转到show.jsp测试整合是否成功。 + +![spring-boot-servlet](D:\spring-samples-for-all\pictures\spring-boot-tomcat.png) + +#### 1.2 项目主要依赖 + +```xml + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + javax.servlet + servlet-api + 2.5 + provided + +``` + +## 二、整合 tomcat + +#### 2.1 修改启动类,继承自SpringBootServletInitializer,并覆盖重写其中configure方法 + +```java +/** + * 如果用外置tomcat,启动报错java.lang.NoClassDefFoundError: javax/el/ELManager + * 是因为tomcat 7.0 el-api包中没有ELManager类 , 切换tomcat 为8.0 以上版本即可 + */ +@SpringBootApplication +public class SpringBootTomcatApplication extends SpringBootServletInitializer { + + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + //传入SpringBoot应用的主程序 + return application.sources(SpringBootTomcatApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(SpringBootTomcatApplication.class, args); + } + +} +``` + +#### 2.2 在application.yml 中指定访问视图文件的前缀和后缀 + +```yml +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp +``` + +#### 2.3 新建controller和show.jsp 测试整合是否成功 + +```java +@Controller +@RequestMapping("index") +public class JspController { + + @RequestMapping + public String jsp(Model model){ + Programmer programmer = new Programmer("heibai", 21, 1298.31f, LocalDate.now()); + model.addAttribute("programmer",programmer); + return "show"; + } +} +``` + +```jsp +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + programmer + + + +
    +
  • 姓名: ${programmer.name}
  • +
  • 年龄: ${programmer.age}
  • +
+ + +``` + diff --git a/spring-boot/spring-boot-websocket/README.md b/spring-boot/spring-boot-websocket/README.md new file mode 100644 index 0000000..6682892 --- /dev/null +++ b/spring-boot/spring-boot-websocket/README.md @@ -0,0 +1,183 @@ +# spring boot websocket + +## 一、说明 + +### 1.1 项目结构说明 + +1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候将临时用户名存储在session当中; +2. 关于websocket的主要配置在websocket文件夹下; +3. 模板引擎采用freemaker; +4. 项目以web的方式构建。 + +![spring-scheduling](D:\spring-samples-for-all\pictures\spring-boot-websocket.png) + + + +### 1.2 主要依赖 + +```xml + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework.boot + spring-boot-starter-freemarker + +``` + + + +## 二、spring boot websocket + +#### 2.1 创建消息处理类ChatSocket,使用@ServerEndpoint声明websocket服务 + +```java +@ServerEndpoint(value = "/socket/{username}") +@Component +public class ChatSocket { + + /** + * 建立连接时候触发 + */ + @OnOpen + public void onOpen(Session session, @PathParam("username") String username) { + // 这个方法是线程不安全的 + Constant.nameAndSession.putIfAbsent(username, session); + } + + + /** + * 关闭连接时候触发 + */ + @OnClose + public void onClose(Session session, @PathParam("username") String username) { + Constant.nameAndSession.remove(username); + } + + /** + * 处理消息 + */ + @OnMessage + public void onMessage(Session session, String message, @PathParam("username") String username) throws UnsupportedEncodingException { + // 防止中文乱码 + String msg = URLDecoder.decode(message, "utf-8"); + // 简单模拟群发消息 + Constant.nameAndSession.forEach((s, webSocketSession) + -> { + try { + webSocketSession.getBasicRemote().sendText(username + " : " + msg); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } +} + +``` + +#### 2.2 配置ServerEndpointExporter,ServerEndpointExporter会在运行时候自动注册我们用@ServerEndpoint声明的websocket服务。 + +```java +@Configuration +public class WebSocketConfig { + + /*** + * 检测{@link javax.websocket.server.ServerEndpointConfig}和{@link ServerEndpoint} 类型的bean, + * 并在运行时使用标准Java WebSocket时注册。 + * 我们在{@link com.heibaiying.springboot.websocket.WebSocketConfig}中就是使用@ServerEndpoint去声明websocket服务 + */ + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } +} +``` + +#### 2.3 前端websocket的实现 + +```jsp + + + + ${Session["username"]}您好!欢迎进入群聊大厅! + + +
${Session["username"]}您好!欢迎进入群聊大厅!
+ + +
+ +
+ + + + +``` + +#### 2.4 简单登录的实现 + +```java + + + + Title + + +
+ + +
+ + +``` + +```java +/** + * @description : 简单登录 + */ +@Controller +public class LoginController { + + @PostMapping("login") + public String login(String username, HttpSession session) { + session.setAttribute(Constant.USER_NAME, username); + return "chat"; + } + + @GetMapping + public String index() { + return "index"; + } +} +``` + diff --git a/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/constant/Constant.java b/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/constant/Constant.java index 4a3b0a8..529eb1d 100644 --- a/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/constant/Constant.java +++ b/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/constant/Constant.java @@ -4,11 +4,7 @@ import javax.websocket.Session; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -/** - * @author : 罗祥 - * @description : - * @date :create in 2018/12/27 - */ + public interface Constant { String USER_NAME="username"; diff --git a/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/controller/LoginController.java b/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/controller/LoginController.java index 582497f..868311f 100644 --- a/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/controller/LoginController.java +++ b/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/controller/LoginController.java @@ -8,9 +8,7 @@ import org.springframework.web.bind.annotation.PostMapping; import javax.servlet.http.HttpSession; /** - * @author : 罗祥 * @description : 简单登录 - * @date :create in 2018/12/27 */ @Controller public class LoginController { diff --git a/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/websocket/ChatSocket.java b/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/websocket/ChatSocket.java index 4080827..40474bf 100644 --- a/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/websocket/ChatSocket.java +++ b/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/websocket/ChatSocket.java @@ -21,20 +21,28 @@ import java.net.URLDecoder; @Component public class ChatSocket { - - @OnOpen //建立连接时候触发 + /** + * 建立连接时候触发 + */ + @OnOpen public void onOpen(Session session, @PathParam("username") String username) { // 这个方法是线程不安全的 Constant.nameAndSession.putIfAbsent(username, session); } - @OnClose //关闭连接时候触发 + /** + * 关闭连接时候触发 + */ + @OnClose public void onClose(Session session, @PathParam("username") String username) { Constant.nameAndSession.remove(username); } - @OnMessage //处理消息 + /** + * 处理消息 + */ + @OnMessage public void onMessage(Session session, String message, @PathParam("username") String username) throws UnsupportedEncodingException { // 防止中文乱码 String msg = URLDecoder.decode(message, "utf-8"); diff --git a/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/websocket/WebSocketConfig.java b/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/websocket/WebSocketConfig.java index 4bba5dd..4e97831 100644 --- a/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/websocket/WebSocketConfig.java +++ b/spring-boot/spring-boot-websocket/src/main/java/com/heibaiying/springboot/websocket/WebSocketConfig.java @@ -4,12 +4,19 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; +import javax.websocket.server.ServerEndpoint; + /** * @author : heibaiying */ @Configuration public class WebSocketConfig { + /*** + * 检测{@link javax.websocket.server.ServerEndpointConfig}和{@link ServerEndpoint} 类型的bean, + * 并在运行时使用标准Java WebSocket时注册。 + * 我们在{@link com.heibaiying.springboot.websocket.WebSocketConfig}中就是使用@ServerEndpoint去声明websocket服务 + */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); diff --git a/spring-boot/spring-boot-yml-profile/README.md b/spring-boot/spring-boot-yml-profile/README.md new file mode 100644 index 0000000..937d48d --- /dev/null +++ b/spring-boot/spring-boot-yml-profile/README.md @@ -0,0 +1,247 @@ +# spring-boot 基础 + +## 一、说明 + +#### 1.1 项目结构说明 + +1. 本项目搭建一个简单的hello spring 的 web工程,简单说明spring-boot 的开箱即用的特性; +2. 模板引擎采用freemaker 和 thymeleaf 作为示例,分别对应模板文件makershow.ftl 和 leafShow.html; +3. spring boot 2.x 默认是不支持jsp的,需要额外的配置,关于使用jsp的整合可以参考spring-boot-jsp项目。 + +![spring-boot-base](D:\spring-samples-for-all\pictures\spring-boot-base.png) + +#### 1.2 项目依赖 + +导入相关的starter(启动器) + +```xml + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + com.heibaiying + spring-boot-base + 0.0.1-SNAPSHOT + spring-boot-base + Demo project for Spring Boot + + + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-freemarker + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + +``` + +1. spring boot 项目默认继承自spring-boot-starter-parent,而spring-boot-starter-parent继承自spring-boot-dependencies, spring-boot-dependencies中定义了关于spring boot 依赖的各种jar包的版本,是spring boot 的版本管理中心。 + +![spring-boot-dependencies](D:\spring-samples-for-all\pictures\spring-boot-dependencies.png) + +2. 关于spring boot 2.x官方支持的所有starter 可以参见官方文档 [Table 13.1. Spring Boot application starters](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#using-boot-starter) + + + +## 二、spring boot 主启动类 + + 如果采用IDEA 或者 Spring Tool Suite (STS) 等开发工具创建的spring boot 工程,会默认创建启动类,如果没有创建,需要手动创建启动类 + +```java +package com.heibaiying.springbootbase; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootBaseApplication { + + // 启动类默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase + public static void main(String[] args) { + SpringApplication.run(SpringBootBaseApplication.class, args); + } + +} +``` + +@SpringBootApplication 注解是一个复合注解,里面包含了@ComponentScan注解,默认开启包扫描,扫描与主程序所在包及其子包,对于本工程而言 默认扫描 com.heibaiying.springbootbase + +```java +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@SpringBootConfiguration +@EnableAutoConfiguration +@ComponentScan(excludeFilters = { + @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), + @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) +public @interface SpringBootApplication { + ... +} +``` + + + +## 三、开箱即用的web工程 + +在springbootBaseApplication.java 的同级目录创建controller文件夹,并在其中创建RestfulController.java,启动项目访问localhost:8080/restful/programmers 即可看到项目搭建成功。 + +```java +/** + * @author : heibaiying + * @description : restful 控制器 + */ +@RestController +@RequestMapping("restful") +public class RestfulController { + + @GetMapping("programmers") + private List getProgrammers() { + List programmers = new ArrayList<>(); + programmers.add(new Programmer("xiaoming", 12, 100000.00f, LocalDate.of(2019, Month.AUGUST, 2))); + programmers.add(new Programmer("xiaohong", 23, 900000.00f, LocalDate.of(2013, Month.FEBRUARY, 2))); + return programmers; + } +} +``` + +这里之所以能够开箱即用,是因为我们在项目中导入spring-boot-starter-web启动器,而@SpringBootApplication 复合注解中默认开启了@EnableAutoConfiguration注解允许开启自动化配置,spring在检查导入starter-web的依赖后就会开启web的自动化配置。 + + + +## 四、模板引擎 + +这里我们在一个项目中同时导入了freemaker 和 thymeleaf的starter(虽然并不推荐,但是在同一个项目中是可以混用这两种模板引擎的)。 + +#### 4.1 freemarker + +```java +/** + * @author : heibaiying + * @description : 跳转渲染模板引擎 默认模板的存放位置为classpath:templates + */ +@Controller +@RequestMapping("freemarker") +public class FreeMarkerController { + + @RequestMapping("show") + private String programmerShow(ModelMap modelMap){ + List programmerList=new ArrayList<>(); + programmerList.add(new Programmer("xiaoming",12,100000.00f,LocalDate.of(2019,Month.AUGUST,2))); + programmerList.add(new Programmer("xiaohong",23,900000.00f,LocalDate.of(2013,Month.FEBRUARY,2))); + modelMap.addAttribute("programmers",programmerList); + return "markerShow"; + } +} + +``` + +```html + + + + + freemarker模板引擎 + + +
    + <#list programmers as programmer> +
  • 姓名: ${programmer.name} 年龄: ${programmer.age}
  • + +
+ + +``` + +#### 4.2 thymeleaf + +```java +/** + * @author : heibaiying + * @description : 跳转渲染模板引擎 默认模板的存放位置为classpath:templates + */ +@Controller +@RequestMapping("thymeleaf") +public class ThymeleafController { + + @RequestMapping("show") + private String programmerShow(ModelMap modelMap) { + List programmerList = new ArrayList<>(); + programmerList.add(new Programmer("xiaoming", 12, 100000.00f, LocalDate.of(2019, Month.AUGUST, 2))); + programmerList.add(new Programmer("xiaohong", 23, 900000.00f, LocalDate.of(2013, Month.FEBRUARY, 2))); + modelMap.addAttribute("programmers", programmerList); + return "leafShow"; + } +} + +``` + +```html + + + + + thymeleaf模板引擎 + + +
    +
  • + 姓名: + 薪水: +
  • +
+ + +``` + +#### 4.3 文档说明 + +freemarker:提供了完善的中文文档,地址 http://freemarker.foofun.cn/ + +thymeleaf:官方英文文档地址:[thymeleaf 3.0.11RELEASE](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf) + +注:我在本仓库中也上传了一份[thymeleaf中文文档(gangzi828(刘明刚 译)](https://github.com/heibaiying/spring-samples-for-all/tree/master/referenced%20documents),翻译的版本为3.0.5RELEASE \ No newline at end of file diff --git a/spring-boot/spring-boot-yml-profile/src/main/resources/application.yml b/spring-boot/spring-boot-yml-profile/src/main/resources/application.yml index 0b9e370..7c5541a 100644 --- a/spring-boot/spring-boot-yml-profile/src/main/resources/application.yml +++ b/spring-boot/spring-boot-yml-profile/src/main/resources/application.yml @@ -1,10 +1,10 @@ -# ָ +# 指定启动的配置 spring: profiles: active: dev -# õIJȼ devûage,Իõģproddev,ᰴվȷȣʹdevе -# õȼԲ鿴ĿREADME.md +# 配置的补充与优先级 在dev中没有配置age,所以会采用主配置的,在prod中配置dev,会按照精确优先,使用dev中的配置 +# 更多配置的优先级可以查看项目的README.md programmer: age: 999 diff --git a/spring/spring-dubbo-annotation/README.md b/spring/spring-dubbo-annotation/README.md index bf9d8fb..75b8e75 100644 --- a/spring/spring-dubbo-annotation/README.md +++ b/spring/spring-dubbo-annotation/README.md @@ -16,7 +16,7 @@ ## 二、项目依赖 -**在父工程的项目中同一导入依赖dubbo依赖的的jar包** +**在父工程的项目中统一导入依赖dubbo依赖的的jar包** 这里需要注意的是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 异常 diff --git a/spring/spring-dubbo/README.md b/spring/spring-dubbo/README.md index 52b799b..bc4bc80 100644 --- a/spring/spring-dubbo/README.md +++ b/spring/spring-dubbo/README.md @@ -16,7 +16,7 @@ ## 二、项目依赖 -**在父工程的项目中同一导入依赖dubbo依赖的的jar包** +**在父工程的项目中统一导入依赖dubbo依赖的的jar包** 这里需要注意的是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 异常 diff --git a/spring/spring-websocket-annotation/README.md b/spring/spring-websocket-annotation/README.md index 1306854..c235a81 100644 --- a/spring/spring-websocket-annotation/README.md +++ b/spring/spring-websocket-annotation/README.md @@ -4,7 +4,7 @@ ### 1.1 项目结构说明 -1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候用临时用户名作为session的id; +1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候将临时用户名存储在session当中; 2. webconfig 包是基础注解的方式配置web,在spring-base-annotation项目中已经讲解过每个类作用; 3. CustomHander为消息的自定义处理器; 4. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器; diff --git a/spring/spring-websocket/README.md b/spring/spring-websocket/README.md index 8f82b01..7752223 100644 --- a/spring/spring-websocket/README.md +++ b/spring/spring-websocket/README.md @@ -4,7 +4,7 @@ ### 1.1 项目结构说明 -1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候用临时用户名作为session的id; +1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候将临时用户名存储在session当中; 2. CustomHander为消息的自定义处理器; 3. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器; 4. 项目以web的方式构建。