后端登录校验——Filter过滤器和Interceptor拦截器

一、Filter过滤器

前面我们学会了最先进的会话跟踪技术jwt令牌,那么我们要让用户使用某些功能时就要根据jwt令牌来验证用户身份,来决定他是否登陆了、让不让用户访问这个页面(或功能)

但是这样一来,没发一个请求,后端都要执行一次jwt逻辑判断的逻辑,就会很麻烦

那就需要一个工具:【Filter过滤器】,前端发完请求之后卡在中间,执行一次jwt令牌校验,先过他这关再访问

另外我们前面了解过Servlet是什么了,那么Filter其实也就是【Servlet容器】的【过滤器】,也是Servlet新增的一个功能。“广义上” Filter就是Servlet里的一个【子类】,“狭义上” Filter是Servlet里的一个【接口】

完整官方的表述就是:【实现了javax.servlet.Filter接口的类】

那么【javax.servlet.Filter接口】里定义了三个方法:

1、init( ):初始化Filter

2、doFilter( ):执行拦截、过滤逻辑操作

3、destory( ):销毁Filter

我们不用了解这里面究竟怎么实现,代码是怎样的,这个结构是人家想个半死写出来的,底层结构就是这样,我们只需要记住【Filter就这么三个结构】

那么我们需要怎么用Filter?

简单的Filter实现类的大致结构:

1、Filter是【实现了javax.servlet.Filter接口的类】嘛,那就 “implement” 【实现】这个接口

注意:它是Servlet的“儿子”,那就只能到这个包【javax.servlet】

(如果你实在是搞不清楚导【javax.servlet.annotation.WebFilter】还是导【jakarta.servlet.annotation.WebFilter】包,那么就直接多加一个注解【@Component】,因为当你在一个类上使用【@Component】注解时,Spring 容器会在类路径下扫描这个注解,并自动注册该类作为一个 Bean。

2、既然它实现的接口,那【接口实现类】就必须【实现接口的所有抽象方法】

但是这里有个例外:因为可能很多开发者也意识【init】和【destroy】没什么用,所以有没有都无所谓,所以不写这两个方法也可以

3、最后加上两个注解:

在【Filter实现类】上加【@WebFilter(urlPatterns = "/*")】,代表Filter要拦截的是哪些路径,【"/*"】代表所有路径,后续会详细讲具体拦哪些路径要怎么写

在【......Application启动类】上加【@ServletComponentScan】

我们运行整个spring boot都是通过运行【......Application启动类】来引导,那就要加上对Serevlet组件的使用引导

测试发送请求,后端控制台显示拦截成功

另外注意:spring的扫描机制是基于【启动类Application】扫描跟它同级目录的文件、以及同级目录包里的文件。如果层级再深一点,不用一些特殊注解是不会自动扫描到的。

Filter结构中间的【doFilter( )】方法详解:

那么我们能够知道一个大致的Filter结构是怎么样,很显然,我们根本不需要了解【第一部分init( )】和【最后一部分destroy( )】,只要知道有这两玩意,记得写(复制粘贴)上去就行

重点是中间的【doFilter( )】方法,该如何执行这个方法进行逻辑操作?

很简单

第一步:加一个【chain.doFilter(request , response);】,就可以取消拦截而【放行】

【chain.doFilter(? , ?);】方法就是放行拦截,里面要传一个【ServletRequest请求对象】和一个【ServletResponse响应对象】;

给【chain.doFilter(request , response);】打个断点,【chain.doFilter(request , response);】未执行时,拦截请求

【chain.doFilter(request , response);】执行后,请求成功

第二步:了解【doFilter( )】执行逻辑

前面说过,【doFilter( )】会执行多次,那么逻辑就是【doFilter( )】的运行周期也分为三部分:【chain.doFilter( )前:放行前】、【chain.doFilter( ):放行】、【chain.doFilter( )后:放行后】

那么【chain.doFilter( )前:放行前】的内容只会执行一次,然后执行完一次【doFilter( )】后会再次循环回到【doFilter( )】,但是不会执行【chain.doFilter( )前:放行前】的内容,只会执行【chain.doFilter( )后:放行后】的内容

第三步:了解放行路径

前面我们讲过【@WebFilter(urlPatterns = "路径")】表示拦截的路径

那么一共有这么几种拦截路径,看你喜欢来选

这里我就不想演示了,你们自己试试。

第四步:了解过滤器链

这个我也不想多说,简单解释就是:在刚刚那个案例Filter类的基础上,我们再新建N个Filter类,直接把刚刚那个Filter类里的代码原封不动复制粘贴给这N个Filter类,那么当发送请求时,就会链式连着将所有Filter类都执行一遍,然后再连着返回响应。

那么怎么区分哪个Filter类先执行,哪个后执行?看他们【类名】,根据首字母在24个字母的排序来分优先级。比如:【AAA建材王哥Filter.class】就比【z你佬.class】优先执行

利用Filter实现登录校验

首先大体流程:

注意三个点:

1、【登录】接口是例外,不应该被拦截;那么如果不是登录请求,那么再校验令牌,对了通行,不对不通行

2、然后校验令牌也分为两种错误情况:1、没有令牌  2、有令牌,但是是错的(根据【签名密钥】来【解析令牌】,只要【解析令牌】不报错就说明是对的,只要令牌有一个符号变了,就会报错)

3、其实JWT令牌的传递是通过【token】这个东西传递的,他跟Cookie的传递很类似,只不过它可以通过【请求头】传递、也可通过【请求体】,一般是在【请求头】

(我上一篇文章讲过)

接下来我们来写一下代码:

这里我累了,你们看代码吧,我不想解释了

package com.czm.tliaswebmanagement.filter;

import com.alibaba.fastjson.JSONObject;
import com.czm.tliaswebmanagement.pojo.Result;
import com.czm.tliaswebmanagement.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.annotation.WebFilter;

import javax.servlet.*;
import java.io.IOException;

@WebFilter(urlPatterns = "/*" )
public class DemoFilter implements Filter {
    @Autowired
    private JwtUtils jwtUtils;

    @Override //执行拦截、过滤逻辑操作,【执行多次】
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //【HttpServletRequest】是【ServletRequest】的父类,【HttpServletResponse】是【ServletResponse】的父类
        //为什么强转request和response的类型?因为ServletRequest没有【.getRequestURI()】获取【请求url】的功能
        //ServletResponse也没有【.getWriter().write("...")】可以直接把内容写进【响应头】的方法
        javax.servlet.http.HttpServletRequest req = (javax.servlet.http.HttpServletRequest) request;
        javax.servlet.http.HttpServletResponse res = (javax.servlet.http.HttpServletResponse) response;

        //1、获取请求url
        String url = req.getRequestURI();

        //2、判断url中是否含有登录接口(/login),是登录接口(/login)就放行
        if(url.contains("login")){
            //放行
            chain.doFilter(request , response);
            return;//然后结束【doFilter】,不再执行下面内容
        }

        //3、获取【请求头】里的【token】里存着的【jwt令牌】
        //HttpServletRequest 的 getHeader("...");可以获取到请求头的某个属性的值
        String jwt = req.getHeader("token");

        //4、判断令牌是否存在,如果不存在,返回错误结果(未登录)
        //【StringUtils.hasLength(字符串)】可以判断有没有字符串
        //  StringUtils选这个【org.springframework.util】包!!!
        if(!StringUtils.hasLength(jwt)){
            //为什么不能直接return这个【Result.error()】信息?
            //因为那是在controller层,有【@RestController】注解帮我们把【对象】转成【json】给回前端
            //但是在现在这是在Filter类里,要我们手动把【对象】转成【json】,再return
            Result error = Result.error("未登录");

            //调用阿里巴巴的【fastjson】帮忙转
            String notLogin = JSONObject.toJSONString(error);
            //然后HttpServletResponse 的 getWriter().write("...")可以直接把内容写进【响应头】
            res.getWriter().write(notLogin);
            return;
        }

        //5、如果有令牌,那就判断令牌有无有误(错的令牌在【解析jwt】时就会直接报错)
        try {
            //调用jwtUtils工具【解析jwt】
            jwtUtils.parseJWT(jwt);
        } catch (Exception e){
            e.printStackTrace();
            Result error = Result.error("未登录");

            //调用阿里巴巴的【fastjson】帮忙转
            String notLogin = JSONObject.toJSONString(error);
            //然后HttpServletResponse 的 getWriter().write("...")可以直接把内容写进【响应头】
            res.getWriter().write(notLogin);
        }

        //6、如果不是【登录】请求,也不存在【4】【5】的令牌错误,那就说明这个用户令牌无误,给他放行
        chain.doFilter(request, response);
    }
}

注意三个地方:

1、代码第【4】处,因为在controller层时,有【@RestController】注解帮我们把【对象】转成【json】给回前端,但是在现在这是在Filter类里,要我们手动把【对象】转成【json】,再return,那我们就要用到【阿里巴巴的fastjson】

首先在pom.xml引入依赖:

<!-- 阿里巴巴转JSON依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.0</version>
</dependency>

然后使用【JSONObject.toJSONString(对象信息);】就可以转成【JSON字符串】了

2、【StringUtils.hasLength(字符串)】可以判断有没有字符串,但是要注意导包不能错!!

3、注意【HttpServletRequest】和【HttpServletResponse】到底导哪个包,只导【javax.servlet】的!

二、Interceptor拦截器

Interceptor是区别于Filter的一种【动态】拦截器,基于springMVC技术实现,简单说Filter就是一个傻楞的门卫挡在城门外,然后Interceptor就是在城区里动态拦你路的烦人保安,这里只做简单解释,结尾我会详细讲它两的区别

简单入门(大体上就两步):

(注意,这里跟Filter一样比较绕脑,但是千万别去理解为什么要这么做!!你要做的就是记住我这里的步骤,你只要知道你要按步骤做就行别记代码!别研究为什么这么做!

第一步:注册拦截器

        先新建一个【interceptor】目录,里面创建一个【interceptor类】(跟一个Filter类的类名一样,名字随你起)

        Interceptor其实就是【HandleInterceptor】接口的实现类!所以我们要让它implement【HandleInterceptor】

        然后还是老样子,实现类要重写父接口类的【抽象方法】,直接【Ctrl + O】选择实现上面三个方法

        然后看着三个结构,跟Filter很像,也先简单理解为分为【前】【中】【后】,只是要留意一下第一部分,跟Filter不同的是它是在第一部分拦截住了,return true是放行rerturn false是不放行

        最后要加一个【Component】让这个【注册好的拦截器工具】放入IOC容器,让外面能用

第二步:配置注册类

        刚刚我们注册好了一个Interceptor拦截器类,但是跟Filter不一样,Filter只要在【启动类】加一个【@ServletComponentScan】开启对Servlet组件支持,就能被扫描、运行到;但是Interceptor拦截器不是Servlet的东西,它是属于springMVC的,所以还要通过springMVC的配置来实现它

        首先我们创建一个新包名叫【config】,这不单单是为了Interceptor的配置类而创建的,以后我们还会用到很多配置类,所有的【配置类】就统一放这个【config】包;然后我们创建一个Interceptor的配置类,名字随你起

        然后我们要实现的是springMVC里的【WebMvcConfigurer】这个接口父类

        然后老生常谈,【Ctrl + O】,只重写实现这个方法【addInterceptor( )】这个方法

        然后调用它给的那个参数registry的【addIntercepter()】和【addPathPatterns()】方法

先不用管参数registry是啥,他给你了就直接用

        然后【addIntercepter()】方法就是来配置你刚刚注册好的那个【Interceptor类】

【addPathPatterns()】方法是拦截哪些接口的请求,区别于Filter的拦截的是,"/**"才是全部路径

        最后加一个注解【@Configuration】标注它是一个配置类,然后spring boot运行时就会扫描它,然后找到里面要配置的Interceptor,从而扫描到Interceptor类

第三步:启动一下

        因为前面我们设置了Filter过滤器的类,那就把那些Filter全注释了,以免影响我们运行

        然后运行,然后我们到apifox尝试发送一个请求试一下

可以清晰看到控制台里Interceptor的拦截流程

详细讲解:

那为什么说他是动态的呢?看下图

例子:

那么我们现在结合登录校验案例来实现Interceptor的逻辑

超嗨简单,逻辑跟Filter一模一样!!我们只要记住三个区别

1、拦截的方法位置不同

        Filter过滤器的拦截是在中间的那个【doFilter( )】方法

        Interceptor拦截器是在第一部分【preHandle( )】方法!!注意这两的拦截逻辑写的位置!!

2、放行的机制不同

        Filter过滤器放行是通过在中间部分调用【chain.doFilter( request , responnse )】方法

        Interceptor拦截器是通过在第一部分【return true或false】来控制放行

3、参数不同,一个要强转类型一个不用

        Filter过滤器的中间的拦截方法【doFilter( )】里传的是【ServletRequest  和  ServletResponse】类型的请求和响应对象参数,所以要强转这两个对象参数类型成【HttpServletRequest  和  HttpServletResponse】才能用;

        Interceptor拦截器【preHandle( )】方法里传的直接就是【HttpServletRequest  和  HttpServletResponse】,所以不用强转类型,直接爽用好爽啊

所以你就可以直接复制粘贴Filter那的代码,然后改完这三个地方直接用了:

package com.czm.tliaswebmanagement.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.czm.tliaswebmanagement.pojo.Result;
import com.czm.tliaswebmanagement.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Autowired
    private JwtUtils jwtUtils;

    @Override//目标资源方法运行前运行,返回true:放行;返回false:不放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle,执行【目标资源方法】前");
        //为什么这里不用强转两个参数的类型?因为request就是【HttpServletRequest】类型啊,response就是【HttpServletResponse】类型啊

        //1、获取请求url
        String url = request.getRequestURI();

        //2、判断url中是否含有登录接口(/login),是登录接口(/login)就放行
        if(url.contains("login")){
            //放行
            return true;
        }

        //3、获取【请求头】里的【token】里存着的【jwt令牌】
        //getHeader("...");可以获取到请求头的某个属性的值
        String jwt = request.getHeader("token");

        //4、判断令牌是否存在,如果不存在,返回错误结果(未登录)
        if(!StringUtils.hasLength(jwt)){
            Result error = Result.error("未登录");

            //调用阿里巴巴的【fastjson】手动帮忙转json返回值
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);

            //不放行
            return false;
        }

        //5、如果有令牌,那就判断令牌有无有误(错的令牌在【解析jwt】时就会直接报错)
        try {
            //调用jwtUtils工具【解析jwt】
            jwtUtils.parseJWT(jwt);
        } catch (Exception e){
            e.printStackTrace();
            Result error = Result.error("未登录");

            //调用阿里巴巴的【fastjson】帮忙转
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);

            //不放行
            return false;
        }

        //6、如果不是【登录】请求,也不存在【4】【5】的令牌错误,那就说明这个用户令牌无误,给他放行
        return true;
    }

    @Override//目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle,执行【目标资源方法】后");
    }

    @Override//视图渲染完毕后运行,最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion,最后执行【视图渲染完毕】后");
    }
}

用apifox请求验证一下

先【不登陆】,发送一下别的接口的请求

结果拦截了,不放行,不返回要的数据

然后【登录】,发现请求路径里有login,是登录接口请求,直接放行并生成token令牌返回

然后在登录后再【回到其他接口】发请求,在请求头带上token参数,然后成功放行请求了

好爽啊!!!!爽啊!!!好舒服啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!!!!

三、Filter过滤器与Interceptor拦截器的区别!!!

那么这两玩意到底有什么区别?不都是拦截吗?下面我就以几个重点分析一下:

1、身世不同

Filter是【Servlet】“家族”的人,写Filter类的时候你们也能发现必须要到Servlet的包

Interceptor是【springMVC】“家族”的人,写Interceptor的配置类的时候就用到springMVC

2、实现逻辑不同

Filter过滤器是基于函数回调

我们写代码时会发现每个Filter类都有三个参数,其中最后一个参数是【FilterChain】类参数,这就是一个接口参数

然后每个类都有一个【doFilter( )】函数,而我们执行放行的操作不就是在【doFilter( )】函数里调用那个【FilterChain】的【doFilter( )】函数吗?写的时候会不会觉得很奇怪,其实这就是在回调这个【FilterChain】父接口的【doFilter( )】函数,说起来可能不明白,看图:

Interceptor则是基于Java的反射机制(动态代理)实现的

不好解释我也不知道怎么解释,反正就是直接return回一个布尔值,然后在拦截器接口内部调用其他方法,根据return回来的布尔值来处理放不放行,具体源代码内部逻辑我也没研究所以我不会.......

3、拦截的位置、范围不一样

Filter的拦截的位置是【在Servlet容器里面、SpringMVC容器外面】就进行过滤拦截了

那么它的拦截范围也就是对【所有访问、资源】都进行拦截

Interceptor的拦截位置是【在SpringMVC容器里面、Controller接口前面】

那么它的拦截范围也就是【针对Spring MVC的访问】进行拦截

(多看几张图加深一下记忆,图片都是来自别的博主文章摘录)

4、触发时机不同

既然位置不同,那么触发时机肯定就不一样了,Filter在外面阻拦,那肯定先执行它;然后再到Interceptor;结束了Interceptor之后又回到Filter

5、使用范围不同、实现接口不同

        Filter过滤器实现的是【 javax.servlet.Filter 】接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用

        而Interceptor拦截器是一个【Spring MVC组件】,实现的是【Handler Interceptor】接口,并由【Spring MVC容器】管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中

另外:DispatcherServlet

Spring环境下,Servlet还提供了一个组件【DispatcherServlet】,它是在Spring MVC容器里的最前面的,在Interceptor拦截器前面,那么大致流程就是:Filter过滤器先过滤拦截外部请求访问——>然后放到DispatcherServlet——>DispatcherServlet再往Controller传,但又遇到Interceptor拦截器阻拦——>最后Interceptor放行,请求资源到达Controller层

我最后最后用最通俗易懂的话来解释:

外部请求是一个乘客,买了一张高铁票要上高铁

Filter就是高铁检票的N多个闸机,它就固定在那,你要进去大厅、进站台就得被它拦一次

然后DispatcherServlet就是站台,你终于通过重重难关来到站台,等待高铁到来然后坐到座位

Interceptor就是检票员,你终于准备要找到高铁座位,准备坐下,Interceptor检票员就动态随机的出现在你面前,要检查你的高铁票,发现你是逃票、站票就让你滚一边呆着,是坐票你就坐着

最后到家的时候,你还是原路返回,只不过Interceptor检票员不会查你了,而是直接放行;你回到站台;到了大厅,过了检票闸机;最后回家,让你爸妈获得你这个宝贝儿子/女儿。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/787482.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数学建模中常用的数据处理方法

常用的数据处理方法 本文参考 B站西电数模协会的讲解视频 &#xff0c;只作笔记提纲&#xff0c;想要详细学习具体内容请观看 up 的学习视频。一般来说国赛的 C 题一般数据量比较大。 这里介绍以下两种方法&#xff1a; 数据预处理方法 数据分析方法 数据预处理方法 1. 数据…

2024 Q3 NAND闪存价格|企业级依然猛涨,消费级放缓

在企业领域持续投资于服务器基础设施&#xff0c;特别是在人工智能应用的推动下&#xff0c;企业级SSD需求增加的同时&#xff0c;消费电子市场却依旧疲软。加之NAND供应商在2024年下半年积极扩大生产&#xff0c;预计到2024年第三季度&#xff0c;NAND闪存供应充足率将上升至2…

【Go】函数的使用

目录 函数返回多个值 init函数和import init函数 main函数 函数的参数 值传递 引用传递&#xff08;指针&#xff09; 函数返回多个值 用法如下&#xff1a; package mainimport ("fmt""strconv" )// 返回多个返回值&#xff0c;无参数名 func Mu…

Java项目:基于SSM框架实现的农家乐信息管理平台含前后台【ssm+B/S架构+源码+数据库+答辩PPT+开题报告+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的农家乐信息管理平台 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功…

01:简易的电动车防盗报警器

简易的电动车防盗报警器 1、震动传感器模块的使用2、使用震动传感器模块控制继电器开关3、433M无线发射接收模块的使用 需要材料&#xff1a; 1、51单片机 2、震动传感器模块 3、继电器模块 4、高功率喇叭 5、433M无线发射接收模块 6、弱干杜邦线 1、震动传感器模块的使用 接好…

安全求交集PSI

安全求交集定义 求交集的PSI&#xff1a;交集可以被两方看见或其中一方看见&#xff0c;非交集进行保护有两方的PSI半诚实的PSI&#xff1a;攻击者要严格遵守协议&#xff0c;在此基础上得到他人的秘密是做不到的 Two-Party Semi-Honest PSI 挑战一&#xff1a;隐藏非交集元素…

Day65 代码随想录打卡|回溯算法篇---组合总和II

题目&#xff08;leecode T40&#xff09;&#xff1a; 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含…

硕博电子移动控制器在无人驾驶卡车上的应用

传统港口行业一直是一个典型的劳动密集型行业&#xff0c;以前&#xff0c;集装箱的每次起吊操作需要多人配合&#xff0c;包括操作员、指挥手、理货员等至少7名现场工作人员。传统码头设施陈旧&#xff0c;重型设备难以更新换代。而且&#xff0c;港口还经常受到天气状况的影响…

GraphRAG——一个基于图的检索增强生成的开源项目【送源码】

GraphRAG 最近几天&#xff0c;微软团队开源了GraphRAG&#xff0c;这是一种基于图&#xff08;Graph&#xff09;的检索增强生成方法。 先说说RAG吧&#xff0c;检索增强生成&#xff0c;相当于是从一个给定好的知识库中进行检索&#xff0c;接入LLM模型&#xff0c;让模型生…

ByteMD富文本编辑器的vue3配置

Git地址&#xff1a;GitHub - bytedance/bytemd: ByteMD v1 repository 控制面板输入 npm install bytemd/vue-next 下载成功后在src/main.ts中引用 import "bytemd/dist/index.css";引入后保存&#xff0c;下面是一些插件&#xff0c;比如说我用到gmf和hightLight&…

数据类型及数据块认知

西门子STEP7编程语言 梯形图(LAD) 功能块图(FBD) 语句表(STL) 其中梯形图和功能块图可以相互转换 CPU常用数据区 信号输入区 I 信号输出区 Q 程序中表现形式&#xff0c;IX.X/QX.X;IWX/QWX-访问的是CPU输出输入过程映像区 另一种形式IWX:P/QWX:P-访问的是信号端口地址&#xf…

Transformer-LSTM预测 | Matlab实现Transformer-LSTM时间序列预测

Transformer-LSTM预测 | Matlab实现Transformer-LSTM时间序列预测 目录 Transformer-LSTM预测 | Matlab实现Transformer-LSTM时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现Transformer-LSTM时间序列预测&#xff0c;Transformer-LSTM&#xf…

如何评价Flutter?

哈喽&#xff0c;我是老刘 我们团队使用Flutter已经快6年了。 有很多人问过我们对Flutter的评价。 今天在这里回顾一下6年前选择Flutter时的原因&#xff0c;以及Flutter在这几年中的实际表现如何。 选择Flutter时的判断 1、性能 最开始吸引我们的就是其优秀的性能。 特别是…

【SQL】做项目时用到的语句整理(去重/多表关联)

1. 对日期去重&#xff08;groupby&#xff09; 需要&#xff1a;新建一张表&#xff0c;对原来表中的某个列(href)进行去重&#xff0c;并按照最新的日期进行排版 适用&#xff1a;如果有一张表&#xff0c;我们重复往里面存入数据&#xff0c;有一些除了日期以外&#xff0…

符号同步、定时同步和载波同步

符号同步、定时同步和载波同步是通信系统中重要的同步技术&#xff0c;它们各自承担着不同的功能和作用。以下是对这三种同步技术的详细解释&#xff1a; 符号同步 定义&#xff1a; 符号同步&#xff0c;也称为定时恢复或时钟恢复&#xff0c;是指在数字通信系统中&#xff…

Java字符串(String、字符串拼接、原理)

文章目录 一、String字符串1.1创建方式【直接赋值、new一个对象】1.1.1 使用字符串字面值直接赋值&#xff1a;&#xff08;1&#xff09;字符串字面量创建String对象的转换过程&#xff08;2&#xff09;一些方法&#xff08;3&#xff09;说明 1.1.2 使用new关键字创建字符串…

MySQL:TABLE_SCHEMA及其应用

MySQL TABLE_SCHEMA及其应用 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/ar…

285个地级市出口产品质量及技术复杂度(2011-2021年)

出口产品质量与技术复杂度&#xff1a;衡量国家竞争力的关键指标 出口产品质量是衡量国内企业生产的产品在国际市场上竞争力的重要标准。它不仅要求产品符合国际标准和目标市场的法律法规&#xff0c;而且需要保证产品质量的稳定性和可靠性。而出口技术复杂度则进一步体现了一…

龙迅LT8641UXE HDMI四进一出切换开关,支持标准HDMI 2.0内置MCU

龙迅LT8641UXE描述&#xff1a; Lontium LT8641UX HDMI2.0开关具有符合HDMI2.0/1.4规范的4&#xff1a;1开关&#xff0c;最大6Gbps高速数据速率&#xff0c;自适应均衡RX输入和预先强调的TX输出支持长电缆应用&#xff0c;没有XTAL板上节省BOM成本。LT8641UX HDMI2.0开关自动…

C++之goto陈述

关键字 goto用于控制程式执行的顺序&#xff0c;使程式直接跳到指定标签(lable) 的地方继续执行。 形式如下 标签可以是任意的识别字&#xff0c;后面接一个冒号。 举例如下 #include <iostream>int main() {goto label_one;label_one: {std::cout << "Lab…