框架
- Core
- IoC容器
- AOP功能
- 数据绑定
- 类型转换等
- Testing
- Data Access
- Web Servlet
核心
IOC容器
定义
IoC(Inversion of Control)是控制反转的意思,这是一种面向对象编程的设计思想。
优点
在不采用这种思想的情况下,我们需要自己维护对象与对象之间的依赖关系,很容易造成对象之间的耦合度过高,在一个大型的项目中这十分的不利于代码的维护。IoC则可以解决这种问题,它可以帮我们维护对象与对象之间的依赖关系,降低对象之间的耦合度。
实现方式
DI是依赖注入的意思,它是IoC实现的实现方式,就是说IoC是通过DI来实现的。
- 谁依赖谁:应用程序依赖IOC容器
- 为什么需要以依赖:应用程序需要IOC容器来提供对象需要的外部资源
- 注入什么:注入某个对象所依赖的外部资源
而实现依赖注入的关键是IoC容器,它的本质就是一个工厂。
管理Bean
定义
Spring通过IoC容器来管理Bean,我们可以通过XML配置或者注解配置,来指导IoC容器对Bean的管理。因为注解配置比XML配置方便很多,所以现在大多时候会使用注解配置的方式。
javaBean
定义
JavaBean是一个遵循特定写法的Java类
特点
- 这个Java类必须具有一个无参的构造函数
- 属性必须私有化。
- 私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
属性
- JavaBean的属性可以是任意类型,并且一个JavaBean可以有多个属性。
- 每个属性通常都需要具有相应的setter、 getter方法,setter方法称为属性修改器,getter方法称为属性访问器。
- 属性修改器必须以小写的set前缀开始,后跟属性名,且属性名的第一个字母要改为大写,例如,name属性的修改器名称为setName,password属性的修改器名称为setPassword。
- 属性访问器通常以小写的get前缀开始,后跟属性名,且属性名的第一个字母也要改为大写,例如,name属性的访问器名称为getName,password属性的访问器名称为getPassword。
- 一个JavaBean的某个属性也可以只有set方法或get方法,这样的属性通常也称之为只写、只读属性。
常用注解
- @ComponentScan:用于声明扫描策略,通过它的声明,容器就知道要扫描哪些包下带有声明的类,也可以知道哪些特定的类是被排除在外的。
- @Component、@Repository、@Service、@Controller:用于声明Bean,它们的作用一样,但是语义不同。@Component用于声明通用的Bean,@Repository用于声明DAO层的Bean,@Service用于声明业务层的Bean,@Controller用于声明视图层的控制器Bean,被这些注解声明的类就可以被容器扫描并创建。
- @Autowired、@Qualifier:用于注入Bean,即告诉容器应该为当前属性注入哪个Bean。其中,@Autowired是按照Bean的类型进行匹配的,如果这个属性的类型具有多个Bean,就可以通过@Qualifier指定Bean的名称,以消除歧义。
@Autowired与@Resource
- @Autowired是Spring提供的注解,@Resource是JDK提供的注解。
- @Autowired是只能按类型注入,@Resource默认按名称注入,也支持按类型注入。
- @Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。
- @Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
- 需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
- @Scope:用于声明Bean的作用域,默认情况下Bean是单例的,即在整个容器中这个类型只有一个实例。可以通过@Scope注解指定prototype值将其声明为多例的,也可以将Bean声明为session级作用域、request级作用域等等,但最常用的还是默认的单例模式。
singleton
在Spring容器中仅存在一个实例,即Bean以单例的形式存在。
prototype
每次调用getBean()时,都会执行new操作,返回一个新的实例。
request
每次HTTP请求都会创建一个新的Bean。
session
同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
globalSession
同一个全局的Session共享一个Bean,一般用于Portlet环境。
多例化的目的
- 用多例是为了防止并发
- 比如一个请求改变了对象的状态,此时对象又处理另一个请求,而之前请求对对象状态的改变导致了对象对另一个请求做了错误的处理
- @PostConstruct、@PreDestroy:用于声明Bean的生命周期。其中,被@PostConstruct修饰的方法将在Bean实例化后被调用,@PreDestroy修饰的方法将在容器销毁前被调用。
Bean的生命周期
定义
Spring容器管理Bean,涉及对Bean的创建、初始化、调用、销毁等一系列的流程,这个流程就是Bean的生命周期。
图示
干预
我们可以自定义初始化方法,并在该方法前增加@PostConstruct注解,届时Spring容器将在调用SetBeanFactory方法之后调用该方法。
我们可以自定义销毁方法,并在该方法前增加@PreDestroy注解,届时Spring容器将在自身销毁前,调用这个方法。
注入方式
- 构造方法注入
就是被注入对象可以在它的构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。然后,IoC Service Provider会检查被注入的对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
- setter方法注入
通过setter方法,可以更改相应的对象属性。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些, 可以在对象构造完成后再注入。
- 接口注入
相对于前两种注入方式来说,接口注入没有那么简单明了。被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。相对于前两种依赖注入方式,接口注入比较死板和烦琐。
构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在使用最多的注入方式。而接口注入因为侵入性较强,近年来已经不流行了。
AOP功能
定义
AOP(Aspect Oriented Programing)是面向切面编程思想,这种思想是对OOP的补充,它可以在OOP的基础上进一步提高编程的效率。简单来说,它可以统一解决一批组件的共性需求(如权限检查、记录日志、事务管理等)。
面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。
功能
在AOP思想下,我们可以将解决共性需求的代码独立出来,然后通过配置的方式,声明这些代码在什么地方、什么时机调用。当满足调用条件时,AOP会将该业务代码织入到我们指定的位置,从而统一解决了问题,又不需要修改这一批组件的代码。
术语
- 连接点(join point):对应的是具体被拦截的对象,因为Spring只能支持方法,所以被拦截的对象往往就是指特定的方法,AOP将通过动态代理技术把它织入对应的流程中。
- 切点(point cut):有时候,我们的切面不单单应用于单个方法,也可能是多个类的不同方法,这时,可以通过正则式和指示器的规则去定义,从而适配连接点。切点就是提供这样一个功能的概念。
- 通知(advice):就是按照约定的流程下的方法,分为前置通知、后置通知、环绕通知、事后返回通知和异常通知,它会根据约定织入流程中。
- 目标对象(target):即被代理对象。
- 引入(introduction):是指引入新的类和其方法,增强现有Bean的功能。
- 织入(weaving):它是一个通过动态代理技术,为原有服务对象生成代理对象,然后将与切点定义匹配的连接点拦截,并按约定将各类通知织入约定流程的过程。
- 切面(aspect):是一个可以定义切点、各类通知和引入的内容,SpringAOP将通过它的信息来增强Bean的功能或者将对应的方法织入流程。
实现方式
-
JDK动态代理:这是Java提供的动态代理技术,可以在运行时创建接口的代理实例。Spring AOP默认采用这种方式,在接口的代理实例中织入代码。
-
CGLib动态代理:采用底层的字节码技术,在运行时创建子类代理的实例。当目标对象不存在接口时,Spring AOP就会采用这种方式,在子类实例中织入代码。
对比:
- 在性能方面,CGLib创建的代理对象比JDK动态代理创建的代理对象高很多。
- 但是,CGLib在创建代理对象时所花费的时间比JDK动态代理多很多。
- 所以,对于单例的对象因为无需频繁创建代理对象,采用CGLib动态代理比较合适。反之,对于多例的对象因为需要频繁的创建代理对象,则JDK动态代理更合适。
应用场景
- 一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP的使用,灵活地编制到模块中,
比如可以通过AOP实现应用程序中的日志功能
- 另一方面,在Spring内部,一些支持模块也是通过Spring AOP来实现的
比如事务处理
不能对哪些类增强
Spring AOP只能对IoC容器中的Bean进行增强,对于不受容器管理的对象不能增强。
由于CGLib采用动态创建子类的方式生成代理对象,所以不能对final修饰的类进行代理。
Spring容器
定义
Spring 容器保存与Spring框架相关数据的容器,通常用于保存各种各样的Bean。
意义(为什么要使用Spring容器)
- 当一个对象被频繁的使用时,对内存资源会有较大的消耗。如果使用Spring管理bean类,由于Spring默认管理bean是单例模式,所以会避免不断的创建新的实例从而导致并发量很大时垃圾回收效率低的问题。但这样样很容易会导致多线程问题。
- 是当一个对象被使用的次数极少,这个时候就没有必要让这个对象一直存在到内存中,可以将spring管理bean的作用域设置为prototype,在每次调用的时候创建一次 ,用完销毁。
因此,使用Spring管理bean对象可以实现对象对资源的有效使用。
类型
BeanFactory
-
是基础类型的IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延 迟初始化策略。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
-
BeanFactory是一个类工厂,与传统类工厂不同的是,BeanFactory是类的通用工厂,它可以创建并管理各种类的对象。这些可被创建和管理的对象本身没有什么特别之处,仅是一个POJO,Spring称这些被创建和管理的Java对象为Bean。并且,Spring中所说的Bean比JavaBean更为宽泛一些,所有可以被Spring容器实例化并管理的Java类都可以成为Bean。
-
BeanFactory是Spring容器的顶层接口,Spring为BeanFactory提供了多种实现,最常用的是XmlBeanFactory。但它在Spring 3.2中已被废弃,建议使用XmlBeanDefinitionReader、DefaultListableBeanFactory替代。BeanFactory最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的Bean。
ApplicationContext
ApplicationContext:它是在BeanFactory的基础上构建的,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等。ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容 器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中,ApplicationContext类型的容器是比较合适的选择。
初始化流程
-
初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中
-
将配置类的BeanDefinition注册到容器中
-
调用refresh()方法刷新容器
Spring循环依赖
定义
就是A对象依赖了B对象,B对象依赖了A对象。
图示
情况分类
-
构造器的循环依赖
这种依赖spring是处理不了的,直接抛出BeanCurrentlylnCreationException异常。
-
单例模式下的setter循环依赖
通过“三级缓存”处理循环依赖。
-
非单例循环依赖
无法处理。
问题解决
三级缓存
-
singletonObjects
缓存某个 beanName 对应的经过了完整生命周期的bean
-
earlySingletonObjects
缓存提前拿原始对象进行了 AOP 之后得到的代理对象,原始对象还没有进行属性注入和后续的 BeanPostProcesso r等生命周期
-
singletonFactories
缓存的是一个 ObjectFactory ,主要用来去生成原始对象进行了 AOP之后得到的「代理对象」,在每个 Bean 的生成过程中,都会提前暴露一个工厂,这个工厂可能用到,也可能用不到,如果没有出现循环依赖依赖本 bean,那么这个工厂无用,本 bean 按照自己的生命周期执行,执行完后直接把本 bean 放入 singletonObjects 中即可,如果出现了循环依赖依赖了本 bean,则另外那个 bean 执行 ObjectFactory 提交得到一个 AOP 之后的代理对象(如果有 AOP 的话,如果无需 AOP ,则直接得到一个原始对象)
解决方案
Spring事务(Transaction)
定义
事务是指一组SQL语句的集合,集合中有多条SQL语句,可以是insert、update、select、delete,希望这些SQL语句执行是一致的,作为一个整体执行。要么都成功,要么都失败。
开发中应用
事务放在service类的业务方法中,因为业务方法会调用多个dao,执行多条SQL语句
不足
-
不同数据库访问技术,处理事务的对象、方法不同。需要了解不同数据库使用事务的原理。
-
需要掌握多种数据库事务的处理逻辑,什么时候提交事务,什么时候回滚事务。
-
处理事务的方法种类多。
解决不足:
spring提供了处理事务的统一模型,能够使用统一的步骤、方式完成多种不同数据库访问技术的事务处理。使用spring的事务处理机制可以完成mybatis、hibernate访问数据库的事务处理。
就是多种数据库访问技术,不同的事务处理的机制、对象、方法。较难掌握。
解决问题
具体步骤
-
指定要使用的事务管理器实现类,使用
-
指定哪些类,哪些方法需要加入事务的功能
-
指定方法需要的事务的隔离级别、传播行为、超时时间。
具体方案
基于注解的方案(@Transactional):适用于中小型项目
注解属性
// org.springframework.transaction.annotation.Transactional
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/**
* 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。
*/
@AliasFor("transactionManager")
String value() default "";
/**
* 同上。
*/
@AliasFor("value")
String transactionManager() default "";
/**
* 事务的传播行为,默认值为 REQUIRED。
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* 事务的隔离规则,默认值采用 DEFAULT。
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* 事务超时时间。
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* 是否只读事务
*/
boolean readOnly() default false;
/**
* 用于指定能够触发事务回滚的异常类型。
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* 同上,指定类名。
*/
String[] rollbackForClassName() default {};
/**
* 用于指定不会触发事务回滚的异常类型
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* 同上,指定类名
*/
String[] noRollbackForClassName() default {};
}
注解使用步骤
- 声明事务管理器对象
- 开启事务注解驱动,告诉spring,使用注解的方式管理事务。spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务功能。
- 在目标方法(需要增加事务功能的)方法上面加入@Transactional注解
基于XML配置文件方案(AspectJ框架):适用大型项目
定义
大型项目中有很多类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
实现步骤
- 加入依赖
- 声明事务管理器对象
- 声明方法需要的事务类型(配置方法的事务属性:隔离级别、传播行为、超时)
- 配置AOP:指定哪些类需要创建代理对象。
spring管理事务
编程式事务(AspectJ框架)
Spring提供了TransactionTemplate模板,利用该模板我们可以通过编程的方式实现事务管理,而无需关注资源获取、复用、释放、事务同步及异常处理等操作。相对于声明式事务来说,这种方式相对麻烦一些,但是好在更为灵活,我们可以将事务管理的范围控制的更为精确。
声明式事务(@Transactional)
Spring事务管理的亮点在于声明式事务管理,它允许我们通过声明的方式,在IoC配置中指定事务的边界和事务属性,Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说,这种方式十分的方便,只需要在需要做事务管理的方法上,增加@Transactional注解,以声明事务特征即可。