面试宝典
2023-03-16 13:46:53 0 举报
AI智能生成
登录查看完整内容
为你推荐
查看更多
个人总结的一份java面试宝典
作者其他创作
大纲/内容
超文本编辑语言
thead
tbody
tfoot
table
表格布局
真实宽高 width height
内边距 padding
边框 border
外边距 margin
盒子模型?
left
左浮动
right
右浮动
浮动 float
普通文档流定位 (默认)
相对于自己原来的位置定位,不释放原来的 占用的空间
relative
相对定位
absolute
绝对定位
fixed
固定定位
定位 position
div布局
布局
跨列
colspan
跨行
rowspan
表格
HTML
全称:层次样式表 CSS 语法由三部分构成:选择器、属性和值: body {color: blue}
#id
id选择器
类选择器 class
p h1
[attribute=value]
属性选择器
:checked :active
伪类选择器
:nth-child(n)
:nth-of-type(n)
层级选择器
选择器有哪些?
<span style="color:red"></span>
内嵌式
head style
内联式
<link rel="stylesheet" type="text/css" href="theme.css" />
@import "style.css"
外部引入
css引入的方式
CSS
未声明变量,会抛出 not defined
typeof 未声明的变量 却是返回undefined
JS中undifined和not defined的区别
Object.prototype.toString.call(xxx)=='[object Array]'
1.使用Object.prototype.toString来判断
xxx._proto_==Array.prototype
2.使用原型链来判断
$.isArray(xxx)
3.利用JQ
怎么判断一个Object是否是数组(array)?
string
number
boolean
undefined
Array
Object
JS的数据类型
字符串(String)对象
Array 对象
Date 对象
Math 对象
Number 对象
RegExp 对象
JS的内置对象
赋值符
=
比较若类型不同,先尝试转换类型,再作值比较,最后返回值比较结果
==
===
=、==、===的区别
setTimeout
一次性定时器
setInterval
任务定时器
clearTimeout(obj)和clearInterval(obj)。
清除
任务定时器、一次性定时器怎么定义,如何清除?
typeof
判断某个值是什么类型?
parseInt()
string-->整数
parseDouble()
string-->浮动
数值的转换
JS如何判断数据类型以及强制转化为整数和浮点数
getElementById
getElementsByTagName
getElementsByClassName
JS得到DOM节点对象的方式
/(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)/
身份证号码
邮箱
/^1[3456789]\\d{9}$/
手机号
JS正则表达式(邮箱、手机号、身份证号码)
JavaScript
用于将任何对象包裹成为JQ对象,然后可以调用JQ方法
包裹选择器则可以返回一个包含所有匹配的DOM元素数组的JQ对象
JQuery的$()是什么意思
用于文档进入ready状态时执行代码
当DOM完全加载,JQ允许你执行代码
适用于所有浏览器
onload()的方法是在页面加载完成后才发生,这包括DOM元素和其他页面元素(例如图片)的加载。而$(document).ready()所要执行的代码是在DOM元素被加载完成的情况下执行,所以,使用document.ready()方法的执行速度比onload()的方法要快
$(document).ready()是什么函数?有什么用?
url : 请求路径
data: 前端给后端数据
datatype: json,text 服务器 预期返回给我们的数据类型
success: 请求响应成功 200 执行 function(){}
type: 发送的请求post get 请求
error: function 失败的时候
$.ajax
JQuery发送ajax
$(document).get(0)
$(document)[0]
JQ->DOM
$(DOM)
DOM->JQ
JQ对象和DOM对象相互转化
节省服务器带宽、下载速度快
分支主题
已经从CDN下载类相同的jq版本,就不会再下载一次,即用户访 问过使用任何这些CDN的jQuery框架的任何站点,它将被缓存
使用CDN加载jq库的优势
类选择器
标签选择器
层次选择器
表单选择器
选择器
JQuery
第四章:网页设计与开发
每进行一次http操作,就建立一次连接,完成就断开连接
默认短连接
Http1.0
数据传输完成TCP连接不断开,等待下一次传输
默认长连接
Http1.1
http1.1消息中增加了版本号
1.可扩展性
http1.1有缓存
2.缓存
http1.0有浪费带宽的现象,如断点下载的时候,如果第一次下载断开了,第二次必须重新从头下载
http1.1允许请求资源的某一部分
3.带宽优化
http1.1长连接
4.长连接
5.消息传递
6.Host头域
http1.0定义16个响应状态码,不够具体
http1.1定义24个响应状态码
7.错误提示
Http1.0和Http1.1的区别
Http长连接和短连接
客户端请求成功
200
请求的URL已移走
301
重定向
302
客户端请求有语法错误
400
请求未经授权
401
服务器拒绝服务
403
请求资源不存在
404
服务器发送不可预期错误
500
服务器当前不可服务,过一段时间可以恢复
503
http常见状态码
1.cookie是服务器发送浏览器信息,浏览器在本地存储cookie。session是服务器的一块信息,session中存储特定用户会话产生的信息
2.无论客户端怎么设置,session都能正常工作,但是如果禁用cookie则cookie无法使用
3.cookie只能 存储String对象,而session可以存储任意java类型
Cookie和Session的区别
后端生成一个sessionID,设置cookie,之后所有请求带上该cookie,然后从cookie里获取sessionID,从而查询用户信息
单点登录原理
关键点是这个session ID,而不是cookie
使用HTTP请求头来传输
单点登录,若cookie被禁用?
CS是Client/Server的缩写
BS是Brower/Server的缩写
CS一般建立在专用网络,小范围网络环境
BS用于广域网,适应范围更广
1.硬件环境不同
CS一般面向固定用户群,安全性更强
BS安全性相对差些,可能面向不可知用户
2.安全要求不同
CS程序可以更注重流程
BS对安全和访问速率更要求
3.程序架构不同
CS不可避免对整体性考虑
构件相对独立,便于重用
4.软件重用不同
CS与操作系统相关,升级较难
BS在浏览器之上,开发成本低
5.系统维护不同
CS处理固定用户群
BS处理更广的用户群
6.处理问题不同
CS建立在操作系统,表现方式有限
BS建立在浏览器上,表现方式丰富
7.用户接口不同
CS中央集权的机械式处理
BS信息流向变化,像交易中心
8.信息流不同
区别
BS与CS
全称:异步JavaScript和Xml
JS对象的XmlHttpRquest
核心
提高用户体验
提高应用程序性能
进行局部刷新
优点
同步时阻塞的,异步非阻塞的
同步当一个线程在使用,其他线程就必须一直等待锁
异步当前线程使用不影响其他线程,各行其事
同步和异步的区别
协议、域名、端口都相同才是同域,否则就是跨域
如何解决跨域问题?
跨域
ajax的过程
乱码问题
AJAX(Asynchronous JavaScript And XML)
当前页上下文
page
本次请求
request
当前会话
session
当前应用程序
application
会话跟踪技术
返回请求的特定参数值
getParameter()
返回所有可用属性名的枚举
getParameterNames()
返回包含参数所有值的数组
getParameterValues()
获取页面参数值的方式
getSession()创建session
setAtrribute()设置值
getAtrribute()获得值
invalidate()销毁session
<session-config> <session-timeout>15</session-timeout> </session-config>
web.xml
<session-config> <session-timeout>30</session-timeout> </session-config>
tomcat目录下conf/web.xml
setMaxInactiveInterval()
方法>web.xml>外部web.xml
优先级的问题,从高到低
session的过期时间计算是从当前session的最后一次请求开始的
设置session存活时间
Tomcat为30分钟
session的默认失效时间
Session的主要方法
1.get的请求参数会在地址栏显示出来,post请求参数在请求头里
2.get携带的数据不多,一般不超过2k,post理论上没有限制,但是不建议携带过多数据
3.get安全性较低但是执行效率快,post相反
4.get是向服务器索取数据,post是向服务器传送数据
Get和Post的区别
实现Servlet接口
继承HttpServlet
使用Servlet注解
继承GenericServlet
创建serlvlet的三种方式
创建一个Servlet实例
1.创建
初始化数据,可以在we.xml配置
2.初始化
请求和响应处理的方法
3.服务
结束服务
4.销毁
Servlet生命周期
web.xml注册
@WebServlet
注解形式
2 注册
1个servlet对应一个 url
1 精确匹配
2 后缀匹配
/*
/
/* 和 / 的区别 /* 会拦截 .jsp
3 全匹配
3 url的匹配形式有哪些?
Servlet
filter用于拦截用户请求,在服务器作出响应前,可以在拦截后修改request和response,这样实现很多开发者想得到的功能。
构造器:创建Filter实例时调用,Filter实例服务器一旦启动就会被创建
init():实例创建后马上被调用,用来对Filter做一些初始化的操作
doFilter():Filter的主要方法,用来完成过滤器主要功能的方法,每次访问目标资源时都会调用。
destroy():服务器停止时调用,用来释放资源。
Filter的生命周期
init
doFilter
destroy
方法
处理全站中文乱码问题
实现自动登录
过滤敏感词汇
压缩网页
选择性让浏览器缓存
常见用途
Filter
ServletRequest
HttpSession
ServletContext
被监听的对象
第一维度
在application创建时就调用
contextInitialized
当application销毁时调用
contextDestroyed
ServletContextListener
application监听器
当打开一个浏览器时,就会触发这个方法
sessionCreated
当调用session.invalidate();或超时时调用
sessionDestroyed
HttpSessionListener
session监听器
request初始化
requestInitialized
request销毁
requestDestroyed
ServletRequestListener
request监听器
监听域对象的创建与销毁
当调用application.setAttribute()时调用
attributeAdded
当调用applcaition.removeAttribute()时调用
attributeRemoved
/当调用两次application.setAttribute()赋予相同属性时调用
attributeReplaced
ServletContextAttributeListener
application属性监听器
当调用session.setAttribute()时调用
当调用session.removeAttribute()时调用
当调用两次session.setAttribute()赋予相同属性时调用
HttpSessionAttributeListener
session属性监听器
增加属性
属性删除
属性替换(第二次设置同一属性)
ServletRequestAttributeListener
request属性监听器
监听域对象的属性变化
第二维度
统计在线人数,利用HttpSessionLisener
加载初始化信息:利用ServletContextListener
统计网站访问量
实现访问监控
监听器常用的用途
ContextLoaderListener
框架web.xml中的监听器
Listener
JAVAWEB三大组件
Request
Response
PageContext
Session
Application
Out
Config
Page
JSP内置对象
1.转发是服务器端的行为,重定向是客户端的行为
2.转发在当前项目转发,而重定向可以在任何网站
3.转发发送一次请求,重定向发送两次请求,产生状态码302
4.转发url地址栏不变,而重定向会变
5.转发携带request对象的数据不会丢失,重定向会丢失
转发和重定向的区别
第五章:JavaWEB开发
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射
把SQL独立进XML,维护便利
封装底层JDBC,转化成java Bean对象,简化重复工作
能够完成复杂的查询
①频繁链接和释放数据库的浪费、
②SQL语句与代码的高耦合、
③参数传递麻烦、
④结果解析麻烦。
MyBatis则简化了传统JDBC方法,解决了:
什么是Mybatis
UserDao userMapper = sqlSession.getMapper(UserDao.class);
使用
调用getResourceAsStream()
1.Resource类加载核心配置文件
Mapper接口及其映射文件是在加载mybatis-config配置文件的时候存储进去的
MyBatis都会将xml映射文件和Mapper接口进行关联
解析过程中
返回的就是一个实现了SqlSessionFactory接口的DefaultSqlSessionFactory对象
创建一个XMLConfigBuilder解析对象后返回configuration对象
2.SqlSessionFactoryBuilder调用build()创建SqlSessionFactory
最终创建了实现SqlSession接口的DefaultSqlSession对象
生成Transaction对象和执行核心执行器Executor
3.SqlSessionFactory调用openSession()创建SqlSession
4.jdk动态代理生成mapper接口的代理对象MapperProxy
5.通过MapperProxy调用Maper中相应的方法
执行流程
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术
ORM
用过即丢
三个对象
properties属性
autoMappingBehavior
自动映射
lazyLoadingEnabled
aggressiveLazyLoading
懒加载
cacheEnabled
二级缓存
logImpl
日志
setting设置
<typeAlias alias="Author" type="domain.blog.Author"/>
alises type
<package name="domain.blog"/>
package
@Alias("author")
@Alias
typeAliases别名
PageHelper方法使用了静态的ThreadLocal参数,分页参数和线程是绑定的
执行查询的时候通过拦截器在sql语句中添加分页参数,之后实现分页查询,查询结束后在 finally 语句中清除ThreadLocal中的查询参数
ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量
桥接模式
PageHelper原理
分页插件
plugin插件
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment></environments>
enviroment环境配置
databaseIdProvider数据库厂商标识
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
resource
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
url
<package name="org.mybatis.builder"/>
<mapper class="org.mybatis.builder.AuthorMapper"/>
class
mappers映射器
核心配置文件
多对一就是一对一,多对多就是一对多
public class Shoporder { private Integer sid; private String sname; private Double price; private Integer num; private Integer uid;// 一对一 private User user;}
public class User { private Integer uid; private String uname;// 一对多 ArrayList<Shoporder> shoporders;}
一对一
<resultMap id="rm" type="user"> <id property="uid" column="uid"/> <result property="uname" column="uname"/> <collection property="shoporders" ofType="shoporder"> <id property="sid" column="sid"></id> <result property="sname" column="sname"/> <result property="price" column="price"/> <result property="num" column="num"/> <result property="uid" column="uid"/> </collection> </resultMap>
一对多
正常
select:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement)
column:订单信息中关联用户信息查询的列,是user_id
<resultMap id="rm" type="shoporder"> <id property="sid" column="sid"/> <result property="uid" column="uid"/> <!-- 使用缓存--> <association property="user" javaType="user" select="dao.UserDao.findByUserId" column="uid"/> </resultMap> <select id="getAllById" resultMap="rm"> select sname from shoporder where sid = #{sid} </select>
<select id="findByUserId" resultType="pojo.User"> select * from user where uid=#{id} </select>
一对一延迟加载
<resultMap id="rm" type="user"> <id property="uid" column="uid"/> <result property="uname" column="uname"/> <collection property="shoporders" ofType="shoporder" select="dao.ShoporderDao.getAllById" column="uid"/> </resultMap> <select id="findByUserId" resultMap="rm"> select * from user where uid=#{id} </select>
<select id="getAllById" resultType="shoporder"> select * from shoporder where uid=#{uid} </select>
一对多延迟加载
延迟加载:例如进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力
一对一、一对多、多对一、多对多
cache
resultType简单类型,resultMap复杂类型
resultType自动映射,resultMap自定义映射
resultType
resultMap
insert
update
delete
select
1.一个参数直接写
4.两个参数,Map
传入参数
1.#{}会预编译,${}是直接填充
2.#{}更安全,${}有SQL注入的危险
#{}和${}区别
@Select
@Update
@Delete
@Insert
注解
sql
include
映射文件
if
where
choose\\when\\otherwise
set
trim
foreach
动态SQL
进入一级缓存的查询流程前,先在CachingExecutor 进行二级缓存的查询
底层使用LinkedHashMap实现
LRU - 最近最少回收,移除最长时间不被使用的对象(默认)
span style=\
FIFO - 先进先出,按照缓存进入的顺序来移除它们
SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象
eviction: 缓存回收策略
由于二级缓存是应用级缓存,可以跨线线程使用。当不同线程获取到同一个对象时,做不同操作时,就会产生不同的结果相互影响。获取到的对象进行反序列化,获得的对象ID不同,但是对象的值相同,这样不同的操作就不会相互干扰。
二级缓存需要序列化对象
cache 标签有多个属性
第一次SqlSession 未提交
探究多表操作对二级缓存的影响
更新对二级缓存影响
二级缓存失效的条件
缓存是以namespace为单位的,不同namespace下的操作互不影响。
通常使用MyBatis Generator生成的代码中,都是各个表独立的,每个表都有自己的namespace。
多表操作一定不要使用二级缓存,因为多表操作进行更新操作,一定会产生脏数据。
使用二级缓存的注意点
核心配置文件settings设置
映射文件加<cache/>标签
二级缓存需要手动开启
是本地缓存,默认开启
不同的session
同session,查询条件不同
同session,查询过程中进行了增删改
调用clearCache手动清除缓存
四个失效条件
一级缓存是session级别的
一级缓存和二级缓存
Mybatis
Java企业级应用开源开发JavaEE框架
core和beans模块提供了整个框架最基础的部分,包括了IOC和DI。
Context建立在Core和Beans模块提供的基础之上:他提供了框架式访问对象的方式
core、beans、context构成了Spring的骨架
Expression:提供了一种强大的用于在运行时操作对象的表达式语言
Beans、Core、Context、Expression
Spring核心容器
什么是Spring
xml解析得到类名,通过反射创建对象返回
工厂模式
默认寻找无参构造器创建
创建bean对象
依赖注入:注入bean和属性
“控制反转”,不是什么技术,而是一种设计思想
加载配置文件不创建对象,使用的时候创建
Spring内部使用,开发一般不用
BeanFactory
加载配置文件时就创建对象
BeanFactory的子接口,面向开发人员使用
ApplicationContext
实现IOC的两种方式
其实它们是同一个概念的不同角度描述
IOC 是通过DI 来实现的,DI 是IOC 实现的手段
IOC和DI的关系
什么是IOC
一般定义的都是普通bean
定义什么类型返回什么类型
普通bean
实现FactoryBean接口,实现方法Object,getObjectType,isSingleton
定义和返回的类型可以不一样
工厂bean
普通Bean和工厂Bean
注入外部bean,用ref
注入内部bean,写在properties里
注入bean
<bean id="student" class="com.xmx.pojo.Student"> <property name="sid" value="100"/> <property name="sname" value="张三"/> <property name="age" value="100"/></bean>
普通字段注入
<bean id="teacher" class="com.xmx.pojo.Teacher"> <property name="tname" value="金平"/> <property name="book" ref="book"/></bean>
<bean id="xx" class="..." autowire="byName"> <property> </property> </bean>
byName 按照名称注入
<bean id="xx" class="..." autowire="byType"> <property> </property> </bean>
byType 按照类型注入
自动注入(自动装配)
引用类型注入(类)
<property name="array"> <array> <value>arr1</value> <value>arr2</value> </array> </property>
数组类型属性注入
1.引入命名空间util
2.定义<util:list id="a"></util:list>
3.使用<properties ref="a">
集合注入的提取
<property name="list"> <list> <value>list1</value> <value>list2</value> </list> </property>
list类型属性注入
<property name="map"> <map> <entry key="map-key1" value="map-value1"></entry> <entry key="map-key2" value="map-value2"></entry> </map> </property>
map类型属性注入
<property name="set"> <set> <value>set1</value> <value>set2</value> </set> </property>
set类型属性注入
使用<value></value>注入空字符串值
使用<null/>注入null值
set注入
<bean id="student" class="com.xmx.pojo.Student"> <constructor-arg index="0" value="1"/> <constructor-arg index="1" value="张三"/></bean>
构造方法注入(以有参构造函数注入)
<bean id="book" class="com.xmx.pojo.Book" p:bid="111" p:bname="平凡的世界"></bean>
底层用到set方式注入
p 命名空间 注入
注入属性
DI依赖注入
加载xml配置文件创建
singleton(默认)
调用getBean时创建
每次对该Bean请求的时候,Spring IoC都会创建一个新的作用域。
prototype
Request作用域针对的是每次的Http请求,Spring容器会根据相关的Bean的定义来创建一个全新的Bean实例
针对http session起作用,Spring容器会根据该Bean的定义来创建一个全新的Bean的实例
类似标准的http session作用域,不过仅仅在基于portlet的web应用当中才有意义
global session
bean的作用域
1.通过无参构造器创建bean对象
调用set方法
2.为bean对象属性设置值或其他bean引用
创建后置处理器
3.把bean实例传给后置处理器的方法postProcessBeforeInitialization
init-method="自定义初始化 方法名"
需要配置初始化方法
4.调用bean初始化的方法
5.bean对象获取到
6.把bean实例传给后置处理器的方法postProcessAfterInitialization
destory-method="自定义销毁方法名"
((ClassPathXmlApplicationContext)context).close()
手动销毁bean
需要配置销毁方法
7.容器关闭,调用bean销毁方法
bean的生命周期
1.引入context命名空间头
<context:property-placeholder location="url"/>
2.使用标签引入
${key}
3.读取
xml加载properties文件
1.引入aop依赖
2.引入context命名空间头
自定义扫描过滤器
<context:component-scan base-package="url"/>
3.开启注解扫描
使用注解(类、方法、属性上)
@Component
用于标注Dao类
@Reposity
用于标注业务类
@Service
用于标注控制器类
@Controller
标注bean的作用范围
@Scope
@Value
@Autowired\\@Qualifer
2 Autowired首先按照类型匹配,Resources首先按照名称匹配
3 Autowired根据名称匹配,要配合@Qualifier注解,Resources配合自己的name属性
@Resource
原生注解
指定当前类是 一个Spring 配置类, 当创建容器时,从该类上 加载注解(相当于beans的配置文件)
@Configuration
指定Spring初始化 扫描的包 (相当于下面的扫描语句)<context:component-scan base-package="com.lpc"/>
@ComponentScan(base-Packages="")
把返回的对象放在配置文件beans中
@Bean
加载 .properties 文件
@PropertySource
导入其他配置类
@Import
新注解(完全注解开发)
注解类型
IOC
事务一般加在服务Service层
原子性
隔离性
一致性
持久性
事务
PlatformTransactionManager
TransactionDefinition(推荐)
声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,而编程式事务则需要在代码里进行增加事务代码逻辑
与声明式事务的区别
编程式事务
对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
底层是AOP
REQUIRED(默认值)==required
无论方法本身有无事务,都创建新的事物
REQUIRES_NEWS==required_new
使用本身事务,若无抛异常
MANDATORY==mandatory
使用本身事务,若无则以无事务执行
SUPPORTS==supports
无论本身有无事务,都以无事务执行
NOT_SUPPORTED==not_supported
以无事务执行,有事务抛异常
NEVER==never
本身有事务则在嵌套事务执行,否则如required操作
NESTED==nested
propagation:事务传播机制
读未提交
读已提交
mysql默认
可重复读
可串行化
事务隔级别
timeout,一定时间内需要提交,否则回滚,默认-1,不超时
事务超时
默认false
readOlny
是否只读
设置哪些异常可以进行回滚
rollbackFor
设置哪些异常不进行回滚
norollbackFor
可以设置属性
定义数据源
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
定义事务管理器
导入tx的命名空间头
<tx:annotation-driven />
开启注解
在service类或方法添加事务注解@Transactional
使用try-catch包裹代码,如果异常则会回滚
相当于spring容器
替换 <tx:annotation-driven />
@EnableTransactionaManagement
可替换数据源注入和事务管理器注入
完全注解形式
步骤
注解配置
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
2 定义事务管理器
<!-- 配置事务传播特性 --> <tx:advice id="txAdvice"> <tx:attributes> <!-- <tx:method name="get*" propagation="SUPPORTS" />--> <!-- <tx:method name="add*" propagation="REQUIRED" />--> <!-- <tx:method name="del*" propagation="REQUIRED" />--> <!-- <tx:method name="update*" propagation="REQUIRED" />--> <!-- propagation事务的隔离级别--> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
3 设置事务属性
<!-- 定义切面 --> <aop:config> <aop:pointcut id="serviceMethod" expression="execution(* lpc.service..*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/> </aop:config>
4 定义事务切面
XML配置
声明式事务
Spring事务
概念:spring框架对JDBC进行封装
mysql、druid、spring-jdbc、spring-tx、spring-orm
1.引入依赖
2.配置数据库连接池DataSource
3.配置JdbcTemplate,注入DataSource
4.开启注解扫描
5.类上注入JdbcTemplate
6.使用JdbcTemplate
JDBCTemplate
面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面
设计原则:开闭原则
不改变原代码,在原有方法代码上添加新功能
基本概念
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
特点:
Proxy(代理类)
InvocationHandler(接口)
JDK动态代理
有接口
1 引入jar包
特点:CGLib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
CGlib
无j接口
1 JDK动态JDK直接支持,CGLib第三方jar包支持
2 JDK动态是必须要有接口才能代理,2 CGlib代理类是目标类的子类,可以针对于普通类做代理。但是不能是final的类。
3 JDK1.6之前,CGLib底层采用ASM字节码生成框架,效率比JDK动态代理要高JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,JDK1.8的时候,JDK代理效率高于CGLib代理
JDK动态代理和CGlib的有什么区别?
动态代理
底层原理
一个模块化的横切逻辑
切面
某个切面具体执行点(哪些方法可以被增强,这些方法就叫连接点)
连接点
切入点
被切的、被增强的对象
目标对象
在执行点上执行的代码逻辑
增强对象
AOP自动创建,执行增强的方法
代理对象
切面的过程
织入
切面、切入点、连接点、目标对象、增强对象、代理对象、织入
aop和aspects依赖
Spring依赖
cglib、aopalliance、aspectj.weaver依赖
AspectJ依赖
导入依赖
execution(public * *(..))
匹配所有目标类的public方法,第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法;
execution(* com.baobaotao.Waiter.*(..))
第一个*代表返回任意类型 *代表Waiter类口中的所有方法;
execution(* com.baobaotao.*.*(..))
匹配com.baobaotao包下所有类的所有方法;
execution(* com.baobaotao..*.*(..))
匹配com.baobaotao包、子孙包下所有类的所有方法
execution(* com..*Dao.find*(..))
com的任何包下类名后缀为Dao的类,方法名必须以find为前缀
切入点表达式
1.开启注解扫描
2.创建增强类和被增强类
<aop:aspectj-autoproxy/>
开启aop生成代理对象
3.增强类上加@Aspect生成代理对象
前置增强
@Before(value="切入点表达式")
后置增强
@AfterReturning
异常增强
@AfterThrowing
最终增强
@After
环绕增强
@Around
4.配置不同的增强类型
@Pointcut(value="切入点表达式")定义在方法上
如:@Before(value="a()")
其他增强注解引入该方法名()
切入点表达式相同,对切入点提取
决定增强类的优先级
@Order(1)越小优先级越高
开启aop生成代理对象,替换<aop:aspectj-autoproxy/>
@EnableAspectJAutoProxy(proxyTargetClass=true)
完全注解开发
aop:before
aop:after-returning
aop:after-throwing
aop:after
aop:around
xml配置
配置方式
不是Spring框架部分,是AOP框架
AspectJ+Spring
AOP
也就是我们常说的new
实例化一个Bean
也就是IOC注入
对实例化的Bean进行配置
Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法
Bean初始化
使用BeanWrapper
使用BeanFactory
使用ApplicationContext
Bean的调用
使用配置文件中的 destory-method 属性
实现 org.springframwork.bean.factory.DisposebleBean接口
Bean销毁
Bean的生命周期
面试题
Spring
springMVC是spring框架的WEB模块
视图(View)-对应组件:JSP或者HTML文件
控制器(Controller)-对应组件:Servlet
模型(Model)-对应组件:JavaBean
是什么
MVC三个模块相互独立,松耦合架构
多视图共享一个模型,大大提高代码的可重用性
控制器提高了应用程序的灵活性和可配置性
MVC优点
有利于软件工程化管理
增加了系统结构和实现的复杂性,不适合小型规模的项目
视图层与模型之间需要控制器做中间的连接控制,所以效率较低
MVC缺点
优缺点
MVC模式
继承HttpServletBean
调用doDispatch
调用doService
里面有processRequest
有doGet和doPost方法
继承FrameworkServlet
前端控制器DispatcherServlet
返回HandlerMapping
Handler
根据当前地址找到能处理请求的类
getHeader()
HandlerAdapter
根据处理类找到能处理该类的适配器
getHeaderAdapter()
执行后返回一个ModelAndView
适配器执行目标方法
运行流程
前端控制器接收到所有请求
前端控制器匹配@RequestMapping,从而找到对应的执行类
前端控制器找到目标类和方法,并返回方法返回值
返回值是一个地址,再由视图解析器解析
前端控制器帮我们转发到指定地址
bean
context
core
expression
核心容器
aop
支持注解
log4j
web
webmvc
web模块
1 导入依赖
扫描所有组件
<context:component-scan base-package="com.xmx"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".html"/></bean>
配置视图解析器
2. 编写 Springmvc的核心文件.
不拦截jsp,拦截其他
拦截jsp及所有
拦截所有请求
WEB-INF/前端控制器名-servlet.xml(servlet-name)
默认读取
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <!-- 加载Spring MVC配置文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup></servlet><servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern></servlet-mapping>
拦截所有请求,智能派发
DispatcherServlet
是一个servlet
前端控制器
3.配置 web.xml
匹配任意一个字符
?
匹配任意多个字符
*
匹配多层路径
**
可以模糊匹配
精确匹配优先于模糊匹配
地址
value
RequestMethod.GET
请求方式
method
必须包含该参数
param
必须不包含该参数
!param
不带该参数或该参数值必须不是value
param!=value
该参数值必须为value
param=value
params
规定请求头
headers
规定请求头里的Context-Type的类型
consumes
告诉浏览器返回的内容,给响应头加Context-Type
produces
@RequestMapping
方法上注解
Request 请求对象方式,getParameter
POJO对象的形式
参数名
参数必须有,false反之
required=true
设置默认值
defaultValue
参数名不一致是: @RequestParam
软件架构思想
查
GET
增
POST
改
PUT
删
DELETE
请求方式区分
支持REST风格
占位符
@PathVariable
路径变量 @PathVariable获取路径中的参数接收
获取请求头key值
@RequestHeader
获取某个cookie的key值
@CookieValue
HttpServletRequest
HttpServletResponse
可以写原生API
方法参数
addAttribute()设置参数
使用Model对象
map.put()
使用Map 对象
使用ModelMap对象
不管哪一种都是BindingAwareModelMap在工作
数据输出(出参)
Springmvc会出参数据存放到request域对象中
方法体内
出参
addObject()
setViewName()
使用ModelAndView对象
视图和数据合并方式
方法返回值
控制器
4.编写 Controller
XML文件方式
2 配置 web.xml
@RequestMapping("/login") public String hello() { return "login"; }
3 编写 Controller
4 编写 Springmvc的核心文件.
注解方式
<mvc:default-servlet-handler default-servlet-name="所使用的Web服务器默认使用的Servlet名称" />
<mvc:default-servlet-handler />
<mvc:resources mapping="/static/**" location="/static/" />
<mvc:resources />
SpringMVC静态资源引入
<mvc:interceptors> <!-- 日志拦截器 --> <mvc:interceptor> <mvc:mapping path="/**" /> <mvc:exclude-mapping path="/static/**" /> <bean class="拦截器java代码路径" /> </mvc:interceptor> </mvc:interceptors>
日志拦截器
<mvc:interceptors>\t\t<!-- 非法登录拦截器 -->\t\t<mvc:interceptor>\t\t\t<!--拦截器匹配哪些请求-->\t\t\t<mvc:mapping path="/**" />\t\t\t<mvc:exclude-mapping path="/login.html"/>\t\t\t<mvc:exclude-mapping path="/css/*"/>\t\t\t<mvc:exclude-mapping path="/img/*"/>\t\t\t<mvc:exclude-mapping path="/user/login"/>\t\t\t<bean class="com.xmx.config.MyInterceptor" />\t\t</mvc:interceptor>\t</mvc:interceptors>
拦截请求
xml形式
正常情况下,对于preHandle就是在在处理函数之前先执行,然后再执行处理函数,接着执行postHandle,最后再执行afterCompletion。afterCompletion无论是否出错是肯定要执行的,而postHandle则不是,不一定会执行。之后看源代码就知道他们的执行情况。AsyncHandlerInterceptor接口则增添了afterConcurrentHandlingStarted方法,对于此还未研究,先不讨论。HandlerInterceptorAdapter则默认实现了上述的接口,所以当我们仅仅要实现某个方法时,只需继承HandlerInterceptorAdapter,然后覆盖相应的方法。
要实现拦截器,要继承 HandlerInterceptor
java类配置
SpringMVC 拦截器
异常处理
JSR303校验
SpringMVC
javax.servlet-api
spring-webmvc
spring-tx
springmvc/spring
aopalliance
aspectjweaver
spring AOP
mysql-connector-java
mysql连接驱动
c3p0
commons-dbcp2
连接池
spring-jdbc
mybatis
mybatis-spring
lombok
junit
jackson-core
jackson-databind
jackson-annotations
json
pageHelper
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build>
build打包
1 导入需要的 jar包,依赖。
mybatis-config.xml
application-context.xml
springmvc-servlet.xml
database.properties
log4j.properties
2 导入 各框架的核心配置文件
<settings> <setting name="logImpl" value="LOG4J"/> </settings> <typeAliases> <package name="com.xmx.pojo"/> </typeAliases> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="reasonable" value="true"/> </plugin> </plugins>
配置设置,别名和插件
<context:component-scan base-package="com.xmx.*"/> <!-- 读取数据库配置文件 --> <context:property-placeholder location="classpath:database.properties"/> <!-- 获取数据源(使用dbcp连接池) --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close" scope="singleton"> <property name="driverClassName" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${user}" /> <property name="password" value="${password}" /> </bean> <!-- 事务管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置mybitas SqlSessionFactoryBean--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.xmx.dao" /> </bean>
driver=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://127.0.0.1:3306/others?useUnicode=true&characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=UTC&allowPublicKeyRetrieval=trueuser=rootpassword=123456
4 各配置文件的细节
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:application-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
Spring部分
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
Springmvc部分
3 Spring 和 Springmvc 配置进web.xml文件
SSM整合
SpringMvc
SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架
能快速创建出生产级别的Spring应用
自动装配
开箱即用
Tomcat
Jetty
内嵌式容器简化Web项目
约定大于配置
Starter启动器,简化依赖问题
整合spring生态圈的一站式框架
什么是SpringBoot
1.导入父依赖
2.导入spring-boot-starter-web依赖
@SpringBootApplication
3.创建Springboot启动类
pojo\\dao\\service\\controller
4.写业务代码
5.运行项目
第一个SpringBoot项目
组合了 @Configuration 注解,实现配置文件的功能
@SpringBootConfiguration
打开自动配置的功能,也可以关闭某个自动配置的选项
@EnableAutoConfiguration
Spring组件扫描
@ComponentScan
SpringBoot的核心注解
继承spring-boot-starter-parent项目
导入spring-boot-dependencies项目依赖
使用springboot
包含了一系列可以集成到应用里面的依赖包
Starters启动器
从里面的@Import注解里的类AutoConfigurationImportSelector
找到它类里的SpringFactoriesLoader类,有loadFactoryNames方法
这个方法会加载类路径及所有jar包下META-INF/spring.factories配置中映射的自动配置的类
Spring Boot 自动配置原理
JCL
SLF4J
jboss-logging
日志门面(接口)
util.logging
log4j2
logback
日志(实现)
SpringBoot底层是Spring框架,
Spring框架默认使用的是 JCL(Commons Logging )
SpringBoot 选用的是SLF4j和 logback.
日志选用
logging.level.root=WARN
logging.level.com.lpc=trace
logging.file.path=mylog
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} == %contextName == [%thread] %-5level %logger{36} - %msg%n
配置文件
SLF4j的使用(slf4j+ logBack)
trace
debug
info
warn
error
fatal
日志的级别
SpringBoot 日志原理
连接池的作用就是为了提高性能
性能方面 hikariCP>druid>tomcat-jdbc>dbcp>c3p0
druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。
综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池。
默认连接池HikariCP
驱动名
dataSourceClassName
jdbcUrl
获取连接时使用的默认身份验证用户名
username
获取连接时使用的默认身份验证密码
password
等待来自池的连接的最大毫秒数
maxWait
控制允许连接在池中闲置的最长时间
idleTimeout
连接池支持的最大连接数
maxActive
连接池中最大空闲连接数
maxIdle
连接池中最小空闲连接数
minIdle
初始化连接数目
initialSize
所创建的连接的自动提交(auto-commit)状态
defaultAutoCommit
连接池常用配置
SpringBoot
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI [C语言](编写、支持网络、可基于内存亦可持久化的日志型、Key-Value[数据库],并提供多种语言的API。称之为结构化数据库
定义
3.发布订阅系统
4.地图信息分析
5.计时器\\计数器
作用
String
List
Set
Hash
Zset
五大基本数据类型
geospatial
Hyperloglog
Bitmaps
三大特殊数据类型
类型
在指定时间间隔里将内存中的数据集快照写入磁盘,恢复时,将快照文件直接读到内存。
什么是RDB?
redis默认是RDB,一般不需要修改这个配置。
RDB保存文件是dump.rdb
save 3600 1
1.满足配置里save规则,自动触发
1.只需要把rdb文件放到redis的启动目录即可。
2.查看需要存在的位置:config get 目录(该目录存在drump.rdb)
恢复RDB文件
在主从复制中,rdb是备用的,在从机上。
优点:如果对数据完整性要求不高,适合大规模的数据恢复。
缺点:需要一定的时间间隔进程操作,如果redis意外宕机,最后一次修改就没了。fork进程的时候,会占用一定内容空间。
RDB(Redis Data Base)
默认不开启的。
将我们的所有命令都记录下来,history,恢复的时候把这个文件全部再执行一遍。
AOF保存的是appendonly.aof,默认无限添加。
重启redis服务即可生效
开启:appendonly yes
如果aof文件有错位,这时候redis启动不起来,我们需要修复这个aof,redis给我们提供了redis-check-aof修复工具。
命令:redis-check-aof --fix appendonly.aof
优点:每一次修改都同步,文件完整性更加好,每秒同步一次,可能会丢失1s数据。
缺点:相对于数据文件来说,aof远远大于rdb,修复速度也比rdb慢,aof运行效率比rbd慢。
AOF(Append Only File)
Redis持久化
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
单位:大小写不敏感
# include /path/to/local.conf
# include /path/to/other.conf
包含别的配置文件
bind 192.168.1.100 10.0.0.1 # listens on two specific IPv4 addresses
# bind 127.0.0.1 ::1 # listens on loopback IPv4 and IPv6
protected-mode no
安全模式
port 6379
端口
网络
daemonize yes
pidfile /var/run/redis_6379.pid
配置文件的pid文件,以后台方式运行
# warning (only very important / critical messages are logged)
日志级别
logfile ""
日志文件名
databases 16
默认16个数据库
always-show-logo no
是否显示logo
通用GENERAL
SNAPSHOTTING快照:持久化,在规定时间内,执行了多少操作,会被持久化到文件.rda .aof
# save 3600 1
在3600s内,如果至少有1个key进行了修改,我们进行持久化操作
# save 300 100
在300s内,如果至少有100个key进行了修改,我们进行持久化操作
# save 60 10000
在60s内,如果至少有10000个key进行了修改,我们进行持久化操作
stop-writes-on-bgsave-error no
持久化出错是否继续工作
rdbcompression yes
是否压缩rdb文件,需要消耗一些cpu资源
rdbchecksum yes
保持rdb文件时,进行错误校验
dir ./
rdb文件保持的目录
SNAPSHOTTING快照
REPLICATION复制
requirepass foobared xxx
设置密码
SECURITY安全
maxclients 10000
能连接的最大客户端数量
CLIENTS 客户端限制
maxmemory <bytes>
设置最大的内存容量
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
maxmemory-policy noeviction
内存达到上限的处理策略
MEMORY MANAGEMENT内存
appendonly no
开启aof
appendfilename "appendonly.aof"
持久化的文件的名字
# appendfsync always 每次修改都同步,消耗性能
# appendfsync no 不同步,操作系统自己同步数据,速度最快
appendfsync everysec 每秒执行一次,可能会丢失这1s的数据
APPEND ONLY MODEAOF的配置
Redis配置文件
测试连接
ping
选择数据库
select index
关闭客户端
quit
服务端验证
auth
Collection
清空当前数据库
flushdb
清空所有数据库
flushall
关闭服务器
shutdown
Server
删除key
del key
查询是否存在key,存在返回1,不存在返回0
exists key
设置key生效时间(秒)
expire key 10
查询所有的key
keys *
presist key 移除key的过期时间
返回随机的key
randownkey
重命名key
rename key
Keys
字符串追加
append key value
自减
decr key
减少步长
decr key 2
自增
incr key
增加步长
incrby key 2
获得key的值
get key
获取key的范围值
getrange key start end
设置key值并获取的是key前一个值
getset key value
设置一个key-value
set key value
key不存在,设置key,存在不设置
setnx key value
设置多个key-value
mset k1 v1 k2 v2...
获取多个key的value
mget k1 k2...
对应给定的keys到他们相应的values上。只要有一个key已经存在,MSETNX一个操作都不会执行
msetnx k1 v1 k2 v2...
返回value的字符串长度
strlen
头部设置list值,命令:lpush list xxx
尾部设置list值,命令:rpush list xxx
通过区间获取值,命令:lrange list 0 -1
获取单个值,命令:lindex list x(下标)
查看长度,命令:llen list
可以更新list的值,前提是list存在元素,命令:lset list 0(下标) xxx
判断一个list是否存在,命令:exists list
设置set值,命令:sadd set xxx
查看set值,命令:smembers set
判断是否存在指定元素,命令:sismember set xxx
查看set的长度,命令:scard set
移除set的值,命令:srem set xxx
随机移除元素,命令:spop set x(个数)
将一个set的指定的值(kkk)移到新的set,命令:smove set set2 kkk
差集:sdiff set set2
交集:sinter set set2
并集:sunion set set2
设置hashmap值,命令:hset hash key value
获得hashmap值,命令:hget hash key
批量设置,命令:hmset hash k1 v1 k2 v2
批量获取,命令:hmget hash k1 k2
获取所有的数据,命令:hgetall hash
获取hash的长度,命令:hlen hash
判断hash中指定字段是否存在,命令:hexists hash key
获取所有的key名,命令:hkeys hash
增加,命令:hincrby hash key value
减少,命令:hdecrby hash key value
存在不设置,不存在设置,命令:hsetnx hash key value
设置zset的值,命令:zadd zset 1(score) one
遍历zset的值,命令:zrange zset 0 -1
范围:-inf(负无穷) +inf(正无穷),可以随便设置(-inf-2500)
查看zset的长度,命令:zcard zset
从大到小排序,命令:zrevrange zset 0 -1
获取区间的数量,命令:zcount zset 1 2
Sorted Set
常用命令
Watch(乐观锁)
发布订阅
Redis主从复制
对缓存做高可用,防止缓存宕机
使用断路器,如果缓存宕机,为了防止系统全部宕机,限制部分流量进入 DB,保证部分可用,其余的请求返回断路器的默认值
如何解决呢?
通常,我们会使用缓存用于缓冲对 DB 的冲击,如果缓存宕机,所有请求将直接打在 DB,造成 DB 宕机——从而导致整个系统宕机。
解释 1:缓存查询一个没有的 key,同时数据库也没有,如果黑客大量的使用这种方式,那么就会导致 DB 宕机
解决方案:我们可以使用一个默认值来防止,例如,当访问一个不存在的 key,然后再去访问数据库,还是没有,那么就在缓存里放一个占位符,下次来的时候,检查这个占位符,如果发生时占位符,就不去数据库查询了,防止 DB 宕机。
解释 2:大量请求查询一个刚刚失效的 key,导致 DB 压力倍增,可能导致宕机,但实际上,查询的都是相同的数据
解决方案:可以在这些请求代码加上双重检查锁。但是那个阶段的请求会变慢。不过总比 DB 宕机好。
缓存雪崩
解释:多个客户端写一个 key,如果顺序错了,数据就不对了。但是顺序我们无法控制。
数据库乐观锁;
在任意时刻,只有一个客户端能持有锁。
互斥性
客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁
只要大部分的Redis节点正常运行,客户端就可以加锁和解锁
不能死锁
容错性
通过Lua脚本实现解锁
set key value px milliseconds nx
基于Redis的分布式锁;
基于ZooKeeper的分布式锁。
分布式锁常见的三种实现方式
解决方案:使用分布式锁,例如 zk,同时加入数据的时间戳。同一时刻,只有抢到锁的客户端才能写入,同时,写入时,比较当前数据的时间戳和缓存中数据的时间戳。
缓存并发竞争
解释:连续写数据库和缓存,但是操作期间,出现并发了,数据不一致了。
这么做的问题是:当有 2 个请求同时更新数据,那么如果不使用分布式锁,将无法控制最后缓存的值到底是多少。也就是并发写的时候有问题
先更新数据库,再更新缓存。
这么做的问题:如果在删除缓存后,有客户端读数据,将可能读到旧数据,并有可能设置到缓存中,导致缓存中的数据一直是老数据。
使用“双删”,即删更删,最后一步的删除作为异步操作,就是防止有客户端读取的时候设置了旧值。使用队列,当这个 key 不存在时,将其放入队列,串行执行,必须等到更新数据库完毕才能读取数据。
先删缓存,再更新数据库。
这个实际是常用的方案,但是有很多人不知道,这里介绍一下,这个叫 Cache Aside Pattern,老外发明的。如果先更新数据库,再删除缓存,那么就会出现更新数据库之前有瞬间数据不是很及时。同时,如果在更新之前,缓存刚好失效了,读客户端有可能读到旧值,然后在写客户端删除结束后再次设置了旧值,非常巧合的情况。
有 2 个前提条件:缓存在写之前的时候失效,同时,在写客户度删除操作结束后,放置旧数据 —— 也就是读比写慢。设置有的写操作还会锁表。
所以,这个很难出现,但是如果出现了怎么办?使用双删!!!记录更新期间有没有客户端读数据库,如果有,在更新完数据库之后,执行延迟删除。还有一种可能,如果执行更新数据库,准备执行删除缓存时,服务挂了,执行删除失败怎么办???这就坑了!!!不过可以通过订阅数据库的 binlog 来删除。
先更新数据库,再删除缓存。
缓存和数据库双写不一致
随机抽取 20 个 key
删除这 20 个key中过期的key
如果过期的 key 比例超过 1/4,就重复步骤 1,继续删除。
Redis 是单线程,全部扫描岂不是卡死了。而且为了防止每次扫描过期的 key 比例都超过 1/4,导致不停循环卡死线程,Redis 为每次扫描添加了上限时间,默认是 25ms
Redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,默认每 100ms 进行一次过期扫描
定期删除策略
从库不会进行过期扫描,从库对过期的处理是被动的。主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key
从库的过期策略
删除指令 del 会直接释放对象的内存,大部分情况下,这个指令非常快,没有明显延迟。不过如果删除的 key 是一个非常大的对象,比如一个包含了千万元素的 hash,又或者在使用 FLUSHDB 和 FLUSHALL 删除包含大量键的数据库时,那么删除操作就会导致单线程卡顿。
unlink 指令,它能对删除操作进行懒处理,丢给后台线程来异步回收内存
懒惰删除策略
Redis采用的是定期删除 + 懒惰删除策略。
Redis 的过期策略
配置最大占用
1、通过配置文件配置
config set maxmemory 100mb
2、通过命令修改
noeviction(默认策略):对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)
allkeys-lru:从所有key中使用LRU算法进行淘汰
volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰
allkeys-random:从所有key中随机淘汰数据
volatile-random:从设置了过期时间的key中随机淘汰
volatile-ttl:在设置了过期时间的key中,根据key的过期时间进行淘汰,越早过期的越优先被淘汰
当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误
config get maxmemory-policy
获取当前内存淘汰策略
maxmemory-policy allkeys-lru
通过配置文件设置淘汰策略
config set maxmemory-policy allkeys-lru
通过命令修改淘汰策略
设置淘汰策略
Redis的内存淘汰
如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉
即最近最少使用,是一种缓存置换算法
Redis使用的是近似LRU算法,它跟常规的LRU算法还不太一样。近似LRU算法通过随机采样法淘汰数据,每次随机出5(默认)个key,从里面淘汰掉最近最少使用的key
LRU算法
h1 class=\"rich_media_title\" id=\"activity-name\" style=\
Redis
微服务架构风格是一种将单个应用程序作为一套小型服务开发的方法,每种应用程序都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。
传统构架是部署在一个tomcat上的,Tomcat 默认配置的最大请求数是 150,也就是说同时支持 150 个并发,当某个应用拥有 250 个以上并发的时候,应考虑应用服务器的集群。因此当用户达到一定数量的时候就要考虑到集群。
单体架构
通过nginx代理,假设每台服务器能支持150的并发,上面的图中能最大支持300并发。但是配置集群最大的问题就是session共享问题,tomcat的节点越多,它们之间的关系就越复杂。当tomcat集群中节点数量增加,服务能力先增加后下降。所以集群中节点数量不能太多,一般也就5个左右。
集群架构
根据业务进行拆分,添加服务层
垂直结构
SOA:Service Oriented Architecture面向服务的架构。也就是把工程拆分成服务层、表现层两个工程。服务层中包含业务逻辑,只需要对外提供服务即可。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。
SOA架构
把系统按照模块拆分成多个子系统。
1、把模块拆分,使用接口通信,降低模块之间的耦合度。
2、把项目拆分成若干个子项目,不同的团队负责不同的子项目。
3、增加功能时只需要再增加一个子项目,调用其他系统的接口就可以。
4、可以灵活的进行分布式部署。
优点:
1、系统之间交互需要使用远程通信,接口开发增加工作量。
2、各个模块有一些通用的业务逻辑无法共用。
缺点:
微服务架构
Java架构的演变
Consistency
可用性
Availability
分区容错性
Partition tolerance
CAP理论指的是一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项
如果不要求A(可用),相当于每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统。设计成CP的系统其实不少,最典型的就是分布式数据库,如Redis、HBase等。对于这些分布式数据库来说,数据的一致性是最基本的要求,因为如果连这个标准都达不到,那么直接采用关系型数据库就好,没必要再浪费资源来部署分布式数据库。
CP without A
如果要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。典型的应用就如某米的抢购手机场景,可能前几秒你浏览商品的时候页面提示是有库存的,当你选择完商品准备下单的时候,系统提示你下单失败,商品已售完。这其实就是先在 A(可用性)方面保证系统可以正常的服务,然后在数据的一致性方面做了些牺牲,虽然多少会影响一些用户体验,但也不至于造成用户购物流程的严重阻塞。
AP without C
如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计的初衷的。传统的关系型数据库RDBMS:Oracle、MySQL就是CA。
CA without P
CAP理论
什么是微服务
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
什么是SpringCloud
提供服务注册和发现
@EnableEurekaServer
Eureka Server也会注册自己(因此Eureka Server理论上也是一个Eureka Client)
同时每个Eureka Server会定期(默认60s)执行失效服务的检测,当检测到超过一定时间(默认90s)没有发送心跳的客户端,则会注销该客户端的服务节点。
心跳失败比例在15分钟内,低于85%,则开始怀疑自己了,不剔除客户端了
开始调查自己的原因
为了防止这种误杀,Eureka提供了自我保护机制:Eureka在15分钟内收到服务端心跳数小于Eureka本应该收到的总心跳数 * 自我保护阈值(默认0.85)就会触发
自我保护
两个eureka可以互相注册
Eureka Server
注册中心
@EnableDiscoveryClient
每个Eureka Client会周期性(默认30s)的向Eureka Server发送心跳,已验证该客户端仍然与服务器连接。
Eureka Client
服务提供者
作为一个实例,还会涉及到一个默认的持续时间为30秒的注册表(通过客户端的Service URL)的周期性心跳
直到实例、服务端和客户端在本地缓存中都具有相同的元数据(因此它可能会花费3个心跳周期),客户端才可发现服务
来改变周期时间;将其设置为小于30秒,加快客户端连接到其他服务端的过程
在生产中,最好是坚持使用默认,因为在服务器内部有一些计算,它们会对租赁续订期做出一些假设
设置eureka.instance.leaseRenewalIntervalInSeconds
为什么注册服务这么慢
# 设为false,关闭自我保护主要eureka.server.enable-self-preservation# 清理间隔(单位毫秒,默认是60*1000)eureka.server.eviction-interval-timer-in-ms
server端
# 开启健康检查(需要spring-boot-starter-actuator依赖)eureka.client.healthcheck.enabled = true# 租期更新时间间隔(默认30秒)eureka.instance.lease-renewal-interval-in-seconds =10 # 租期到期时间(默认90秒)eureka.instance.lease-expiration-duration-in-seconds =30
client端
如何解决Eureka Server不踢出已关停的节点的问题
1. 服务多的来保护,服务少的不保护。
2.Eureka Server在启动时会创建一个定时任务,每隔一段时间(默认60秒),从当前服务清单中把超时没有续约(默认90秒)的服务剔除。我们可以把定时任务间隔的时间设置得短一点,做到快速下线。防止拉取到不可用的服务
Eureka Server为了避免同时读写内存数据结构造成的并发冲突问题,采用了3级缓存机制来进一步提升服务请求的响应速度
3.默认情况下,client每隔30秒就会向服务端发送一次心跳。这个时间也可以适当调小一点。
默认情况下,server在90s之内没有收到client心跳,将我踢出掉。为了让服务快速响应,可以适当地把这个时间改的小一点。
4.服务端剔除客户端的时间间隔
api-client从eureka-server拉取注册表信息是按照defaultZone配置的顺序依次拉取的,当eureka1不可用的时候再从eureka2中获取/注册。但是如果eureka1一直不挂。所有的微服务都会先从eureka1中获取信息,导致eureka1压力过大。在实际生产中,每个微服务可以随机配置不同的defaultZone顺序
5.手动做到负载均衡
api-client会定时到eureka-server拉取注册表。默认情况下每30秒拉取一次。可以根据实际情况设置拉取时间间隔。
6.客户端拉取注册表更及时
优化
Eureka日均承受几十万次访问量
服务测算
注册
续约
下线
剔除
集群同步
流程
Eureka
利用负载均衡的策略和RestTemplate调用远程服务,而且不用知道远程服务的ip和端口,只需要知道远程服务名就可以了
调用负载均衡器根据相应的规则选择某一个服务进行调研
将逻辑url,也就是服务名替换ip加端口号的形式
在RestTemplate发出请求之前利用拦截器进行拦截,在拦截器中创建负载均衡器(第一次调用的时候),负载均衡器从EurekaClient端获取到已经拉取到本地的服务列表,然后根据配置的负载均衡策略选取一个服务进行调用,调用的时候会将服务名替换成ip和端口号的形式进行调用
在启动服务的时候,通过LoadBalancerAutoConfiguration这个配置类,会创建带有LoadBalanceClient的拦截器,然后将这个拦截器注入到带有@LoadBalance注解的RestTemplate。这样我们在调用RestTemplate发送请求的时候首先会被拦截器拦截。
1.给@LoadBalance修饰的RestTemlate设置拦截器。2.当RestTemlate发送请求的时候,先创建request对象,会将拦截器设置到request对象中,3.当执行restTemplate的execute方法的时候,会调用request的execute方法,调用拦截器里面的拦截方法。4.在拦截器中会从逻辑url中取出服务名,服务名为后面创建子容器备用,然后调用负载均衡客户端获取服务。5.到了负载均衡客户端中,如果是ribbon首次执行这个服务的请求,会先去创建一个子容器,从子容器中取出负载均衡器,负载均衡器会去获取Eureka Client里面的服务列表,以及创建一个定时任务定时刷新服务列表,然后负载均衡器会从服务列表中利用Rule对象选出一个服务,这个Rule算法默认是轮训算法。6.拿到服务对象后,将url改成ip加端口的形式进行服务调用,返回结果。
1.根据服务名称从ribbon子容器中从获取负载均衡器,如果是第一次调用就会创建这个服务对象的子容器,起到配置隔离的作用,这也是为什么ribbon第一次调用服务的时候很容易出现超时的问题,这是因为需要创建子容器。
2.负载均衡器根据负载均衡算法,选中一个服务进行调用。
3.构建RibbonServer,记录服务花费时间,服务并发数,为负载均衡算法选取服务提供数据参考
4.执行请求。
原理
随机 (Random)轮询 (RoundRobin)一致性哈希 (ConsistentHash)哈希 (Hash)加权(Weighted)
负载均衡算法
Ribbon
Nacos是阿里巴巴开源的一款支持服务注册与发现,配置管理以及微服务管理的组件
Nacos
单体应用拆分成多个服务后,对外需要一个统一入口,解耦客户端与内部服务
不要有耗时操作在网关上处理
可以和服务注册中心完美的整合
非阻塞式
函数式编程端点
WebFlux
Gateway是基于WebFlux(提供响应式编程支持)的
动态路由:能够匹配任何请求属性;
可以对路由指定 Predicate(断言)和 Filter(过滤器)
集成Hystrix的断路器功能
请求限流功能
功能
支持路径重写
路由是网关的基本构件。它由ID、目标URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则匹配路由
Route路由
参照Java8的新特性Predicate。这允许开发人员匹配HTTP请求中的任何内容,比如头或参数
Predicate断言
可以在发送下游请求之前或之后修改请求和响应
Filter过滤器
因为Zuul已经进入了维护阶段,而且Gateway是SpringCloud团队研发的,是亲儿子产品,值得信赖。而且很多功能Zuul都没有;用起来也非常的简单便捷
我们为什么选择Gateway
客户端向 Spring Cloud Gateway 发出请求
然后在 Gateway Handler Mapping 中找到与请求相匹配的路由
发送到 Gateway Web Handler
Handler 再通过指 定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回
Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等
在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑
Spring Cloud Gateway 工作原理
GateWay
Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。Feign的英文表意为“假装,伪装,变形”, 可以理解为将HTTP报文请求方式伪装为简单的java接口调用方式。
封装了Http调用流程,更适合面向接口化的变成习惯
基于面向接口的动态代理方式生成实现类
根据Contract协议规则,解析接口类的注解信息,解析成内部表现
基于 RequestBean,动态生成Request
使用Encoder 将Bean转换成 Http报文正文(消息解析和转码逻辑)
拦截器负责对请求和返回进行装饰处理
日志记录
基于重试器发送HTTP请求
发送Http请求
工作原理
Feign
(豪猪,,因其背上长满棘刺,从而拥有了自我保护的能力)
分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应
硬件故障:如服务器宕机,机房断电,光纤被挖断等。
流量激增:如异常流量,重试加大流量等。
缓存穿透:一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端服务,造成服务提供者超负荷运行,引起服务不可用。
程序BUG:如程序逻辑导致内存泄漏,JVM长时间FullGC等。
同步等待:服务间采用同步调用模式,同步等待造成的资源耗尽。
雪崩效应常见场景
硬件故障:多机房容灾、异地多活等。
流量激增:服务自动扩容、流量控制(限流、关闭重试)等。
缓存穿透:缓存预加载、缓存异步加载等。
程序BUG:修改程序bug、及时释放资源等。
同步等待:资源隔离、MQ解耦、不可用服务调用快速失败等。资源隔离通常指不同服务调用采用不同的线程池;不可用服务调用快速失败一般通过熔断器模式结合超时机制实现。
雪崩效应应对策略
雪崩效应
Hystrix
将Session存储于Redis上,然后将整个系统的全局Cookie Domain设置于顶级域名上,这样SessionID就能在各个子系统间共享
基于Redis的Session共享方案
共享Session
基于OpenId的单点登录
基于Cookie的OpenId存储方案
项目中单点登录的实现原理
SpringCloud
MybatisPlus
Swagger
拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。
解决方案
消息队列中,如何保证消息的顺序性
当系统中出现“生产“和“消费“的速度或稳定性等因素不一致的时候,就需要消息队列
业务系统触发短信发送申请,但短信发送模块速度跟不上,需要将来不及处理的消息暂存一下,缓冲压力。就可以把短信发送申请丢到消息队列,直接返回用户成功,短信发送模块再可以慢慢去消息队列中取消息进行处理
提高系统响应速度
提高系统稳定性
好处
业务无关,一个具有普适性质的消息队列组件不需要考虑上层的业务模型,只做好消息的分发就可以了,上层业务的不同模块反而需要依赖消息队列所定义的规范进行通信
为什么需要消息队列?使用消息队列有什么好处
消息队列(Message Queue)是一种应用间的通信方式
消息队列是一种应用间的异步协作机制
消息发布者只管把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的
概念
需要提升系统服务的性能,这时可以将一些不需要立即生效的操作拆分出来异步执行,比如发放红包、发短信通知等
用于业务解耦
最终一致性、广播、错峰流控
使用场景
无需等待订阅者处理完成,响应更快速
吞吐量提升
服务没有直接调用,不存在级联失败问题
故障隔离
不会造成无效的资源占用
调用间没有阻塞
每个服务都可以灵活插拔,可替换
耦合度极低
不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件
流量削峰
异步的好处
架构复杂了,业务没有明显的流程线,不好管理
需要依赖于Broker的可靠、安全、性能
异步的坏处
异步
消息队列
channel:操作MQ的工具
exchange:路由消息到队列中
queue:缓存消息
virtual host:虚拟主机,是对queue、exchange等资源的逻辑分组
RabbitMQ中的几个概念
1个publisher:消息发布者,将消息发送到队列queue
1个queue:消息队列,负责接受并缓存消息
1个consumer:订阅队列,处理队列中的消息
只有三个部分
简单队列模型
prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息
设置上限
一个队列多个消费者共同处理消息处理
WorkQueue 工作模型(任务模型)
exchange负责消息路由,而不是存储,路由失败则消息丢失
工作模型加入了exchange(交换机)
Fanout Exchange 会将接收到的消息广播到每一个跟其绑定的queue
发布订阅Fanout 广播 Exchange
Direct Exchange 会将接收到的消息根据规则路由到指定的Queue,因此称为路由模式(routes)
每一个Queue都与Exchange设置一个BindingKey
Exchange将消息路由到BindingKey与消息RoutingKey一致的队列
一个队列可以绑定 多个 RoutingKey
发布订阅-DirectExchange 路由
TopicExchange与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并且以 . 分割。
Queue与Exchange指定BindingKey时可以使用通配符:#:代指0个或多个单词*:代指一个单词
发布订阅TopicExchange 话题
发布Publish 订阅Subscribe
常见的消息模型
RabbitMQ
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。Spring使你能够编写更干净、更可管理、并且更易于测试的代码。
Spring配置复杂,繁琐,所以推出了Spring boot,约定优于配置,简化了spring的配置流程。
Spring Cloud构建于Spring Boot之上,是一个关注全局的服务治理框架。
Spring是核心,提供了基础功能;Spring MVC 是基于Spring的一个 MVC 框架 ;Spring Boot 是为简化Spring配置的快速开发整合包;Spring Cloud是构建在Spring Boot之上的服务治理框架。
Spring,SpringMVC,SpringBoot,SpringCloud有什么区别和联系
认证就是我们常说的登录
授权就是权限鉴别,看看请求是否具备相应的权限
认证和授权
支持基于 URL 的请求授权
主要功能
自动装配原理
默认用户名是user
UUID
密码是默认生成
有个默认的用户名和密码
spring.security.user.name
spring.security.user.password
默认的密码有一个问题就是每次重启项目都会变,这很不方便
application.yml中配置
对明文密码进行加密,返回加密之后的密文
encode
对传来的明文加密,与数据库查询到的密码密文比对,返回布尔值
matches
是否还要进行再次加密,一般不用
upgradeEncoding
PasswordEncoder
BCryptPasswordEncoder
加密方案
密码加密
设置用户名、密码
hasRole
设置角色
hasAuthority
设置权限
http.loginPage(\"/login.html\");
自定义登录页
http.exceptionHandling().accessDeniedPage(\"/noAuthority.html\");
自定义403页面
http.logout().logoutUrl(\"/logout\").logoutSuccessUrl(\"/index\").permitAll();
用户注销
configure 方法
注入Bean
继承WebSecurityConfigurerAdapter
配置
设置角色、权限
MyUserDetailsService
自定义编写实现类UserDetailsService
SpringSecurity
Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据
资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
运行指标,例如 QPS、线程池、系统负载等;
控制的效果,例如直接限流、冷启动、排队等。
Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状
流量控制
如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积
Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果
Hystrix 通过线程池的方式
通过并发线程数进行限制
通过响应时间对资源进行降级
Sentinel 对这个问题采取了两种手段
熔断降级设计理念
熔断降级
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩
在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去
系统负载保护
Sentinel
第六章:主流Java框架技术
普通用户和 root 都可以执行
存放系统命令
/bin
设备文件保存位置
/dev
如用户 liming 的主目录就是 /home/liming
普通用户的主目录(也称为家目录)
/home
系统调用的函数库保存位置
/lib
系统建议用来挂载媒体设备,如软盘和光盘
挂载目录
/media
/usr/local/ 目录也可以用来安装软件
第三方安装的软件保存位置
/opt
普通用户主目录在 /home/ 下,root 主目录直接在“/”下
root 的主目录
/root
只有 root 可以使用这些命令进行系统环境设置,但也有些命令可以允许普通用户查看
保存与系统环境设置相关的命令
/sbin
系统启动目录,保存与系统启动相关的文件
/boot
配置文件保存位置
/etc
建议这个目录用来挂载额外的设备,如 U 盘、移动硬盘和其他操作系统的分区
/mnt
一些系统服务启动之后,可以在这个目录中保存所需要的数据
服务数据目录
/srv
建议此目录中不能保存重要数据,最好每次开机都把该目录清空
临时目录
/tmp
此目录用于存储系统软件资源
全称为 Unix Software Resource
Linux 系统中,所有系统默认的软件都存储在 /usr 目录下,/usr 目录类似 Windows 系统中 C:\\Windows\\ + C:\\Program files\\ 两个目录的综合体
/usr
/var 目录用于存储动态数据,例如缓存、日志文件、软件运行过程中产生的文件等
/var
主要目录
当系统意外崩溃或意外关机时,产生的一些文件碎片会存放在这里
/lost+found
该目录中的数据并不保存在硬盘上,而是保存到内存中
虚拟文件系统
/proc
和 /proc/ 目录相似,该目录中的数据都保存在内存中,主要保存与内核相关的信息
/sys
次要目录
目录结构
显示隐藏文件和可见文件
ls -a
查看当前目录下所有可见文件的详细属性
ls -l
ls
列出文件列表
创建目录
mkdir
删除空目录
rmdir
创建目录和删除空目录
tail
显示文件后几行内容
打包
tar -xvf
打包压缩
tar -zcvf
grep 关键字 文件
查询字符串
pwd
显示当前所在目录
touch 1.txt
创建空文件
vim/vi
编译器
tail -f 1.txt |grep a
tail -f xxx.log | perl -pe 's/(ERROR)/\\e[1;31m$1\\e[0m/g'
输出最后n行的内容,同时监视文件的变化,一旦变化就显示出来
tail -nf test.log
tail -f 日志文件,输出最后10行的内容,同时监视文件的变化,一旦变化就显示出来
tail -n 10 filename
输出文件最后10行的内容
tail -n +20 test.log
在数字参数前补个“+”号即表示从文档开始截取
从第20行至末尾
tail -c 10 test.log
显示最后10个字符
1.创建日志文件a.log
2.执行tail -f a.log
3.另一个ssh连接执行 echo '999' >> a.log
4.可以看见这边的ssh连接的tail -f a.log监听到了内容的新增
实时检测步骤
实时动态查看日志
ps -ef|grep xxx
查看指定进程PID
kill -9 PID
强制终止进程
ps -aux|grep xxx
查看所有进程里CMD是xxx的进程信息
关机命令
reboot
重启命令
ifconfig
查看网卡信息
ping 127.0.0.1
PING
第七章:Linux操作系统
Apache Subversion 通常被缩写成 SVN,是一个开放源代码的版本控制系统,Subversion 在 2000 年由 CollabNet Inc 开发,现在发展成为 Apache 软件基金会的一个项目,同样是一个丰富的开发者和用户社区的一部分。
什么是SVN
repository(源代码库):源代码统一存放的地方
Checkout(提取):当你手上没有源代码的时候,你需要从repository checkout一份
Commit(提交):当你已经修改了代码,你就需要Commit到repository
Update (更新):当你已经Checkout了一份源代码, Update一下你就可以和Repository上的源代码同步,你手上的代码就会有最新的变更
目录版本控制
真实的版本历史
自动提交
纳入版本控管的元数据
创建版本库
检出
更新
解决冲突
提交更改
生命周期
超链接
https://blog.csdn.net/HeyShHeyou/article/details/87979276
安装教程
服务器下载地址: http://subversion.apache.org/packages.html
svn --version
安装成功
可以在服务器管理界面,新建仓库
安装服务器 之后,可以新建 用户,新建 组
SVN服务器 安装
http://subversion.apache.org/packages.html
下载地址:https://tortoisesvn.net/downloads.html
SVN客户端安装
在服务器上自己创建 一个 仓库。名字 自己起,如 lpcRepositories, 建的 仓库中,可以存放多个 项目 。建文件夹即可。
在客户端检出: svn服务器中的 项目。
客户端 更新 查看 等操作。
对于冲突的解决,对于两个 用户,对于 相同的 一行 文件,做操作,两个人都去提交,会有冲突。
解决冲突。
SVN 操作
https://www.cnblogs.com/blogchen/articles/9040211.html
教程
IDEA中集成SVN
SVN集中式版本控制系統
分布式版本控制
本地版本控制
所有的版本数据都保存在服务器上,协同开发者做修改和保存
有问题: 可能服务器故障或者损坏,会丢失数据,丢失历史文件
集中版本控制
所有版本信息仓库
版本控制分类
git init
git clone
git add
工作区
git commit
暂存区
git push
本地仓库
git pull
远程仓库
Git 的工作流程
通过git add 状态变为Staged
未跟踪
Untracked
暂存状态
Staged
Unmodify
Modified
Git文件的状态
git branch dev 创建一个开发分支
git branch –v 查看现在有哪些分支
git checkout xxx 切换分支
Git的分支
git分布式版本控制系统
版本管理工具
数组,可以说数组几乎能表示一切的数据结构,在每一门编程语言中,数组都是重要的数据结构
插入快
查找慢
删除慢
数组一旦创建后,大小就固定了
数组的局限性
冒泡排序
选择排序
插入排序
排序
"大O表示法"表示程序的执行时间或占用空间随数据规模的增长趋势。
时间复杂度
空间复杂度
大O表示法
数组
栈(英语:stack)又称为堆栈或堆叠,栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表
利用数组 模拟实现。
Java本身的Stack类型
借用LinkedList来简介实现Stack
栈
队列(queue)是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列
数组模拟队列
队列
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。
内部类实现单向链表
单向链表
内部类实现双端链表
双端链表
链表
满二叉树
完全二叉树
非完全二叉树
前中后序遍历
二叉树
树
哈希表
堆
图
数据结构
有穷性
确定性
可行性
有输入
有输出
五大特性
正确性
健壮性
高效率与低存储量需求
设计原则
算法
数据结构与算法(算法+数据结构=程序)
java开发工具包,包括jre
JDK
java运行环境,包括jvm
JRE
java虚拟机,运行字节码文件
JVM
简单的服务器应用java平台
javase
复杂的服务器应用java平台
javaee
微型手机和其他小型设备java平台
javame
1998-2006年之间的JDK
sdk
1998-2006年之间的java版本
j2
1.java基础名词
可执行程序,如编译器、解释器
bin
javadb的数据库
db
本地代码头文件
java运行环境
jre
可执行文件的依赖文件
lib
jdk类库,源代码文件
src
2.JDK下的目录
由数字、字母、下划线和$符号组成
不以数字开头
小写字母做前缀,如m_xxx
匈牙利命名法
高低起伏,如run_Fast
骆驼命名法(Camel)
与骆驼命名法类似,首字母大小,如MyLike
Pascal命名法
命名的方式
3.变量命名规范
byte(1字节)
short(2字节)
int(4字节)
long(8字节)
整数型
float(4字节)
double(8字节)
浮点型
char(2字节)
字符型
boolean(1字节)
布尔型
8种基本数据类型
String、类、数组
引用类型
5.java的数据类型
JDK是java的开发工具集,包括JRE
JRE是 java的运行环境,执行字节码文件,包括JVM
JVM是java的虚拟机,可以加载类
4.JDK、JRE、JVM的区别
Java5引入的自动装箱、拆箱机制
装箱:把基本数据类型转化为包装类
拆箱:把包装类转化成基本数据类型
Byte
Short
Integer
Long
Float
Double
Character
Boolean
包装类
6.包装类
&和&&都是运算符两边同为true,才返回true
1.按位与
2.逻辑与
&
左边表达式为false,直接中断判断
短路与
&&
7.&与&&的区别
隐藏属性和实现细节,对外提供最简单的接口
封装
从已有类得到继承信息,创建新类的过程。提供信息的叫父类,得到信息的叫子类。提高代码复用性
继承
允许不同子类对象,对同一信息,作出不同响应。
分为编译时多态和运行时多态。而方法重载实现了编译时多态,方法重写实现了运行时多态
多态
用一类对象的共同特征来构造类的过程
抽象
8.面向对象的特征
被abstract修饰符修饰的类就是抽象类
抽象类不能实例化
抽象类中有抽象方法和普通方法
有抽象方法的类一定时抽象类
抽象类
比抽象类还抽象
接口不能实例化
接口的成员变量都是常量
接口的方法都是公有方法public
接口
9.抽象类和接口的区别
该类不可以被,继承
1.修饰类
该方法不可以被,重写
2.修饰方法
该变量为,常量
3.修饰变量
10.final关键字的作用
一个类中,同名但参数列表不同(参数,类型、个数、顺序不同)
修饰符和返回值可以相同也可以不同
重载
必须有继承关系,子类重写父类的方法
返回值必须相同
重写的方法,访问修饰符范围不得小于被重写方法
不能比被重写方法抛出更多的异常
重写
11.方法重载与重写的区别
系统级错误,程序不必处理的异常
Error
需要捕捉,程序需要处理的异常
Exception
12.Error和Exception的区别
2.外部比较器实现Comparator接口的compare()方法
Collection实现比较的接口
HashMap是Java1.2引进的Map的一个实现
HashMap是线程不安全的,但效率高于HashTable
HashMap允许null作为key或value
HashMap
HashTable是Java1.1一类,基于Dictionary类
HashTable是线程安全的
但Hashtable不允许null作为key或 value
HashTable
HashMap和HashTable的区别
ArrayList不是线程安全
ArrayList和Vector都有初始容量。当超过容量时,默认ArrayList以原来的0.5倍增长
访问快,修改删除慢
ArrayList
Vector是线程安全的
当超过容量时,默认Vector以原来的1倍增长
Vector
底层是双向链表,访问慢,修改删除快
LinkedList
ArrayList和Vector、LinkedList的区别
List是有序可重复的
Set是不可重复的
List和Set的区别
无序不可重复的双列集合
Map
是List和Set的父接口
是集合工具类
Collections
Collection和Collections的区别
初始化设置容量
ArrayList加入一万条数据,如何提高效率
底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素(模拟实现栈和队列)
LinkedList 是List接口实现类
底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
ArrayList 是List接口实现类
java提供实现栈的类
Stack 是Vector类的实现类
底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
Vector 是List接口实现类
元素可以重复,可以通过索引访问元素
List接口
底层数据结构采用链表和哈希表共同实现,有序不重复。线程不安全,效率高。
LinkedHashSet
底层数据结构采用哈希表实现,无序不重复,线程不安全,效率高,可以存储null元素
HashSet
底层数据结构采用二叉树来实现,有序不重复
TreeSet
Set接口
Collection接口
Hashtable
LinkedHashMap
WeakHashMap
基于哈希表实现,非线程安全
非线程安全基于红黑树实现
TreeMap
Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复
Map接口
keySet()
entrySet()
Iterator迭代器
集合
13.集合
线程是操作系统能够运行计算调度的最小单位
所有线程共享一片相同的内存空间
每个线程都拥有自己独立的栈内存
什么是线程
线程是程序中一个单一的顺序控制流程;而多线程就是在单个程序中同时运行多个线程来完成不同的工作。
多线程
线程是进程的子集,一个进程可以有多个线程
不同的进度使用不同的内存空间
进程
继承Thread类
无fan返回值
实现Runnable接口
有返回值
实现Callable接口
java中实现线程方式
事先创建若干个可执行的线程放入一个池中,需要的时候从池中获取不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销
单线程线程池,只有一个线程工作
newSingleThreadExecutor
固定大小线程池,使用一次创一个线程直到最大
newFixedThreadPool
可缓存线程池,最大容量大于使用的,会回收,智能分配
newCachedThreadPool
无限大线程池,支持定时和周期执行任务需求
newScheduledThreadPool
单线程线程池,支持定时和周期执行任务需求
线程池
两个或两个以上的线程,争夺资源而造成的相互等待的现象
“一物一人”
资源一次只被一个线程使用
互斥条件
“你不给我,我不放”
请求资源被阻塞后,对以获得资源不释放
请求与保持条件
“我不放,你也抢不了我的”
未使用完之前,不允许被剥夺资源
不可剥夺条件
“这种尴尬的状态”
头尾相接的循环等待资源关系
循环等待条件
死锁的四个条件
阻止循环等待,标识和排序线程,线程请求资源按顺序
解决
死锁
start()被用于创建新线程
start()内部调用run()方法
run()只会在原来的线程中调用
start()和run()区别与联系
1.sleep给其他线程机会时,不考虑优先级,而yeild会考虑
sleep执行进入阻塞状态,yield执行进入就绪状态
sleep方法会抛出异常,而yield不会
sleep比yield有更好的移植性
sleep()和yield()区别
1.来自不同的类,wait是Object类,sleep是Thread类
2.关于锁的释放,wait会释放锁,sleep不会释放锁
3.使用的范围不同,wait必须在同步代码块中,sleep可以任何地方使用
4.是否需要捕获异常,wait不需要捕获异常,sleep必须捕获异常
sleep()和wait()区别
让三个线程顺序执行
sleep()睡眠
考虑优先级,使线程进入就绪状态
yield()礼让
wait()等待
join()插队
设置线程优先级
setPriority()
唤醒一个等待的线程,不确定哪个线程,由JVM确定唤醒,但与优先级无关
notify()
唤醒所有等待线程,所有线程竞争获得锁
notifyAll()
常用方法
线程执行流程
实现Serializable接口
对象流:将对象的内容进行流化
处理对象流的机制,流化后对象进行读写操作或在网络传输
序列化
stream结尾的都是字节流
reader和writer结尾的都是字符流
字节流按字节写,字符流按字符写
字节流和字符流
文件IO
1.加载驱动
2.创建连接
3.获取操作对象
4.执行SQL语句
5.处理结果集
6.释放资源
1.PreparedStatement代表预编译语句
2.PreparedStatement可以防止SQL注入
3.PreparedStatement会缓存SQL语句,执行效率快
Statement和PreparedStatement的区别
为数据库或其他持久化机制提供抽象接口的对象
不暴露底层持久化方案实现细节的前提下,提供各种数据访问操作
包括Data Accessor数据访问器和Data Object数据对象
什么是Dao模式
要么都成功,要么都失败
1.原子性
修改操作后和操作前系统状态一致
2.一致性
并发执行事务彼此无法看见中间态
事务A读取到事务B未提交的数据
脏读
事务A读取两次,数据不一样(第二次读事务A读取了事务B已提交修改后的数据和第一次不一致)
不可重复读
事务A重新执行查询,返回的行中有事务B提交的行
幻读
出现脏读、不可重复读、幻读
1.读未提交
出现不可重复读、幻读
2.读已提交
出现幻读
3.可重复读
4.可串行化
隔离级别
3.隔离性
事务完成后的持久化操作
4.持久性
事务的特性
JDBC
1.构造器不能被重写,但可以被重载(无参、有参)
2.String类不可以被继承,被final修饰了
3.java传参只有值传递
类变量、属于类,可以用类名访问
静态变量
对象成员变量
实例变量
4.静态变量和实例变量的区别
只读字符串,内容不可修改
Java5引入,线程不安全,可以修改字符串对象,效率高于StringBuffer
StringBuilder
线程安全,可以修改字符串对象
StringBuffer
5.String、StringBuilder、StringBuffer的区别
throw明确抛出哪个异常
throws声明抛出可能抛出的异常
6.throw和throws的区别
try指定预防异常程序
catch紧跟try后,捕获异常并处理
finally不管发生什么都会执行
7.try、catch、finally用法
ArithmeticException算数异常
ClassCastException类转化异常
IllegalArgumentException非法参数异常
IndexOutOfBoundsException下标超界异常
NullPointerException空指针异常
SecutiyException安全异常
8.运行时异常
是一个java修饰符
final
放在try...catch后,不管什么怎么样都运行
finally
Object类方法,调用在垃圾收集器将对象从内存清除之前做清理工作
finalize
9.final、finally、finalize区别
hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的
hashCode的存在主要是用于查找的快捷性
hashCode是用来在散列存储结构中确定对象的存储地址
两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同
“存放在同一个篮子里”
两个对象的hashCode相同,并不一定表示两个对象就相同
对象的equals方法被重写,那么对象的hashCode也要重写
equals和hashcode方法的理解
杂烩
equals() 的作用是用来判断两个对象是否相等。
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置
在这种情况下,该类的“hashCode() 和 equals() ”没有半毛钱关系的
这里的相等是指,通过equals()比较两个对象时返回true
如果两个对象相等,那么它们的hashCode()值一定相同
这是因为虽然p1 和 p2的内容相等,但是它们的hashCode()不等;所以,HashSet在添加p1和p2的时候,认为它们不相等。
如果两个对象hashCode()相等,它们并不一定相等
会创建“类对应的散列表”
1.同一个对象(没有发生过修改)无论何时调用hashCode()得到的返回值必须一样。
2.hashCode()的返回值相等的对象不一定相等,通过hashCode()和equals()必须能唯一确定一个对象。
3.一旦重写了equals()函数(重写equals的时候还要注意要满足自反性、对称性、传递性、一致性),就必须重写hashCode()函数。
原则
hashCode() 和 equals() 之间的关系
Java语言是一种单继承结构语言,Java中所有的类都有一个共同的祖先。这个祖先就是Object类
Object类的构造方法
Object()
可以命名任何你想要你的C函数
registerNatives()
用来另存一个当前存在的对象。只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常
clone()
该方法返回的是此Object对象的类对象/运行时类对象Class
getClass()
用来比较两个对象的内容是否相等
equals()
返回其所在对象的物理地址(哈希码值)
hashCode()
返回该对象的字符串表示
toString()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法
wait()
超过指定的时间量
wait(long timeout)
其他某个线程中断当前线程,或者已超过某个实际时间量
唤醒在此对象监视器上等待的单个线程
唤醒在此对象监视器上等待的所有线程
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法
finalize()
13种方法
Object有几种方法
如果你需要得到一个有序的结果时就应该使用TreeMap(因为HashMap中元素的排列顺序是不固定的)。除此之外,由于HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap。
HashMap 和 TreeMap 都是非线程安全
实现Comparator接口,重写compare方法
实现comparable接口,重新compareTo方法
如何让他降序
TreeMap中默认是按照升序进行排序的
如何决定使用 HashMap 还是 TreeMap
扩容造成死循环
在对table进行扩容到newTable后,需要将原来数据转移到newTable中,注意10-12行代码,这里可以看出在转移元素的过程中,使用的是头插法,也就是链表的顺序会翻转,这里也是形成死循环的关键点
jdk1.7
对HashMap进行了优化,在发生hash碰撞,不再采用头插法方式,而是直接插入链表尾部,因此不会出现环形链表的情况,但是在多线程的情况下仍然不安全
jdk1.8
在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。
HashMap是线程不安全
对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。核心作用是对象状态的保存与重建
客户端从文件中或网络上获得序列化后的对象字节流,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象
反序列化
对象序列化可以实现分布式对象。
java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据
序列化可以将内存中的类写入文件或数据库中
对象、文件、数据,有许多不同的格式,很难统一传输和保存
为什么需要序列化和反序列化
序列化与反序列化
ArrayList和ListkedList怎么选取
就是将相同hash值的对象组织成一个链表放在hash值对应的槽位
链表法
是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位
开放地址法
HashMap怎样解决hash冲突
进程是资源分配最小单位,线程是程序执行的最小单位
进程有自己独立的地址空间,每启动一个进程,系统都会为其分配地址空间,建立数据表来维护代码段、堆栈段和数据段,线程没有独立的地址空间,它使用相同的地址空间共享数据
CPU切换一个线程比切换进程花费小
创建一个线程比进程开销小;
线程之间通信更方便,同一个进程下,线程共享全局变量,静态变量等数据,进程之间的通信需要以通信的方式(IPC)进行
进程与线程的区别
1.7中采用数组+链表,1.8采用的是数组+链表/红黑树,即在1.7中链表长度超过一定长度后就改成红黑树存储
1.7是采用表头插入法插入链表,1.8采用的是尾部插入法
数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在
HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对
HashMap的实现原理
Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法的实现
Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int
Comparable和Comparator
第一章:java基础
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
Class.forName()
类名.class
对象.getClass()
获取Class对象
//拿到所有公有构造方法Constructor[] constructors = cz.getConstructors();
//拿到不管访问修饰符的构造方法
Constructor[] dconstructors = cz.getDeclaredConstructors();
1.Constructor constructor = cz.getConstructor();
用公有的构造函数创建对象
1.Constructor declaredConstructor = cz.getDeclaredConstructor();
2.declaredConstructor.setAccessible(true);
3.Teacher teacher = (Teacher) declaredConstructor.newInstance();
用私有的构造函数创建对象
获取Class对象的所有构造函数
Field[] fields = cz.getFields();
拿到所有的字段 不区分 公有和私有Field[] declaredFields = cz.getDeclaredFields();
Field sname = cz.getDeclaredField("age");// 拿到不区分访问修饰符的字段
获取Class对象所有成员变量的名字、类型
Method[] methods = cz.getDeclaredMethods();
1.Method method = cz.getDeclaredMethod("say");
2.method.setAccessible(true);
3.method.invoke(teacher);
执行Class的方法
获取Class所有的方法
反射
业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点
一个目标类就需要一个代理类
面向接口,增加一个方法,除了目标类需要实现该方法外,代理类也需要实现该的方法
缺点
静态代理:事先就知道代理什么
private Object target;
1.目标对象没具体
2.创建返回代理的方法
3.调用
集体实现类
创建返回代理的方法
实现接口MethodInterceptor
调用
具体实现
CGLib的动态代理
动态代理有哪些?
与静态代理的在接口声明所有方法相比,能集中处理一个方法,用反射invoke调用
还是摆脱不了接口代理,其根本是因为java不能多继承
动态代理:运行的时候才知道代理什么
两种都用了。
Spring中用了那种动态代理?
动态代理和静态代理得区别
一个类一个职责
1.单一职责原则
子类可以扩展父类的功能,但不能改变父类原有的功能
2.里氏替换原则
面向接口编程,依赖于抽象而不依赖于具体
3.依赖倒置原则
接口中,不存在子类用不到却必须实现的方法
4.接口隔离原则
一个类对自己依赖的类,知道的越少越好
5.迪米特法则
首先考虑合成和聚合,而不是继承
6.合成复用原则
java的设计原则,总原则:开闭原则
工厂类对实现同一接口的类进行实例化(在一个方法内)
普通简单工厂
工厂类里多个方法分别对实现同一接口的类进行实例化
多方法简单工厂
工厂类里多个静态方法分别对实现同一接口的类进行实例化,工厂方法直接调用
静态方法简单工厂
普通简单工厂模式
区别于普通简单工厂,把工厂做成接口,多个工厂实现类
工厂方法模式
区别于工厂方法一个抽象产品类而言,它是多个抽象产品类
抽象工厂像工厂,工厂方法像产品生产线
抽象工厂模式
单例类只能有一个实例
构造器私有化
当前类的成员变量
对外创建的公共方法
实现
类初始化的时候,创建对象
饿汉模式
第一次使用时才创建对象
懒汉模式
双重检查
线程安全的懒汉模式
枚举创建
单例模式
建造者模式
基本数据类型变量重新创建,引用类型不变
浅复制
彻底复制,基本数据类型和引用类型都不变
深复制
以y一个对象为原型,对其进行复制和克隆,产生和原对象类似的 新对象
原型模式
创建型模式
类的适配器
对象的适配器
接口的适配器
把类的接口转化成客户端期望的接口
适配器模式
装饰器模式
代理模式
外观模式
组合模式
享元模式
结构型模式
策略模式
模板方法模式
观察者模式
迭代子模式
责任链模式
命令模式
备忘录模式
状态模式
访问者模式
中介者模式
解释器模式
行为型模式
设计模式
23种设计模式
运行 Java 字节码文件(.class文件)的虚拟机
JVM栈
本地方法栈
类的对象放在heap(堆)中,所有的new 出的东西都放在堆中。
method区 存放所有的 1 类的字节码文件,2 静态变量(static变量),3 方法的模板。
方法区
程序计数器用来记录当前正在执行的指令.
程序计数器
包含
1.用户自定义加载器
2.启动类(根boot)加载器
3.扩展类(ext)加载器 jre/lib/ext/
4.应用程序(app)加载器 jre/rt.jar
类加载器
双亲委派机制
沙箱安全机制
JVM加载类
JVM的分类
内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
代码中存在死循环或循环产生过多重复的对象实体;
使用的第三方软件中的BUG;
启动参数内存值设定的过小;
原因
第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
解决方法
内存溢出
1.GC是垃圾回收机制,会自动清理堆中已死亡或长期没使用的对象。也可以调用System.gc()或Runtime.getRuntime().gc()来请求清理。
2.垃圾回收机制出于安全性考虑,可以防止内存泄漏,减少程序员的工作量
引用计数法
复制算法
根搜索算法
标记清除法
标记压缩整理
分代收集
3.垃圾回收机制的常用算法
内存不足也不会回收
强引用
内存足不回收,内存不足回收
软引用
被GC扫描到就直接回收
弱引用
同没有引用类似,被扫描到后会被回收,用于跟踪对象被回收的活动
必须与引用队列联用
虚引用
4.垃圾回收时,Java对象的引用类型
GC机制
垃圾回收机制
JVM和GC机制
Java8的新特性
先不初始化单例,等第一次使用的时候再初始化,即“懒加载”
好处是更启动速度快、节省资源,一直到实例被第一次访问,才需要初始化单例
小坏处是写起来麻烦,大坏处是线程不安全,if语句存在竞态条件
基础的饱汉
最粗暴的犯法是用synchronized关键字修饰getInstance()方法,这样能达到绝对的线程安全。
好处是写起来简单,且绝对线程安全
坏处是并发性能极差,事实上完全退化到了串行
性能不敏感的场景建议使用。
饱汉 - 变种 1
变种2是“臭名昭著”的DCL 1.0。
针对变种1中单例初始化后锁仍然无法避开的问题,变种2在变种1的外层又套了一层check,加上synchronized内层的check,即所谓“双重检查锁”(Double Check Lock,简称DCL
DCL仍然是线程不安全的,由于指令重排序,你可能会得到“半个对象”,即”部分初始化“问题
饱汉 - 变种 2
变种3专门针对变种2,可谓DCL 2.0。
instance上增加了volatile关键字
多线程环境下,变种3更适用于性能敏感的场景
饱汉 - 变种 3
饱汉模式
类加载时初始化单例,以后访问时直接返回即可
好处是天生的线程安全(得益于类加载机制),写起来超级简单,使用时没有延迟
坏处是有可能造成资源浪费(如果类加载后就一直不使用单例的话)
核心仍然是静态变量,足够方便和线程安全;通过静态的Holder类持有真正实例,间接实现了懒加载
相对于饿汉模式,Holder模式仅增加了一个静态内部类的成本,与饱汉的变种3效果相当(略优),都是比较受欢迎的实现方式。同样建议考虑
public class Singleton3 { private static class SingletonHolder { private static final Singleton3 singleton = new Singleton3(); private SingletonHolder() { } } private Singleton3() { } public static Singleton3 getInstance() { return SingletonHolder.singleton; }}
Holder模式
用枚举实现单例模式,相当好用,但可读性是不存在的。
枚举模式
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器加1;当引用失效时,计数器值减1;任何时刻计数器为0的对象就是不能再被引用的
引用计数算法
可达性分析算法的基本思路是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Root没有任何引用链相连时,则证明此对象是不可用的
可达性分析算法
如何判断一个对象是否存活
第二章:java高级特性
数据定义
数据组织、存储和管理
数据操纵(增删改查)
数据库的事务管理和运行管理
数据库的建立和维护功能
其他功能
DBMS主要功能
虚拟表、存储的查询
create view 视图名 as select 字段名 from 表名;
创建视图的表叫基表
简化用户操作,注意力集中在自己关心的操作上
隐藏字段,对数据有一定的安全性
适当使用视图,便于查询
对重构数据库有一定的逻辑独立性
1.如果视图和基表的行是一一对应的,则可以
2.如果视图的行是基表多行计算获得的,则不行
视图可以对基表进行修改、删除和添加么?
视图
包括逻辑判断的sql语句集合
经过预编译,存于数据库中
调用存储过程(有参、无参)名字执行
DELIMITER // CREATE PROCEDURE myproc(OUT s int) BEGIN SELECT COUNT(*) INTO s FROM students; END //DELIMITER ;
简化复杂业务逻辑,可重复用
隐藏底层细节
降低网络通信量
可以设置访问权限提高安全性
预编译提高效率
可移植性差,不能跨多个数据库
服务器压力增加,维护更难
存储过程
1.使用exists代替in
虽然索引提高查询效率,但是会降低insert和update的效率
2.使用表的索引不超过6个
提高查询效率,减少储存开销
3.尽量使用数字型字段
使用char(10),表示存储的字符将占10个字节(包括7个空字符)
存储字符串'abc'
char和varcahr的区别
节省存储空间
4.尽量使用varchar/nvarchar代替char/nchar
5.尽量使用>=,不使用>
使用where、group by或建立索引
6.避免使用全表扫描
否则索引失效,全表扫描
7.避免使用where使用不等号或or、in、not in
8.避免使用模糊查询的%xxx%和%xxx(xxx%不会)
9.避免使用where子句使用参数,字段进行表达式操作或者函数操作
10.不使用select * 查询所有,而是列出所有字段
11.首先考虑在where和order by涉及的列建立索引
SQL语句性能优化/索引优化
选取最适用的字段属性
使用连接(join)代替子查询
使用联合查询(union)代替手动创建临时表
使用事务
使用外键
使用索引
优化SQL语句
MySQL数据库优化
InnoDB是默认的MySQL引擎
具有提交、回滚和崩溃恢复能力的事物安全
在SQL查询中,可以自由地将InnoDB类型的表和其他MySQL的表类型混合起来,甚至在同一个查询中也可以混合
InnoDB是为处理巨大数据量的最大性能设计。它的CPU效率可能是任何其他基于磁盘的关系型数据库引擎锁不能匹敌的
InnoDB支持外键完整性约束,存储表中的数据时,每张表的存储都按主键顺序存放,如果没有显示在表定义时指定主键,InnoDB会为每一行生成一个6字节的ROWID,并以此作为主键。
InnoDB将它的表和索引在一个逻辑表空间中
InnoDB
MyISAM拥有较高的插入、查询速度,但不支持事务
大文件(达到63位文件长度)在支持大文件的文件系统和操作系统上被支持
NULL被允许在索引的列中,这个值占每个键的0~1个字节
可以把数据文件和索引文件放在不同目录
当把删除和更新及插入操作混合使用的时候,动态尺寸的行产生更少碎片。这要通过合并相邻被删除的块,以及若下一个块被删除,就扩展到下一块自动完成
每个MyISAM表最大索引数是64,这可以通过重新编译来改变。每个索引最大的列数是16
MyISAM
MEMORY存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问
MEMORY表的每个表可以有多达32个索引,每个索引16列,以及500字节的最大键长度
MEMORY不支持BLOB或TEXT列
MEMORY支持AUTO_INCREMENT列和对可包含NULL值的列的索引
当不再需要MEMORY表的内容时,要释放被MEMORY表使用的内存,应该执行DELETE FROM或TRUNCATE TABLE,或者删除整个表(使用DROP TABLE)
MEMORY
Archive
1. InnoDB 支持事务,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;
2. InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;
3. InnoDB 是聚集索引,MyISAM 是非聚集索引。聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
4. InnoDB 不保存表的具体行数,执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
5. InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。这也是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;
InnoDB和MyISAM的区别(重点)
MyISAM:读写插入为主,比如博客,新闻门户InnoDB:更新删除频率高,或者数据完整性;并发性高,支持事务和外键,比如自动化办公系统
Mysql(核心)存储引擎
字段具有原子性,不可再拆分
第一范式
在第一范式的基础上,数据库每一行可以被唯一的区分,有主键
第二范式
订单表里除了有订单id,还有书籍id,不要在这个订单表里放书籍名称,不然每多一个书籍id,就必须带上书籍名称,造成数据冗余。书籍id直接依赖于订单id,但是书籍名称不直接依赖于订单id。
在第二范式的基础上,不产生传递依赖关系,每个字段直接依赖于主键,而是间接依赖
第三范式
三大范式
行级别
关注修改
表级别
关注插入和删除
事务A重新执行查询,返回的行集合中有事务B提交的行
加快查询表中数据,不至于扫描整个表
不能创建在视图上
索引是数据结构
这种索引叫做非聚集索引
主索引(数据库为主键自动创建的索引)要求key是唯一的,而辅助索引(用户创建的索引)的key可以重复
MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址,按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址
这种索引叫做聚集索引
为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大
用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一棵B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择
InnoDB的数据文件本身就是索引文件,这棵树的叶节点data域保存了完整的数据记录,InnoDB的辅助索引data域存储相应记录主键的值而不是数据记录地址,首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录
InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同
不同存储引擎对索引的实现方式是不同的
索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上
普通索引
唯一索引
主键索引
组合索引
全文索引
逻辑上
分类
若左子树不空,则左子树上所有节点的值均小于它的根节点的值
若右子树不空,则右字数上所有节点的值均大于它的根节点的值
它的左、右子树也分别为二叉排序数(递归定义)
极端情况会出现所有节点都位于同一侧,直观上看就是一条直线,那么这种查询的效率就比较低了,因此需要对二叉树左右子树的高度进行平衡化处理,于是就有了平衡二叉树(Balenced Binary Tree)
二叉排序树
这棵树的各个分支的高度是均匀的,它的左子树和右子树的高度之差绝对值小于1
平衡二叉树
B树事实上是一种平衡的多叉查找树,也就是说最多可以开m个叉(m>=2),我们称之为m阶b树
每个节点至多可以拥有m棵子树。
根节点,只有至少有2个节点
非根非叶的节点至少有的Ceil(m/2)个子树(Ceil表示向上取整,图中5阶B树,每个节点至少有3个子树,也就是至少有3个叉)
从根到叶子的每一条路径都有相同的长度
从根节点依次比较每个结点,因为每个节点中的关键字和左右子树都是有序的,所以只要比较节点中的关键字,或者沿着指针就能很快地找到指定的关键字,如果查找失败,则会返回叶子节点,即空指针
从根节点P开始,K的位置在P之前,进入左侧指针。左子树中,依次比较C、F、J、M,发现K在J和M之间。沿着J和M之间的指针,继续访问子树,并依次进行比较,发现第一个关键字K即为指定查找的值
B树
有n棵子树的节点含有n个关键字(也有认为是n-1个关键字)。
所有的关键字全部存储在叶子节点上,且叶子节点本身根据关键字自小而大顺序连接。
非叶子节点可以看成索引部分,节点中仅含有其子树(根节点)中的最大(或最小)关键字
和B树的区别
所有关键字都存储在叶子节上,且链表中的关键字恰好是有序的。不可能非叶子节点命中返回。非叶子节点相当于叶子节点的索引,叶子节点相当于是存储(关键字)数据的数据层。更适合文件索引系统。
B+树
在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。做这个优化的目的是为了提高区间访问的性能,例如图4中如果要查询key为从18到49的所有数据记录,当找到18后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率
一般在数据库系统或文件系统中使用
优化的B+树:增加了顺序访问指针
拓展
索引
mysql关键字limit
sqlserver关键字top
oracle关键字rownum
分页语句
from>where>group by>having>select>order by
执行顺序
Select语句的执行顺序
avg()
count()
max()
min()
sum()
group by()
聚合函数
以左表为基准,左表全显示,右表匹配就显示,否则显示null
左外连接
以右表为基准,右表全显示,左表匹配就显示,否则显示null
右外连接
外连接
只显示匹配到的行
内连接
先左外连接,后右外连接
全连接
自连接
查询语句连接
其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全局唯一的 id 来支持
这个就是说你的系统里每次得到一个 id,都是往一个库的一个表里插入一条没什么业务含义的数据,然后获取一个数据库自增的一个 id。拿到这个 id 之后再往对应的分库分表里去写入
这个方案的好处就是方便简单,谁都会用;缺点就是单库生成自增 id,要是高并发的话,就会有瓶颈的;如果你硬是要改进一下,那么就专门开一个服务出来,这个服务每次就拿到当前 id 最大值,然后自己递增几个 id,一次性返回一批 id,然后再把当前最大 id 值修改成递增几个 id 之后的一个值;但是无论如何都是基于单个数据库
并发不高,但是数据量太大
适合的场景
数据库自增 id
现在有 8 个服务节点,每个服务节点使用一个 sequence 功能来产生 ID,每个 sequence 的起始 ID 不同,并且依次递增,步长都是 8
在用户防止产生的 ID 重复时,这种方案实现起来比较简单,也能达到性能目标。但是服务节点固定,步长也固定,将来如果还要增加服务节点,就不好搞了
适用的场景
设置数据库 sequence 或者表自增字段步长
基于数据库的实现方案
好处就是本地生成,不要基于数据库来了;不好之处就是,UUID 太长了、占用空间大,作为主键性能太差了;更重要的是,UUID 不具有有序性,会导致 B+ 树索引在写的时候有过多的随机写操作(连续的 ID 可以产生部分顺序写),还有,由于在写的时候不能产生有顺序的 append 操作,而需要进行 insert 操作,将会读取整个 B+ 树节点到内存,在插入这条记录后会将整个节点写回磁盘,这种操作在记录占用空间比较大的情况下,性能下降明显
如果你是要随机生成个什么文件名、编号之类的,你可以用 UUID,但是作为主键是不能用 UUID 的
问题是,并发很高的时候,比如一秒并发几千,会有重复的情况,这个是肯定不合适的。基本就不用考虑了
一般如果用这个方案,是将当前时间跟很多其他的业务字段拼接起来,作为一个 id,如果业务上你觉得可以接受,那么也是可以的。你可以将别的业务字段值跟当前时间拼接起来,组成一个全局唯一的编号
获取系统当前时间
是 twitter 开源的分布式 id 生成算法
snowflake 算法
分库分表之后,id 主键如何处理
不区分
utf8_general_ci,表示不区分大小写
utf8_general_cs,表示区分大小写,也可以使用utf8_bin,表示二进制比较,同样也区分大小写
直接修改sql语句,在要查询的字段前面加上binary关键字
如何解决需要区分英文大小写的场景
MySQL查询字段区不区分大小写
第三章:关系型数据库
运行时异常都是RuntimeException类及其子类异常
这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生
运行时异常
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类
对于这种异常,JAVA编译器强制要求我们必需对出现的这些异常进行catch并处理,否则程序就不能编译通过
非运行时异常
运行时异常与非运行时异常的区别
关系型数据天然就是表格式的
非关系型数据通常存储在数据集中,就像文档、键值对或者图结构
数据存储方式不同
SQL数据库是纵向扩展
NoSQL数据库是横向扩展
扩展方式不同
SQL数据库支持对事务原子性细粒度控制,并且易于回滚事务
NoSQL数据库也可以使用事务操作,但稳定性方面没法和关系型数据库比较
对事务性的支持不同
关系型数据库和非关系型区别
off
all
一般使用BigDecimal来解决商业运算上丢失精度的问题的时候,声明BigDecimal对象的时候一定要使用它构造参数为String的类型的构造器
其他的如BigDecimal b = new BigDecimal(1)这种,还是会发生精度丢失的问题
BigDecimal一定不会丢失精度吗?
1、能通过less命令打开文件,通过Shift+G到达文件底部,再通过?+关键字的方式来根据关键来搜索信息。
3、能通过vi来编辑文件。
4、能通过chmod来设置文件的权限。
目前大多数的互联网项目,都是部署在Linux上,也就是说,日志都是在Linux,下面归纳些实际的Linux操作。
疑难点
面试宝典
0 条评论
回复 删除
下一页