微信支付与登录之项目开发阶段1

项目开发

1. 开源工具的优缺点选择和抽象方法的建议

简介:讲解开源工具的好处和弊端,如pageHeper分页拦截器,tk自动生成工具,抽象方法的利弊等

1、开源工具
好处:开发方便,使用简单,使用aop方式进行分页,只需要引入相关依赖,然后PageHelper.startPage(page, size); 开启分页

弊端:对于分库分表等情况下使用有问题,深度分页逻辑判断会复杂
mysql资料:

  • 深度分页常用案例:
    https://www.cnblogs.com/lpfuture/p/5772055.html
    https://blog.csdn.net/li772030428/article/details/52839987
  • 推荐书籍:
    https://book.douban.com/subject/23008813/

2、封装的好坏

关于抽象和不抽象的选择,比如tk这些工具,通用mapper,service,controller
好处:

  • 代码量大大减少,开发新模块可以马上进行使用

弊端:

  • 对应过度封装,新手等比较难理解,不能保证团队里面所有人都有对应的水平,或者有高度封装的思想,也不是过度封装

课程案例:

  • 分页采用pageHelper
  • 封装通用工具类,如缓存操作等
  • 利于解耦,如切换缓存框架

2. 基础工程搭建(普通的maven工程)

2.1. 创建一个api工程

所有的实体类以及接口放在该工程中

Pom依赖

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.guangyang.xdclass</groupId>
    <artifactId>api</artifactId>
    <version>1.0</version>

    <parent>
        <artifactId>parent</artifactId>
        <groupId>com.guangyang.xdclass</groupId>
        <version>1.0</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

</project>

2.2. 创建一个parent工程

用于进行版本控制

1、这个parent工程是为了保证项目中所有依赖的版本一致性,新建其他子项目的时候直接继承这个项目即可

在gmall-parent工程中定义好技术框架的各种版本
这个是管理整个项目的版本号,需要打包成pom
gmall-parent不存储任何的jar包只负责管理版本号

2、注意事项
在子项目中不是引入这个依赖其他后,其他任何的依赖都不引入,这个工程只是对版本的控制,版本的控制,版本的控制,重要的事情说三遍,子项目还是正常引入依赖只是不用写版本号了,版本号由这个项目进行管理。

pom.xml中需要添加如下
pom

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.guangyang.xdclass</groupId>
    <artifactId>parent</artifactId>
    <version>1.0</version>

    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>

        <commons-lang3.version>3.7</commons-lang3.version>
        <mybatis.version>2.1.1</mybatis.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--
            commons-lang3是用于针对java.lang类中数据基本类型的处理工具包
            比如判断字符串是否为空if(StringUtils.isblank(s))
            -->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${commons-lang3.version}</version>
            </dependency>

            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

2.3. 创建一个service-util

服务层用到的工具类,以及将工具类整合到springboot中所需要的配置类,都将放在该工程中

2.4. 创建web-util

控制层用到的工具类,以及将工具类整合到springboot中所需要的配置类,都将放在该工程中

2.5. 创建一个common-util

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.guangyang.xdclass</groupId>
    <artifactId>common-util</artifactId>
    <version>1.0</version>

    <parent>
        <artifactId>parent</artifactId>
        <groupId>com.guangyang.xdclass</groupId>
        <version>1.0</version>
    </parent>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

控制层和服务层共同使用的工具类

3. IDEA热部署

1、 需要引入的依赖,放到common-util中

<dependency>
	 <groupId>org.springframework.boot</groupId>
	 <artifactId>spring-boot-devtools</artifactId>
	 <optional>true</optional>
</dependency>

idea里面要设置
1、相关偏好里开启自动编译
2、Shift+Ctrl+Alt+/,选择Registry
选 compiler.automake.allow.when.app.running
重启项目就可以了

参考
https://www.cnblogs.com/aqsunkai/p/6690574.html

4. Mysql逆向工程神器之使用IDEA自动生成实体类

实战使用IDE根据Mysql自动生成java pojo实体类
1、IDEA连接数据库
菜单View→Tool Windows→Database打开数据库工具窗口

2、 生成POJOS.groovy文件

在这里插入图片描述

3、 修改POJOS.groovy文件,配置生成的实体类的包路径以及属性类型

在这里插入图片描述

4、 生成实体类

在这里插入图片描述

5. 接口配置文件自动映射到属性和实体类配置

使用@value注解配置文件自动映射到属性和实体类

1、 添加 @Component或者Configuration 注解;
2、 使用 @PropertySource 注解指定配置文件位置;(属性名称规范: 大模块.子模块.属性名)

#=================================微信相关==================
#公众号
wxpay.appid=wx5beac15ca207cdd40c
wxpay.appsecret=554801238f17fdsdsdd6f96b382fe548215e9

3、必须 通过注入IOC对象Resource 进来 , 才能在类中使用获取的配置文件值。

@Autowired
private WeChatConfig weChatConfig;

示例

@Configuration
@PropertySource(value="classpath:application.properties")
public class WeChatConfig {

	@Value("${wxpay.appid}")
	private String appId;

6. 整合mybatis访问数据库和阿里巴巴数据源

1、 整合mysql 加入mybatis依赖,和加入alibaba druid数据源

<!-- 引入starter-->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>1.3.2</version>
</dependency>

<!-- MySQL的JDBC驱动包	-->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>
<!-- 引入第三方数据源 -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>1.1.6</version>
</dependency>

2、 加入配置文件

spring:
  datasource:
    username: guangyang
    password: SRNDxrhc=1314
    url: jdbc:mysql://192.168.1.200:3306/gmall?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 使用我们自己的druid数据源
    type: com.alibaba.druid.pool.DruidDataSource

    initialSize: 10 #初始化连接个数
    minIdle: 5    #最小连接个数
    maxActive: 500 #最大连接个数
    maxWait: 60000 #最大等待时间
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500


mybatis:
  # config-location和configuration不能同时配置,否则会抛出异常
  # 一般只配置configuration即可,会自动找到mybatis全局配置文件
  #  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mapper/*Mapper.xml
  # 对应实体类的路径,只能指定具体的包,多个配置可以使用英文逗号隔开
  type-aliases-package: com.qinglingwang.gmall.user.entity
  configuration:
    # Mybatis SQL语句控制台打印
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 开启驼峰命名规则
    # 在数据库中字段可以采用驼峰命名规则,mybatis会把 下划线去掉并把下划线后面的首字母认为是大写
    map-underscore-to-camel-case: true

3、 启动类增加mapper扫描

@MapperScan("com.guangyang.xdclass.test.mapper")

4、 创建mapper文件

VideoMapper类例子
@Select("SELECT * FROM video")
@Results({
	@Result(column = "create_time",property = "createTime")  //javaType = java.util.Date.class
})
List<Video> getAll();

7. 使用Mybatis注解开发视频列表增删改查

讲解:使用Mybatis3.x注解方式 增删改查实操, 控制台打印sql语句

1、控制台打印sql语句
#增加打印sql语句,一般用于本地开发测试
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

2、增加mapper代码

	   @Select("select * from video")
//    @Results({
//            @Result(column = "cover_img",property = 	
	"coverImg"),
//            @Result(column = "create_time",property = 	
	"createTime")
//    })
    List<Video> findAll();
    @Select("SELECT * FROM video WHERE id = #{id}")
    Video findById(int id);
    @Update("UPDATE video SET title=#{title} WHERE id =#{id}")
   int update(Video Video);

    @Delete("DELETE FROM video WHERE id =#{id}")
    int delete(int id);

    @Insert("INSERT INTO `video` ( `title`, `summary`, " +
        "`cover_img`, `view_num`, `price`, `create_time`," +
            " `online`, `point`)" +
        "VALUES" +
        "(#{title}, #{summary}, #{coverImg}, #{viewNum}, #{price},#{createTime}" +
        ",#{online},#{point});")
@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")
int save(Video video);

3、保存保存
技巧:保存对象,获取数据库自增id
@Options(useGeneratedKeys=true, keyProperty=“id”, keyColumn=“id”)

4、技巧:
数据库字段下划线和Java实体类映射
mybatis 下划线转驼峰配置,两者都可以
#mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.map-underscore-to-camel-case=true

8. 使用Mybatis 的xml完成增删改查

1、 配置文件

mybatis:
  # config-location和configuration不能同时配置,否则会抛出异常
  # 一般只配置configuration即可,会自动找到mybatis全局配置文件
  #  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mapper/*Mapper.xml
  # 对应实体类的路径,只能指定具体的包,多个配置可以使用英文逗号隔开
  type-aliases-package: com.guangyang.xdclass.bean
  configuration:
    # Mybatis SQL语句控制台打印
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 开启驼峰命名规则
    # 在数据库中字段可以采用驼峰命名规则,mybatis会把 下划线去掉并把下划线后面的首字母认为是大写
    map-underscore-to-camel-case: true

2、 在resources中创建mapper目录,将VideoMapper.xml文件放在该目录下

基本结构

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.guangyang.xdclass.test.mapper.VideoMapper">
    
</mapper>

查找所有的Video实体类

<select id="findVideoById" parameterType="String" resultType="com.guangyang.xdclass.bean.Video">
        select * from `video` where id = #{id}
    </select>

插入一个对象到数据库中

<insert id="saveVideo" parameterType="com.guangyang.xdclass.bean.Video" >
        INSERT INTO video (
            title,
            summary,
            cover_img,
            view_num,
            price,
            create_time,
            online,
            point
        )
        VALUES
            (
                #{title},
                #{summary},
                #{coverImg},
                #{viewNum},
                #{price},
                #{createTime},
                #{online},
                #{point}
            )
    </insert>

注意事项:#{对象属性名,不是数据库中的字段名} ,数据库中的字段没有单引号

更新与删除操作

<update id="updateVideo" parameterType="com.guangyang.xdclass.bean.Video" >
        update video set title = #{title} where id = #{id}
    </update>

    <delete id="deleteVideoById" parameterType="com.guangyang.xdclass.bean.Video">
        delete from video where id = #{id}
    </delete>

9. 搭建video微服务

1、创建xdclass-video-web以及xdclass-video-service服务
2、引入dubbo框架,实现项目的分布式架构,后期要改成使用使用springcloud

# =========================================== dubbo配置扫描 ===================================
# dubbo中的服务名称,容器名称,一般就是项目名称
dubbo.application.name=xdclass-video-web
# dubbo的通讯协议名称
dubbo.protocol.name=dubbo
# zookeeper的通讯协议名称
dubbo.registry.protocol=zookeeper
#zookeeper注册中心的地址
dubbo.registry.address=192.168.1.200:2181
dubbo.scan.base-packages=com.guangyang.xdclass
#设置消费者与服务提供者连接超时时间(ms)
dubbo.consumer.timeout=600000
#设置在启动的时候是否检查服务存在
dubbo.consumer.check=false
#暴露服务端口(默认端口是20880,修改端口,不同的服务提供者端口不能重复)
dubbo.protocol.port=20880

3、 引入duboo依赖

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>

        <dubbo.version>2.6.5</dubbo.version>
        <zkclient.version>0.10</zkclient.version>
        <dubbo-starter.version>0.2.1.RELEASE</dubbo-starter.version>
        <curator-framework-version>2.12.0</curator-framework-version>
    </properties>

<!--整合dubbo需要以下4个依赖-->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>dubbo</artifactId>
	<version>${dubbo.version}</version>
</dependency>
<!-- zookeeper的客户端jar依赖包 -->
<dependency>
	<groupId>com.101tec</groupId>
	<artifactId>zkclient</artifactId>
	<version>${zkclient.version}</version>
</dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
	<groupId>com.alibaba.boot</groupId>
	<artifactId>dubbo-spring-boot-starter</artifactId>
	<version>${dubbo-starter.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.curator</groupId>
	<artifactId>curator-framework</artifactId>
	<version>${curator-framework-version}</version>
</dependency>

9.1. 配置文件存放问题

Springboot会扫描主函数的目录下所有的文件(包括maven模块),但是不会扫描maven模块中的xml配置文件,xml配置文件需要放在当前springboot模块下

9.2. 日志报错

在common-util中引入的依赖为

<!--===========================整合dubbo需要以下4个依赖============================-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
        </dependency>
        <!--dubbo-springBoot依赖-->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
        <!-- zookeeper的客户端jar依赖包 -->
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

需要排除slf4j-log4j12否则启动项目时候,日志会报错

9.3. Video项目结构

对视频的删除以及保存和更新操作都是管理员才能做的事情,普通用户无法操作,后期会对权限进行管理。

在这里插入图片描述

10. 日志配置

# =========================================== 日志配置 ===================================
logging.level.com.atguigu=info
#logging.path=
# 不指定路径在当前项目下生成springboot.log日志
# 可以指定完整的路径;
#logging.file=G:/springboot.log

# 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件
logging.file.path=/var/log

#  在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n

11. Vidoe相关接口完善和规范协议

@Controller
public class VideoController {
    @Reference
    VideoService videoService;

    /**
     * 分页接口
     * @param page 第几页,默认第1页
     * @param size 一页多少条,默认10条
     * @return
     */
    @GetMapping("page")
    @ResponseBody
    public Video page(@RequestParam(value = "page" , defaultValue = "1") int page,
                      @RequestParam(value = "size" , defaultValue = "10") int size) {
        return null;
    }

    /**
     * 通过id查找视频接口
     * @param videoId 视频id
     * @return
     */
    @GetMapping("find_by_id")
    @ResponseBody
    public Video findVideoById(@RequestParam(value = "video_id" , required = true)String videoId) {
        return videoService.findVideoById(videoId);
    }

    /**
     * 保持视频接口
     * @param video
     * @return
     */
    @PostMapping("save")
    @ResponseBody
    public int saveVideo(Video video) {
        return videoService.saveVideo(video);
    }

    /**
     * 通过id更新视频接口
     * @param video
     * @return
     */
    @PutMapping("update_video")
    @ResponseBody
    public int updateVideo(@RequestBody Video video) {
        return videoService.updateVideo(video);
    }

    /**
     * 通过id删除视频接口
     * @param videoId 视频id
     * @return
     */
    @DeleteMapping("del_id")
    @ResponseBody
    public int deleteVideoById(@RequestParam(value = "video_id" , required = true)String videoId) {
        return videoService.deleteVideoById(videoId);
    }
}

删除视频以及报错视频都是管理员才有的权限,将这两个接口,放到controller/admin/AdminVideoController.java中

@Controller
@RequestMapping("/admin/video")
public class AdminVideoController {

    @Reference
    VideoService videoService;

    /**
     * 保持视频接口
     * @param video
     * @return
     */
    @PostMapping("save")
    @ResponseBody
    public int saveVideo(Video video) {
        return videoService.saveVideo(video);
    }

    /**
     * 通过id更新视频接口
     * @param video
     * @return
     */
    @PutMapping("update_video")
    @ResponseBody
    public int updateVideo(@RequestBody Video video) {
        return videoService.updateVideo(video);
    }

    /**
     * 通过id删除视频接口
     * @param videoId 视频id
     * @return
     */
    @DeleteMapping("del_id")
    @ResponseBody
    public int deleteVideoById(@RequestParam(value = "video_id" , required = true)String videoId) {
        return videoService.deleteVideoById(videoId);
    }
}

12. 动态Sql语句Mybaties SqlProvider

简介:讲解什么是动态sql,及使用
动态sql:当从前端传到后台一个对象时候,里面的属性有的有值有的没有值,这时候我们只希望更新有值的,没有值的不要给我进行更新

1@UpdateProvider(type=VideoSqlProvider.class,method="updateVideo")  更新
	@InsertProvider   插入
	@DeleteProvider	  删除
	@SelectProvider   查询

2、写法

public String updateVideo(final Video video){  
   return new SQL(){{  
	   UPDATE("video");

	   //条件写法.  
	   if(video.getAuthorId()!= null){  
		  SET("author_id=#{authorId}");  
	   }    
	   if(video.getTotalEpisode()!= null){
		   SET("total_episode=#{totalEpisode}");
	   }

	   WHERE("id=#{id}");
   }}.toString();  
}

12.1. 在api模块中创建provider类

在这里插入图片描述

12.2. 反射工具类

按照上面的方法判断类中属性值是否为空,太麻烦了,在这里使用反射机制获取类中的属性和值,工具类如下

package com.guangyang.xdclass.utils;

import com.guangyang.xdclass.bean.Video;
import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 反射工具类
 */
public class ReflectionUtil {
    /**
     * 返回一个类中所有的属性(Attributes)以及值
     * 返回值中是一个List集合,集合中是Map集合,map集合只包含两个键值对
     * name --- 属性名
     * value --- 属性值
     * @param object
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    List<Map<String,Object>> getAttributesAndValue(Object object) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        List<Map<String,Object>> list = new ArrayList<>();

        // 获取实体类的所有属性,返回Field数组
        Field[] field = object.getClass().getDeclaredFields();
        for(int i = 0; i < field.length;i++){
            Map<String,Object> map = new HashMap<>();

            // 获取属性的名字
            String name = field[i].getName();
            System.out.println(name);
            map.put("name",name);

            // 将属性的首字母大写
            name = name.replaceFirst(name.substring(0,1),name.substring(0,1).toUpperCase());

            // 获取属性类型名字
            String type = field[i].getGenericType().toString();
            System.out.println(type);

            if (type.equals("class java.lang.String"))
            {
                // 如果type是类类型,则前面包含"class ",后面跟类名
                Method m = object.getClass().getMethod("get" + name);
                // 调用getter方法获取属性值
                String value = (String) m.invoke(object);
                map.put("value",value);
            }

            if (type.equals("class java.lang.Integer"))
            {
                Method m = object.getClass().getMethod("get" + name);
                Integer value = (Integer) m.invoke(object);
                map.put("value",value);
            }
            if (type.equals("class java.lang.Short"))
            {
                Method m = object.getClass().getMethod("get" + name);
                Short value = (Short) m.invoke(object);
                map.put("value",value);
            }
            if (type.equals("class java.lang.Double"))
            {
                Method m = object.getClass().getMethod("get" + name);
                Double value = (Double) m.invoke(object);
                map.put("value",value);
            }
            if (type.equals("class java.lang.Boolean"))
            {
                Method m = object.getClass().getMethod("get" + name);
                Boolean value = (Boolean) m.invoke(object);
                map.put("value",value);
            }
            if (type.equals("class java.util.Date"))
            {
                Method m = object.getClass().getMethod("get" + name);
                Date value = (Date) m.invoke(object);
                map.put("value",value);
            }

            list.add(map);
        }

        return list;
    }

//    /**
//     * 测试反射,获取类中的所有属性和值
//     */
//    public static void main(String[] args)  throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//        Video video = new Video();
//        video.setCoverImg("哈哈哈");
//        video.setPrice("1234");
//        List<Map<String, Object>> list = getAttributesAndValue(video);
//        for (Map<String, Object> stringObjectMap : list) {
//            System.out.println(stringObjectMap.get("name") + " = " + stringObjectMap.get("value"));
//        }
//    }
}

12.3. 使用反射工具类写的动态sql方法

public class VideoSqlProvider {

    public String updateVideo(final Video video){
        return new SQL(){{
            UPDATE("video");

            //条件写法.  这块使用反射机制获取属性名和属性值,并且使用谷歌的驼峰转换工具进行
            // 相应的转换
            try {
                List<Map<String, Object>> attributesAndValue = ReflectionUtil.getAttributesAndValue(video);
                for (Map<String, Object> stringObjectMap : attributesAndValue) {
                    if (stringObjectMap.get("value") != null) {
                        String name = (String) stringObjectMap.get("name");
                        // 使用谷歌的驼峰转换工具进行转换,比如属性名为testDate,转换结果为test_date
                        String caseFormat = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, name);
                        SET(caseFormat + "=#{" + name + "}");
                    }
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            WHERE("id=#{id}");
        }}.toString();
    }

}

12.4. 修改mapper接口

@UpdateProvider(type = VideoProvider.class,method = "updateVideo")
 int update(Video Video);

13. PageHelper分页插件使用

1、 基本原理
sqlsessionFactory -> sqlSession-> executor -> mybatis sql statement
通过mybatis plugin 增加拦截器,然后拼装分页
org.apache.ibatis.plugin.Interceptor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pEtuPdtd-1610709121074)(http://img.itvip666.com/blog-img/2021/01/05/1609814873525.png)]

2、 引入依赖

<!-- 分页插件依赖 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.1.0</version>
</dependency>

3、 配置类

	@Configuration
	public class MyBatisConfig {
	    @Bean
	    public PageHelper pageHelper(){
	        PageHelper pageHelper = new PageHelper();
	        Properties p = new Properties();
	        p.setProperty("offsetAsPageNum","true");
	        p.setProperty("rowBoundsWithCount","true");
	        p.setProperty("reasonable","true");
	        pageHelper.setProperties(p);
	        return pageHelper;
	    }
	}

4、 包装类

PageHelper.startPage(page, size);
PageInfo<VideoOrder> pageInfo = new PageInfo<>(list);

5、 服务层分页

@Override
    public Map<String, Object> page(int page, int size) {
        Map<String,Object> map = new HashMap<>();
        PageHelper.startPage(page, size);
        List<Video> list = videoMapper.findAll();
        PageInfo<Video> videoPageInfo = new PageInfo<>(list);
        map.put("total_size",videoPageInfo.getTotal()); // 获取总条数
        map.put("total_page",videoPageInfo.getPages()); // 总页数
        map.put("current_page",page); // 获取当前页
        map.put("data",videoPageInfo.getList()); // 获取数据
        return map;
    }

13.1. 关于报错但不影响正常使用问题

1、 今天使用pagehelper的时候一直出现如下警告,但是系统依旧可以正常使用。

在这里插入图片描述

2、 原因:

项目里面使用的是dubbo进行分布式调用,只在服务层引入了pagehelper依赖,表现层没有引入pagehalper依赖,服务层查询到数据之后,先将数据序列化,在表现层需要进行反序列化,但是表现层没有引入pagehelper依赖,反序列化的时候找不到:com.github.pagehelper.Page这个包,所以报出了这个警告,但是项目依旧可以正常使用,因为Page类是ArrayList的子类,ArrayList类是可以找到的,所以依旧可以使用。

3、 解决办法:
在表现层引入pagehelper依赖

14. 单机和分布式应用的登录检验讲解

简介:讲解单机和分布式应用下登录校验,session共享,分布式缓存使用

1、单机tomcat应用登录检验

sesssion保存在浏览器和应用服务器会话之间
用户登录成功,服务端会保证一个session,当然会给客户端一个sessionId,
客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId

2、分布式应用中session共享
真实的应用不可能单节点部署,所以就有个多节点登录session共享的问题需要解决
1)tomcat支持session共享,但是有广播风暴;用户量大的时候,占用资源就严重,不推荐
2)使用redis存储token:
服务端使用UUID生成随机64位或者128位token,放入redis中,然后返回给客户端并存储在cookie中,用户每次访问都携带此token,服务端去redis中校验是否有此用户即可(也有缺点,不太推荐)

15. 微服务下登录检验

简介:微服务下登录检验解决方案 JWT讲解 json wen token

1、JWT 是一个开放标准,它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。
JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名

简单来说,就是通过一定规范来生成token,然后可以通过解密算法逆向解密token,这样就可以获取用户信息

{
		id:888,
		name:'小D',
		expire:10000
	}
	
	funtion 加密(object, appsecret){
		xxxx
		return base64( token);
	}

	function 解密(token ,appsecret){

		xxxx
		//成功返回true,失败返回false
	}


优点:
1)生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库
2)存储在客户端,不占用服务端的内存资源

缺点:
token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,如用户权限,密码等

2、JWT格式组成 头部、负载、签名
header+payload+signature

头部:主要是描述签名算法。
负载:主要描述是加密对象的信息,如用户的id等,也可以加些规范里面的东西,如iss签发者,exp 过期时间,sub 面向的用户。
签名:主要是把前面两部分进行加密,防止别人拿到token进行base解密后篡改token。

3、关于jwt客户端存储
可以存储在cookie,localstorage和sessionStorage里面

16. Jwt通用工具类

package com.qinglingwang.gmall.util;

import io.jsonwebtoken.*;

import java.util.Date;
import java.util.Map;

public class JwtUtil {
    // 过期时间为一周
    private static final long EXPIRE = 1000*60*60*24*7;
    /**
     * 加密(默认过期时间为一周)
     * @param key 服务器公共密钥
     * @param param 用户信息
     * @param salt 盐值(ip+time)
     * @return token
     */
    public static String encode(String key, Map<String,Object> param, String salt){
        if(salt!=null){
            key+=salt;
        }
        JwtBuilder jwtBuilder = Jwts.builder().signWith(SignatureAlgorithm.HS256,key)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE ));

        jwtBuilder = jwtBuilder.setClaims(param);

        String token = jwtBuilder.compact();
        return token;

    }


    /**
     * 解密,salt是key的盐值
     * @param token
     * @param key
     * @param salt
     * @return
     */
    public  static Map<String,Object> decode(String token ,String key,String salt){
        Claims claims=null;
        if (salt!=null){
            key+=salt;
        }
        try {
            claims= Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
        } catch ( JwtException e) {
           return null;
        }
        return  claims;
    }
}

17. 微信登录

17.1. 数据信息安全–微信授权一键登录功能介绍

简介:讲解登录方式优缺点和微信授权一键登录功能介绍
1、手机号或者邮箱注册
优点:
1)企业获取了用户的基本资料信息,利于后续业务发展推送营销类信息
2)用户可以用个手机号或者邮箱获取对应的app福利注册送优惠券
3)反馈信息的时候方便,直接报手机号即可账户出问题,被盗等
缺点:
1)步骤多
2)如果站点不安全,如站点被攻击,泄漏了个人信息,如手机号,密码等
3)少量不良企业贩卖个人信息,如手机号

2、OAuth2.0一键授权登录
例子:
豆瓣:www.douban.com
小D课堂:www.xdclass.net

优点:
使用快捷,用户体验好,数据相对安全
缺点:
1、反馈问题麻烦,比较难知道唯一标识
2、如果是企业下面有多个应用,其中有应用不支持Auth2.0登录,则没法做到用户信息打通,积分不能复用等
如app接入了微信授权登录,但是网站没有,则打不通,
或者授权方只提供了一种终端授权,则信息无法打通,

3、选择方式:
1)看企业和实际业务情况
2)务必区分,普通密码和核心密码

17.2. 微信扫一扫开发前期准备

简介:讲解微信扫一扫功能相关开发流程和资料准备

1、 微信开放平台介绍(申请里面的网站应用需要企业资料)
网站:https://open.weixin.qq.com/

2、 什么是appid、appsecret、授权码code
appid和appsecret是 资源所有者向申请人分配的一个id和秘钥
code是授权凭证,A->B 发起授权,想获取授权用户信息,那a必须携带授权码,才可以向B获取授权信息
(你要从我这里拿东西出去,就必须带身份证)

3、 将appid以及appsecret还有开放平台的回调地址写入到配置文件中,当上线时候以便修改,需要使用配置类,使用springboot进行读取相关参数

@Configuration
@PropertySource(value="classpath:application.properties")
public class WeChatConfig {
    /**
     * 公众号的appid
     */
    @Value("${wxpay.appid}")
    private String appid;

    /**
     * 公众号的秘钥
     */
    @Value("${wxpay.appsecret}")
    private String appsecret;

    /**
     * 开放平台的appid
     */
    @Value("${wxopen.appid}")
    private String openAppid;

    /**
     * 开放平台的appsecret
     */
    @Value("${wxopen.appsecret}")
    private String openAppsecret;

    /**
     * 开放平台回调url
     */
    @Value("${wxopen.redirect_url}")
    private String openRedirectUrl;

    public String getAppid() {
        return appid;
    }

    public void setAppid(String appid) {
        this.appid = appid;
    }

    public String getAppsecret() {
        return appsecret;
    }

    public void setAppsecret(String appsecret) {
        this.appsecret = appsecret;
    }

    public String getOpenAppid() {
        return openAppid;
    }

    public void setOpenAppid(String openAppid) {
        this.openAppid = openAppid;
    }

    public String getOpenAppsecret() {
        return openAppsecret;
    }

    public void setOpenAppsecret(String openAppsecret) {
        this.openAppsecret = openAppsecret;
    }

    public String getOpenRedirectUrl() {
        return openRedirectUrl;
    }

    public void setOpenRedirectUrl(String openRedirectUrl) {
        this.openRedirectUrl = openRedirectUrl;
    }
}

17.3. 微信Oauth2.0交互流程

参考:
https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN

1、区分角色 用户,第三应用,微信开放平台:画图讲解
2、如果想看时序图知识,请跳转到微信支付章节,时序图知识讲解

17.4. 获取微信开放平台APPID/APPsecret

什么是域名映射: 就是当前开发机器的ip 通过和域名绑定,让外部网络可以通过域名就行访问到本地机器接口。

重点看这!!!! 由于Ngrock官方近期改版,导致不能使用。

课程用到ngrock的两个功能: 微信登录 + 微信支付(重点)

无奈微信开放平台对开发者不友好,一定要公司营业执照 + 300元认证认证,才可以认证微信开放平台接入微信登录。

建议:微信登录功能比较简单,可以看课程即可,课程代码也可以直接在自己项目中使用(小D课堂免费使用)实操的话需要使用自己公司认证的微信开发平台账号,如果自己公司有账号则可以直接使用,没的话建议是跳过。

下面的是可以测试使用,出现二维码,但是不能进行回调
狼途
AppID:wx025575eac69a2d5b
AppSecret:f5b6730c592ac15b8b1a5aeb8948a9f3
授权回调域名: http://16webtest.ngrok.xiaomiqiu.cn

建豪器械
AppID:wxe9ef2df5e786b62a
AppSecret:c973a39ce5d2638e95f251a893cdfac4
授权回调域名: http://xdclasstest2.ngrok.xiaomiqiu.cn

微信支付功能可以实操,也是重点功能,小D课堂单独开发了支付网关对接微信支付,

使用免费的域名映射工具,生成的是随机域名。
工具下载:https://natapp.cn/
工具使用文档:https://natapp.cn/article/natapp_newbie

支付回调域名可以自定义,微信支付接入详情
https://mp.weixin.qq.com/s?__biz=MzUyMDg1MDE2MA==&mid=2247484232&idx=1&sn=9adc2c399ae7093d0e9e85f05aaff0e4&chksm=f9e55cd7ce92d5c159e49ed42b6a77f2210434bb105cffcc9670cb24ad46cf4f49cbf7adaa96&mpshare=1&scene=23&srcid=1016NFMGqvbyyORetzSf7n54#rd

17.5. HttpClient4.x工具获取使用

简介:讲解httpClient4.x相关依赖,并封装基本方法。
1、加入依赖

<dependency>
	 <groupId>org.apache.httpcomponents</groupId>
	 <artifactId>httpclient</artifactId>
	 <version>4.5.3</version>
 </dependency>
 <dependency>
	 <groupId>org.apache.httpcomponents</groupId>
	 <artifactId>httpmime</artifactId>
	 <version>4.5.2</version>
 </dependency>

 <dependency>
	 <groupId>commons-codec</groupId>
	 <artifactId>commons-codec</artifactId>
 </dependency>
 <dependency>
	 <groupId>commons-logging</groupId>
	 <artifactId>commons-logging</artifactId>
	 <version>1.1.1</version>
 </dependency>
		 <dependency>
		 <groupId>org.apache.httpcomponents</groupId>
		 <artifactId>httpcore</artifactId>
 </dependency>


<!-- gson工具,封装http的时候使用 -->
<dependency>
 <groupId>com.google.code.gson</groupId>
 <artifactId>gson</artifactId>
 <version>2.8.0</version>
</dependency>

引入依赖注意事项,在springboot中已经对如下进行了版本控制,我们不需要在我们的自定义的父依赖parent中控制版本,直接在common-util中引入依赖即可(不需要填写版本号)

<dependency>
   <groupId>commons-codec</groupId>
   <artifactId>commons-codec</artifactId>
</dependency>

</dependency>
	 <dependency>
	 <groupId>org.apache.httpcomponents</groupId>
	 <artifactId>httpcore</artifactId>
</dependency>

工具类

package net.xdclass.xdvideo.utils;

import com.google.gson.Gson;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * 封装http get post
 */
public class HttpUtils {


    private static  final Gson gson = new Gson();

    /**
     * get方法
     * @param url
     * @return
     */
    public static Map<String,Object> doGet(String url){

        Map<String,Object> map = new HashMap<>();
        CloseableHttpClient httpClient =  HttpClients.createDefault();

        RequestConfig requestConfig =  RequestConfig.custom().setConnectTimeout(5000) //连接超时
                .setConnectionRequestTimeout(5000)//请求超时
                .setSocketTimeout(5000)
                .setRedirectsEnabled(true)  //允许自动重定向
                .build();

        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(requestConfig);

        try{
           HttpResponse httpResponse = httpClient.execute(httpGet);
           if(httpResponse.getStatusLine().getStatusCode() == 200){

              String jsonResult = EntityUtils.toString( httpResponse.getEntity());
               map = gson.fromJson(jsonResult,map.getClass());
           }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                httpClient.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return map;
    }


    /**
     * 封装post
     * @return
     */
    public static String doPost(String url, String data,int timeout){
        CloseableHttpClient httpClient =  HttpClients.createDefault();
        //超时设置

        RequestConfig requestConfig =  RequestConfig.custom().setConnectTimeout(timeout) //连接超时
                .setConnectionRequestTimeout(timeout)//请求超时
                .setSocketTimeout(timeout)
                .setRedirectsEnabled(true)  //允许自动重定向
                .build();


        HttpPost httpPost  = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        httpPost.addHeader("Content-Type","text/html; chartset=UTF-8");

        if(data != null && data instanceof  String){ //使用字符串传参
            StringEntity stringEntity = new StringEntity(data,"UTF-8");
            httpPost.setEntity(stringEntity);
        }

        try{

            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            if(httpResponse.getStatusLine().getStatusCode() == 200){
                String result = EntityUtils.toString(httpEntity);
                return result;
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try{
                httpClient.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        return null;

    }
}

2、封装工具类的使用
封装doGet doPost

17.6. 微信扫码登录回调本地域名映射工具Ngrock(现在不可用了)

简介:讲解微信扫码回调本地域名ngrock讲解

1、为什么要用这个,微信扫码需要配置回调,需要配置对应的域名在本地电脑开发,微信没法回调,所以需要配置个地址映射,就是微信服务器可以通过这个地址访问当前开发电脑的地址

2、使用文档:
https://mp.weixin.qq.com/s/oakwABv56Jc6u450AHt9iA

17.7. 微信扫码登录回调本地域名映射EchoSite

17.7.1. EchoSite配置

官网:https://www.echosite.cn/

1、 点击控制台,然后选择抢占域名,需要付费,一个月3元
2、 点击客户端下载,选择生成链接(需要下载客户端以及配置文件)

在这里插入图片描述

3、 修改配置文件
修改手机号以及秘钥

在这里插入图片描述


在这里插入图片描述

4、 启动echosite

echosite -config=config.yml start web_b 

17.7.2. 配置nginx

upstream guangyang.easy.echosite.cn {
	#映射到的ip地址以及端口号
	server	guangyang.easy.echosite.cn:80;
}
server {
    listen       80;
	# http://16webtest.ngrok.xiaomiqiu.cn/
    # server_name需要配置成将要访问的域名
	server_name	 16webtest.ngrok.xiaomiqiu.cn;
    location / {	
		# 配置域名,注意事项域名不要带www. 否则不好使
		#指向上面配置的upstream www.passport.com
		proxy_pass    http://guangyang.easy.echosite.cn;
		index  index.html index.htm;  
    }
}

17.7.3. 本地域名解析配置

如果想要访问16webtest.ngrok.xiaomiqiu.cn映射到guangyang.easy.echosite.cn然后在映射到本地,你需要配置本地域名解析

127.0.0.1 16webtest.ngrok.xiaomiqiu.cn

这回当扫码登录成功后,最终会通过guangyang.easy.echosite.cn映射到本地

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sgy_yuebin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值