在伪装是小白之重学Spring MVC(一)中曾经介绍了Spring MVC比拟罕用的知识点,然而还是有些脱漏,比方将申请放入Session,在配置文件中配置太烦了,咱们是否将配置文件挪动至配置类中,又比方SSM(Spring Spring MVC MyBatis)整合。
本篇的次要内容就是介绍这些,将配置文件转换成配置类须要懂一些Spring的外围注解,比方@Import,这个注解在欢迎光临Spring时代(一) 上柱国IOC列传曾经介绍过了,不懂的能够再翻一下这篇文章。
放入session中
咱们如何将数据放入session中呢? 在原生Servlet时代,咱们从HttpServletRequest中获取HttpSession对象就好,像上面这样:
当然你在Spring MVC框架还能够接着用,Spring MVC又提供了@SessionAttributes注解用于将数据放入Session中,@SessionAttribute用于将session中的值,通过翻阅源码,咱们能够发现@SessionAttributes只能作用于类上,在翻阅源码的过程中发现了@SessionAttribute注解,原本认为和@SessionAttributes是一样的用途 ,起初翻源码才发现,这个是用来取出Session中的值放到办法参数中,只能作用办法参数上。
又通过看源码的正文,咱们能够看到SessionAttributes的两个value和name是同义语,假如有申请域(申请域中的数据都是呈K、V对模式,我门当初谈的就是key)中有和value值雷同的数据,那么就会将该数据也会放到Session中。而Type属性则是,只有是申请域的数据是该类型的,就放入到Session中。
咱们当初来测试一下@SessionAttributes:
ControllerSessionAttributes(value = "password")public class SessionDemoController { @RequestMapping(value = "/session", method = RequestMethod.GET) public String testGet(@RequestParam(value = "name") String userName, @RequestParam(value = "password") String password ,Model model) { System.out.println("username:" + userName); System.out.println("password:" + password); model.addAttribute("password",password); return "success"; }}
success.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>Title</title></head><body><h1> success </h1><h1> username: ${sessionScope.userName}</h1><h1> password: ${sessionScope.password}</h1></body></html>
url: http://localhost:8080/studySpringFrameWork_war_exploded/session?name=aa&&password=aaa
测试后果:
SessionAttribute:
RequestMapping(value = "/servlet", method = RequestMethod.GET) public String testGet(@RequestParam(value = "name") String userName, @SessionAttribute(value = "password")String password) { System.out.println("username:" + userName); System.out.println("password:" + password); return "success"; }
url: http://localhost:8080/studySpringFrameWork_war_exploded/servlet?name=aaa
测试后果:
能够看到url中没有password,password仍然打印进去有值,这个就是session中的值。
配置文件的配置挪动至配置类
简略的说配置文件的实质就是,在读取配置文件的时候,依据标签将对应类的对象注册进IOC容器中。然而配置文件配置了太多也是很烦的,可不可以转换成配置类的模式呢? Spring MVC: 当然能够。
那首先第一个问题就是以前是配置文件中让Tomcat启动的时候读取配置文件,注册DispacherServlet怎么移植?
通过org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer来移植,下面的正文很清晰,有的时候碰见英文不要冲突,读一读多累积几个单词,语法大多都不难:
如果感觉这个格局不是很喜爱,能够去看对应的Java Doc,上面是地址:
- https://docs.spring.io/spring...
接着咱们来看WebApplicationInitializer的正文:
Interface to be implemented in Servlet 3.0+ environments in order to configure the ServletContext programmatically -- as opposed to (or possibly in conjunction with) the traditional web.xml-based approach.
该接口在在Servlet 3.0以上的版本才被实现可能实现以配置类的模式配置ServletContext ,和传统的基于web.xml的配置形式绝对。Implementations of this SPI will be detected automatically by SpringServletContainerInitializer, which itself is bootstrapped automatically by any Servlet 3.0 container. See its Javadoc for details on this bootstrapping mechanism.
SpringServletContainerInitializer会主动监测该接口的实现类,而后被任何反对Servlet 3.0版本以上的容器主动加载。
感觉是不是有点拗口,有点不明确什么意思的感觉,我的了解就是在反对Servlet 3.0的Servelt容器能力应用Spring MVC配置类的模式。上面这段正文说的是该接口的实现类会被Servlet容器主动加载。
然而咱们这次并不是抉择实现WebApplicationInitializer,这样做有些麻烦了,Spring MVC提供了一个简略点的Web初始化器: org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer,供咱们应用。
AbstractAnnotationConfigDispatcherServletInitializer是一个抽象类,咱们继承它,而后继承一下它看看:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[0]; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[0]; } /** * 用于设置拦挡的URL * @return */ @Override protected String[] getServletMappings() { return new String[0]; }}
getRootConfigClasses和getServletConfigClasses这两个办法,咱们就须要专门拿进去讲讲,从名字上能够推断都是加载配置类,那这两个办法有什么区别呢? 咱们还是翻翻官网文档,提供了Java Config 和 基于xml的等价写法:
咱们看下比照哈:
也就是说getServletConfigClasses作用就是等同于加载Spring MVC的配置文件,getServletMappings就是设定要拦挡的URL。getRootConfigClasses。咱们本次学习的Spring MVC都是只注册了一个DispatcherServlet,Spring MVC容许咱们注册多个DispacherServlet,那么多个Spring MVC的配置文件就是隔开的,然而呢,在这些不同的DispatcherServlet之间如果有一些bean须要共享怎么办呢?getRootConfigClasses就加载顶层的配置类,被各个DispatcherServlet共享。
以后咱们就只有一个DispatcherServlet,所以咱们就只建一个空类。所以AppInitializer 就被革新成了上面这样:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{RootConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; } /** * 用于设置拦挡的URL * * @return */ @Override protected String[] getServletMappings() { return new String[]{"/"}; }}@Configurationpublic class RootConfig {}@EnableWebMvc@ComponentScan(basePackages = {"org.example.mvc"})@Configurationpublic class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyHandlerInterceptor()); }}
@EnableWebMvc
是新面孔,咱们这里须要大抵讲一下。
@EnableWebMvc简介
要齐全讲清楚@EnableWebMvc
,咱们齐全能够再开一篇文章去讲,因为在这个注解身上,Spring做了大量的工作,要齐全讲清楚不是一件简略的事件,这里我做的介绍只是让各位同学明确,@EnableWebMvc
相当于配置文件中做了什么?
能翻官网文档,尽量还是翻官网文档,官网文档有这部分的介绍:
这个截图仿佛还不是很清晰,咱们放在IDE里截图看一下:
<mvc:annotation-driven/> 这行不少视频都说,这个是把加上了@Controller的类变成解决申请的类,然而也不晓得是不是跟我用的Spring MVC版本有关系,我没加这个也行。
然而倡议你还是加,解释分明不加也行(可能会出一些莫名的问题),然而这也不是一件简略的事件,因为这要波及很多源码的解读,如果有兴致理解能够参看:
spring mvc的
<mvc:annotation-driven/>
有什么用?
拦截器等标签的移植
各位认真看下面的WebConfig这个类,这个类继承了WebMvcConfigurer ,我在外面重写了addInterceptors办法,从办法名字上能够推断进去,这个办法是加拦截器的。而后咱们将残缺的写一下:
@EnableWebMvc@ComponentScan(basePackages = {"org.example.mvc"})@Configurationpublic class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("login"); }}
咱们再度比照一下:
对于bean标签咱们不讲怎么移植,这在欢迎光临Spring时代(一) 上柱国IOC列传曾经讲过了,这里不再讲一遍。WebMvcConfigurer 的办法这里就不一一拿进去细讲了,能够本人看下正文。咱们这里只讲大抵的思维。
测试一下
留神要先正文掉web.xml事后加载的那个配置。基本上测试是胜利的。这里我就不再贴代码了。
我把代码放在码云上,各位有兴致的同学能够翻翻看看。基本上到这里,Spring MVC就告一段落了。
地址如下:
- https://github.com/CXK6013/SSM
SSM整合
SSM 整合前言
到这里咱们就能够开始讲一下,SSM整合了,过后跟同学一块学SSM整合的时候,有的同学不了解思维,就是背,有的时候本人尝试做整合,背错了就整合失败了。这里我心愿讲思维,有了这个思维,我想再去做整合的时候,就会晓得本人错在哪里。
在看到这里的时候,我心愿你对Spring的思维有一点理解,倡议参看我Spring系列的文章:
- 欢迎光临Spring时代-绪论
- 欢迎光临Spring时代(一) 上柱国IOC列传
- 代理模式-AOP绪论
- 欢迎光临Spring时代(二) 上柱国AOP列传
留神这里是SSM整合,那MyBatis也要求会,如果不会请参看我MyBatis系列的文章。
- 伪装是小白之重学MyBatis(一)
如果不会Spring MVC,请参看我Spring MVC系列的文章:
- 伪装是小白之重学Spring MVC(一)
SSM整合的思维-天下归Spring统
Spring Framework 是一个胜利的框架,解决了对象之间有简单的依赖关系的时候,在不心愿是硬编码的状况下,取对象优雅和可配置化,那么其余框架也要纳入到Spring Framework中,让咱们取该框架的对象时候优雅一点。这也就是整合思维,将该框架的外围对象放入IOC容器中,而后取出来。再讲一遍,假如是MyBatis须要数据源、扫描xml,咱们就对立在IOC容器中配置。
而后取的时候用Spring 提供的取对象形式,取出来就行了。SSM整合的核心思想就是将Spring MVC、MyBatis的外围对象放进IOC容器中,而后咱们取。
那么咱们对MyBatis 整合的愿景是什么呢? 咱们再看下咱们学习MyBatis的代码:
那怎么整合将MyBatis整合进入Spring能力实现这样的成果呢? 仅仅看目前的代码,咱们是将SqlSessionFactory放入IOC容器中,而后从IOC容器获取session,再调用getMapper办法。这样是不是有点麻烦呢? 有没有更简洁的计划呢? 这个解决方案叫mybatis-spring,依赖如下:
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency>
这个解决方案可能做到将Mapper接口(即Xml对应的接口,下文会统称为Mapper对象)对象放入到IOC容器中,咱们只用在须要对应Mapper的中央,用@Autowired
注解注入即可
整合MyBatis
咱们这里只介绍基于xml模式的,基于配置类的,将bean标签挪动至配置类即可。
基于xml模式的
首先咱们用Druid连接池治理数据源,那当然是在配置文件中配置一个即可:
<!--加载配置文件--><bean id = "placeholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:jdbc.properties</value> </list> </property> </bean>
<bean id = "dataSource" class = "com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.dev.driver}"> </property> <property name = "url" value="${jdbc.dev.driver}"> </property> <property name = "username" value = "${jdbc.dev.username}"> </property> <property name = "password" value = "${jdbc.dev.password}"> </property> </bean>
要实现将Mapper接口的对象放入到IOC容器中,外围是两个类:
- org.mybatis.spring.SqlSessionFactoryBean
- org.mybatis.spring.mapper.MapperScannerConfigurer
SqlSessionFactoryBean概览:
MapperScannerConfigurer概览:
其实下面也有官网文档:
地址是: http://mybatis.org/spring/zh/...
这下面讲了mybatis-spring的起源和如何整合,咱们照着做就行。
<bean id = "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref = "dataSource"></property> <property name = "mapperLocations" value="org\example\mybatis\*.xml"> </property> </bean> <bean id = "mapperScannerConfigurer" class = "org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value = "org.example.mybatis" /> <property name="sqlSessionFactoryBeanName" value = "sqlSessionFactory"></property> </bean>
可能有同学会问,那之前的配置文件咋弄? 切换环境怎么办?
SqlSessionFactoryBean中有一个configuration字段是Configuration类型的,咱们看下Configuration类:
而后再配置就行了。如果你不想配置bean,也能够指定门路,通过SqlSessionFactoryBean的configLocation指定MyBatis的配置文件地位就能够了。
整合Spring MVC
其实在下面介绍Spring MVC的时候曾经在用Spring,大家可能感触不到整合的这个过程,这就叫无缝集成。
接下来讲的就是,如果你有多个DisPatcherServlet,然而有一批bean是共享的,又不想在在每个配置文件中都配置一份。怎么办。在用配置类的状况下,咱们是通过getRootConfigClasses来配置的,还记得它的xml写法吗?
<web-app> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 这里用来加载多有DispatcherServlet共享的bean--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/root-context.xml</param-value> </context-param> <servlet> <servlet-name>app1</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/app1-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app1</servlet-name> <url-pattern>/app1/*</url-pattern> </servlet-mapping></web-app>
小插曲
我认为这里整合的逆风逆水一点故障都没有,就打算收尾了,而后就启动了我的项目,而后就报少jar包,少的是这两个:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.8.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> </dependency>
而后发现补上了之后,还是注入失败,此时我陷入了沉思,最大的苦楚还是,我不晓得我错在了哪里? 因为控制台也不报错,比报错还可怕的就是,程序不报错。于是我开始排查,在main函数里加载配置文件试了试看:
public static void main(String[] args) throws IOException { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-MVC.xml"); BlogMapper blogMapper = applicationContext.getBean(BlogMapper.class); System.out.println(blogMapper.selectAllReturnMap());}
发现胜利获取,于是我就狐疑我的配置文件没加载,上面是我的web.xml中的配置:
而后是Spring MVC的配置文件:
于是我猜测首先加载的是context-param标签中的配置文件,在这个过程中实现对各个bean的初始化。
而后再加载Spring MVC的配置文件,对Spring MVC的一些bean进行初始化。
而后我把整合MyBatis的代码就移入了applicationContext.xml中,而后就胜利了。
排错思路
基本上我也是在引入日志之后,才推断进去我的Spring-MVC配置文件没有胜利加载的,日志能够把Spring IOC容器创立的bean打印进去:
通过日志我发现我独自在main函数中加载Spring MVC的配置文件的时候,有sqlSessionFactory创立胜利的日志,然而我启动Tomcat就没有。所以我才推断Spring-MVC的配置文件加载的是不是有些晚了。而后验证一下果然是。日志配置也有一些坑,比如说Log 4j 2不反对properties的配置文件,名字也必须交log4j2.properties。而后日志怎么配,跟着GitHub上的配就行了。GitHub上对应我的项目的地址如下:
- https://github.com/CXK6013/SSM
源码选讲之Spring MVC的执行流程
接下来咱们钻研一下Spring MVC的执行流程,这也是高频面试题,钻研这个是为了让咱们对Spring MVC的了解更进一步。
首先申请总是先进入到DispatcherServlet中,而后咱们还是大抵看一下DispatcherServlet这个源码,源码太宏大了,不便于展现,这里就间接看两个外围办法了:
- HandlerExecutionChain (处理器执行链)
- doDispatch (申请散发)
咱们次要看doDispath办法:
基于此咱们能够大抵画出Spring MVC的执行流程:
其实这个Spring MVC执行流程曾经人尽皆知了,毕竟是高频面试题,然而我是想从源码的角度去看。基本上整个流程都在DispatcherServlet的dispatch办法中,有兴致的同学能够也看一下。
总结一下
每次重学框架都会有新的认知,这次也算是小有收货,把心里以前的疑难都扫清了。心愿对各位同学而学习Spring MVC会有所帮忙,然而到这里就完结了吗? 远远没有,不感觉这些配置有些烦吗? 还有依赖治理,其实我做的时候都是小心翼翼的,惟恐起了依赖抵触,能不能简化一下呢? 当然能够,这些都将由Spring 家族的一个明星成员Spring Boot给出答案。
参考资料
- 详解@EnableWebMvc
- 如何应用纯java config来配置spring mvc
- Spring MVC官网文档
- getServletConfigClasses() 和 getRootConfigClasses()的不同点