首页 > 代码库 > Spring In Action读书笔记

Spring In Action读书笔记

第一章

1.Spring採用4种策略减少Java开发复杂度


基于POJO的轻量级和最小侵入性编程
依赖注入和面向接口实现松耦合
基于切面和惯例进行声明式编程
通过切面和模板降低样板式代码

PS:POJO

POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。
使用POJO名称是为了避免和EJB混淆起来, 并且简称比較直接. 当中有一些属性及其getter setter方法的类,没有业务逻辑。有时能够作为VO(value -object)或dto(Data Transform Object)来使用.当然,假设你有一个简单的运算属性也是能够的,但不同意有业务方法,也不能携带有connection之类的方法。


2.依赖注入的三种方式

1. 接口注入

2. Setter方法注入 

3. 构造方法注入

详细可參考:http://developer.51cto.com/art/201106/266978.htm

依赖注入:让组件依赖于抽象,当组件要与其它实际对象发生依赖关系时。通过抽象来注入依赖的实际对象。

最大的优点是松耦合。


3.Spring最经常使用的三种应用上下文

1)ClassPathXmlApplicationContext:从classpath处获取xml文件来载入一个上下文。
2)ClassPathXmlApplicationContext:从文件系统中获取xml文件来载入一个上下文。
3)XmlWebApplicationContext:从web应用获取xml文件来载入一个上下文。

这三个类都是ApplicationContext接口的实现。

使用:
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");//config.xml在src文件夹下
ApplicationContext context = new FileSystemXmlApplicationContext("d:/config.xml");


4.Bean的生命周期

传统Java应用中的: new进行实例化,就能够被使用,一旦不被使用,JVM自己主动回收

Spring容器中的Bean:

实例化。填充属性,(假设实现对应接口就)传BeanID。传BeanFactory容器实例,传应用上下文的引用。调用postProcessBeforeInitialization(),调用afterpropertiesSet()。假设声明了init-method,也调用该方法。调用postProcessAfterInitialization方法。
此时已经能够被应用程序使用。将一直驻留在应用上下文中,直到该应用上下文被销毁。
假设Bean实现了DisosableBean接口,调用destroy()接口方法,假设用destroy-method声明了销毁方法。就调用该方法。


第二章

1.两种配置bean的方法

用一个或多个XML;基于Java注解

2.factory-method

假设想声明的bean没有公开的构造方法,能够在配置文件里加factory-method指定返回实例的方法

3.bean的作用域

singleton

在每一个Spring IoC容器中一个bean定义相应一个对象实例。

prototype

一个bean定义相应多个对象实例。每次调用都创建一个实例

request

在一次HTTP请求中。一个bean定义相应一个实例;即每次HTTP请求将会有各自的bean实例。 它们根据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext 情形下有效。

session

在一个HTTP Session 中,一个bean定义相应一个实例。

该作用域仅在基于web的SpringApplicationContext 情形下有效。

global session

在一个全局的HTTP Session 中,一个bean定义相应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在portlet上下文中有效。


4.initi-method和destroy-method

假设非常多bean拥有同样名字的初始化方法和销毁方法,能够在beans元素中定义default-init-method和default-destroy-method


第三章

1.自己主动装配与自己主动检測

自己主动装配:让spring自己主动识别怎样装配bean的依赖关系。降低对<property>元素的使用。

 
自己主动检測:让spring自己主动识别哪些类须要配置成spring Bean,降低对<bean>元素的使用。 

2.4种类型的自己主动装配

1.byName:寻找和属性名同样的bean,若找不到。则装不上。

2.byType:寻找和属性类型同样的bean,找不到,装不上,找到多个抛异常。

3.constructor:查找和bean的构造參数一致的一个或多个bean,若找不到或找到多个。抛异常。

依照參数的类型装配 

4.autodetect: 首先尝试constructor进行自己主动装配。然后再尝试byType. 


 <bean id="bar" class="Bar" autowire="byName"/>


3.@Autowired和@Inject

@Autowired有required属性,@Inject没有。所以inject注解所标注的依赖关系必须存在,假设不存在就抛出异常


4.自己主动检測与过滤条件

首先改动beans.xml。使用<context:component-scan>替代<bean>
@Component 等来标注类
在使用自己主动检測bean 的同一时候,我们还能够通过过滤组件来定义我们的扫描策略。通过为<context:component-config/>配置<context:include-filter/>和<context:exclude-filter/>来实现。

这两个过滤组件的作用正好相反,<context:include-filter/>告知哪些类须要被注冊,<context:exclude-filter/>告知哪些类不须要注冊。

比如:

<context:component-scan>

<context:include-filter type=”assigable”expression=”com.springinaction.springidol.Instrument”/>

</context:component-scan>

type与expression的联合使用使继承Instrument的类注冊为Spring bean。


5.限定依赖

@Qualifier("XXX") 中的 XX是 Bean 的名称。所以 @Autowired 和 @Qualifier 结合使用时,自己主动注入的策略就从 byType 转变成 byName 了。
@Autowired 能够对成员变量、方法以及构造函数进行凝视,而 @Qualifier 的标注对象是成员变量、方法入參、构造函数入參。


第四章


1.AOP的术语

这个内容有点多。直接转载了。

1、连接点(Joinpoint)

 程序运行的某个特定位置:如类開始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。这些代码中的特定点,称为“连接点”。Spring仅支持方法的连接点。即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序运行点织入增强。

 

2、切点(Pointcut)

 每一个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点。但在这为数众多的连接点中。怎样定位到某个感兴趣的连接点上呢?AOP通过“切点”定位特定的连接点。通过数据库查询的概念来理解切点和连接点的关系:连接点相当于数据库中的记录。而切点相当于查询条件。一个切点能够匹配多个连接点。

Spring中,切点通过Pointcut接口进行描写叙述。

 

3、通知(Advice)

    通知是织入到目标类连接点上的一段程序代码。通知既包括了用于加入到目标连接点上的一段运行逻辑,又包括了用于定位连接点的方为信息,所以Spring所提供的增强接口都是带方位名的:BeforeAdvice(方法调用前的位置)、AfterReturningAdvice(訪问返回后的位置)、ThrowsAdvice等。


 

4、目标对象(Target)

    通知逻辑的织入目标类。

 

5、织入(Weaving)

    织入是将通知加入到目标类详细连接点上的过程。AOP有三种织入方式:

1)编译期织入,这要求使用特殊的Java编译器;

2)类装载期织入,这要求使用特殊的类装载器;

3)执行期动态代理织入,在执行期为目标类加入增强生成子类的方式。


Spring採用动态代理织入。而AspectJ採用编译期织入和类装载期织入。

 

6、切面(Aspect)

切面由切点和通知组成,它既包含了横切逻辑的定义,也包含了连接点的定义。Spring AOP就是负责实施切面的框架。它将切面所定义的横切逻辑织入到切面所指定的连接点中。


7、其它基础知识

    Spring AOP使用了两种代理机制:一种是基于JDK的动态代理,还有一种是基于CGLib的动态代理。之所以须要两种代理机制,非常大程度上是由于JDK本身仅仅提供接口的代理,而不支持类的代理。

    JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。

    CGLib採用很底层的字节码技术,能够为一个类创建子类。并在子类中採用方法拦截的技术拦截全部父类方法的调用。

    有研究表明,CGLib所创建的动态代理对象的性能比JDK的所创建的代理对象的性能高了不少。但CGLib在创建代理对象时所花费的时间却比JDK动态代理多。所以对于singleton的代理对象或者具有实例池的代理,由于无须频繁创建代理对象。所以比較适合用CGLib动态代理技术,反之则适合用JDK动态代理技术。

 CGLib不能对目标类中的final方法进行代理。



2.通过XML配置切面

这个能够随便找个样例。非常好理解
如:
  <aop:config>
 5           <!-- 
 6                切入点表达式
 7            -->
 8           <aop:pointcut expression="execution(* cn.itcast.spring.aop.sh.PersonDaoImpl.*(..))" id="perform"/>
 9           <aop:aspect ref="myTransaction">
10           <!-- 
11               <aop:before method="beginTransaction" pointcut-ref="perform"/>
12               <aop:after-returning method="commit" pointcut-ref="perform" returning="var"/>
13            -->
14               <aop:after method="finallyMethod" pointcut-ref="perform"/>
15               <aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/>
16               <aop:around method="aroundMethod" pointcut-ref="perform"/>
17           </aop:aspect>
18       </aop:config>

3.注解切面

 @Component("myTransaction")
26 @Aspect
27 public class MyTransaction extends HibernateUtils{
28     private Transaction transaction;
29     
30     @Pointcut("execution(* cn.itcast.spring.aop.annotation.sh.PersonDaoImpl.*(..))")
31     private void aa(){}//方法签名  返回值必须是void  方法的修饰符最好是private ,aa是随便定义的 做类比
32     
33     @Before("aa()")
34     public void beginTransaction(JoinPoint joinpoint){
35         this.transaction = sessionFactory.getCurrentSession().beginTransaction();
36     }
37     
38     @AfterReturning(value="http://www.mamicode.com/aa()",returning="val")
39     public void commit(Object val){
40         this.transaction.commit();
41     }



第五章

1.可能抛出SQLException的常见问题

1)应用程序无法连接数据库

2)查询语法粗偶

3)查询中使用的表或列不存在

4)视图插入或更新的数据违反了数据库的完整性约束


2.ORM的一些特性

1)延迟载入

比方我们要查询一组PurchaseOrder对象。每一个对象都包括一个LineItem对象所形成的集合。

假设仅仅关心PurchaseOrder对象。那么能够延迟载入lineitem对象。可是会把一次查询分解成多次。

2)预先抓取

与延迟载入相对。预先抓取能够在一个操作中一起提取出lineitem对象,节省多次查询的成本。

3)级联

更改数据库的表会同一时候改动其它表。


3. @repository注解

在类上使用这个注解。会为我们做两件事。首先,会被自己主动检測到。不必声明bean。另外。假设在Spring配置文件里声明了PersistenceExceptionTranslationPostProcessor,它会给全部@repository注解的类上加一个通知器(advisor)。这样就会捕获全部平台相关的异常而且以Spring的非检查型数据訪问异常的形式又一次抛出。


第六章

能够參考http://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/

对Spring事务有具体解说

1.事务的4个特性

ACID

原子性(atomic):事务中的所有操作要么所有发生要不所有不发生。当中随意一个活动失败,整个事务也失败而且回滚。

一致性(Consistent):一旦事务完毕,系统必须确保它所建模的业务处于一致的状态。

隔离性(Isolated): 并发事务之间互相影响的程度,比方一个事务会不会读取到还有一个未提交的事务改动的数据。在事务并发操作时,可能出现的问题有:
     脏读:事务A改动了一个数据。但未提交,事务B读到了事务A未提交的更新结果,假设事务A提交失败,事务B读到的就是脏数据。
     不可反复读:在同一个事务中,对于同一份数据读取到的结果不一致。

比方。事务B在事务A提交前读到的结果。和提交后读到的结果可能不同。

不可反复读出现的原因就是事务并发改动记录。要避免这样的情况。最简单的方法就是对要改动的记录加锁,这回导致锁竞争加剧,影响性能。还有一种方法是通过MVCC能够在无锁的情况下,避免不可反复读。
     幻读:在同一个事务中。同一个查询多次返回的结果不一致。

事务A新增了一条记录,事务B在事务A提交前后各运行了一次查询操作。发现后一次比前一次多了一条记录。幻读是由于并发事务添加记录导致的,这个不能像不可反复读通过记录加锁解决,由于对于新增的记录根本无法加锁。须要将事务串行化。才干避免幻读。
     事务的隔离级别从低到高有:
     Read Uncommitted:最低的隔离级别。什么都不须要做。一个事务能够读到还有一个事务未提交的结果。

全部的并发事务问题都会发生。


     Read Committed:仅仅有在事务提交后,其更新结果才会被其它事务看见。能够解决脏读问题
     Repeated Read:在一个事务中。对于同一份数据的读取结果总是同样的。不管是否有其它事务对这份数据进行操作,以及这个事务是否提交。能够解决脏读、不可反复读


     Serialization:事务串行化运行。隔离级别最高,牺牲了系统的并发性。

能够解决并发事务的全部问题


     通常。在project实践中,为了性能的考虑会对隔离性进行折中。
 持久性(Durability)
     事务提交后。对系统的影响是永久的。

一般涉及到把结果存储到数据库或其它形式的持久化存储中。


     

2.Spring与事务

Spring通过回调机制把实际的事务实现从事务性的代码中抽象出来。分编码式事务和声明式事务。编码式细粒度。声明式易用。
Spring并不直接管理事务,而是提供了多种事务管理器。将事务管理的职责托付给各种事务管理器,适用不同场景。(比方Hibernate,JMS等)

??3.编码式事务

能够方便的使用模板

public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionTemplate transactionTemplate;
......
public boolean transfer(final Long fromId。 final Long toId, final double amount) {
return (Boolean) transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus status) {
Object result;
try {
result = bankDao.transfer(fromId, toId, amount);
} catch (Exception e) {
status.setRollbackOnly();
result = false;
System.out.println("Transfer Error!");
}
return result;
}
});
}
}

4.声明式事务

基于 @Transactional 的声明式事务管理

除了基于命名空间的事务配置方式,Spring 2.x 还引入了基于 Annotation 的方式。详细主要涉及@Transactional 标注。@Transactional 能够作用于接口、接口方法、类以及类方法上。

当作用于类上时,该类的全部 public 方法将都具有该类型的事务属性,同一时候。我们也能够在方法级别使用该标注来覆盖类级别的定义。如清单12所看到的:

清单12. 基于 @Transactional 的事务管理演示样例配置文件
@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId, double amount) {
return bankDao.transfer(fromId, toId, amount);
}

Spring 使用 BeanPostProcessor 来处理 Bean 中的标注,因此我们须要在配置文件里作例如以下声明来激活该后处理 Bean,如清单13所看到的:

清单13. 启用后处理Bean的配置
<tx:annotation-driven transaction-manager="transactionManager"/>


声明式事务通过事务属性来定义。事务属性描写叙述了事务策略怎样应用到方法上。

事务隔离级别

上文有

事务传播行为

所谓事务的传播行为是指,假设在開始当前事务之前。一个事务上下文已经存在,此时有若干选项能够指定一个事务性方法的运行行为。

在TransactionDefinition定义中包含了例如以下几个表示传播行为的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:假设当前存在事务,则增加该事务;假设当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务。假设当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:假设当前存在事务。则增加该事务;假设当前没有事务,则以非事务的方式继续执行。

  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式执行,假设当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式执行,假设当前存在事务,则抛出异常。

  • TransactionDefinition.PROPAGATION_MANDATORY:假设当前存在事务,则增加该事务。假设当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:假设当前存在事务,则创建一个事务作为当前事务的嵌套事务来执行;假设当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

这里须要指出的是。前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享同样的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(假设存在外部事务的话),此时,内嵌事务并非一个独立的事务,它依赖于外部事务的存在。仅仅有通过外部的事务提交,才干引起内部事务的提交,嵌套的子事务不能单独提交。

假设熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就非常easy理解了,事实上嵌套的子事务就是保存点的一个应用,一个事务中能够包含多个保存点。每个嵌套子事务。另外。外部事务的回滚也会导致嵌套子事务的回滚。

事务超时

所谓事务超时,就是指一个事务所同意运行的最长时间,假设超过该时间限制但事务还没有完毕,则自己主动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

事务的仅仅读属性

事务的仅仅读属性是指,对事务性资源进行仅仅读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比方数据源、 JMS 资源,以及自己定义的事务性资源等等。

假设确定仅仅对事务性资源进行仅仅读操作,那么我们能够将事务标志为仅仅读的。以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否仅仅读。

事务的回滚规则

通常情况下。假设在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。

假设没有抛出不论什么异常。或者抛出了已检查异常,则仍然提交事务。

这通常也是大多数开发人员希望的处理方式,也是 EJB 中的默认处理方式。可是。我们能够依据须要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。





Spring In Action读书笔记