• 谷粒商城十一商城系统及整合thymeleaf渲染商城首页


    商城系统简介

    我们的商城系统本应该也是前后端分离的,就像后台管理系统那样,然而出于教学考虑,前后端分离的话就会屏蔽掉很多细节,所以我们进行服务端的页面渲染式开发(有点儿类似freemarker

    这些页面直接粘贴到微服务中去就行了,

    用户访问所有请求,全部先访问的是nginx,nginx作为反向代理将数据全部转发给网关,网关再路由到各个服务

    nginx在后面部署的时候,我们可以将微服务中的页面的静态资源部署到nginx中,这样就在部署期间做到了动静分离,好处是可以分担微服务的压力

    动静分离中的静指的是:图片、js、css等静态资源(以实际文件存在的方式)

    每一个微服务只来管理自己的页面,最终做到每一个微服务都可以独立部署、运行、升级、独立自治的,每一个微服务的数据库、技术都是自治的,不一定商品服务用java开发,用php、js都可以,无论是从技术层面、架构层面还是业务都是独立自治的。
    在这里插入图片描述

    模板技术与前端框架的区别

    在这里插入图片描述

    整合thymeleaf渲染商城首页

    
    <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>
    
    
    • 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
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    将静态资源和页面复制到resources下,将controller包改名为app,新增web包,该包下的controller是返回给thymeleaf的,它默认的前缀和后缀分别是classpath:/templates/.html,springboot访问项目的时候自动会找index.html

    // 关闭缓存
    spring:
      thymeleaf:
        cache: false
    
    • 1
    • 2
    • 3
    • 4
    // 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;
        }
    
    • 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
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    nginx搭建域名访问环境

    反向代理配置

    我们在访问项目的时候访问的域名是本地的,http://localhost:12000/#

    我们希望它是一个合法的域名,而不是本地,

    按照正常流程,项目上线是需要买一台服务器的,我们有一个公网ip地址,这样的话别人就都能访问到这个服务器,然后再为这个公网ip地址绑定域名,最后再做一些备案等操作,

    那这样,别人访问我们的域名就能访问到我们的服务器,进而访问到项目。

    对于现在正在开发的我们太麻烦了,我们先搭建出开发的基本环境,上线以后再做上面正规的流程。

    nginx相关知识看这里 todo

    修改windowshosts

    windows域名解析会先找C:\Windows\System32\drivers\etc下的hosts文件,然后才找dns服务器

    当我们在浏览器输入gulimall.com的时候,就会去找我们的虚拟机,此时虚拟机中的nginx是运行的话,就可以访问到nginx,因为nginx监听的是80端口

    192.168.56.10	gulimall.com
    
    • 1

    配置nginx.conf

    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;
    
    
    }
    
    • 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

    配置nginx/conf.d/gulimall.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;
        #}
    }
    
    • 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

    在这里插入图片描述

    gateway网关路由配置

    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
    
    • 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

    反向代理流程

    1. 客户端访问windows浏览器端gulimall.com,浏览器会访问我们配置好的虚拟机
    2. 虚拟机中的nginx监听80端口(server_name也配置了gulimall.com),nginx.conf中include /etc/nginx/conf.d/*.conf;配置代表包含conf.d下的所有.conf文件,用来配置server
    3. gulimall.conf配置 proxy_pass http://gulimall;,代表访问这个地址会转交到nginx.conf的upstream gulimall下,这里配置的是网关的地址
    4. 在上述nginx将请求移交给网关的时候,nginx会丢失好些数据,例如host、cookie等等,而我们网关配置的断言又是根据host,proxy_set_header Host $host;这个配置代表保留host信息
    5. gateway根据域名断言将请求转发给对应的服务

    在这里插入图片描述

    nginx代理给网关的时候,会丢失请求的Host信息,需要配置。

    域名映射最终的效果

    1. 接口的调用可以通过gulimall.com
    2. 请求页面可以通过gulimall.com
    3. nginx直接代理给网关,网关判断 /api/** 转交给对应的服务器,如果满足域名,则转交给对应的服务

    springboot 页面跳转

    这个是之前做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"; }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    动态页面

    动态页面的访问需要先请求服务器,访问后台的应用程序,然后再转向访问页面。
    spring boot默认使用thymeleaf做动态页面,建议不要使用jsp,下面是spring boot整合thymeleaf的写法

    <dependency>
    	<groupId>org.springframework.bootgroupId>
    	<artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 在tempates目录下新建hello.html文件,此时若直接访问http://localhost:8080/hello.html访问的是静态文件夹(static目录)下的hello.html,再通过controller层跳转访问static会报500错误,这是因为

      • 静态页面的return默认是跳转到static目录下的
      • 当引入thymeleaf之后,动态跳转会覆盖默认的静态跳转
      • 动态跳转默认跳转到templates目录下
      • 两者return代码区别:动态跳转有无.html后缀都可

    使用thymeleaf之后的controller层

    @Controller
    public class HelloController {
        @RequestMapping("/hello")
        public String hello() { return "hello"; }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    若在使用动态页面时还想跳转到/static/index.html,使用重定向return “redirect:/index.html”

    部分内容转载自:
    https://www.freesion.com/article/8977511878/

    多佛邮车和往常一样“有好亲切”:警卫怀疑乘客,乘客既互相怀疑,也怀疑警卫,大家都怀疑别人,马车夫则除了那几匹马之外,什么也不相信。

    双城记
    狄更斯

  • 相关阅读:
    pygame制作游戏全套的
    python创建exe文件
    Redis系列:Redis的数据结构
    基于EIoT能源物联网的工厂智能照明系统应用改造-Susie 周
    Web前端大作业——基于HTML+CSS+JavaScript仿英雄联盟LOL游戏网站
    【附源码】计算机毕业设计java中学网站设计与实现设计与实现
    Docker网络学习
    牛客-TOP101-BM43
    Spring Boot项目配置多个数据源
    (37)Verilog实现RAM【伪双端口】
  • 原文地址:https://blog.csdn.net/weixin_44431371/article/details/128071846