参考:网络资料。
- spring简介
spring是目前最流行的开发框架,不论是web项目还是CS项目都能看到spring的身影,这是因为spirng提供了很多常用的组件的同时,不会强制你去使用它们。spring利用两大核心技术(DI 依赖注入和AOP面向切面编程)实现应用程序各模块之间的高度解耦,spring封装了在开发过程中的很多通用模块,大大提高了应用的开发效率。 - spring的优点
1)低侵入式设计,代码污染低
2)独立于各种应用服务器,基于spring框架的应用可以实现一次编写多次运行。
3)spring的ioc容器降低了业务对象替换的复杂性,实现了组件时间的解耦合
4)spring的aop支持了将很多通用任务如安全、事务、日志等进行集中处理,从而提高了代码的复用性,通过减小了核心业务代码的复杂性。
5)spring高度开放,并不强制应用完全依赖于spring,可以自由选择用spring框架的部分功能。 - spring框架的组成结构
- spring的核心机制
1.管理bean
spring容器负责创建、装配和配置对象,并且对他们的声明周期进行管理,容器是spring框架的核心。Spring容器使用依赖呼入管理构成应用组件,它创建互相协作的组件之间的关系。
常用的spring容器入口有三种:
ClassPathXmlApplicationContext:从类路径下的xml配置文件中加载上下文定义,吧应用上下文定义文件当做资源文件。
FileSystemXmlApplicationContext:读取文件系统下的xml配置文件并加载上下文定义。
XmlWebApplicationContext:读取web应用下的xml配置文件并装载上下文定义。 - 依赖注入
spring框架的核心功能有两个
1)Spring容器作为超级大工厂,负责创建,管理所有Java对象,这些Java对象被称为bean。
2)Spring容器管理容器中bean之间的依赖关系,Spring使用一种被称为依赖注入的方式来管理 bean之间的依赖管理,使用依赖注入不仅可以为bean注入普通的属性值,还可以注入其他bean的引用。依赖注入是一种优秀的解耦方式,其可以让bean以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起。
理解依赖注入:
当某个Java对象需要调用另外一个Java对象的方法时,传统 模式下通常有两种做法
1)调用者手动创建被依赖对象,然后再调用被依赖对象的方法
2)还有一种常用方式就 是利用工厂模式,调用者先找到被依赖对象的工,然后主动通过工厂去获取被依赖对象,最后在调用被依赖对象的方法。
这两种调用都是主动方式,会导致调用者与被依赖对象之间的耦合,非常不利于项目的升级和维护,使用spring框架之后,调用者无需主动获取被依赖对象,只要被动接受spring容器为调用者的称为变量赋值即可,由此可以见,使用spirng容器后,调用者获取被依赖对象的方式由原来的主动获取变成了被动接受。
另外从spring容器的角度来看,spring容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入了它的依赖实例。
依赖注入的方式最常用的有以下两种,
1)利用setter方法注入
这种方式简单直接,因而在spring的依赖注入中大量使用。
<bean id="simpleBeanTwos" class="com.spring.test.di.SimpleBeanTwo"/>
<bean id="simpleBeanThree" class="com.spring.test.di.SimpleBeanThree">
<property name="simpleBeanTwos" ref="simpleBeanTwos"/>
</bean>
2)利用构造器注入
这种方式就是驱动spring在底层一反射的方式执行带指定参数的构造器,当执行带参数的构造器时,就可以利用构造器参数对程序变量进行初始化。
<bean id="movieFinder" class="com.spring.test.di.MovieFinder"/>
<!-- 一个参数 -->
<bean id="simpleMovieLister" class="com.spring.test.di.SimpleMovieLister">
<constructor-arg ref="movieFinder"/>
</bean>
这两个注入方式 的对比
setter注入有以下优势:
1)与传统的javabean的写法相似,程序开发人员更容易理解,通过setter方法设置依赖关系,显得很直观很自然。
2)对于复杂依赖,如果采用构造器注入,会导致构造器过于臃肿,难以阅读,Spring在创建bean实例时,需要同时实例化其他依赖的全部实例,会导致性能下降。
3)在某些成员变量为可选的情况下,多参数的构造器显得更加笨重
利用构造器的方式注入有以下优势:
1)构造器注入可以在构造器中决定依赖关系的注入顺序,
2)对于依赖关系无需变化的bean,利用构造器注入更加简洁,无需写入很多setter方法。
3)依赖关系只能 在构造器总设定,则只有组件的创建者才能改变组件的依赖关系,对㢟的调用者而言,组件内部的依赖关系完全透明,符合高内聚低耦合的原则。
实际开发中,尽量采用setter注入为主,构造器注入为辅。对于依赖关系无需变化的注入,尽量采用构造器注入,而其他关系的注入则考虑采用setter注入。
- Spring中的bean
对于开发者莱索,使用spring框架主要做两件事:1)开发bean 2)配置bean。对于spring框架来说,它要做的就是根据配置文件来创建bean实例,并调用实例方法完成依赖注入。
当通过spring容器创建一个bean实例时,不仅可以完成bean的实例化,还可以为bean指定特定的作用域,Spring支持五种作用域:
1)singleton:单利模式,在整个spring Ioc容器中,singleton作用域的bean将只能生产一个实例。
2)prototype:每次通过容器的getBean()方法获取prototype作用域 bean时,将产生一个新的bean实例。
3)rquest:对于每次http请求,request作用域的bean将只产出一个实例,这就是说,在痛一次http请求内,程序每次请求该bean,总是得到同一个实例,只有在web中使用spring容器时才会使用到该作用域。
4)session:对于痛一次会话,值产生一个bean的实例,这意味着在同一次http会话内,程序内存访问bean,得到的总是同一个实例,只有在web应用中使spring时,该作用域才能起作用。
5)global session:每个局部的http Session对应一个bean实例,在典型的情况下,仅在使用portlet context时才有效(web项目)。
如果不制定bean的作用域spring默认使用singleton作用域,prototype作用域的bean实例创建、销毁代价都比较大,而singleton作用域的bean实例一点创建成功,就可以重复使用。因此应避免将bean的作用域设置成prototytpe作用域。 - 利用自动装配简化配置文件
Spring能自动装配bean与bean之间的依赖关系,即无需使用ref显示指定依赖bean,而是由Spring容器检查xml配置文件内容,根据某种规则,为调用者bean注入依赖的bean。
Spring自动装配可以通过beans标签的default-autowire属性指定,该属性对配置文件中的所有bean元素起作用,也可以通过对bean元素的autowire属性指定,该属性只对bean起作用。
autowire和default-autowire可以接受如下值
1)no:不使用自动装配。bean依赖必须通过ref元素定义。这是默认配置,在较大的部署环境中不鼓励改变这种配置,显示配合作者能够得到更清楚的依赖关系。
2)byName:根据setter方法名进行自动装配,spring容器查找容器中的全部bean,找出其id与setter方法名去掉setter前缀,并小写首字母后面的bean来完成注入,如果没有找到匹配bean的实例,则spring不进行注入。
3)byType:根据setter方法的形参类型来自动装配。Spring查找容器中的全部bean,如果正好有一个bean类型与setter方法的形参类型匹配,就自动注入这个bean,如果找到多个这样的bean,就是抛出异常;如果没有找到这个bean,则什么也不会发生,setter方法不会被调用。
4)constructor:与byType相似,区别是用于自动匹配构造参数。如果容器不能恰好找到一个与构造参数匹配的bean,则会抛出异常。
5)autodetect:spring容器根据bean内部是结构,自动决定使用constructor或者byType策略。先按constructor,在按byType进行自动装配。
当一个bean即使用自动装配依赖,又使用ref显示指定依赖时,则显示指定的依赖覆盖自动装配的依赖;对于大型应用不鼓励使用自动装配,虽然自动装配可以减少配置文件的工作,但大大将死了依赖关系的清晰性和透明性,依赖关系的装配依赖于文件的属性名和属性类型会导致bean与bean时间的耦合关系降低到代码层次,不利于高层次解耦。
- 创建bean的三种方式
1)使用构造器创建bean
使用构造器来创建bean实例是最常见的方式,如果不采用构造注入,spring地层会调用bean的无参构造函数,来创建实例,因此要求bean类提供无参构造函数。
<bean id="testBean" name="testBeanName" class="com.spring.test.entity.TestEntity"/>
2)使用静态工厂创建bean
使用静态工厂创建bean时,class属性必须制定,但此时class属性并不是指定bean实例的实现类,而是静态工厂类,spring通过该属性知道由哪个工厂类创建bean实例。
此外,还需要使用factory-method属性来指定静态工厂方法,spring调用静态工厂方法返回一个bean的实例,一单获得的指定bean的实例,spring后面的处理步骤与采用普通方法创建bean实例完成一样,如果静态工厂方法需要参数,则使用`<constructor-args\>`元素来指定静态工厂的方法参数。
<bean id="testFactoryBean" class="com.spring.test.beanfactory.StaticBeanFactory"
factory-method="testBeanFactory"/>
3)使用实例工厂创建bean
实例工厂方法与静态工厂方法只有一个不同:调用静态工厂方法值需要使用工厂类即可,而调用实例工厂方法则需要工厂实例,使用实例工厂方法时,配置bean实例的bean元素无需class属性,配实例工厂方法使用factory-bean指定工厂实例。
<bean id="factoryBean" class="com.spring.test.beanfactory.InstanceBeanFactory"/>
<bean id="instanceFactoryBean" factory-bean="factoryBean" factory-method="getTestEntity"/>
- 协调作用域不同的bean
当singleton作用域的bean依赖于rototype作用域的bean时,会产生不同步的现象,原因是因为当spring容器启动时,容器会预初始化容器中所有的singleton bean,由于singleton bean依赖于prototype bean,因此spring容器在初始化singleton bean之前,会先创建prototype bean,然后才创建singleton bean,接下来将prototype bean注入到singleton bean中。
解决不同步的方法有两种:
1)放弃依赖注入:singleton作用域的bean每次需要prototype作用域的bean时,主动向容器请求新的bean实例,即可保证每次注入的prototype bean实例是最新的实例。
2)利用方法注入
方法通常使用lookup方法注入,使用lookup方法注入可以使spring容器重写与容器中bean的抽象或具体方法,返回查找容器中其他bean的结果,被查找的bean通常是一个non-singleton bean,spring通过使用jdk动态代理或者cglib库修改客户端的二进制文件,从而实现上述要求。
建议采用第二种方法,使用方法注入,为了使用lookup方法注入,大致需要如下两步:
①将调用者bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖bean
②在bean标签中添加lookup-method子元素,使sring为调用者bean实现类实现指定的抽象方法。
public static class I{
public void doSomething(){
System.out.println("I do something.");
}
}
public static abstract class K{
public void process(){
I i = createAnotherBean();
i.doSomething();
System.out.println(i);
}
protected abstract I createAnotherBean();
}
<bean id="k" class="com.spring.test.temp.A$K">
<lookup-method name="createAnotherBean" bean="i"/>
</bean>
- Spring的零配置支持
1)所搜bean
Spring2.5以后,提供了一下几个注解来注册bean类到spring容器
@Component:标注一个普通的Spring Bean类
@Controller:标注一个控制器组件类
@Service:标注一个业务逻辑组件类
@Repository:标注一个Dao组件类
在Spring配置文件中做如下配置,指定自动扫表包及其子包,spring会自动扫描这些包下被以上注解标注的Bean类到sprig容器中
<context:component-scan base-package="com.spring.test">
2)使用@Autowired或@Resource注解进行装配
@Autowired是Spring提供的装配注解,可以标注在setter方法、构造器或成员变量上,实现依赖的自动注入
@Autowired
public void setInstrument(Instrument instrument){
this.instrument = instrument;
}
或
@Autowired
public Instrument(Instrument instrument){
this.instrument = instrument;
}
或
@Autowired
private Instrument instrument;
@Resource是jdk提供的标准的依赖注入注解,利用这个注解可以进一步实现代码与spring的解耦,但更常用的是@Autowired,以为一个基于spring的应用很少会迁移到别的平台。
用法与@Autowird相同。
- Spring的AOP
AOP也就是面向切面编程,它作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。
AOP专门用于处理系统中分布在不同模块中的交叉关注点的问题,在javaEE的应用中经常通过aop来处理一些横切性质的系统级服务,如事务管理,安全检查和缓存,AOP已经成为一种非常常用的解决方案。
使用AspectJ实现AOP
AspectJ是一个基于Java语言的aop框架,提供了强大 的aop功能,主要包含两个部分,一个部分定义了如果表达、定义编程中的语法规范,通过这套规范可以方便的运用aop来解决java语言中存在的交叉关注点问题,另一部分是工具,包括编译 和调整等。
同时,spring本身也提供了一套完整aop解决方案。
AOP中的基本概念
切面:切面即是一个关注点,切面用于组织多个通知,通知放在切面中。
通知:通知定义了切面是什么和在何处使用。
连接点:程序执行过程中明确的一点,比如方法调用,或者抛出异常。
切点:切点用于定义吧切面放在何处,它与通知一起完整的说明了切面。
使用spring aop前,需要在配置文件中添加aop支持的声明
<aop:aspectj-autoproxy/>
下面是一个将事务配置成切面的例子:
<!-- transaction aspect -->
<bean id="transaction" class="com.spring.test.proxy.Transaction"/>
<aop:config>
<aop:pointcut expression="execution(* com.spring.test.dao.impl.AopPersonDaoImpl.*(..))" id="perform"/>
<aop:aspect ref="transaction">
///通知
</aop:aspect>
</aop:config>