我们的商城系统本应该也是前后端分离的,就像后台管理系统那样,然而出于教学考虑,前后端分离的话就会屏蔽掉很多细节,所以我们进行服务端的页面渲染式开发
(有点儿类似freemarker)
这些页面直接粘贴到微服务中去就行了,
用户访问所有请求,全部先访问的是nginx,nginx作为反向代理将数据全部转发给网关,网关再路由到各个服务
nginx在后面部署的时候,我们可以将微服务中的页面的静态资源
部署到nginx中,这样就在部署期间做到了动静分离
,好处是可以分担微服务的压力
动静分离中的静指的是:图片、js、css等静态资源(以实际文件存在的方式)
每一个微服务只来管理自己的页面
,最终做到每一个微服务都可以独立部署、运行、升级、独立自治的
,每一个微服务的数据库、技术都是自治的,不一定商品服务用java开发,用php、js都可以,无论是从技术层面、架构层面还是业务都是独立自治的。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.5version>
<relativePath/>
parent>
<groupId>com.atlinxi.gulimallgroupId>
<artifactId>gulimall-productartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>gulimall-productname>
<description>谷粒商城-商品服务description>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>2020.0.4spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
<dependency>
<groupId>com.atlinxi.gulimallgroupId>
<artifactId>gulimall-commonartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-loadbalancerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
将静态资源和页面复制到resources下,将controller包改名为app,新增web包,该包下的controller是返回给thymeleaf的,它默认的前缀和后缀分别是classpath:/templates/
和.html
,springboot访问项目的时候自动会找index.html
// 关闭缓存
spring:
thymeleaf:
cache: false
// controller
package com.atlinxi.gulimall.product.web;
import com.atlinxi.gulimall.product.entity.CategoryEntity;
import com.atlinxi.gulimall.product.service.CategoryService;
import com.atlinxi.gulimall.product.vo.Catelog2Vo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.Map;
/**
* 返回给thymeleaf用的 @Controller
*/
@Controller
public class IndexController {
@Autowired
CategoryService categoryService;
@GetMapping({"/","/index.html"})
public String indexPage(Model model){
// 1. 查出所有的一级分类
List<CategoryEntity> categoryEntities = categoryService.getLevel1Categorys();
// model中的数据,springmvc就会放到页面的请求域中
model.addAttribute("categorys",categoryEntities);
return "index";
}
/**
* 上面是跳转页面,不加@ResponseBody
* 这儿是返回json,要加
* @return
*/
@GetMapping("/index/catalog.json")
@ResponseBody
public Map<String, List<Catelog2Vo>> getCatalogJson(){
Map<String, List<Catelog2Vo>> map = categoryService.getCatalogJson();
return map;
}
}
// service 前端的js和页面都是复制进来的
@Override
public List<CategoryEntity> getLevel1Categorys() {
List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
return categoryEntities;
}
@Override
public Map<String, List<Catelog2Vo>> getCatalogJson() {
// 1. 查出所有一级分类数据
List<CategoryEntity> level1Categorys = getLevel1Categorys();
// 2. 封装数据
// 这儿的key和value都是level1Categorys
Map<String, List<Catelog2Vo>> parentCid = level1Categorys.stream().collect(Collectors.toMap(key -> key.getCatId().toString(), value -> {
// 1. 每一个的一级分类,查询这个一级分类的二级分类
List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>()
.eq("parent_cid", value.getCatId()));
// 2. 封装上面的结果
List<Catelog2Vo> catelog2Vos = null;
if (categoryEntities != null) {
catelog2Vos = categoryEntities.stream().map(level2 -> {
Catelog2Vo catelog2Vo = new Catelog2Vo(value.getCatId().toString(), null, level2.getCatId().toString(), level2.getName());
// 1. 找当前二级分类的三级分类封装成vo
List<CategoryEntity> level3Catelog = baseMapper.selectList(new QueryWrapper<CategoryEntity>()
.eq("parent_cid", level2.getCatId()));
if (level3Catelog != null){
// 2. 封装成指定格式
List<Catelog2Vo.Catelog3Vo> collect = level3Catelog.stream().map(level3 -> {
Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(level2.getCatId().toString(),level3.getCatId().toString(),level3.getName());
return catelog3Vo;
}).collect(Collectors.toList());
catelog2Vo.setCatalog3List(collect);
}
return catelog2Vo;
}).collect(Collectors.toList());
}
return catelog2Vos;
}));
return parentCid;
}
我们在访问项目的时候访问的域名是本地的,http://localhost:12000/#
我们希望它是一个合法的域名,而不是本地,
按照正常流程,项目上线是需要买一台服务器的,我们有一个公网ip地址,这样的话别人就都能访问到这个服务器,然后再为这个公网ip地址绑定域名
,最后再做一些备案等操作,
那这样,别人访问我们的域名就能访问到我们的服务器,进而访问到项目。
对于现在正在开发的我们太麻烦了,我们先搭建出开发的基本环境,上线以后再做上面正规的流程。
windows域名解析会先找C:\Windows\System32\drivers\etc下的hosts文件,然后才找dns服务器
当我们在浏览器输入gulimall.com的时候,就会去找我们的虚拟机,此时虚拟机中的nginx是运行的话,就可以访问到nginx,因为nginx监听的是80端口
192.168.56.10 gulimall.com
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
upstream gulimall{
server 192.168.56.1:88;
}
include /etc/nginx/conf.d/*.conf;
}
server {
listen 80;
server_name gulimall.com;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
proxy_set_header Host $host;
proxy_pass http://gulimall;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
spring:
cloud:
gateway:
routes:
# - id: test_route
# uri: https://www.baidu.com
# predicates:
# # 根据参数来匹配
# - Query=url,baidu
# 和admin_route顺序不能乱,否则页面访问报404,因为被它拦截了
# 我们一般把精确的路由放在上面,优先级高
# 匹配了这个路由之后,不会匹配下面的路由
- id: product_route
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
# 前端的请求是 http://localhost:88/api/product/category/list/tree
# 后端实际需要的请求是,http://localhost:12000/product/category/list/tree
filters:
- RewritePath=/api/(?>.*),/$\{segment}
# http://localhost:88/api/thirdparty/oss/policy
- id: third_party_route
# lb 负载均衡
uri: lb://gulimall-third-party
# 匹配所有以api开头的请求
predicates:
- Path=/api/thirdparty/**
filters:
# 路径重写
# (?.*) $\{segment} 相当于片段
- RewritePath=/api/thirdparty/(?>.*),/$\{segment}
- id: member_route
# lb 负载均衡
uri: lb://gulimall-member
# 匹配所有以api开头的请求
predicates:
- Path=/api/member/**
filters:
# 路径重写
# (?.*) $\{segment} 相当于片段
- RewritePath=/api/(?>.*),/$\{segment}
- id: ware_route
uri: lb://gulimall-ware
predicates:
- Path=/api/ware/**
filters:
- RewritePath=/api/(?>.*),/$\{segment}
# 前端项目发送请求都以 /api 开头
- id: admin_route
# lb 负载均衡 到renren-fast服务
uri: lb://renren-fast
# 匹配所有以api开头的请求
predicates:
- Path=/api/**
filters:
# 路径重写
# http://localhost:88/api/captcha.jpg 在网关匹配到相应的规则后
# 就变成了 http://localhost:8080/api/captcha.jpg
# 但实际上我们需要真正访问的是 http://localhost:8080/renren-fast/captcha.jpg
# (?.*) $\{segment} 相当于片段
- RewritePath=/api/(?>.*),/renren-fast/$\{segment}
# 域名映射只能放在最后面,否则会出现错误
# 例如我们访问product的api路径,优先匹配到这个的话就会去直接找product
# 不会再路径重写了
- id: gulimall_host_route
uri: lb://gulimall-product
predicates:
- Host=**.gulimall.com
gulimall.com
,浏览器会访问我们配置好的虚拟机include /etc/nginx/conf.d/*.conf;
配置代表包含conf.d下的所有.conf文件,用来配置serverproxy_pass http://gulimall;
,代表访问这个地址会转交到nginx.conf的upstream gulimall
下,这里配置的是网关的地址proxy_set_header Host $host;
这个配置代表保留host信息nginx代理给网关的时候,会丢失请求的Host信息,需要配置。
这个是之前做thymeleaf文章的时候总结的,总结完之后发现这个知识点不知道应用场景,先在这儿放着吧,有用的时候再说
关于SpringBoot的页面跳转问题,跳转页面一般分为静态页面和动态页面。
我理解静态页面就是不需要任何后台数据的获取,单纯获取这个页面,动态页面则反之。
在springboot项目template下的html用一个点击事件从后台controller跳转页面也不行。
在src/main/resources下面有两个文件夹,static和templates,springboot默认static中放静态页面和静态资源文件,而templates中放动态页面,动态页面访问的话需要Thymeleaf的依赖,动态页面需要从后台Controller跳转,静态页面直接类似于:http://127.0.0.1:8080/index.html 访问就可以了;如果static下的无法访问,首先你要保证了你这几个文件访问,不会被拦截器干掉
SpringBoot项目中static目录和templates目录,默认static中放静态资源文件,例如:img、js、css、font等等,如果静态html页面放在static下,一是可以直接当做静态资源访问;另外如果有一种情况,如果页面是放在static下面的,同时也需要从controller来跳转,那么可以采用重定向的方式,因为spring boot 默认的模板存放在 /resource/templates下,不会到 static 目录下去寻找。redirect其实就是重定向到外部资源;其实动态页面放在templates下,大家也都知道,需要从Controller来跳转访问,这些都是SpringBoot约定成俗的一些配置;
静态页面是可以直接访问的,在static目录下新建一个hello.html就可以在浏览器直接访问http://localhost:8080/hello.html,也可以通过controller层跳转访问。
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello() { return "hello.html"; }
}
动态页面的访问需要先请求服务器,访问后台的应用程序,然后再转向访问页面。
spring boot默认使用thymeleaf做动态页面,建议不要使用jsp,下面是spring boot整合thymeleaf的写法
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
在tempates目录下新建hello.html文件,此时若直接访问http://localhost:8080/hello.html访问的是静态文件夹(static目录)下的hello.html,再通过controller层跳转访问static会报500错误,这是因为
使用thymeleaf之后的controller层
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello() { return "hello"; }
}
若在使用动态页面时还想跳转到/static/index.html,使用重定向return “redirect:/index.html”
部分内容转载自:
https://www.freesion.com/article/8977511878/
多佛邮车和往常一样“有好亲切”:警卫怀疑乘客,乘客既互相怀疑,也怀疑警卫,大家都怀疑别人,马车夫则除了那几匹马之外,什么也不相信。
双城记
狄更斯