一、SpringMVC概述
SpringMVC是Spring中的一个组件,SpringMVC用于web层,相当于controller(等价于传统的servlet和struts的action,或者hendler),用来处理用户请求。 同时,SpringMVC也是一种基于Java的实现MVC设计模式的请求驱动类型的轻量级Web框架,使用了MVC架构模式的思想,将web层进行职责解耦。举个例子,用户在地址栏输入http://网站域名/login,那么springmvc就会拦截到这个请求,并且调用controller层中相应的方法,(中间可能包含验证用户名和密码的业务逻辑,以及查询数据库操作,但这些都不是springmvc的职责),最终把结果返回给用户,并且返回相应的页面(当然也可以只返回json/xml等格式数据)。springmvc就是做前面和后面过程的活,与用户打交道!!springmvc需要有spring的jar包作为支撑才能跑起来,所以需要Spring的基础。
总结: SpringMVC作用相当于servlet,但是简化了servlet的复杂操作,将这部分复杂操作交给框架去实现
二、Springmvc处理流程
在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件
以下组件通常使用框架提供实现:
- DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。 - HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 - Handler:处理器
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。 - HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 - ViewResolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
开发流程:
- 导入相应的依赖
- 在web.xml中配置SpringMVC的核心控制器
- 在web.xml中指定配置文件的路径
- 在springmvc的配置文件中,扫描具有注解的类,同时使springmvc的注解生效
- 在controller类上添加注解@Controller
- 给该类中的方法添加@RequstMapping注解,表示请求地址
三、SpringMVC入门案例(注解版)
第一步:新建项目
在 IDEA 中新建 Spring MVC 项目(IDEA会自动帮我们导入SpringMVC所需要的jar包),并且取名为 【HelloSpringMVC】,点击【Finish】
第二步:修改 web.xml
web.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置SpringMVC前端控制器DispatcherServlet(固定写法) -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- SpringMVC的配置文件所在的位置和名称(默认在src下,如果是maven,则是在resource中) -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--框架进行转发规则的定义文件-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--表示服务器一启动,最优先加载前端控制器DispatcherServlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- SpringMVC拦截所有请求,再进行转发 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置编码级过滤器,防止中午乱码 -->
<!-- 编码级过滤器 -->
<filter>
<filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
把<url-pattern>
元素的值改为 *.form
(或者其他的名字),表示要拦截请求以.form
结尾的请求,并交由Spring MVC的后台控制器来处理,改完之后:
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.form</url-pattern>
</servlet-mapping>
说明:
- 方式一: *.form , 可以访问以.form结尾的地址,由DispatcherServlet进行解析。此方法最简单,不会导致静态资源(jpg,js,css)被拦截。【开发中经常使用】
- 方式二:/ ,所有访问的地址都由DispatcherServlet进行解析,此方法可以实现REST风格的url,很多互联网类型的应用使用这种方式。但是此方法会导致静态文件(jpg,js,css)被拦截后不能正常显示,所以对静态文件的解析需要配置不让DispatcherServlet进行解析。【开发中建议使用】
第三步:创建springmvc.xml文件
在src目录下创建SpringMVC的配置文件springmvc.xml,并加入如下代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置扫描包,告诉SpringMVC被注解的class都在哪个包下 -->
<context:component-scan base-package="com.myw.controller"></context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<!-- 使springmvc的注解生效 -->
<mvc:annotation-driven ></mvc:annotation-driven>
<!--静态资源过滤器 -->
springmvc默认不支持静态资源(html/css/js/img..),所以需要配置静态资源过滤器
<!--
location:"/"要过滤静态资源路径
mapping="/**"表示要过滤哪些内容
-->
<mvc:resources location="/" mapping="/**"></mvc:resources>
<!--
//配置处理器映射器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
//配置处理器映射器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
-->
<!--
直接配置处理器映射器和处理器适配器比较麻烦,可以使用注解驱动annotation-driven来加载
-->
<!--
//配置视图解析器,告诉SpringMVC在网站的哪个目录下能找到jsp文件(现在一般不用jsp,所以这一段一般可以不写)
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
-->
</beans>
第四步:编写接受请求的控制器
相当于编写servlet
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorld {
/**
* 1. 使用RequestMapping注解来映射请求的URL,写在方法上面,一个请求对应一个方法
* 2. 使用ResponseBody注解(Ajax常用此法返回值,如果是其他对象,则返回json对象)
* 当返回值是String,并且方法上方有此注解时,返回的字符串将不在被解析为springmvc的视图(即jsp文件),会被
* 直接以字符串展示在浏览器里
*/
@RequestMapping("helloworld")
@ResponseBody
public String hello(){
System.out.println("hello world");
return "success";
}
}
注:
@Controller
注解:
很明显,这个注解是用来声明控制器的,但实际上这个注解对 Spring MVC 本身的影响并不大。
@RequestMapping
注解:
很显然,这就表示路径 helloworld
会映射到该方法上
第五步:开启tomcat
修改端口号为http的默认端口80(非必须),并且找到安装好的tomcat(Application server)
修改项目的虚拟路径名
第六步:准备前端html页面,通过ajax跳转请求并获取相应的返回结果
$.ajax({
url:"helloworld",
type:"post",
//data:{"username":username,"password":password},
//dataType:"json",
success:function (result){
console.log(result);
}
});
四、 SpringMVC映射规则
二级映射
在类上和方法上同时注解@RequestMapping,相当于地址栏里有两级的地址
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("one")
public class TestController {
@RequestMapping("two")
public String test(){
return "index";
}
}
//如上注解后,映射地址为:http://localhost:8080/xx/one/two
参数绑定的含义
所谓的参数绑定,就是怎么样获取到前台页面传过来的值,通常是跟据参数名(key)来获取值;
//页面端提交请求的程序
$.post("../hc.v",
{
name : "shoji",
price : "8888"
},
function(d) {
alert(d);
}
)
//后台响应上面ajax的post请求的代码
//通过两段代码里“name”和“price”的相同,把“shouji”和“8888”传到hc方法里
//问号传值的方式同样适用 ?name=shoji&price=88882.
...
//这里一般会添加@Responsebody或者在类上添加
@RequestMapping("hc.v")
@Responsebody
public String hc(String name,String price){
return "test";
}
...
映射方法的返回值类型
@ResponseBody注解
- 当返回值是String,并且方法上方有此注解时,返回的字符串将不在被解析为springmvc的视图(即jsp文件),会被直接以字符串展示在浏览器里(如果是map集合或者pojo类这些有键值对的对象时,则转化为json字符串返回)
@RequestMapping("/test")
@ResponseBody
public String hhh() {
return "first";
}
//first将被ajax接收
注:
这个注解如果这个类中,全是返回ajax请求的方法,则直接在类上写@ResponseBody即可,和@Controller可以合并缩写为@RestController
@RestController
public class HelloSpringMVC{
@RequestMapping("/test")
public String hhh() {
return "first";
}
}
五、完整登录实例
这里写一个登录的实例,没有用Mybatis所以没有用Dao层,直接默认用户名为admin,密码为123456
//先写pojo类
package com.myw.pojo;
public class User {
private Integer id;
private String account;
private String password;
public User() {
super();
}
public User(String account, String password) {
this.account = account;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account == null ? null : account.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
}
再写service层(虽然没有Dao层,但是所有的逻辑验证都应该放在service层)
//service的接口
package com.myw.service;
import com.myw.pojo.User;
import java.util.Map;
public interface IUserService {
/**
* 如果匹配成功,则返回user,否则返回null
* @return
*/
public User login(String account, String password);
}
//service的实现类(因为没有用mybatis所以mapper包为空)
package com.myw.service.Impl;
//import com.myw.mapper.UserMapper;
import com.myw.pojo.User;
import com.myw.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements IUserService {
//@Autowired
//private UserMapper um;
@Override
public User login(String account, String password) {
//User user = um.selectByUserName(username);
//如果用户名存在并且密码匹配
if("admin".equals(account) && "123456".equals(password)){
return new User(account,password);
}
return null;
}
}
写web层
package com.myw.web;
import java.util.Map;
import com.myw.pojo.User;
import com.myw.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController //现在jsp有点过时,一般是用ajax,所以直接使用RestController注解
public class UserController {
@Autowired
private IUserService us;
@RequestMapping("login")
public Map<String, Object> login(User user, HttpSession session){
User u = us.login(user.getAccount(),user.getPassword());
Map<String,Object> map = new HashMap<>();
//如果用户成功登录,则将其添加到session中
if(u != null){
session.setAttribute("user",u);
map.put("code",1);
}else{
map.put("code",0);
}
return map;
}
}
前端部分:
//index.html(如果前端页面的名字和请求名一样,则会无法显示页面)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SpringMVCDemo</title>
<script src="js/jquery.js"></script>
</head>
<body>
用户名:<input type="text" id="account"/>
密码:<input type="password" id="password"/>
<input type="button" value="登录" onclick="login()"/>
</body>
<script>
function login(){
let account = $("#account").val();
let password = $("#password").val();
$.ajax({
url:"login",
type:"post",
data:{"account":account,"password":password},
dataType:"json",
success:function (result){
console.log(result);
if(result.code==0){
alert("用户名或密码错误");
//location.href = "register.html";
}else{
alert("登录成功");
}
}
});
}
</script>
</html>