控制反转: 把创建对象的权利交给框架,在使用过程中直接去得到这个对象;它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup);
Spring 概况
Spring 是一个开源框架,为了解决企业应用开发的复杂性而创建的,但现在已经不止应用于企业应用。
-
是一个轻量级的控制反转(IOC)和面向切面(AOP)编程的容器框架
- 从大小和开销两方面而言 Spring 都是轻量的
- 通过控制反转(IOC)的技术达到松耦合的目的
- 提供了面向切面(AOP)编程的丰富的支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
- 包含并管理应用对象的配置和生命周期,这个意义上是一种容器 【它会管理由 Spring 它自己创建的对象,管理对象配置的信息以及这个对象的生命周期,也就是这个对象从创建到销毁都是由 Spring 自己来控制的】
- 将简单的组件配置、组合成为复杂的应用,这个意义上是框架
Spring 作用
- 容器
- 提供了对多种技术的支持和集成
- JMS
- MQ支持
- UnitTest
- ...
- AOP(事务管理、日志等)
- 提供了众多方便应用的辅助类(JDBC Templatede等)
- 对主流应用框架(Hibernate等)提供了良好的支持
Spring 适用范围
- 构建企业应用(SpringMVC + Spring + Hibernate/ibatis、myBatis)
- 单独使用Bean容器(Bean 管理)
- 单独使用AOP进行切面的处理
- 其他的 Spring 功能,如:对消息的支持等
- 在互联网中的应用...
Spring-IOC 控制反转
接口及面向接口编程
-
接口:
- 用于沟通的中介物的抽象化【对外调用说明、规范】
- 实体把自己提供给外界的一种抽象化说明,用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式【接口是对外的一种说明,说明了我会提供哪些功能,至于内部的实现,对外是不公开的;OC的.h文件也就类似于Java的接口】
- 对应 Java 接口即声明,声明了哪些方法是对外公开提供的
- Java 8 中,接口可以拥有方法体
-
面向接口编程:
- 结构设计中,分清层次及调用关系,每层只向外(上层)
- 好处:接口实现的变动不影响各层级间的调用,这一点在向外或企业内部提供公共服务中尤为重要【稳定性】
- “面向接口编程”中的“接口”是用于隐藏具体实现和实现多态性的组件
啥是IOC
IOC: 控制反转,控制权的转移,应用程序本省不负责依赖对象的创建和维护,而是由外部容器负责创建和维护;当我们需要一个对象的时候,不是去 new 一个,而是由外部容器创建,我们向外部容器去申请一个或者说拿来一个使用。
DI(依赖注入):是控制反转的一种实现方式 ;由 IOC 容器在运行期间,动态的将某种依赖关系注入到对象之中
目的:创建对象并且组装对象之间的关系
Spring 的 Bean 配置
- 配置:在 spring-ioc.xml 中通过<bean id="bean的id" class="javaBean实现类的全路径"/>配置映射关系
- 使用:context.getBean(“配置的Bean的id”)拿到 Bean 对象,调用对应的方法
Bean 的初始化
-
基础:两个包
- org.springramework.beans : BeanFactory 提供配置结构和基本功能,加载并初始化Bean
- org.springramework.context : ApplicationContext 保存了 Bean 对象并在 Spring 中被广泛使用
-
ApplicationContext 初始化方式:
-
加载本地文件
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("F:/workspace/appcontext.xml);
-
Classpath 相对路径
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring-context.xml);
-
Web 应用中依赖 servlet 或 listener
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>context</servlet-name> <servlet-class>rg.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
-
Spring 常用注入方式
IOC 容器在加载的时候会扫描 xml 文件中 Bean 的相关配置,然后对这些 Bean 进行实例化
Spring 注入:是指在启动 Spring 容器加载 Bean 配置的时候,完成对变量的赋值行为【A里面有B,初始化A的时候同时对B进行赋值】
-
注入方式
- 设值注入:xml 配置 property 属性对应 Bean 的set方法
- 构造注入:xml 配置 constructor-arg 属性对应 Bean 的构造方 法
Spring Bean
Bean 的配置项
* Id:在整个 IOC 容器中这个 Bean 的唯一标识
* Class:具体要实例化的哪一个类
* Scope:范围,作用域
* Constructor arguments:构造器的参数
* Properties:属性
* Autowiring mode:自动装配的模式
* lazy-initilizition mode:懒加载模式
* initilizition/destruction method:初始化和销毁的方法
以上配置项共同构成了 Spring IOC 容器中对于 Bean 的配置,使用时理论上只有 Class 是必须的,其它的都可以不配置,但是想从 Bean 容器中得到某一个实例,两种方式:Id 和 Bean 的类型,如果根据 Id 获取,那么 Id 这一项就需要配置,如果是根据类型来获取就只需要获取 Class 这一项。
Bean 的作用域 (scope)
- singleton:单例,指容器中只存在一个 Bean 的实例【Spring bean 默认模式】
- prototype:每次请求(每次使用)创建新的实例,destory 方式不生效
web 应用相关:
- request:每次 http 请求创建一个实例仅在当前 request 内有效
- sesion:同上,每次 http 请求创建,当前 session 内有效
- global session:基于 portlet 的 web 中有效(portlet 定义了 global session),如果是在 web 中,同session。
Bean 的生命周期
- 定义:Bean xml配置中 定义 id 和 class
- 初始化:当 IOC 容器 context 启动的时候加载配置文件中的 Bean 并对其初始化【可单独或全局配置,三种方式】
- 使用:从 Bean 容器中取出一个 Bean 的实例,然后去调用它的方法
- 销毁:Bean 容器停止的时候销毁由当前这个 Bean 容器创建的所有的 Bean 的实例
Bean 的自动装配
- NO: 不做任何操作【默认】
- byName:根据属性名字自动装配。检查容器并根据名字查找与属性完全一致的 bean,并将其与属性自动装配。
default-autowire=“byName”、依赖别的 Bean 的 Bean 中声明 set 方法 属性名字跟配置文件中的配置的id一样。 - byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配,如果存在多个该类型的bean,那么抛出异常,并指出不能使用 byType 类型进行自动装配;如果未找到相匹配的bean,那么将不做任何操作【跟上面一样 只不过根据 Bean 的类型来匹配,跟 配置文件里的 Bean 的id无关】
- Constructor:与 byType 方式类似,不同之处在于它应用于构造器参数,如果容器中没有找到于构造器参数类型一致的 Bean 【无关id】,抛出异常
Resources&ResourcesLoader
- 针对资源文件的统一接口【通过 Spring 加载一些资源文件的时候可以用 Resources 去完成】
Resources
- UrlResources:Url 对应的资源,根据 Url 地址即可构建
- ClassPathResources:获取类路径下的资源文件【相对路径】
- FileSystemResources:获取文件系统里面的资源【绝对路径】
- ServletContextResources:ServletContext封装的资源,用于访问ServletContext环境下的资源【和web相关】
- InputStreamResources:针对于输入流封装的资源
- ByteArrayResources:针对字节数组封装的资源
ResourcesLoader
对 Resources 进行加载,在 Spring 容器中,所有的 ApplicationContext 都实现了ResourcesLoader 这个接口,都可以获取 Resources 的实例,
Resources src = ctx.getResources(“资源路径”); classpath、file:、http:、
Bean 的自动装配 - Bean 的定义及作用域的注解实现
-
Classpath 扫描与组件管理
从 Spring3.0 开始,Spring JavaConfig 项目提供了很多特性,包括使用 java 注解而不是 xml 定义 Bean
@Component 是一个通用注解,可用于任何 bean
-
@Repository、@Service、@Controller 是 @Component 的子注解,是更有针对性的注解。
- @Repository 通常用于注解 DAO 层,即持久层
- @Service 通常用于注解 Service 类,即服务层
- @Controller 通常用于注解 Controller 类,即控制层(MVC)
注解到类上 Spring 可以自动检测类并注册 Bean 到 ApplicationContext 上
默认情况下,类被发现并注册 bean 的条件是:使用 @Component、@Repository、@Service、@Controller 注解或使用 @Component 的自定义注解。
@scope:通常情况下自动查找的 Spring 组件,其 scope 是 singleton,Spring 2.5提供了一个标识scope的注解 @scope(“xxx”)【使用场景:在每一个线程中使用一个 bean 的实例】
@Required:表示受影响的 Bean 属性必须在配置时被赋值【不常用】
@AutoWired(自动注入):可以理解为一个传统的 set 方法,可以用于变量、set方法、构造方法【常用;默认下,如果因找不到合适的 Bean 将会导致 AutoWired 失败,抛出异常;使用前提,配置扫描的范围
@qualifier:使用在 成员变量、方法参数上 作用:缩小范围
-
基于 Java 容器的注解
- @Bean:类似于XMl配置文件中的<bean/> ,配合@Configuration注解使用
- @ImportResource:引入一个资源
- @Value(“${属性名}”):获取资源文件中的内容
- 妈蛋。。。
AOP 概述
AOP:(Aspect Oriented Programming),面向切面编程,通过预编译方式 & 运行期动态代理实现程序功能的统一维护的一种技术。
主要功能:日志统计,性能统计,安全验证,事务处理,异常处理等。
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
切面是和功能垂直的
AOP实现方式:
- 预编译
- AspectJ
- 运行期动态代理(JDK动态代理,CGlib动态代理)
- SpringAOP,JbossAOP
相关概念:
切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象,多个对象执行过程中,都会被该切面控制。
连接点(Joinpoint):程序执行过程中的某个特定的点,【比如一个类中一个方法执行的开始】
-
通知(Advice):在切面的某个特定的连接点上执行的动作【方法执行开始的时候额外执行的切面的动作】
- 前置通知(before advice): < aop:before pointcut-ref=“” method=“” />
- 返回之后的通知(after returning advice): < aop:after-returning pointcut-ref=“” method=“”/>
- 异常后通知(after throwing advice):
- 返回通知(after(finally)advice):< aop:after pointcut-ref=“” method=“”/>【方法结束前的最后一行代码】
- 环绕通知(Around advice):通知方法的第一个参数必须是 ProceedingJoinPoint类型;< aop:around>
- 簡介(introductions):简介允许一个切面声明一个实现指定接口对象的通知,并且提供了一个接口实现类来代表这些对象
切入点(PointCut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联【如何在切面中匹配一个具体的连接点】【切入时机】【方式贼多,需要配置】
引入(Introduction):在不修改类代码的前提下,为类添加新的方法和属性
目标对象(Target Object):被一个或多个切面所通知的对象
AOP代理(AOP Proxy):AOP 框架创建的对象,用来实现切面契约(包括通知方法执行等功能)
织入(Weaving):把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入,类加载时织入,执行时织入【把切面和对象关联起来】
Spring AOP 实现:
- 纯 java 实现,无需特殊的编译过程,不需要控制类加载器层次
- 目前只支持方法执行连接点(通知 Spring Bean 的方法执行)
- 不是为了提供最完整的 AOP 实现;而是侧重于提供一种 AOP 实现和 Spring IOC 容器之间的整合。
- 不会和 AspectJ(全面综合解决方案)竞争,不会提供全面综合的解决方案。
有接口和无接口Spring AOP 实现区别:
- 有接口:默认使用标准的 JavaSE 动态代理作为AOP代理,这使得任何接口和任何接口集都可以被代理
- 无接口:使用CGLib 代理(如果一个业务对象没有实现任何一个接口)