最近看完了<<spring in action(第二版)>>,感觉写得确实经典,本人将在接下来的时间来分享个人的学习笔记,内容可能不是太丰富,只是对一些知识点的总结,如果要详细学习的,建议大家去看这本书
1 基本 Bean 装配
1.1 Spring 容器
1、 Bean 工厂
(org.springframework.beans.factory.BeanFactory 接口定义 ) ,提供了基础的依赖注入
BeanFactory factory =
new XmlBeanFactory( new ClassPathResource( "hello.xml" ));
GreetingService greetingService =
(GreetingService) factory.getBean( "greetingService" );
2、 应用上下文
org.springframework.context.ApplicationContext
ApplicationContext ctx =
new ClassPathXmlApplicationContext(
"com/springinaction/chapter01/knight/knight.xml" );
Knight knight =
(Knight) ctx.getBean( "knight" );
1.2 配置的 XML
<? xml version = "1.0" encoding = "UTF-8" ?>
< beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd " >
< bean id = "greetingService"
class = "com.springinaction.chapter01.hello.GreetingServiceImpl" >
< property name = "greeting" value = "Buenos Dias!" />
</ bean >
</ beans >
1.3 创建 Bean
1.3.1 声明一个简单 Bean
< bean id = "sonnet29" class = "com.springinaction.springidol.Sonnet29" />
< bean id = "saxophone" class = "com.springinaction.springidol.Saxophone" />
1.3.2 通过构造函数注入
< bean id = "duke"
class = "com.springinaction.springidol.PoeticJuggler" >
< constructor-arg value= "15" /> <!— 注入数值 -->
< constructor-arg ref = "sonnet29 " /> <!— 注入 XML 中声明的对象 -->
</ bean >
Setter 注入
< bean id = "Saxophonist"
class = "com.springinaction.springidol.Instrumentalist" >
< property name = "song" value = "Jingle Bells" /> <!— 注入简单数值 à
< property name = "instrument" ref = "saxophone" /> <!— 注入对象 à
</ bean >
1.3.3 注入内部 Bean
特点:没有 id 属性
缺点:
1、 不能复用
2、 还会影响 Spring 上下文中 XML 的可读性
< bean id = "Saxophonist"
class = "com.springinaction.springidol.Instrumentalist" >
< property name = "song" value = "Jingle Bells" />
< property name = "instrument" >
<bean class="com.springinaction.springidol.Saxophone" />
</ property >
</ bean >
< bean id = "duke"
class = "com.springinaction.springidol.PoeticJuggler" >
< constructor-arg value= "15" /> <!— 注入数值 -->
< constructor-arg >
<bean class="com.springinaction.springidol.Sonnet29" />
</ constructor-arg />
</ bean >
1.3.4 装配集合
<list> 、 <set> :可与任意的 java.util.Collection 中的属性交换
<map> 、 <props> : props 要求值、键都是 String , map 没有要求
< bean id = "springIdol" class = "com.springinaction.springidol.SpringIdol" >
< property name = "performers" >
< list >
< ref bean = "duke" />
< ref bean = "kenny" />
< ref bean = "hank" />
</ list >
</ property >
</ bean >
< bean id = "hank" class = "com.springinaction.springidol.OneManBand" >
< property name = "instruments" >
<props>
<prop key="GUITAR">STRUM STRUM STRUM</prop>
<prop key="CYMBAL">CRASH CRASH CRASH</prop>
<prop key="HARMONICA">HUM HUM HUM</prop>
</props>
</ property >
</ bean >
<map> 中的每个 <entry> 都有一个键和值组成
key(String),key-ref,value(String),value-ref
< bean id = "hank" class = "com.springinaction.springidol.OneManBand" >
< property name = "instruments" >
< map >
< entry key = "GUITAR" value = "STRUM STRUM STRUM" />
< entry key-ref = "instrument1" value-ref = "saxophone " />
</ map >
</ property >
</ bean >
1.3.5 装配空值
< property name = "someNonNullProperty" > <null/> </ property>
1.3.6 自动装配 (autowire)
byName,byType,constructor,autodetect
< bean id = "duke"
class = "com.springinaction.springidol.PoeticJuggler"
autowire = "constructor" >
<!-- <constructor-arg ref ="sonnet29" /> -->
</ bean >
默认自动装配
在 Spring 配置文件的根元素 <beans> 中设置 default autowire ,就可以将所有的 Bean 设置为自动装配如:
< beans default-autowire = "byName" >
……
</ bean >
缺点:不透明,不推荐使用
1.4 控制 Bean 创建
1.4.1 Bean 范围化
每次请求时生成唯一的实例,设置 scope 属性
singleton,prototype,request,session,global-session
< bean id = "saxophone"
class = "com.springinaction.springidol.Saxophone"
scope="prototype" />
1.4.2 配置单例 Bean
< bean id = "theStage"
class = "com.springinaction.springidol.Stage"
factory-method="getInstance" />
1.4.3 初始化和销毁 Bean
在 Bean 中使用 init-method 和 destroy-method 属性
init-method=” 初始化方法名 ” destroy-method=” 销毁方法名 ”
默认初始化和销毁方法
在 <beans> 中使用
default-init-method=” 初始化方法名 ”,
default-destroy-method=” 销毁方法名 ”
还可以重写 InitializingBean 和 DisposableBean 接口,分别实现 afterPropertiesSet(),destroy() 方法,实现这些接口的缺点是应用 Bean 与 SpringAPI 相互耦合,建议用 init-method 和 destroy-method
2 高级 Bean 装配
2.1 声明父 Bean 和子 Bean
2.1.1 抽象基 Bean 类型
< bean id = "baseSaxophonist"
class = "com.springinaction.springidol.Instrumentalist"
abstract="true" >
< property name = "song" value = "Jingle Bells" />
< property name = "instrument" ref = "saxophone" />
</ bean >
继承
< bean id = "kenny" parent = "baseSaxophonist" />
< bean id = "david" parent = "baseSaxophonist" />
覆盖继承的属性
< bean id = "frank" parent = "baseSaxophonist" >
< property name = "song" value = "Mary had a little lamb" />
</ bean >
2.1.2 抽象共同属性
定义共同属性 song ,但它没有设置 class 属性
< bean id = "basePerformer" abstract="true" >
< property name = "song" value = "Somewhere Over the Rainbow" />
</ bean >
子 Bean 利用自己的 class 属性来确定自己的类型
< bean id = "taylor" class = "com.springinaction.springidol. Vocalist "
parent = "basePerformer"/ >
< bean id = "stevie" class = "com.springinaction.springidol. Instrumentalist "
parent = "basePerformer" >
< property name = "instrument" ref = "guitar" />
</ bean >
2.2 方法注入
方法替换
获取器注入
2.3 注入非 Spring Bean
2.0 可以明确地配置在 Spring 之外实例化的 Bean 。也就是说,这些 Bean 不是由 Spring 创建的,但可以由 Spring 配置
@Configurable("pianist")
public class Instrumentalist implements Performer {
...
}
@Configurable 的作用:
1、 表示 Instrumentalist 实例即使在 Spring 之外创建的,仍然可以由 Spring 进行配置
2、 它把 Instrumentalist 类与 id 为 pianist 的 Bean 关联 起来。当S pring 企图配置 Instrumentalist 实例时,会以 pianist Bean 作为模板
还需在 Spring 配置中添加如下内容:
< aop:spring-configured />
2.4 注册自定义属性编辑器
2.5 使用 Spring 的特殊 Bean
后处理 Bean
Bean 工厂的后处理
配置属性的外在化
提取文本消息
程序事件的解耦
让 Bean 了解容器
2.6 脚本化的 Bean
3 通知 Bean
3.1 AOP 简介
Aspect Oriented Programming ,面向切面编程,切面有助于实现交叉事务的模块化
3.1.1 术语
通知 (Advice)
通知定义了切面是什么以及何时使用
切入点 (Pointcut)
切入点定义了“何地”
切面 (Aspect)
切面是通知和切入点的结合。通知和切入点共同定义了关于切面的全部内容 — 它的功能、在何时和何地完成其功能
连接点 (Joinpoint)
引入 (Introduction)
目标 (Target)
代理 (Proxy)
织入 (Weaving)
3.1.2 Spring 对 AOP 的支持
经典的基于代理的 AOP( 各版本 Spring)
@AspectJ 注解驱动的切面 ( 仅 Spring2.0)
纯 POJO 切面 ( 仅 2.0)
注入式 AspectJ 切面 ( 各版本 )
3.2 创建典型的切面
3.2.1 创建通知
5 种形式
Before( 前 ) , After-returning( 返回后 ) , After-throwing( 抛出后 ) , Around( 周围 ) , Introduction( 引入 )
public class AudienceAdvice implements MethodBeforeAdvice,
AfterReturningAdvice, ThrowsAdvice {
public AudienceAdvice() {}
public void before (Method method, Object[] args, Object target)
throws Throwable {
audience .takeSeats();
audience .turnOffCellPhones();
}
// 前通知,第一个参数是个 java.lang.reflect.Method 对象,它代表要使用这个通知的方法。第二个参数是一个 Objects 数组,是方法被调用时要传递的参数。最后一个参数是方法调用的目标。
public void afterReturning (Object rtn, Method method,
Object[] args, Object target) throws Throwable {
audience .applaud();
}
//rtn 表示从被调用方法返回的值
public void afterThrowing(Method method, Object[] args, Object target,
Throwable throwable) {
audience .demandRefund();
}
// 最后一个参数必须,其他可选
// injected
private Audience audience ;
public void setAudience(Audience audience) {
this . audience = audience;
}
}
定义 Bean
< bean id = "audience" class = "spring.simple.springidol.Audience" />
< bean id = "audienceAdvice"
class = "spring.simple.springidol.AudienceAdvice" >
< property name = "audience" ref = "audience" />
</ bean >
3.2.2 周围通知
由 MethodInterceptor 接口来定义
public class AudienceAroundAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
audience .takeSeats();
audience .turnOffCellPhones(); // 调用前
Object returnValue = invocation.proceed(); // 调用目标方法
audience .applaud(); // 成功返回之后
return returnValue;
} catch (PerformanceException throwable) {
audience .demandRefund(); // 在出现异常之后执行
throw throwable ;
}
}
// injected
private Audience audience ;
public void setAudience(Audience audience) {
this . audience = audience;
}
}
好处:一是能以简洁的方式在一个方法里定义前通知和后通知。如果需要在执行方法之前和之后都使用通知,使用周围通知会比分别单独实现两种通知更方便。利用周围通知还可以检查和修改被通知方法的返回值
必须要调用 proceed() 方法
3.3 定义切点和通知者
3.3.1 正则表达式切点
< bean id = "audiencePointcut"
class = "org.springframework.aop.support.JdkRegexpMethodPointcut" >
< property name = "pattern" value = ".*perform" />
</ bean >
Pattern 属性用于指定方法匹配所使用的切点模板,本例中被设置为一个正则表达式,匹配任何类里名为 perform() 的方法
定义通知:
< bean id = "audienceAdvisor"
class = "org.springframework.aop.support.DefaultPointcutAdvisor" >
< property name = "advice" ref = "audienceAdvice" />
< property name = "pointcut" ref = "audiencePointcut" />
</ bean >
联合切点与通知者
RegexpMethodPointcutAdvisor 是个特殊的通知者类,可以在一个 Bean 里定义切点和通知者
< bean id = "audienceAdvisor"
class = "org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
< property name = "advice" ref = "audienceAdvice" />
< property name = "pattern" value = ".*perform" />
</ bean >
3.3.2 AspectJ 表达式切点
< bean id = "audiencePointcut"
class = "org.springframework.aop.aspectj.AspectJExpressionPointcut" >
< property name = "expression" value = "execution(* *.perform(..))" />
</ bean >
execution(* *.perform(..)):
执行方法时 ( 使用任意返回类型 任意类 .perform() 方法 ( 任意参数 ))
要使用的通知类是 AspectJExpressionPointcutAdvisor
< bean id = "audienceAdvisor"
class = "org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor" >
< property name = "advice" ref = "audienceAdvice" />
< property name = "expression" value = "execution(* *.perform(..))" />
</ bean >
通知者把通知与切点关联起来,从而完整地定义一个切面,但切面在 Spring 里是以代理的方式实现的。
3.4 使用 ProxyFactoryBean
< bean id = "bo"
class = "org.springframework.aop.framework.ProxyFactoryBean" >
< property name = "target" ref = "boTarget" /> // 被通知的 Bean
< property name = "proxyInterfaces"
value = "com.springinaction.springidol.Performer" /> // 应该实现的哪个接口
< property name = "interceptorNames" value = "audienceAdvisor" />// 通知者
</ bean >
3.4.1 抽象 ProxyFactoryBean
< bean id = "audienceProxyBase"
class = "org.springframework.aop.framework.ProxyFactoryBean"
abstract=”true” >
< property name = "proxyInterfaces"
value = "com.springinaction.springidol.Performer" /> < property name = "interceptorNames" value = "audienceAdvisor" />
</ bean >
利用 audienceProxyBase 进行扩展
< bean id = "stevie" parent="audienceProxyBase" >
< property name = "target" ref = "stevieTarget" />
</ bean >
< bean id = "duke" parent="audienceProxyBase" >
< property name = "target" ref = "dukeTarget" />
</ bean >
3.5 自动代理
3.5.1 为 Spring 切面创建自动代理
在 Spring 上下文里声明如下的 <bean>
< bean class = "org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
对于简单的通知或运行于 java5 之前的环境时,以上自动代理是不错的
3.5.2 自动代理 @AspectJ 切面
@Aspect // 声明切面
public class Audience {
public Audience() {}
@Pointcut("execution(* *.perform(..)) ") // 定义演出切点
Public void performance(){}
@Before("performance()") // 定义之前执行
public void takeSeats() {
System. out .println( "The audience is taking their seats." );
}
@Before("performance()") // 定义之前执行
public void turnOffCellPhones() {
System. out .println( "The audience is turning off their cellphones" );
}
@AfterReturning("performance()") // 定义之后执行
public void applaud() {
System. out .println( "CLAP CLAP CLAP CLAP CLAP" );
}
@AfterThrowing("performance()") // 定义出错执行
public void demandRefund() {
System. out .println( "Boo! We want our money back!" );
}
}
把以上 Audience 应用为一个切面还须在 Spring 上下文里声明一个自动代理 Bean ,它知道如何把 @AspectJ 注解的 Bean 转化为代理通知
自动代理创建器类: AnnotationAwareAspectJAutoProxyCreator ,也可以用 aop 命名空间 <aop:aspectj-autoproxy>
注解周围通知
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
System. out .println( "The audience is taking their seats." );
System. out .println( "The audience is turning off their cellphones" );
try {
jp.proceed();
System. out .println( "CLAP CLAP CLAP CLAP CLAP" );
} catch (Throwable throwable) {
System. out .println( "Boo! We want our money back!" );
}
}
3.6 利用 AOP 定义切面
< aop:config >
< aop:pointcut
id = "performance"
expression = "execution(* *.perform(..))" /> <!-- 定义切点 -->
< aop:aspect ref = "audience" >
< aop:before
method = "takeSeats"
pointcut-ref = "performance" />
< aop:before
method = "turnOffCellPhones"
pointcut-ref = "performance" />
< aop:after-returning
method = "applaud"
pointcut-ref = "performance" />
< aop:after-throwing
method = "demandRefund"
pointcut-ref = "performance" />
<aop:around
method="aroundAdvice"
pointcut-ref="performance" />
</ aop:aspect >
</ aop:config >
3.7 注入 AspectJ 切面
( 这部分在学习的时候也未搞清楚 , 示例代码是 .aj 的文件 ,还望高人指点)
package com .springinaction .springidol ;
public aspect JudgeAspect {
public JudgeAspect() {}
pointcut performance() : execution(* perform(..));
after() returning() : performance() {
System.out.println (criticismEngine.getCriticism());
}
// injected
private CriticismEngine criticismEngine;
public void setCriticismEngine(CriticismEngine criticismEngine) {
this.criticismEngine = criticismEngine;
}
}
< bean id = "criticismEngine"
class = "com.springinaction.springidol.CriticismEngineImpl" >
< property name = "criticismPool" >
< list >
< value > I'm not being rude, but that was appalling. </ value >
< value > You may be the least talented person in this show. </ value >
< value > Do everyone a favor and keep your day job. </ value >
</ list >
</ property >
</ bean >
< bean class = "com.springinaction.springidol.JudgeAspect"
factory-method="aspectOf" >
< property name = "criticismEngine" ref = "criticismEngine" />
</ bean >