SpringBoot+zk+dubbo架构实践(五):搭建微服务电商架构(内附GitHub地址)

35岁的程序员 2018-06-08 15:12:58 ⋅ 883 阅读

往期回顾

SpringBoot+zk+dubbo架构实践(一):本地部署zookeeper
SpringBoot+zk+dubbo架构实践(二):SpringBoot 集成 zookeeper
SpringBoot+zk+dubbo架构实践(三):部署Dubbo-admin管理平台SpringBoot+zk+dubbo架构实践(四):sb+zk+dubbo框架搭建(内附源码Git地址)

先给大家看一个正在开发的一款小程序微商城项目-数据模型:https://100boot.cn/interface/wxshop.html (有小程序源码可以下载哦!)

前言

我们的sb+zk+dubbo微服务架构实践就要完结了。最后完成2件事情。
1、Spring boot + zk + dubbo 集成 mybatis 和 swagger 
2、实现登录用户的 增删改查业务,附带一个pagehelpe实现的分页查询功能。 

目的是让大家能够快速的使用起来,直接配置一下数据源就可以用喽~~~
我们基于 SpringBoot+zk+dubbo架构实践(四) 来完成今天内容。

user_info 数据库表脚本

CREATE TABLE `user_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `user_name` varchar(30) DEFAULT NULL COMMENT '用户名',
  `password` varchar(30) DEFAULT NULL COMMENT '密码',
  `sex` varchar(2) NOT NULL COMMENT '性别(0 未知,1 男 ,2 女)',
  `content` varchar(255) DEFAULT NULL COMMENT '简介',
  `create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户信息表'

shop-api 子项目

pom.xml 增加pagehelpe maven配置
<dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>4.1.6</version>
</dependency>
UserInfoService.java 增加用户信息接口
package com.itunion.shop.service;
import com.github.pagehelper.PageInfo;
import com.itunion.shop.dto.UserInfoDto;
import com.itunion.shop.dto.UserInfoReqListDto;
import java.util.List;/** * 用户数据服务 * Created by lin on 2018年06月07日21:12:04 */public interface UserInfoService {    /**     * 新增用户信息     */    int addUserInfo(UserInfoDto record);    /**     * 查询所有用户信息     */    List<UserInfoDto> getUserInfoList();    /**     * 根据用户ID删除用户信息     */    int delUserInfoById(Integer id);    /**     * 根据用户ID修改用户信息     */    int modifyUserInfoById(UserInfoDto record);    /**     * 分页查询     */    PageInfo getUserInfoListPage(UserInfoReqListDto userInfoReqListDto); }
增加UserInfoDto.java 和 UserInfoReqListDto.java
UserInfoDto : 用户信息封装 (具体可以GitHub下载源码)
UserInfoReqListDto : 分页数据封装(具体可以GitHub下载源码)

shop-ds 子项目 主要用来接口实现和读写数据库

pom.xml 增加maven配置
<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.27</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.1.6</version>
        </dependency>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>    <settings>        <setting name="cacheEnabled" value="true"/>        <setting name="lazyLoadingEnabled" value="true"/>        <setting name="aggressiveLazyLoading" value="true"/>        <setting name="useGeneratedKeys" value="true"/>        <setting name="defaultExecutorType" value="SIMPLE"/>        <setting name="defaultStatementTimeout" value="600"/>        <setting name="callSettersOnNulls" value="true"/>    </settings>    <plugins>        <plugin interceptor="com.github.pagehelper.PageHelper">            <property name="dialect" value="mysql"/>        </plugin>    </plugins>    <mappers>        <mapper resource="mappers/UserInfoMapper.xml"/>    </mappers></configuration>
DatasourceConfig.java 链接数据源
package com.itunion.shop.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.annotation.PostConstruct;
import java.sql.DataSource;

@Configuration@MapperScan(basePackages = "com.itunion.shop.mapper")
public class DatasourceConfig {    
private static Logger log = LoggerFactory.getLogger(DatasourceConfig.class);    
@Value("${druid.driver}")    
private String driverClassName;    
@Value("${druid.url}")    
private String url;    
@Value("${druid.username}")    
private String username;    
@Value("${druid.password}")    
private String password;    
@Value("${druid.init-size}")    
private int initSize;    
@Value("${druid.min-idel}")    
private int minIdel;    
@Value("${druid.max-active}")    
private int maxActive;    
@Value("${druid.login.timeout.seconds}")    
private int loginTimeoutSeconds;    
@Value("${druid.query.timeout.seconds}")    
private int queryTimeoutSeconds;    
   @Bean    public DataSource dataSource() {        DruidDataSource ds = new DruidDataSource();        ds.setDriverClassName(driverClassName);        ds.setUrl(url);        ds.setUsername(username);        ds.setPassword(password);        ds.setInitialSize(initSize);        ds.setMinIdle(minIdel);        ds.setMaxActive(maxActive);        ds.setLoginTimeout(loginTimeoutSeconds);        ds.setQueryTimeout(queryTimeoutSeconds);        
       return ds;    }    
   @Bean    public SqlSessionFactory sqlSessionFactory() throws Exception {        final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();        sqlSessionFactory.setDataSource(dataSource());        sqlSessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));        sqlSessionFactory.setFailFast(true);        
       return sqlSessionFactory.getObject();    }    
   public DataSourceTransactionManager dataSourceTransactionManager() {        log.debug("> transactionManager");        
       return new DataSourceTransactionManager(dataSource());    }    
   @PostConstruct    public void postConstruct() {        log.info("jdbc settings={}", this);    } }
UserInfo.java 、UserInfoMapper.java 和 UserInfoMapper.xml 自己生成(GitHub 源码里有)
UserInfoMapper.xml 增加 分页查询业务
 <select id="getUserInfoList" parameterType="com.itunion.shop.dto.UserInfoReqListDto" resultType="com.itunion.shop.dto.UserInfoDto">
        select
        id, user_name as userName, password, sex, content, create_date as createDate, update_date as updateDate
        from user_info
    </select>
shop-ds-rovider.xml 注册服务配置
    <dubbo:service interface="com.itunion.shop.service.UserInfoService" ref="userInfoService"/>
UserInfoMapper.xml 增加 查询用户信息集合业务
 /**     * 获取用户信息集合     * @return     */
    List<UserInfoDto> getUserInfoList(UserInfoReqListDto userInfoReqListDto);
UserInfoServiceImpl.java 增加用户业务实现服务类
package com.itunion.shop.service.impl;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itunion.shop.dto.UserInfoDto;
import com.itunion.shop.dto.UserInfoReqListDto;
import com.itunion.shop.mapper.UserInfoMapper;
import com.itunion.shop.model.UserInfo;
import com.itunion.shop.service.UserInfoService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/** * 用户信息-服务提供方 * Created by lin on 2018年06月07日21:48:13 */

public class UserInfoServiceImpl implements UserInfoService {    
private final static Logger LOGGER = LoggerFactory.getLogger(UserInfoServiceImpl.class);    
   @Autowired    private UserInfoMapper userInfoMapper;    
   @Override    public int addUserInfo(UserInfoDto record) {        LOGGER.info("进入用户信息-服务提供方-UserInfoServiceImpl.addUserInfo[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        UserInfo userInfo = new UserInfo();        BeanUtils.copyProperties(record, userInfo);        userInfo.setUpdateDate(new Date());        
       return userInfoMapper.insertSelective(userInfo);    }  
   @Override    public List<UserInfoDto> getUserInfoList() {        LOGGER.info("进入用户信息-服务提供方-UserInfoServiceImpl.getUserInfoList[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        return userInfoMapper.getUserInfoList(null);    }    
   @Override    public int delUserInfoById(Integer id) {        LOGGER.info("进入用户信息-服务提供方-UserInfoServiceImpl.delUserInfoById[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        return userInfoMapper.deleteByPrimaryKey(id);    }    
   @Override    public int modifyUserInfoById(UserInfoDto record) {        LOGGER.info("进入用户信息-服务提供方-UserInfoServiceImpl.modifyUserInfoById[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        UserInfo userInfo = new UserInfo();        BeanUtils.copyProperties(record, userInfo);        userInfo.setUpdateDate(new Date());      
    return userInfoMapper.updateByPrimaryKeySelective(userInfo);    }  
    @Override    public PageInfo getUserInfoListPage(UserInfoReqListDto userInfoReqListDto) {        LOGGER.info("进入用户信息-服务提供方-UserInfoServiceImpl.getUserInfoListPage[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        PageHelper.startPage(userInfoReqListDto.getPage(), userInfoReqListDto.getSize());        List<UserInfoDto> userInfoDtos = userInfoMapper.getUserInfoList(userInfoReqListDto);        PageInfo<UserInfoDto> userInfoDtoPageInfo = new PageInfo<>(userInfoDtos);        
    return userInfoDtoPageInfo;    } }
ShopDSApplication.java 项目启动
package com.itunion.shop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication

// 使用 providers.xml 配置

@ImportResource(value = {"classpath:shop-ds-rovider.xml"})
public class ShopDSApplication {    
public static void main(String[] args) {        SpringApplication.run(ShopDSApplication.class, args);    } }

好了, shop-ds 项目配置好了,接下来开始配是 移动端子项目和PC端子项目(我们文章只配置一下移动端的 PC端自己copy)

shop-web 子项目

pom.xml 增加 swagger 和 pagehelper maven配置
<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.1.6</version>
        </dependency>
shop-web-consumer.xml 注册服务配置
        <dubbo:reference id="userInfoService" check="false" interface="com.itunion.shop.service.UserInfoService"/>

UserInfoController.java 移动端业务入口

package com.itunion.shop.web.controller;
import com.github.pagehelper.PageInfo;
import com.itunion.shop.common.UniformResultTemplate;
import com.itunion.shop.dto.UserInfoDto;
import com.itunion.shop.dto.UserInfoReqListDto;
import com.itunion.shop.service.UserInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/** * 用户信息-移动端消费方 * Created by lin on 2018年06月07日22:02:07 */

@Controller@RequestMapping("userInfo")
@Api(description = "测试移动")
public class UserInfoController {    
private Logger LOGGER = LoggerFactory.getLogger(UserInfoController.class);    
   @Autowired    UserInfoService userInfoService;    
   @ApiOperation(value = "getUserInfoList", notes = "查询所有用户信息")        @RequestMapping(value = "getUserInfoList", method = RequestMethod.GET)    
   @ResponseBody    public UniformResultTemplate<List<UserInfoDto>> getUserInfoList() {        LOGGER.info("进入用户信息-移动端消费方-UserInfoController.getUserInfoList[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        UniformResultTemplate<List<UserInfoDto>> uniformResultTemplate = new UniformResultTemplate<>();        uniformResultTemplate.setCode(100);        uniformResultTemplate.setMessage("查询所有用户信息成功!");        uniformResultTemplate.setResult(userInfoService.getUserInfoList());        
       return uniformResultTemplate;    }    
   @ApiOperation(value = "getUserInfoListPage", notes = "查询所有用户信息-分页")    
   @ApiImplicitParam(name = "userInfoReqListDto", value = "{\"page\":\"1\",\"size\":\"2\"}")    
   @RequestMapping(value = "getUserInfoListPage", method = RequestMethod.POST)    
   @ResponseBody    public UniformResultTemplate<PageInfo> getUserInfoList(@RequestBody UserInfoReqListDto userInfoReqListDto) {        LOGGER.info("进入用户信息-移动端消费方-UserInfoController.getUserInfoListPage[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        UniformResultTemplate<PageInfo> uniformResultTemplate = new UniformResultTemplate<>();        uniformResultTemplate.setCode(100);        uniformResultTemplate.setMessage("查询所有用户信息成功分页!");        uniformResultTemplate.setResult(userInfoService.getUserInfoListPage(userInfoReqListDto));        
       return uniformResultTemplate;    }    
@ApiOperation(value = "addUserInfo", notes = "新增用户信息")  
 @ApiImplicitParam(name = "userInfoDto", value = "{\"userName\":\"测试用户名\",\"password\":\"000000\",\"sex\":1,\"content\":\"这里是IT实战联哦~~~\"}")    
 @RequestMapping(value="addUserInfo", method = RequestMethod.POST)    
 @ResponseBody    public UniformResultTemplate<String> addUserInfo(@RequestBody UserInfoDto userInfoDto) {    LOGGER.info("进入用户信息-移动端消费方-UserInfoController.addUserInfo[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        UniformResultTemplate<String> uniformResultTemplate = new UniformResultTemplate<>();        Integer num = userInfoService.addUserInfo(userInfoDto);        if(num > 0){            uniformResultTemplate.setCode(100);            uniformResultTemplate.setMessage("新增用户信息成功!");            uniformResultTemplate.setResult(num+"");        }else{            uniformResultTemplate.setCode(400);            uniformResultTemplate.setMessage("新增用户信息失败!");            uniformResultTemplate.setResult(num+"");        }        
         return uniformResultTemplate;    }    
    @ApiOperation(value="delUserInfoById", notes="根据用户ID删除用户信息")      @ApiImplicitParam(name = "id", value = "4" , paramType="path" , dataType="Integer")    
    @RequestMapping(value="delUserInfoById/{id}", method = RequestMethod.POST)    
    @ResponseBody    public UniformResultTemplate<String> deleteKdgVipItem(@PathVariable Integer id) {        LOGGER.info("进入用户信息-移动端消费方-UserInfoController.delUserInfoById[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        UniformResultTemplate<String> uniformResultTemplate = new UniformResultTemplate<>();        Integer num = userInfoService.delUserInfoById(id);        if(num > 0){            uniformResultTemplate.setCode(100);            uniformResultTemplate.setMessage("根据用户ID删除用户信息成功!");            uniformResultTemplate.setResult(num+"");        }else{            uniformResultTemplate.setCode(400);            uniformResultTemplate.setMessage("根据用户ID删除用户信息失败!");            uniformResultTemplate.setResult(num+"");        }        
        return uniformResultTemplate;    }    
    @ApiOperation(value = "modifyUserInfo", notes = "修改用户信息")  
     @ApiImplicitParam(name = "userInfoDto", value = "{\"id\":10,\"userName\":\"测试修改用户名\",\"password\":55555,\"sex\":1,\"content\":\"这里是最新的IT实战联哦~~~\"}")    
     @RequestMapping(value="modifyUserInfo", method = RequestMethod.POST, produces= MediaType.APPLICATION_JSON_UTF8_VALUE)    
     @ResponseBody    public UniformResultTemplate<String> modifyUserInfo(@RequestBody UserInfoDto userInfoDto) {        LOGGER.info("进入用户信息-移动端消费方-UserInfoController.modifyUserInfo[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "]");        UniformResultTemplate<String> uniformResultTemplate = new UniformResultTemplate<>();        Integer num = userInfoService.modifyUserInfoById(userInfoDto);        if(num > 0){            uniformResultTemplate.setCode(100);            uniformResultTemplate.setMessage("修改用户信息成功!");            uniformResultTemplate.setResult(num+"");        }else{            uniformResultTemplate.setCode(400);            uniformResultTemplate.setMessage("修改用户信息失败!");            uniformResultTemplate.setResult(num+"");        }        return uniformResultTemplate;    } }

备注:工具类 可以GitHub下载源码

Application.java 项目启动
package com.itunion.shop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication@ImportResource(value = { "classpath:shop-web-consumer.xml" })
public class Application extends SpringBootServletInitializer {  
 public static void main(String[] args) throws Exception {        SpringApplication.run(Application.class, args);    } }
application.properties
server.port=8081  #端口
server.context-path=/wxShop #项目名称
swagger.enable=true
swagger.protocol=http swagger.host=127.0.0.1:8081

备注:shop-pc 子项目和 shop-web 配置都是一样的,不多做介绍

项目启动

访问地址
http://127.0.0.1:8081/wxShop/swagger-ui.html
效果


可以看到我们一共写了5个业务 用户的 增、删、改、查和分页查询。

总结

SpringBoot+zk+dubbo架构实践 分解为5篇文章写完,为了让大家能够最快入门微服务架构 并实现编码,没有做深入的剖析, 小编将源码上传GitHub:https://github.com/yundianzixun/weixin-shop。在具体工作中还会遇到更多复杂的问题,架构师之路就是不断解决问题的,一边解决问题一边进阶。小编能做的还很少,希望该实践系列能够对大家有用,谢谢支持!

关注我们

更多精彩内容请关注“IT实战联盟”公众号,平台会提供更多技术实践内容,也可以加入交流群和作者互撩哦~~~


全部评论: 0

    我有话说:

    SpringBoot+zk+dubbo架构实践(四):sb+zk+dubbo框架源码GitHub地址

    本篇案例模拟了一个provider服务提供方和PC、Web两个服务消费方GitHub源码......

    SpringBoot+zk+dubbo架构实践(一):本地部署zookeeper

    SpringBoot+zk+dubbo架构实践系列实现目标:自己动手服务架构

    SpringBoot+zk+dubbo架构实践(三):部署Dubbo-admin管理平台

    本系列架构实践不做深入探讨,主旨是带领大家能够快速踏入服务架构门槛,能够轻松的一套属于自己的服务架构。——写代码我们是认真滴!

    SpringBoot+zk+dubbo架构实践(二):SpringBoot 集成 zookeeper

    不啰嗦,本篇完成两件事:1、SpringBoot 框架;2、基于spring boot框架访问zookeeper。

    服务架构网站扫码登录的功能设计

    信扫码登录大家都是应用比较多的登录方式了,现在大的购物网站像京东、淘宝等都支持使用APP扫码登录网站了。今天就用APP扫码登录网站的实例来举例说明服务架构过程。

    架构实战篇:认识一下服务架构

    服务是一个新兴的软件架构,就是把一个大型的单个应用程序和服务拆分为数十个的支持服务

    架构实战篇(三)-Spring Boot架构RESTful API案例

    之前分享了Spring Boot 整合Swagger 让API可视化和前后端分离架构 受到了大家一致好评 ,本节就接着上节的代码做了详细的查询代码的补充和完善并RESTful API架构案例。

    架构实战篇:一个可供中小团队参考的服务架构技术栈

    作者近年一直在一线互联网公司(携程,拍拍贷等)开展服务架构实践,根据我个人的一线实践经验和我平时对Spring Cloud的调研,我认为Spring Cloud技术栈中的有些组件离生产级开发尚有

    架构实战篇(十):Spring Boot 集成 Dubbo

    Dubbo是阿里巴巴SOA服务化治理方案的核心框架,一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案。

    服务架构下的若干常用设计模式

    在我们选择了用服务架构来设计、交付数字化应用后,因服务架构本身所带来的一些共性问题。

    信小程序实战-购物车(上)

    好久没更新小程序实战的文章了,是因为最近一直做整体架构调整,一些准备工作也是比较耗费时间的。在这几天将会有新版的 小程序教程推出.......

    【分享】一次单体架构改造成服务架构的拆分实践

    从5个方面设计这次服务的拆分方案,以及经验总结!

    服务架构实战篇:快速入手SpringBoot 2.0,欢迎入坑哦~~~

    SpringBoot 2.0 基本要求Java最低要求8以上,不再支持Java 6 和 7等低版本。

    服务架构学习笔记(一):重新认识服务

    服务(Microservice)是服务化思路的一种最佳实践方向,遵循SOA的思路,各个企业在服务化治理的道路上走的时间长了,踩的坑多了,整个软件交付链路上各个环节的基础设施逐渐成熟了,服务

    「轻阅读」阿里云-开放平台高级技术家教你服务架构的四大金刚利器

    孔凡勇,花名云狄,阿里云-开放平台高级技术家,对高并发、高性能、高可用、可伸缩的分布式系统架构设计有丰富经验,Cloud Native坚定拥护者,坚守开发一线打磨匠艺的架构师。

    精品推荐:服务架构下静态数据通用缓存机制

    在分布式系统中,特别是最近很火的服务架构下,有没有或者能不能总结出一个业务静态数据的通用缓存处理机制或方案,这篇文章将结合一些实际的研发经验,尝试理清其中存在的关键问题以及探寻通用的解决之道。

    信小程序实战-首页(下)

    上一篇:信小程序实战-首页(上)好了,上一期我们把首页搜索、导航栏和广告轮播给做完了,那么接下来会继续

    信小程序实战-首页(上)

    上一篇:信小程序实战-入门篇 嗨,大家好!经过近两周的精心准备终于开始信小程序实战之路喽。那么最终会做成什么样呢?好了,不啰嗦了 我们先看首页长什么样吧!   首页效果图

    架构实战篇(十二):Spring Boot 分布式Session共享Redis

    分布式Web网站一般都会碰到集群session共享问题,小编整理了一套解决方案,GitHub 源码地址哦~~~