1、Spring是什么?
- 轻量级:Spring是非侵入性的 - 基于Spring开发的应用中的对象可以不依赖于Spring的API
- 依赖注入(DI --- dependency injection、IOC)
- 面向切面编程(AOP --- aspect oriented programming)
- 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
- 框架:Spring实现了使用简单的组件配置组合成一个复杂的应用,在 Spring 中可以使用 XML 和 Java 注解组合这些对象
- 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上 Spring 自身也提供了展现层的 SpringMVC 和持久层的 Spring JDBC)
2、IOC(DI)
- IOC(Inversion of Control):其思想是反转资源获取的方向。传统的资源查找方式要求组件向容器发起请求查找资源。作为回应,容器适时的返回资源。而应用了IOC之后,则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。这种行为也被称为查找的被动形式。
-
DI(Dependency Injection)— IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter方法和构造器)接受来自如容器的资源注入。相对于IOC而言,这种表述更直接。
IOC/DI通过反射机制实现。
3、AOP
AOP(Aspect-Oriented Programming,面向切面编程),是对OOP(Object-Oriented Programing,面向对象编程)的补充和完善。AOP技术利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即切面。所谓“切面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
AOP可以在不改变业务模型的基础上,不需要改变接口或类,就能实现某些功能,如日志记录,性能统计,安全控制,事务处理,异常处理等。
AOP的相关术语:
① 通知(Advice):通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。
② 连接点(Joinpoint):程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。
③ 切入点(Pointcut):通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称
④ 切面(Aspect):通知和切入点共同组成了切面:时间、地点和要发生的“故事”
⑤ 引入(Introduction):引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
⑥ 目标(Target):即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事
⑦ 代理(proxy):应用通知的对象,类似设计模式里面的代理模式
⑧ 织入(Weaving):把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器
(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码
(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术
底层技术实现原理:(http://www.iteye.com/topic/1116696)
① 动态代理
② CGLIB字节码植入
Spring AOP采用动态代理机制和CGLIB字节码生成技术实现。动态代理机制和字节码生成都是在运行期间为目标对象生成一个代理对象,而将横切逻辑织入到这个代理对象中,系统最终使用的是织入了横切逻辑的代理对象,而不是真正的目标对象。
Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术;如果目标对象没有实现接口,则默认会采用CGLIB代理。
默认情况下,如果Spring AOP发现目标对象实现了相应接口Interface,则采用动态代理机制为其生成代理对象实例,即动态代理依靠接口实现。
而如果目标对象没有实现任何接口Interface,Spring AOP尝试使用CGLIB(Code Generation Library)这种动态字节码生成类库。CGLIB是针对类来实现代理的,原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
效率:JDK动态代理创建速度优于CGLIB字节码生成,但是动态代理在函数的调用性能上不如CGLIB。
(CGLIB:http://baike.baidu.com/view/1254036.htm)
Spring提供了4种实现AOP的方式:
① 经典的基于代理的AOP
② @AspectJ注解驱动的切面
③ 纯POJO切面
④ 注入式AspectJ切面
Spring支持五种类型的通知:
① Before(前) org.apringframework.aop.MethodBeforeAdvice
② After-returning(返回后) org.springframework.aop.AfterReturningAdvice
③ After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
④ Arround(周围) org.aopaliance.intercept.MethodInterceptor
⑤ Introduction(引入) org.springframework.aop.IntroductionInterceptor
4、注解
注解(Annotation)提供了一种安全的类似注释的机制,为了在代码中添加信息提供一种形式化得方法,使得可以在稍后某个时刻方便的使用这些数据(通过解析注解来使用这些数据),用来将任何的信息或者元数据与程序元素(类、方法、成员变量等)进行关联。其实就是更加直观更加明了的说明,这些说明信息与程序业务逻辑没有关系,并且是供指定的工具或框架使用的。Annotation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的申明语句中。
Annotation其实是一种接口。通过java的反射机制相关的API来访问Annotation信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。Java语言解释器在工作时会忽略这些Annotation,因此在JVM中这些Annotation是“不起作用”的,只能通过配套的工具才能对这些Annotation类型的信息进行访问和处理。
常用注解:
① @Component:是所有受Spring管理组件的通用形式,@Component注解可以放在类的头上,@Component不推荐使用。
② @Controller:对应表现层的Bean,也就是Action
③ @Service:对应的是业务层Bean
④ @Repository:对应数据访问层Bean
注解 | 来源 | 说明 |
---|---|---|
@Autowired | @Autowired 在org.springframework.bean.factory 包里定义,是Spring 框架的一部分,可以和@Qualifier结合使用。 |
@Autowired默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required = false)。
如果想使用名称装配可以结合@Qualifier注解使用,如下:
@Autowired
@Qualifier("baseDao")
private BaseDao baseDao;
|
@Resource | @Resource 在javax.annotation 包里定义,是Java 的一部分。 | @Resource默认按名称进行装配,装配顺序如下: 1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常; 2. 如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到则抛出异常; 3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常; 4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。 |
5、Bean的配置:
- 配置形式:基于 XML 文件的方式;基于注解的方式(基于注解配置 Bean;基于注解来装配 Bean 的属性)
- Bean 的配置方式:通过全类名(反射)、通过工厂方法(静态工厂方法 & 实例工厂方法)、FactoryBean
6、Bean的作用域
作用域 | 描述 |
---|---|
singleton(默认) | 单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例。 |
prototype | 原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。 |
request | 对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效。 |
session | 对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效。 |
global-session | 每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效。 |
如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此除非必要,否则尽量避免将Bean被设置成 prototype 作用域。
7、Bean的生命周期
通常使用ApplicationContext作为Spring容器,因此主要讲解ApplicationContext中Bean的生命周期。
1:Bean的建立:
容器寻找Bean的定义信息并将其实例化。
2:属性注入:
使用依赖注入,Spring按照Bean定义信息配置Bean所有属性。
3:BeanNameAware的setBeanName():
如果Bean类有实现BeanNameAware接口,会回调该接口的 setBeanName() 方法,传入该Bean的ID,此时该Bean就获得了自己在配置文件中的ID。
4:BeanFactoryAware的setBeanFactory():
如果Bean类有实现BeanFactoryAware接口,会回调该接口的 setBeanFactory() 方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory。
5:ApplicationContextAware的ApplicationContextAware():
如果Bean实现了ApplicationContextAware接口,会回调该接口的 setApplicationContext() 方法,传入该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext。
6:BeanPostProcessors的ProcessBeforeInitialization()
如果有Bean实现了BeanPostProcessor接口,则会回调该接口的 postProcessBeforeInitialzation() 方法。
7:initializingBean的afterPropertiesSet():
如果Bean类已实现InitializingBean接口,则会回调该接口的 afterProPertiesSet() 方法。
8:Bean定义文件中定义init-method:
可以在Bean定义文件中使用"init-method"属性设定方法名称,如果有以上设置的话,则执行到这个阶段,就会执行initBean()方法。
9:BeanPostProcessors的postProcessAfterInitialization()
如果有任何的BeanPostProcessors实例与Bean实例关联,则执行BeanPostProcessors实例的 postProcessAfterInitialization() 方法。
此时,Bean已经可以被应用系统使用,并且将保留在BeanFactory中直到它不在被使用。
10:Bean准备就绪:
经过流程9之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的IOC容器中会缓存一份该bean的实例;而对于scope为prototype的Bean,每次被调用都会new一个新的对象,其生命周期就交给调用方管理了,不再是Spring容器进行管理。
11:DisposableBean的destroy():
容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法。
12:Bean定义文件中定义destroy-method:
如果Bean配置了“destroy-method”方法,则会执行destroy-method配置的方法,至此,整个Bean的生命周期结束。
8、Spring MVC
(http://www.imooc.com/article/1392 & http://blog.csdn.net/cswhale/article/details/16941281 & http://blog.sina.com.cn/s/blog_7ef0a3fb0101po57.html & http://downpour.iteye.com/category/196182)
Spring web MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的 web 应用程序的组件。MVC 模式导致了应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)的分离,同时提供了在这些元素之间的松散耦合。
-
模型封装了应用程序数据,并且通常它们由 POJO 组成。
-
视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。
- 控制器主要用于处理用户请求,并且构建合适的模型并将其传递到视图呈现。
Spring的MVC框架主要由DispatcherServlet、处理器映射、处理器(控制器)、视图解析器、视图组成。
Spring MVC接口解释:
DispatcherServlet接口:
Spring提供的前端控制器,所有的请求都有经过它来统一分发。在DispatcherServlet将请求分发给Spring Controller之前,需要借助于Spring提供的HandlerMapping定位到具体的Controller。
DispatcherServlet是整个Spring MVC的核心。它负责接收HTTP请求组织协调Spring MVC的各个组成部分。其主要工作有以下三项:
1. 截获符合特定格式的URL请求。
2. 初始化DispatcherServlet上下文对应的WebApplicationContext,并将其与业务层、持久化层的WebApplicationContext建立关联。
3. 初始化Spring MVC的各个组成组件,并装配到DispatcherServlet中。
HandlerMapping接口:
能够完成客户请求到Controller映射。
Controller接口:
需要为并发用户处理上述请求,因此实现Controller接口时,必须保证线程安全并且可重用。Controller将处理用户请求,这和Struts Action扮演的角色是一致的。一旦Controller处理完用户请求,则返回ModelAndView对象给DispatcherServlet前端控制器,ModelAndView中包含了模型(Model)和视图(View)。
从宏观角度考虑,DispatcherServlet是整个Web应用的控制器;从微观考虑,Controller是单个Http请求处理过程中的控制器,而ModelAndView是Http请求过程中返回的模型(Model)和视图(View)。
ViewResolver接口:
Spring提供的视图解析器(ViewResolver)在Web应用中查找View对象,从而将相应结果渲染给客户。
SpringMVC原理图:
SpringMVC运行原理:
1、客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),web容器将请求转交给 DispatcherServlet。
2、DipatcherServlet接收到这个请求之后将根据请求的信息(包括URL、Http方法、请求报文头和请求参数Cookie等)以及HandlerMapping的配置找到处理请求的处理器(Handler)。
3-4、DispatcherServlet根据HandlerMapping找到对应的Handler,将处理权交给Handler(Handler将具体的处理进行封装),再由具体的HandlerAdapter对Handler进行具体的调用。
5、Handler对数据处理完成以后将返回一个ModelAndView()对象给DispatcherServlet。
6、Handler返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet通过ViewResolver将逻辑视图转化为真正的视图View。
7、Dispatcher通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端显示结果。