Spring面试,IoC和AOP的理解
spring 的优点?
1.降低了组件之间的耦合性,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部
什么是DI机制?
依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色
需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中
创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者
因此也称为依赖注入。
spring以动态灵活的方式来管理对象, 注入的两种方式,设置注入和构造注入。
设置注入的优点:直观,自然
构造注入的优点:可以在构造器中决定依赖关系的顺序。
什么是AOP?
面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面
1.面向切面编程提供声明式事务管理
2.spring支持用户自定义的切面
面向切面编程(aop)是对面向对象编程(oop)的补充,
面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。
AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象,
是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。
aop框架具有的两个特征:
1.各个步骤之间的良好隔离性
2.源代码无关性
java 有内存泄露没?
在语法级别上其实没有,你不用去回收内存,内存自动被垃圾回收器回收了。但是写程序的时候,你调用了一些资源,如连接池或者打开一个文件(io),你打开了必须关闭,垃圾回收器不会自动收集,会造成内存泄露。
Java中的内存泄露当然是指:存在无用但是垃圾回收器无法回收的对象。而且即使有内存泄露问题存在,也不一定会表现出来。
Java的一个重要优点就是通过垃圾收集器(GarbageCollection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的责任,而是GC或 JVM的问题。其实,这种想法是不正确的,因为Java也存在内存泄露,但它的表现与C++不同。
在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
简单的说,一个对象没有任何引用,他会被GC;但是如果这个对象一直被引用,就不会被GC,如果你往这个对象添加数据,他就越来越大,就是nusedbut still referenced,就OOM(outofmemeory)了。
Hibernate问题
1 SessoinFactory
a) 用来产生和管理Session
b) 通常情况下每个应用只需要一个SessionFactory
c) 除非要访间多个数据库的情况
d) 关注两个方法即:openSessiongetCurrentsession的区别
i. open session每次都是新的,需要close
ii. getCurrentsession从上下文找,如果有,用旧的,如果没有,建新的
1. 用途,界定事务边界
2. 事务提交自动close
3. 上下文配置可参见xml文件中
<propertyname="current_session_context_classs">thread</property>
4. current_session_context_class (jta、thread常用 managed、custom.Class少用)
a) thread 使用connection但数据库连接管理事务
b)jta (全称javatransaction api)-java分布式事务管理(多数据库访问)
jta由中间件提供(jbossWebLogic等,tomcat不支持)
2 三种状态区别:
a) transient:内存中一个对象,没ID,缓存中也没有
b) persistent:内存中有,缓存中有,数据库有(ID)
c) detached:内存有,缓存没有,数据库有,ID
3exists 与in 选哪个?
fromTopic t where not exists (select m.id from Msg m where m.topic.id=t.id)
fromTopic t where not in(select m.id from Msg m where m.topic.id=t.id)
一般都选择exists ,因为exists的执行效率比in高。
4java 有内存泄露没?
在语法级别上其实没有,你不用去回收内存,内存自动被垃圾回收器回收了。但是写程序的时候,你调用了一些资源,如连接池或者打开一个文件(io),你打开了必须关闭,垃圾回收器不会自动收集,会造成内存泄露。
Java中的内存泄露当然是指:存在无用但是垃圾回收器无法回收的对象。而且即使有内存泄露问题存在,也不一定会表现出来。
Java的一个重要优点就是通过垃圾收集器(GarbageCollection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的责任,而是GC或 JVM的问题。其实,这种想法是不正确的,因为Java也存在内存泄露,但它的表现与C++不同。
在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
简单的说,一个对象没有任何引用,他会被GC;但是如果这个对象一直被引用,就不会被GC,如果你往这个对象添加数据,他就越来越大,就是nusedbut still referenced,就OOM(outofmemeory)了。
5hibernate 中什么是1+N问题?
在Session的缓存中存放的是相互关联的对象图。默认情况下,当Hibernate从数据库中加载Customer对象时,会同时加载所有关联的Order对象(典型的ManyToOne)。以Customer和Order类为例,假定ORDERS表的CUSTOMER_ID外键允许为null,图1列出了CUSTOMERS表和ORDERS表中的记录。
以下Session的find()方法用于到数据库中检索所有的Customer对象:
List customerLists=session.find("from Customer as c");
运行以上find()方法时,Hibernate将先查询CUSTOMERS表中所有的记录,然后根据每条记录的ID,到ORDERS表中查询有参照关系的记录,Hibernate将依次执行以下select语句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID=1;
select * from ORDERS where CUSTOMER_ID=2;
select * from ORDERS where CUSTOMER_ID=3;
select * from ORDERS where CUSTOMER_ID=4;
通过以上5条select语句,Hibernate最后加载了4个Customer对象和5个Order对象,在内存中形成了一幅关联的对象图,参见图2。
Hibernate在检索与Customer关联的Order对象时,使用了默认的立即检索策略。这种检索策略存在两大不足:
(1) select语句的数目太多,需要频繁的访问数据库,会影响检索性能。如果需要查询n个Customer对象,那么必须执行n+1次select查询语句。这就是经典的n+1次select查询问题。这种检索策略没有利用SQL的连接查询功能,例如以上5条select语句完全可以通过以下1条select语句来完成:
select * from CUSTOMERS left outer join ORDERS
on CUSTOMERS.ID=ORDERS.CUSTOMER_ID
以上select语句使用了SQL的左外连接查询功能,能够在一条select语句中查询出CUSTOMERS表的所有记录,以及匹配的ORDERS表的记录。
(2)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略。延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数目。
解决办法:
a) @ManyToOne(fetch=FetchType.LAZY)
//fetch=FetchType.LAZY 解决N+1问题 说明如下:
//当多对一(@ManyToOne)已经设定属性"fetch=FetchType.LAZY "时
//只有当需要时(如:t.getCategory().getName()时)才会去获取关联表中数据 可以解决N+1问题
b) @BatchSize
//@BatchSize 解决N+1问题 说明如下:
//在与查询表(此例中为Topic类)关联的表类(此例中为Category类)头处加@BatchSize(size=5)
//表示每次可查出5条记录 从而减少了select语句的个数
c) join fetch
//join fetch 解决N+1问题 说明如下:
//修改hql语句为--" from Topic t left join fetch t.category c "
d) QBC
//QBC(Query By Criteria) 解决N+1问题 说明如下:
//使用QBC的createCriteria(*.class)执行查询 也可避免N+1问题
6 list和iterate的区别
a) list取所有
b) iterate先取ID,等用到的时候再根据ID来取对象
c) session中list第二次发出,仍会到数据库査询
d) iterate 第二次,首先找session级缓存
7. 一级缓存和二级缓存和査询缓存
a) 什么是缓存
缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。
b) 什么是一级缓存,session级别的缓存
c) I什么是二级缓存,SessionFactory级别的缓存,可以跨越session存在
i. 经常被访间
ii. 改动不大不会经常改动
iii. 数重有限
d) 打开二级缓存
i. hibernate.cfg.xml 设定:
<property
name="cache.use_second_level_cache">true</property>
<property
name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
ii. @Cache注解(由hibernate扩展提供)
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
注:使用EhCache二级缓存 需要导入ehcache-1.2.3.jar及commons-logging-1.0.4.jar包
e) load默认使用二级缓存,iterate默认使用二级缓存
f) list默认往二级缓存加数据,但是查询的时候不使用
g) 如果要query用二级缓存,需打开查询缓存
<propertyname="cache.use_query_cache">true</property>
调用Query的setCachable(true)方法指明使用二级缓存
例如:session.createQuery("fromCategory").setCacheable(true).list();
h) 缓存算法:
i. LRU LFU FIFO
1. Least Recently Used –最近很少被使用
2. Least Frequently Used (命中率高低)
3. First In First Out 按顺序替换
memoryStoreEvictionPolicy= "LRU" (ehcache.xml中配置)
a) 事务:ACID
i. Atomic Consistency Itegrity Durability
事务的原子性、一致性、独立性及持久性事务的原子性是指一个事务要么全部执行,要么不执行.也就是说一个事务不可能只执行了一半就停止了.比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱.不可能划了卡,而钱却没出来.这两步必须同时完成.要么就不完成.事务的一致性是指事务的运行并不改变数据库中数据的一致性.例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变.事务的独立性是指两个以上的事务不会出现交错执行的状态.因为这样可能会导致数据不一致.事务的持久性是指事务运行成功以后,就系统的更新是永久的.不会无缘无故的回滚
b) 事务并发时可能出现的问题
1. 第一类丢失更新(LostUpdate)
2. dirtyread脏读(读到了另一个事务在处理中还未提交的数据)
3. non-repeatableread 不可重复读(同一个事物里,对同一个数据读两回的值是不一样的)
4. second lost update problem 第二类丢失更新(不可重复读的特殊情况)
5. phantom read 幻读(插入与删除问题,在读数据的时候,插入了或删除了数据,影响了读的结果)
(只要考虑2,3,5,其他都是特殊情况)
c) 数据库的事务隔离机制
i. 查看 java.sql.Connection 文档
ii. 1:read-uncommitted 2:read-committed 4:repeatableread 8:serializable(数字代表对应值)
为什么取值要使用 1 2 4 8 而不是 1 2 3 4
1=0000 2=0010 4=0100 8=1000(位移计算效率高)
1. 只要数据库支持事务,就不可能出现第一类丢失更新
2. read-uncommitted(允许读取未提交的数据)会出现dirtyread, phantom-read,non-repeatable read 问题
3. read-commited(读取已提交的数据 项目中一般都使用这个)不会出现dirtyread,因为只有另 一个事务提交才会读出来结果,但仍然会出现
non-repeatableread 和phantom-read 使用read-commited机制可用悲观锁 乐观锁来解决non-repeatableread 和phantom-read问题
4. repeatableread(事务执行中其他事务无法执行修改或插入操作 较安全)
5. serializable解决一切问题(顺序执行事务 不并发,实际中很少用,效率低)
乐观锁和悲观锁的前提也就是hibernate.conection.isolation=2 也就是隔离级别为read-committed
悲观锁是依赖数据库中的锁, 而乐观锁是在程序中做处理, 所以乐观锁的效率要高
d) 设定hibernate的事务隔离级别(使用hibernate.connection.isolation配置取值1、2、4、8)
i. hibernate.connection.isolation = 2(如果不设默认依赖数据库本身的级别)
ii. 用悲观锁解决repeatable read的问题(依赖于数据库的锁)
1. select ... for update
2. 使用另一种load方法--load(xx.class, i , LockMode.Upgrade)
a) LockMode.None无锁的机制,Transaction结束时,切换到此模式
b) LockMode.read在査询的时候hibernate会自动获取锁
c) LockMode.write insert update hibernate 会自动获取锁
d) 以上3种锁的模式,是hibernate内部使用的(不需要设)
e) LockMode.UPGRADE_NOWAIT是 ORACLE 支持的锁的方式
e) Hibernate(JPA)乐观锁定(ReadCommitted)
实体类中增加version属性(数据库也会对应生成该字段,初始值为0),并在其get方法前加
@Version注解,则在操作过程中没更新一次该行数据则version值加1,即可在事务提交前判断该数据是否被其他事务修改过.