SpringCloud

Mybatis-Plus

原理

常用注解

TableId注解:

value值代表数据库中主键列的列名

type代表主键类型,常用的类型有:IdType.AUTO:数据库 ID 自增

当设置type为ID自增时,表示其主键字段的值由数据库自动生成,即使用数据库的自增机制,当我们插入记录时,并不需要显式的设置主键值,数据库会自动生成并赋值。

@TableId中的type效果与Mybatis中的useGeneratedKeys="true" keyProperty="id" 配置具有相似的作用,都是用来指定主键生成策略,并确保在插入记录时能够正确处理主键值。

核心用法

条件构造器

基于id的简单查询直接使用现成方法就行。

构建复杂的 WHERE 条件、选择特定的查询字段以及部分更新某些字段的值时可以使用QueryWrapper

知识补充:方法引用

将已经有的方法拿来用,当做函数式接口中抽象方法的方法体

注意:

1.需要有函数式接口

2.被引用方法已经存在

3.被引用方法的形参和返回值需要跟抽象方法保持一致

(在④中,被引用方法的形参,需要与抽象方法的第二个形参到最后一个形参保持一致,返回值保持一致)

4.被引用方法的功能要满足当前要求

方法引用的分类

①引用静态方法

类名::方法名

举例:将集合中string类型的数字转换为int类型

②引用成员方法

Ⅰ其他类:对象::方法名

Ⅱ本类:this::方法名

特殊点:如果本类为static,静态方法中没有this和super,这种情况下无法使用this::方法名,只能使用创建本类对象::方法名(比如main: public static void main())

③引用构造方法 类名::new

特殊点:引用对象构造方法时,如果构造方法的形参与抽象方法的形参不一致,需要在java bean中补写一个与其参数一致的构造方法,而返回值无需考虑因为在调用构造方法时,已经创建好对象了。

④其他调用方式

Ⅰ使用类名引用成员方法 类名::方法名

该类名是有限制的,必须与被引用方法的调用者的类一致

Mybatis-Plus中LambdaQueryWrapper用到了类名::方法的方法引用

自定义SQL

当mp无法编写复杂的字段比如count(id) as id,我们可以在定义wrapper时使用setSql来拼接sql语句。但wrapper是定义在service层的,业务层按照标准不能写sql语句,违背企业开发规范,因此将自定义的wrapper传递到mapper层中,以替代复杂的where条件而自己编写其他字段

IService接口

用在Service层

在ServiceImpl需要自动注入该mapper的bean对象

知识补充:@RequestParam@PathVariable注解区别

@RequestParam用于将请求参数(通常是查询参数或表单数据)绑定到控制器方法的参数上。@RequestParam可以设置默认值,并且可以指定参数是否是必需的

@PathVariable用于将URL路径中的变量绑定到控制器方法的参数上。

BeanUtil

选择IService还是BaseMapper?

当简单的crud时,无需书写复杂业务逻辑时直接在controller层中调用service,而当需要写逻辑时,在service层调用basemapper来自定义sql。

简单情况(controller层)

复杂情况

IService中的Lambda方法

模糊查询

lambdaQuery()的返回值是wrapper,因此后面可以直接拼接条件

lambdaUpdate

IService的批量插入操作

Docker

容器是隔离的,有自己的文件系统,端口等

docker ps :查看所有正在运行的容器

docker ps -a:查看所有容器,包括停止的容器

docker stop/start:停止/开启容器,并不是删除掉容器

docker save/load:打包/加载镜像

docker exec -it 容器名 bash:进入容器内部,并以命令行的形式

数据卷

方式①:挂载到宿主机下的固定位置/var/lib/docker/volume/....

使用后docker inspect 容器名查看容器信息(docker volume inspect 卷名--查看卷的信息)

方式②,指定挂载的本地目录

mysql存在匿名卷。当删除容器再创建容器时,匿名卷的名字不一样会导致原来容器内的数据没法匹配到新数据卷中,同时数据卷挂载位置太深。因此考虑挂载到指定的本地目录中

自定义镜像

docker load -i xx.tar 用于从一个归档文件(通常是 .tar 文件)中加载 Docker 镜像。这个命令可以将预先保存的 Docker 镜像从文件系统中导入到 Docker 的本地镜像库中。

对自定好的镜像进行docker run

网络

当两个容器处于同一个网桥中,其可以互相连接,可以通过容器名即可ping通,解决了ip地址变化带来的无法ping通

项目部署

java应用:打包成jar包并准备好DockerFile,上传至虚拟机,使用docker build创建镜像,创建容器并启动

静态资源:html和conf:将其与容器内的nginx的html和conf进行挂载,创建容器并运行

mysql:创建容器挂在完成后启动

保证三者都在一个网桥内,使用容器名互联

微服务

微服务拆分,每一个模块单独有自己的数据库,需要有自己的启动类,配置文件

远程调用

当微服务拆分后,模块之间物理上相互独立,当一个模块需要使用另一个模块的数据时,可以通过远程调用来实现

有关第三方bean注入

方法一:@autowired(不推荐)

方法二:@RequeiredArgsConstrustor +想要注入的对象加finnal修饰。构造函数注入

使用RestTemplate

需要自定义bean注入,书写麻烦,并且在多实例部署时不知道哪一个实例还在健康运行

使用服务中心Nacos

Nacos使用步骤

1.首先基于docker部署nacos服务,nacos也依赖于mysql数据库,需要在nacos配置文件中配置数据库的连接,并在mysql中初始化nacos所需要的表结构,运行nacos实例

这样在nacos中可以查看到注册的服务

服务发现

消费者去nacos订阅服务

使用discoveryClient中的方法拉取服务列表,获取到其Uri,这样解决了写死服务ip 的做法,实现了负载均衡

OpenFeign

最佳实践

新建一个api模块,用于抽取所有的Feign客户端和所需要用到的实体类

引入依赖,即OpenFeign所需要的依赖

默认情况下,Spring Boot 会扫描启动类所在包及其子包中的所有组件(包括配置类、服务类、控制器类、仓库类等),并自动进行Spring容器的注册和管理,但不会自动处理Mybatis的Mapper接口,因此MapperScan注解会自动将 MyBatis 的 Mapper 接口注册为 Spring 的 Bean,使其可以被注入和使用。

网关

网关使用步骤

GlobalFilter

自定义过滤器,实现业务的校验登陆操作

AuthProperties类

jwt类,未添加component注解,原因是EnableConfigurationProperties注解,Spring Boot 会自动将配置文件中的属性值绑定到指定的属性绑定类上,并且将该属性绑定类注册为 Spring 容器中的一个 bean,这样就不需要在属性绑定类上再加 @Component 注解了

Spring 会扫描所有的 @Configuration 类,并处理这些类中的 @Bean 方法来创建和管理 Bean。同时被@Configuration修饰的类本身也会被创建为bean

Spring Boot 会处理 @EnableConfigurationProperties 注解,将 JwtProperties 类注册为一个 Bean,并将配置文件中的属性绑定到 JwtProperties 实例中。

实现过滤器

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    //排除路径,无需过滤
    @Autowired
    private AuthProperties authProperties;

    //解析jwt 的工具类
    @Autowired
    private JwtTool jwtTool;
    //spring提供的用于路径匹配的工具类
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取request请求体
        ServerHttpRequest request = exchange.getRequest();
        //2.判断是否需要进行拦截
        //getPath获取到的是路径对象,需要转成字符串
        if(isEXclude(request.getPath().toString())){
            return chain.filter(exchange);
        }
        //3.获取token
        String token = null;
        Long userId = null;
        List<String> headers = request.getHeaders().get("authorization");
        if(headers!=null && !headers.isEmpty()){
            //判断请求头不为空
            token = headers.get(0);
        }
        //4.校验并解析token,获取用户信息,拿到的token中封装着所需要的jwt
        try {
            //在parseToken若解析失败,则会抛出异常,我们希望捕获该异常而不希望抛出,终止该次请求
                userId = jwtTool.parseToken(token);
        } catch (UnauthorizedException e) {
            //拦截,设置响应码为401
            //获取响应体,中封装着响应码
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //设置终止,不会再后续转发请求至其他filter
            return  response.setComplete();
        }
        //5.传递用户信息
        System.out.println("userId:"+ userId);
        //6.放行
        return chain.filter(exchange);
    }

    //判断路径是否需要排除
    private boolean isEXclude(String string) {
        for (String pathPattern : authProperties.getExcludePaths()) {
            if (antPathMatcher.match(pathPattern, string)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

这里使用的是Spring cloud gateway的组件,在spring项目启动时将该过滤器注册为bean需要使用component注解而非WebFliter注解。

ServerHttpRequest对象

jwt,token,authorization

List<String> headers = request.getHeaders().get("authorization");

生成的token(jwt)即被封装在authorization中,http请求体的请求头中,请求头中是一个map,可以根据键值来查找所需要的请求头。

传递用户信息

exchange提供了mutate方法,其返回一个build,mutate() 方法在 Spring WebFlux 中用于创建一个新的 ServerWebExchange 实例,该实例是当前 exchange 的一个变体。这个方法允许你在不修改原始 ServerWebExchange 实例的情况下,对请求或响应进行修改。request中的consumer是函数式接口,这里使用lambda表达式,添加一个新的请求头和值,build构建的结果会返回一个新的exchange,将其传递给下一层的过滤器进行操作。

拦截器获取用户信息

思想:我们在微服务模块的所有controller方法之前,获取到当前用户的id,以便进行后续操作。我们可以考虑使用springMVC 的拦截器实现。

当网关通过过滤器获取到用户信息并转发给微服务时,避免在每个微服务中都要进行获取,我们定义了拦截器,通过拦截器获取到用户信息后存入到ThreadLocal中。而ThreadLocal具有隔离性

public class UserInfoInterceptor implements HandlerInterceptor {
    public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
        //1.获取登陆用户信息
        String userInfo = request.getHeader("user-info");

        //2.判断是否获取到用户登录信息,如果有,则存入ThreadLocal中
        if (StrUtil.isNotBlank(userInfo)) {
            UserContext.setUser(Long.valueOf(userInfo));
        }
        //3.放行
        return true;
    }

    public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception {
        UserContext.removeUser();
    }
}

定义拦截器并重写方法,preHandle用于在执行controller之前获取到用户id,aferCompletion发生在完成调用请求后完成渲染后最后执行。

定义好拦截器后需要进行注册

@Configuration
@ConditionalOnClass(WebMvcConfigurer.class)
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserInfoInterceptor());
    }
}
1.自动装配的问题

在购物车微服务中,spring自动装配扫描不到该拦截器,解决方法为

spring factories文件
2.SpringMVC环境问题

在之前的网关模块中,pom文件中引入的是

spring-cloud-starter-gateway

环境。Spring Cloud Gateway 是基于 WebFlux 而不是基于 Spring MVC,因此使用SpringMVC的 拦截器这种定义方式是适用于 Spring Cloud Gateway 环境的。

为了避免这种问题,在mvcconfig上添加注解

@ConditionalOnClass(DispatcherServlet.class)

这样,拦截器所生效的模块均为业务模块,而不会在没有springMVC环境的网关模块中生效

openfeign传递给其他服务

其目的是允许你在发送 HTTP 请求之前对请求进行修改。RequestTemplate 是 Feign 用于表示 HTTP 请求的模板,它允许你修改请求的 URL、请求头、请求体等。

为了实现该接口方便,直接使用匿名内部类来实现该接口,并将其注册为Bean。当创建一个 RequestInterceptor 的实现类时,你需要实现 apply 方法来定义你的拦截逻辑。将实现 RequestInterceptor 接口的 Bean 注册到 Spring 上下文中,Feign 会自动将其应用到所有的 Feign 客户端,无需autowired

注意:这里的配置类并没有使用Configuration注解来标记,它是通过 Feign 的配置机制来加载的。在Feign的客户端注解中指定出配置类即可。

微服务体系下的登录整体思路

配置管理

配置热更新

代码部分将想要进行热更新的变量使用配置类来封装,并生成Bean对象,在使用时注入Bean对象即可。

在Nacos中创建配置文件。命名格式为:

微服务保护和分布式事务

微服务保护:Sentinel:请求限流、线程隔离、FAllback、服务熔断

分布式事务:Seata

XA模式

AT模式

RabbitMQ

SpringAMQP收发消息

WorkQueue

利用死信交换机实现超时订单取消

将规定时间内未付款的订单取消

用户进行下单(交易服务),接收消息都是在交易服务中进行的

接口常量

ES

倒排索引

①将字段的index设置为false,代表该字段将不会创建索引,因此也无法通过该字段进行搜索操作,ES每个字段的index默认为true

②当字段类型为text时,es设置的索引为倒排索引。其他类型只要index设置为true,es将取决于字段类型来设置不同的索引和查询方式。

ES对索引库的操作

创建:PUT /索引库名

添加字段

_mapping是固定路径

ES对文档的操作

批量操作

JavaRestClient操作

在java代码中操作es

建立与es连接

RestHighLevelClient 是 Elasticsearch 提供的 Java 客户端,用于通过 RESTful API 与 Elasticsearch 集群进行交互。它封装了底层的 RestClient,提供了一些高级的操作,使开发人员可以更方便地使用 Java 代码来执行各种 Elasticsearch 操作,如索引文档、搜索文档、更新文档、删除文档等。

索引库操作

在初始化好RestHighLevelClient客户端后,通过XxxIndexRequest来操作es(Create,Get,Delete)

发送请求为client.indices().xxx()方法:create、get(exists)、delete

文档操作

DSL语句

在之前的对索引和文档的crud中,仅仅是对简单的id进行查询,为构造复杂的查询条件,需要用到DSL语句。

查询

叶子查询

复合查询

排序、分页、高亮

JavaRestClient查询

发送请求仍然分成三部

构建查询DSL

.source()是对应整个大请求体,QueryBuilders中提供大量的静态方法,对比下图

构建排序和分页DSL

数据聚合

类似于sql中的group by

****************************************************************

Springboot中的一些忘记的知识点

AOP面向切面编程

该类添加@component:AOP底层是基于动态代理,当需要调用被标记的方法时,调用的是该动态代理,注入的也是该动态代理,因此需要交给IOC容器管理

@Aspect:标识为一个切面类

如果是Around,需要使用ProceedingJoinPoint类的实例来调用原始方法执行,而Before,After等需要使用的是其父类JoinPoint

调用原始方法执行使用proceeding方法。

切入点表达式用的比较多的是基于注解的方式

①自定义一个注解

②定义表达式

③在需要的方法上添加自定义注解即可

HTTP请求

请求参数

Bean的组件扫描

并且在测试类中,扫描范围默认与启动类扫描范围一样

存在同类型多个bean

在service接口中存在多个实现类时,使用以上注解用于区分注入哪一个实现类。

但是在Dao层一般将mapper注解直接加在mapper接口上,原因是Mybatis底层基于动态代理为mapper实现好了实现类,而且一般只有一个,所以不会有同类型多个bean的情况

第三方Bean

当自定义的bean中需要其他类型的bean时,可以直接以形参的方式设置该bean

Spring自动配置

在引入第三方依赖后引入第三方依赖中定义好的bean和配置类,从而实现自动配置。

方法一:使用ComponentScan注解,其中添加要扫描的包(使用组件扫描的方式

方法二:import

方法三:spring.factories文件(使用Spring自动配置的方式

需要在第三方依赖中(常用于微服务各个模块)新建spring.factories文件。

springboot 3.x版本废弃spring.factories文件,改用

组件扫描和自动配置的区别

自定义starter

@Configuration注解和@ConfigurationProperties(prefix = "")注解

Configuration:

这个注解通常用于声明该类是一个配置类,定义和管理 Spring 应用程序上下文中的 Bean。并且可以包含一个或多个使用 @Bean 注解的方法。这些方法负责创建和配置 Bean,然后将这些 Bean 注册到 Spring 容器中

ConfigurationProperties:

这个注解常用于属性绑定类中,主要目的是从配置文件(application.yml)中读取属性并将它们注入到类的字段中。这样在其他类中需要使用到配置文件中的值时,可以讲属性绑定类注入并直接获取起属性值。

二,HTTP协议

HTTP协议是一种无状态的协议,无状态的意思是每个 HTTP 请求都是独立的,与其他请求没有关联。这意味着服务器在处理每个请求时不会保留任何前一个请求的状态信息

在使用 Spring MVC 进行 Web 开发时,HTTP 请求被封装在 HttpServletRequest 对象中,而服务器返回的信息则被封装在 HttpServletResponse 对象中。

在 Spring MVC 中,控制器方法的返回值会被自动处理并封装到 HttpServletResponse 的响应体中。

如果想直接操作请求和相应对象,可以直接在控制器方法中获取请求(HTTPServletRequest),设置状态码、内容类型,并写入响应体。通过这种方式,可以完全控制 HTTP 响应的各个方面,以满足特定需求。

为了在无状态HTTP上管理会话,有Cookie,Session,JWT技术。

一次会话:浏览器打开到关闭这一整个过程中。

多次请求:在一次会话中可以有多次请求。

Cookie

Cookie是存放在浏览器本地中,当浏览器发现Response中setCookie有值时,会将其存在本地

在请求头中的Cookie是用于存放服务端传回来的Cookie值,

响应头中的Set-Cookie字段存放的是本次会话一直使用的Cookie

JWT令牌技术

流程:在每次请求登录接口时,如果校验通过,则生成JWT令牌,其中封装用户信息并返回给前端,浏览器拿到后保存在本地并在之后的每次请求中将jwt令牌存放在Request请求头的token字段中(这是由前端完成的),服务端只需要每次拦截请求获取到请求头中的token进行校验jwt是否合法。

直接创建好工具类进行调用

public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     *
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息
     * @return
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);

        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);

        return builder.compact();
    }

    /**
     * Token解密
     *
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }
拦截器、过滤器

在传统的 Java Web 单体应用中,通常使用的是 Servlet 过滤器,需要实现Filter接口,并添加@WebFilter注解并重写其中的方法doFilter。

并且Filter是JavaWeb的组件不是Spring的,要想生效还需要在启动类上添加@ServletComponentScan注解才能生效。

@WebFilter(urlPatterns = "/*")
public class MyServletFilter implements Filter {
    
    //有默认实现,可以不重写
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化逻辑
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 放行之前的处理逻辑

        //放行
        chain.doFilter(request, response);

        
        // 放行之后的处理逻辑
    }

    //有默认实现,可以不重写
    @Override
    public void destroy() {
        // 清理资源
    }
}

在基于 Spring Cloud Gateway 的微服务架构中,使用的是 Spring WebFlux 的过滤器。这些过滤器与传统的 Servlet 过滤器不同,因为 Spring WebFlux 是基于响应式编程模型的。

拦截器

拦截器是Spring框架提供的,使用步骤需要定义拦截器后在配置类中添加进拦截器。

webMVC配置类的实现有两种方法:①继承WebMvcConfigurationSupport类

②实现WebMvcConfigurer接口(更推荐)

https://blog.csdn.net/m0_73249076/article/details/139451649?spm=1001.2014.3001.5506

在preHandler方法中,只有当return 的值为true时,才能放行,进入到访问的controller执行业务。若为false,则只能执行postHandler方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值