什么是spring
开源框架,为了解决企业应用开发的复杂性而创建的,现在是一个庞大的家族
轻量级的控制反转和面向切面的容器框架
-大小和开销都是轻量的
-控制反转达到松耦合
-面向切面,允许通过分离应用的业务逻辑和系统服务进行内聚兴的开发
-包含并管理对象,这个意义上来说是一个容器
-将简单的组件配置成复杂的应用,这个意义上是框架
IOC:控制反转
应用程序本身不负责对象的创建和维护,而是由外部容器负责创建和维护
比如,我们想用一辆车,我们不生产车,去买一辆好了
DI:依赖注入,是IOC实现的一种方式,创建对象,并且组装对象之间的关系
理解:在初始化的时候,会创建一系列的对象,并且通过注入的方式组织起来
比如类A B,ioc负责组装
业务对象进入容器,根据配置文件创建和生产符合我们需要的对象,我们需要的时候,直接拿就行
这也就是应用程序本身不负责对象的创建和维护,而是由外部容器负责创建和维护
应用程序可以更关注对象的使用
2004年,martin fowler探讨了这个问题,到底是什么被翻转了
货的依赖对象的过程被反转了,由自身管理变成了ioc容器注入,于是起了个名叫依赖注入DI
看一下例子
加载过程源码,以后详细分析
bean容器初始化
涉及两个包:
1.org.springframework.beans
beanfactory:提供配置结构和基本功能,加载并初始化bean
2.org.springframework.context
applicationcontext:保存bean对象,也是就是容器
applicationcontext加载三种方式:1.本地文件2.classpath3.依赖listener或者servlet
Bean
bean常用的配置项:
id:整个IOC,bean的唯一标识
class:具体是哪一个类
scope:范围,作用域
Constructor argument:构造器参数
Properties:属性
Autowiring mode:自动装配模式
lazy-initalization mode:懒加载模式
Initialzation/destruction method:初始化,销毁的方法
一般来说,class是必须配置的
得到实例的方式:根据id,根据class
作用域:
singleton:单例,每一个bean容器中只存在一个,一个context上下文就是一个容器
prototype:原型模式,每次使用创建新的实例,destory不生效,等待被垃圾回收
request:每次http请求创建一个,并且只在当前request内有效
session:当前session有效
global session:基于portlet web中有效,比如单点登录
具体使用场景,后续
生命周期
定义
初始化
使用
销毁
初始化,销毁:
1.配置init-method方法
配置destory-method
2.全局默认初始化,销毁
3.implements Intializing DisposableBean
覆盖destory和afterPropertiesSet方法
bean中不需要另外配置
三种方式同时使用,默认的不生效,最先执行的是方法3,实现接口的这一种,然后会执行2,init-method方法
Aware接口
Spring中有一些以Aware结尾的接口,实现了Aware接口的Bean在初始化之后,可以获得相应的资源
通过Aware接口,可以对spring资源进行操作
为spring进行简单的扩展,提供了入口
自动装配
NO:不做任何操作
byname:根据名字自动装配
bytype:如果容器中存在一个与指定属性类相同的bean,自动装配,如果存在多个该类型的bean,则抛出异常,如果找不到,什么也不发生
Constructor:应用于构造器参数,如果没能找到与构造器参数一致的bean,抛出异常
byName例子:
1.创建一个DAO类
2.在service中定义,dao对象,生成set方法(自动调用)
3.单元测试
在之前我们会通过如下方式配置
现在如果通过byName则不需要,只需要定义两个bean就可以了,在引用的bean,也就是service中一定要声明属性,和set方法(可以在set方法中输出一句话,可以看到set方法会被调用)
具体set方法是怎么被执行的呢?
因为我们设置了byName方法,会在容器中寻找DAO这个bean的id,然后会通过set方法,赋值给我们在引用类中声明的对象
byType:和byName相似,根据类来判断,即使id不一致,也可以装配成功
Constructor:
我们需要在引用类中,声明一个对象,通过构造方法进行赋值
装配过程:根据构造器传入方法的类型,去容器中查找对应类型的实例,和id无必然关系,如果有赋值给引用类中声明的对象
Resources:
针对与资源文件统一接口:
ResourceLoader:所有的application context 都实现了这个接口
例子:
Bean管理的注解实现及例子
classpath扫描与组件管理
spring3.0开始
可以使用Java而不是xml定义bean
@Configuration @Bean @Import @DependOn
@Component是一个通用注解,可以用于任何的bean
@Respository:通常用于注解DAO类,持久层
@Service:注解Service类,服务层
@Controller:注解Controller类,控制层
Spring可以自动检测并注册Bean到ApplicationContext中
比如注解在一个类上,可以被自动检测,并且注册到ApplicationContext中
配置context,会扫描这个包下所有的类,将@Component @Respository @Service @Controller 注解的类,注册到ioc容器中
用过滤器进行自定义扫描:
那么,如何定义bean呢?
扫描过程中,组件被自动检测,Bean名称是由BeanNameGenerator生成的,会有一个name属性,显式的设置name
比如
如果不显式的命名
会默认生成一个首字母小写的bean id
当然,我们也可以自己实现bean命名策略,实现BeanNameGenerator接口,并且一定要包含一个无参构造器,需要在配置文件中指定我们自己的策略
作用域
@Scope,可以指定作用域,默认的是单例
如果自己来实现的话,也是要提供一个无参的构造器,配置文件中指定
@Autowired
可将@Autowired理解为传统的setter方法
1.用于构造器
2.用于成员变量
3.用于set方法
需要注意的
@Autowired是由BeanPostProcessor处理的,所以不能在自己的BeanPostProcessor或者BeanFactoryPostProcessor中使用
必须通过xml或者Spring 的@Bean来加载
@Bean
配置和初始化一个由IOC容器管理新对象的方法,不用去xml中配置bean了
经常和@Configuration一起使用
和下图作用是一样的
可以显式的指定id
和初始化,销毁的方法
AOP
面向切面,通过预编译的方式,和运行期间动态代理实现程序功能统一维护的一种技术
主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理等
OOP:对象作为计算主体,拥有自己的名称,状态以及接受外界消息的接口。在对象模型中,产生新对象,旧对象销毁,发送消息,响应消息就构成OOP计算模型的根本。它强调对象的“抽象”、“封装”、“继承”、“多态”。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP:它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。
AOP常见概念:
通知(Advice)
通知分为五中类型:
Before:在方法被调用之前调用
After:在方法完成后调用通知,无论方法是否执行成功
After-returning:在方法成功执行之后调用通知
After-throwing:在方法抛出异常后调用通知
Around:通知了好、包含了被通知的方法,在被通知的方法调用之前后调用之后执行自定义的行为
连接点(Join point)
比如:方法调用、方法执行、字段设置/获取、异常处理执行、类初始化、甚至是 for 循环中的某个点
理论上, 程序执行过程中的任何时点都可以作为作为织入点, 而所有这些执行时点都是 Joint point
但 Spring AOP 目前仅支持方法执行 (method execution)
切点(Pointcut)
通知(advice)定义了切面何时,那么切点就是定义切面“何处” 描述某一类 Joint points, 比如定义了很多 Joint point, 对于 Spring AOP 来说就是匹配哪些方法的执行
描述方式:直接指定 Jointpoint 所在的方法名, 功能比较单一, 通常只支持方法级别的 AOP 框架
正则表达式
特定的描述语言, 如 AspectJ 提供的 Pointcut 描述语言
切面(Aspect)
切面是切点和通知的结合。通知和切点共同定义了关于切面的全部内容,它是什么时候,在何时和何处完成功能
引入(Introduction)
引用允许我们向现有的类添加新的方法或者属性
织入(Weaving)
组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
重点关注动态代理技术
Spring的动态代理包括两个部分:
JDK动态代理
JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
CGLib动态代理
CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。
Spring AOP 框架对 AOP 代理类的
处理原则是:
如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;
如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类——不过这个选择过程对开发者完全透明、开发者也无需关心。
Spring AOP 会动态选择使用 JDK 动态代理、CGLIB 来生成 AOP 代理,如果目标类实现了接口,Spring AOP 则无需 CGLIB 的支持,直接使用 JDK 提供的 Proxy 和 InvocationHandler 来生成 AOP 代理即可。