Spring Framework 是一个开源的 Java/Java EE 全功能栈(full-stack)的应用程序框架,以 Apache 许可证形式发布,也有 .NET 平台上的移植版本。
关键字:轻量级
、非侵入式
、一站式
、模块化
,其目的是用于简化企业级应用程序开发。
可以说 Spring 是一个容器框架,用于配置 bean、处理和维护 bean 之间的关系的框架
注意:Spring 中的 bean 概念可以是 java 中的任何一种对象,比如可以是 javabean、service、action、数据源、dao、ioc(控制反转)、di(依赖注入)等
可以说 Spring 贯穿各个层,上至 web 下至持久层,很厉害…
快速入门
一般来说,当 Spring 加载的时候,会自动创建配置的 bean 对象(当作用域是 singleton 的时候),并加载到内存中去;使用 property 标签来注入对象(属性)
最简单的栗子,使用 Spring 获取一个对象,不需要再 new 了,首先定义一个简单的 bean,这里不多说就写一个
1 | public class User { |
然后是在 xml 进行配置,文件名一般写 applicationContext 放在 src 目录下,如果 bean 对象引用了其他对象,可以使用 property 的 ref 属性设置
1 | "1.0" encoding="UTF-8" xml version= |
进行测试下,在启动时 Spring 就给你创建好对象了,并且是单例的,默认情况下;最好是使用接口,这样以后就不需要改动代码了,只改配置文件即可
1 | // 解析配置文件并且创建其中的 bean 到内存中,非常耗费资源,一般写成工具类 单例模式 |
bean的作用域相关
配置 bean 的时候有一个属性是 scope ,用来配置作用域,有五种可选值
- singleton
在 IOC 容器中,每一个 bean 定义对应一个对象实例,也就是单例 【默认】 - prototype
一个 bean 定义可以对应多个实例,并且在获取(getBean)的时候才会被实例化 - request
request 表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP request 内有效 - session
session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效 - global session
类似于标准的 HTTP Session 作用域,不过它仅仅在基于 portlet 的 web 应用中才有意义。
Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。
在 global session 作用域中定义的 bean 被限定于全局 portlet Session 的生命周期范围内。如果你在 web 中使用 global session 作用域来标识 bean,那么 web 会自动当成 session 类型来使用。
然后获取 bean 的方法上面提到了一种,就是使用 ac 的 getBean 方法,除了这种还可以通过工厂的方式获得,但是不推荐使用,因为通过工厂获取会进行懒加载,也就是说当 get 的时候才会创建相应的实例
1 | BeanFactory bf = new ClassPathXmlApplicationContext("applicationContext.xml"); |
bean的生命周期
在默认情况下,也就是当作用域是 singleton 的时候,bean 对象是单例的,这种情况生命周期相对复杂一点,下面的顺序
- 首先是实例化,当配置文件加载的时候,默认调用无参的构造函数
- 根据配置文件中的 property 进行属性注入,必须要有相应的 set 方法
- 如果 bean 对象实现了 BeanNameAware 接口,会调用其 setBeanName 方法,这个方法必须复写,通过这个方法可以获得配置的 id 名称
和前面的 struts2 有点相似,实现这个接口可以获得配置的 id 名称 - 如果 bean 对象实现了 BeanFactoryAware 接口,会调用其 setBeanFactory 方法,通过这个方法可以获得 bean 工厂
- 如果 bean 对象实现了 ApplicationContextAware 接口,会调用其 setApplicationContext 方法,通过这个方法可以获得上下文对象
- 调用 BeanPostProcessor (后置处理器)的 postProcessBeforeInitialization 方法,如果配置了处理器的话
后置处理器类似过滤器,每个 bean 实例化时(前、后)都要调用此处理器,有点像拦截器
设置处理器需要建一个类实现 BeanPostProcessor 接口即可,再配置到 xml 文件中(一切皆是 bean) - 如果 bean 对象实现了 InitializingBean 接口,会调用其 afterPropertiesSet 方法
- 如果在 xml 配置文件中的 bean 标签定义了 init-method 属性,那么会在这时调用其指定的方法
使用这种方式耦合性比较低 - 调用 BeanPostProcessor (后置处理器)的 postProcessAfterInitialization 方法
- 然后就可以使用 bean 了!!
- 容器关闭….
- 如果 bean 对象实现了 DisposableBean 接口,这时会调用其 destroy 方法
- 如果在 xml 配置文件中的 bean 标签定义了 destroy-method 属性,那么会在这时调用其指定的方法
使用这种方式耦合性比较低
如果不是用上下文获取的 bean,而是采用的工厂方法,那么就简单一些了,上面的5、6、9 就没有了
然后下面是两幅图,第一张比较详细,第二张比较简洁
使用注解指定
在配置文件中加入:<context:annotation-config/>
来启用注解
比如第八点自定义的 init 方法可以使用 @PostConstruct
注解来指定,不需要在 xml 文件中配置了
destroy 方法可以通过 @PreDestroy
注解来指定
更多注解的使用后面再说
装载bean
普通属性就不说了,主要是对于数组、集合的装填,首先看下配置的 xml 文件,后面用注解可能会更轻松
1 | <bean id="collectionTest" class="com.bfchengnuo.domain.CollectionTest"> |
然后可以跑一下,测试看看
1 | private static void collectionTest() { |
property 标签中还可以继续再套一个 bean 标签,这样就相当于内部类,在其他地方就不能通过 ref 进行引用了
如果有继承关系可以使用 bean 的 parent 属性指定父 bean,配置的相关属性会被继承过来,当然你可以进行覆盖
如果需要设置值为空,可以使用 <null/>
标签
自动装配
使用 bean 标签中的 autowire 属性为一个 bean 定义指定自动装配模式,也就是说指定了自动装配就不需要注入对象了,它会自动根据设置去 IoC 容器中寻找合适的 bean 然后自动注入。但是并不推荐使用,仅作为了解;但是我看经常配合注解使用,尤其是在 SSH 的项目中
模式 | 描述 |
---|---|
no | 这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。 |
byName | 由属性名自动装配。当找不到相应的连接关系时会尝试使用属性名去搜寻对应的 bean (id 和 name 相匹配);如果找不到或者找到多个则抛异常 |
byType | 由属性数据类型自动装配。当找不到相应的连接关系时会尝试使用属性的类型搜寻对应的 bean (class 相匹配);如果找不到或者找到多个则抛异常 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
autodetect | Spring 首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
默认值也是可以设置的,在 beans 标签的 defualt-autowire 属性指定
分散配置
使用 Spring 的特殊 bean 来达到分散配置的目的,占位符使用 ${name}
的形式;前面说生命周期的时候说的那几个接口就是体现的这个功能
首先新建个 properties 文件,然后引入到配置文件中:
1 | <!--分散配置,引入 prop 文件;如果引入多个文件可以用逗号分割--> |
获取ApplicationContext
首先要明确的是 ApplicationContext 是个接口;其实它也是 BeanFactory 的子类,获取 ApplicationContext 实现类常用的有下面几种方式:
- ClassPathXmlApplicationContext 通过类路径,上面写过了,通常用在“桌面”系统开发
- FileSystemXmlApplicationContext 通过文件系统获取,需要指明绝对路径,用的很少
- XmlWebApplicationContext 通过 web 方式获取,传统项目这个用的还是比较多的
- AnnotationConfigApplicationContext 使用 Java 配置的方式来构建 ApplicationContext,例如 SpringBoot
- AnnotationConfigWebApplicationContext 使用 Java 配置方式的 Web 应用,可以在 web.xml 中指定。
AOP编程和IoC
AOP 和 IoC 是 Spring 的两大特点
AOP(Aspect Orient Programming),也就是面向切面编程。 可以这样理解,面向对象编程(OOP)是从静态角度考虑程序结构,面向切面编程(AOP)是从动态角度考虑程序运行过程。
这种在运行时,动态地将代码“切入”到类的指定方法、指定位置上的编程思想就是面向切面的编程。
或者说在不增加代码的基础上增加新的功能;面向(作用于)全部对象,或者一类对象(想一下学过的监听器、拦截器、过滤器)
下面就说说常见的几个术语:
- 通知/增强(Advice)
切面的工作被称为通知,通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。它应该应用在某个方法被调用之前?之后?之前和之后都调用?还是只是在方法抛出异常时调用?
简单说就是你想要(切入)的具体功能实现(想要干啥),比如安全,事物,日志操作等。 - 连接点(JoinPoint)
连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
就是 Spring 允许你使用通知的地方 - 切点(Pointcut)
一组连接点的总称,用于指定某个通知(增强)应该在何时被调用。
切入点是「在哪干」,你可能在很多地方(连接点)都可以干,但并不是每个地方都要干,要干的地方叫切点 - 切面(Aspect)
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能
通知说明了干什么和什么时候干(什么时候通过方法名中的before、after、around 等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。 - 织入(weaving)
把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring 采用的是运行时
Spring 中的通知有五种类型:
- 前置通知(Before):在目标方法被调用之前调用通知功能;[接口: MethodBeforeAdvice]
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;[接口: AfterReturningAdvice]
- 返回通知/最终通知(After-returning):在目标方法成功执行之后调用通知,如果有后置通知会在其之后执行;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;[接口: ThrowsAdvice]
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。[接口: MethodInterceptor]
运行顺序:前置通知/环绕通知–目标方法执行–返回通知/异常通知–后置通知/环绕通知
这里需要注意的是:环绕通知由于和前置、后置处于同一个 aspect 内,所以是无法确定其执行顺序的,当然可以通过其他手段来解决
实际开发中,一般会将顺序执行的 Advice 封装到不同的 Aspect,然后通过注解或者实现接口的方式控制 Aspect 的执行顺序,二选一(对于在同一个切面定义的通知函数将会根据在类中的声明顺序执行)
关于 AOP 的更详细介绍:[1]:http://cometzb-xujun.iteye.com/blog/1537274;[2]:http://www.jianshu.com/p/66d21dae6a68
简单解释下什么是 IoC(控制反转 Inversion of Control),它是一种设计思想,和依赖注入有些相似之处(或者说不同名称,相同含义),就是说把创建对象和维护对象关系的控制权从程序中转移到了 Spring 容器中(applicationContext.xml),程序本身不再维护,换句话说就是从代码移到了配置文件中 ,对象的创建交给外部容器处理
你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制 ,所以说这个容器很重要,IoC 是有专门一个容器来创建这些对象,即由 Ioc 容器来控制对象的创建
简单总结:
控制反转解决对象的创建问题(交给别人创建),依赖注入解决在创建对象后,对象关系的处理问题(通过 set 方法依赖注入)
参考:http://luanxiyuan.iteye.com/blog/2279954
AOP快速入门
Spring 中“使用” AOP 的步骤一般是:
- 定义接口
- 编写对象(目标对象、被代理对象)
- 编写通知
- XML 文件中进行配置,包括被代理对象、通知、代理对象(ProxyFactoryBean)
- 通过代理对象进行使用
如果看过动态代理相关的知识,还是很好理解的,那就搞一个简单的前置通知来玩玩,前两部就省略了,很简单;主要是通知和 XML 的配置,这种 XML 的配置好像很 low 了,随便看看就好,稍后我会专门更新一篇新版的.
1 | public class MyMethodBeforeAdvice implements MethodBeforeAdvice { |
然后是相关的配置文件,如果代理了多个接口记得要在 proxyInterfaces 都写进去,这样就可以在 getBean 后随意进行切换
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
然后测试了下,还是挺不错的,如果之前写了多个接口,可以直接进行各种强转,当然你的通知要都实现了这些接口,终究还是多态的特性
1 | public static void main(String[] args) { |
最后再来补充一个,就是定义切点,是自定义,也就是说指定某个通知在指定的方法切入,配置完后记得在代理对象中将通知换成这个就可以了
1 | <!--自定义切入点--> |
评论框加载失败,无法访问 Disqus
你可能需要魔法上网~~