1.docker安装nacos,运行容器

1 2 3
| docker pull nacos.nacos-server:1.2.0 docker run --env MODE=standalone --name nacos --restart=always -d -p 8848:8848 nacos/nacos-server:1.2.0 http://192.168.200.130:8848/nacos
|
- docker ps: 查看运行的容器
- docker logs -f 容器ID:查看运行日志
2.虚拟网络设置
账号:root 密码:itcast


3.微服务解构

4.接口工具
swagger
1 2 3 4 5 6 7 8
| <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency>
|
自动配置


配置代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.heima.common.swagger;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration @EnableSwagger2 public class SwaggerConfiguration {
@Bean public Docket buildDocket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(buildApiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.heima")) .paths(PathSelectors.any()) .build(); }
private ApiInfo buildApiInfo() { Contact contact = new Contact("黑马程序员","",""); return new ApiInfoBuilder() .title("黑马头条-平台管理API文档") .description("黑马头条后台api") .contact(contact) .version("1.0.0").build(); } }
|
启动user微服务,访问地址:http://localhost:51801/swagger-ui.html
knife4j
依赖
1 2 3 4
| <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> </dependency>
|
配置代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.heima.common.swagger;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration @EnableSwagger2 @EnableKnife4j @Import(BeanValidatorPluginsConfiguration.class) public class Swagger2Configuration {
@Bean(value = "defaultApi2") public Docket defaultApi2() { Docket docket=new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .groupName("1.0") .select() .apis(RequestHandlerSelectors.basePackage("com.heima")) .paths(PathSelectors.any()) .build(); return docket; } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("黑马头条API文档") .description("黑马头条API文档") .version("1.0") .build(); } }
|
| 注解 | 说明 |
| ————————- | —————————————————————————————— |
| @EnableSwagger2
| 该注解是Springfox-swagger框架提供的使用Swagger注解,该注解必须加 |
| @EnableKnife4j
| 该注解是knife4j
提供的增强注解,Ui提供了例如动态参数、参数过滤、接口排序等增强功能,如果你想使用这些增强功能就必须加该注解,否则可以不用加 |
在Spring.factories中新增配置
1 2 3
| org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.heima.common.swagger.Swagger2Configuration, \ com.heima.common.swagger.SwaggerConfiguration
|
在浏览器输入地址:http://host:port/doc.html
5.网关
appGateway依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> </dependencies>
|
bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11 12
| server: port: 51601 spring: application: name: leadnews-app-gateway cloud: nacos: discovery: server-addr: 192.168.200.130:8848 config: server-addr: 192.168.200.130:8848 file-extension: yml
|
在nacos的配置中心创建dataid为leadnews-app-gateway的yml配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| spring: cloud: gateway: globalcors: add-to-simple-url-handler-mapping: true corsConfigurations: '[/**]': allowedHeaders: "*" allowedOrigins: "*" allowedMethods: - GET - POST - DELETE - PUT - OPTION routes: - id: user uri: lb://leadnews-user predicates: - Path=/user/** filters: - StripPrefix= 1
|
环境搭建完成以后,启动项目网关和用户两个服务,使用postman进行测试
请求地址:http://localhost:51601/user/api/v1/login/login_auth
6.JWT全局验证
全局过滤器实现jwt校验
思路分析:
- 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录
- 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
- 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN
- 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误
具体实现:
第一:
在认证过滤器中需要用到jwt的解析,所以需要把工具类拷贝一份到网关微服务
第二:
在网关微服务中新建全局过滤器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| package com.heima.app.gateway.filter;
import com.heima.app.gateway.util.AppJwtUtil; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono;
@Component @Slf4j public class AuthorizeFilter implements Ordered, GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse();
if(request.getURI().getPath().contains("/login")){ return chain.filter(exchange); }
String token = request.getHeaders().getFirst("token");
if(StringUtils.isBlank(token)){ response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); }
try { Claims claimsBody = AppJwtUtil.getClaimsBody(token); int result = AppJwtUtil.verifyToken(claimsBody); if(result == 1 || result == 2){ response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } }catch (Exception e){ e.printStackTrace(); response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); }
return chain.filter(exchange); }
@Override public int getOrder() { return 0; } }
|
测试:
启动user服务,继续访问其他微服务,会提示需要认证才能访问,这个时候需要在heads中设置设置token才能正常访问。
7.垂直分表

8.Freemarker模板技术
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
常用的java模板引擎还有哪些?
Jsp、Freemarker、Thymeleaf 、Velocity 等。
- Jsp 为 Servlet 专用,不能单独进行使用。
- Thymeleaf 为新技术,功能较为强大,但是执行的效率比较低。
- Velocity从2010年更新完 2.0 版本后,便没有在更新。Spring Boot 官方在 1.4 版本后对此也不在支持,虽然 Velocity 在 2017 年版本得到迭代,但为时已晚。
freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。
创建测试工程
创建一个freemarker-demo 的测试工程专门用于freemarker的功能测试与模板的测试。
pom.xml如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>heima-leadnews-test</artifactId> <groupId>com.heima</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>freemarker-demo</artifactId>
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> </dependencies>
</project>
|
配置文件
配置application.yml
1 2 3 4 5 6 7 8 9 10
| server: port: 8881 spring: application: name: freemarker-demo freemarker: cache: false settings: template_update_delay: 0 suffix: .ftl
|
源码

创建模型类
在freemarker的测试工程下创建模型类型用于测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.heima.freemarker.entity;
import lombok.Data;
import java.util.Date;
@Data public class Student { private String name; private int age; private Date birthday; private Float money; }
|
创建模板
在resources下创建templates,此目录为freemarker的默认模板存放目录。
在templates下创建模板文件 01-basic.ftl ,模板中的插值表达式最终会被freemarker替换成具体的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello World!</title> </head> <body> <b>普通文本 String 展示:</b><br><br> Hello ${name} <br> <hr> <b>对象Student中的数据展示:</b><br/> 姓名:${stu.name}<br/> 年龄:${stu.age} <hr> </body> </html>
|
创建controller
创建Controller类,向Map中添加name,最后返回模板文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package com.heima.freemarker.controller;
import com.heima.freemarker.entity.Student; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import java.text.SimpleDateFormat; import java.util.Calendar;
@Controller public class FreemarkerDemoController {
@GetMapping("/basic") public String helloFreemarker(Model model) { model.addAttribute("name", "清清");
Student student = new Student(); student.setAge(22); Calendar calendar = Calendar.getInstance(); calendar.set(2000, 11, 17);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); String birthday = sdf.format(calendar.getTime()); System.out.println("birthday:===============" + birthday);
student.setBirthday(calendar.getTime());
student.setMoney(99999999.0f); student.setName("小青");
model.addAttribute("stu", student); return "01-basic"; } }
|
01-basic.ftl,使用插值表达式填充数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello World!</title> </head> <body> <b>普通文本 String 展示:</b><br><br> Hello ${name} <br> <hr> <b>对象Student中的数据展示:</b><br/> 姓名:${stu.name}<br/> 年龄:${stu.age} <hr> </body> </html>
|

list遍历和if
模板文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello World!</title> </head> <body> <#-- list 数据的展示 --> <b>展示list中的stu数据:</b> <br> <br> <table> <tr> <td>序号</td> <td>姓名</td> <td>年龄</td> <td>钱包</td> </tr> <#if stus??> <#list stus as stu> <#-- =和==一样的 --> <#if stu.name='小虹'> <tr style="color: red"> <td>${stu_index + 1}</td> <td>${stu.name}</td> <td>${stu.age}</td> <td>${stu.money}</td> </tr> <#else> <tr> <td>${stu_index + 1}</td> <td>${stu.name}</td> <td>${stu.age}</td> <td>${stu.money}</td> </tr> </#if> </#list> </#if> </table> <hr>
<#-- Map 数据的展示 --> <b>map数据的展示:</b> <br/><br/> <a href="###">方式一:通过map['keyname'].property</a><br/> 输出stu1的学生信息:<br/> 姓名:${stuMap['stu1'].name}<br/> 年龄:${stuMap['stu1'].age}<br/> <br/> <a href="###">方式二:通过map.keyname.property</a><br/> 输出stu2的学生信息:<br/> 姓名:${stuMap.stu1.name}<br/> 年龄:${stuMap.stu1.age}<br/>
<br/> <a href="###">遍历map中两个学生信息:</a><br/> <table> <tr> <td>序号</td> <td>姓名</td> <td>年龄</td> <td>钱包</td> </tr>
<#list stuMap?keys as key> <tr> <td>${key_index + 1}</td> <td>${stuMap[key].name}</td> <td>${stuMap[key].age}</td> <td>${stuMap[key].money}</td> </tr> </#list>
</table> <hr> </body> </html>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @GetMapping("/list") public String list(Model model) { ArrayList<Student> stus = new ArrayList<>();
Student stu1 = new Student(); stu1.setName("小虹"); stu1.setBirthday(new Date()); stu1.setMoney(2232231.89f); stu1.setAge(18); stus.add(stu1);
Student stu2 = new Student(); stu2.setName("jojo"); stu2.setBirthday(new Date()); stu2.setMoney(9999999.89f); stu2.setAge(22); stus.add(stu2); model.addAttribute("stus", stus);
HashMap<String, Student> stuMap = new HashMap<>(); stuMap.put("stu1", stu1); stuMap.put("stu2", stu2); model.addAttribute("stuMap", stuMap);
return "02-list"; }
|
运算符
1 2 3 4 5 6 7
| <b>算数运算符</b> <br/><br/> 100+5 运算: ${100 + 5 }<br/> 100 - 5 * 5运算:${100 - 5 * 5}<br/> 5 / 2运算:${5 / 2}<br/> 12 % 10运算:${12 % 10}<br/> <hr>
|
=
或者==
:判断两个值是否相等.
!=
:判断两个值是否不等.
>
或者gt
:判断左边值是否大于右边值
>=
或者gte
:判断左边值是否大于等于右边值
<
或者lt
:判断左边值是否小于右边值
<=
或者lte
:判断左边值是否小于等于右边值
空值处理
1、判断某变量是否存在使用 “??”
用法为:variable??,如果该变量存在,返回true,否则返回false
例:为防止stus为空报错可以加上判断如下:
1 2 3 4 5
| <#if stus??> <#list stus as stu> ...... </#list> </#if>
|
2、缺失变量默认值使用 “!”
内建函数
内建函数语法格式: 变量+?+函数名称
1、和到某个集合的大小
${集合名?size}
2、日期格式化
显示年月日: ${today?date}
显示时分秒:${today?time}
显示日期+时间:${today?datetime}
自定义格式化: ${today?string("yyyy年MM月")}
3、内建函数c
model.addAttribute(“point”, 102920122);
point是数字型,使用${point}会显示这个数字的值,每三位使用逗号分隔。
如果不想显示为每三位分隔的数字,可以使用c函数将数字型转成字符串输出
${point?c}
4、将json字符串转成对象
一个例子:
其中用到了 assign标签,assign的作用是定义一个变量。
1 2 3
| <#assign text="{'bank':'工商银行','account':'10101920201920212'}" /> <#assign data=text?eval /> 开户行:${data.bank} 账号:${data.account}
|