1.对spring理解
1)spring是容器框架,创建bean,维护bean之间的关系,使用spring ,没有new对象,我们把创建对象的任务交给spring框架
2)spring是粘合剂,可以管理web层,持久层,业务层,dao层,spring可以配置各个层的组件,并且维护各个层的关系
spring核心:
1)IOC,控制权由对象本身转向容器,由容器根据配置文件创建对象实例并实现各个对象的依赖关系,核心是bean工厂
2)AOP,面向切面编程,核心是代理模式
2.对springmvc的理解
核心是DispatcherServlet,包括映射器、适配器、视图解析器
1)用户发送请求至前端控制器DispatcherServlet
2)DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5)执行处理器(Controller,也叫后端控制器)。
6)Controller执行完成返回ModelAndView
7)HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8)DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9)ViewReslover解析后返回具体View
10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
11)DispatcherServlet响应用户
3.对mybatis理解
mybatis是一个优秀的持久层框架,他对jdbc操作数据库的过程进行了封装,使开发着只用关注sql本身,不用去关注例如注册驱动,加载链接,得到statement,处理结果集等复杂的过程。
mybatis通过xml或者注解的方式,将要执行的各种sql语句配置起来,并通过Java对象和statement中的sql语句映射生成最终的sql语句,最后由mybatis框架执行sql语句,并将结果映射成Java对象返回。
工作原理:获取sqlSession。SQLSession包含了执行sql语句的所有方法,可以通过SQLSession直接运行映射的sql语句,完成对数据的增删改查和事物的提交工作,用完之后关闭SQLSession。
4.对hibernate理解
hibernate是一个开放源代码的对象关系映射(ORM)框架,它对JDBC进行了非常轻量级的对象封装,使得java程序员可以随心所欲的使用对象编程思维来操纵数据库。hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。
工作原理:
1)读取并解析配置文件
2)读取并解析映射信息,创建SessionFactory
3)打开Sesssion
4)创建事务Transation
5)持久化操作
6)提交事务
7)关闭Session
8)关闭SesstionFactory
5.对MQ的理解
是一种分布式应用程序的的通信方法,它是消费-生产者模型的一个典型的代表,producer往消息队列中不断写入消息,而另一端consumer则可以读取或者订阅队列中的消息。RabbitMQ是一款基于AMQP协议可复用的企业消息系统。业务上,可以实现服务提供者和消费者之间的数据解耦,提供高可用性的消息传输机制。
假设P1和C1注册了相同的Broker,Exchange和Queue。P1发送的消息最终会被C1消费。基本的通信流程大概如下所示:
1)P1生产消息,发送给服务器端的Exchange
2)Exchange收到消息,根据ROUTINKEY,将消息转发给匹配的Queue1
3)Queue1收到消息,将消息发送给订阅者C1
4)C1收到消息,发送ACK给队列确认收到消息
5)Queue1收到ACK,删除队列中缓存的此条消息
应用
需要包:org.springframework.amqp.spring-rabbit.1.5.2.RELEASE
com.rabbitmq.amqp-client.3.5.6
org.springframework.ampq.spring-amqp.1.5.2.RELEASE
spring-rabbitmq配置:
<rabbit:connection-factory>连接MQ服务配置
<rabbit:template id="amqpTemplate" exchange="" connectionFactory="">
<rabbit:admin connection-factory="">设置admin自动创建路由
<rabbit:queue id="" durable="" auto-delete="" name="rsp">创建队列
<rabbit:topic-exchange name="" ...><rabbit:bindings><rabbit:binding queue="" pattern="">绑定队列topic类型的路由
MQ用法说明:连接MQ服务端->创建路由并设置属性->创建队列并设置相关熟悉->队列绑定路由->发送消息到路由,路由根据消息key及绑定的队列,把消息转发到相应的队列->发布-订阅模式(队列绑带路由在消费者端绑定,每个消费者都配置不一样的队列名称那么路由会自动创建多个队列,匹配规则跟发布routing key匹配的都会被广播到服务提供方,只要对应的路由,按规则发布消息即可)
<rabbit:listener-container connection-factory="" acknowlege="">消息到达时会通知监听在对应队列上的监听对象
<rabbit:listener queues="" ref="wrListener"><bean id="wListener" class="">
监听类:org.springframework.amqp.core.onMessage
public void onMessage(Message message){
byte[] msgBody = message.getBody();
int funCode = ProtoHelper.getFunCodeForNTAS(msgBody);
try{
byte[] body = ProtoHelper.getProtoForNTAS(msgBody);
switch(funCode){
case 123456{
xxxxxx
}
}
}
}
6.对redis的理解
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
应用
需要包: redis.clients.jedis.2.7.3 org.springframework.data.spring-data-redis.1.7.1RELEASE
spring-redis配置:JedisPoolConfig连接池;JedisConnectionFactory连接工厂;RedisTemplate队RedisConnection进行了分装,提供连接管理序列化等功能,极大方便简化了Redis操作;
RedisUtil工具:redisTemplate.opsForHash().put(key, field, value); 更新或新增缓存
redisTemplate.opsForHash().delete(key, field); 从缓存删除
redisTemplate.opsForZSet().rangeByScore(key, min, max);
持久化方式:
1)RDB:记录一段时间内的操作
2)AOF(append only file)每一步操作都会进行持久化,追加
看了一下项目中redis服务器的redis.config,发现用的是AOF模式
7.zookeeper注册中心(dubbo推荐的)
为分布式应用提供一致性服务的软件,包括:配置维护、域名服务、分布式同步、组服务等。
应用
项目中使用Dubbo框架的情况,Zookeeper主要是做注册中心用。基于Dubbo框架开发的提供者、消费者都向Zookeeper注册自己的URL,消费者还能拿到并订阅提供者的注册URL,以便在后续程序的执行中去调用提供者。而提供者发生了变动,也会通过Zookeeper向订阅的消费者发送通知。
8.protobuf
Google Protocol Buffer(简称 Protobuf)是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通讯协议和数据存储等领域。
- 平台无关,语言无关,可扩展;
- 提供了友好的动态库,使用简单;
- 解析速度快,比对应的XML快约20-100倍;
- 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10。
9.tomcat
结构很复杂,但是也非常的模块化,从上图中可以看出心脏是两个组件:Connector 和 Container
优化:
1)Connector优化:Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以 Connector的优化是重要部分。默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。protocol:协议类型,可选类型有四种,分别为BIO(阻塞型IO),NIO,NIO2和APR,Tomcat在默认情况下,是以bio模式运行的。(BIO更适合处理简单流程,如程序处理较快可以立即返回结果。简单项目及应用可以采用BIO;NIO更适合后台需要耗时完成请求的操作,如程序接到了请求后需要比较耗时的处理这已请求,所以无法立即返回结果,这样如果采用BIO就会占用一个连接,而使用NIO后就可以将此连接转让给其他请求,直至程序处理完成返回为止;APR可以大大提升Tomcat对静态文件的处理性能,同时如果你使用了HTTPS方式传输的话,也可以提升SSL的处理性能。)
2)线程池优化
Executor代表了一个线程池,可以在Tomcat组件之间共享。使用线程池的好处在于减少了创建销毁线程的相关消耗,而且可以提高线程的使用效率。要想使用线程池,首先需要在 Service标签中配置 Executor。
<Service name="Catalina">
<Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="1000"
minSpareThreads="100"
maxIdleTime="60000"
maxQueueSize="Integer.MAX_VALUE"
prestartminSpareThreads="false"
threadPriority="5"
className="org.apache.catalina.core.StandardThreadExecutor"/>
....
线程池配置完成后需要在 Connector中指定:
<Connector executor="tomcatThreadPool"
...
10.Java内存结构
粗糙的来分,Java内存可以分为堆内存与栈内存,堆内存是Java虚拟机所管理的内存中最大的一块,用于存放实例化的对象及数组,属于线程共享的,是垃圾收集器管理的主要区域。包括新生代和老年代,新生代又可以细分为Eden区和2个survivor区;然后栈的话,主要是指虚拟机栈,用于存放对象的引用地址,描述的是Java方法的内存模型,属于线程私有,而且生命周期和线程同步;还有方法区,它存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码;最后是程序计数器,它相当于是当前线程所执行的字节码的行号指示器。
11.Java垃圾回收
首先对象一般会优先在Eden区创建,当eden区满了的时候会触发一次Minor GC,存活下来的对象会进入到from_survivor中,再清除Eden区。当eden和from_survivor满了时,会把存活对象放到to_survivor;如果to_survivor不足以存放则会直接进入到老年代;老年代满了就会进行Full GC。新生代采用复制算法,老年代采用标记-整理法。附都是通过可达性分析算法去判定对象的存活。
12.Java内存模型
Java内存模型定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
用volatile变量对所有线程可见,禁止对其进行重排序保证有序性,但没有保证原子性;使用场景:状态量标记,屏障前后一致性
13.单例模式
// 双重锁:严格单例,懒加载,性能提升
public class SingletonObject {
private static volatile SingletonObject instance;
private SingletonObject () {}
public SingletonObject getInstance() {
if (null == instance) {
synchronized (SingletonObject.class) {
if (null == instance) {
instance = new SingletonObject();
}
}
}
return SingletonObject.instance;
}
}
14.SQL优化
1)建索引
a.字段有大量重复的不要建索引
b.索引不是越多越好,增改操作会因重建索引而变慢
c.避免对索引进行以下操作:计算操作;用not,<>,!=;用IS NULL和IS NOT NULL;数据类型转换;使用函数;使用空值。
2)where之后的优化
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。就是说索引最好能建在where之后要用到的字段上面,但同时也要注意避免上述索引的问题。所以在此重复强调一遍,主要就是避免在WHERE子句中使用in,not in,or 或者having,还有不要任何计算和函数。
a.应尽量避免在 where 子句中使用 != 或 < > 操作符
b.应尽量避免在 where 子句中使用 or 来连接条件,用 union all 来替换:
c.慎用 in 和 not in,改用 exists 和 between
d.应尽量避免在 where 子句中对字段进行表达式操作或者函数
3)select 优化
a.任何地方都不要使用 select * from t
b.推荐使用UNION ALL,尽量避免UNION(UNION和UNION ALL关键字都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同。对重复结果的处理:UNION在进行表链接后会筛选掉重复的记录,Union All不会去除重复记录。对排序的处理:Union将会按照字段的顺序进行排序;UNION ALL只是简单的将两个结果合并后就返回。从效率上说,UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复数据且不需要排序时的话,那么就使用UNION ALL。)
15.类的加载之双亲委派模型
双亲委派模型的过程:如果一个类加载器收到了类加载的请求,首先不会自己去加载,而是把请求为派给自己的父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父类反馈自己无法完成这个加载请求时,子类加载器才会尝试自己完成。
16.一个SQL很慢怎么查原因
1. 没有索引或者没有用到索引
2. 查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)
17.ThreadLocal
ThreadLocal的实例代表了一个线程局部的变量,每条线程都只能看到自己的值,并不会意识到其它的线程中也存在该变量。它采用采用空间来换取时间的方式,解决多线程中相同变量的访问冲突问题。最常见的ThreadLocal使用场景为用来解决数据库连接、Session管理等。get()方法是用来获取ThreadLocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,remove()用来移除当前线程中变量的副本,initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法。
public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
18.AOP应用
19.spring bean作用域
作用域限定了Spring Bean的作用范围,在Spring配置文件定义Bean时,通过声明scope配置项,可以灵活定义Bean的作用范围。例如,当你希望每次IOC容器返回的Bean是同一个实例时,可以设置scope为singleton;当你希望每次IOC容器返回的Bean实例是一个新的实例时,可以设置scope为prototype。
附:很赞的面试分享