目录
- SpringCloud
- JWT 是什么? 加密和签名?对称加密和非对称加密的区别?JWT 的优缺点?
- synchronized 锁升级
- CAS存在的问题
- 分布式:session 存在的问题?
- Redis 事务和 MySQL 事务的区别?
- Redis 实现分布式锁?
- TCP 协议?协议中的状态(TIME_WAIT 等)?TIME_WAIT 为什么要 2MSL 时间?
- Mac地址和IP地址的区别?
- OSI七层模型和TCP四层模型,各层之间数据包如何发送?
- 网关的作用是什么?
- Mysql索引失效的情况?
- Spring模块
- Spring 事务
- 反射对单例模式的破坏
- 枚举可以防止反射破坏单例
- MySQL 三大范式
- docker
- jar 包和 war 包的区别
- jar 包占用内存过大如何排查
- 自定义注解如何实现
- SQL 注入及防止
- Redis 与数据库的 一致性
- OutOfMemory 解决方法
- Spring Bean的完整生命周期
- Bean的简单生命周期
- Spring Bean的作用域
- Spring Bean的注入
- Spring 中的 单例 Bean是线程安全吗
- BeanFactory接口和ApplicationContext接口区别
- Spring IOC初始化流程
- IOC 中的设计模式
- Spring AOP的调用过程
- Spring IOC自动装配
- MyBatis 逻辑分页,物理分页
- MyBatis 多种分页方式(5种)
- MyBatis 的批量处理
- Spring中的设计模式
- 七大设计原则
- case when 使用
- linux下用户组、文件权限
SpringCloud
Spring cloud是一个基于Spring Boot实现的服务治理工具包,在微服务架构中用于管理和协调服务的。
常问组件:
Eureka:负责服务的注册与实现。各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里。
Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台。
Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求。
Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
Zuul:如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务。
springcloud
优点:
1、服务拆分粒度更细,有利于资源重复利用,有利于提高开发效率
2、可以更精准的制定优化服务方案,提高系统的可维护性
3、微服务架构采用去中心化思想,服务之间采用Restful等轻量级通讯,比ESB更轻量
4、适于互联网时代,产品迭代周期更短
缺点:
1、微服务过多,治理成本高,不利于维护系统
2、分布式系统开发的成本高(容错,分布式事务等)对团队挑战大
JWT 是什么? 加密和签名?对称加密和非对称加密的区别?JWT 的优缺点?
JWT:跨域身份验证解决方案。
- jwt验证方式是将用户信息通过加密生成token,每次请求服务端只需要使用保存的密钥验证token的正确性,不用再保存任何session数据了,进而服务端变得无状态,容易实现拓展。(这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。 简称JWT,在HTTP通信过程中,进行身份认证。)
- 一个token分为3部分:
- 头部(header):JWT的头部分是一个JSON对象,描述元数据
- 载荷(payload):用来存放实际需要传递的数据信息,也是一个JSON对象。
- 签名(signature):对前两部分(头部,载荷)的签名,防止数据篡改。
- JWT 使用
1、服务端根据用户登录状态,将用户信息加密到token中,返给客户端
2、客户端收到服务端返回的token,存储在cookie中
3、客户端和服务端每次通信都带上token,可以放在http请求头信息中,如:Authorization字段里面
4、服务端解密token,验证内容,完成相应逻辑
加密与签名
-
token应用:
-
避免用户多次输入密码
-
实现自动登陆
-
避免在终端直接存储用户的密码
-
标示客户端的请求是否合法
-
-
加密:加密的主要作用就是避免明文传输,就算被截获报文,截获方也不知道报文的具体内容
- 在客户端与服务器进行交互时,必然涉及到交互的报文(或者通俗的讲,请求数据与返回数据),如果不希望报文进行明文传输,则需要进行报文的加密与解密
- 用的比较多的加密算法主要是对称加密中的 3DES 加密与非对称加密中的 RSA
-
签名:
-
在客户端与服务器进行交互时,报文虽然加密了,但我们并不能确认这个报文是谁发过来的。例如,与第三方服务器B进行交互时,我方收到了一个已加密的请求,但我方并不能确认是服务器B发送的这个报文,此时我们可以用数字签名的方式来进行验证。作用:认证数据来源
-
如果我方收到一个B服务器签名的请求,那么B服务器也无法否认这个请求,因为带有它的签名,作用:抗否认性
-
我方收到一个B服务器签名的请求,但我方并不能确认这个请求是否被篡改过(虽然报文加了密,也可能被篡改),此时即可用签名,验证签名中的报文与传过来的报文是否一致。作用:保证了数据的完整性
-
平时工作中用的最多的签名算法是:RSA。
- 使用加解密为了保证数据的安全性,公钥加密私钥解密;私钥加密公钥解密。
- 签名机制是为了保证数据通信的可靠性,私钥签名公钥验签。
对称加密和非对称加密的区别
-
对称加密
对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥加密
也就是密钥也可以用作解密密钥,这种方法在密码学中叫做对称加密算法,对称加密算法使用起来简单快捷,密钥较短,且破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,而且对计算机功能要求也没有那么高 -
非对称加密
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。甲方只能用其专用密钥解密由其公用密钥加密后的任何信息。
-
对称加密解密的速度比较快,适合数据比较长时的使用。非对称加密和解密花费的时间长、速度相对较慢,只适合对少量数据的使用。
-
传输的安全性不同
对称加密的过程中无法确保密钥被安全传递,密文在传输过程中是可能被第三方截获的,如果密码本也被第三方截获,则传输的密码信息将被第三方破获,安全性相对较低。
非对称加密算法中私钥是基于不同的算法生成不同的随机数,私钥通过一定的加密算法推导出公钥,但私钥到公钥的推导过程是单向的,也就是说公钥无法反推导出私钥。所以安全性较高。
JWT 优缺点
好处
那Token相对于Cookie的好处:
- 支持跨域访问: Cookie是不允许垮域访问的,token支持
- 无状态: token无状态,session有状态的
- 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在 你的API被调用的时候, 你可以进行Token生成调用即可.
- 更适用于移动应用: Cookie不支持手机端访问的
- 性能: 在网络传输的过程中,性能更好
- 基于标准化: 你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在 多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如: Firebase,Google, Microsoft)
缺陷
-
占带宽
正常情况下要比 session_id 更大,需要消耗更多流量,挤占更多带宽,假如你的网站每月有 10 万次的浏览器,就意味着要多开销几十兆的流量。听起来并不多,但日积月累也是不小一笔开销。实际上,许多人会在 JWT 中存储的信息会更多。 -
无法在服务端注销,那么久很难解决劫持问题
-
性能问题
JWT 的卖点之一就是加密签名,由于这个特性,接收方得以验证 JWT 是否有效且被信任。但是大多数 Web 身份认证应用中,JWT 都会被存储到 Cookie 中,这就是说你有了两个层面的签名。听着似乎很牛逼,但是没有任何优势,为此,你需要花费两倍的 CPU 开销来验证签名。对于有着严格性能要求的 Web 应用,这并不理想,尤其对于单线程环境。
JWT概述
JWT
签名与加密
对称与非对称加密
对称与非对称加密2
JWT 优缺点
synchronized 锁升级
CAS存在的问题
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。
ABA问题
因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。
在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
分布式:session 存在的问题?
Redis 事务和 MySQL 事务的区别?
Redis
Redis 实现分布式锁?
TCP 协议?协议中的状态(TIME_WAIT 等)?TIME_WAIT 为什么要 2MSL 时间?
主动关闭一方最后的状态是 TIME_WAIT。
第一,为了保证A发送的最后一个ACK报文能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。如果A在TIME-WAIT状态不等待一段时间,而是在发送完ACK报文段后就立即释放连接,就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常的步骤进入CLOSED状态。
第二,A在发送完ACK报文段后,再经过2MSL时间,就可以使本连接持续的时间所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求的报文段。
https://blog.csdn.net/qq_28098067/article/details/80811938
https://blog.csdn.net/q1007729991/article/details/69686781
https://blog.csdn.net/yrx420909/article/details/104483591/
https://blog.csdn.net/weixin_43728446/article/details/100121446
Mac地址和IP地址的区别?
一、bai地址性质不du同
MAC地址是物理地址,IP地址是逻辑地zhi址。MAC地址是不可改变dao的,IP地址是可以更改的。
二、可变性
MAC地址具有唯一性,每个硬件出厂时候的MAC地址是固定的;IP地址不具备唯一性,因此,很多应用软件是围绕MAC地址开发的。
三、工作层次不同
二层基于MAC地址转发数据帧,三层基于IP地址转发报文。二层交换机基于MAC地址表转发数据,路由器基于路由表(IP地址)转发数据。
四、长度定义
MAC地址是Ethernet网卡上带的地址,长度为48位,IP地址目前主流是32位长。IP地址和MAC地址通过ARP协议联系到一起。
五、分配依据不同
IP地址的分配是基于网络拓扑,MAC地址的分配是基于制造商。
OSI七层模型和TCP四层模型,各层之间数据包如何发送?
各层设备
网关的作用是什么?
- 易于监控,可以在网关收集监控数据并将其推送到外部系统进行分析;
- 易于认证,可以在网关进行认证,然后再将请求转发到后端的微服务,而无需在每个微服务中进行认证;
- 减少客户端和各个微服务之间的交互次数。
Mysql索引失效的情况?
- 1、like 以%开头,索引无效;当like前缀没有%,后缀有%时,索引有效。
- 2、or语句前后没有同时使用索引。当or左右查询字段只有一个是索引,该索引失效,只有当or左右查询字段均为索引时,才会生效。
- 3、组合索引,不是使用第一列索引,索引失效。
- 4、数据类型出现隐式转化。如varchar不加单引号的话可能会自动转换为int型,使索引无效,产生全表扫描。
- 5、在索引列上使用 IS NULL 或 IS NOT NULL操作。索引是不索引空值的,所以这样的操作不能使用索引,可以用其他的办法处理,例如:数字类型,判断大于0,字符串类型设置一个默认值,判断是否等于默认值即可。(此处是错误的!)
- 6、在索引字段上使用not,<>,!=。不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。 优化方法: key<>0 改为 key>0 or key<0。
- 7、对索引字段进行计算操作、字段上使用函数。(索引为 emp(ename,empno,sal))
- 8、当全表扫描速度比索引速度快时,mysql会使用全表扫描,此时索引失效。
Spring模块
核心容器(Spring Core)
核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。
应用上下文(Spring Context)
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。
面向切面编程(Spring AOP)
AOP(Aspect Oriented Programming)
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
JDBC和DAO模块(Spring DAO)
JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
对象实体映射(Spring ORM)
ORM(Object Relational Mapping)
Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。
Web模块(Spring Web)
Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文。所以Spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
MVC模块(Spring Web MVC)
MVC(Model View Controller)
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。
Spring 事务
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。
- JDBC操作数据库,想要用到事务,可以按照以下步骤进行:
- 连接 Connection con = DriverManager.getConnection()
- 开启事务con.setAutoCommit(true/false);
- 执行CRUD
- 提交事务/回滚事务 con.commit() / con.rollback();
- 关闭连接 conn.close()
- 使用Spring的事务管理功能后,我们可以不再写步骤 2 和 4 的代码,而是由Spirng 自动完成
- 配置文件开启注解驱动,在相关的类和方法上通过注解 @Transactional 标识。
- spring 在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据@Transaction的相关参数进行相关配置注入,这样就在代理中为我们把相关的事务处理掉了(开启正常提交事务,异常回滚事务)。
- 真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象ProxyObject。
- 整个事务的增强执行过程:
在同一个Class内,非代理增强方法中调用了被@Transactional注解增强的方法,注解会失效。背后的实际原因是Spring AOP是基于代理,同一个类内这样调用的话,只有第一次调用了动态代理生成的ProxyClass,之后调用是不带任何切面信息的方法本身,因为没有直接调用Spring生成的代理对象。
AspectJ事务方面甚至被编织成私有方法(Spring3.0检查)。
反射对单例模式的破坏
- 反射破坏单例模式,访问私有属性—— setAccessible(true)方法
通过反射获得单例类的构造函数,由于该构造函数是private的,通过setAccessible(true)指示反射的对象在使用时应该取消 Java 语言访问检查,使得私有的构造函数能够被访问,这样使得单例模式失效。
枚举可以防止反射破坏单例
MySQL 三大范式
- 1NF:字段不可分,每个字段都是原子级别的
- 2NF:有主键,非主键字段依赖主键,可以表示这一条数据是唯一的
- 3NF:非主键字段不能互相依赖
docker
docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。简言之,就是可以在Linux上镜像使用的这么一个容器。
jar 包和 war 包的区别
WAR文件代表了一个Web应用程序,jar是类的归档文件。
war包和项目的文件结构保持一致,jar包则不一样。
jar包里没有静态资源的文件(index.jsp)
jar 包占用内存过大如何排查
自定义注解如何实现
SQL 注入及防止
定义:
通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
防止非法:
-
检查用户输入变量的数据类型和格式
a. 前端使用JS检查是否包含非法字符
b. 使用正则表达式过滤传入的参数
c. 使用PHP函数检查变量 -
过滤特殊的符号
对于一些无法固定格式的变量,要进行一些特殊符号的过滤或转义处理。如PHP通常采用addslashes函数,它会在制定的预定义字符前添加反斜杠转义,这些预定义字符包括单引号、双引号、反斜杠和NULL。该方法相对安全,但还是能破解 -
绑定变量,使用预编译语句
MySQL的mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法。实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示,这样也就无法改变SQL语句的结构,即使用户输入 “ 'or 1=1 – ” 这样的数据也只会当作字符串来解释查询,这样就从根本上杜绝了SQL攻击的发生
Redis 与数据库的 一致性
1.先更新数据库,再更新缓存——脏数据
2.先删缓存,再更新数据库——导致不一致
3.先更新数据库,再删缓存——较好
Redis和MySQL数据的同步,代码级别大致可以这样做:
读: 读redis->没有,读MySQL->把mysql数据写回redis
写: 写mysql->成功,写redis
并发不高的情况:
读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;
写: 写mysql->成功,再写redis;
并发高的情况:
读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;
写:异步话,先写入redis的缓存,就直接返回;定期或特定动作将数据保存到mysql,可以做到多次更新,一次保存;
OutOfMemory 解决方法
- 指定内存
//分配最小内存、最大内存
-Xms512m -Xmx2048m
- 借助相关软件查看内存:OutOfMemoryError神器 jvisualvm
该工具位于jdk安装目录下bin目录下,是jdk自带分析工具,功能非常强大
- 检查自己代码中是否出现了以下情况
- 内存中加载的数据量过于庞大,如一次从数据库取出大量过多数据
- 集合类中有对 对象 的引用,使用完未清空,使得 JVM 不能回收
- 代码中存在死循环或循环产生过多重复的对象实体
Spring Bean的完整生命周期
Bean的完整生命周期
- 1.instantiate bean对象实例化
- 2.populate properties 封装属性/依赖注入
- 3.如果bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法并执行
- 4.如果bean实现了BeanFactoryAware接口 或者 ApplicationContextAware ,spring将调用setBeanFactory方法,将BeanFactory实例传进来
- 5.如果存在类实现 BeanPostProcessor(后处理Bean) ,执行postProcessBeforeInitialization,BeanPostProcessor接口提供钩子函数,用来动态扩展修改Bean。(程序自动调用后处理Bean)
- 6.如果Bean实现InitializingBean 执行 afterPropertiesSet
- 7.调用 指定初始化方法 init
- 8.如果存在类实现 BeanPostProcessor(处理Bean) ,执行postProcessAfterInitialization
- 9.执行业务处理
- 10.如果Bean实现 DisposableBean,执行 destroy
- 11.调用指定销毁方法 customerDestroy
Bean的简单生命周期
- 1.bean定义:在配置文件里面用< bean >< /bean> 进行定义
- 2.bean 初始化:有两种方式进行初始化
- 在配置文件中通过指定 init-method属性来完成
- 实现org.springframwork.beans.factory.InitializingBean接口
- 3.bean 调用:三种方式得到bean实例并进行调用
- 构造方法创建
- 静态工厂创建
- 实例工厂创建
- 4.bean销毁:两种方式
- 使用配置文件指定的 destroy-method属性
- 实现org.springframwork.bean.factory.DisposeableBean接口
Spring Bean的作用域
spring 起初的版本只有singleton,也就是是否是单例模式。
作用域包括:singleton、prototype、request、session和globalSession
- singleton
全局只有一个实例 - prototype
每次调用产生一个新的实例
在web使用的时候还有三个作用域,但是必须在web.xml中注册一个RequestContextListener , 目的是为了设置每次请求开始和结束都可以使spring得到相应的事件。 - request
每次请求产生一个bean - session
每个用户session可以产生一个新的bean,不同用户之间的bean互相不影响 - globalSession
作用和session类似,只是使用portlet的时候使用。
Spring Bean的注入
spring支持构造器注入和setter方法注入
- 构造器注入,通过 元素完成注入
- setter方法注入, 通过 元素完成注入【开发中常用方式】
Spring 中的 单例 Bean是线程安全吗
-
如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。
比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。 -
对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。
-
使用ThreadLocal的好处
使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。这是一种以空间换时间的方式。
当然也可以通过加锁的方法来解决线程安全,这种以时间换空间的场景在高并发场景下显然是不实际的。
有状态与无状态
-
有状态对象(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。
-
无状态对象(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。
BeanFactory接口和ApplicationContext接口区别
- ApplicationContext 接口继承BeanFactory接口,Spring核心工厂是BeanFactory ,BeanFactory采取延迟加载,第一次getBean时才会初始化Bean, ApplicationContext是会在加载配置文件时初始化Bean。
- ApplicationContext是对BeanFactory扩展,它可以进行国际化处理、事件传递和bean自动装配以及各种不同应用层的Context实现
开发中基本都在使用ApplicationContext, web项目使用WebApplicationContext ,很少用到BeanFactory
Spring IOC初始化流程
-
Resource定位;指对BeanDefinition的资源定位过程。通俗地讲,就是找到定义Javabean信息的XML文件,并将其封装成Resource对象。
-
BeanDefinition的载入;把用户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。
-
向IoC容器注册这些BeanDefinition。
IOC 中的设计模式
- 工厂模式
- 单例模式
- 策略模式
- 装饰者模式
Spring AOP的调用过程
Spring IOC自动装配
MyBatis 逻辑分页,物理分页
-
物理分页
物理分页依赖的是某一物理实体,这个物理实体就是数据库,比如MySQL数据库提供了limit关键字,程序员只需要编写带有limit关键字的SQL语句,数据库返回的就是分页结果。 -
逻辑分页(内存分页)
逻辑分页依赖的是程序员编写的代码。数据库返回的不是分页结果,而是全部数据,然后再由程序员通过代码获取分页数据,常用的操作是一次性从数据库中查询出全部数据并存储到List集合中,因为List集合有序,再根据索引获取指定范围的数据。
两者区别
1.数据库负担
物理分页每次都访问数据库,逻辑分页只访问一次数据库,物理分页对数据库造成的负担大。
2.服务器负担
逻辑分页一次性将数据读取到内存,占用了较大的内容空间,物理分页每次只读取一部分数据,占用内存空间较小。
3.实时性
逻辑分页一次性将数据读取到内存,数据发生改变,数据库的最新状态不能实时反映到操作中,实时性差。物理分页每次需要数据时都访问数据库,能够获取数据库的最新状态,实时性强。
4.适用场合
逻辑分页主要用于数据量不大、数据稳定的场合,物理分页主要用于数据量较大、更新频繁的场合。
MyBatis使用 【RowBounds】 实现的分页是 【逻辑分页】 ,也就是先把数据记录全部查询出来,然在再根据offset和limit截断记录返回(数据量大的时候会造成内存溢出),不过可以用插件或其他方式能达到物理分页效果。
MyBatis 多种分页方式(5种)
- 数组分页
查询出全部数据,然后再list中截取需要的部分。 - sql分页
通过sql语句参数 limit 实现分页。 - 拦截器分页
创建拦截器,拦截mybatis接口方法id以ByPage结束的语句 - RowBounds 分页
数据量小时,RowBounds不失为一种好办法。但是数据量大时,实现拦截器就很有必要了(一次性取出放内存 然后分页)详细
前4种参考文章 - PageHelper 插件实现分页
PageInfo<Person> getByPage(int currentPage, int pageSize, Map<String,Object> map)
其中PageHelper.startPage(currentPage,pageSize,true);
第一个参数表示从第几页开始,第二个参数表示一页多少条记录,第三个参数表示是否返回总的真实记录数
注意:PageHelper.startPage(currentPage,pageSize,true);要紧接着查询,否则可能不分页
PageInfo 参数
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//结果集
private List<T> list;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;
MyBatis 的批量处理
- 使用 BATCH 的方式创建 SqlSession
@Test
public void batchTest() {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = getUsers();
long startTimeMillis = System.currentTimeMillis();
for (int i = 0; i < users.size(); i++) {
User user = users.get(i);
mapper.insertUser1(user);
}
sqlSession.flushStatements();
System.out.println("batch方式共执行" + (System.currentTimeMillis() - startTimeMillis) + "ms");
}
<insert id="insertUser1" parameterType="User">
insert into t_user (id, name, age) values(#{id}, #{name}, #{age})
</insert>
- 使用 foreac标签
@Test
public void testForeachInsert() {
List<User> users = getUsers();
long startTimeMillis = System.currentTimeMillis();
userMapper.insertUsers(users);
System.out.println("foreachInsert 共执行" + (System.currentTimeMillis() - startTimeMillis) + "ms");
}
<insert id="insertUsers" parameterType="list">
insert into t_userxxx (id, name, age) values
<foreach collection="users" item="user" separator=",">
(#{user.id}, #{user.name}, #{user.age})
</foreach>
</insert>
- MyBatisBatchItemWriter
@Test
public void testConfigurationUsingSqlSessionTemplate() {
//通过整合包提供的Builder,构造MyBatisBatchItemWriter
MyBatisBatchItemWriter<User> itemWriter = new MyBatisBatchItemWriterBuilder<User>()
.sqlSessionTemplate(new SqlSessionTemplate(this.sqlSessionFactory, ExecutorType.BATCH))
.statementId("com.zto.xxx.mapper.UserMapper.insertUser1")
.build();
//对构造出对itemWriter,进行了校验
itemWriter.afterPropertiesSet();
List<User> users = getUsers();
long startTimeMillis = System.currentTimeMillis();
itemWriter.write(users);
System.out.println("itemWriter 执行共计" + (System.currentTimeMillis() - startTimeMillis) + "ms");
}
注意:数据库连接时需要加上配置使允许批量处理
allowMultiQueries=true
//例如
?characterEncoding=UTF-8&allowMultiQueries=true
- foreach性能为何如此强大?
动态sql,通过foreach标签进行了sql拼接出一个大sql,交由数据库执行,只需一次调用。(不同DB对foreach支持程度不同,业务开发中也不会一次性提交大量数据,通常会给出限制) - 选foreach还是batch模式开发?
1)mybatis batch模式,需要通过手动设置ExecutorType.BATCH,驱动配置添加 rewriteBatchedStatements=true。相比foreach开发较为麻烦,但性能强大。
2)foreach使用简单,无需额外配置。
两者均可用于日常开发,但从上面测试结果看,添加驱动配置后,对simple类型执行性能有所影响,需要根据业务场景选择。如:独立服务中使用batch模式可以避免副作用同时提高数据操作性能。
Spring中的设计模式
个人了解:
单例模式;工厂方法模式;代理模式;观察者模式;模板方法模式…
七大设计原则
case when 使用
linux下用户组、文件权限
- useradd 和 usermod 的操作命令
-d 目录指定的目录
-c comment 注释描述
-g 用户组 制定用户所在的组
-G 用户组,用户组指定用户所属的附加组
-s shell文件 指定用户登录shell
-u 用户号
- passwd的操作命令
-l 锁定口令
-u 口令解锁
-d 使账号无口令
-f 强迫用户下次登陆时修改口令
- 用户命令
添加 useradd handle(操作) name
删除 userdel -r name
修改 usermod handle(操作) name
用户口令管理 passwd handle(操作)用户名
- 组命令
添加组 groupadd handle(操作) name
删除组 groupdel name
密码命令
-
passwd handle(操作) 用户名
-
文件权限(读写操作)
查看权限:ls - l (filename)
权限 rwx读写操作 - 没有权限
-rw-r--r-- root root 483997 jul 15 17:31 sobsrc.tgz (三个为一组 普通文件 文件主 文件组 其他用户)root :用户; root :组 ;文件大小:483997; 日期; 文件名:sobsrc.tgz;