精华内容
下载资源
问答
  • 2019-06-12 10:01:39
    com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.huasisoft.egAffairs.JsonResult: no suitable constructor found, can not deserialize from Object value
    

    没有无参构造函数,再json转换的时候bean类要加上无参构造函数,如JsonResult
    Json转对象失败:"No suitable constructor found for type [simple type, class com.test.faster.domain.respons
    今天自己定制一个json转换对象的方法,转换过程中报错:

    "No suitable constructor found for type [simple type, class com.test.faster.domain.response.User]: can not instantiate from JSON object (need to add/enable type information?)
    

    原因java bean中没有提供无参的构造函数,在bean中构造一个方法就可以了

    更多相关内容
  • 开源对象存储MinIO技术白皮书

    万次阅读 多人点赞 2019-09-27 13:38:36
    在MinIO使用zookeeper/raft,会使整个系统增加不必要的复杂性。   优势 •实际操作极其简单,有效代码不足一千行,易理解,易维护。 •超高的性能。详细数据请参考文献[12]   4 云网关模式 ...

    MinIO创始者是Anand Babu Periasamy, Harshavardhana(戒日王)等人, Anand是GlusterFS的初始开发者、Gluster公司的创始人与CTO,Harshavardhana曾经是GlusterFS的开发人员,直到2011年红帽收购了Gluster公司。MinIO在设计上汲取了GlusterFS的相关经验与教训,系统复杂度上作了大量简化。
     

    一、MinIO简介

    01.概述

         MinIO对象存储系统是为海量数据存储、人工智能、大数据分析而设计,基于Apache License v2.0开源协议的对象存储系统,它完全兼容Amazon S3接口,单个对象最大可达5TB,适合存储海量图片、视频、日志文件、备份数据和容器/虚拟机镜像等。MinIO主要采用Golang语言实现,整个系统都运行在操作系统的用户态空间,客户端与存储服务器之间采用http/https通信协议。

    02.设计哲学

         极简理念——采用尽可以简单可靠的集群管理方案,摒弃复杂的大规模集群调度管理,减少风险因素与性能瓶颈,聚焦产品的核心功能,打造高可靠的集群、灵活的扩展能力以及超高的性能;

        积木式扩展——建立众多的中小规模、易管理的集群,支持跨数据中心将多个集群聚合成超大资源池,而非直接采用大规模、统一管理的分布式集群。

    03.设计原则

     

    04.产品特点

     

    05.高级特性

     

    二、技术架构

    01 数据组织结构

           NAS系统把整个存储资源组织为目录树的形式。与此不同,对象存储系统把存储资源组织为租户-桶-对象的形式。数据结构组织见下图:

    对象:类似于hash表中的表项:它的名字相当于关键字,它的内容相当于“值”。

    桶:是若干个对象的逻辑抽象,是盛装对象的容器。

    租户:用于隔离存储资源。在租户之下可以建立桶、存储对象。

    用户:在租户下面创建的用于访问不同桶的账号。可以使用MinIO提供的mc命令设置不用用户访问各个桶的权限。

     

    02 数据分布与均衡

    1 去中心化架构

        MinIO采用去中心化的无共享架构,对象数据被打散存放在不同节点的多块硬盘,对外提供统一命名空间访问,并通过Web负载均衡器或DNS轮询(DNS round-robin)在各服务器之间实现负载均衡。 

     

    2 统一命名空间

          MinIO对象存储系统主要有两种部署方式,一种是常见的本地分布式集群部署,一种是联盟模式部署。本地分布式集群部署方式即在多个本地服务器节点部署MinIO软件,并将其组件成单套分布式存储集群,并提供统一命名空间和标准S3访问接口。联盟部署模式即将多个MinIO集群在逻辑上组成了统一命名空间,实现近乎无限的扩展与海量的数据规模管理,这些集群可以都在本地,或分布在不同地域的数据中心。

           如下图所示,4个服务器节点组成一个MinIO集群,每个服务器节点中会选择相同数据的硬盘创建一个纠删组,某个桶的数据会根据MinIO的分布式算法,切片分散存储到对应的纠删组中(详见纠删码相关内容)。

     

    3 分布式锁管理

           与分布式数据库相类似,MinIO对象存储系统也面临数据一致性问题:一个客户端程序在读取一个对象的同时,另一个客户端程序可能正在修改或者删除这个对象。为了避免出现数据不一致情况,MinIO相关开发人员为MinIO对象存储专门设计并实现了dsync分布式锁管理器。它采用如下分布式锁管理机制:

    l  任何一个节点的锁请求都会广播给集群内所有在线节点;

    l  如果n/2 + 1个节点回应“是”,则成功获得锁;

    l  客户端获得锁以后可保留任意时间,不需要时自己释放即可。释放操作也会广播给所有的节点,从而恢复锁的可用状态。写锁仅能被一个写入者获得。

     

    设计目标

    要求设计简单,因为简单的设计,可以避免程序中很多非常棘手的条件分支的支持。

    不存在主节点,因为一旦在设计上引入主节点,那么如果主节点宕机,整个锁管理器机制即将失效,这对MinIO对象存储系统影响非常严重,是不可接受的。

    系统必须是弹性的,即使存在多个失效的节点,只要它们的个数小于n/2, 整个锁管理系统是可以正常工作的。

    完全可以替代Golang标准库中的sync.RWMutex互斥锁。这样可以简化MinIO对象存储系统的编程。

    当失效节点重启以后,其它节点重新连接

     

    不使用zookeeper/raft等技术的原因

           zookeeper/raft功能丰富,而MinIO对象储存的使用用例其实很有限。在MinIO中使用zookeeper/raft,会使整个系统增加不必要的复杂性。

     

    优势

    •实际操作极其简单,有效代码不足一千行,易理解,易维护。

    •超高的性能。详细数据请参考文献[12]

     

    4 云网关模式

           MinIO存储系统的后端可以是磁盘,也可以作为云网关,对接第三方的NAS系统、分布式文件系统或公有云存储资源,并为业务系统转换提供标准的对象访问接口。

         目前MinIO支持Google 云存储、HDFS、阿里巴巴OSS、亚马逊S3, 微软Azure Blob 存储等第三方存储资源。

     

    03 元数据

    1 架构

          MinIO对象存储系统无元数据数据库,所有的操作都是对象级别的粒度的。这种做法的优势是:

    • 个别对象的失效,不会溢出为更大级别的系统失效。

    •便于实现“强一致性”这个特性。此特性对于机器学习与大数据处理非常重要。

     

    2 管理

          元数据与数据一起存放在磁盘上:数据部分纠删分片以后存储在磁盘上,元数据以明文形式存放在元数据文件里(xl.json)。假定对象名字为obj-with-metadata, 它所在的桶的名字是bucket_name,  disk是该对象所在纠删组的任一个磁盘的路径,如下目录:

    disk/bucket_name/obj-with-metadata 

    记录了这个对象在此磁盘上的信息。其中的内容如下:

     

          其中的xl.json即是此对象的元数据文件。part.1 即此对象的第一个数据分片。对象的元数据文件xl.json的内容是如下这种形式的json字符串:

    字段说明

     

    1 format字段

         该字段指明了这个对象的格式是xl。MinIO内部存储数据主要有两种数据格式:xl与fs。使用如下命令启动的MinIO使用的存储格式是fs:

     

           这种模式主要用于测试, 对象存储很多API都是并没有真正实现的桩函数。在生产环境所用的部署方式(本地分布式集群部署、联盟模式部署、云网关模式部署)中,存储格式都是xl。

     

    2 stat字段

          记录了此对象的状态,包括大小与修改时间,如下图:

    3 erasure字段

          这个字段记录此对象与纠删码有关的信息,如下图:

    • 其中的algorithm指明了此对象采用的是Klaus Post实现的纠删码,生成矩阵是范德蒙矩阵。

    • data,parity指明了纠删组中数据盘、校验盘的个数。

    • blockSize 指明了对象被分块的大小,默认是5M(请参见上一节“数据分布与均衡”)。

    •index指明了当前磁盘在纠删组中的序号。

    • distribution:每个纠删组的数据盘、校验盘的个数是固定的,但是不同的对象的分片写入这个纠删组的不同磁盘的顺序是不同的。这里记录了分布顺序。

    • checksum:它下面的字段个数跟此对象的分片数量有关。在旧版本的MinIO对象存储系统,每一个分片经过hash函数计算出的checksum会记录在元数据文件的这个位置。最新版的MinIO会把checksum直接计入分片文件(即part.1等文件)的前32个字节。

          此字段之下algorithm的值是”highwayhash256S”表明checksum值是写入分片文件的。

          

    4 minio字段

           这个字段记录了存储此对象的minio的版本。

     

    5 meta字段

          Content-type, etag两个字段是MinIO对象存储系统自动生成的。

          用户在使用Python等语言的写作的访问MinIO的程序中,如果上传对象时候指定了几个自定义属性,比如:

    author属性值为Zhangsan

    Nation属性值为Cn

    Type属性值为love

    那么对象元数据文件的meta字段就会出现如下几个子字段:

    X-Amz-Meta-Author

    X-Amz-Meta-Nation

    X-Amz-Meta-Type

    6 parts字段

           记录各个分片的信息:

     

    04 集群扩展

    1 扩展方式

           MinIO支持联盟部署模式,即将多个MinIO集群组成一个统一命名空间(一个ETCD集群,若干个CoreDNS服务)。其中ETCD集群相当于整个对象存储系统的配置数据库,很多重要的信息,如桶IP地址等存储于其中。这种模式的MinIO的架构如下图:

    联盟模式多集群部署

     

           同样,MinIO在扩展时也采用相同机制,而不是传统分布式存储的添加节点方式。MinIO主要通过添加新的集群来扩大整个系统,可用空间大幅增加且仍然保持统一命名空间。通过这种方式,MinIO对象存储系统几乎可以无限的扩展总体性能和容量。

     

    2 统一域名访问

        MinIO集群扩展加入新了集群或桶后,对象存储的客户端程序需要通过统一域名/url(如bucket1.domain.com)来访问数据对象,这个过程涉及到了CoreDNS系统。

    CoreDNS实现单一域名/URL访问

     

    MinIO对象存储的某个客户端(比如mc),首先向某个MinIO服务发送创建桶的请求。MinIO服务把这个桶所在的MinIO集群的外部网址(一般为一个Nginx的IP地址,或者MinIO集群的每一台服务器的IP地址),写入到etcd集群中。

    假定域名为domain.com,桶名为buc-1,集群的服务器IP地址为192.168.1.108、192.168.1.109,那么写入etcd集群的共有两条数据.第一条数据的key,value二元组为:

    第二条数据的key,value二元组为:

    CoreDNS通过etcd系统获知”bucket1.domain.com”这个url所对应的两个IP地址为192.168.1.108, 192.168.1.109。对象存储的客户端主机设置如上所配置的CoreDNS服务之后,客户端程序就可以通过域名”bucket1.domain.com”来找到访问这个桶。

     

    3 优势特性

    单一的、超大的命名空间需要花费大量的创建、维护与停机时间,复杂的部署管理,进而带来更严重的次生故障。MinIO的设计理念就是化整为零,简化集群扩展,减小单个集群的体量,轻量化单个集群的运维,从而使得超大规模的存储管理与维护变得更加容易。

    • 集群的节点完全对等,没有主节点,多个节点可以并发提供对象访问服务;

    • 创建桶的时候,可以指定数据中心/地域,以匹配对应的业务访问;

    • 无论添加多少个集群,原有集群的性能几乎是不变的;

    • 集群不会过大(32个节点),可实现可靠的分布式锁管理器,进而保证更新、删除等操作的强一致性。传统的架构允许集群扩容到数百上千节点,此情况下的强一致性容易产生性能问题;

    • 故障的影响范围小,限制在单个集群内部。

     

    05 纠删码

          在同一集群内,MinIO会自动生成若干纠删组,用于存放桶数据。一个纠删组中的一定数量的磁盘发生的故障(故障磁盘的数量小于等于校验盘的数量),通过纠删码算法可以恢复出正确的数据。MinIO集成了Reed-Solomon纠删码库,MinIO存储对象数据时,首先把它生成若干等长的片段(对于大对象,默认按5MB切片),然后每一个片段会纠删算法分成若干分片,包括数据分片与校验分片,每个分片放置在一个纠删组的某个节点上。对象的每一个数据分片、校验分片都被“防比特位衰减”算法所保护。

     

     

    对于一个对象,MinIO是如何定位它所在的纠删组呢?

         假定所有的纠删组都有一个序号(从0开始,直至纠删组个数减1)。MinIO会根据对象名(类似于文件系统的全路径名),使用crc32哈希算法计算出一个整数。然后使用这个整数除以纠删组的个数,得到一个余数。这个余数,可以作为纠删组的序号,这样就确定了这个对象所在的纠删组。MinIO采用CRC32哈希算法,与GlusterFs的Davies-Meyer哈希算法(性能、冲突概率与md4, md5相近)不一样的是, CRC32算法的哈希值分布较不均匀,但运算速度极快,高出md4数倍。相对于容量均衡,MinIO更看重数据的写入速度。

     

    06 数据修复

    比特位衰减(Bitrot)是指存在存储介质中的数据发生了缓慢的变化,如向存储介质写入一段比特流,一段时间后再读出来,二者并不一致。比特位衰减的原因大致有:磁记录磨损、磁盘幻象写(phantom writes)、磁盘指向错误(misdirectedreads/writes)、宇宙射线的辐射等。MinIO对象存储系统从设计之初即考虑到修复静默错误,从被修复的目标来说,按照大小可以分为以下三种类型的修复:某个对象、某个桶、整个集群。

    在控制台上执行mc命令即开始进行数据修复。该命令一方面向minio发送数据修复的HTTP请求,另一方面不断地接收minio服务进程返回的修复进度信息,而后输出到控制台,直到修复工作完毕。

    如前文所述,每个对象都被分成多个分片,然后存储于多台主机的磁盘上。数据修复可以分为正常、深度两种模式,正常模式下只是简单地检查分片状态信息,深度模式下会使用hash算法来校验分片的内容,找出比特位错误,同时也更耗费资源。

    MinIO具体修复流程如下:

    • mc命令作为MinIO对象存储的客户端软件、管理工具,它内部链接了minio软件(代码网址:https://github.com/minio/minio/)的madmin软件模块,通过调用madmin中的修复函数,mc包装了mc命令的命令行参数,然后向minio服务进程发送HTTP消息。

     

    •mc发送一个修复请求,在minio中被类healSequence所描述。每一个healSequence可以启动、停止、查询状态。minio服务程序收到新的任务的时候,会检查是否跟原有的healSequence有重叠的任务,如果有重叠,则启动的修复任务失败。如果minio服务没有发现错误,则使用深度优先搜索的算法,按照磁盘元数据信息、桶、对象的顺序,不断地给后台修复线程推送任务。

     

    •minio后台修复线程修复对象的流程算法:对于对象的每一个block(默认大小为5M),从纠删组的各个主机读取各个分片,如果有错误的分片,就需要修复,有两种可能:校验分片错误——minio使用各个数据分片重新计算缺失的校验片。数据分片错误——使用纠删算法恢复数据(需要计算逆矩阵)。

     

     

    07 lambda计算

           MinIO对象存储软件支持lambda计算通知机制,即桶中的对象支持事件通知机制。MinIO当前支持的事件类型有:对象上传、对象下载、对象删除、对象复制等。MinIO对象存储软件当前支持的事件接受系统有:Redis,NATS, AMQP, MQTT,Apache Kafka, MySql, PostgreSQL, Elasticsearch等。

           对象通知机制,极大地增强了MinIO对象存储的扩展性,可以让用户通过自行开发来实现某些MinIO对象存储不便实现的功能,比如基于元数据进行的各种检索、各种跟用户的业务有关的计算。既方便了用户,又有助于MinIO对象存储的生态建设。

           对象通知机制,使用极为简单,用户只需在MinIO进行少许配置即可。请参考文献[15]。

     

    08 持续备份

         传统的复制的一大问题是不能有效地扩展,很难超过几百TB。在当今的时代,为了支持灾难恢复,任何单位都需要一个备份策略。而且这个备份策略需要跨越不同的地理位置、不同的数据中心、多种多样的云环境。

        MinIO的持续备份是为跨数据中心的大型部署而设计的。通过使用lambda计算通知机制,它可以快速、有效地计算处需要增量备份的内容,这远比传统的批处理模式优秀。持续备份使得未备份的数据尽可能的少,这意味着发生灾难或者严重错误时候,丢失的数据尽可能的少,很好地保护了用户的数据资产。

     

    9 软件模块

         MinIO对象存储系统主要由以下软件模块部分组成:存储服务器软件minio,存储客户端软件mc,多种语言的客户端SDK。minio分为上下两层,上层负责minio的系统管理与对外接口,下层实现具体的逻辑。

     

    1 cmd模块

      这是minio的上层,也就是源代码中的cmd子目录,参见: https://github.com/minio/minio/tree/master/cmd。这一部分主要负责minio的命令行参数解析、初始化系统、格式化磁盘、管理内嵌的web服务器、S3 API的解析与逻辑处理。

     

    2 各个软件包

         这个是minio底层逻辑实现,也就是源代码目录中的pkg子目录。其中一些软件包(比如madmin), 可被其它组织(或个人)在编写辅助minio的软件的时候所重复使用。

    • madmin:使用这个软件包可以自己使用Golang语言撰写MinIO集群的管理程序,比如获取服务的状态(磁盘、cpu等信息)、重启某个机器服务、启动修复某个桶的任务、重新配置系统、获取剖析信息等等。

    • S3 select:如果对象存储系统中有很多超大型的对象,比如大小是几个GB甚至几个TB的对象。如果应用程序(比如spark分析程序),要把符合条件的若干个对象都读过去,然后再做分析,会及其的慢,浪费很多带宽(毕竟对象中可能只有很少的一部分是对某个分析程序有用的)。因此Amazon引入了S3 Select 的功能。通俗地说,就是把select 类型的sql语句在某个对象上执行,从对象中取出一部分内容返回给应用。MinIO提供了S3 Select 功能。相对于S3 Select, MinIO要求对象的内容必须是CSV、 JSON,或者 Parquet格式。S3Select API实现中使用的语法分析器是 Alec Thomas写的如下项目:

    https://github.com/alecthomas/participle

    这个实现的分析算法是带有栈的ll(k)分析算法。

     

    三 性能测试

         MinIO已经为高性能做过高度优化,尤其是部分关键的算法已经使用SIMD指令对Intel(AVX2/AVX512)、Arm(NEON)的cpu做过特殊优化,主要包括:

    1) 纠删码部分用到的伽罗瓦域的运算:加法、乘法、乘方等等;

    2) 监测比特位衰减(bitrot)的哈希函数,如HighwayHash。

    另外每一个MinIO集群都是无中心的,其中的每一个节点都是对等的,从而在性能上,不会存在单点瓶颈,也不会有单点故障。

        如下的硬件配置之下:Intel Skylake CPU, NVMe磁盘,以及Mellanox CX5 dual 100-GbE网卡。下图是MinIO inc的测试结果:

     

    四 设计讨论

    为什么MinIO单集群不支持扩展?

    •传统的扩展方式的劣势

         通过增加节点来扩展单集群,一般需要进行数据均衡,否则群集内各存储节点会因负载不均而出现新的瓶颈。除了数据均衡操作的时机这个问题以外,在均衡过程中一般需要从存储使用率高的节点向使用率低的节点迁移数据。当集群扩容之后,大量已经写入的文件落点会出现改变,文件需要迁移到真实的落点。当存储系统容量比较大时,则会发生大量的文件/对象进行迁移,迁移过程可能由于占用大量资源而导致上层应用性能下降。而且当文件/对象迁移过程中,机器故障可能会导致一些意想不到的情况,尤其是有大量业务的时候。当然针对此类问题,Gluterfs之类的文件系统有一些比较复杂的处理办法。

     

    •使用场景

          人工智能、大数据分析、视频监控等典型使用场景中,对象存储系统中存储的数据往往写入以后一般不再修改。如果现有MinIO集群存储空间使用完毕,重新添加新集群,然后继续写入新集群即可。MinIO对象存储的客户端应用,从业务层面自行决定那些对象存在于哪个集群里面,使用起来并不麻烦。

        单集群不可扩展,也就是说系统不需要处理扩展和数据均衡,不仅有效降低系统复杂性,而且可以使得系统部署规划具有很好的可预测性。

       对于海量对象存储应用场景,数据通常具有典型的生命周期特征,根据实际需求设计好单集群规模,按联合方式扩展,整个系统具有非常好的可维护性。

     

    •MinIO方案的优势

         不支持对单个集群进行扩展,MinIO对象存储系统的这种设计,使得系统的很多模块更加简单(比如从一个对象转换到它所在的纠删组,只用简单的哈希即可。)降低了整个系统出错的概率,使得MinIO对象存储系统更加可靠、稳定。

    详细的讨论参见文献[14]

     

    MinIO是否有类似于GlusterFs 的translator类机制?

        没有,GlusterFs是使用c语言实现的,而c语言是比较低级的语言,本身没有模块机制。Golang语言自身有强大的模块机制,所以也就不需要类似于translator之类的机制。

     

    MinIO的纠删码机制,为何没有采用柯西矩阵?

     

        就Reed-Solomon纠删码的生成矩阵来说,Klaus的纠删码库里面可以选择柯西生成矩阵。不过当前MinIO软件使用的仍然是范德蒙矩阵的Reed-Solomon纠删算法。这是因为:虽然柯西矩阵的生成相比范德蒙矩阵更快,不过MinIO编码矩阵的生成是只进行一次的操作(程序运行中,生成的这个矩阵会被保存起来)。使用柯西矩阵对数据的吞吐量并没有什么影响。

     

    五 对象存储产品选型讨论

          开源对象存储软件以MinIO,Ceph为典型代表。为帮助相关人员在选择对象存储系统之时选择合适的产品,此处对二者的特点、特性做一定讨论。

    01 MinIO优势

    1 部署极其简单

         MinIO系统的服务程序仅有minio一个可执行文件,基本不依赖其它共享库或者rpm/apt包。minio的配置项很少(大部分都是内核之类系统级的设置),甚至不配置也可以正常运行起来。百度、google、bing等搜索引擎上基本没有关于MinIO部署问题的网页,可见在实践中,很少有使用者遇到这方面的问题。

          相比之下,Ceph系统的模块,相关的rpm、apt包众多,配置项非常多,难以部署,难调优。某些Linux发行版的Ceph安装包甚至有bug,需要使用者手动改动Ceph的python脚本,才能安装完毕。

     

    2 二次开发容易

         MinIO对象存储系统除了极少数代码使用汇编实现以外,全部使用Golang语言实现。Ceph系统是使用业界闻名的难学难用的c++语言编写的。Golang语言由于产生较晚,吸收了很多语言尤其是c++的教训,语言特性比较现代化。相对而言,MinIO系统的维护、二次开发比较容易。

     

    3 网管模式支持多种其他存储

         通过网关模式,MinIO对象存储后端,可以对接各种现有的常见其它存储类型,比如的NAS系统,微软Azure Blob 存储、Google 云存储、HDFS、阿里巴巴OSS、亚马逊S3等,非常有利于企业复用现有资源,有利于企业低成本(硬件成本约等于零,部署MinIO对象存储软件即可)地从现有系统平滑升级到对象存储。

     

    02 Ceph优势 

    •数据冗余策略更加丰富

         Ceph同时支持副本、纠删码,而MinIO只支持纠删码。对于个别的对于数据可靠性要求极高的单位,Ceph对象存储更加合适。

    •社区目前更成熟

     

    03 其他对比

    1 厂商支持

         国内使用Ceph的厂商、基于Ceph进行自研的存储厂商都比较多,在使用过程中遇到的问题(有些时候,甚至需要修改、增强乃至重新实现Ceph本身的功能),可以向相关厂商寻求支持。国际方面,Ceph早已被红帽收购,而红帽近期又被IBM收购。

        MinIO开发与支持的厂商只有MinIO公司。由于架构比较先进,语言高级,MinIO本身的程序比较容易读懂、修改。招聘Golang程序员来 维护MinIO所花费的成本,显然低于招聘c++程序员来维护Ceph。 

     

    2 多语言客户端SDK

        二者均有常见编程语言的客户端,比如:python, java等。MinIO对象存储软件的开发SDK另外支持纯函数式的语言Haskell。

     

    3 技术文档  

         内部实现的文档MinIO基本不存在。想要了解内部实现乃至参与开发的技术人员,只能到如下社区:

    http://minio.slack.com/ ,与MinIO的开发人员直接交流,或者自己阅读代码。Ceph的各种实现文档、算法说明文档非常丰富。这方面Ceph要比MinIO成熟很多。

     

    04 结论

        由以上讨论,可见作为对象存储软件来说,MinIO, Ceph都非常优秀,各自有各自的优势。准备使用对象存储软件的用户,应该根据自己单位的需求、技术储备等实际情况,选择适当的软件。

     

    六 参考硬件

     

         MinIO是符合软件定义存储SDS理念的,兼容主流X86服务器以及ARM/飞腾平台,同时也可以移植到诸如申威(Alpha架构)和龙芯(Mips架构)等硬件平台。

        下面这些符合工业标准的、广泛采用的服务器是经过MinIO inc.优化测试过的、MinIO对象存储软件表现优异的服务器:

     

    参考文献

    1https://github.com/krishnasrinivas/wikinotes/wiki/minio-scaling

    2https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/dev/Welcome.html

    3Klaus Post官网:https://klauspost.com/

    4https://github.com/klauspost/reedsolomon 

    5https://developer.ibm.com/articles/cl-cloudstorage/

    6https://github.com/minio/dsync

    7https://github.com/minio/dsync/pull/22#issue-176751755

    8https://github.com/minio/minio/blob/master/cmd/xl-sets.go 

    9https://min.io/resources/docs/MinIO-throughput-benchmarks-on-NVMe-SSD.pdf 

    10https://github.com/minio/minio/blob/master/cmd/admin-heal-ops.go

    11https://github.com/klauspost/reedsolomon/blob/master/options.go

    12https://github.com/minio/dsync

    13https://min.io/resources/docs/CPG-MinIO-implementation-guide.pdf 

    14https://github.com/minio/minio/issues/7986

    15https://docs.min.io/docs/minio-bucket-notification-guide.html

    (TaoCloud团队原创 《MinIO技术白皮书》微信公众号版

    展开全文
  • 过滤器、监听器,以及JSON、Ajax和正则表达式等开发必备技能,并学习文件上传和验证码等功能开发。 2.4.1 JSON基础 JSON的基础语法,以及流行JSON框架FastJSON的使用。 内容: JSON语法规则: JSON数据是以key/...

    过滤器、监听器,以及JSON、Ajax和正则表达式等开发必备技能,并学习文件上传和验证码等功能开发。

    2.4.1 JSON基础

    JSON的基础语法,以及流行JSON框架FastJSON的使用。
    内容:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    JSON语法规则:
    在这里插入图片描述
    JSON数据是以key/value的形式描述,key和value中间使用冒号分隔开;多组key/value值使用逗号分隔开,例如:{ “firstName”:“John” , “lastName”:“Doe” }。
    JSON的作用主要就是数据的存储与传输,易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。使用这种方式可以更快的进行前后台数据的传输。在存储数据方面逐渐替代了xml的功能。从实际的使用看,他们的用途不一样,xml主要用作配置文件和一些简单数据的存储,从后面的学习中可以看到xml作为配置文件是非常常见的。而json更多得用在数据的请求处理中,如通过请求服务器得到json数据,然后在前端或移动端页面中进行显示。
    console.log(json);在浏览器控制台中对json内容进行输出。console是浏览器内置对象,log()方法是向控制台输出日志。
    JavaScript对JSON有天然的支持特性。
    在 JavaScript 中,document.write() 可用于直接向 HTML 输出流写内容。内容中的符合html结构的内容可被直接解析为对应的html标签。用于动态补充或展示页面内容。
    Ctrl+Shift+F快捷键格式化代码。
    JSON与字符串互相转换:
    在这里插入图片描述
    JSON是浏览器(IE8以下不支持JSON对象)内置对象。
    在这里插入图片描述
    在javascript的字符串中出现多对””,要使用转义符号\,避免混淆。JSON以[ ]开头表示这是一个json数组的含义,里面含有多个json对象。而只编写{ }则表示的是一个json对象。
    JSON对象书写的另一种方式,类似对象。
    在这里插入图片描述
    每个载入浏览器的 HTML 文档都会成为 Document 对象。Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问。document.write()表示向文档写 HTML 表达式 或 JavaScript 代码。除此之外常用的还有document.getElementById(“id值”)返回对拥有指定 id 值的第一个标签对象的引用。
    console.log()、console.info()的作用都是在浏览器控制台打印信息的,可理解它们两个其实就是同一个,只不过名字不一样,其中console.info()打印出的信息,在控制显示的时候信息前面会出现一个小图标。
    Java是开源的全世界的工程师都在为Java添砖增瓦。
    JSON与Java交互:
    JSON工具包是用来JAVA与JSON之间序列号与反序列化。
    在这里插入图片描述
    github全世界最著名的开源软件平台(http://www.github.com)。搜索fastjson下载alibaba开发的,可在github下载也可在https://repo1.maven.org/maven2/com/alibaba/fastjson/。下载。
    JavaBean简单笼统的说是一种符合一定规范编写的Java类,不是一种技术,而是一种规范 , 表达实体和信息的规范。大家针对这种规范,总结了很多开发技巧、工具函数。符合这种规范的类,可以被其它的程序员或者框架使用。它的方法命名,构造及行为必须符合特定的约定:1、这个类必须具有一个公共的(public)无参构造函数;2、所有属性私有化(private);
    2、私有化的属性必须通过public类型的方法(getter和setter)暴露给其他程序,并且方法的命名也必须遵循一定的命名规范。4、这个类应是可序列化的。(比如可以实现Serializable 接口)。这些特点使它们有更好的封装性和可重用性。并且可以被序列化(持久化),保存在硬盘或者在网络上传输。
    Bean译过来是“豆子”的意思,我们是不是只能看到豆子硬硬的外皮,而看不到内部的东西。
    通常我们说的API是API文档的简称。API文档就像字典或使用说明书 一样的存在,对某样技术用到的接口、类、方法等具体的解释。就像字典一样,在使用时,可以在API中的方法时能够快速找到,学会使用这个方法。
    FastJson序列化与JSON注解:
    日历类Calendar:月份是从0开始的,这是java中约定俗成的规定,其余的是从1开始的。
    在这里插入图片描述
    JSON序列化:格式:JSON.toJSONString(需要序列化的JAVA对象/对象数组/对象集合),将JAVA对象转换成JSON字符串。
    在这里插入图片描述
    JSON注解:格式:@JSONField(属性=属性值),放在JAVA对象中需要设置序列化JSON字符串格式的属性上面。如图:
    在这里插入图片描述
    在这里插入图片描述
    @JSONField(format=“yyyy-MM-dd HH:mm:ss”)对(下面代码所示时间属性)属性序列化注解,@JSONField(serialize=false)不对(下面代码所示属性)属性进行序列化。
    JSON反序列:格式:JSON.parseObject(需要转换成JAVA对象的JSON字符串,需要转换成JAVA对象的类型),将需要转换成JAVA对象的JSON字符串转换成JAVA对象的类型。
    在这里插入图片描述
    这里指Employee类编译好的字节码文件,表示将json转换为Employee对象。其他类加不加.class看是否将json转换具体对象。
    序列化 (Serialization)是指将对象的状态信息转换为可以存储或传输的形式的过程。可以将其保存到磁盘文件中或通过网络发送到任何其他程序;反序列化是相反的过程(将流转换为对象)。而序列化的一种选择,就是使用json来当作临时存储的方式。JSON又称JavaScript 对象表示法(JavaScript Object Notation),但是它本身是和平台与语言无关的,具有自我描述性,易于理解。json是能直接转换为js对象的一种格式,使用它来存储对象,更加方便。
    JSON序列化是把对象包装成JSON字符串传输。Json的优点:明文结构一目了然,可以跨语言,属性的增加减少对解析端影响较小。缺点:字节数过多,无法处理对象方法等数据,依赖于不同的第三方类库。
    导入项目后需要配置自己的jdk。项目右击,选择Build Path,Configure Build Path。在右侧Libraies中选择JRE System Library,点击Edit,选择Alternate JER,点击下箭头,选择自己的jdk,点击Finish,Apply and Close。
    json反序列化话需要对应实体类有无参构造器。
    对于FastJson来说如果JAVA对象的某个属性为null,则序列化不输出此属性的JSON字符串。
    JSON.parseArray(JSON字符串,转换成的JAVA对象类型),JSON字符串(多个JSON对象的数组)转换成JAVA对象List集合:
    在这里插入图片描述

    2.4.2 jQuery与Ajax基础

    介绍JQuery的常用语法规则及如何使用Ajax进行页面局部刷新。
    jQuery与Ajax:
    内容:
    在这里插入图片描述
    JavaScript库:
    在这里插入图片描述
    jQuery介绍:
    在这里插入图片描述
    jQuery下载与安装:
    在这里插入图片描述

    EL 表达式是从域中取出之前存好的属性,是 ${},而jQuery 是$() 并且是获取页面的元素,虽然写法比较类似,但是其实还是没有太大关联的。
    

    jQuery选择器实验室:
    在这里插入图片描述
    js里addClass()和className 的区别:1、addclass()是JQuery的函数。JavaScript中没有。className 属性可以用来设置或返回元素的 class 属性。2、 addClass() 方法向被选元素添加一个或多个类,该方法不会移除已存在的 class 属性,仅仅添加一个或多个 class 属性–>就是说addClass方法只会向当前被选中的元素添加属性,而不会移除原有属性。className将其赋值为"“可以达到清空元素的class的目的.
    选择器的用途:选择需要的元素 $(“a”) ( 选 择 器 表 达 式 ( 字 符 串 形 式 ) ) 。 / / 移 除 指 定 的 c s s 类 , 移 除 所 有 具 有 该 c s s 类 的 处 理 。 (选择器表达式(字符串形式))。//移除指定的css类,移除所有具有该css类的处理。 (())//csscss(”*").removeClass(“highlight”);在css样式表中书写时,ID选择符前面应该加前缀符号‘#’,标签选择器直接写标签名,类选择器应该加前缀符号‘.’。
    Web页面开发的两个要素:
    在这里插入图片描述
    jQuery选择器:
    在这里插入图片描述
    基本选择器:
    在这里插入图片描述
    层叠选择器:
    在这里插入图片描述
    注:兄弟选择器只能选择在该元素后边并且平级的元素。
    属性选择器:
    在这里插入图片描述
    在这里插入图片描述

    注:属性选择器只会选择明确写了属性的选择器,eg:对于<input>标签默认type=”text”,当不写type时,属性选择器${“input[type=’text’]”不会选择这个<input>

    位置选择器:
    在这里插入图片描述
    表单选择器:
    在这里插入图片描述

    表单选择器的”:”后边是表单包含的元素的type属性值。注:对于<input>标签默认type=”text”,当不写type时,表单选择器”:text”会选择这个<input>,刚好补充了属性选择器${“input[type=’text’]”不会选择这个<input>。$("selector:input")----------所有输入框。
    $("form:text")是选择form表单中的type=’text’元素,而$("selector:text")(写法$(“:text”))是选择页面中所有type=’text’元素;如果是获取写在form表单中的type=’text’就可以使用$("form:input")。
    浏览器输入URL中http:// 这个加与不加没区别的,浏览器解析时会默认用这个协议的。
    

    操作元素属性:
    在这里插入图片描述
    在这里插入图片描述
    attr()方法中参数只有一个时,是获取这个参数对应的属性值,参数是两个时是设置第一个参数对应的属性的值为第二个参数。
    注:获取元素属性时,当拥有此属性的元素为多个时,获取的是第一个元素的属性。
    在input的标签中,对于不同的输入类型,value 属性的用法也不同:当type=“button”, “reset”,“submit” -时,value只是为了定义按钮上的显示的文本,所以这里value值并不会清空。
    操作元素的CSS样式:
    在这里插入图片描述
    在这里插入图片描述
    操作元素的css样式中css()方法中参数只有一个时,是获取这个参数对应的样式属性值,参数是两个时是设置第一个参数对应的样式属性的值为第二个参数。当需要设置多个样式属性可用JSON对象表示法。
    注:获取元素样式属性时,当拥有此样式属性的元素为多个时,获取的是第一个元素的样式属性。
    在这里插入图片描述
    操作元素的css样式中addClass()方法为选择器选中的元素添加多个指定类名需添加空格。
    设置元素内容:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    val()----获取或设置输入项的值,设置参数,表示更改匹配元素的值;不设置参数,表示获取匹配元素的值。text()—获取或设置元素的纯文本,设置参数,表示更改匹配元素的值;不设置参数,表示获取匹配元素的值。如果文本内容出现标签,不进行转义,输出标签。html()—获取或设置元素内部的html,设置参数,表示更改匹配元素的值;不设置参数,表示获取匹配元素的值。如果文本内容出现标签,进行转义,浏览器会解析标签。
    当文本内容不含标签建议使用text()方法,text()和html()方法最大的区别是对于文本中的html标签是否进行转义。
    b标签是加粗文本。
    ( " s p a n . m y c l a s s " ) − − > 这 个 是 一 个 组 合 的 选 择 器 , 表 示 选 择 c l a s s 为 m y c l a s s 的 s p a n 标 签 。 j Q u e r y 事 件 处 理 方 法 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002056578. p n g ) j Q u e r y 常 用 事 件 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 202011290021036. p n g ? x − o s s − p r o c e s s = i m a g e / w a t e r m a r k , t y p e Z m F u Z 3 p o Z W 5 n a G V p d G k , s h a d o w 1 0 , t e x t a H R 0 c H M 6 L y 9 i b G 9 n L m N z Z G 4 u b m V 0 L 3 F x X z M 4 M z I 2 M T A 3 , s i z e 1 6 , c o l o r F F F F F F , t 7 0 ) l o a d ( ) 方 法 是 从 服 务 器 加 载 数 据 , 并 把 返 回 的 数 据 放 入 被 选 元 素 中 , 需 要 传 入 u r l 等 参 数 。 当 页 面 初 始 化 后 , 执 行 ("span.myclass")--> 这个是一个组合的选择器,表示选择class为myclass的span标签。 jQuery事件处理方法: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002056578.png) jQuery常用事件: ![在这里插入图片描述](https://img-blog.csdnimg.cn/202011290021036.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MzI2MTA3,size_16,color_FFFFFF,t_70) load()方法是从服务器加载数据,并把返回的数据放入被选元素中,需要传入url等参数。当页面初始化后,执行 ("span.myclass")>classmyclassspanjQuery![](https://imgblog.csdnimg.cn/20201129002056578.png)jQuery![](https://imgblog.csdnimg.cn/202011290021036.png?xossprocess=image/watermark,typeZmFuZ3poZW5naGVpdGk,shadow10,textaHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MzI2MTA3,size16,colorFFFFFF,t70)load()url(function(){})。使用 ( ) 选 中 元 素 需 要 使 用 引 号 引 起 来 。 ()选中元素需要使用引号引起来。 ()使(function(){
    alert(“欢迎来到设置颜色的页面”)😉;
    在这里插入图片描述
    在这里插入图片描述
    ( t h i s ) 是 指 当 前 事 件 产 生 的 对 象 。 按 下 空 格 匹 配 元 素 的 样 式 属 性 c o l o r 为 r e d 。 ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 2020112900212268. p n g ) 处 理 方 法 中 的 e v e n t 参 数 不 是 一 个 固 定 参 数 名 , 可 以 写 成 其 他 名 , 但 最 好 见 名 知 意 还 是 e v e n t 好 。 键 盘 按 键 分 字 符 键 , 非 字 符 键 ( 功 能 键 和 特 殊 按 键 ) , k e y p r e s s 是 用 户 摁 下 按 键 , 并 且 产 生 一 个 字 符 时 发 生 , 所 以 退 格 键 就 只 能 触 发 o n k e y d o w n 和 o n k e y u p 事 件 。 A j a x 介 绍 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002130916. p n g ) a j a x 是 前 端 完 成 的 , 可 通 过 a j a x 访 问 后 台 , 然 后 通 过 后 台 返 回 的 数 据 , 展 示 到 前 端 页 面 中 。 A j a x 的 使 用 流 程 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002140773. p n g ) 创 建 X M L H t t p R e q u e s t 对 象 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 2020112900214841. p n g ) ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002150989. p n g ? x − o s s − p r o c e s s = i m a g e / w a t e r m a r k , t y p e Z m F u Z 3 p o Z W 5 n a G V p d G k , s h a d o w 1 0 , t e x t a H R 0 c H M 6 L y 9 i b G 9 n L m N z Z G 4 u b m V 0 L 3 F x X z M 4 M z I 2 M T A 3 , s i z e 1 6 , c o l o r F F F F F F , t 7 0 ) 在 J S 里 , w i n d o w 是 最 顶 级 对 象 , 它 代 表 了 一 个 窗 体 , 表 示 浏 览 器 的 初 始 网 页 的 时 候 会 自 动 创 建 一 个 w i n d o w 对 象 。 I E 5 I E 6 版 本 以 上 的 浏 览 器 的 X M L H t t p R e q u e s t 是 w i n d o w 的 子 对 象 , 所 以 如 果 是 高 版 本 的 浏 览 器 可 以 直 接 创 建 这 个 X M L H t t p R e q u e s t , 但 是 I E 6.0 , 5.5 都 是 没 有 的 , 它 们 是 使 用 w i n d o w . A c t i v e X O b j e c t 替 代 , 所 以 这 里 需 要 添 加 一 个 判 断 。 发 送 A j a x 请 求 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002200992. p n g ) ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002204371. p n g ) o p e n ( ) 方 法 参 数 : 第 一 个 是 请 求 方 式 , 第 二 个 是 请 求 的 U R L , 第 三 个 是 同 步 ( f a l s e ) 或 异 步 ( t r u e ) . 处 理 服 务 器 响 应 : ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002211714. p n g ) ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002214900. p n g ) ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002217541. p n g ? x − o s s − p r o c e s s = i m a g e / w a t e r m a r k , t y p e Z m F u Z 3 p o Z W 5 n a G V p d G k , s h a d o w 1 0 , t e x t a H R 0 c H M 6 L y 9 i b G 9 n L m N z Z G 4 u b m V 0 L 3 F x X z M 4 M z I 2 M T A 3 , s i z e 1 6 , c o l o r F F F F F F , t 7 0 ) ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20201129002221449. p n g ) i n n e r H T M L 是 j s 的 一 个 属 性 , 设 置 元 素 内 部 的 H T M L 。 使 用 a j a x 进 行 前 后 台 数 据 的 交 互 , h t m l 进 行 静 态 界 面 的 展 示 , 而 j s p 文 件 , 是 动 态 网 页 , 可 以 直 接 可 以 进 行 前 后 台 数 据 的 交 互 , 可 以 不 需 使 用 a j a x , 所 以 这 里 使 用 h t m l + a j a x 的 方 式 进 行 数 据 的 交 互 和 展 示 。 但 是 也 可 以 使 用 j s p 和 a j a x 的 使 用 。 这 就 可 以 实 现 页 面 局 部 刷 新 了 , 页 面 完 全 无 需 重 新 加 载 , 大 大 的 提 升 了 页 面 的 效 率 及 速 度 。 在 实 际 的 开 发 中 , 都 是 可 以 的 , 具 体 根 据 要 求 做 。 在 S e r v l e t 中 要 注 意 , 如 果 前 面 是 A j a x 请 求 发 送 的 话 , 就 不 用 页 面 跳 转 , 而 是 直 接 返 回 我 们 需 要 返 回 的 数 据 , 通 常 这 种 数 据 格 式 通 过 j s o n 来 传 去 客 户 端 。 很 少 回 传 h t m l 。 j s 中 的 J S O N 和 S e r v l e t 里 面 的 J S O N 有 别 。 这 个 J S O N 是 J a v a S c r i p t 内 置 对 象 , 而 S e r v l e t 中 的 J S O N 是 F a s t J s o n 提 供 给 j a v a 的 一 个 对 象 。 不 加 斜 杠 的 参 考 路 径 是 当 前 访 问 的 路 径 ; 加 上 斜 杠 的 参 考 路 径 是 我 们 访 问 的 根 路 径 , 加 载 文 件 是 从 根 路 径 开 始 加 载 。 j s 中 在 初 始 化 变 量 时 , 如 果 不 设 置 初 始 值 , 默 认 为 u n d e f i n e d , 在 拼 接 时 会 将 其 加 上 。 因 此 在 初 始 化 时 要 给 其 初 始 值 , 如 : v a r h t m l = “ ” ; 利 用 A j a x 实 现 新 闻 列 表 。 同 步 和 异 步 的 区 别 : 同 步 状 态 : 代 码 进 入 等 待 状 态 , 响 应 不 返 回 , 程 序 不 会 继 续 向 下 执 行 , 就 是 前 面 代 码 没 执 行 完 后 面 的 代 码 就 会 一 直 等 待 。 异 步 状 态 : 程 序 不 会 处 于 阻 塞 状 态 , 程 序 依 旧 向 下 执 行 , 数 据 返 回 时 , 是 通 过 触 发 o n r e a d y s t a t e c h a n g e 事 件 进 行 数 据 的 获 取 和 处 理 。 通 常 A j a x 设 置 为 异 步 状 态 。 使 用 了 同 步 , x m l h t t p . o n e r e a d y s t a t e c h a n g e = f u n c t i o n ( ) . . . , 该 事 件 失 效 . . ( 将 f u n c t i o n ( ) 代 码 取 出 来 放 在 s e n d ( ) 后 面 就 行 了 ) 。 因 为 J a v a S c r i p t 本 身 是 不 支 持 多 线 程 技 术 的 , A j a x 属 于 J a v a S c r i p t 范 畴 , 因 此 A j a x 是 单 线 程 的 。 在 浏 览 器 中 发 送 请 求 是 : 1 、 在 浏 览 器 任 务 就 被 分 成 两 种 , 一 种 是 同 步 任 务 , 一 种 是 异 步 任 务 。 同 步 任 务 : 只 有 前 一 个 任 务 执 行 完 成 后 , 才 可 执 行 下 一 个 任 务 , 在 主 线 程 中 。 异 步 任 务 : 这 个 队 列 的 所 有 任 务 都 是 不 进 入 主 线 程 执 行 , 而 是 被 浏 览 提 供 的 线 程 执 行 。 当 执 行 完 毕 后 就 会 产 生 一 个 回 调 函 数 , 并 且 通 知 主 线 程 , 在 主 线 程 执 行 完 当 前 所 执 行 的 任 务 后 , 就 会 调 取 最 早 通 知 自 己 的 回 调 函 数 , 使 其 进 入 主 线 程 中 执 行 , 比 如 a j a x 请 求 , 在 主 线 程 中 呈 现 的 就 是 请 求 结 果 。 2 、 如 上 所 述 , 浏 览 器 中 请 求 的 执 行 顺 序 就 是 : ( 1 ) 所 有 同 步 任 务 都 在 主 线 程 上 执 行 , 形 成 一 个 执 行 栈 ( e x e c u t i o n c o n t e x t s t a c k ) 。 ( 2 ) 主 线 程 之 外 , 还 存 在 一 个 " 任 务 队 列 " ( t a s k q u e u e ) 。 只 要 异 步 任 务 有 了 运 行 结 果 , 就 在 " 任 务 队 列 " 之 中 放 置 一 个 事 件 ( 回 调 函 数 c a l l b a c k ) 。 ( 3 ) 一 旦 " 执 行 栈 " 中 的 所 有 同 步 任 务 执 行 完 毕 , 系 统 就 会 读 取 " 任 务 队 列 " , 查 看 里 面 有 哪 些 事 件 , 结 束 等 待 状 态 , 进 入 执 行 栈 , 开 始 执 行 对 应 的 异 步 任 务 。 ( 4 ) 最 后 主 线 程 就 是 不 断 重 复 上 面 的 第 三 步 。 如 上 所 述 就 是 − − > 异 步 请 求 在 浏 览 器 中 的 执 行 过 程 , 所 以 说 异 步 请 求 都 是 在 同 步 请 求 执 行 完 成 后 , 再 去 发 送 请 求 访 问 服 务 器 , 所 以 这 些 请 求 都 是 按 照 顺 序 来 依 次 执 行 的 , 并 不 会 出 现 " 先 处 理 了 后 台 数 据 A , 这 时 候 异 步 读 取 后 台 数 据 A " 这 样 的 情 况 。 T h r e a d . s l e e p ( 5000 ) 代 表 将 当 前 的 线 程 执 行 休 眠 , 也 就 是 现 在 页 面 发 送 过 来 的 请 求 的 这 个 线 程 。 T h r e a d 是 一 个 类 , s l e e p 是 一 个 静 态 方 法 , 可 以 通 过 T h r e a d 类 名 直 接 调 用 s l e e p 方 法 , 不 用 创 建 对 象 。 同 步 是 发 送 一 个 请 求 , 需 要 等 待 返 回 , 然 后 才 能 够 发 送 下 一 个 请 求 , 有 个 等 待 的 过 程 ; 异 步 是 发 送 一 个 请 求 , 不 需 要 等 待 返 回 , 随 时 可 以 再 发 送 下 一 个 请 求 , 不 需 要 等 待 。 j Q u e r y 对 A j a x 的 支 持 : 1. j Q u e r y 对 A j a x 进 行 封 装 , 提 供 了 (this)是指当前事件产生的对象。 按下空格匹配元素的样式属性color为red。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2020112900212268.png) 处理方法中的event参数不是一个固定参数名,可以写成其他名,但最好见名知意还是event好。 键盘按键分字符键,非字符键(功能键和特殊按键),keypress是用户摁下按键,并且产生一个字符时发生,所以退格键就只能触发onkeydown和onkeyup事件。 Ajax介绍: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002130916.png) ajax是前端完成的,可通过ajax访问后台,然后通过后台返回的数据,展示到前端页面中。 Ajax的使用流程: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002140773.png) 创建XMLHttpRequest对象: ![在这里插入图片描述](https://img-blog.csdnimg.cn/2020112900214841.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002150989.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MzI2MTA3,size_16,color_FFFFFF,t_70) 在JS里,window是最顶级对象,它代表了一个窗体,表示浏览器的初始网页的时候会自动创建一个window对象。IE5 IE6 版本以上的浏览器的XMLHttpRequest是window的子对象,所以如果是高版本的浏览器可以直接创建这个XMLHttpRequest,但是IE6.0,5.5都是没有的,它们是使用window.ActiveXObject替代,所以这里需要添加一个判断。 发送Ajax请求: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002200992.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002204371.png) open()方法参数:第一个是请求方式,第二个是请求的URL,第三个是同步(false)或异步(true). 处理服务器响应: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002211714.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002214900.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002217541.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MzI2MTA3,size_16,color_FFFFFF,t_70) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201129002221449.png) innerHTML是js的一个属性,设置元素内部的HTML。 使用ajax进行前后台数据的交互,html进行静态界面的展示,而jsp文件,是动态网页,可以直接可以进行前后台数据的交互,可以不需使用ajax,所以这里使用html+ajax的方式进行数据的交互和展示。但是也可以使用jsp和ajax的使用。这就可以实现页面局部刷新了,页面完全无需重新加载,大大的提升了页面的效率及速度。在实际的开发中,都是可以的,具体根据要求做。 在Servlet中要注意,如果前面是Ajax请求发送的话,就不用页面跳转,而是直接返回我们需要返回的数据,通常这种数据格式通过json来传去客户端。很少回传html。 js中的JSON和Servlet里面的JSON有别。这个JSON是JavaScript内置对象,而Servlet中的JSON是FastJson提供给java的一个对象。 不加斜杠的参考路径是当前访问的路径;加上斜杠的参考路径是我们访问的根路径,加载文件是从根路径开始加载。 js中在初始化变量时,如果不设置初始值,默认为undefined,在拼接时会将其加上。因此在初始化时要给其初始值,如:var html=“”; 利用Ajax实现新闻列表。 同步和异步的区别: 同步状态:代码进入等待状态,响应不返回,程序不会继续向下执行,就是前面代码没执行完后面的代码就会一直等待。异步状态:程序不会处于阻塞状态,程序依旧向下执行,数据返回时,是通过触发onreadystatechange事件进行数据的获取和处理。通常Ajax设置为异步状态。使用了同步,xmlhttp.onereadystatechange=function(){...},该事件失效..(将function()代码取出来放在send()后面就行了)。 因为JavaScript本身是不支持多线程技术的,Ajax属于JavaScript范畴,因此Ajax是单线程的。 在浏览器中发送请求是:1、在浏览器任务就被分成两种,一种是同步任务,一种是异步任务。同步任务:只有前一个任务执行完成后,才可执行下一个任务,在主线程中。异步任务:这个队列的所有任务都是不进入主线程执行,而是被浏览提供的线程执行。当执行完毕后就会产生一个回调函数,并且通知主线程,在主线程执行完当前所执行的任务后,就会调取最早通知自己的回调函数,使其进入主线程中执行,比如ajax请求,在主线程中呈现的就是请求结果。2、如上所述,浏览器中请求的执行顺序就是:(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件(回调函数callback)。(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",查看里面有哪些事件,结束等待状态,进入执行栈,开始执行对应的异步任务。(4)最后主线程就是不断重复上面的第三步。 如上所述就是-->异步请求在浏览器中的执行过程,所以说异步请求都是在同步请求执行完成后,再去发送请求访问服务器,所以这些请求都是按照顺序来依次执行的,并不会出现"先处理了后台数据A,这时候异步读取后台数据A" 这样的情况。 Thread.sleep(5000)代表将当前的线程执行休眠,也就是现在页面发送过来的请求的这个线程。Thread是一个类,sleep是一个静态方法,可以通过Thread类名直接调用sleep方法,不用创建对象。 同步是发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待的过程;异步是发送一个请求,不需要等待返回,随时可以再发送下一个请求,不需要等待。 jQuery对Ajax的支持: 1.jQuery对Ajax进行封装,提供了 (this)colorred![](https://imgblog.csdnimg.cn/2020112900212268.png)eventeventkeypress退onkeydownonkeyupAjax![](https://imgblog.csdnimg.cn/20201129002130916.png)ajaxajax访Ajax使![](https://imgblog.csdnimg.cn/20201129002140773.png)XMLHttpRequest![](https://imgblog.csdnimg.cn/2020112900214841.png)![](https://imgblog.csdnimg.cn/20201129002150989.png?xossprocess=image/watermark,typeZmFuZ3poZW5naGVpdGk,shadow10,textaHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MzI2MTA3,size16,colorFFFFFF,t70)JSwindowwindowIE5IE6XMLHttpRequestwindowXMLHttpRequestIE6.05.5使window.ActiveXObjectAjax![](https://imgblog.csdnimg.cn/20201129002200992.png)![](https://imgblog.csdnimg.cn/20201129002204371.png)open()URL(false)(true).![](https://imgblog.csdnimg.cn/20201129002211714.png)![](https://imgblog.csdnimg.cn/20201129002214900.png)![](https://imgblog.csdnimg.cn/20201129002217541.png?xossprocess=image/watermark,typeZmFuZ3poZW5naGVpdGk,shadow10,textaHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MzI2MTA3,size16,colorFFFFFF,t70)![](https://imgblog.csdnimg.cn/20201129002221449.png)innerHTMLjsHTML使ajaxhtmljsp使ajax使html+ajax使jspajax使ServletAjaxjsonhtmljsJSONServletJSONJSONJavaScriptServletJSONFastJsonjava访访jsundefinedvarhtml=Ajax,onreadystatechangeAjax使xmlhttp.onereadystatechange=function.....functionsend()JavaScript线AjaxJavaScriptAjax线1线线线线线使线ajax线21线executioncontextstack2线""taskqueue""callback3""""4线>访"AA"Thread.sleep(5000)线线ThreadsleepThreadsleep,,,,jQueryAjax1.jQueryAjax.ajax()方法。2.语法:$ajax(option)。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    function(json)中的json表示把从服务器返回的json字符串存储到json对象中。然后JavaScript将json字符串解析为json对象,然后在function中执行调用等操作。ajax请求成功得到响应内容,js将得到的json字符串自动解析为json对象。JavaScript的语法与Java的语法格式有些不同,在JavaScript中,方法中的参数是形参,不需要定义,可以直接使用,而这里function中的json就是形参,可以作为一个变量在方法里直接使用。
    $ 就是 jQuery 对象的引用,可以实现以 jQuery 对象为入口的操作。$(function() {…}) 是 jQuery 中的经典用法,等同于 $(document).ready(function() {…}),即在页面加载完成后才执行某个函数。
    参数"error":function(xmlhttp,errorText){xmlhttp.status 错误状态码}

    jQuery中的append()方法是追加,$("#container").append("<h1>"+title+"</h1>");JQuery append() 方法在被选元素的结尾(仍然在内部)插入指定内容。
    

    在这里插入图片描述
    在这里插入图片描述
    向服务器传递的参数”data”有两种书写方式,一种是URL参数方式,一种是JSON格式,建议使用json方式书写,可读性、分割较好,同时是在发送请求时,参数却会转换URL参数方式在URL后面(get请求)/请求体中(post请求)。在后台获取data中的key值和value值,可直接通过request.getParameter(“t”);进行获取传过来的参数value值。而key值是我们自己设置的名字,用来获取value的,是无法被获取的。
    ajax的本质就是在不刷新整体页面的情况下更新部分页面。
    XMLHttpRequest、xmlhttp.responseText返回值 等内容都是是原生JS的书写方式;而JQuery是对原生JS的封装,所以在JQuery中就可以直接使用了。
    实现二级联动菜单:
    在这里插入图片描述

    2.4.3 Java正则表达式

    介绍正则表达式的语法规则,以及常见的校验规则。
    什么是正则表达式:英文缩写regex。
    在这里插入图片描述
    应用:
    在这里插入图片描述
    正则表达式的分隔符可以有多种如:// {} ()等。
    字符范围匹配:
    在这里插入图片描述
    Tool.oschina.net/regex是开源中国提供的开源的正则表达式在线测试工具。
    元字符:
    在这里插入图片描述
    注意:”.”是匹配任意单个字符(换行符除外),”.”只匹配特殊字符”.”。”-”可以进行原义匹配,所以不加\也是可以的。
    X|Y是匹配 x 或 y,匹配的X或者Y的整个内容,也就是输入要匹配的字符串中要么匹配X(eg:0571),要么匹配Y(eg:4-\d\d\d\d\d\d\d\d)。[XY]则是匹配所包含的单个字符,表示字符串中要匹配X(eg:1),要么匹配Y(eg:4)。
    \d匹配单个数字,d应该代表digitization n.数字化。\n 匹配单个换行符, n应该代表newline character换行符。\s匹配单个空格 ,s应该代表space n.空间。w是Word单词的缩写,\w匹配包括下划线的任何单词字符,等价于’[A-Za-z0-9_]’。
    多次重复匹配:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    1、正则表达式是用来检查一个字符串是否含有符合规则的子串。即只要字符串中有可以匹配到的就算找到,无论它在字符串中的哪个位置。2、如果正则表达式是^\d{3,4}-\d{7,8} , 规 定 了 字 符 串 的 开 始 和 字 符 串 的 结 尾 ,规定了字符串的开始^和字符串的结尾 ,那么1234-123456789就不能匹配到了。
    定位匹配:
    在这里插入图片描述
    ^www.com 中 w w w 表 示 必 须 以 w w w 开 头 , 必 须 以 c o m 结 尾 。 在 进 行 全 字 匹 配 时 添 加 上 和 中^www表示必须以www开头,必须以com结尾。 在进行全字匹配时添加上^和 wwwwwwcom,因为^匹配输入字符串的开始位置。$匹配输入字符串的结束位置,这样可以保证在某些场景时,防止造成子串匹配的问题。
    贪婪模式:
    在这里插入图片描述
    非贪婪模式:?要在描述数量的后边。
    在这里插入图片描述
    表达式分组:
    在这里插入图片描述
    小括号的优先级是最高的,也就是说用小括号括起来的内容会作为一个整体,然后再和其他内容组合。
    在这里插入图片描述
    正则表达式中字符集charset为UTF-8(Unicode)的中文字符编码的范围是\u4e00-\u9fa5,Unicode编码,是一种全世界语言都包括的一种编码。
    (?:)表示非捕获分组,和捕获分组唯一的区别在于,非捕获分组匹配的值不会保存起来。加号(+)进行拼接。
    字符串“fdgdgawwwrwoijjko”用正则表达式“w
    ”匹配共找到16处匹配,用替换文本“88”的替换结果是:“88f88d88g88d88g88a8888r8888o88i88j88j88k88o88”。
    JavaScript表单验证:
    对表单姓名和身份证进行校验:
    在这里插入图片描述
    在这里插入图片描述
    在JS中定义正则表达式对象只需要在/正则表达式/。
    onsubmit是表单的提交事件,返回true表示成功提交(不写return true也会提交的),返回false不提交。
    在JS中定义正则表达式对象只需要在“/”和“/”之间写正则表达式就可以返回一个正则表达式对象,正则表达式对象的test()方法可以校验数据是否符合表达式,返回true代表匹配成功,返回false代表匹配失败。
    innerHTML 属性,设置或返回表格行的开始和结束标签之间的 HTML,意思就是innerHTML可以为元素对象插入内容。append()是js中的函数表示在被选元素的结尾插入指定内容。
    Java中Web页面信息提取:
    在这里插入图片描述
    String字符串的拼接会产生一个新的字符串,而不是更新之前的字符串,这样每次拼接都会产生多余的String,尤其当多次拼接的时候非常浪费内存。而StringBulider则不会,每次拼接都是在原来的字符串上进行拼接。所以这里建议使用StringBulider而不要替换成String。StringBuilder是一个可变的字符序列。
    FileInputStream 从文件系统中的某个文件中获得输入字节。InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。BufferedReader : 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。当关闭bufferedReader 后,底层会自动关闭fis和isr。这里无需手动关闭。eclipse是一个编译器,当编译器关闭时流不会自动关闭。
    在这里插入图片描述
    java中创建正则表达式的对象:Pattern p=Pattern.compile("(正则表达式)");
    匹配正则表达式:Matcher m = p.matcher(content);
    在这里插入图片描述
    当find()方法找到一个正则表达式成功匹配的字符串,可以通过group(0)得到该子串,group(1)得到该子串的第一个表达式分组,依此类推。再次查询时,之前对该方法的调用成功,并且匹配器此后没有重置,则从上一个匹配器未匹配的第一个字符开始。

    2.4.4 过滤器

    介绍过滤器的创建过程、生命周期和过滤器链等内容,及过滤器的应用场景。
    内容:
    在这里插入图片描述
    过滤器-Filter:
    在这里插入图片描述
    过滤链:
    在这里插入图片描述
    开发过滤器三要素:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    过滤器的生命周期:
    在这里插入图片描述
    初始化- Filter.init() //对当前过滤器进行初始化,执行时机tomcat启动后会自动创建Filter这个过滤器对象,一旦对象创建完,马上执行Filter.init()方法。提供服务-Filter.doilter(),当有请求进来时,会先执行Filter.doilter()方法。销毁- Filter.destroy()//执行时机应用(tomcat服务器)关闭或者重启时。
    过滤器的特性:
    在这里插入图片描述
    唯一的过滤器对象在并发环境中采用”单例多线程”提供服务。单例:对象只有一个,多线程:一个项目可能会又多个人同时访问,就会出现多线程。虽然全局只有一个过滤器对象,但是如果是在并发环节中,每一个请求进来的时候,过滤器都会为其创建一个独立的线程来提供服务,线程和线程之间他们是彼此不受影响的,正是因为叫单例多线程的这么一种设计,既保证了过滤器不会因为频繁创建对象消耗系统资源,同时又采用多线程方式,有效提高了多用户在访问同一个过滤器时的执行速度。
    请求转发的链接路径是不会被过滤器拦截,而重定向会被过滤器拦截。
    过滤器两种开发方式:
    1.过滤器的配置形式:
    在这里插入图片描述
    2.过滤器的注解形式:
    在这里插入图片描述
    配置与注解如何选择:
    在这里插入图片描述
    Tomcat是如何发现注解形式的过滤器并启动过滤器的?Servlet3.0版本之后,默认对注解有了支持。每次应用程序启动时,Tomcat会对项目内的所有.class字节码文件(java编译后的文件)进行扫描,当扫描到WebFilter注解时,则启用这个过滤器。同时存在配置形式和注解形式的过滤器时,会先执行配置形式过滤器。一般来说,整个项目只需要采用一种形式的过滤器,尽可能避免使用两种形式。
    doFilter()的含义是调用过滤器链中的过滤器,过滤器链是将请求的内容,与过滤器链中的过滤内容一一匹配,匹配成功,filter(过滤器)在doFilter里做完自己的过滤逻辑,交给下一个过滤器···循环执行,过滤器走到链的末尾,filterChain执行收尾工作,过滤器执行完毕。
    注解形式配置的过滤器适用于短平快的中小型项目,开发更加快速,但不便于修改,不便于统一配置,所以不适用于中大型项目。
    开发字符集过滤器:
    Web中文乱码的解决:
    在这里插入图片描述
    在这里插入图片描述
    res.setContentType()指定 HTTP 响应的编码,同时指定了浏览器显示的编码。
    ServletRequest接口:
    在这里插入图片描述
    oracle公司制定标准,第三方厂商提供具体实现(.jar包),eg:Apache的tomcat服务器提供的.jar包,如RequestFacade实现类是tomcat所特有。
    过滤器的开发技巧:

    过滤器参数化:过滤器参数初始化有两种方式:1.在web.xml中书写<init-param>标签。2.在过滤器类中写注解。
    

    在这里插入图片描述
    优化字符集过滤器:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    基于注解的初始参数是在java文件中写的,如果java文件修改了,只有重新编译再上线才会生效。而基于web.xml文本的初始参数重新启动tomcat服务器就会生效不需要编译再上线。
    url-pattern设置过滤范围:
    url-pattern常用写法:
    在这里插入图片描述
    还可组合使用:/servlet/*.jsp表示过滤以/servlet/开头以.jsp结尾的匹配。
    /映射的问题:

    在web.xml中默认配置了<welcome-file-list></welcome-file-list>标签,则表示当路径映射为/时,会从上至下查找所访问的页面是否存在,然后将其映射。如果含有index.jsp文件,则在路径中输入/index.jsp和/效果是一致的,也可以将<welcome-file-list></welcome-file-list>标签这部分进行删除,会直接访问index.jsp/html。当没有默认文件时(index.jsp)就会跳转到映射地址中。
    

    过滤器的映射地址设置为/,只对servlet生效 的意思是 :/ 只拦截后台的地址,不会拦截页面。也就是只拦截servlet,不拦截jsp/html。context root设置/,过滤器映射地址设置/,过滤器只对Servlet生效的意思是此过滤器只会拦截映射地址为/的Servlet类(一个项目映射地址为/的Servlet类只有一个),而不能拦截其他映射地址为/xxServlet的Servlet类。
    在这里插入图片描述
    Web.xml中配置过滤器filter对多个URL的作用范围。
    在这里插入图片描述
    过滤器类中配置过滤器filter多个URL的作用范围。
    在这里插入图片描述
    getClass().getSimpleName()跟getName()区别:this.getClass()获取当前类的字节码对象。this.getClass().getSimpleName(),获取当前类的的简写名。this.getName()获取当前类的字节码对象名。
    在这里插入图片描述
    在servlet-2.3中,Filter会过滤一切请求,到了servlet-2.4之后Filter默认下只拦截外部提交的请求,不会过滤内部转发的求情。
    过滤链:
    过滤链开发注意事项:
    在这里插入图片描述

    调用chain.doFiiter()将请求向后传递。过滤器的执行顺序以XML配置中的<filter-mapping>从上到下执行。
    

    浏览器发送的请求中,与过滤器链中的过滤内容匹配成功的请求只有通过过滤链doFilter()中的chain.doFilter(),传递到达Servlet、html和jsp等,然后再通过doFilter()中的chain.doFilter()逆序传递最后响应给浏览器,若过滤链某个过滤类没有执行doFilter()中的chain.doFilter(),那么匹配成功的请求不会到达Servlet、html和jsp等,直接通过之前的doFilter()中的chain.doFilter()逆序执行过滤链,最后响应给浏览器(没有执行Servlet、html和jsp等代码)。若在过滤链中某个过滤类的doFilter()中的chain.doFilter()还有其他请求,那么这些请求会冲突导致异常,注销doFilter()中的chain.doFilter()方法,其他请求会到达Servlet、html和jsp等,然后逆序执行过滤链最后响应浏览器。
    过滤链是双向的,响应返回时按逆序通过过滤链。一个来回实际上是以“doFilter”分割的,doFilter其实就是调用过滤器链的下一个过滤器,这个调用是个递归调用,执行完最后匹配的Filter或者Servlet之后,会再返回到调用处。过滤链执行顺序为FilterA>FilterB>FilterC这种配置顺序下:FilterA(doFilter之前的部分)执行->doFilter调用FilterB(同样执行doFilter之前的部分)->调用FilterC->Hello World!->返回执行FilterC(doFilter之后的部分)->返回FilterB(doFilter之后的部分)->返回FilterA之后的部分。
    Chain.doFilter()常用做“防火墙功能”的函数体,比如一个应用不希望海外IP访问,判断条件为是否是本地IP,是才执行Chain.doFilter()调用过滤链的下一个过滤器,不是就不执行Chain.doFilter(),此时不会调用下一个过滤器,而是执行完Chain.doFilter()前面的代码后结束应用。
    以注解型过滤器时,过滤器执行顺序按过滤器类名(不是注解中的filterName)的大小顺序(升序执行)进行,如:filterA filterZ filterV三个过滤器执行顺序为AVZ(不合理)类名有独特含义,不能特地为了过滤链的执行顺序而修改过滤器类名。所以建议项目中最好写配置型过滤器。
    项目中过滤器的应用场景:
    多端设备自动匹配:
    request.getHeader(“user-agent”)来获取访问页面的设备类型。
    在新建项目中Context root(上下文路径) 设置为“/”,大型项目中,每个tomcat对应一个应用,当大型项目需要多个应用时,可安装多个tomcat并设置不同端口,通过请求不同的端口来访问不同的应用。这样做的话每个应用都有独立的tomcat进程,一个应用崩溃了不会影响其他的应用。

    2.4.5 监听器

    介绍监听器,常见应用场景及在Java Web中实现监听器。
    内容:
    在这里插入图片描述
    监听器-Listener:
    在这里插入图片描述
    三种监听对象:
    在这里插入图片描述
    过滤器与监听器的区别:
    在这里插入图片描述
    开发监听器三要素:
    在这里插入图片描述
    监听器的配置方式:1.在web.xml当中配置

    <listener>
    <listener-class>com.imooc.listener.FirstListener</listener-class>
    </listener>
    

    2.注解方式:@WebListener。优先选择配置型监听器。
    在这里插入图片描述
    在这里插入图片描述
    stop是停止服务器的服务,Terminate是非正常关闭,是代表中断。
    在这里插入图片描述
    在这里插入图片描述
    六种常用监听接口:
    内置对象监听接口:
    在这里插入图片描述
    属性监听接口:
    在这里插入图片描述

    1. 一个请求tomcat时 处理的过程:Web应用启动时,ServletContext对象被初始化,启动成功后访问地址时,一个HttpServletRequest对象被创建。同时因为是一个新的浏览器窗口发来的请求,所以tomcat会创建一个session对象,网页处理完成后HttpServletRequest被销毁。2. 第二次请求(当浏览器窗口刷新后),session 并没有被创建,因为session id 已存在,通过确认session id 的存在,所以并不会创建新的session。此时,HttpServletRequest 又经历了一次创建与销毁的过程。3. 当浏览器关闭重新打开一个新的窗口,再次访问这个网址,这时一个新的session被创建。原因是新的浏览器并没有包含任何session id,所以由新的浏览器窗口向tomcat发送请求后,会为其创建一个对应的session,原有的sessionid并不会消失,原因是sessionid的凭证不存在了,30分钟后自然过期,或程序代码人为关闭。
    2. 当(servlet)关闭应用时,ServletContext 才被销毁。
      session对象的销毁:1、手动调用销毁 2、30分过期自动销毁。3.手动调用session的invalidate方法session.invalidate();也可以销毁session。Tomcat关闭后,seesion的处理:Tomcat下发布的web应用程序,如果涉及session的操作,在Tomcat关闭或undeploy的时候,将会在work 目录下相应的路径下创建 SESSION.ser 文件存储该webapp的 session 数据进行序列化,当服务器再次启动的时候会加载此文件,进行反序列化,重新恢复之前的session们。即在 session有效时间内重启Tomcat 或re-deploy,将会加载该文件中的数据,恢复用户原来的 session运行环境。所以关闭Tomcat后seesion并不会马上被销毁,仍然还是要等待seesion的有效期过去。
      属性监听接口:
      实际开发对属性监听接口的实现类非常少(可能会监听属性的新增)。
      监听器的应用场景:
      请求流量分析:
      在这里插入图片描述
      请求流量分析统计的统计功能实现:1、利用全局对象监听器在启动应用创建全局对象时往对象添加两个属性,分别是初始化的时间记录表和初始化的次数统计表集合,两者一一对应(存放时间的位置在次数统计表中对应的是该时间的请求次数)。2、利用请求对象监听器在请求到来时对全局对象里的两个属性进行修改。
      关于当前时间如何转化为指定格式的String字符串:
      1、获取当前时间:Date date=new Date();2、设置时间格式:SimpleDateFormat sdf=new SimpleDateFormat(“HH:mm”);//知识点回顾:JSP中利用JSTL里的fmt格式化标签库对时间进行格式化的方式?3、利用sdf里的format()方法表示将当前时间袼式化为指定的格式。返回String对象: String time=sdf.format(date);
      在这里插入图片描述
      List list=new List();list.indexOf()返回的是查询的值在列表中的位置,若无返回-1;list.get(index)返回的是列表中该位置对应的值;list.set(index,value):将list中index位置的值修改为value。
      监听器类中contextInitialized()方法表示在web应用启动时,将最原始的数据进行初始化,getServletContext()方法的作用就是获取ServletContext的上下文对象。ServletContext对象是程序全局变量,对每个用户每个页面都有效。存放在ServletContext对象中。它的存活时间是最长的,只要服务器没有停下,它们就一直可以使用。所以即便是关掉当前窗口甚至是在另一个浏览器中访问,得到的结果是相同的。
      1、request对象就是用来保存请求信息的。在第一次发送请求到服务器的时候,无论是匹配到了Servlet还是Filter等,在doFilter或Service之类的方法执行之前,request就被初始化了。如果在服务器进行转发,那么request不会丢失,使用的是同一个。当进行重定向,或者浏览器重新发起请求的时候,request就不再是同一个了。2、session是在第一次使用session时初始化的。在第一次使用request.getSession的时候,就被初始化了。
    一般将script放在body之后,避免长时间执行script脚本而延迟阻塞。有一些页面的效果的实现,是需要预先动态的加载一些js脚本,所以这些脚本应该放在<body>之前。
    

    入门百度Echarts组件:
    百度ECharts世界上最强大的图表js组件:http://ECharts.apache.org.里面有许多实例和教程。
    需要引入的js文件是dist目录下的echarts.min.js文件(echarts.js的最小压缩文件)。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    setInterval(必需:要调用的函数或要执行的代码串,必需:周期性执行或调用函数或要执行的代码串之间的时间间隔,以毫秒计) 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。而第一个参数,以字符串形式传入,是语法。
    toString()是返回对象的文本表示形式。(String)是强转。如果某个对象不能用另一个对象表示时就会报错。
    监听器常见用途:1) 统计在线人数,利用HttpSessionLisener;2) 加载初始化信息:利用ServletContextListener;3) 统计网站访问量,实现访问监控:利用RequestListener;
    过滤器常用应用场景:统一编码,统一用户认证,屏蔽非法文字,进行响应数据压缩等操作,便于代码重用,不必每个servlet中还要进行相应的操作。
    静态数据预处理:
    对于长期不会改变的静态数据,可以创建一个全局监听对象,服务器启动后在全局监听对象创建初始化时从数据库中提取数据写入全局属性中,然后直接在页面获取需要的全局属性,可以避免每次加载页面时都要从数据库获取数据。

    2.4.6 FreeMarker

    介绍模板引擎FreeMarker的基本使用以及与Servlet进行整合
    内容:
    在这里插入图片描述
    Freemarker模板引擎:
    在这里插入图片描述
    在这里插入图片描述
    主流模板引擎:在这里插入图片描述
    Freemarker:
    在这里插入图片描述
    JSP与Freemarker:
    在这里插入图片描述
    脚本是一种批处理文件的延伸,是一种纯文本保存的程序,简单地说就是一条条的文字命令,这些文字命令是可以看到的(如可以用记事本打开查看、编辑),脚本程序在执行时,是由系统的一个解释器,将其一条条的翻译成机器可识别的指令,并按程序顺序执行。
    Freemarker安装:freemarker.apache.org。
    FreeMarker的执行步骤:1、加载模板。Configuration config=new Configurarion(Configuration.VERSION_2_3_28(FreeMarker的版本号));(创建核心配置对象)//设置加载目录

    config.setClassForTemplateLoading(FreemarkerSample1.class,"")(在类FreemarkerSample1所在的包中加载指定文件,“”代表当前包)//得到模板对象Template t=config.getTemplate("sample1.ftl");2、创建数据。Map<String,Object> data=new HashMap<>();data.put("site","百度");
    

    data.put(“url”,“http://www.baidu.com”);3、产生输出。t.process(data,new OutputStreamWriter(System.out)(将System.out从字节流转换为字符流));
    在这里插入图片描述
    config.setClassForTemplateLoading(FreemarkerSample1.class,"");的第二个参数的含义是表示加载当前包下的模板,而config.getTemplate()方法表示要加载的模板名称。所以想要加载FreemarkerSample1.class的所在包中的sample2.ftl模板,只需要修改如下代码:config.getTemplate(“sample2.ftl”);就可以获取sample2.ftl的模板对象了。如果模板与加载的类不在一个包下,第二个参数可以直接编写所在路径就可以了。例如:sample2.ftl在com.imooc.freemarker.entity包下,则第二个参数的路径就需要编写为,/com/imooc/freemarker/entity/的路径。具体如下:
    在这里插入图片描述
    FTL取值:
    在这里插入图片描述
    在这里插入图片描述

    freemarker中<#-- -->注释。
    

    对象里map的提取 c o m p u t e r . i n f o [ " c p u " ] 。 因 为 通 过 {computer.info["cpu"]}。 因为通过 computer.info["cpu"]{对象名.属性名}这种方式来获取属性名,所以get方法是必须的,set方法对于获取属性没有影响,但set方法一般也会写上。
    分支判断:1.if分支判断。2.switch分支判断。
    在这里插入图片描述
    在这里插入图片描述

    </#if>作为结束<#if 条件>的闭合。
    

    在这里插入图片描述
    在这里插入图片描述
    ??判断对象是否为空,true不为空,false为空。
    在这里插入图片描述
    字符串的比较用“==”,不是equals()方法。
    使用大于号时要加上用括号。
    在这里插入图片描述
    list迭代列表:
    在这里插入图片描述

    <#list students(被迭代的集合) as(关键字) stu(迭代变量)>
    <li>${stu_index(获取当前循环的索引值,从0开始)}-${stu.name}</li>
    

    在这里插入图片描述
    list迭代Map:
    在这里插入图片描述
    在这里插入图片描述
    LinkedHashMap可以保证数据按存放的顺序进行提取,Map map=new LinkedHashMap();。
    内建函数:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    http://freemarker.foofun.cn是freemarker中文参考手册。
    比如对于1.5,上取整(ceiling)的意思是取大于他的最小整数,也就是2,下取整(floor)是 取小于他的最大整数,也就是1。
    freemarkter的数据、放在List、实体类、json中都是可以的,但在实际的开发中通常后台返回给前端的数据都是key,value的形式,因此实例类以及Map形式应用很广泛一点。

    freemarker三目运算符<#-- 利用?string实现三目运算符的操作 -->${(words?index_of("blood") != -1) ? string("包含敏感词汇","不包含敏感词汇")}。
    //sort_by函数(升序排列)返回新集合
    <#list computers?sort_by("price") as c>${c.sn}-${c.price}</#list>
    //对集合进行逆序
    <#list computers?sort_by("price")?reverse as c>${c.sn}-${c.price}</#list>
    //对集合进行逆序
    <#list computers?reverse('price') as c>${c.sn}-${c.price}</#list>。
    常用内置函数:
    ${name?cap_first}  ${brand?upper_case}	${brand?length}  ${words?replace("blood","*****")}	${words?index_of("blood")}	<#-- 四舍五入 -->${n?round}	<#-- 向下取整 -->
    ${n?floor}	<#-- 向上取整 -->${n?ceiling}。
    公司共有${computers?size}台电脑,第一台:${computers?first.model},最后一台:${computers?last.model}。
    

    Freemarker内建函数写法与java类似,属性名?函数(小括号可有可无)>对象.方法(),Freemarker中函数名由多个单词组成时用”_”分开且都是小写,java中方法名由多个单词组成时第一个单词小写后面每个单词首字母大写。
    Freemarker与Servlet整合:
    Freemarker对Java Web有良好的支持,下载的freemarker.jar包中有一个Servlet是FreemarkerServlet(在freemarker中路径为:freemarker.ext.servlet.FreemarkerServlet),将Freemarker与Servlet整合只需在web.xml文件中配置FreemarkerServlet的映射地址以及.ftl文件在项目中的路径(一般设置为/WEB-INF/ftl)即可。
    按住Ctrl,鼠标移到路径名,路径名下方出现链接横线说明路径名书写正确。
    在这里插入图片描述

    <init-param>是配置初始化参数。TemplatePath是模板路径的意思,是Freemarker的中的设置路径的参数名称,不能修改,这个是固定的参数,直接使用就可以。
    

    ftl文件放在WEB-INF目录下,WEB-INF下的文件是不能通过浏览器直接访问的。web.xml中配置之后,对于ftl结尾的请求,全都会交给FreemarkerServlet处理,它会将TemplatePath模板路径追加到当前这个以ftl结尾的请求路径前,如request.getRequestDispatcher("/employee.ftl").forward(request, response);会正常的去Web根目录的/WEB-INF/ftl下寻找employee.ftl。所以url中不用写这些文件夹。
    init-param和context-param区别?相同:init-param与context-param都是在web.xml里以键值对形式定义的变量。不同:init-param是定义在servlet标签里,而context-param是定义在web-app标签里。
    配置freemarker后,可以将保存数据的集合或列表放在context、session、request的属性里,在 . ftl文件里使用时直接写集合或列表的属性名,freemarker引擎作用域对象的作用域从小到大寻找要迭代的列表。

    jsp等文件和eclipse工具设置的字符集:在jsp中头部中,pageEncoding是jsp文件本身的编码。contentType的charset是指服务器发送给客户端时的内容编码。在<head>标签中,<meta>标签charset属性用于规定HTML文档的字符编码。
    

    eclipse的Text file encoding是Eclipse工作空间编码。这些都需要设置为统一的编码,如都是UTF-8或GBK编码。
    复制完全限定名:选中工程上的java文件,右键选择Copy Qualified Name,复制的是带斜杠的项目工程路径地址。打开java文件,在java文件中,将光标放到类名上,右键选择Copy Qualified Name,复制的是用"."分隔的包名+类名。

    2.4.7 项目:登录注册

    登录注册功能的编写,包括头像上传和验证码等内容。
    内容:
    在这里插入图片描述
    知识点:
    在这里插入图片描述
    MVC设计模式:
    在这里插入图片描述
    Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
    Java Web开发历程:
    在这里插入图片描述
    本项目开发流程(MVC的流程):
    在这里插入图片描述
    用户注册功能的实现:
    用户信息的保存:用户列表的初始化,1.建立用户java类,将所需属性封装起来。2.创建一个监听器,设置全局对象初始化用于保存数据:(1)创建list集合,用于放置用户信息。(2)将list集合保存在ServletContext域中。
    servlet(controller)1. 接收数据。2. 封装数据(调用javaBean)。3. 处理数据(调用javaBean)。4. 作出响应(显示处理结果)。
    用户头像的上传:
    在这里插入图片描述
    文件上传需要post提交,GET请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连,并且提交的数据是有限的,因为它是在地址栏中拼接参数,受浏览器地址栏长度的限制。POST的安全性要比GET的安全性高。POST提交的数据内容更多,也就是没有大小长度限制。
    文件上传原理:
    没有设置enctype属性的情况:
    在这里插入图片描述
    设置了enctype属性的情况:
    在这里插入图片描述
    以下两个包主要是用来进行文件上传的:
    在这里插入图片描述
    在jsp页面写jstl和el表达式:
    在这里插入图片描述
    生成验证码使用的jar:
    在这里插入图片描述
    commons-fileupload和commons-io的jar包可以参考Apache官网进行下载。kaptcha的jar包,可以参考官网地址:http://code.google.com/p/kaptcha/ 但国外的网站,国内访问不到。
    前端一些模板参考:https://blog.csdn.net/sinat_40770656/article/details/95058150。
    如果from表单没有设置enctype=“multipart/form-data"这个属性,那么上传的只是文件的名字,没有文件的内容,加上属性后才能上传文件中的内容(firefox浏览器能解析看到,chrome浏览器不解析看不到)。
    在这里插入图片描述
    smartUpload是第一代jsp+javabean的设计模式经常使用的,一般嵌入到jsp中。
    在form表单添加enctype=“multipart/form-data"属性后,表单中的信息都会以二进制的方式传递。因此通过request是没有办法获取其它参数的,而FileUpload正好可以解决这一问题。
    在这里插入图片描述
    在这里插入图片描述
    虚拟路径的意思是,并不是磁盘中的真实路径。getRealPath(”/upload”)是获取这个项目的根目录下这个upload文件夹在磁盘中的真实路径。项目发布后,WebContent下就是项目的根路径。项目发布后是没有WebContent这个文件夹的,代表的就是根路径。
    从输入流中读取数据并写入文件。byte[] b=new byte[1024];定义一个byte数组。is.read(b)是从输入流中读取数据存放到b数组中。while((len=is.read(b))!=-1)判断是否读取完成,如果没有,则循环读取。os.write(b, 0, len)从0开始,从b字节数组写入len字节到该文件输出流。read每次读取完成后会清空数组。len = is.read(b)做到了边读取,边把这次读取的字节赋值给len,前几次循环时数组被写满,所以len=1024,当读到流的末尾时,数组没有被写满,len就是剩余的字节数。
    idea支持项目的热部署,也就是在Tomcat启动后,改变jsp或html等信息后,不需要再次进行服务器的关闭重启,直接update即可,更加适合开发。
    如果使用的是IDEA需要手动在upload中先添加一个文件。项目部署到tomcat后到编译后到target文件夹中可以看到对应到图片文件。idea的文件不能为空,空文件夹不会被编译。
    String path=request.getServletContext().getRealPath("/upload");和String path=getServletContext().getRealPath("/upload");是一样的。request.getServletContext()是调用HttpServletRequest类中的getServletContext方法。Servlet类继承了HttpServlet,HttpServlet继承了GenericServlet,所以在Servlet类中直接调用了getServletContext方法。
    解决上传文件重名的问题:
    在这里插入图片描述
    UUID.randomUUID().toString()生成随机字符串。
    写为静态方法可以在使用时,直接通过类名就可以直接调用,不需要进行实例化,例如:
    在这里插入图片描述
    用户登录功能的实现:
    用户信息的查询:
    更改包的显示方式:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    登录servlet类。
    在这里插入图片描述
    header.jsp显示用户登录成功/失败的页面。
    验证码的生成和校验:
    验证码生成的流程:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    Graphics类提供了基本的几何图形绘制方法,主要有:画线段、画矩形、画圆、画带颜色的图形、画椭圆、画圆弧、画多边形、画字符串等
    在绘制任意图形之前都需要直接指定绘制时使用的颜色。
    在这里插入图片描述
    在这里插入图片描述
    Graphics2D类ratate()方法旋转图片,第一个参数是弧度。rotate()方法使用的特点,第二次进行旋转时,会在之前旋转的基础上进行。
    Graphics类中的drawLine(int x1, int y1, int x2, int y2) 方法是用来画线的。两点确定一条直线,而在坐标中由横(X)、纵坐标(y)确定一个点,这四参数实际就是确定两个点,是要画的直线的起始点横纵坐标和终点的横纵坐标。X1,Y1是确定直线的起始点,即横坐标为x1,纵坐标为y1的点。同理x2,y2确定直线的终点。例A(x1,y1) B(x2,y2) 就可以画出直线AB了。
    验证码的代码实现:在servlet中生成验证码,通过BufferedImage 在内存中生成一张图片,new BufferedImage(宽,高,类型(RGB));绘制背景色和边框,需要一个画笔通过BufferedImage.getGraphics() 获得一个画笔对象Graphics. 通过setColor(Color);设置背景色然后进行填充fillReact(0,0,width,height);
    ImageIO.writer(xx,“jpg”,response.getOutputStream()); xx是图片对象,response.getOutputStream()是在浏览器上显示的输出流。
    在CheckImgServlet中ImageIO.write(); write方法的含义是以响应对象的输出流将内存中的图片以jpg的形式输出。此时它输出的是一个具体的图片,所以在页面中通过访问${pageContext.request.contextPath }/CheckImgServlet就可以得到这个图片。
    fillRect方法是填充指定的矩形。drawRect方法是绘制指定矩形的边框。
    实现点击刷新图片(验证码),就是从后台重新获取图片,在点击的时候浏览器会优先获取缓存中的数据,所以要让每次的url不一样才能实现点击刷新,只需要在url后面加一个参数,可以是以时间戳为参数。new Date().getTime();(这是js里面获得系统当前时间的毫秒值)。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    return是表示结束方法的运行,使用return就不会再执行下面的语句了。这里的转发只是对请求的转发,按照代码的执行循序,如果没有return,代码会根据顺序依次向下执行(因为request和response是对应的,直到代码块执行完或执行到response),而当验证码不正确时,用户名密码的判断不需要再执行,所以可以直接添加return结束当前方法。
    假设不加code2null,如果code2为null,直接执行后面的code2.equalsIgnoreCase(code1),由于code2为null,所以会报空指针异常。所以前面添加了code2null,这里利用了||的短路性质,如果code2==null则整个表达式为true,因此不会在执行后面的code2.equalsIgnoreCase(code1)了。也就避免了空指针问题。
    Kaptcha验证码的使用(第三方验证码生成的工具):
    Web.xml文件中配置Kaptcha第三方工具包提供的KaptchaServlet的映射地址、参数等。
    在这里插入图片描述
    在这里插入图片描述
    Kaptcha验证码配置参数的介绍:Conifg.class–>Comstans.class中选择需要配置的参数,在Web.xml中进行参数的配置。.class文件需要关联Kaptcha的source.jar才能看代码。
    Kaptcha会在初始化时读取web.xml中配置的初始化参数,所以可以通过配置参数改变this.sessionKeyValue的值。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    Kaptcha验证码的校验:
    在这里插入图片描述
    在这里插入图片描述
    输错验证码后不让输入的用户名和密码重新输入,用局部刷新,通过form表单提交无法实现。需要使用ajax。思路如下:ajax发送请求给后台。后台进行验证,然后向前台返回信息。ajax根据返回的信息。决定做什么操作。如果验证失败。调用changeImg(),改变验证码。如果验证成功跳转至登录成功页面。
    kaptcha的session的key也可以在web.xml的init-param中来修改。不修改就直接使用默认的com.google.code.kaptcha.Contans.KAPTCHA_SESSION_KEY。
    项目中一共有如下几个包:
    在这里插入图片描述
    1、com.imooc.domain的作用是存放项目的实体类。2、com.imooc.service的作用是存放项目的业务层代码的接口。3、com.imooc.service.impl的作用是存放项目的业务层代码的实现类。
    4、com.imooc.utils的作用是存放项目的工具类。5、com.imooc.web.listener的作用是存放项目的监听器的类。6、com.imooc.web.servlet的作用是存放项目的Servlet类,用于后台控制的一些类就放在这里。
    HttpServletRequest是一个接口,在该接口中有getContextPath方法,EL表达式${pageContext.request.contextPath}在底层实质上是调用对应get方法完成的。
    “/”是绝对路径,在web项目中绝对路径是相对于http://localhost:8080,所以前面会自动拼接 http://localhost:8080。
    在form表单中,写一个οnsubmit="return validate form()用于检验用户名密码重置密码的函数。
    疫情实时动态,里面的大量数据的需获取:数据获取一般有三种方式:1 , 手动录入,一般用于比较少的数据 。2 , 从网站爬取或者是授权可用的。3 , 调用第三方接口,一般会专门有供应商做好接口提供给其他网站使用。实时更新一般都是定时发送请求更新数据。
    接口定义的是一系列规范,是java多态的一种形式,在开发过程中可以快速分离工作内容,增强了程序的层次感。同一个接口可以有多个实现类,实现类的不同,其要执行的操作也不同。一个类可以实现多个接口,如Service接口中可以定义service层中常用的增删改查方法,其他service实现类都可以实现该接口,避免了代码冗余。
    jar包是类的归档文件,里面可以封装类库,也就是把很多类封装到jar包中,其他类可以使用。而war包是Java web程序打的包。关于jar包的使用,此案例已经使用jar包了,比如支持文件上传的jar包,里面有很多的类。项目使用jar包的时候,只要引入jar包,然后使用import语句导入类就可以了,不用新建项目。Java web程序除了tomcat去部署,也可以通过其他服务器去部署,比如WebLogic,不过tomcat比较常用。java web的开发主要通过eclipse和IntelliJ IDEA等开发工具完成,一个一个文件建立是比较原始的方式,开发效率低。
    Java框架就是一些类和接口的集合,通过这些类和接口协调来完成一系列的程序实现。框架又叫做开发中的半成品,它不能提供整个WEB应用程序的所有东西,但是有了框架,我们就可以集中精力进行业务逻辑的开发而不用去关心它的技术实现以及一些辅助的业务逻辑。说白了Java框架就是封装好方便程序员操作的类,使项目的开发更简单,维护起来也更容易。
    pageContext对象 这个对象代表页面上下文,该对象主要用于访问JSP之间的共享数据。使用pageContext可以访问page、request、session、application范围的变量。
    jsp九大内置对象和EL表达式十一大内置对象:
    在这里插入图片描述

    并且pageContext:可以获取JSP九大内置对象,相当于使用该对象调用getxxx()方法,因此${pageContext.request.contextPath }的意思就是:pageContext.getRequest().getContextPath ();
    el表达式${pageContext.request.contextPath }中的pageContext是el内置对象,代表该页面的pageContext对象,不是jsp内置对象,pageContext开头el表达式有快捷提示,其他的el表达式就没有,是因为EL表达式其他内置对象如pageScope,requestScope是从作用域中获取值的,由于不知道往这些作用域中放的键是什么,所以就没有提示。
    1,requestScope是获取request域属性值,而pageContext对象可以获取JSP九大内置对象。
    2,pageContext.request获取的是HttpServletRequest对象,而requestScope只是一个概念, 表示request的作用域,是EL的内置对象之一,只有放了才能取,所以通过requestScope获取的数据,必须在之前已经通过set方法set进了request才能获取。
    3,${pageContext.request.contextPath}意思就是取出部署的应用程序名或者是当前的项目名称。
    requestScope只是用于获取request范围内的属性值,所以${requestScope.contextPath}获取不到上下文路径。
    Servlet类请求转发到HTML/JSP页面,1.如果是ajax请求返回的可以直接在js中使用属性名.属性值 这样的方式。2.如果是直接返回的页面。通常使用jstl+el表达式处理。或者直接使用<%%>java 代码块。3.直接返回页面不会使用js操作。使用 2 中的方法操作数据很简单。
    java.util包提供了java中的基础工具类。包括基本的集合框架等基础类以及通用的工具类。java.awt是一个软件包,包含用于创建用户界面和绘制图形图像的所有分类。javaweb的开发中几乎不会用到这个包。
    

    java中常用的包有很多,此处列举一些:
    在这里插入图片描述
    小结及FAQ:

    Java过滤器常用场景。其中三个选项课程中都有提到,权限访问控制,压缩响应信息,字符编码处理为过滤器的常用场景。
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>,prefix属性的值是可以改变的,可用c、d、e等,只不过我们一般习惯用c表示。
    JSTL常用标签的认识。与Java中的for循环对应的是<c:forEach>标签,jstl中没有<c:for>标签;<c:if>与Java的if语句是对应的,<c:when>对应的是switch语句中的case语句。
    

    初始化参数的获取和设置:在初始化方法中应该是先获取初识化参数,即使用getInitParameter方法,随后是设置属性setAttribute方法,在销毁方法中应该是获取属性而非初始化参数了,即使用getAttribute方法。
    Servlet将后台数据对象object,回写给dataType是json的Ajax。Servlet回写数据时的核心代码:json=JSON.toJSONString(object);:输出的数据需要转换为JSON。response.setContentType(“text/html;charset=UTF-8”);:在回写时需要设置写回的数据的输出格式。以防中文乱码问题。response.getWriter().println(json);:需要从Response中获取输出流,将需要输出的数据输出。

    task2.4.7图书系统管理问题:
    ①为了便于web.xml中登录过滤器对Servlet过滤(映射地址书写/servlet/*),给所有Servlet的映射地址如loginServlet改为/servlet/login后,Servlet处理请求响应转发到jsp,发现所有jsp文件路径都隐性在/servlet目录下,因此所有jsp页面对资源的引用和地址的跳转的相对路径写法都需要通过…/跳到上级目\n录,如css引用,js引用,图片引用,超链接等,为什么?明明工程文件目录结构就不需要跳到上级目录
    答:这里不是用的工程文件目录,而是发布后中的文件目录。例如如果访问路径是localhot:8080/servlet/addCategory,而css,js文件等是在locahost:8080/目录下,这时如果想正确访问,应该使用…/返回上一级目录。
    这里建议使用绝对路径加载css,js等,在路径前加上${pageContext.request.contextPath }。
    ②Context root bug :eclipse开发环境下设置应用context root为/,过滤器映射地址为/,eclipse版本 2020-03 (4.15.0),tomcat8.5,jdk1.8.0_231。具体问题:
    context root设置为/,过滤器设置/,问题1:项目启动后,为什么ContextListener监听器contextInitialized()方法会执行两次?方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())第一次打印contextPath:,第二次打印contextPath:“项目名(eg:test)”。问题2:项目启动后浏览器没有请求,为什么过滤器中的doFilter(…)方法会执行一次?问题3:过滤器只对Servlet生效是只会拦截映射地址为/的Servlet类(一个项目映射地址为/的Servlet类只有一个)还是拦截其他所有映射地址为/xxServlet的Servlet类?
    问题1原因;jdk、tomcat等运行环境和各种版本等开发环境问题,正常开发环境在项目启动后ContextListener监听器contextInitialized()方法只会执行一次,所以方法中的syso(sce.getServletContext().getContextPath())只会输出打印一次正确的contextPath。问题2原因:eclipse开发工具原因,idea开发工具不会出现此问题。问题3原因:从知识点上context root设置/,过滤器映射地址设置/,过滤器只对Servlet生效的意思是此过滤器只会拦截映射地址为/的Servlet类(一个项目映射地址为/的Servlet类只有一个),而不能拦截其他映射地址为/xxServlet的Servlet类?
    如果使用eclipse应该经常遇到问题2吧,是实际开发中eclipse应用很少吗?都采用idea等其他开发工具?
    Tomcat 启动两次的问题(ServletContextListener的contextInitialized被执行两次的问题):相关博客回答https://blog.csdn.net/shaokai132333/article/details/53328258。
    它的解释是放到Tomcat的webapps中的Web应用被启动了两次,自己理解这个启动两次是针对项目的context root设置/,在项目发布到tomcat后,会执行一次项目,然后为了达到在浏览器url不输入项目名就访问项目的目的,会再访问在Tomcat的server.xml文件中的Context标签中的docBase属性值,一般就是项目的绝对路径,将这个项目的绝对路径作为根(访问ip可直接访问该应用),path属性是context root设置的context path上下文路径。web应用执行两次这样好像就解释了问题1和问题2,对于问题1:contextInitialized()方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())执行两次,tomcat服务器启动后,web应用第一次启动,contextInitialized()方法中syso(“contextPath:”+sce.getServletContext().getContextPath())打印的contextPath为空;为了访问ip可直接访问该应用根据tomcat的server.xml配置就将web应用名做为根再次启动web应用,web应用第二次启动,syso打印contextPath为项目名了。对于问题2:因为web应用启动两次,第二次将webapps中的web应用名做为根再次启动web应用会被过滤器拦截,所以doFilter()在项目启动后执行一次。
    部署到webapps的项目出现上述问题可能的两种原因是:第一种原因:部署到webapps的项目的contextPath不会被改变,始终是项目名作为根路径,所以当更改项目的contextPath不为/项目名时,为将这个项目名作为根(访问ip可直接访问该应用),tomcat服务器中会有两个内容相同而url不同(一个含/或/其他用户命名的名,另一个含/项目名)项目,所以会生成两个ServletContext对象(所以ContextListener监听器contextInitialized()方法会执行两次,方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())第一次打印contextPath:,第二次打印contextPath:“项目名(eg:test)”。),为将这个项目名作为根(访问ip可直接访问该应用),第二次url含/项目名的应用部署相当于访问第一次应用部署的url(所以只有一个项目的服务器启动后浏览器没有请求,过滤器中的doFilter(…)方法会执行一次。)。第二种原因:部署到webapps的项目的contextPath不会被改变,始终是项目名作为根路径,所以当更改项目的contextPath不为/项目名时,为将这个项目名作为根(访问ip可直接访问该应用),tomcat服务器中会有两个内容相同而url不同(一个含/或/其他用户命名的名,另一个含/项目名)项目,所以会生成两个ServletContext对象(所以ContextListener监听器contextInitialized()方法会执行两次?方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())第一次打印contextPath:,第二次打印contextPath:“项目名(eg:test)”。),由于eclipse开发工具的bug导致只有一个项目的服务器启动后浏览器没有请求,过滤器中的doFilter(…)方法会执行一次。
    部署到webapps同级的其他目录:当把tomcat 的deploy path改为与webapps同级的其他目录后,出现ContextListener监听器contextInitialized()方法执行一次,方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())打印context root设置的值,浏览器没有请求,过滤器中的doFilter(…)方法会执行一次。出现上述问题可能的两种原因:第一种原因:部署到与webapps同级的其他目录的项目的contextPath会被改变,所以当更改项目的contextPath不为/项目名时,为将这个项目名作为根(访问ip可直接访问该应用),tomcat服务器中会有两个内容相同并且url相同(都是context root设置值)项目,所以只会生成一个ServletContext对象(所以ContextListener监听器contextInitialized()方法只会执行一次,方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())打印context root设置值。),为将这个项目名作为根(访问ip可直接访问该应用),第二次应用部署相当于访问第一次应用部署的url(所以只有一个项目的服务器启动后浏览器没有请求,过滤器中的doFilter(…)方法会执行一次。)。第二种原因:部署到与webapps同级的其他目录的项目contextPath会被改变,所以当更改项目的contextPath是否为/项目名时,tomcat服务器中只有一个项目,所以只会生成一个ServletContext对象(所以ContextListener监听器contextInitialized()方法只会执行一次,方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())打印context root设置值。),由于eclipse开发工具的bug导致只有一个项目的服务器启动后浏览器没有请求,过滤器中的doFilter(…)方法会执行一次。
    总结最可能的原因:部署到webapps的项目的contextPath不会被改变,始终是项目名作为根路径,所以当更改项目的contextPath不为/项目名时,为将这个项目名作为根(访问ip可直接访问该应用),tomcat服务器中会有两个内容相同而url不同(一个含/或/其他用户命名的名,另一个含/项目名)项目,所以会生成两个ServletContext对象(所以ContextListener监听器contextInitialized()方法会执行两次,方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())第一次打印contextPath:,第二次打印contextPath:“项目名(eg:test)”。),由于eclipse开发工具的bug导致只有一个项目的服务器启动后浏览器没有请求,过滤器中的doFilter(…)方法会执行一次。部署到与webapps同级的其他目录的项目contextPath会被改变,所以当更改项目的contextPath是否为/项目名时,tomcat服务器中只有一个项目,所以只会生成一个ServletContext对象(所以ContextListener监听器contextInitialized()方法只会执行一次,方法中的syso(“contextPath:”+sce.getServletContext().getContextPath())打印context root设置值。),由于eclipse开发工具的bug导致只有一个项目的服务器启动后浏览器没有请求,过滤器中的doFilter(…)方法会执行一次。
    问题2最可能的原因:在启动项目后,就会访问localhost:8080/项目的context root,一般是项目名/,所以启动项目后请求被映射地址为/的LoginFilter过滤器拦截就会直接执行LoginFilter的doFilter()方法。

    展开全文
  • 解决VSCode无法显示Unity代码提示的真正解决办法

    万次阅读 多人点赞 2020-11-26 06:23:16
    解决VSCode无法显示Unity代码提示的真正解决办法前言不提示原因尝试解决问题步骤1:验证是否正确打开工程目录步骤2:验证工程是否指定正确步骤3:检查和安装对应的.NET开发者版本自助排除故障 前言 作为一个程序猿最...

    前言

    作为一个程序猿最头大的问题大概就是重装系统了…但偏偏时间久了项目一多,Python,Java,DotNet,AndroidSDK,Qt等等一系列环境版本配置的乱七八糟,导致出各种无头绪的报错…崩溃!

    这里记录个我是如何在重装系统后正确解决VSCode代码提示问题的。

    不提示原因

    其实在网上搜罗了一圈以后,发现无非也就是几种情况会导致没有代码提示:

    1. 没有使用VSCode打开工程目录
    2. 没有指定正确的sln工程配置文件
    3. 系统中没有安装对应的.NET开发者版本(大多数人遇到的问题)
    4. Package Manager的VScode插件版本不对

    这个问题似乎纠结了许多网友,给出的解决方式要么看命要么根本不对症。在此整理一下终极解决方式以及我探索出的实际解决方法。

    尝试解决问题

    步骤1:验证是否正确打开工程目录

    使用vscode 写unity文件无法自动提示代码(新手向错误)
    如果你只打开的单独的cs文件而工程目录没有正确的跟进去,VSCode是找不到Unity的Library文件夹的。
    在这里插入图片描述
    你可以查看VSCode左侧的资源管理器,查看列表当中是否有sln文件。如果有继续往下看。

    步骤2:验证工程是否指定正确

    VSCode无法进行Unity C#智能提示,代码补全以及方法跳转等功能的解决方案!
    在这里插入图片描述
    按下键盘的Ctrl/Command + Shift+ P快捷键,选定工程目录下的sln文件。
    至此,如果你以前是有代码提示的,稍等几秒等待自动加载完成以后你可以重试一下是否有代码提示功能。

    步骤3:检查和安装对应的.NET开发者版本

    1. 用VSCode打开你工程目录下的.csproj文件。
    2. 找到Project->PropertyGroup->TargetFrameworkVersion这条数据,如下图:在这里插入图片描述
    3. 记住你的版本号,到这下载对应版本号的开发者版 .NET Framework 。
      https://docs.microsoft.com/zh-cn/dotnet/framework/deployment/deployment-guide-for-developers
      这里我看到有的帖子说要改这个版本号,其实不然。如果你改了下次你创建新工程的时候还需要如此操作,且这种方法不见得就能够生效。在这里插入图片描述
    4. 以我的为例,我的是4.7.1,那么就戳这个连接进去,跳到下载页面。
      在这里插入图片描述
    5. 注意你需要下载Developer版本的,如果是Runtime版本很可能安装的时候会提示你已经安装了当前或更高版本的.NET从而拒绝你安装。当然稳妥方式都装一遍…
    6. 装完以后,重开VSCode。你期待的代码提示就会出现了!

    代码提示有了,但找不到UnityEngine.UI等类

    这个问题很好解决,我这里用的是2019.3.11f1版本。原因是这个版本的UI库文件挪位置了,而Unity新推出的Unity Package Manager插件没跟上步伐导致的…官方的坑!

    步骤1:点开Unity的 Package Manager,找到右Visual Studio Code Editor,选择1.2.0以后的版本,然后点击右下角的升级按钮。(注意如果你用的2019,插件不要用太新的版本.否则还是会出现.不出来的情况.)在这里插入图片描述
    步骤2:保存并关闭Unity和VSCode,到工程目录下删除所有的*.sln和*.csproj文件。
    步骤3:重新打开Unity和这个工程,刚被删除的这两个文件将会被重新创建,届时这个故障就解决了。
    如果你不想重启unity和vscode参照这个帖子https://blog.csdn.net/weixin_45476117/article/details/108911678

    gameobject什么的只能.出一个智能引用

    这里还是Unity Package Manager的插件惹的祸,我这里用1.2.0可以但是升级到1.2.3就出现这个问题.建议降级到低版本尝试.
    在这里插入图片描述

    其他相关

    利用控制台定位问题

    如果你发现按照上面一波操作以后问题依旧没有解决,这时候就需要你自己寻找故障所在了。这里提供一个方法来帮助你快速准确的找到自己的故障点所在。

    1.留意你的VSCode控制台输出面板,通常在这里会弹出启动报错信息。仔细阅读输出的报错信息,大部分错误都会有告诉你解决问题的方式。看不懂的话取去翻译一下根据步骤来,不要盲目相信神论,找到问题的根本才是解决BUG的真谛…在这里插入图片描述
    由于我这里已经解决好了环境问题,所以没有提示出找不到Unity类等错误信息,左侧的大纲也能够正常显示出来了。如果你的输出控制台有一大堆报错也不用慌,其实找到关键问题就都迎刃而解了。

    推荐撸Unity用的VScode插件

    这是VSCode官方推荐的几个社区插件,这里复制过来安利一下
    引用地址: https://code.visualstudio.com/docs/other/unity

    C#
    这个几乎是必装的插件,用于支持C#的一些日常sao操作.

    • .NET Core的轻量级开发工具。
    • .强大的C#编辑支持,包括语法突出显示,IntelliSense,转到定义,查找所有引用等。
    • 对.NET Core(CoreCLR)的调试支持。注意:不支持Mono调试。桌面CLR调试的支持有限。
    • .在Windows,macOS和Linux上支持project.json和csproj项目。

    Unity Code Snippets
    神器一件,这个工具可以帮助你快速的撸出OnTriggerEnter() OnApplicationQuit() 等等的代码片段,几乎全覆盖,去官网看下在这里插入图片描述

    感谢观看,好使了别忘一键三连哦~

    展开全文
  • 自:vscode配置C/C++环境 - 知乎 VS Code配置 作者:谭九鼎 链接:Visual Studio Code 如何编写运行 C、C++ 程序? - 知乎 有改动。个人按照步骤后,做到复制上三个json那一步,就可以运行了。 我将settings....
  • 这其中webpack发挥了很大的作用,它使得我们的代码模块化,引入一些插件帮我们完善功能可以将文件打包压缩,图片base64等。后期对项目的配置使得我们对于脚手架自动生成的代码的理解更为重要,接下来我将基于...
  • css3动画的优点:在性能上会稍微一些,浏览器会对css3的动画做一些优化,代码相对简单;css3动画的缺点:在动画控制上不够灵活,兼容性不好,部分动画功能无法实现。 8.实现某div元素以每秒50px的速度左移100px $...
  • 但把数据量小且访问频繁的消息数据直接存储到 etcd 中也是个不错的选择,如:业务系统中常用的二级代码表(在表中存储代码,在 etcd 中存储代码所代表的具体含义,业务系统调用查表的过程,就需要查找表中代码的含义...
  • 最近在做对接美国的EasyPost快递平台时,发现使用Objects.isNull()判断EasyPost返回的序列化之后的空实体(JSON体为:{})时,返回结果并不是false;然后情不自禁就自己写了个使用反射判断Java对象是否为“NULL”的...
  • 本文需要下面的知识:https://zhuanlan.zhihu.com/p/260811233​zhuanlan.zhihu.com问题描述我最近的一个功能需求是通过axios获取我存储在COSjson文件,同时渲染到页面上。而问题就是,axios可以成功获取数据,...
  • 要在Java开发使用Redis中间件,必须对Jedis熟悉才能写成漂亮的代码。 通过导依赖后使用Jedis对象调用方法完成操作Redis. 参考:https://www.jianshu.com/p/a1038eed6d44 使用步骤 导依赖 <dependency> <groupId>...
  • Vex – 可以在虚拟环境执行命令。 virtualenv – 创建独立 Python 环境的工具。 virtualenvwrapper- virtualenv 的一组扩展。 包管理 管理包和依赖的工具。 pip – Python 包和依赖关系管理工具。 pip-tools – ...
  • awesome-python(python集合框架)

    千次阅读 2021-08-17 13:19:20
    今天我们就介绍这个系列的Python:awesome-python,它是由 vinta 发起维护的 Python 资源列表。 awesome-python 包括了Web 框架、网络爬虫、网络内容提取、模板引擎、数据库、数据可视化、图片处理、文本处理、...
  • 2.1.2 什么是Web服务器 Web服务器是运行及发布Web应用的容器,只有将开发的Web项目放置到该容器,才能使网络的所有用户通过浏览器进行访问。 2.2 常见服务器 开源:OpenSource(1、开放源代码 2、免费) Tomcat...
  • 分布式架构就是将传统结构按照模块进行拆分,不同的人负责不同的模块,不会产生代码冲突问题,方便开发。 什么是SOA架构 SOA架构就是将业务逻辑层提取出来,将相似的业务逻辑形成一个服务,提供外部访问接口,服务...
  • 这篇博文主要是讲解在async/await使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁。内容主要是从作者Stephen Cleary的两篇博文翻译过来. 原文1:Don'tBlock on Async Code 原文2:why the ...
  • [译]async/await使用阻塞式代码导致死锁 原文:[译]async/await使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁。内容主要是从...
  • 官网Vex:可以在虚拟环境执行命令。官网virtualenv:创建独立 Python 环境的工具。官网virtualenvwrapper:virtualenv 的一组扩展。官网buildout:在隔离环境初始化后使用声明性配置管理。官网包管理管理包和依...
  • 我喜欢SQL查询语言,SQL schema是无趣技术的完美示例,我建议在 99% 项目使用它从数据发掘真相:项目代码不完美,而如果你的数据库是结构化 schema 存储的,就可以相对轻松地进行改造。反言之,如果数据库数据...
  • 1000+常用Python库一览

    2021-03-27 00:13:22
    通过pickle模块的序列化操作我们能够将程序运行的对象信息保存到文件去,永久存储;通过pickle模块的反序列化操作,我们能够从文件创建上一次程序保存的对象。 cPickle是[C语言]实现的版本,速度更快。 ...
  • 1、针对flask框架的web项目 ... css文件还有静态资源(例如图片...应用程序根目录是根据初始化app=Flask(__name__)的时候的代码在哪就决定了哪里是根目录 更改flask的默认设置静态资源位置: static_folder app...
  • python常用代码大全-Python常用库大全

    千次阅读 2020-10-28 21:25:17
    Python常用库大全,看看有没有你需要的。...Vex – 可以在虚拟环境执行命令。virtualenv – 创建独立 Python 环境的工具。virtualenvwrapper- virtualenv 的一组扩展。包管理管理包和依赖的工具。pip ...
  • 我们都知道,SpringCloud是微服务的一站式解决方案,是众多组件的集合,而因为SpringCloud几乎所有的组件使用的都是Netflix公司的产品,其中大部分已经进入了停止更新或者维护阶段。我们需要一些别的组件来代替...
  • python代码库-Python常用库大全

    千次阅读 2020-10-29 23:20:33
    Python常用库大全,看看有没有你需要的。...Vex – 可以在虚拟环境执行命令。virtualenv – 创建独立 Python 环境的工具。virtualenvwrapper- virtualenv 的一组扩展。包管理管理包和依赖的工具。pip ...
  • 10万字208道Java经典面试题总结(附答案)

    万次阅读 多人点赞 2021-08-01 16:05:55
    JDK包含JRE,JDK有一个名为jre的目录,里面包含两个文件夹bin和lib,bin就是JVM,lib就是JVM工作所需要的类库。 2、== 和 equals 的区别是什么? 对于基本类型,==比较的是值; 对于引用类型,==比较的是地址...
  • 用express作登录验证,在代码里打印req.body为空对象,通过解决这个问题对http服务,数据提交到服务器,以及工具的使用有了更加深刻的认识。 出现阻塞的原因 对客户端和服务端通信没有深层次的理解 初次工具...
  • } } 编写配置(application.yml) controller-UserController mapper-UserMapper pojo-User servcieUserService 启动测试 存在的问题 在consumer,我们把url地址硬编码到了代码中,不方便后期维护 consumer需要记忆...
  • [译]async/await使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁。内容主要是从作者Stephen Cleary的两篇博文翻译过来. 原文1:Don'...
  • /** *执行 main方法控制台输入模块表名回车自动生成对应项目目录 * * @author xiao pan * @since 1.0.0 */ public class CodeGenerator { public static void main(String[] args) { // String[] tables = new ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,650
精华内容 1,460
关键字:

json转对象控制台好使,代码中失效

友情链接: List.rar