1.谈谈你对 springmvc 的理解

第一印象,以为问的 spring,但是别人问的好像是 springmvc

ps: spring mvc 类似于 struts 的一个 MVC 开框架,其实都是属于 spring,spring mvc需要有 spring 的架包作为支撑才能跑起来,所以 spingMVC 只是 sping 中一个小功能,小模块而已。

继续谈 spring:

Spring 的两大核心 AOP 与 IOC,可以单独用于任何应用,包括与 Struts 等 MVC 框架与Hibernate 等 ORM 框架的集成。

Spring 的核心是 Bean 工厂(Bean Factory),用以构造我们所需要的(Model)。
在此基础之上,Spring 提供了 AOP(Aspect-Oriented Programming, 面向层面的编程)的实现,用它来提供非管理环境下申明方式的事务、安全等服务;

对 Bean 工厂的扩展 ApplicationContext 更加方便我们实现 J2EE 的应用;

DAO/ORM 的实现方便我们进行数据库的开发;

Web MVC 和 Spring Web 提供了 Java Web 应用的框架或与其他流行的 Web
框架进行集成。

那么 AOP 和 IOC 又是什么呢?

IOC 控制反转,简单的理解就是将类的创建和依赖关系写在配置文件里,由配置文件注入,实现了松耦合。

形象的说明:所谓依赖,从程序的角度看,就是比如 A 要调用 B 的方法,那么 A 就依赖于B,反正 A 要用到 B,则 A 依赖于 B。所谓倒置,你必须理解如果不倒置,会怎么着,因为A 必须要有 B,才可以调用 B,如果不倒置,意思就是 A 主动获取 B 的实例:B b = new B(),这就是最简单的获取 B 实例的方法(当然还有各种设计模式可以帮助你去获得 B 的实例,比如工厂、Locator 等等),然后你就可以调用 b 对象了。

所以,不依赖倒置(控制反转),意味着 A 要主动获取 B,才能使用 B;

AOP 面向切面,简单理解:就是将安全,事务等于程序逻辑相对独立的功能抽取出来,利用 spring 的配置文件将这些功能插进去,实现了按照方面编程,提高了复用性。

形象的说明:面向切面编程的目标就是分离关注点。什么是关注点呢?就是你要做的事,就是关注点。

从 Spring 的角度看,AOP 最大的用途就在于提供了事务管理的能力。事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦),所以,Spring 在你访问数据库之前,自动帮你开启事务,当你访问数据库结束之后,自动帮你提交/回滚事务!

2.关于 springmvc 和 structs 的对比

很常见的问题,个人理解,从两方面来说:

a> 设计思想上: spring 可以理解为一个 servlet 的扩展,而 structs 则更符合面向对象思想,它是一个过滤器 filter.

b> 实现机制上:springmvc 的拦截是方法级别的拦截,一个方法对应一个 URL 以及一个request 和 response 对象,所以 URL 参数对于方法是独享的,而 structs 则是类级别的拦截,那么每一次请求的 URL 都会实例化这个 action 类,而类属性被所有方法共享,所以一个 action 共享一个 request 和 response 对象,参数的传递是通过 get/set 来实现。

c> 对 于 ajax 的支持 : springmvc 对 于 ajax 的支持更加简单 ,只需一个注解
@ResponseBody ,然后直接返回响应文本即可,而 structs 则需要 out 流输出。

3.对于常见的 POST 请求乱码,该如何处理?

常用的有三种方式:
a> 在 web.xml 中加入 encoding 来过滤 URL
b> 修改 Tomcat 的配置文件,添加编码,使服务器的编码和工程的编码一致
c> 对方法参数进行 String 编码

4.mybatis 的生命周期是怎么样的?

a> 创建 SqlSessionFactory
b>通过 SqlSessionFactory 创建 SqlSession
c>通过 sqlsession 执行数据库操作
d>调用 session.commit()提交事务
e>调用 session.close()关闭会话

5.mybatis 的配置文件有哪些内容?

properties(属性)
settings(配置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)

6.使用 mybatis 的 mapper 接口调用时有

哪些要求?
a> Mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 相同
b> Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的
parameterType 的类型相同
c> Mapper 接口方法的输出参数类型和mapper.xml 中定义的每个sql 的 resultType的类型相同
d> Mapper.xml 文件中的 namespace 即是 mapper 接口的类路径。

7.mybatis 和 hibernate 的不同

a> Mybatis 和 hibernate 不同,它不完全是一个 ORM 框架,因为 MyBatis 需要程序员自己编写 Sql 语句,不过 mybatis 可以通过 XML 或注解方式灵活配置要运行的 sql 语句,并将 java 对象和 sql 语句映射生成最终执行的 sql,最后将 sql 执行的结果再映射生成 java 对象。
b> mybatis 相对于 hibernate 很灵活,体现在 sql 语句的可控性,但是这就对不同数据库的适应就要写复杂的配置文件。

8.mybatis 的缓存?

a> Mybatis 首先去缓存中查询结果集,如果没有则查询数据库,如果有则从缓存取出返回结果集就不走数据库。Mybatis 内部存储缓存使用一个 HashMap, key 为 hashCode + sqlId + Sql 语句。value 为从查询出来映射生成的 java 对象
b> Mybatis 的二级缓存即查询缓存,它的作用域是一个 mapper 的namespace,即在同一个 namespace 中查询 sql 可以从缓存中获取数据。二级缓存是可以跨 SqlSession的。

9.为什么使用 hibernate,好处是什么?

a> 对 JDBC 访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
b> hibernate 的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。

10. 什么是 hibernate 的延迟加载?

延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。在 Hibernate 中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在 Hibernate3 中还提供了对属性的延迟加载。

11. 怎么优化 hibernate?

a> 使用双向一对多关联,不使用单向一对多
b> 灵活使用单向一对多关联
c> 一对多集合使用 Bag,多对多集合使用 Set
d> 表字段要少,表关联可以使用二级缓存

12. hibernate 中 save 和 saveorupdate?

save() 方法很显然是执行保存操作的,如果是对一个新的刚 new 出来的对象进行保存,自然要使用这个方法了,数据库中没有这个对象。
update() 如果是对一个已经存在的托管对象进行更新那么肯定是要使用update() 方法了,数据中有这个对象。
saveOrUpdate() 这个方法是更新或者插入,有主键就执行更新,如果没有主键就执行插入。
在 Hibernate 中 saveOrUpdate()方法在执行的时候,先会去 session 中去找存不存在指定的字段,如果存在直接 update,否则 save,这个时候问题就发生了

13. 缓存技术?

可做缓存的技术,Ehcache, Linkedhashmap, Memcached, Redis,视需求而定。
其中,LinkedHashMap 和 Ehcache 都是单机缓存技术,即只能在一个应用内实现缓存,不能实现多台机器使用相同的缓存区域(分布式缓存),LinkedHashMap 的底层是用 HashMap 实现的,特点元素的排序是按链表方式排序,按写入或输出的顺序排序,最后一次写入或读取的元素放到最后。JDK 自带 Memcached、Redis 都可以适用于分布式缓存,需要独立部署,使多台机器可以使用同一个缓存服务器,实现集群的缓存共享,其中 redis 支持的数据类型更多,使用更方便,最重要的是:Memcached 的数据只能存在内存中,重启后即消失,而 Redis 可以持久化,因此 Redis 可以作为一个 NoSql 数据库使用。

14. 什么是反射机制?

场景:一个大型的软件,不可能一次就把 它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定 是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
反射的优点当然是体现在它的动态性上面,能运行时确定类型,绑定对象。动态编译最大限度发挥了 java 的灵活性,体现了多态的应用,降低类之间的藕合性。 一句话,反射机制的优点就是可以实现动态创建对象和编译,缺点就是会消耗一些资源。

15. hashtable 和 hashmap 的区别?

Hashtable 继承自 Dictionary 类,而 HashMap 是 Java1.2 引进的 Map interface 的一个实现。从 map 接口实现来看,HashMap 是 Hashtable 的轻量级实现(非线程安全的实现),HashMap 允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许。由于是轻量级实现,hash 算法是一致的,不存在谁的效率高谁的效率低。

16. 接口和抽象类的区别?

从两个方面来回答:

a> 语法层面:
接口只能存在 public abstract 方法,而抽象类可以提供成员方法的具体实现。
接口的成员变量是 public static final 类型的,而抽象类是没有限制的。
接口不可以保护静态代码块以及静态方法,而抽象类是可以的。
接口是可以多实现的,抽象类只能单继承。

b> 设计层面:
抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过 ppt 里面的模板。

17. 线程安全和不安全是什么意思?

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据
造成所得到的数据是脏数据。

18. 怎么实现线程?

在语法上有两种,但是他们其实是一种方式, Thread 类的实例就是一个线程但是它需要调用 Runnable 接口来执行,由于线程类本身就是调用的 Runnable 接口所以你可以继承Thread 类或者直接调用 Runnable 接口来重写 run()方法实现线程。

19. Thread 类中的 start() 和 run() 方法有什么区别?

当你调用 run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

20. ThreadLocal 是什么?

ThreadLocal 并不是一个 Thread,而是 Thread 的局部变量,当使用 ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

21. ThreadLocal 和线程同步相比的优势?

ThreadLocal 和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

而 ThreadLocal 则从另一个角度来解决多线程的并发访问。ThreadLocal 会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal 提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进 ThreadLocal。

简单来说:对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal 采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

22. 谈谈你业务中的分布式?

这个问题,从场景来回答,为什么需要分布式?
随着互联网的发展,传统单工程项目的很多性能瓶颈越发凸显,性能瓶颈可以有几个方面。
a> 应用服务层:随着用户量的增加,并发量增加,单项目难以承受如此大的并发请求导致的性能瓶颈。
b> 底层数据库层:随着业务的发展,数据库压力越来越大,导致的性能瓶颈。

针对上面两点,我觉得可以从两方面解决。

应用服务层的解决方案有几种:

应用系统集群:应用系统集群最简单的就是服务器集群,比如:tomcat 集群。应用系统集群的时候,比较凸显的问题是 session 共享,session 共享我们一是可以通过服务器插件来解决。另外一种也可以通过 redis 等中间件实现。

服务化拆分:服务化拆分,是目前非常火热的一种方式。现在都在提微服务话。通过对传统项目进行服务化拆分,达到服务独立解耦,单服务又可以横向扩容。服务化拆分遇到的经典问题就是分布式事务问题。目前,比较常用的分布式事务解决方案有几种:消息最终一致性、TCC 补偿型事务、尽最大能力通知。

底层数据库层:

如果系统的性能压力出现在数据库,那我们就可以读写分离、分库分表等方案进行解决。

23. 怎么解决 session 共享的问题?

其实,我也没处理过,查找了一些资料:
a> 客户端 cookie 加密。(一般用于内网中企业级的系统中,要求用户浏览器端的 cookie不能禁用,禁用的话,该方案会失效)。
b> 集群中,各个应用服务器提供了 session 复制的功能,tomcat 和 jboss 都实现了这样的功能。特点:性能随着服务器增加急剧下降,容易引起广播风暴;session 数据需要序列化,影响性能。
c> session 的持久化,使用数据库或者 redis 来保存 session。就算服务器宕机也没事儿,数据库中的 session 照样存在。特点:每次请求 session 都要读写数据库,会带来性能开销。使用内存数据库,会提高性能,但是宕机会丢失数据(像支付宝的宕机,有同城灾备、异地灾备)。
d> 用共享存储来保存 session。和数据库类似,就算宕机了也没有事儿。其实就是专门搞一台服务器,全部对 session 落地。特点:频繁的进行序列化和反序列化会影响性能。

From: https://hacpai.com/article/1492737273763