**面试高频问题知识点总结**
Java基础
Java集合类里面基本的接口有哪些?
ArrayList和Vector的区别
Iterator和ListIterator的区别
Enumeration和Iterator的区别?
HashMap与HashTable的区别?
HashMap与HashSet的底层实现?
我们能否让HashMap线程同步?
你知道HashMap的工作原理吗?你知道HashMap的get()方法的工作原理吗?
当两个对象的hashcode相同会发生什么?
List与Map的区别
List, Set, Map是否继承自Collection接口?
final,finally,finalize 三者区别
StringBuffer StringBuilder String 区别
接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?抽象类中是否可以有静态的main方法?
Java中实现多态的机制是什么?
abstractclass(抽象类)和interface(接口)语法上有什么区别?
字节流与字符流的区别
什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用。
Java子父类间静态代码块、构造代码块、构造方法的执行顺序
异常
运行时异常与一般异常有何异同?
error和exception有什么区别?
简单说说Java中的异常处理机制的简单原理和应用。
Java 中,throw 和 throws 有什么区别
JVM
Java 中堆和栈有什么区别?
描述一下JVM加载class文件的原理机制?
Jvm怎么判断对象可以回收了?
heap和stack有什么区别。
GC是什么?为什么要有GC?
垃圾回收的优点和原理。并考虑2种回收机制。
垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
servlet
说一说Servlet的生命周期?
Servlet API中forward()与redirect()的区别?
request.getAttribute()和 request.getParameter()有何区别?
web.xml 中的listener、 filter、servlet 加载顺序
jsp静态包含和动态包含的区别
MVC的各个部分都有那些技术来实现?如何实现?
jsp有哪些内置对象?作用分别是什么?
什么是cookie?Session和cookie有什么区别?
jsp和servlet的区别、共同点、各自应用的范围?
tomcat容器是如何创建servlet类实例?用到了什么原理?
jsp和servlet的区别、共同点、各自应用的范围?
servlet的三大作用域对象:
jsp的四大作用域对象:
jsp的九大内置对象:
HTTP
请求报文的组成:
响应报文的组成:
响应:
get和post方法的区别
javaweb
JDBC访问数据库的基本步骤是什么?
说说preparedStatement和Statement的区别
说说事务的概念,在JDBC编程中处理事务的步骤。
数据库连接池的原理。为什么要使用连接池。
JDBC的脏读是什么?哪种数据库隔离级别能防止脏读?
什么是幻读,哪种隔离级别可以防止幻读?
JDBC的DriverManager是用来做什么的?
execute,executeQuery,executeUpdate的区别是什么?
SQL查询出来的结果分页展示一般怎么做?
JDBC的ResultSet是什么?
spring
spring的事物
Spring MVC运行原理
使用Spring框架的好处是什么?
spring的概述
什么是Spring的依赖注入?有哪些方法进行依赖注入
ApplicationContext通常的实现是什么?
什么是Spring beans?
解释Spring支持的几种bean的作用域。
解释Spring框架中bean的生命周期。
在 Spring中如何注入一个java集合?
Spring框架的事务管理有哪些优点?
设计模式
单例模式
工厂模式
代理设计模式
REST/RESTful(也算一种设计模式)
观察者模式
装饰器模式
spring MVC的运行原理
Spring MVC、struts1和struts2区别
spring mvc的自定义拦截器的实现?
spring mvc的上传图片是怎么实现的?
spring 事务的传播行为类型?
Spring MVC整合
ORM值持久层框架-Hibernate知识点总结
什么是ORM?
get 和 load的区别
Hibernate的运行原理
在hibernate进行多表查询每个表中各取几个字段,也就是说查询出来的结果集没有一个实体类与之对应如何解决?
介绍一下Hibernate的二级缓存
谈谈你对Hibernate的理解。
Hibernate的一对多和多对一双向关联的区别??
Hibernate是如何延迟加载?
Hibernate中SessionFactory是线程安全的吗?Session是线程安全的吗(两个线程能够共享同一个Session吗)?
Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的?有什么区别?
mybatis
mybatis的优缺点?
MyBatis中使用#和$书写占位符有什么区别?
解释一下MyBatis中命名空间(namespace)的作用。
MyBatis中的动态SQL是什么意思?
JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?(jdbc与mybatis的区别?)
MyBatis与Hibernate有哪些不同?
简单的说一下MyBatis的一级缓存和二级缓存?
并发篇
Java中的同步集合与并发集合有什么区别?
什么是线程池? 为什么要使用它?
什么是线程池? 为什么要使用它?
如何避免死锁?
notify()和notifyAll()有什么区别?
数据库篇
MyISAM和InnoDB的主要区别和应用场景
创建索引的条件和注意事项
前端问题
AJAX有哪些有点和缺点?
jQuery 中ajax异步调用的四种方式
Ajax的实现流程是怎样的?
多线程
什么是线程?
线程和进程有什么区别?
如何在Java中实现线程?
线程的概述
Java 关键字volatile 与 synchronized 作用与区别?
有哪些不同的线程生命周期?
你对线程优先级的理解是什么?
什么是死锁(Deadlock)?如何分析和避免死锁?
什么是线程安全?Vector是一个线程安全类吗?
Java中如何停止一个线程?
什么是ThreadLocal?
Sleep()、suspend()和wait()之间有什么区别?
什么是线程饿死,什么是活锁?
什么是Java Timer类?如何创建一个有特定时间间隔的任务?
Java中的同步集合与并发集合有什么区别?
同步方法和同步块,哪个是更好的选择?
什么是线程池? 为什么要使用它?
Java中invokeAndWait 和 invokeLater有什么区别?
多线程中的忙循环是什么?
说一下你了解的几种进程间的通信方式
技术点话术
redis
redis-cluster架构图
控制redis的方法
redis简介与认识?
redis的应用场景
redis的缓存同步
redis 热数据的问题的解决方案
RDB和AOF的区别和联系
缓存
什么是缓存穿透?
如何避免?
什么是缓存雪崩?
如何避免?
分布式缓存系统
缓存一致性问题
缓存数据的淘汰
怎么解决高并发?
solr的话术
什么是solr?
为什么用solr?
dubbo的话术
tomcat
tomcat集群,如何实现session共享?
Tomcat优化
jdk1.7与1.8的区别,有哪些区别?
session与cookie的区别
微信扫描支付的实现话术
大型网站在架构上应当考虑哪些问题?
FastDFS为什么要结合Nginx?
支付话术
面试话术
redis应用场景话术
webService描述
MySQL、Redis、MongoDB对比
RabbitMQ
为什么使用消息队列?
使用消息队列有什么缺点?
消息队列如何选型?
项目有关的问题
怎么保证APP接口传数据的安全性
如何确保服务端的接口调用安全?
Java基础
Java集合图谱
2.3 Java集合图谱-w373
JAVA集合类图
Java集合类里面基本的接口有哪些?
Collection:代表一组对象,每一个对象都是它的子元素。
Set:不包含重复元素的Collection。
List:有顺序的collection,并且可以包含重复元素。
Map:可以把键(key)映射到值(value)的对象,键不能重复。
ArrayList和Vector的区别
这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合
中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,并且其中的
数据是允许重复的,这是与HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其
中的元素,也不允许有重复的元素。
ArrayList与Vector的区别主要包括两个方面:.
(1)同步性:
Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它
的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程
安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编
写线程安全的代码。
(2)数据增长:
ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需
要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存
储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原
来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。
ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提
供设置增长空间的方法。
总结:即Vector增长原来的一倍,ArrayList增加原来的0.5倍。
Iterator和ListIterator的区别
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向遍历也可以后向遍历。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加、替换元素,获取前一个和后一个元素的索引等等。
Enumeration和Iterator的区别?
java中的集合类都提供了返回Iterator的方法,就是迭代器,它和Enumeration(枚举)的主要区别其实就
是Iterator可以删除元素,但是Enumration却不能。
使用Iterator来遍历集合时,应使用Iterator的remove()方法来删除集合中的元素,使用集合的remove()
方法将抛出ConcurrentModificationException异常。
Enumeration接口的功能和Iterator接口的功能是重复的。此外,Iterator 接口添加了一个可选的移除操
作,并使用较短的方法名。新的实现应该优先考虑使用Iterator接口而不是Enumeration接口。
Enumeration速度是Iterator的2倍,同时占用更少的内存。但是,Iterator远远比Enumeration安全,
因为其他线程不能够修改正在被iterator遍历的集合里面的对象。
HashMap与HashTable的区别?
1.HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于
2.HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable。
3.HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。HashMap把
Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法
容易让人引起误解。
4.Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不
需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。
5.Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。
HashMap与HashTable主要从三方面来说。
5.1 历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java1.2引进的Map接口的一个实现
5.2 同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
5.3 值:只有HashMap可以让你将空值作为一个表的条目的key或value
HashMap与HashSet的底层实现?
HashSet底层通过包装HashMap来实现,HashSet在添加一个值的时候,实际上是将此值作为HashMap中的key来进行保存。
HashMap的底层实现是通过初始化化一个Entry数组来实现key、value的保存。
在HashMap的Entry中有四个变量,key、value、hash、next,其中next用于在hash方法添加值冲突时候,所指向的下一个值。
在HashMap中添加值步骤
第一步,对key的hashcode进行hash计算,获取应该保存到数组中的index。
第二步,判断index所指向的数组元素是否为空,如果为空则直接插入。
第三步,如果不为空,则依次查找entry中next所指定的元素,判读key是否相等,如果相等,则替换久的值,返回。
第四步,如果都不相等,则将此链表头元素赋值给待插入entry的next变量,让后将待插入元素插入到entry数组中去。
map遍历方法有两种,keyset 和Map.Entry
我们能否让HashMap线程同步?
HashMap可以通过下面的语句进行同步:
Map Collections.synchronizedMap(Map m)
你知道HashMap的工作原理吗?你知道HashMap的get()方法的工作原理吗?
HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)
从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的
hashCode用于找到bucket位置来储存Entry对象。
当两个对象的hashcode相同会发生什么?
因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个
Entry(包含有键值对的Map.Entry对象)会存储在链表中
List与Map的区别
一个是存储单列数据的集合,另一个是存储键和值这样的双列数据的集合,List中存储的数据是有顺序,并且
允许重复;
Map中存储的数据是没有顺序的,其键是不能重复的,它的值是可以有重复的。
List, Set, Map是否继承自Collection接口?
List,Set是,Map不是
final,finally,finalize 三者区别
Final是一个修饰符:
当final修饰一个变量的时候,变量变成一个常量,它不能被二次赋值
当final修饰的变量为静态变量(即由static修饰)时,必须在声明这个变 量的时候给它赋值
当final修饰方法时,该方法不能被重写
当final修饰类时,该类不能被继承
Final不能修饰抽象类,因为抽象类中会有需要子类实现的抽 象方法,(抽 象类中可以有抽象方法,也可以有普通方法,当一个抽象类中没有抽象方 法时,这个抽象类也就没有了它存在的必要)
Final不能修饰接口,因为接口中有需要其实现类来实现的方法
Finally:
Finally只能与try/catch语句结合使用,finally语句块中的语句一定会执行, 并且会在return,continue,break关键字之前执行
finalize:
Finalize是一个方法,属于java.lang.Object类,finalize()方法是GC (garbage collector垃圾回收)运行机制的一部分,finalize()方法是在 GC清理它所从属的对象时被调用的
StringBuffer StringBuilder String 区别
String 字符串常量 不可变 使用字符串拼接时是不同的2个空间
StringBuffer 字符串变量 可变 线程安全 字符串拼接直接在字符串后追加
StringBuilder 字符串变量 可变 非线程安全 字符串拼接直接在字符串后追加
1.StringBuilder执行效率高于StringBuffer高于String.
2.String是一个常量,是不可变的,所以对于每一次+=赋值都会创建一个新的对象,StringBuffer和
StringBuilder都是可变的,当进行字符串拼接时采用append方
法,在原来的基础上进行追加,所以性能比String要高,又因为StringBuffer是线程安全的而
stringBuilder 是线程非安全的,所以StringBuilder的效率高于StringBuffer.
3.对于大数据量的字符串的拼接,采用StringBuffer,StringBuilder.
接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?抽象类中是否可以有静态的main方法?
接口可以继承接口。抽象类可以实现(implements)接口,抽象类可以继承具体类。抽象类中可以有静态的
main方法。
备注:只要明白了接口和抽象类的本质和作用,这些问题都很好回答,你想想,如果你是java语言的设计
者,你是否会提供这样的支持,如果不提供的话,有什么理由吗?如果你没有道理不提供,那答案就是肯定的了。
只要记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。
Java中实现多态的机制是什么?
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才
动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用
变量的类型中定义的方法。
abstractclass(抽象类)和interface(接口)语法上有什么区别?
抽象类可以有构造方法,接口中不能有构造方法。
抽象类中可以有普通成员变量,接口中没有普通成员变量
抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然
eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
抽象类中可以包含静态方法,接口中不能包含静态方法
抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是publicstatic final类型,并且默认即为publicstatic final类型。
一个类可以实现多个接口,但只能继承一个抽象类。
字节流与字符流的区别
要把一段二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一段二进制数据,不管输入
输出设备是什么,我们要用统一的方式来完成这些操作,用一种抽象的方式进行描述,这个抽象描述方式起名为
IO流,对应的抽象类为OutputStream和InputStream,不同的实现类就代表不同的输入和输出设备,它们都
是针对字节进行操作的。
计算机中的一切最终都是二进制的字节形式存在。对于经常用到的中文字符,首先要得到其对应的字
节,然后将字节写入到输出流。读取时,首先读到的是字节,可是我们要把它显示为字符,我们需要将字节转换
成字符。由于这样的需求很广泛,Java专门提供了字符流包装类。
底层设备永远只接受字节数据,有时候要写字符串到底层设备,需要将字符串转成字节再进行写入。字
符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为我们向IO设备
写入或读取字符串提供了一点点方便。
字符向字节转换时,要注意编码的问题,因为字符串转成字节数组,其实是转成该字符的某种编码的字
节形式,读取也是反之的道理。
什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用。
我们有时候将一个java对象变成字节流的形式传出去或者从一个字节流中恢复成一个java对象,例如,要将
java对象存储到硬盘或者传送给网络上的其他计算机,这个过程我们可以自己写代码去把一个java对象变成某 个格式的字节流再传输。但是,jre本身就提供了这种支持,我们可以调用OutputStream的writeObject方
法来做,如果要让java帮我们做,要被传输的对象必须实现serializable接口,这样,javac编译时就会进
行特殊处理,编译的类才可以被writeObject方法操作,这就是所谓的序列化。需要被序列化的类必须实现
Serializable接口,该接口是一个mini接口,其中没有需要实现方法,implements Serializable只是
为了标注该对象是可被序列化的。
例如,在web开发中,如果对象被保存在了Session中,tomcat在重启时要把Session对象序列化到硬
盘,这个对象就必须实现Serializable接口。如果对象要经过分布式系统进行网络传输,被传输的对象就必须
实现Serializable接口。
Java子父类间静态代码块、构造代码块、构造方法的执行顺序
一个简单的静态测试程序:
class FatherStaticTest
{
static//静态代码块
{
System.out.println(“执行父类的静态代码块。”);
}
FatherStaticTest()//父类不带参数的构造方法
{
System.out.println("执行父类的不带参数的构造方法。");
}
FatherStaticTest(int num)//父类带参数的构造方法
{
System.out.println("执行父类的带参数的构造方法。");
}
//构造代码块
{
System.out.println("执行父类的构造代码块。");
}
}
class SonStaticTest extends FatherStaticTest
{
static
{
System.out.println(“执行子类的静态代码块。”);
}
SonStaticTest()
{
System.out.println("执行子类的不带参数的构造方法。");
}
SonStaticTest(int num)
{
System.out.println("执行子类的带参数的构造方法。");
}
{
System.out.println("执行子类的构造代码块。");
}
}
class StaticTest
{
public static void main(String[] args)
{
new SonStaticTest();
}
}
测试2
class FatherStaticTest
{
static
{
System.out.println(“执行父类的静态代码块。”);
}
FatherStaticTest()
{
System.out.println("执行父类的不带参数的构造方法。");
}
FatherStaticTest(int num)
{
System.out.println("执行父类的带参数的构造方法。");
}
FatherStaticTest(String str)
{
System.out.println("执行父类的带参数的构造方法。");
}
{
int i = 1;
int j = 2;
int sum = (i+j);
System.out.println("执行父类的构造代码块。"+sum);
}
{
int i = 1;
int j = 2;
int sum = (i+j);
System.out.println("执行父类的构造代码块。"+sum);
}
{
int m = 3;
int n = 4;
int sum = (m+n);
System.out.println("执行父类的构造代码块。"+sum);
}
}
class SonStaticTest extends FatherStaticTest
{
static
{
System.out.println(“执行子类的静态代码块。”);
}
SonStaticTest()
{
System.out.println("执行子类的不带参数的构造方法。");
}
SonStaticTest(int num)
{
super(7);
System.out.println("执行子类的带参数的构造方法。");
}
SonStaticTest(String str)
{
super(7);
System.out.println("执行子类的带参数的构造方法。");
}
{
int i = 1;
int j = 2;
int sum = (i+j);
System.out.println("执行子类的构造代码块。"+sum);
}
{
int i = 1;
int j = 2;
int sum = (i+j);
System.out.println("执行子类的构造代码块。"+sum);
}
{
int m = 3;
int n = 4;
int sum = (m+n);
System.out.println("执行子类的构造代码块。"+sum);
}
}
class StaticTest
{
public static void main(String[] args)
{
new SonStaticTest("a");
}
}
结论:
执行顺序:父类的静态代码块—>子类的静态代码块—>父类的构造代码块—>父类的构造方法—>子
类的构造代码块—>子类的构造方法。
静态代码块随类的加载而执行,只执行一次,优先于main方法,用于初始化整个类。
构造代码块是给一个类的所有的对象进行初始化,可执行多次。只要对象一建立,就会调用构造代码块。构
造代码块可以重复,可以有多份。
构造方法是给与之对应的对象进行初始化,有针对性。构造方法要么带参数,要么不带参数。当类中没有显
式的构造方法时,jvm会默认执行一个不带参数的构造方法。同一个类中不能出现两个或两个以上相同的构造方
法(方法名和参数列表都相同)。
在子类的所有构造方法中如果没有显式的super语句,则默认第一条语句为隐式的super语句:super();
会访问父类的不带参数的构造方法。当父类中只有带参数的构造方法时,子类必须用显式的带参数的super语句
访问父类的构造方法。若显示的super语句不带参数,则编译失败。当父类中只有显式的不带参数的构造方法
时,子类必须用显示的不带参数的super语句访问父类的构造方法。否则,编译失败。
在创建子类的实例对象时未传入参数,若子类只有带参数的构造方法则编译失败。若子类有不带参数的构造
方法,则执行子类的不带参数的构造方法。若子类没有构造方法,则执行隐式的不带参数的构造方法。
在创建子类的实例对象时传入参数:若子类有带参数的构造方法,则执行子类的带参数的构造方法;若子类
没有带参数的构造方法或者没有构造方法,则编译失败。
异常
运行时异常与一般异常有何异同?
异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,
是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛
出未被捕获的运行时异常。
error和exception有什么区别?
表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情
况。exception表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
简单说说Java中的异常处理机制的简单原理和应用。
异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活
中的事件可以包含事件发生的时间、地点、人物、情节等信息,可以用一个对象来表示,Java使用面向对象的 方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。
Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为
java.lang.Throwable,Throwable下面又派生了两个子类:
Error和Exception,Error表示应用程序本身无法克服和恢复的一种严重问题,程序只有奔溃了,
例如,说内存溢出和线程死锁等系统问题。
Exception表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常:
系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无
法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件挂掉,例如,数组脚本越界
(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);
普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空
间不够,发生这样的异常后,程序不应该死掉。
java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try…catch处理或用throws声
明继续抛给上层调用方法处理,所以普通异常也称为checked异常,而系统异常可以处理也可以不处理,所以,
编译器不强制用try…catch处理或用throws声明,所以系统异常也称为unchecked异常。
Java 中,throw 和 throws 有什么区别
throw 用于抛出 java.lang.Throwable 类的一个实例化对象,意思是说你可以通过关键字 throw 抛出
一个Exception,如:
throw new IllegalArgumentException(“XXXXXXXXX″)
而throws 的作用是作为方法声明和签名的一部分,方法被抛出相应的异常以便调用者能处理。Java 中,任
何未处理的受检查异常强制在 throws 子句中声明。
JVM
Java 中堆和栈有什么区别?
JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分
配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
"栈":在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码
块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为
该变量分配的内存空间,该内存空间可以立即被另作它用。
“堆”:堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收 器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值
等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用
栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。
描述一下JVM加载class文件的原理机制?
JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader是一个重要的Java运行时
系统组件。它负责在运行时查找和装入类文件的类。
Jvm怎么判断对象可以回收了?
1、 对象没有引用
2、 作用域发生未捕获异常
3、 程序在作用域正常执行完毕
4、 程序执行了System.exit()
5、 程序发生意外终止(被杀进程等)
heap和stack有什么区别。
java的内存分为两类,一类是栈内存,一类是堆内存。栈内存是指程序进入一个方法时,会为这个方法单独分
配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这
个栈中的变量也将随之释放。
堆是与栈作用不同的内存,一般用于存放不在当前方法栈中的那些数据,例如,使用new创建的对象
都放在堆里,所以,它不会随方法的结束而消失。方法中的局部变量使用final修饰后,放在堆中,而不是栈
中。
GC是什么?为什么要有GC?
GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或
者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域
从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。
垃圾回收的优点和原理。并考虑2种回收机制。
Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而
解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于垃圾回收机制,Java中的对象不再
有"作用域"的概念,只有对象的引用才有"作用域"。
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的
低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序
员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。
回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。
垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图
的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。
当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。
程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。
servlet
说一说Servlet的生命周期?
Servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由
javax.servlet.Servlet接口的init(),service()和destroy方法表达。
Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派
遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方
法。
web容器加载servlet,生命周期开始。通过调用servlet的init()方法进行servlet的初始化。通过调用
service()方法实现,根据请求的不同调用不同的do***()方法。结束服务,web容器调用servlet的
destroy()方法。
Servlet API中forward()与redirect()的区别?
1.从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后
把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的
是新的URL.所以redirect等于客户端向服务器端发出两次request,同时也接受两次response。
2.从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.
redirect不仅可以重定向到当前应用程序的其他资源,还可以重定向到同一个站点上的其他应用程序
中的资源,甚至是使用绝对URL重定向到其他站点的资源.
forward方法只能在同一个Web应用程序内的资源之间转发请求.forward 是服务器内部的一种操
作.
redirect 是服务器通知客户端,让客户端重新发起请求.
所以,你可以说 redirect 是一种间接的请求, 但是你不能说"一个请求是属于forward还是redirect"
3.从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
4.从效率来说
forward:高.
redirect:低.
request.getAttribute()和 request.getParameter()有何区别?
1,request.getParameter()取得是通过容器的实现来取得通过类似post,get等方式传入的数据。
request.setAttribute()和getAttribute()只是在web容器内部流转,仅仅是请求处理阶段。
2,getAttribute是返回对象,getParameter返回字符串
3,getAttribute()一向是和setAttribute()一起使用的,只有先用setAttribute()设置之后,才能够
通过getAttribute()来获得值,它们传递的是Object类型的数据。而且必须在同一个request对象中使用才
有效。,而getParameter()是接收表单的get或者post提交过来的参数
web.xml 中的listener、 filter、servlet 加载顺序
真正的加载顺序为:context-param -> listener -> filter -> servlet
jsp静态包含和动态包含的区别
1、<%@include file=“xxx.jsp”%>为jsp中的编译指令,其文件的包含是发生在jsp向servlet转换的时
期,而
java文件编译为class文件的时期
2、使用静态包含只会产生一个class文件,而使用动态包含会产生多个class文件
3、使用静态包含,包含页面和被包含页面的request对象为同一对象,因为静态包含只是将被包含的页面的内
容复制到包含的页面中去;而动态包含包含页面和被包含页面不是同一个页面,被包含的页面的request对象可
以取到的参数范围要相对大些,不仅可以取到传递到包含页面的参数,同样也能取得在包含页面向下传递的参数
MVC的各个部分都有那些技术来实现?如何实现?
MVC是Model-View-Controller的简写。
Model代表的是应用的业务逻辑(通过JavaBean,EJB组件实现)
View是应用的表示面(由JSP页面产生)
Controller是提供应用的处理过程控制(一般是一个Servlet),通过这种设计模型把应用逻辑,处理
过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。
jsp有哪些内置对象?作用分别是什么?
JSP共有以下9个内置的对象:
1. request 用户端请求,此请求会包含来自GET/POST请求的参数
2. response 网页传回用户端的回应
3. pageContext 网页的属性是在这里管理
4. session 与请求有关的会话期
5. application servlet 正在执行的内容
6. out 用来传送回应的输出
7. config servlet的构架部件
8. page JSP网页本身
9. exception 针对错误网页,未捕捉的例外
什么是cookie?Session和cookie有什么区别?
Cookie是会话技术,将用户的信息保存到浏览器的对象.
区别:
(1)cookie数据存放在客户的浏览器上,session数据放在服务器上
(2)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安
全应当使用session
(3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要
考虑到减轻服务器性能方面,应当使用COOKIE
(4)单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K。
结论:
将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中。
jsp和servlet的区别、共同点、各自应用的范围?
JSP是Servlet技术的扩展,本质上就是Servlet的简易方式。JSP编译后是“类servlet”。
Servlet和JSP最主要的不同点在于:Servlet的应用逻辑是在Java文件中,并且完全从表示层中
的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。
JSP侧重于视图,Servlet主要用于控制逻辑。在struts框架中,JSP位于MVC设计模式的视图层,而
Servlet位于控制层.
tomcat容器是如何创建servlet类实例?用到了什么原理?
当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对xml文件进行解析,并读
取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。(有时
候也是在第一次请求时实例化)
在servlet注册时加上1如果为正数,则在一开始就实例化,如
果不写或为负数,则第一次请求实例化。
jsp和servlet的区别、共同点、各自应用的范围?
JSP是Servlet技术的扩展,本质上就是Servlet的简易方式。JSP编译后是“类servlet”。
Servlet和JSP最主要的不同点在于:Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里
分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。
JSP侧重于视图,Servlet主要用于控制逻辑。在struts框架中,JSP位于MVC设计模式的视图层,而Servlet
位于控制层.
servlet的三大作用域对象:
request(HttpServletRequest)
session(HttpSession):
application(ServletContext):tomcat启动时创建,tomcat关闭时销毁,整个web的生命周期只有一
个
jsp的四大作用域对象:
pageContext(pageContext)
request(HttpServletRequest)
session(HttpSession)
application(ServletContext)
jsp的九大内置对象:
pageContext(pageContext)
request(HttpServletRequest)
session(HttpSession)
application(ServletContext)
response(HttpResponse)
config(ServletConfig)
out(JspWriter)
page(Object)
exception(Throwable)
HTTP
请求报文的组成:
请求方法。
请求的资源的URI。
协议版本。
可选的请求首部字段。
内容实体。
响应报文的组成:
协议版本。
状态码。
用于解释状态码的原因短语。
可选的响应首部字段。
实体主体
响应:
响应码:回应客户端此次响应是否成功。如:404(找不到请求的资源),500(服务器内部错误),200(成功响应)等。
消息头:服务器与客户端通信的暗码,告诉客户端该怎么执行某些操作。
响应正文:传递服务器响应给客户端要显示的内容,可以是下载文件或者显示界面。
get和post方法的区别
1,Get是向服务器发索取数据的一种请求,而Post是向服务器提交数据的一种请求
2,Get是获取信息,而不是修改信息,类似数据库查询功能一样,数据不会被修改
3,Get请求的参数会跟在url后进行传递,请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&
相连,%XX中的XX为该符号以16进制表示的ASCII,如果数据是英文字母/数字,原样发送,如果是空格,转换
为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
4,Get传输的数据有大小限制,因为GET是通过URL提交数据,那么GET可提交的数据量就跟URL的长度有直接
关系了,不同的浏览器对URL的长度的限制是不同的。
5,GET请求的数据会被浏览器缓存起来,用户名和密码将明文出现在URL上,其他人可以查到历史浏览记录,数
据不太安全。
在"服务器端",用Request.QueryString来获取Get方式提交来的数据
Post请求则作为http消息的实际内容发送给web服务器,数据放置在HTML Header内提交,Post没有限制提
交的数据。Post比Get安全,当数据是中文或者不敏感的数据,则用get,因为使用get,参数会显示在地址,
对于敏感数据和不是中文字符的数据,则用post。
6,POST表示可能修改变服务器上的资源的请求,在服务器端,用Post方式提交的数据只能用Request.Form
来获取。
javaweb
JDBC访问数据库的基本步骤是什么?
1,加载驱动
2,通过DriverManager对象获取连接对象Connection
3,通过连接对象获取会话
4,通过会话进行数据的增删改查,封装对象
5,关闭资源
说说preparedStatement和Statement的区别
1,效率:预编译会话比普通会话对象,数据库系统不会对相同的sql语句不会再次编译
2,安全性:可以有效的避免sql注入攻击!sql注入攻击就是从客户端输入一些非法的特殊字符,而使服务器端
在构造sql语句的时候仍然能够正确构造,从而收集程序和服务器的信息和数据。
比如:
“select * from t_user where userName = ‘” + userName + “ ’ and password =’” +
password + “’”
如果用户名和密码输入的是’1’ or ‘1’=’1’ ; 则生产的sql语句是:
“select * from t_user where userName = ‘1’ or ‘1’ =’1’ and password =’1’ or
‘1’=’1’ 这个语句中的where 部分没有起到对数据筛选的作用。
说说事务的概念,在JDBC编程中处理事务的步骤。
1 事务是作为单个逻辑工作单元执行的一系列操作。
2,一个逻辑工作单元必须有四个属性,称为原子性、一致性、隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务
事务处理步骤:
3,conn.setAutoComit(false);设置提交方式为手工提交
4,conn.commit()提交事务
5,出现异常,回滚 conn.rollback();
数据库连接池的原理。为什么要使用连接池。
1,数据库连接是一件费时的操作,连接池可以使多个操作共享一个连接。
2,数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需
要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接
数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量、使
用情况,为系统开发,测试及性能调整提供依据。
3,使用连接池是为了提高对数据库连接资源的管理
JDBC的脏读是什么?哪种数据库隔离级别能防止脏读?
当我们使用事务时,有可能会出现这样的情况,有一行数据刚更新,与此同时另一个查询读到了这个刚更新的
值。这样就导致了脏读,因为更新的数据还没有进行持久化,更新这行数据的业务可能会进行回滚,这样这个数
据就是无效的。数据库的TRANSACTIONREADCOMMITTED,TRANSACTIONREPEATABLEREAD,和
TRANSACTION_SERIALIZABLE隔离级别可以防止脏读。
什么是幻读,哪种隔离级别可以防止幻读?
幻读是指一个事务多次执行一条查询返回的却是不同的值。假设一个事务正根据某个条件进行数据查询,
然后另一个事务插入了一行满足这个查询条件的数据。之后这个事务再次执行了这条查询,返回的结果集中会包
含刚插入的那条新数据。这行新数据被称为幻行,而这种现象就叫做幻读。
只有TRANSACTION_SERIALIZABLE隔离级别才能防止产生幻读。
JDBC的DriverManager是用来做什么的?
JDBC的DriverManager是一个工厂类,我们通过它来创建数据库连接。当JDBC的Driver类被加载进来时,它
会自己注册到DriverManager类里面
然后我们会把数据库配置信息传成DriverManager.getConnection()方法,DriverManager会使用注册到
它里面的驱动来获取数据库连接,并返回给调用的程序。
execute,executeQuery,executeUpdate的区别是什么?
1,Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个
ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回
false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpdateCount()方法来获取
更新的记录条数。
2,Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使
查询不到记录返回的ResultSet也不会为null。我们通常使用executeQuery来执行查询语句,这样的话如果
传进来的是insert或者update语句的话,它会抛出错误信息为 “executeQuery method can not be used for update”的java.util.SQLException。 ,
3,Statement的executeUpdate(String query)方法用来执行insert或者update/delete(DML)语
句,或者 什么也不返回,对于DDL语句,返回值是int类型,如果是DML语句的话,它就是更新的条数,如果是
DDL的话,就返回0。
只有当你不确定是什么语句的时候才应该使用execute()方法,否则应该使用executeQuery或者
executeUpdate方法。
SQL查询出来的结果分页展示一般怎么做?
Oracle:
select * from
(select ,rownum as tempid from student ) t
where t.tempid between ” + pageSize(pageNumber-1) + ” and ” + pageSize*pageNumber
MySQL:
select * from students limit ” + pageSize*(pageNumber-1) + “,” + pageSize;
sql server:
select top ” + pageSize + ” * from students where id not in +
(select top ” + pageSize * (pageNumber-1) + id from students order by id) +
“order by id;
JDBC的ResultSet是什么?
在查询数据库后会返回一个ResultSet,它就像是查询结果集的一张数据表。
ResultSet对象维护了一个游标,指向当前的数据行。开始的时候这个游标指向的是第一行。如果调用了
ResultSet的next()方法游标会下移一行,如果没有更多的数据了,next()方法会返回false。可以在for
循环中用它来遍历数据集。
默认的ResultSet是不能更新的,游标也只能往下移。也就是说你只能从第一行到最后一行遍历一遍。不过也
可以创建可以回滚或者可更新的ResultSet
当生成ResultSet的Statement对象要关闭或者重新执行或是获取下一个ResultSet的时候,ResultSet对
象也会自动关闭。
可以通过ResultSet的getter方法,传入列名或者从1开始的序号来获取列数据。
spring
spring的事物
1.spring事物不生效的原因
1.配置有问题1)切点表达式有问题 2)事物传播特性
tx:attributes
aop:config
2.如果你用了mysql数据库,查看数据库存储引擎,MyISAM是不支持事物的,需要改成InnoDB
3.将service的注解放到子容器初始化,会产生事物失效
子容器(是servlet):springmvc --加载的配置文件—》加载扫描—》如何子容器扫描到了service注解,那么就会实例化一个没有经过事务加强处理的Service
父容器(监听器):spring - 加载配置文件—》加载扫描—》父容器进行初始化的Service是有事物处理能力的
4. 没有将异常抛给spring
} catch (Exception e) {
log.error(“添加用户失败”,e,e);
throw new RuntimeException(“添加用户失败”);
}
Spring MVC运行原理
整个处理过程从一个HTTP请求开始:
1.Tomcat在启动时加载解析web.xml,找到spring mvc的前端总控制器DispatcherServlet,并且通过
DispatcherServlet来加载相关的配置文件信息。
2.DispatcherServlet接收到客户端请求,找到对应HandlerMapping,根据映射规则,找到对应的处理器
(Handler)。
3.调用相应处理器中的处理方法,处理该请求后,会返回一个ModelAndView。
4.DispatcherServlet根据得到的ModelAndView中的视图对象,找到一个合适的ViewResolver(视图解
析器),根据视图解析器的配置,DispatcherServlet将要显示的数据传给对应的视图,最后显示给用户。
使用Spring框架的好处是什么?
轻量:Spring 是轻量的,基本的版本大约2MB。
控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
容器:Spring 包含并管理应用中对象的生命周期和配置。
MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。
spring的概述
1.Spring是实现了工厂模式的工厂类(在这里有必要解释清楚什么是工厂模式),这个类名为
BeanFactory(实际上是一个接口),在程序中通常BeanFactory的子类ApplicationContext。Spring
相当于一个大的工厂类,在其配置文件中通过元素配置用于创建实例对象的类名和实例对象的属性。
Spring提供了对IOC良好支持,IOC是一种编程思想,是一种架构艺术,利用这种思想可以很好地实现模块
之间的解耦,IOC也称为DI(Depency Injection)。
Spring提供了对AOP技术的良好封装, AOP称为面向切面编程,就是系统中有很多各不相干的类的方法,
在这些众多方法中要加入某种系统功能的代码,例如,加入日志,加入权限判断,加入异常处理,这种应用称为
AOP。实现AOP功能采用的是"代理技术",客户端程序不再调用目标,而调用代理类,代理类与目标类对外具有
相同的方法声明,有两种方式可以实现相同的方法声明,。“一是实现相同的接口,二是作为目标的子类”
在JDK中采用Proxy类产生动态代理的方式为某个接口生成实现类,如果要为某个类生成子类,则可以用CGLI
B。在生成的代理类的方法中加入系统功能和调用目标类的相应方法,系统功能的代理以Advice对象进行提供,
显然要创建出代理对象,至少需要目标类和Advice类。spring提供了这种支持,只需要在spring配置文件中 配置这两个元素即可实现代理和aop功能。
什么是Spring的依赖注入?有哪些方法进行依赖注入
依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它
如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一
个容器(IOC容器)负责把他们组装起来。
构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表
一个对其他类的依赖。
Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,
调用该bean的setter方法,即实现了基于setter的依赖注入。
ApplicationContext通常的实现是什么?
FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文
件的全路径名必须提供给它的构造函数。
ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设
置classpath因为这个容器将在classpath里找bean配置。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
什么是Spring beans?
Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管
理。这些beans通过容器中配置的元数据创建。比如,以XML文件中 的形式定义。
Spring 框架定义的beans都是单件beans。在bean tag中有个属性”singleton”,如果它被赋为TRUE,
bean 就是单件,否则就是一个 prototype bean。默认是TRUE,所以所有在Spring框架中的beans 缺省
都是单件。
解释Spring支持的几种bean的作用域。
“Spring框架支持以下五种bean的作用域:”
singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情
形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring
ApplicationContext情形下有效。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
解释Spring框架中bean的生命周期。
1,Spring容器 从XML 文件中读取bean的定义,并实例化bean。
2,Spring根据bean的定义填充所有的属性。
3,如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法。
4,如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方
法。
5,如果有任何与bean相关联的BeanPostProcessors,Spring会在
postProcesserBeforeInitialization()方法内调用它们。
6,如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,
调用此初始化方法。
7,如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方
法将被调用。
8,如果bean实现了 DisposableBean,它将调用destroy()方法。
在 Spring中如何注入一个java集合?
Spring提供以下几种集合的配置元素:
类型用于注入一列值,允许有相同的值。
类型用于注入一组值,不允许有相同的值。
Spring框架的事务管理有哪些优点?
它为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。
它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如
它支持声明式事务管理。
它和Spring各种数据访问抽象层很好得集成。
设计模式
单例模式
单例就是该类只能返回一个实例。
单例所具备的特点:
1.私有化的构造函数
2.私有的静态的全局变量
3.公有的静态的方法
饿汉式:
public class Singleton1 {
private Singleton1() {};
private static Singleton1 single = new Singleton1();
public static Singleton1 getInstance() {
return single;
}
}
懒汉式:
public class Singleton2 {
private Singleton2() {}
private static Singleton2 single=null;
public tatic Singleton2 getInstance() {
if (single == null) {
single = new Singleton2();
}
return single;
}
}
线程安全:(双重判定锁)
public class Singleton3 {
private Singleton3() {}
private static Singleton3 single ;
public static Singleton3 getInstance() {
if(null == single){
synchronized(single ){
if(null == single){
single = new Singleton3();
}
}
}
return single;
}
}
参考:
通过双重判断来保证单列设计模式在多线程中的安全性,
并且它在性能方面提高了很多。
synchronized在方法上加锁 (同步锁)
synchronized在代码块内部加锁 (同步代码块)
synchronized(同步锁)
使用synchronized如何解决线程安全的问题?
1.synchronized在方法上加锁
2.synchronized在代码块内部加锁
工厂模式
接口:
public interface DataSource {
public Connection getConnection();
}
mysql数据源实现类:
public class MysqlDataSource implements DataSource{
public Connection getConnection() {
return mysql数据库的连接
}
}
oracle数据源实现类:
public class OracleDataSource implements DataSource{
public Connection getConnection() {
return oracle数据库的连接
}
}
数据源工厂:
public class DataSourceFactory {
public static DataSource getDataSource(String dbType) {
if (dbType.equals(“mySql”)) {
return new MysqlDataSource();
} else if (dbType.equals(“oracle”)) {
return new OracleDataSource();
} else {
throw new RuntimeException(“数据源类型不匹配”);
}
}
}
客户端测试类:
public class Client() {
public static void main(String[] args) {
DataSourceFactory.getDataSource(“mySql”).getConnection();
}
}
代理设计模式
接口:
public interface BrandService {
public List getBrandList();
}
接口的真实实现:
public class BrandServiceImpl implements BrandService {
public List getBrandList() {
brandDao.getBrandList();
}
}
接口的代理实现:
public class BrandServiceProxy implements BrandService {
private BrandService brandService;
public BrandServiceProxy(BrandService brandService) {
this.brandService = brandService;
}
public List getBrandList() {
if (cache中不存在) {
List brandList = brandService.getBrandList();
cache.put(“brandList”, brandList);
}
return brandList;
}
}
客户端调用:
public class client {
public static void main(String[] args) {
BrandService brandService = new BrandServiceProxy(new BrandServiceImpl());
brandService.getBrandList();
}
}
REST/RESTful(也算一种设计模式)
URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。
屌丝大法概念:你去追女神的时候,要在她身边安插很多眼线,当女神有任何动静的时候你的这些眼线都会知道。懂吗?泡过妞的都知道吧
传统理论概念:有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监
听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
装饰器模式
屌丝大法概念:陪女神逛街,逛街的时候要把自己的所有生活费都拿出来,因为一个女人买了一件衣服,就肯定要配鞋,买完鞋子配包包,买完包包要配内衣。。污这些都是来修饰女神的业务,但下半月的日子你要吃泡面过了。
传统理论概念:比如我们的struts2,你真正去编写的业务代码只是一个普通的class,真正的拥有servlet机
制的接收Http请求,和页面的跳转你避开了http的对象,这是因为StrutsPrepareEndExcuteFilter就是
一个装饰器模式,你只要像考虑业务问题,其他的接收参数和跳转页面都交给struts2去做.
spring MVC的运行原理
整个处理过程从一个HTTP请求开始:
1.Tomcat在启动时加载解析web.xml,找到spring mvc的前端总控制器DispatcherServlet,并且通过
DispatcherServlet来加载相关的配置文件信息。
2.DispatcherServlet接收到客户端请求,找到对应HandlerMapping,根据映射规则,找到对应的处理器
(Handler)。
3.调用相应处理器中的处理方法,处理该请求后,会返回一个ModelAndView。
4.DispatcherServlet根据得到的ModelAndView中的视图对象,找到一个合适的ViewResolver(视图解
析器),根据视图解析器的配置,DispatcherServlet将要显示的数据传给对应的视图,最后显示给用户。
Spring MVC、struts1和struts2区别
1.spring mvc 单例 非线程安全
struts1单例 非线程安全
struts2线程安全对每个请求都产生一个实例
2.spring mvc的入口是servlet,而struts2是filter
spring 的前端总控制器为 DispatcherServlet
struts2 的前端总控制器为 FilterDispatcher
struts1 的前端总控制器为 actionServlet
3. 参数传递:struts是在接受参数的时候,
可以用属性来接受参数,这就说明参数是让多个方法共享的。
springmvc 用方法来接受参数
4.spring mvc是基于方法的设计,而sturts是基于类
spring mvc的自定义拦截器的实现?
SpringMVC 中的Interceptor 拦截器,它的主要作用是拦截用户的请求并进行相应的处理。用户可以自
定义拦截器来实现特定的功能,比如通过它来进行权限验证,或者是来判断用户是否登陆等。
SpringMVC的拦截器提供了HandlerInterceptorAdapter抽象类,对应提供了三个preHandle,
postHandle,afterCompletion方法。
preHandle在业务处理器处理请求之前被调用,
postHandle在业务处理器处理请求执行完成后,生成视图之前执行,
afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等 。
所以要想实现自己的拦截管理逻辑,需要继承HandlerInterceptorAdapter并重写其三个方法。
spring mvc的上传图片是怎么实现的?
首先在spring mvc的配置文件中,配置上传文件的组件配置
然后前端上传图片的时候 需要添加标签
spring 事务的传播行为类型?
事务的隔离级别:
事务的隔离级别也分为四种,由低到高依次分别为:read uncommited(读未提交)、read
commited(读提交)、read repeatable(读重复)、serializable(序列化),这四个级别可以逐个解
决脏读、不可重复读、幻读这几类问题。
1.首先,要在web.xml里面配置SpringMVC的核心控制器,DispatcherServlet,对指定的后缀请求进行拦
截。
2.Controller层要加 @Controller注解,表明该类是MVC的控制层。
3.创建Service接口,给实现类加上注解 @Component或者 @Service 表明这是Service业务处理层
4.在Controller层声明Service变量(属性),给变量(属性) 加上 @Autowired注解,通过自动绑定机制将
Service注入到Controller。 (注:@Autowired默认是ByType,如果想根据属性名注入,那么就再加上注解
@Resource(name=“属性名”))
5.在Controller层的方法上加上注解 @RequestMapping(“requestAddress”) 表明该方法的请求地址
6.Dao层要加上注解 @Repository 表明这是数据库持久层
7.同样将dao实例注入到service层中。
8.配置视图解析器 “InternalResourceViewResolver”,对处理后的跳转进行统一配置。
ORM值持久层框架-Hibernate知识点总结
什么是ORM?
对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的
关系模型互不匹配问题的技术;
简单的说,ORM是通过使用描述对象和数据库之间映射的元数据(在Java中可以用XML或者是注解),将
程序中的对象自动持久化到关系数据库中或者将关系数据库表中的行转换成Java对象,其本质上就是将数据从
一种形式转换到另外一种形式。
get 和 load的区别
加载方式:
load为延迟加载(返回的是一个只有id属性的代理,只有使用该对象属性时,才发出sql语句);
get为立即加载(执行时,会立即向数据库发出sql语句)
返回结果:
load检索不到记录时,会抛ObjectNotFoundException异常
get检索不到记录时,会返回null
Hibernate的运行原理
首先通过configuration去加载hibernate.cfg.xml这个配置文件,根据
配置文件的信息去创建sessionFactory,sessionFactory是线程安全的,
是一个session工厂,用来创建session,session是线程不安全的,相当于jdbc的connection,最后通过
session去进行数据库的各种操作,在进行操作
的时候通过transaction进行事务的控制。
在hibernate进行多表查询每个表中各取几个字段,也就是说查询出来的结果集没有一个实体类与之对应如何解决?
解决方案一,:按照Object[]数据取出数据,然后自己组bean
解决方案二:对每个表的bean写构造函数,比如表一要查出field1,field2两个字段,那么有一个构造函数就
是Bean(type1filed1,type2,field2) ,然后在hql里面就可以直接生成这个bean了。
介绍一下Hibernate的二级缓存
按照以下思路来回答:
(1)首先说清楚什么是缓存
(2)再说有了hibernate的Session就是一级缓存,即有了一级缓存,为什么还要有二级缓存
(3)最后再说如何配置Hibernate的二级缓存。
1,缓存就是把以前从数据库中查询出来和使用过的对象保存在内存中(一个数据结构中),这个数据结构
通常是或类似HashMap,当以后要使用某个对象时,先查询缓存中是否有这个对象,如果有则使用缓存中的对
象,如果没有则去查询数据库,并将查询出来的对象保存在缓存中,以便下次使用。
2,Hibernate的Session就是一种缓存,我们通常将之称为Hibernate的一级缓存,当想使用session
从数据库中查询出一个对象时,Session也是先从自己内部查看是否存在这个对象,存在则直接返回,不存在才
去访问数据库,并将查询的结果保存在自己内部。
由于Session代表一次会话过程,一个Session与一个数据库连接相关连,所以Session最好不要
长时间保持打开,通常仅用于一个事务当中,在事务结束时就应关闭。并且Session是线程不安全的,被多个线
程共享时容易出现问题。通常只有那种全局意义上的缓存才是真正的缓存应用,才有较大的缓存价值,因此,
Hibernate的Session这一级缓存的缓存作用并不明显,应用价值不大。Hibernate的二级缓存就是要为
Hibernate配置一种全局缓存,让多个线程和多个事务都可以共享这个缓存。我们希望的是一个人使用过,其
他人也可以使用,session没有这种效果。
3,二级缓存是独立于Hibernate的软件部件,属于第三方的产品,多个厂商和组织都提供有缓存产品,
例如,EHCache和OSCache等等。在Hibernate中使用二级缓存,首先就要在hibernate.cfg.xml配置文件
中配置使用哪个厂家的缓存产品,接着需要配置该缓存产品自己的配置文件,最后要配置Hibernate中的哪些
实体对象要纳入到二级缓存的管理中。明白了二级缓存原理和有了这个思路后,很容易配置起Hibernate的二
级缓存。
扩展知识:一个SessionFactory可以关联一个二级缓存,也即一个二级缓存只能负责缓存一个数据库中
的数据,当使用Hibernate的二级缓存后,注意不要有其他的应用或SessionFactory来更改当前数据库中的
数据,这样缓存的数据就会与数据库中的实际数据不一致。.
谈谈你对Hibernate的理解。
2.为了在关机和内存空间不够的状况下,保持程序的运行状态,需要将内存中的对象状态保存到持久化设备和从
持久化设备中恢复出对象的状态,通常都是保存到关系数据库来保存大量对象信息。从Java程序的运行功能上
来讲,保存对象状态的功能相比系统运行的其他功能来说,应该是一个很不起眼的附属功能,java采用jdbc来
实现这个功能,这个不起眼的功能却要编写大量的代码,而做的事情仅仅是保存对象和恢复对象,并且那些大量
的jdbc代码并没有什么技术含量,基本上是采用一套例行公事的标准代码模板来编写,是一种苦活和重复性的
工作。
3.通过数据库保存java程序运行时产生的对象和恢复对象,其实就是实现了java对象与关系数据库记录的映射
关系,称为ORM(即Object RelationMapping),人们可以通过封装JDBC代码来实现了这种功能,封装出
来的产品称之为ORM框架,Hibernate就是其中的一种流行ORM框架。使用Hibernate框架,不用写JDBC代
码,仅仅是调用一个save方法,就可以将对象保存到关系数据库中,仅仅是调用一个get方法,就可以从数据库
中加载出一个对象。
4.使用Hibernate的基本流程是:配置Configuration对象、产生SessionFactory、创建session对
象,启动事务,完成CRUD操作,提交事务,关闭session。
5.使用Hibernate时,先要配置hibernate.cfg.xml文件,其中配置数据库连接信息和方言等,还要为每个
实体配置相应的hbm.xml文件,hibernate.cfg.xml文件中需要登记每个hbm.xml文件。
6.在应用Hibernate时,重点要了解Session的缓存原理,级联,延迟加载和hql查询。
(以上,也可以结合自己使用JDBC时的繁琐谈hibernate的感受)
Hibernate的一对多和多对一双向关联的区别??
一对多关联映射和多对一关联映射实现的基本原理都是一样的,既是在多的一端加入一个外键指向一的一端外
键,而主要的区别就是维护端不同。
它们的区别在于维护的关系不同:
一对多关联映射是指在加载一的一端数据的同时加载多的一端的数据多对一关联映射是指在加载多的一端数据的
同时加载一的一端的数据。
Hibernate是如何延迟加载?
SessionFactory对应Hibernate的一个数据存储的概念,它是线程安全的,可以被多个线程并发访问。
SessionFactory一般只会在启动的时候构建。对于应用程序,最好将SessionFactory通过单例模式进行封
装以便于访问。
Session是一个轻量级非线程安全的对象(线程间不能共享session),它表示与数据库进行交互的一个工
作单元。Session是由SessionFactory创建的,在任务完成之后它会被关闭。Session是持久层服务对外提
供的主要接口。
Session会延迟获取数据库连接(也就是在需要的时候才会获取)。为了避免创建太多的session,可以使
用ThreadLocal将session和当前线程绑定在一起,这样可以让同一个线程获得的总是同一个session。
Hibernate 3中SessionFactory的getCurrentSession()方法就可以做到。
Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的?有什么区别?
Hibernate的对象有三种状态:瞬时态(transient)、持久态(persistent)和游离态(detached)。
瞬时态的实例可以通过调用save()、persist()或者saveOrUpdate()方法变成持久态;
游离态的实例可以通过调用 update()、saveOrUpdate()、lock()或者replicate()变成持久
态。save()和persist()将会引发SQL的INSERT语句,而update()或merge()会引发UPDATE语句。
save()和update()的区别在于一个是将瞬时态对象变成持久态,一个是将游离态对象变为持久态。
merge()方法可以完成save()和update()方法的功能,它的意图是将新的状态合并到已有的持久化对象上或
创建新的持久化对象。
对于persist()方法,按照官方文档的说明:
1、persist()方法把一个瞬时态的实例持久化,但是并不保证标识符被立刻填入到持久化实例中,标识符的填
入可能被推迟到flush的时间;
2、persist()方法保证当它在一个事务外部被调用的时候并不触发一个INSERT语句,当需要封装一个长会话
流程的时候,persist()方法是很有必要的;
3、save()方法不保证第2条,它要返回标识符,所以它会立即执行INSERT语句,不管是在事务内部还是外
部。至于lock()方法和update()方法的区别,update()方法是把一个已经更改过的脱管状态的对象变成持久
状态;lock()方法是把一个没有更改过的脱管状态的对象变成持久状态。
mybatis
mybatis的优缺点?
一、MyBatis框架的优点:
与JDBC相比,减少了50%以上的代码量。
MyBatis是最简单的持久化框架,小巧并且简单易学。
MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用。
提供XML标签,支持编写动态SQL语句。
提供映射标签,支持对象与数据库的ORM字段关系映射。
二、MyBatis框架的缺点:
SQL语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写SQL语句的功底有一定要求。
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
三、MyBatis框架适用场合:
MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
MyBatis中使用#和$书写占位符有什么区别?
#将传入的数据都当成一个字符串,会对传入的数据自动加上引号;
将 传 入 的 数 据 直 接 显 示 生 成 在 S Q L 中 。 注 意 : 使 用 将传入的数据直接显示生成在SQL中。 注意:使用 将传入的数据直接显示生成在SQL中。注意:使用占位符可能会导致SQL注射攻击,能用#的地方就不要使用 , 写 o r d e r b y 子 句 的 时 候 应 该 用 ,写order by子句的时候应该用 ,写orderby子句的时候应该用而不
是#。
解释一下MyBatis中命名空间(namespace)的作用。
在大型项目中,可能存在大量的SQL语句,这时候为每个SQL语句起一个唯一的标识(ID)就变得并不容易了。
为了解决这个问题,在MyBatis中,可以为每个映射文件起一个唯一的命名空间,这样定义在这个映射文件中的
每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的,即使
在不同映射文件中的语句ID相同,也不会再产生冲突了。
MyBatis中的动态SQL是什么意思?
对于一些复杂的查询,我们可能会指定多个查询条件,但是这些条件可能存在也可能不存在,如果不使用持久层
框架我们可能需要自己拼装SQL语句,不过MyBatis提供了动态SQL的功能来解决这个问题。MyBatis中用于实
现动态SQL的元素主要有:
用法举例:
select * from t_blog where 1 = 1 and title = #{title} and content = #{content} and owner = #{owner} JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?(jdbc与mybatis的区别?)1、JDBC:数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
MyBatis:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
2、JDBC:Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
MyBatis:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
3、JDBC:向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
MyBatis: Mybatis自动将java对象映射至sql语句。
4、JDBC:对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
MyBatis:Mybatis自动将sql执行结果映射至java对象。
MyBatis与Hibernate有哪些不同?
1、Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过
mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的
sql,最后将sql执行的结果再映射生成java对象。
2、Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常
适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,
一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数
据库的软件则需要自定义多套sql映射文件,工作量大。
3、Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化
软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的缺点是学习门槛高,要精通门
槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经
验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,
所以框架只有适合才是最好。
(这里也可以结合自己的理解说,别说的收不住)
简单的说一下MyBatis的一级缓存和二级缓存?
Mybatis首先去缓存中查询结果集,如果没有则查询数据库,如果有则从缓存取出返回结果集就不走数据库。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成
的java对象
Mybatis的二级缓存即查询缓存,它的作用域是一个mapper的namespace,即在同一个namespace中查询
sql可以从缓存中获取数据。二级缓存是可以跨SqlSession的。
并发篇
Java中的同步集合与并发集合有什么区别?
同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在
Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5介绍
了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。
不管是同步集合还是并发集合他们都支持线程安全,他们之间主要的区别体现在性能和可扩展性,还有他们如
何实现的线程安全上。
同步HashMap, Hashtable, HashSet, Vector, ArrayList 相比他们并发的实现 (ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteHashSet)会慢得多。造成如此慢的
主要原因是锁, 同步集合会把整个Map或List锁起来,而并发集合不会。并发集合实现线程安全是通过使用
先进的和成熟的技术像锁剥离。
比如ConcurrentHashMap 会把整个Map 划分成几个片段,只对相关的几个片段上锁,同时允许多线程
访问其他未上锁的片段。
同样的,CopyOnWriteArrayList 允许多个线程以非同步的方式读,当有线程写的时候它会将整个
List复制一个副本给它。
如果在读多写少这种对并发集合有利的条件下使用并发集合,这会比使用同步集合更具有可伸缩性。
什么是线程池? 为什么要使用它?
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建
的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的
线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程
池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线
程池)
线程池的作用,就是在调用线程的时候初始化一定数量的线程,有线程过来的时候,先检测初始化的线程
还有空的没有,没有就再看当前运行中的线程数是不是已经达到了最大数,如果没有,就新分配一个线程去处
理。
就像餐馆中吃饭一样,从里面叫一个服务员出来;但如果已经达到了最大数,就相当于服务员已经用
尽了,那没得办法,另外的线程就只有等了,直到有新的“服务员”为止。
线程池的优点就是可以管理线程,有一个高度中枢,这样程序才不会乱,保证系统不会因为大量的并
发而因为资源不足挂掉。
什么是线程池? 为什么要使用它?
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能
创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里
面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线
程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展
线程池)
线程池的作用,就是在调用线程的时候初始化一定数量的线程,有线程过来的时候,先检测初始化的
线程还有空的没有,没有就再看当前运行中的线程数是不是已经达到了最大数,如果没有,就新分配一个线程去
处理。
就像餐馆中吃饭一样,从里面叫一个服务员出来;但如果已经达到了最大数,就相当于服务员已经用
尽了,那没得办法,另外的线程就只有等了,直到有新的“服务员”为止。
线程池的优点就是可以管理线程,有一个高度中枢,这样程序才不会乱,保证系统不会因为大量的并
发而因为资源不足挂掉。
如何避免死锁?
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
三种用于避免死锁的技术:
加锁顺序(线程按照一定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
死锁检测
notify()和notifyAll()有什么区别?
1,notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
2,void notify(): 唤醒一个正在等待该对象的线程。
3,void notifyAll(): 唤醒所有正在等待该对象的线程。
两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上
的锁,一旦该对象被解锁,他们就会去竞争。
notify他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在
等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁,此时如果该对象没有再次使用
notify语句,即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状
态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
数据库篇
MyISAM和InnoDB的主要区别和应用场景
主要区别:
1).MyISAM是非事务安全型的,而InnoDB是事务安全型的。
2).MyISAM锁的粒度是表级,而InnoDB支持行级锁定。
3).MyISAM支持全文类型索引,而InnoDB不支持全文索引。
4).MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。
5).MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。
6).InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter
table tablename type=innodb)。
应用场景:
1).MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查
询,那么MyISAM是更好的选择。
2).InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT
或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。
创建索引的条件和注意事项
为什么不对表中的每一个列创建一个索引呢?这是因为,增加索引也有许多不利的一个方面:
第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加;
第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚
簇索引,那么需要的空间就会更大,如果非聚集索引很多,一旦聚集索引改变,那么所有非聚集索引都会跟着
变;
第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,一旦一个数据改变,并且改变的
列比较多,可能会引起好几个索引跟着改变,这样就降低了数据的维护速度。
第四、每个索引都有统计信息,索引越多统计信息越多,过多索引会导致优化器优化过程需要评估的组合增
多。创建索引的时候,应该仔细考虑在哪些列上可以创建索引,在哪些列上不能创建索引。
一般来说,应该在哪些列上创建索引?
例如:
在经常需要搜索的列上,可以加快搜索的速度;
在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;这样查询可以利
用索引的排序,加快排序查询时间;
在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。当增加索引时,会提高检索性能,但是
会降低修改性能
前端问题
AJAX有哪些有点和缺点?
优点:
1、最大的一点是页面无刷新,用户的体验非常好。
2、使用异步方式与服务器通信,具有更加迅速的响应能力。
3、可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
4、基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
缺点:
1、ajax不支持浏览器back按钮。
2、安全问题 AJAX暴露了与服务器交互的细节。
3、对搜索引擎的支持比较弱。
4、破坏了程序的异常机制。
5、不容易调试。
jQuery 中ajax异步调用的四种方式
Ajax的实现流程是怎样的?
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.
具体一点:
1,创建XNLHttpRequest对象
(不考虑ie)XMLHttpRequest request = new XMLHttprequest();
2,创建新的Http请求
XMLHttprequest.open(method,url,flag,name,password);
3,设置响应Http请求变化的函数
XMLHttprequest.onreadystatechange=getData;
function getData(){
if(XMLHttprequest.readyState==4){
获取数据
}
}
4,发送http请求
XMLHttprequest.send(data);
5,获取异步调用返回的对象
,function(data){
//异步提交后,交互成功,返回的data便是异步调用返回的对象,该对象是一个string类型的
}
6,使用js、DOM实现局部刷新
myDiv.innerHTML=’‘这是刷新后的数据’’
多线程
多线程图谱
什么是线程?
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通
过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫
秒,那么用十个线程完成改任务只需10毫秒。
线程和进程有什么区别?
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空
间,而所有的线程共享一片相同的内存空间。每个线程都拥有单独的栈内存用来存储本地数据。
如何在Java中实现线程?
两种方式:java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执
行,由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接调用
Runnable接口来重写run()方法实现线程。
线程的概述
线程的状态以及状态之间的相互转换:
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于
可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就
绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入
锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为
阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状
态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
实现线程的两种方式:
是继承Thread类或实现Runnable接口,但不管怎样,当new了这个对象后,线程就已经进入了初始状态
wait和sleep的区别:
线程访问:
锁池状态,之后等待锁释放,然后访问代码
wait
等待队列(释放资源)—>调用notify或者notifyall之后锁池状态—>( 等待锁释放)—>可运行
状态—>运行状态---->访问代码
sleep,join
不释放资源–>结束后直接进入可运行状态—>运行状态---->访问代码
一个java控制台程序,默认运行两个线程,一个主线程,一个垃圾回收线程。
线程与进程的区别:
1.线程(Thread)与进程(Process)
进程定义的是应用程序与应用程序之间的边界,通常来说一个进程就代表一个与之对应的应用程序。不同的
进程之间不能共享代码和数据空间,而同一进程的不同线程可以共享代码和数据空间。
2.一个进程可以包括若干个线程,同时创建多个线程来完成某项任务,便是多线程。
Java 关键字volatile 与 synchronized 作用与区别?
1,volatile
它所修饰的变量不保留拷贝,直接访问主内存中的。
在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性
能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变 量在某个瞬间,在一个线
程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。 一个变量
声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。
2,synchronized
当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个
时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然
可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线
程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个
object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用.
有哪些不同的线程生命周期?
当我们在Java程序中新建一个线程时,它的状态是New。当我们调用线程的start()方法时,状态被
改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变Running。
其他的线程状态还有Waiting,Blocked 和Dead。
你对线程优先级的理解是什么?
每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实
现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先
级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优
先级。
什么是死锁(Deadlock)?如何分析和避免死锁?
死锁是指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。
分析死锁,我们需要查看Java应用程序的线程转储。我们需要找出那些状态为BLOCKED的线程和他们等待的资
源。每个资源都有一个唯一的id,用这个id我们可以找出哪些线程已经拥有了它的对象锁。
避免嵌套锁,只在需要的地方使用锁和避免无限期等待是避免死锁的通常办法。
什么是线程安全?Vector是一个线程安全类吗?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果 和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计
数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线
程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全
的。
Java中如何停止一个线程?
Java提供了很丰富的API但没有为停止线程提供API。JDK 1.0本来有一些像stop(), suspend() 和
resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了,之后Java API的设计
者就没有提供一个兼容且线程安全的方法来停止一个线程。当run() 或者 call() 方法执行完的时候线程会
自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来
中断线程
什么是ThreadLocal?
ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不
是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。
每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程
内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。
Sleep()、suspend()和wait()之间有什么区别?
Thread.sleep()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有
对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程
调用了interrupt()方法,它将唤醒那个“睡眠的”线程。
注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调t.sleep(),
(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep(),也是当前线程进入睡眠,而不是t线程。
t.suspend()是过时的方法,使用suspend()导致线程进入停滞状态,该线程会一直持有对象的监视器,
suspend()容易引起死锁问题。
object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而不
是thread。调用object.wait()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把
当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用object.notify(),这样将唤醒原来
等待中的线程,然后释放该锁。基本上wait()/notify()与sleep()/interrupt()类似,只是前者需要获
取对象锁。
什么是线程饿死,什么是活锁?
当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI中线程活锁可
能发生在以下情形:
1,当所有线程在程序中执行Object.wait(0),参数为0的wait方法。程序将发生活锁直到在相应的对象上
有线程调用Object.notify()或者Object.notifyAll()。
2,当所有线程卡在无限循环中。
什么是Java Timer类?如何创建一个有特定时间间隔的任务?
java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。Timer类可
以用安排一次性任务或者周期任务。
java.util.TimerTask是一个实现了Runnable接口的抽象类,我们需要去继承这个类来创建我们
自己的定时任务并使用Timer去安排它的执行。
Java中的同步集合与并发集合有什么区别?
同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。
在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。
Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可
扩展性。
同步方法和同步块,哪个是更好的选择?
同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整
个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。
什么是线程池? 为什么要使用它?
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。
为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。
从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。
Java中invokeAndWait 和 invokeLater有什么区别?
这两个方法是Swing API 提供给Java开发者用来从当前线程而不是事件派发线程更新GUI组件用的。InvokeAndWait()同步更新GUI组件,比如一个进度条,一旦进度更新了,进度条也要做出相应改变。如果进度被多个线程跟踪,那么就调用invokeAndWait()方法请求事件派发线程对组件进行相应更新。而invokeLater()方法是异步调用更新组件的。
多线程中的忙循环是什么?
忙循环就是程序员用循环让一个线程等待,不像传统方法wait(), sleep() 或 yield() 它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是为了保留CPU缓存。
在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。
说一下你了解的几种进程间的通信方式
管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程
的亲缘关系通常是指父子进程关系。
高级管道popen:将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方
式我们成为高级管道方式。
有名管道named pipe :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了
信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,
但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计
的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,
防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间
的同步手段。
套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通
信。
信号sinal: 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
技术点话术
redis
redis-cluster架构图
图片 1 set up-w120****
架构细节:
(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中超过半数的节点检测失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value
redis-cluster投票:容错
(1)领着投票过程是集群中所有master参与,如果半数以上master节点与master节点通信超过(cluster-node-timeout),认为当前master节点挂掉.
(2):什么时候整个集群不可用(cluster_state:fail)?
a:如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完成时进入fail状态. ps : redis-3.0.0.rc1加入cluster-require-full-coverage参数,默认关闭,打开集群兼容部分失败.
b:如果集群超过半数以上master挂掉,无论是否有slave集群进入fail状态.
ps:当集群不可用时,所有对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误
redis集群有16384个槽,集群中的每个结点分配自已槽,通过查看集群结点可以看到槽占用情况。
redis默认有16个数据库,db0-db15,默认使用的是db0,库的数量可以到redis.conf里寻找databases直接修改
单击版的数据库有多个,自由定义,里面的各个数据库都是独立的,但如果是在集群中就只有一个库,就是db0
控制redis的方法
Jedis客户端
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止
Redis支持的键值数据类型如下:
字符串类型、散列类型、列表类型、集合类型、有序集合类型。
redis的应用场景
1.缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
2.分布式集群架构中的session分离。(session共享)
3.聊天室的在线好友列表。
4.任务队列。(秒杀、抢购、12306等等)
5.应用排行榜。
6.网站访问统计。
7.计时器(短信验证码的场景)
8.计数器(点击率的统计)
9.数据过期处理(可以精确到毫秒)
10.首页访问频率太高,每次都调数据,会对数据库造成非常大的压力,所以我们使用缓存来解决此问题
redis的缓存同步
在服务层发布一个接口,后台内容发生改变后,直接调用此接口,把缓存服务清空即可,清空后,客户下次访问
的时候就从数据库中取出,然后再放到redis中
redis 热数据的问题的解决方案
问题1:数据库有200兆数据,redis只有10兆内存,如何保证这10兆数据一直是热点?谁有好的解决方案
解决方案1:提供一种简单实现缓存失效的思路: LRU(最近少用的淘汰)
即redis的缓存每命中一次,就给命中的缓存增加一定ttl(过期时间)(根据具体情况来设定, 比如10分钟).一
段时间后, 热数据的ttl都会较大, 不会自动失效, 而冷数据基本上过了设定的ttl就马上失效了.
问题2:mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?
解决方案2:限定 Redis 占用的内存,Redis 会根据自身数据淘汰策略,加载热数据到内存。
所以,计算一下 20W 数据大约占用的内存,然后设置一下 Redis 内存限制即可。
RDB和AOF的区别和联系
RDB持久化可以在指定的时间间隔内生成数据集的时间点快照
优势
RDB 是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份
RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快(因为其文件要比AOF的小)
RDB的性能更好
劣势
RDB的持久化不够及时
RDB持久化时如果文件过大可能会造成服务器的阻塞,停止客户端请求
AOF redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。
优势
AOF的持久性更加的耐久(可以每秒 或 每次操作保存一次)
AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。
AOF是增量操作
劣势
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。
AOF 在过去曾经发生过这样的 bug : 因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。)
当 Redis 启动时, 如果 RDB 持久化和 AOF 持久化都被打开了, 那么程序会优先使用 AOF 文件来恢复数据集, 因为 AOF 文件所保存的数据通常是最完整的。
缓存
什么是缓存穿透?
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。如
果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫
做缓存穿透。
如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。
什么是缓存雪崩?
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
3:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期(此点为补充)
分布式缓存系统
缓存一致性问题
1:缓存系统与底层数据的一致性。这点在底层系统是“可读可写”时,写得尤为重要
2:有继承关系的缓存之间的一致性。为了尽量提高缓存命中率,缓存也是分层:全局缓存,二级缓存。他们是存在继承关系的。全局缓存可以有二级缓存来组成。
3:多个缓存副本之间的一致性。为了保证系统的高可用性,缓存系统背后往往会接两套存储系统(如memcache,redis等)
缓存穿透和缓存雪崩
上面有讲述。
缓存数据的淘汰
缓存淘汰的策略有两种:
(1) 定时去清理过期的缓存。
(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据.
并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来
都要判断缓存失效,逻辑相对比较复杂,具体用哪种方案,大家可以根据自己的应用场景来权衡。
将数据存到redis缓存
使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器.
使用Ngnix负载均衡
在我们的电商项目,会有某一件商品许多用户去访问,这个时候就会产生高并发,我解决的方式就是使用redis缓
存去解决
solr的话术
什么是solr?
Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比
Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化。
Solr可以独立运行,运行在Jetty、Tomcat等这些Servlet容器中,Solr 索引的实现方法很简单,用 POST
方法向 Solr 服务器发送一个描述 Field 及其内容的 XML 文档,Solr根据xml文档添加、删除、更新索
引 。Solr 搜索只需要发送 HTTP GET 请求,然后对 Solr 返回Xml、json等格式的查询结果进行解析,
组织页面布局。Solr不提供构建UI的功能,Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和
运行情况。
为什么用solr?
在服务层发布搜索服务供前台来调用,可以是PC端调用,可以是移动端,而portal是不能直接访问数据库的,
而我们为了快速的访问,只返回了一些基本的数据,这样就能快速的把页面返回给用户,商品的描述就比较多,
我们选择延迟加载来完成,还有产品的规格我们选择是安需来加载的,只有客户点击才会加载
就算是这样商品详情页面访问也很多,而我们也会把它缓存起来,而商品信息特别的多,如果都放在缓存
中,势必会造成内存的浪费,这样我们就会选择让缓存存在1,2天就会过期,过期后再访问时再取,这样后台数
据库压力就小了
solr中默认是中文分析器IK Analyzer
dubbo的话术
(1)协议支持
Dubbo支持多种协议,如下所示:
Dubbo协议 Hessian协议
HTTP协议 RMI协议
WebService协议
Thrift协议 Memcached协议 Redis协议
在通信过程中,不同的服务等级一般对应着不同的服务质量,那么选择合适的协议便是一件非常重要的事情。你
可以根据你应用的创建来选择。例如,使用RMI协议,一般会受到防火墙的限制,所以对于外部与内部进行通信
的场景,就不要使用RMI协议,而是基于HTTP协议或者Hessian协议。
(2)默认使用Dubbo协议
连接个数:单连接
连接方式:长连接
传输协议:TCP
传输方式:NIO异步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要使用dubbo协议传输大文件或超大字符串
使用场景:常规远程服务方法调用
从上面的适用范围总结,dubbo适合小数据量大并发的服务调用,以及消费者机器远大于生产者机器数的情况,不适合传输大数据量的服务比如文件、视频等,除非请求量很低。
Dubbo基本原理
client一个线程调用远程接口,生成一个唯一的ID(比如一段随机字符串,UUID等),Dubbo是使用
AtomicLong从0开始累计数字的
将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象
callback,全部封装在一起,组成一个对象object
向专门存放调用信息的全局ConcurrentHashMap里面put(ID, object)
将ID和打包的方法调用信息封装成一对象connRequest,使用IoSession.write(connRequest)异步
发送出去
当前线程再使用callback的get()方法试图获取远程返回的结果,在get()内部,则使用synchronized
获取回调对象callback的锁, 再先检测是否已经获取到结果,如果没有,然后调用callback的wait()方
法,释放callback上的锁,让当前线程处于等待状态。
服务端接收到请求并处理后,将结果(此结果中包含了前面的ID,即回传)发送给客户端,客户端socket
连接上专门监听消息的线程收到消息,分析结果,取到ID,再从前面的ConcurrentHashMap里面get(ID),
从而找到callback,将方法调用结果设置到callback对象里。
监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已释
放callback的锁了),再notifyAll(),唤醒前面处于等待状态的线程继续执行(callback的get()方法继
续执行就能拿到调用结果了),至此,整个过程结束。
当前线程怎么让它“暂停”,等结果回来后,再向后执行?
先生成一个对象obj,在一个全局map里put(ID,obj)存放起来,再用synchronized获取obj锁,再调用
obj.wait()让当前线程处于等待状态,然后另一消息监听线程等到服 务端结果来了后,再map.get(ID)找到
obj,再用synchronized获取obj锁,再调用obj.notifyAll()唤醒前面处于等待状态的线程。
tomcat
tomcat集群,如何实现session共享?
方法一:通过修改tomcat配置文件实现
缺点:当节点太多,tomcat集群之间session传递会影响性能
五台为分界点
通过此方法配置的集群,session信息将会被自动复制到各个节点
在Server.xml中,找到被注释节点,修改为如下:
port=“45564” frequency=“500” dropTime=“3000”/> autoBind=“100” selectorTimeout=“5000” maxThreads=“6”/>
2、修改web.xml
在web.xml中,标志为
方法二:基于Cookie的Session共享
这 个方案我们可能比较陌生,但它在大型网站中还是比较普遍被使用。原理是将全站用户的Session信息加密、序列化后以Cookie的方式,统一种植在根 域名下(如:.host.com),利用浏览器访问该根域名下的所有二级域名站点时,会传递与之域名对应的所有Cookie内容的特性,从而实现用户的 Cookie化Session 在多服务间的共享访问。
这 个方案的优点无需额外的服务器资源;缺点是由于受http协议头信心长度的限制,仅能够存储小部分的用户信息,同时Cookie化的 Session内容需要进行安全加解密(如:采用DES、RSA等进行明文加解密;再由MD5、SHA-1等算法进行防伪认证),另外它也会占用一定的带 宽资源,因为浏览器会在请求当前域名下任何资源时将本地Cookie附加在http头中传递到服务器。
方法三:基于缓存(redis memcached)
https://github.com/zhangwei5095/TomcatRedisClusterEnabledSessionManager
以上两行配置有顺序
Tomcat优化
增大内存(堆,持久代)并开启server模式
我在做XXX项目时,用到了poi导入和导出数据,由于公司的业务比较繁多,数据量很大,测试时报内存溢出,经过我的分析再结合上网查阅资料,发现可能是tomcat内存不足,需要增大,修改配置文件后测试不再报错.
tomcat增大内存的方式通过修改tomcat配置文件
window下, 在bin/catalina.bat文件中最前面添加:
set JAVA_OPTS=-XX:PermSize=64M -XX:MaxPermSize=128m –Xms1024m -Xmx1024m
linux下,在catalina.sh最前面增加:
JAVA_OPTS="-XX:PermSize=64M -XX:MaxPermSize=128m –Xms1024m -Xmx1024m "
-client –service
当我们在cmd中运行-java时,黑窗口会出现-client -service这两参数.其作用是设置虚拟机运行模式;client模式启动比较快,但运行时性能和内存管理效率不如server模式,通常用于客户端应用程序。server模式启动比client慢,但可获得更高的运行性能。Windows默认为client,如果要使用server模式,就需要在启动虚拟机时加-server参数,以获得更高性能,对服务器端应用,推荐采用server模式,尤其是多个CPU的系统。在Linux,Solaris上,默认值为server模式.
JDK版本
影响虚拟机还有JDK的版本,JDK分为32位,64位两种版本,32位装在32位系统,64位系统可以装32位和64位JDK.64位JDK性能优于32位JDK.
增加Tomcat最大连接数
使用场景
我在做完一个XXX项目后,测试时发现并发数量增加到一定程度就会很卡,于是我想到了是不是tomcat最大连接数设置有限制.果不其然,配置文件中最大值才500,于是我更改了最大连接数,根据业务我修改了连接数为2000,完美的解决了这个问题;
修改方法在conf/service.xml中默认值
,修改maxthreads的值即可
HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求服务器对应资源后,从服务器端将资源文件压缩,再输出到客户端,由客户端的浏览器负责解压缩并浏览。
相对于普通的浏览过程HTML ,CSS,Javascript,Text,它可以节省60%左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP , JSP , ASP , Servlet,SHTML等输出的网页也能进行压缩,压缩效率也很高。
启用tomcat 的gzip压缩
要使用gzip压缩功能,你需要在Connector节点中加上如下属性
记住来源:http://www.qi788.com/info-42.html
compression=“on” 打开压缩功能
compressionMinSize=“50” 启用压缩的输出内容大小,默认为2KB
noCompressionUserAgents=“gozilla, traviata” 对于以下的浏览器,不启用压缩
compressableMimeType=“text/html,text/xml,text/javascript,text/css,text/plain” 哪些资源类型需要压缩
jdk1.7与1.8的区别,有哪些区别?
jdk1.7的新特性
1.自动资源管理
Java中某些资源是需要手动关闭的,如InputStream,Writes,Sockets,Sql classes等。这
个新的语言特性允许try语句本身申请更多的资源,这些资源作用于try代码块,并自动关闭。
2.新增一些取环境信息的工具方法
System.getJavaHomeDir() // JRE的安装目录
File System.getUserHomeDir() // 当前用户目录
File System.getUserDir() // 启动java进程时所在的目录
3.两个char间的equals
4.switch中使用string
Jdk1.8新特性
1.函数式接口
函数式接口(functional interface 也叫功能性接口,其实是同一个东西)。简单来说,函数式
接口是只包含一个方法的接口。比如Java标准库中的java.lang.Runnable和java.util.Comparator
都是典型的函数式接口。
java 8提供 @FunctionalInterface作为注
解,这个注解是非必须的,只要接口符合函数式接口的标准(即只包含一个方法的接口),虚拟机会自
动判断,但 最好在接口上使用注解==@FunctionalInterface==进行声明,以免团队的其他人员错误地
往接口中添加新的方法。
2.Lambda语法
包含三个部分
1.一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数
2.一个箭头符号:->
3.方法体,可以是表达式和代码块,方法体函数式接口里面方法的实现,如果是代码块,则必须用{}
来包裹起来,且需要一个return 返回值,但有个例外,若函数式接口里面方法返回值是void,则无
需{}
(parameters) -> expression或者 (parameters) -> { statements; }
lambda表达式语法代码简洁,能简化集合上数据的多线程或者多核的处理,提供更快的集合处理速度
3.两个char间的equals
4.switch中使用string
session与cookie的区别
session是存储在服务器端,cookie是存储在客户端的,所以安全来讲session的安全性要比cookie
高,然后我们获取session里的信息是通过存放在会话cookie里的sessionid获取的。又由于
session是存放在服务器的内存中,所以session里的东西不断增加会造成服务器的负担,所以会把很
重要的信息存储在session中,而把一些次要东西存储在客户端的cookie里,然后cookie确切的说分
为两大类分为会话cookie和持久化cookie,会话cookie确切的说是存放在客户端浏览器的内存中,所
以说他的生命周期和浏览器是一致的,浏览器关了会话cookie也就消失了,然而持久化cookie是存放
在客户端硬盘中,而持久化cookie的生命周期就是我们在设置cookie时候设置的那个保存时间,然后
我们考虑一问题当浏览器关闭时session会不会丢失,从上面叙述分析session的信息是通过
sessionid获取的,而sessionid是存放在会话cookie当中的,当浏览器关闭的时候会话cookie消
失所以我们的sessionid也就消失了,但是session的信息还存在服务器端,这时我们只是查不到所谓
的session但它并不是不存在。那么,session在什么情况下丢失,就是在服务器关闭的时候,或者是
sessio过期,再或者调用了invalidate()的或者是我们想要session中的某一条数据消失调用
session.removeAttribute()方法,然后session在什么时候被创建呢,确切的说是通过调用
session.getsession来创建,这就是session与cookie的区别
微信扫描支付的实现话术
参考word文档
大型网站在架构上应当考虑哪些问题?
我们在使用FastDFS部署一个分布式文件系统的时候,通过FastDFS的客户
端API来进行文件的上传、下载、删除等操作。同时通过FastDFS的HTTP
服务器来提供HTTP服务。但是FastDFS的HTTP服务较为简单,无法提供负
载均衡等高性能的服务,所以FastDFS的开发者——淘宝的架构师余庆同
学,为我们提供了Nginx上使用的FastDFS模块(也可以叫FastDFS的
Nginx模块)。其使用非常简单。
支付话术
支付宝支付流程:
安全问题?
签名机制
参数信息 (秘钥)— > 根据加密的算法进行加密 -->生成加密的签名 ---->进行
传递 —> 我会根据秘钥+传过来的参数,在进行一次加密 也会生成一个签名 ,传过来的签名和我
自己生成的签名进行对比,如果一直说明数据没有修改,如果不一致,说明数据修改了。
面试话术
我们网站目前支持支付宝支付和微信支付 。
其实所有的第三方支付都是差不多的,就根据他的文档直接写代码实现
自己的业务逻辑就可以了,它的接口是什么要什么样的数据。我们就给他什
么样的数据,支付成功后会跳转到我们的回调接口。(像支付宝有同步和异
步的回调,微信配置一个回调的授权域名)
因为我们的网站考虑到后期可能还会对接其他的支付,减轻对接的麻
烦,所以我们对外提供了一个公共的方法,根据传过来的值(paytype),
进行路由判断,找到对应的支付方式,比如路由到支付宝的默认支付,那就
走支付宝的默认支付代码。
我们网站目前对接的是支付宝,微信,
redis应用场景话术
在我做的项目里面,我将一些热数据存放到Redis缓存里面,这样可以减轻数据库的压力,加快用户的访问速度,
使用户对我们的产品提升好感.
我在之前的公司做过一个电商项目,这个电商项目,我们将Redis技术用到了商品这一模块,我们将用户查看的商
品放入redis缓存中,等用户下次访问就会比第一次访问的速度更快,当然这样也会出现问题,会给redis缓存增
加压力,数据会越来越多,这一问题我们是这样的解决的,当用户点击某一个商品,我们将这一个商品放入redis
缓存中,在放入缓存中我们给这件商品设置一个过期时间,当这件商品或者某一件商品达到了我们设置的这个过期
时间的时候,就将它从redis缓存中删除,这样就不会使那些点击次数少的数据一直存放在redis缓存中.
其他的还有首页里面一些不经常变动的数据我也会将它放入redis缓存里
webService描述
(主动说)
webservice是SOA(面向服务编程)的一种实现,
主要是用来实现异构平台通信也就
是不同平台不同项目之间的数据传输,从而避免了信息孤岛的问题,
它之所以能够
进行异构平台通信是因为它是完全基于xml的,
所以说,webService是跨平台,
跨语言,跨框架的,在java中通常有三种技术框架分别是xfire,cxf,axis2。
我们为了保证
webservice的安全性,采用了基于
WS-Security标准的安全验证(使用回调函数)。
(没必要主动说)
webservice的三要素分别是:
wsdl(webservice description language)
用来描述发布的接口(服务)
soap(simple object access protocol)
是xml和http的结合,是webservice数据通信的协议
uddi 用来管理,查询webService的服务
(没必要主动说)
webservice的具体三种实现方式(框架)或者三种实现框架的区别
1. Axis2:可以用多种语言开发,
是一个重量级框架,功能非常强大,
但是它的性能比较低。
2. Xfire:它相比Axis2来说是一个轻量级框架,
它的性能要比Axis2高。
3. cxf:是Xfire的升级版,就好比是,
struts2是webwork的升级,
然后cxf和spring集成起来非常方便,简易,
性能方面也要比Xfire高。
【注】jdk6 自带的webservice jws
(主动说)
业务场景
我在以前做项目的时候,其中遇到一个功能,
需要进行两个项目之间的数据的传输,
项目经理让我去完成这个任务,我根据以往的项目经验,
想到两种解决方案,第一种
就是开放另外一个项目的数据库的权限给我,
然后我直接通过访问另外一个项目的数据
库,来得到需要的信息,但后来我分析了下,觉的这种方式不安全,
而且因为当时
这个项目是另外一家公司负责在做,所以数据库里面的表结构,
以及以后牵涉
到的责任问题都很多,所以我就采用了第二种方案,
即通过webservices的方式,进行
异构系统之间数据信息的传递,webservices的具体实现,
有xfire,cxf,axis2,
我根据以往的项目经验,了解到cxf是xfire的升级版本,适用于java语言,
xfire/cxf 性能比axis2要高,并且和spring整合起来也比较方便,
而axis2支持更多的语言,
性能相对于cxf要低,通过上面分析,
结合我们目前的两个项目都是基于java
语言的,所以我采用cxf这种方式实现了两个项目之间数据的传递,
我们为了保证
webservice的安全性我们采用了基于
WS-Security标准的安全验证(使用CXF回调函数)。
(没必要主动说)
webservice服务端配置流程
首先在web.xml中引入cxfServlet核心类,
指定对以/cxf开头的url路径提供webservice服务,
之后我们在要发布成webservice接口上添加@Webservice 注解,
而且还要在实现类上添加同样的webservice注解并且要说明实现了哪个接口,
之后在spring-webservice.xml中发布webservice服务,
通过jaxws:endpoint这个标签,
并且在标签配置implementor和address来表明实现服务的类,
以及发布的地址,
最后在浏览器中输入相关的webservice地址?wsdl来验证服务是否发布成功。
(没必要主动说)
webservice客户端的配置
首先通过wsdl2java根据发布的webservice服务端地址的wsdl
生成客户端调用的中间桥梁java类,
将生成的java类拷贝到客户端项目中,
配置spring-client.xml文件,
通过jaxws:client定义一个bean,
并通过address属性指明要访问的webservice的服务地址,
通过serviceClass指明充当中间桥梁的服务类,之后获取该bean,
就可以通过它来访问发布的webservice接口中的方法。
MySQL、Redis、MongoDB对比
特点:
1-1 MySQL:
1-2. Redis:
1-3. MongoDB:
使用场景的不同:
MongoDB适用于
①网站数据:适合实时的插入,更新与查询,并具备网站实时数据存储所需对的复制及高度伸缩性;
②缓存:由于性能很高,也适合作为信息基础设施的缓存层,在系统重启之后,搭建的持久化缓存可以避免下层的数据源过载;
③大尺寸、低价值的数据也是MongoDB的最佳选择,使用传统的关系数据库存储一些数据时可能会比较贵,再次之前很多程序员往往会选择传统的文件进行存储
④高伸缩的场景,非常是个由数十或者数百台服务器组成的数据库
⑤用于对象及json数据的存储,MongoDB的bson数据格式非常适合文档格式化的存储及查询。
而mysql还是更加适用于
①高度事务性的系统。例如银行或者会计系统,传统的关系型数据库目前还是更实用于需要大量原子性复杂事务的应用程序
②传统的商业智能应用,针对特定问题的BI数据库会对产生高度优化的查询方式,对于此类应用,数据仓库可能是更合适的选择
Redis应用场景:
为什么使用消息队列?
这个问题,咱只答三个最主要的应用场景(不可否认还有其他的,但是只答三个主要的),
即以下六个字:解耦、异步、削峰
使用消息队列有什么缺点?
系统可用性降低:
你想啊,本来其他系统只要运行好好的,那你的系统就是正常的。现在你非要加个消息队列进去,那消息
队列挂了,你的系统不是呵呵了。因此,系统可用性降低
系统复杂性增加:
要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。
因此,需要考虑的东西更多,系统复杂性增大。
消息队列如何选型?
(1)中小型软件公司,建议选RabbitMQ.一方面,erlang语言天生具备高并发的特性,而且他的管理界面用起
来十分方便。正所谓,成也萧何,败也萧何!他的弊端也在这里,虽然RabbitMQ是开源的,然而国内有几个能
定制化开发erlang的程序员呢?所幸,RabbitMQ的社区十分活跃,可以解决开发过程中遇到的bug,这点对于
中小型公司来说十分重要。不考虑rocketmq和kafka的原因是,一方面中小型软件公司不如互联网公司,数据
量没那么大,选消息中间件,应首选功能比较完备的,所以kafka排除。不考虑rocketmq的原因是,
rocketmq是阿里出品,如果阿里放弃维护rocketmq,中小型公司一般抽不出人来进行rocketmq的定制化开
发,因此不推荐。
(2)大型软件公司,根据具体使用在rocketMq和kafka之间二选一。一方面,大型软件公司,具备足够的资金
搭建分布式环境,也具备足够大的数据量。针对rocketMQ,大型软件公司也可以抽出人手对rocketMQ进行定制
化开发,毕竟国内有能力改JAVA源码的人,还是相当多的。至于kafka,根据业务场景选择,如果有日志采集
功能,肯定是首选kafka了。具体该选哪个,看使用场景。
如何保证消息队列是高可用的?
rabbitMQ普通集群和镜像集群模式
如何保证消息不被重复消费?
其实无论是那种消息队列,造成重复消费原因其实都是类似的。正常情况下,消费者在消费消息时候,消费完毕
后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。只是
不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个
CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念,简单说一下(如果还不懂,出门找一个kafka
入门到精通教程),就是每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知
道自己已经消费过了。那造成重复消费的原因?,就是因为网络传输等等故障,确认信息没有传送到消息队列,
导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。
如何解决?这个问题针对业务场景来答分以下3种情况
(1)比如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现
重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
(2)再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一
样的,set操作本来就算幂等操作。
(3)如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个
全局id,只要消费过该消息,将
询有没消费记录即可。
如何保证消费的可靠性传输?
每种MQ都要从三个角度来分析:生产者弄丢数据、消息队列弄丢数据、消费者弄丢数据
(1)生产者丢数据
从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。
transaction机制就是说,发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程
中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。
然而缺点就是吞吐量下降了。因此,按照博主的经验,生产上用confirm模式的居多。一旦channel进入
confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹
配的队列之后,rabbitMQ就会发送一个Ack给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确
到达目的队列了.如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。处理
Ack和Nack的代码如下所示(说好不上代码的,偷偷上了):
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("nack: deliveryTag = “+deliveryTag+” multiple: "+multiple);
}
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("ack: deliveryTag = “+deliveryTag+” multiple: "+multiple);
}
});
(2)消息队列丢数据
处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你
可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡
了,那么生产者收不到Ack信号,生产者会自动重发。
那么如何持久化呢,这里顺便说一下吧,其实也很容易,就下面两步
1、将queue的持久化标识durable设置为true,则代表是一个持久的队列
2、发送消息的时候将deliveryMode=2
这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据
(3)消费者丢数据
消费者丢数据一般是因为采用了自动确认消息模式。这种模式下,消费者会自动确认收到信息。这时rahbitMQ
会立即将消息删除,这种情况下如果消费者出现异常而没能处理该消息,就会丢失该消息。
至于解决方案,采用手动确认消息即可。
如何保证消息的顺序性?
项目有关的问题
怎么保证APP接口传数据的安全性
第一种方案:
用户登录时传给服务器一个用户的唯一标示(比如token),之后用户在做每一个操作时都必须带上token。来
确保数据传输的安全性。
第二种方案:
用ssl(SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。)做双端验证。用Https协
议做通信。
如何确保服务端的接口调用安全?
在设计API时,要保证RESTful API的安全性,主要考虑三个大方面:
1.对受限资源的登录授权
2.对请求做身份认证
3.对敏感数据进行加密
思路:
一、受限资源的登录授权
此流程不是本文重点,不赘述,基本流程如下:
客户端提交账号信息(用户名+密码)到服务端
服务端验证成功,返回AccessToken给客户端存储
3.访问受限资源时,客户端带入AccessToken就可访问。
二、请求认证
如果不对请求进行签名认证,那么可以简单的通过fiddler等工具轻易抓包拿到数据,并进行篡改,提交,大规
模批量调用,则会使系统产生大量垃圾数据,系统资源被大量消耗,甚至无法正常使用(另说,当然可以通过
GateWay进行限流),因而我们需要对请求进行签名认证。
URL格式
URL:schema://domain/path?query&imei×tamp&sign
参数说明
签名方法
sign=signature(path?query&imei×tamp&SIGN_KEY)
验证过程
认证逻辑
1、初始时,服务端存有各App版本的SIGN_KEY,客户端存有对应版本的SIGN_KEY
2、当要发送请求之前,通过签名方法加密,得到一个sign
3、发送请求的时候,连同sign一起发送给服务器端
4、服务器端首先验证时间戳timestamp是否有效,比如是服务器时间戳5分钟之前的请求视为无效;
5、然后取对应版本的SIGN_KEY验证sign是否合法
6、为了防止重放攻击,需要检查sign是否在redis中存储,如不存在则存入redis(缓存5分钟)
如何防止数据篡改
这里通过签名参数中包含原有请求的所有参数,改动任意参数,sign值都会不同,因此无法篡改。
如何防止重放攻击
由于签名算法中还有imei(设备唯一Id)、timestamp参数,且签名算法为不可逆算法(如md5或sha1),因
而对于正常的每个请求sign值不会重复。此时服务端可以存储5分钟的sign值,来做重放攻击时的验证过滤,
超过5分钟的请求则直接被timestamp校验过滤。
总结
如此便实现了请求认证,防止数据篡改,重放攻击,但是需要确保App密钥(SIGN_KEY)的安全保存,其优点是
容易理解与实现,缺点是需要承担安全保存密钥和定期更新密钥的负担。
三、敏感据加密
1)、部署SSL基础设施(即HTTPS),敏感数据的传输全部基于SSL。
2)、仅对部分敏感数据做加密(例如账号+密码),并加入某种随机数作为加密盐,以防范数据被篡改。