一.Spring 概述
1. 什么是spring?
Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。
2. 如何实现简化java开发的目标?
为了降低java开发的复杂性,spring主要采取以下4种关键策略:
- 基于POJO的轻量级和最小侵入性编程。
- 通过依赖注入和面向接口编程实现松耦合。
- 基于切面和惯例进行声明式编程。
- 通过切面和模版减少样式代码。
3. 使用Spring框架的好处是什么?
- 轻量:Spring 是轻量的,基本的版本大约2MB。
- 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
- 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开,并使系统服务模块化。
- 容器:Spring 包含并管理应用中对象的生命周期和配置。
- MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
- 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
- 异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。
提供模版:减少了样板式代码
4. Spring由哪些模块组成?
spring可以分为6个定义明确的模块组成:
- 核心容器模块:是spring中最核心的模块。负责Bean的创建,配置和管理。主要包括:beans,core,context,expression等模块。
- Spring的AOP模块:主要负责对面向切面编程的支持,帮助应用对象解耦。
- 数据访问和集成模块:包括JDBC,ORM,OXM,JMS和事务处理模块,其细节如下:
- JDBC模块提供了不再需要冗长的JDBC编码相关了JDBC的抽象层。
ORM模块提供的集成层。流行的对象关系映射API,包括JPA,JDO,Hibernate和iBatis。
OXM模块提供了一个支持对象/ XML映射实现对JAXB,Castor,使用XMLBeans,JiBX和XStream 的抽象层。
Java消息服务JMS模块包含的功能为生产和消费的信息。
事务模块支持编程和声明式事务管理实现特殊接口类,并为所有的POJO。
- Web和远程调用:包括web,servlet,struts,portlet模块。
- 测试模块:test
- 工具模块
5.常用的几个应用上下文
- ClassPathXmlApplicationContext:从类路径下的xml配置文件中加载上下文定义.
- FileSystemXmlApplicationContext:读取文件系统下xml配置文件并加载
- XmlWebApplicationContext:读取Web应用下的Xml配置文件并加载上下文定义
二.容器
创建应用对象之间关系的传统方法通常会导致对象之间耦合性过高,且难以复用和测试.spring通过容器使得对象无需自己负责查找和创建与其关联的其他对象,做到解耦.
装配:创建应用对象之间协作关系的行为称为装配,这是依赖注入的本质.
1.依赖注入(DI)
概念:对象的依赖关系将有负责协调系统中各个对象的第三方组建在创建对象时设定。对象无需自行创建或管理它们的依赖关系—依赖关系将自动注入到需要它们的对象中去。
对象无需知道依赖对象来自何处或依赖的实现方式。
依赖注入能让相互协作的软件组件保持松散耦合。AOP编程允许把你遍布应用各处的功能分离出来形成可重用的组件
2.Spring容器可归为两种:
Bean工厂(由org.springframework.beans.factory.BeanFactory)
应用上下文(org.springframework.context.ApplicationContext)
3.Spring框架中bean的生命周期。
- Spring容器 从XML 文件中读取bean的定义,并实例化bean。
- Spring根据bean的定义填充所有的属性。
- 如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法。
- 如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方法。
- 如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。
- 如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。
- 如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方法将被调用。
- 如果bean实现了 DisposableBean,它将调用destroy()方法。
4.spring的核心框架带有10个命名空间配置
5.构造器注入
6.通过工厂方法创建Bean
<bean id="" class="工厂类" factory-method="对应方法" />
7.Bean的作用域
Spring框架支持以下五种bean的作用域:
- singleton : bean在每个Spring ioc 容器中只有一个实例。
- prototype:一个bean的定义可以有多个实例。
- request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
- global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
缺省的Spring bean 的作用域是Singleton.
8.Bean的初始化的销毁
为Bean定义初始化和销毁只需要使用init-method和destroy-method参数配置<bean>元素就可以.定义全部bean的默认的初始化和销毁在<beans> 元素里配置default-init-method和default-destroy-metho.另一种方式时通过分别实现 InitializingBean和DisposableBean 接口,但这种方法产生了应用与框架的耦合,建议使用第一种.
<beans
…....
default-init-method="onLight"
default-destroy-method=”offLight”
>
<bean id="light" class="joe.spring.util.Light"/>
</beans>
<!--注:该方法所在的类必须创建Bean -->
9.注入bean属性
<!--简单值-->
<property name="" value=""/>
<!--引用-->
<property name="" ref=""/>
或
<property name="">
<bean class="" />
</property> <!--使用命名空间p装配-->
<bean id="" class="" p:strs="" p:user-ref="" />
集合属性的装配包括:list,set,map,properties.
- list:内容成员有<ref bean=""/>,<value>,<bean><null/>. list中还可以包含另一个<list>作为成员.list属性也可以用set来装配,同理set属性也可以用list来装配.(如果Bean的属性类型为数据类型或者java.util.Collection接口的任意实现都可以用<list>元素).
- set:同上
- map: 成员包括<entry key="" key-ref=""/> 和<entry value="" value-ref=""/>
- props:可以代替map.但限定键和值必须为String类型.成员<prop key=""> value</prop>
10.SpEL拥有的特性
- 使用bean的ID引用bean
- 调用方法和访问对象的属性
- 对值进行算术,关系和逻辑运算
- 正则表达式匹配
- 集合操作
11.SpEL常用操作
- 字面值:#{5} 值为5,<property name="" value="#{user}"> bean的id. 同时可以访问bean的方法和属性如:#{user.name},#{user.getAge()}
- 操作类:T() 访问类的静态方法和属性. #{T(java.lang.Math).PI},#{T(java.lang.Math).random() }.
- 运算操作:算术运算(+,-,*,/,%,^),关系运行(lt,gt,eq,le,ge),逻辑运算(and,or,not,|),条件运算(? : ),正则运算(matches).
- <util:list> :spring命名空间,定义bean的list集合. <util:list id="cities"> ....</util:list> ,取值用#{cities[2]}
- 从java.util.Properties取值:<util:properties id="settings' location="classpath:settings.properties"/> #{settings['jdbc.Driver']}
- systemEnviroment: 包含应用程序所在机器的所有环境变量,时java.util.Properties的一个集合. #{systemEnviroment['HOME']}
- systemPorperties:包含java应用程序启动时所设置的所有属性. 如-Dapplication.home=/etc/myapp #{systemProperties['application.home']}
- 查询结合 :".^[]"集合中第一个匹配项和".$[]"集合中最后一个匹配项.#{cities.^[population gt 100000]}
- 投影集合".![]":从集合中的每一个成员中选择特定的属性放入一个新的集合中.
12.自动装配Bean属性
spring提供了4中类型的自动装配方式:
- no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配。
- byName: 把与Bean的属性具有相同名字(或ID)的其他Bean自动装配到Bean对应的属性中.如果没有与属性名字一样的bean,则该属性不进行装配.(缺点:名字必须一样)
- byType:把与Bean的属性具有相同类型的其他Bean自动装配到Bean对应的属性中.如果没有,则不装配.如果有多个Bean的类型相同的话Spring会抛出异常,可以通过设置首选Bean或取消某个Bean的自动装配候选资格.设置首选可以在<Bean>中的primary属性设置为true,取消候选资格可以将autowire-candidate设置为false.(如果同时设置:则该bean的设置为取消候选资格.primary默认设置为false,所以需要用primary时得将其他bean的primary设置为true)
- constructor:把与Bean的构造器参数具有相同类型的的其他Bean自动装配到Bean构造器对应的参数中.设置该项,这配置文件中的<constructor-arg>可以省略
- autodetect:首先尝试使用constructor装配,失败则用byType装配
的速度
<beanid="property"class="joe.spring.util.BeanProperty" autowire=”byName”>
<propertyname="string" value="string Value"/>
</bean>
<beanid="user"class="joe.spring.entity.User"/>
设置默认自动装配,即在beans中加入属性default-autowire=”true”
自动装配和手工装配结合的混合装配模式优先考虑手工装配,如果无手工装配,则采用自动装配.
13.注解装配
spring 容器默认关闭注解装配,启用方式是用Spring的context命名空间配置<context:annotation-config>
有三种方式进行注解装配:
- spring自带的@Autowire.可以放在setter上或 任何需要转配bean的方法上.也可以放在私有或公共属性上(可以删除setter).以byType类型去查找bean,所以如果有多个Bean则会报错.解决方案时加入@Qualifier注解指定注入的Bean.可以设置@Autowire(required=false)如果找不到对应的bean则设置为null. required表示是否一定要装配,设置为false找不到这设置为null并且不报错.当多个构造器使用@Autowire 会调用参数满足最多的构造器.
- JSR-330的Inject注解.也可以转配属性,方法,构造器,不同的是没有required这个属性,所以依赖关系必须存在否则报错.针对bean歧义,采用@Name注解. 提供Provider接口可以实现bean的延迟注入以及注入bean的多个实例功能.
- JSR-250 的@Recourse
@Value用于装配String类型的值和基本类型的值(如int,boolean).可以使用SpEL.
14.自动检测Bean
使用自动检测首先需要在spring配置文件配置一下代码
<context:component-scan base-package=”joe.spring.service” ></context:component-scan>
<context:component-scan>会扫描指定的包及其所有子包,并查找能够自动注册的spring Bean类. 默认查找构造型注解所标注的类:
@Component 标注类为spring组件
@Controller 定义为spring mvc controller
@Repository 定义为数据仓库
@Service 将该类定义为服务
使用@Component标注的任意自定义注解
<context:component-scan>配置<context:include-filter type="" expression="">和<context:exclude-filter type="过滤类型" expression="">,进行过滤.
过滤类型包括:
- annotation:过滤器扫描使用制定注解所标注的那些类,通过expression指定要扫面的注解.
- assignable:过滤器扫描派生与expression属性所指定的类型的那些类.
- aspectj:过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类.
- custom:使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定.
- regex:过滤器扫描的名称与expression所指定的正则表达式所匹配的哪些类.
三.面向切面AOP
横向关注点:分布在应用多处的功能.
面向切面编程的目标是将这些横向关注点和业务逻辑分开.
依赖注入(Dependency Injection ,DI)实现了应用之间的解耦,面向切面编程(aspect-oriented programing ,AOP)实现了横向关注点与它们所影响的对象解耦.
切面:横切关注点可以被模块化特殊的类(通知和切点的结合,即what,when,where--它是什么,在何时何处完成其功能.)
通知:切面的工作(定义了切面是什么以及何时调用).五种类型通知:
- before :方法被调用之前调用
- after: 方法完成后调用通知,无论方法执行是否成功
- After-returning: 在方法成功执行之后调用通知
- After-throwing 方法抛出异常后调用通知
- Around :通知包裹了被通知的方法,在被通知的方法调用之前或之后执行自定义的行为
连接点:应用可能对数以千计的时机应用通知,这些时机称为连接点.连接点是在应用执行过程中能够插入一个切面的点.这个点可能是调用方法,抛出异常或修改字段时.切面代码可以利用这些点插入到正常程序流程中,并加入其他行为.
切点:缩小切面所通知的连接点范围.切点即定义何处切入.
引入:允许我们向现有的类添加方法或属性.
织入:将切面应用到目标对象来创建新的代理对象过程.切面在制定的连接点被织入到对象中.在目标对象的声明周期里有多个点可以织入:
- 编译器,切面在目标类编译时被织入
- 类加载器,切面在目标类加载到jvm 时被织入.需要特殊的类加载器,在目标类被引入应用时增强该目标类的字节码
- 运行期,切面在应用运行的某个时刻被织入
spring 提供了4中AOP编程:
1.基于代理的AOP编程
2.@Aspectj 注解驱动的切面
3.纯POJO切面
4.注入式Aspectj切面编程
前面3个是spring基于代理的AOP编程,所以局限于方法拦截.如果要对属性或构造器拦截者应调用 Aspectj里面的切面.
AspectJ的切点表达式语言:
- arg() :限制连接点匹配参数为指定类型的执行方法
- @args():限制连接点匹配参数由指定注解标注的执行方法
- execution():用于匹配是连接点的执行方法
- this():限制连接点匹配AOP代理的Bean引用为指定类型的类(没懂)
- target():限制连接点匹配目标对象为制定类型的类
- @target():限制连接点匹配目特定的执行对象,这些对象对应的类要具备指定类型的注解.
- within():限制连接点匹配指定的类型
- @within:限制连接点匹配指定注解所标注的类型
- @annotation:限制匹配带有制定注解的连接点
还可以使用bean()指示器,使用bean的ID或名称作为参数限定匹配切点只匹配特定的Bean.
使用注解切面时需要在配置文件中加入
<aop:aspectj-autoproxy>
环绕通知,参数 (ProceedingJoinPoint joinpoint)
joinpoint.proceed() 即执行被调用方法