Spring是一个轻量级框架,核心内容是IOC和AOP。它目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。主要包括以下七个模块(简单了解一下就行):
● Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
● Spring Core:核心类库,所有功能都依赖于该类库,提供IOC和DI服务;
● Spring AOP:AOP服务;
● Spring Web:提供了基本的面向Web的综合特性,提供对常见框架如Struts2的支持,Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器;
● Spring MVC:提供面向Web应用的Model-View-Controller,即MVC实现。
● Spring DAO:对JDBC的抽象封装,简化了数据访问异常的处理,并能统一管理JDBC事务;
● Spring ORM:对现有的ORM框架的支持;
(1)Spring属于低侵入式设计,代码的污染极低;
(2)Spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
(3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。
(4)Spring对于主流的应用框架提供了集成支持。
IOC,英文Inversion of Control的缩写,即“控制反转”,是一种设计思想。所谓控制反转就是把对象(bean)对象和维护对象(bean)之间的关系的权利转移到Sqring容器中去了(ApplicationContext.xml)而程序本身不在维护了。
什么意思呢?在javase中一个对象中使用另外的对象,就必须主动通过new指令去创建依赖对象,使用完后还需要销毁(比如Connection等),对象始终会和其他接口或类耦合起来。也就是说创建对象的时机和主动权都是由自己把控的,而 IOC 则是由专门的容器来帮忙创建对象,将所有的类都通过Spring容器中注册,当需要某个对象时,不再需要自己主动去 new 了,只需告诉Spring容器,然后让Spring容器主动给你。也就是说,对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,而在IOC中,所有的对象都被 Spring容器控制,由 Spring 容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转。
IOC的一个重点就是在程序运行时,动态的向某个对象提供它所需要的其他对象,这一点是通过DI(Dependency Injection,依赖注入)来实现的,即应用程序在运行时依赖 IoC 容器来动态注入对象所需要的外部依赖。而 Spring 的 DI 具体就是通过反射实现注入的,反射允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性。
IOC是解决耦合问题的一种设计思想,DI是IOC的一种实现。IOC主要的实现方式有两种:依赖查找(DL),依赖注入(DI)。
AOP,称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
JDK动态代理只提供接口的代理,不支持类的代理,因此要求被代理类实现接口。
CGLIB是通过继承的方式做的动态代理。
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。静态代理与动态代理区别在于生成AOP代理对象的时机不同
(1)AspectJ是静态代理,也称为编译时增强,AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类的代理类(即最终真正的代理类,这个类继承自Proxy并实现了我们定义的接口),当代理对象调用真实对象的方法时, InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;
② 如果被代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
1.连接点(Join point):指程序运行过程中所执行的方法。在Spring AOP中,一个连接点总代表一个方法的执行。
2.切点(Pointcut):切点用于定义要对哪些Join point进行拦截。
切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截,比如指定拦截add*、search*。annotation方式可以指定被哪些注解修饰的代码进行拦截。
3.通知(Advice):指要在连接点(Join Point)上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知有各种类型,包括Around、Before、After、After returning、After throwing。
4.切面(Aspect):可以看成 Pointcut切点 和 Advice通知的结合,通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
5.目标对象(Target):包含连接点的对象,也称作被通知(Advice)的对象。 由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象。
6.织入(Weaving):通过动态代理,在目标对象(Target)的方法(即连接点Join point)中执行增强逻辑(Advice)的过程。
7.引入(Introduction):添加额外的方法或者字段到被通知的类。Spring允许引入新的接口(以及对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。
1. 前置通知(Before Advice):在连接点(Join point)之前执行的通知。
2. 后置通知(After Advice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
3. 环绕通知(Around Advice):包围一个连接点的通知,这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
4. 返回后通知(AfterReturning Advice):在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)
5. 抛出异常后通知(AfterThrowing advice):在方法抛出异常退出时执行的通知
● set()方法注入;
● 构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
● 接口注入;(用的比较少)
注意:通过Spring创建的对象默认是单例的,如果需要创建多实例对象可以在标签后面添加一个属性scope="prototype":
<bean name="..." class="..." scope="prototype">
Spring Bean的生命周期只有四个阶段:实例化 Instantiation --> 属性赋值 Populate --> 初始化 Initialization --> 销毁 Destruction
singleton, 也就是单例,意味着在整个Spring容器中只会存在一个Bean实例。
prototype,翻译成原型,意味着每次从IOC容器去获取指定Bean的时候,都会返回一个新的实例对象。
request, 针对每一次http请求,都会创建一个新的Bean
session,以sesssion会话为纬度,同一个session共享同一个Bean实例,不同的session产生不同的Bean实例
globalSession,针对全局session纬度,共享同一个Bean实例
当 bean 在 Spring 容器中组合在一起时,它被称为装配或 bean 装配。Spring容器需要知道需要什么 bean 以及容器应该如何使用依赖注入来将 bean 绑定在一起,同时装配 bean。
Spring 容器能够自动装配 bean。也就是说,可以通过检查 BeanFactory 的内容让 Spring 自动解析 bean 的协作者。 自动装配的不同模式:
no - 这是默认设置,表示没有自动装配。应使用显式 bean 引用进行装配。
byName - 它根据 bean 的名称注入对象依赖项。它匹配并装配其属性与 XML文件中由相同名称定义的 bean。
byType - 它根据类型注入对象依赖项。如果属性的类型与 XML 文件中的一个 bean 名称匹配,则匹配并装配属性。构造函数- 它通过调用类的构造函数来注入依赖项。它有大量的参数。
autodetect - 首先容器尝试通过构造函数使用 autowire 装配,如果不能,则尝试通过 byType 自动装配。
1)给容器注入组件注解
@Component:泛指各种组件
@Controller、@Service、@Repository都可以称为@Component。
@Controller:控制层
@Service:业务层
@Repository:数据访问层
2)注入bean的注解
@Autowired、@Inject是默认按照类型匹配的,
@Resource是按照名称匹配的,@Autowired如果需要按照名称匹配需要和@Qualifier一起使用,@Inject和@Name一起使用。
@Primary让spring进行自动装配的时候,默认使用首选的bean,和@Qualifier一个效果。
3)切面(AOP)相关注解
@Aspect 声明一个切面
@After 在方法执行之后执行(方法上)
@Before 在方法执行之前执行(方法上)
@Around 在方法执行之前与之后执行(方法上)
@PointCut 声明切点
(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入
1. BeanFactory是Spring里面最底层的接口,是最简单的容器,只提供了实例化对象和拿对象的功能;ApplicationContext是应用上下文,继承BeanFactory接口,它是Spring的一个更高级的容器,提供了更多的有用的功能。
2. 两者在装载Bean的时候,BeanFactory在启动的时候不会去实例化Bean,只有从容器中拿Bean的时候才会去实例化,因此应用启动的时候占用资源很少;而ApplicationContext在启动的时候就把所有的Bean全部实例化了。这样系统在启动的时候,可以尽早的发现系统中的配置问题, 建议web应用在启动的时候就把所有的Bean都加载了,以便让费时的操作放到系统启动中完成。
两者的区别在:前者是工厂类,简单理解成 beanName和bean对象映射关系的维护者(是个容器),提供根据beanName查询bean对象的能力;后者是工厂类,描述的是Bean对象实例化的过程,用于生成特定类型的对象。BeanFactory is a factory, FactoryBean is a bean。
FactoryBean 当你向容器注册名字为 factoryBeanName 的 FactoryBean的时候,你向容器注册的是 名字为&factoryBeanName的FactoryBean的对象,,通过factoryBeanName获取到的是 FactoryBean#getObject 返回的对象,该对象不受Spring 容器管理.
当创建Bean的过程中涉及到多个依赖对象的复杂配置(不是简单的属性注册),或者存在一定的复用性时,可以通过 FactoryBean 简化一部分实例过程,减少无关Bean的注册。例如 AbstractEntityManagerFactoryBean 相关实现。
Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:
RDB:是Redis DataBase缩写快照,按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
优点:
只有一个文件 dump.rdb,方便持久化。
容灾性好,一个文件可以保存到安全的磁盘。
性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单 独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
相对于数据集大时,比 AOF 的启动效率更高。
缺点:
数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数 据丢失。所以这种方式更适合数据要求不严谨的时候)
==
AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件
中,当重启Redis会重新将持久化的日志中文件恢复数据。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复
优点:
数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录 到 aof 文件中一次。
通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一 致性问题。
AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flflushall))
缺点:
AOF 文件比 RDB 文件大,且恢复速度慢。
数据集大的时候,比 rdb 启动效率低。
类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。
比如下图中A类依赖了B类,B类依赖了C类,而最后C类又依赖了A类,这样就形成了循环依赖问题。
1.通过构造方法进行依赖注入时产生的循环依赖问题。
该情况会报异常,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
2.通过setter方法进行依赖注入且是在多例模式下产生的循环依赖问题。
该情况会报异常,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。
3.通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。
因此在Spring中,前两种都会报异常,只有第(3)种方式的循环依赖问题能被解决。
Spring在单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要功臣。解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。
Spring中有三个缓存,用于存储单例的Bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储。如果调用getBean,则需要从三个缓存中依次获取指定的Bean实例。 读取顺序依次是一级缓存 ==> 二级缓存 ==> 三级缓存。
Spring启动过程是IOC容器的启动过程,本质是创建和初始化bean工厂(BeanFactory).BeanFactory是Spring IOC的核心,Spring使用beanFactory来实例化,配置和管理bean。对于web程序,IOC容器启动过程即是建立上下文的过程,web容器会提供一个全局的servletContext上下文环境。其启动过程主要包含三个类,ContextLoaderListener,ContextLoader和XmlWebApplicationContext。
在web.xml中提供ContextLoaderListener上下文监听器,在web容器启动时,会触发容器初始化事件,ContextLoaderListener会监听到这个事件,从而触发ContextInitialized方法完成上下文初始化,这个方法中调用父类ContextLoader的方法完成上下文初始化。ContextLoader类中主要完成三件事:
1)创建WebApplicationContext;
2)加载对应的Spring配置文件中的bean;(refresh方法,完成bean的加载)
3)将WebApplicationContext放入servletContext中。ContextLoaderListener监听器初始化完之后,开始初始化web.xml中配置的servlet,如DispatcherSevlet
ContextLoaderListener监听器监听的是servletContext,当web容器初始化后,servletContext发生变化时,会触发相应事件。
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。Spring只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过 redo log 和 undo log实现的。Spring会在事务开始时,根据当前环境中设置的隔离级别,调整数据库隔离级别,由此保持一致。
①编程式事务管理使用TransactionTemplate。
②声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
Spring事务的传播机制说的是,当多个事务同时存在的时候,Spring如何处理这些事务的行为。事务传播机制实际上是使用简单的ThreadLocal实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。① PROPAGATION_REQUIRED:(默认传播行为)如果当前没有事务,就创建一个新事务;如果当前存在事务,就加入该事务。
② PROPAGATION_REQUIRES_NEW:无论当前存不存在事务,都创建新事务进行执行。
③ PROPAGATION_SUPPORTS:如果当前存在事务,就加入该事务;如果当前不存在事务,就以非事务执行。‘
④ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
⑤ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按REQUIRED属性执行。
⑥ PROPAGATION_MANDATORY:如果当前存在事务,就加入该事务;如果当前不存在事务,就抛出异常。
⑦ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
② ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。
③ ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。
④ ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。
⑤ ISOLATION_SERIALIZABLE:所有事务逐个依次执行。
1. @Transactional配置的方法非public权限修饰(例如private的就别加了);
2. @Transactional所在类非Spring容器管理的bean(例如一个Util就别加了,都没注入容器,你叫Spring怎么用他的AOP帮你管理事务?);
3. @Transactional所在类中,注解修饰的方法被类内部方法调用(例如同一个class类中,方法A调用方法B,只在方法B加了@Transactional注解,那就失效了);
4. 业务代码抛出异常类型非RuntimeException,事务失效;
5. 业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有throw new RuntimeExecption异常(只有该异常或者他的父异常例如Exception可以回滚);
6. 注解@Transactional中Propagation属性值设置错误(例如Propagation.NOT_SUPPORTED,一般不会这么设置)
7. mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用。
在java中异常的基类为Throwable,Error和Exception继承Throwable。Exception中RuntimeException及其子类成为未检查异常(unchecked),其它Exception成为已检查异常(checked)。
Spring的事务管理默认是针对unchecked exception回滚,也就是默认对Error异常和RuntimeException异常以及其子类进行事务回滚,且必须抛出异常,若使用try-catch对其异常捕获则不会进行回滚!(Error异常和RuntimeException异常抛出时不需要方法调用throws或try-catch语句);
checked异常,checked异常必须由try-catch语句包含或者由方法throws抛出,且事务默认对checked异常不进行回滚。
单例模式应该是大家印象最深的一种设计模式了。在Spring中最明显的使用场景是在配置文件中配置注册bean对象的时候设置scope的值为singleton 。
<bean class="com.bjpowernode.pojo.User" id="user" scope="singleton">
<property name="name" value="张三"></property>
</bean>
原型模式也叫克隆模式,Spring中该模式使用的很明显,和单例一样在bean标签中设置scope的属性prototype即表示该bean以克隆的方式生成
<bean class="com.bjpowernode.pojo.User" id="user" scope="prototype">
<property name="name" value="张三"></property>
</bean>
模板模式的核心是父类定义好流程,然后将流程中需要子类实现的方法就抽象话留给子类实现,Spring中的JdbcTemplate就是这样的实现。我们知道jdbc的步骤是固定
1)加载驱动,
2)获取连接通道,
3)构建sql语句.
4)执行sql语句,
5)关闭资源
在这些步骤中第3步和第4步是不确定的,所以就留给客户实现,而我们实际使用JdbcTemplate的时候也确实是只需要构建SQL就可以了.这就是典型的模板模式。我们以query方法为例来看下JdbcTemplate中的代码.
观察者模式定义的是对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。使用比较场景是在监听器中而spring中Observer模式常用的地方也是listener的实现。如ApplicationListener.
简单工厂模式:就是通过工厂根据传递进来的参数决定产生哪个对象。Spring中我们通过getBean方法获取对象的时候根据id或者name获取就是简单工厂模式了。
<context:annotation-config/>
<bean class="com.bjpowernode.pojo.User" id="user" >
<property name="name" value="张三"></property>
</bean>
工厂方法模式:在Spring中我们一般是将Bean的实例化直接交给容器去管理的,实现了使用和创建的分离,这时容器直接管理对象,还有种情况是,bean的创建过程我们交给一个工厂去实现,而Spring容器管理这个工厂。这个就是我们讲的工厂模式,在Spring中有两种实现一种是静态工厂方法模式,一种是动态工厂方法模式。以静态工厂来演示
/**
* User 工厂类
*
*/
public class UserFactory {
/**
* 必须是static方法
* @return
*/
public static UserBean getInstance(){
return new UserBean();
}
}
将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。这就是适配器模式。在Spring中在AOP实现中的Advice和interceptor之间的转换就是通过适配器模式实现的。
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
// 通知类型匹配对应的拦截器
return new MethodBeforeAdviceInterceptor(advice);
}
}
装饰者模式:装饰者模式又称为包装模式(Wrapper),作用是用来动态的为一个对象增加新的功能。装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。
具体的使用在Spring session框架中的SessionRepositoryRequestWrapper使用包装模式对原生的request的功能进行增强,可以将session中的数据和分布式数据库进行同步,这样即使当前tomcat崩溃,session中的数据也不会丢失。
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略。
责任链模式:AOP中的拦截器链
委托者模式:DelegatingFilterProxy,整合Shiro,SpringSecurity的时候都有用到。
桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库。
Spring MVC是一个基于MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
1. 与Spring框架集成,因此拥有Spring的优点。
2. 支持RestFul风格以及支持各种请求资源的映射策略。
3. SpringMVC提供强大的约定大于配置的契约式编程支持,例如前端控制器(dispatcherServlet) ,请求到处理器映射(handlerMapping),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)。开发人员仅需规定进行配置即可。
4. 可以十分灵活地实现数据验证、格式化和数据绑定机制,可以使用任意对象进行数据绑定操作。
(1)前端控制器 DispatcherServlet(不需要程序员开发)
作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
(2)处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的URL来查找Handler
(3)处理器适配器HandlerAdapter
注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
(4)处理器Handler(需要程序员开发)
(5)视图解析器 ViewResolver(不需要程序员开发)
作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)
(6)视图View(需要程序员开发jsp)
View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
流程不用记得那么清楚,能把SpringMVC程序开发步骤说清楚就行。
第一步:发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器向处理器映射器(HandlerMapping)发请求查找 Handler (可以根据xml配置、注解进行查找)
第三步:处理器映射器向前端控制器返回Handler,处理器映射器会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略
第四步:前端控制器调用处理器适配器(HandlerAdapter)去执行Handler
第五步:处理器适配器将会根据适配的结果去执行Handler
第六步:Handler执行完成给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
第八步:前端控制器请求视图解析器(ViewResolver)去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
第十一步:前端控制器向用户响应结果
mvc是一种思想,可以理解成一种设计模式(设计模式就是日常开发中编写代码的一种好的方法和经验的总结)。模型(model)-视图(view)-控制器(controller),三层架构的设计模式。用于实现前端页面的展现与后端业务数据处理的分离。
mvc设计模式的好处
1.分层设计,实现了业务系统各个组件之间的解耦,有利于业务系统的可扩展性,可维护性。
2.有利于系统的并行开发,提升开发效率。
@Controller:该注解用于控制层SpringMVC中的控制器
@RequestMapping:该注解用于url请求映射,可用于类或方法上。
@RequestBody:该注解实现接收http请求的json数据,将json转换为java对象。
@ResponseBody:该注解实现将controller方法返回对象转化为json对象响应给客户。
请求路径上有个id的变量值,可以通过@PathVariable来获取 @RequestMapping(value =“/page/{id}”, method = RequestMethod.GET)
@RequestParam用来获得静态的URL请求入参 spring注解时action里用到。
是单例模式,在多线程访问的时候有线程安全问题,解决方案是在控制器里面不能写可变状态量,如果需要使用这些可变状态,可以使用ThreadLocal机制解决,为每个线程单独生成一份变量副本,独立操作,互不影响。
可以将异常抛给Spring框架,由Spring框架来处理;我们只需要配置简单的异常处理器,在异常处理器中添视图页面即可。
相同点:都是基于mvc的表现层框架,都用于web项目的开发。
不同点
1、前端控制器不一样。Spring MVC的前端控制器是servlet:DispatcherServlet。struts2的前端控制器是filter:StrutsPreparedAndExcutorFilter。
2、请求参数的接收方式不一样。Spring MVC是使用方法的形参接收请求的参数,基于方法的开发,线程安全,可以设计为单例或者多例的开发,推荐使用单例模式的开发(执行效率更高),默认就是单例开发模式。struts2是通过类的成员变量接收请求的参数,是基于类的开发,线程不安全,只能设计为多例的开发。
3、Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,Spring MVC通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。
4、与spring整合不一样。Spring MVC是spring框架的一部分,不需要整合。在企业项目中,Spring MVC使用更多一些。
(1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4"
(2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com"
SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
过滤器:依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。
拦截器:依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。
通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以通过el表
达式拿到。
返回值可以有很多类型,有String, ModelAndView,但一般用String比较好。
通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 :
(1)加入Jackson.jar
(2)在配置文件中配置json的映射
(3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。