SpringMVC架构:
Spring MVC 的精髓就是她核心的执行流程~
接下来,通过整合起来的代码,解读一个个配置文件~
1.web.xml
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
filter-name:当前节点的名称
filter-class:字符集编码格式的一个过滤器,用来避免乱码
filter-mapping:帮助我们做请求匹配 ,"/*"表示所有请求,意思就是所有的请求都要找这个名为encodingFilter过滤器,就可以找到上面的filter了,最终做的事情就是把所有的请求都转换为utf-8 的编码格式。
为什么要这样做?
一般我们写servlet 的时候,每一个servlet的都要先写两句话:
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8");
说白了就是把所有的请求和响应都转化为utf-8的编码格式,这个操作所有的servlet都要做,现在我们呢再web.xml中统一进行了配置,意味着不用在代码中每次都写这两句话了~
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
context-param取代了:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
由于配置spring的地方在web层中,所以她和本地的main方法有一定的区别,因为在本地有对象调对象,有方法调方法,自己写了什么要写什么自己很清楚,但是在web层,对象由tomcat创建,方法也是tomcat来调用的,所以她需要明确的时时刻刻的知道Spring中发生了什么变化,所以需要配置监听器listener
这两个东西配合起来tomcat才能帮我们管理spring
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
这个一看不就是servlet嘛~
这个就是用来配置springMVC的前端控制器
DispatcherServlet:之前我们有一个请求就需要对应一个处理这个请求的servlet ,现在SpringMVC做了一件很牛的事情:她全局只有一个servlet,所有的请求都往这一个servlet发,然后再从这个servlet中往出分发,这就是这个前端控制器的作用~
原理
为什么要用servlet?
没有servlet前端和后台的代码就无法交互了,就是一个桥梁,为什么要有多个servlet,因为我们有多个请求,一个请求对应一个servlet,就意味着一个请求有一种处理方式,但是spring不这样认为,spring提出,一个请求对应一种处理方式肯定没问题,但是处理请求不一定要放在servlet中啊,或者说处理请求就不应该放在servlet中,这是后端代码的事情~
发送请求,接收请求是servlet,但是处理请求就不一定要用servlet了啊,因为请求servlet已经接上了,接上之后,可不可以用另外一个东西处理请求,servlet只要把数据给到他不就好了,servlet只做一个中转站,只需要把数据拿上,然后传到处理请求的地方,处理请求的地方拿到数据就可以进行处理了,servlet只是扮演了一个搬运工的角色。
Spring做了一件很牛的事情,就是所有的请求都往一个servlet中发,因为我们可以发现一件事情,就是只要把请求发到servlet中,其实就已经到后台了,那么servlet再怎么跟别的类交互,就不受前端后台这个跨度的影响了,所以,所有的请求都往一个servlet中发, 然后这一个servlet,再往你自己创建的类里面分发,因为请求只有servlet可以处理,servlet拿到的是数据,所以servlet分发的是数据,只要把数据给了后台处理就行了!
而对于后台代码来讲,不行啊~你把数据给了我,我不知道这个请求是谁鸭,刚刚不是提到对应的请求有对应的处理嘛。现在再来讲,请求是什么?servlet不是可以截获到你请求的路径嘛,之前玩servlet的时候,最后一个"/"后面的就是你的请求,那OK,servlet直接把这个请求截获,不就可以进行匹配了?这其中只有两件事,第一件事:DispatcherServlet她必须知道你要做的事情是什么,因为这个servlet中有请求,她要知道你要做的是什么,你是处理哪个请求的方法,所以DispatcherServlet需要知道的是你要处理哪个请求,第二件事:DispatcherServlet要做数据的抽取和封装,她要从请求中把数据拿出来,拿出来之后,她还能进行格式转换,因为前端传到后后台的数据全都是String类型的,但是后台的数据类型又有很多的区分,都是String类型的肯定是不行的,对后台很不友好,DispatcherServlet要做的就是,拿到数据之后,转换为它本身的数据类型,其他的就可以交给后台处理了。
所以,DispatcherServlet的作用就是,接收请求,剥离数据,转换数据(SpringMVC的内置转换器)转换为对应的类型,分发请求到后台处理对应请求的方法上去,她前期的工作就结束了,后台拿到请求处理完数据是有返回值的,这个返回值是要返回前端页面的,但是无法直接传给前端,这时候DispatcherServlet就又出来工作了,你把数据返回给DispatcherServlet,DispatcherServlet可以和前端页面进行交互啊,所以后台把结果返回给DispatcherServlet,然后DispatcherServlet把结果送到前端页面上去。
后台业务产生的结果肯定是不一样的所以给DispatcherServlet返回一个String或者int她不认识,因为这是后台业务类型的东西,无法公用,而DispatcherServlet工作的时候只能处理公用的东西,返回个String它是不公用的,为什么?因为换个业务返回值不一样了,这个String什么都无法代表。
这时候,Spring又想了个办法,她自己封装了一个类:ModelAndView,Model:指的是模型层(业务层+dao),View:视图。所以你给DispatcherServlet返回的数据,DispatcherServlet要两部分,第一部分,Model,就是dao,业务层处理完返回的数据,第二部分,做完数据处理,有了返回值之后,你要返回哪个页面,要给一个View 。
DispatcherServlet是一个中间人,对于这个请求处理完要去哪个页面是我们根据自己的业务做决定的,所以需要告诉DispatcherServlet你要去哪个页面,你还得告诉DispatcherServlet产生了哪些数据,所以Spring把这两个东西封装成了Model(只有数据),View(只有页面),ModelAndView(两个都有)
这就是DispatcherServlet的所有工作,当你把ModelAndView给了DispatcherServlet后,你的工作就结束了。
SpringMVC也需要配置,所以在param-value中指定了SpringMVC配置文件的路径。servlet-mapping中的url-pattern,这个"/"表示所有的请求都有走这个servlet。所有的SpringMVC的玩法都基于DispatcherServlet。
2.spring-mvc.xml
<!--启动SpringMVC注解驱动-->
<mvc:annotation-driven/>
<!--扫描包-->
<context:component-scan base-package="com.shy.controller"/>
<!--静态资源过滤-->
<mvc:resources mapping="/statics/**" location="/statics/" />
不做静态资源过滤,DispatcherServlet不认识是啥,会直接拦下扔到一边去。
所以加上静态资源过滤表示,这个文件夹下的东西别动。
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
ViewResolver:视图解析器,理解你View中写的是什么东西,把你写的View和前端页面进行匹配,说白了就是,她把你写的东西转换为页面了。因为她要去找页面。
Adapter:适配器,Handler:控制器
全局异常处理
<!-- 全局异常处理,出现未处理的异常统统会到这里来,error表示页面名字 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >
<property name="exceptionMappings">
<props>
<prop key="java.lang.RuntimeException">error</prop>
</props>
</property>
</bean>
key表示出现什么异常的时候去什么页面,就算出错也不会暴露源代码
数据库配置,和Mybatis的配置就不说了,之前都有发过~
行了,就到这吧~
小知识点
1.@RequestMapping("/allUser")
,用来标记当前方法可以处理什么请求,在类上标记的话是用来分包的。
2.真正的springMVC的方法要做返回值,通常是String类型的,String类型返回的是View对象,写的是String,但是到了DispatcherServlet中会把它包装成一个View对象,这个String的值就是逻辑视图名,所以你返回一个逻辑视图名他就会自动去找这个页面。这是真正常用的用法,想要存放信息,你可以在参数列表中接收一个Model对象,这个Model对象是SpringMVC给你提供的,当它发现方法参数中又Model,View,ModelAndView对象的时候,它会帮助你自动提供,可以直接用,你可以把它当作request用。
@RequestMapping("/allUser")
public String list(Model model){}
3.前端页面可以接收一个键值对,这个键值对并不是从前端页面接收过来的而是跟刚刚的Model是一个效果
@RequestMapping("/allUser")
public String list(Map<String,String> map){}
从页面传过来的东西只有值,没有键,这个键值对也可以当request用,放到map中的东西就会进入request范围内,提示代码的扩展性和移植性可以用这个。
4.真正在实际开发中,后台要传给前端的是json字符串,json对象,这样前端才能解析
5.@ResponseBody
,直接把当前信息返回到页面上,不要做包装,为了让方法返回异步请求,返回json对象
6.@RestController=@ResponseBody+@Controller
7.前端发出的请求是异步的,所以后端做出的也是异步响应
8.controller层不要写业务,只返回json字符串
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
用法:JSON.toJSONString()