Spirng+In+Action(Craig Walls Ryan Breidenbach)
目录
1.开始Spring之旅(简介)
2.装配Bean(IoC)
3.创建切面(AOP)
。。。
第一章:开始Spring之旅
1.1 为什么使用Spring:简化了企业级系统开发。
1.1.1 javaEE开发者的一天:EJB功能强大,即使简单的应用系统都需要各种组件的支持,使用较为复杂。
1.1.2 Spring的承诺:
1.好的设计比实现技术更重要
2.通过接口松散耦合的JavaBean是一个很好的模型
3.代码应该容易被测试
1.2 Spring是什么:Spring是一个轻量级的IOC和AOP容器框架
轻量级:1MB多的jar包源代码
非侵入式:基于Spring开发系统中的对象一般不依赖于Spring框架中的类
反向控制:创建一个对象时,需要在容器系统中寻找所对应的类,而反向控制,则是在容器启动实例化时,主动为引用的实例类注入依赖类对象
面向切面:提取多种业务的共性,形成一种过滤效果,使开发者只需要关注具体的逻辑业务问题
容器:可以管理系统中对象的生命周期与设置(BeanFactory/ApplicationContext)
框架:通过XML文件配置组合
spring组成模块:
1.3 开始Spring之旅
//接口定义 package com.cys.test;
public interface GreetingService { public void sayGreeting(); }
//具体实现类 package com.cys.test; public class GreetingServiceImpl implements GreetingService { private String greeting; public GreetingServiceImpl() {} public GreetingServiceImpl(String greeting) { this.greeting = greeting; } public void sayGreeting() { System.out.println("GreetingServiceImpl [greeting=" + greeting + "]"); } public void setGreeting(String greeting) { this.greeting = greeting; } }
//主函数类 package com.cys.test; import java.io.FileInputStream; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; public class HelloApp{ public static void main(String[] args) throws Exception{ BeanFactory factory = new XmlBeanFactory(new FileInputStream("spring-beans.xml")); GreetingService greeting = (GreetingService)factory.getBean("greetingService"); greeting.sayGreeting(); } }
<?xml version="1.0" encoding="UTF-8"?> <!-- xml配置文件 --> <beans> <!-- 无参数构造 --> <bean id="greetingService" class="com.cys.test.GreetingServiceImpl"> <property name="greeting" value="Hello World" /> </bean> <!-- 带参数构造 --> <bean id="greetingService" class="com.cys.test.GreetingServiceImpl"> <constructor-arg> <value>Hello World</value> </constructor-arg> </bean> </beans>
1.4 理解反向控制(依赖注入):获得依赖对象的方式反转
1.5 应用AOP:IoC使软件组件松散连接成为可能,AOP让你能够捕捉经常使用的功能,把该功能转化为组件
1.6 Spring比较
小结:
第二章:装配Bean
2.1 容器是Spring框架的核心:BeanFactory、ApplicationFactory
2.1.1 BeanFactory:Bean简单工厂管理模式
常见实现类:XmlBeanFactory
BeanFactory factory = new XmlBeanFactory(new FileInputStream("beans.xml"));
MyBean myBean = factory.getBean("myBeanId");
2.1.2 ApplicationFactory:提供更多附加的功能(文本信息解析工具、载入文件资源、注册监听器)
常见实现类:ClassPathXmlApplicationFactory、FileSystemXmlApplicationFactory、XmlWebApplicationFactory
ApplicationFactory factory = new ClassPathXmlApplicationFactory("beans.xml");
ApplicationFactory factory = new FileSystemXmlApplicationFactory("c:/beans.xml");
XmlWebApplicationFactory:见第八章
2.1.3 Bean生命周期
Bean工厂容器与ApplicationContext容器的唯一区别:ApplicationContext容器会调用ApplicationContextAware接口的setApplicationContext()方法
2.2 基本配置(Beans.xml)
2.2.1 xml配置:<beans><bean id="" class=""/><beans ...></beans>
2.2.2 添加一个Bean
bean属性:
单实例与原型:singleton="true"--单实例(默认) singleton="false"--原型(每次getBean(),获取的对象都是一个新的)
实例化与销毁:init-method="name1"--初始化时调用name1()方法 destroy-method="name2"--销毁时调用name1()方法
2.2.3 set属性注入
基本类型注入(int、String、...):<property name="xxx"><value>xxx(值)</value></property>
引入其他bean:<property name="xxx"><ref bean="xxx(beanId)"></property>(常用)、<property name="xxx"><bean class="" /></property>(不常用)
List与数组:<property name="xxx"><list><value>xxx(值)</value><ref ...><...></list></property>(值可以是任意元素)
Set:<property name="xxx"><set><value>xxx(值)</value><ref ...><...></set></property>(值可以是任意元素)
Map:<property name="xxx"><map><entry key="xxx1"><value>xxx(值)</value></entry><entry key="xxx2"><ref ...></entry><entry key="..."><...></entry></map></property>(值可以是任意元素)
Property:<property name="xxx"><prop key="xxx1">xxx(值)</prop><prop key="xxx2">xxx(值)</prop>...</property>(值只能是String类型,不需要<value>标签)
设置null:<property name="xxx"><null/></property>(默认可以不设置,这是显示装配)
2.2.4 构造函数注入
与set同理,<property>改为<constructor-arg>,并提供了index、tyep属性
index:参数序号,从0开始--<constructor-arg index="0"><value><ref ...>...</constructor-arg>
type:属性类型(java.lang.String)--<constructor-arg type="java.lang.String"><value><ref ...>...</constructor-arg>
默认情况可以不设置,在多个参数类型相同时,为区分参数先后顺序,必须设置index属性
set注入与构造函数注入区别:构造函数可以强制关联依赖关系,达到初始化一个完整可用的对象
2.3 自动装配Bean(一般不考虑)
。。。
2.4 特殊bean:实现spring特定的接口,在bean生命周期的过程中处理bean信息
2.4.1 对bean进行后处理:实例化后初始化前--初始化后
接口:BeanPostProcessor
覆盖方法(实例化后初始化前方法):postProcessBeforeInitialization(Object bean, String name)
覆盖方法(初始化后方法):postProcessAfterInitialization
注册bean:
Bean工厂容器:factory.addBeanPostProcssor(BeanPostProcessor接口类的实现类)
上下文容器:在xml配置文件里添加实现BeanPostProcessor接口的类bean即可,上下文容器会自动识别:<bean id="" class="" />
spring框架自动BeanPostProcessor实现
。。。
2.4.2 对Bean工厂进行后处理:实例化之前,即容器加载bean配置文件定义,开始实例化每个bean之前
接口:BeanFactoryPostProcessor
覆盖方法(实例化之前):postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
2.4.3 分散配置:Spring容器定义bean信息集中在一个配置文件里,不利于维护和修改,容易形成硬性编码
ApplicationContext上下文容器自带引用加载properties类型文件实现类:PropertyPlaceholderConfigurer
在xml配置文件里添加以下代码,加载属性,可以使用${key}来获取properties属性文件所对应的值
<bean id="xxx" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>xxx.properties</value>
<value>...</value>
<property>
</bean>
2.4.4 属性类型编辑器:对参数值类型转换接收
。。。
2.4.5 解析文本信息
。。。
2.4.6 监听事件:ApplicationContext容器在生命周期中默认会触发许多事件
触发事件:ContextCloseEvent--应用上下文关闭时触发 ContextRefreshedEvent--应用上下文初始化或刷新时触发 RequestHandledEvent--Web服务应用上下文中请求被处理后触发
这个三个事件类都是抽象类org.springframework.context.ApplicationEvent的子类
监听事件处理:实现org.springframework.context.ApplicationListener接口,实现onApplicationEvent方法,并在spring配置文件里注册bean
2.4.7 设置触发事件
。。。
2.4.8 感知bean:用于获取bean属性信息
常用接口:BeanNameAware--获取beanID BeanFactoryAware--获取bean容器 ApplicationContextAware--获取bean容器
bean类实现BeanNameAware接口,获取beanID值
//声明属性,设置属性值,注册bean后,自动加载属性赋值
public class GetBeanName implements BeanNameAware{
private String beanName;
public void setBeanName(String beanName){
this.beanName = beanName;
}
}
bean类实现BeanFactoryAware接口,获取容器
//声明属性,设置属性值,注册bean后,自动加载属性赋值
public class GetBeanFactory implements BeanFactoryAware{
private BeanFactory factory;
public void setBeanFactory(BeanFactory factory){
this.factory= factory;
}
}
bean类实现ApplicationContextAware接口,获取容器
//声明属性,设置属性值,注册bean后,自动加载属性赋值
public class GetBeanFactory implements ApplicationContextAware{
private ApplicationContext context;
public void setApplicationContext(ApplicationContext context){
this.context = context;
}
}
第三章:创建切面
3.1 AOP介绍
3.1.1 定义AOP术语:切面(Aspect)、连接点(Joinpotion)、通知(Advice)、切入点(Pointcut)、引入(Introduction)、目标对象(Target)、代理(Proxy)、织入(Weaving)
3.2.2 Spring的AOP实现
。。。
3.2 创建通知
3.2.1 前置通知:目标方法前执行
//创建通知实体类,实现前置通知接口,复写接口方法 public class WelcomeAdvice implements MethodBeforeAdvice{ //Method:目标方法 //args:目标方法参数列表 //target:目标对象 public void before(Method method, Object[] args, Object target){ //具体通知逻辑业务 } }
<!-- 配置文件 --> <bean id="xxx1" class="目标对象" /> <bean id="xxx2" class="具体通知对象"> <bean id="xxx3" class="org.springframework.aop.framework.ProxyFactoryBean(代理对象)"> <property name="proxyInterfaces(目标对象实现的接口类)"> <value>xxx.xxxxx.xxxx.xxx4</value> </property> <property name="interceptorNames(通知列表)"> <list> <value>xxx2</value> ... </list> </property> <property name="target(目标对象)"> <value>xxx1</value> </property> </bean>
3.2.2 后置通知:目标方法后执行
//创建通知实体类,实现后置通知接口,复写接口方法 public class ThankYouAdvice implements AfterReturningAdvice{ //returnValue:目标方法的返回值 //Method:目标方法 //args:目标方法参数列表 //target:目标对象 public void afterReturning(Object returnValue, Method method, Object[] args, Object target){ //具体通知逻辑业务 } }
3.2.3 环绕通知:目标方法前后都行,但需要手动调用目标方法并返回值
//创建通知实体类,实现环绕通知接口,复写接口方法 public class OnePerCustomerInterceptor implements MethodInterceptor{ //MethodInterceptor:封装目标对象相关类 public Obeject invoke(MethodInvocation invocation){ //手动调用目标方法,返回目标方法返回值 Obeject xxx = invocation.proceed(); //手动返回 return xxx; } }
3.2.4 异常通知:调用目标方法抛出异常时执行
实现ThrowsAdvice接口,并覆盖void afterThrowing(Throwable throwable)与void afterThrowing(Throwable throwable, Method method, Object[] args, Object target)任意一个方法
3.2.5 引入通知:给目标对象添加行为或属性
。。。
3.3 定义切入点:定义通知在那些地方应用
3.3.1 在Spring中定义切入点
//切入点核心接口 public interface Poincut{ //类过滤 ClassFilter getClassFilter(); //方法过滤 MethodMatcher getMethodMatcher(); }
//类过滤接口 public interface ClassFilter{ //根据类名过滤 boolean maches(Class clazz); }
//方法过滤接口 public interface MethodMatcher{ //判断是否目标方法是否需要通知 boolean maches(Method m, Class targetClass); //如果maches返回true,则调用判断是静态通知还是动态通知 public boolean isRuntime(); //只有isRuntime返回true,则为动态通知,才会执行以下方法 public boolean maches(Method m, Class targetClass, Object[] args); }
提示:尽量使用静态通知,动态通知对性能会有消耗
3.3.2 理解Advisor:通知与切入点整合接口类
//Advisor接口 public interface PointcutAdvisor{ //获取切入点类 Pointcut getPointcut(); //获取通知类 Advice getAdvice(); }
3.3.3 使用Spring的静态切入点
父类:StaticMethodMatcherPointcut
常用子类(名称映射):NameMatchMethodPointcut
映射方法:public void setMappedName(String), public void setMappedName(String[]);
配置NameMatchMethodPointcut类型切入点:
<beans> <bean id="xxx1" class="(目标实例类)"/> <bean id="xxx2" class="(通知类)"/> <!-- 定义切入点 --> <bean id="xxx3" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <!-- 定义通知切入的规则 --> <property name="mappedName"> <value>order*</value> <!-- 不适用通配符,指定具体切入方法 <list> <value>orderxxx1</value> <value>orderxxx2</value> </list> --> </property> <!-- 绑定切入点的通知 --> <property name="advice"> <ref bean="xxx2" /> </property> </bean> <!-- 切面代理类 --> <bean id="xxx4" class="org.springframework.aop.framework.ProxyFactoryBean(代理对象)"> <property name="proxyInterfaces(目标对象实现的接口类)"> <value>xxx.xxxxx.xxxx.xxx5</value> </property> <property name="interceptorNames(通知列表)"> <list> <value>xxx3</value> ... </list> </property> <property name="target(目标对象)"> <value>xxx1</value> </property> </bean> </beans>
规则表达式切入点:RegexpMethodPointcut
配置RegexpMethodPointcut类型切入点:
<beans> <bean id="xxx1" class="(目标实例类)"/> <bean id="xxx2" class="(通知类)"/> <!-- 定义切入点 --> <bean id="xxx3" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 定义通知切入的规则 --> <property name="pattern"> <!-- 定义通知切入的规则:所有类以get开头,后面至少有一个字符,接着有By,再后面至少有一个字符 --> <value>.*get.+By.+</value> </property> <!-- 绑定切入点的通知 --> <property name="advice"> <ref bean="xxx2" /> </property> </bean> <!-- 切面代理类 --> <bean id="xxx4" class="org.springframework.aop.framework.ProxyFactoryBean(代理对象)"> <property name="proxyInterfaces(目标对象实现的接口类)"> <value>xxx.xxxxx.xxxx.xxx5</value> </property> <property name="interceptorNames(通知列表)"> <list> <value>xxx3</value> ... </list> </property> <property name="target(目标对象)"> <value>xxx1</value> </property> </bean> </beans>
3.3.4 动态切入点
。。。
3.3.5 切入点合并与交叉:通过切入点与切入点、对象、方法、等等的组合与交集形成新的切入点,创建一个实体类,编写参数切入点组合,通过属性配置bean注入切入点参数
1.编写切入点组合工具类
//编写组合代码工具类 public class UnionPointcut implements Pointcut{ //声明合并的Pointcut实例 private Pointcut delegate; //接口方法 public ClassFilter getClassFilter(){ return getDelegate().getClassFilter(); } public MethodMatcher getMethodMatcher(){ return getDelegate().getMethodMatcher(); } //声明异常 private Pointcut getDelegate(){ if(delegate == null){ throw new AopConfigException("No pointcuts have configured."); } return delegate; } //组合切入点的逻辑代码 public void setPointcuts(List pointcuts){ if(pointcuts == null || pointcuts.size() == 0){ throw new AopConfigException("Must have at least one Pointcut."); } } delegate = (Pointcut)pointcuts.get(0); for(int i = 1; i<pointcuts.size(); i++){ Pointcut pointcut = (Pointcut)pointcuts.get(i); delegate = Pointcuts.union(delegate, pointcut); } }
配置<bean id="xxx" class="xxx.xxx.UnionPointcut"><property name="pointcuts"><Lits><value>xxx</value>...</List></property></bean>
2.编写切入点交集工具类:
。。。
3.4 创建引入切面:对已有的实体类添加行为或属性
。。。
3.5 ProxyFactoryBean详解
常用属性:target、proxyInterfaces、interceptorNames
3.6 自动代理:BeanNameAutoProxyCreator、DefaultAutoProxyCreator
BeanNameAutoProxyCreator可以的代理类型:切入点、通知、拦截器,DefaultAutoProxyCreator只能代理切入点(Advisor)
BeanNameAutoProxyCreator配置
<bean id="xxx1" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <!-- 正则表达式 --> <value>*Service</value> </list> </property> <property name="interceptorNames"> <value>xxx(切入点、通知、拦截器)</value> </property> </bean>
DefaultAutoProxyCreator配置:
<bean id="xxx1" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref bean="xxx2(通知)"> </property> <property name="pattern"> <value>.+Service\..+</value> </property> </bean> <!-- 自动代理Advisor通知 --> <bean id="xxx3" class="org.springframework.aop.framework.autoproxy.DefaultAutoProxyCreator"/>