前后端交互主要目的
个人觉得,前后端交互的目的无非就是为了实现视图和业务逻辑的转换,前端发出请求,后端根据前端请求进行相应的数据处理然后给出不同响应
先以Servlet为基础的Model工作流程了解一下前后端交互流程
主要实现
前后端交互有许多种实现方式,但都是围绕MVC编程模式来实现的,MVC编程模式。MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式:
- Model(模型)表示应用程序核心(比如数据库记录列表)
- View(视图)显示数据(数据库记录)
- Controller(控制器)处理输入(写入数据库记录)
针对MVC编程又产生了一些框架中最出名即SpringMVC了,SpringMVC是基于Model2实现的技术框架;Spring MVC 分离了控制器、模型对象、过滤器以及处理程序对象的角色,这种分离让它们更容易进行定制。在SpringMVC中,Action不叫Actin,而被成为Controller;Controller接受参数request和response,并返回ModelAndView。这就是MVC编程的核心流程,通过对response和request对象进行一些操作再返回ModelAndView不正是V和M的转换吗。
而SpringMVC的实现步骤分为6步:
- 客户端向Spring容器发起HTTP请求。
- 发起的请求被前端控制器(DispatcherServlet)所拦截,前端控制器回去寻找恰当的映射处理器来完成这次请求。
- DispatcherServlet根据处理器映射(HandlerMapping)来选择并决定将请求发送给哪一个控制器。
- 选定控制器之后,Dispatcher便将请求发送给它,在控制器中处理所发送的请求,并以ModelAndView(属性值和返回的页面)的形式返回给前端控制器DispatcherServlet,典型情况下是以JSP页面的形式返回。
- 返回给前端控制器的未必都是JSP页面,可能仅仅是一个逻辑视图名,通过逻辑视图名可以查找到实际的视图。前端控制器正式通过查询viewResolver对象将从控制器返回的逻辑视图名解析为一个具体的试图实现。
- 如果前端控制器找到相应的视图,则将视图返回给客户端,否则就抛出异常。
以上是SpringMVC的实现基本实现原理,下面我们通过一个SSM框架的简单登录小实例来了解一下实现过程
基本配置文档spring-mybatis.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置c3p0数据池作为数据源 -->
<bean id="data" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/zyhs?useUnicode=true&characterEncoding=UTF-8&useSSL=false"/>
<!--连接池中保留的最小连接数。-->
<property name="minPoolSize" value="3"/>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="30"/>
<!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="5"/>
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="60"/>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="3"/>
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
<property name="maxStatements" value="0"/>
<!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="10"/>
<!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts" value="30"/>
<!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
<property name="breakAfterAcquireFailure" value="false"/>
<!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
等方法来提升连接测试的性能。Default: false -->
<property name="testConnectionOnCheckout" value="false"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 加载mybatis的核心配置文件 -->
<!--<property name="configLocation" value="classpath:mybatis/sqlMapConfig.xml"/>-->
<!-- 数据源 -->
<property name="dataSource" ref="data"/>
</bean>
<!-- 配置mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="zhumeng.zyhs.ssm.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!-- 开启包扫描(扫描生成bean的注解) -->
<context:component-scan base-package="zhumeng.zyhs"/>
<!--使用mvc:annotation-driven标签替代上两个标签配置
另外该标签会加载很多参数绑定的方法:例如json解析转换器
实际开发使用该标签-->
<mvc:annotation-driven conversion-service="conversionService" validator="validator"/>
<!-- 视图解析器 -->
<!--<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
<!--<!–<property name="prefix" value="/WEB-INF"/>–>-->
<!--<!– 后缀-->
<!--<property name="suffix" value=".jsp"></property>–>-->
<!--</bean>-->
</beans>
spring-transaction.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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 事务管理器配置 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="data"/>
</bean>
<!-- 开启事务的注解配置,使用@Transactional声明 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<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:spring/spring-*.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</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>
数据持久化类Manager.java
package zhumeng.zyhs.ssm.pojo;
public class Manager {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
ManagerMapper.java
package zhumeng.zyhs.ssm.mapper;
import org.springframework.stereotype.Repository;
import zhumeng.zyhs.ssm.pojo.Manager;
@Repository
public interface ManagerMapper {
Manager selectByUserName(String name);
}
ManagerMapper.xml sql映射文件,文件中配置了操作数据库的sql语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="zhumeng.zyhs.ssm.mapper.ManagerMapper">
<resultMap id="BaseResultMap" type="zhumeng.zyhs.ssm.pojo.Manager">
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="password" property="password" jdbcType="VARCHAR"/>
</resultMap>
//数据库匹配
<select id="selectByUserName" resultMap="BaseResultMap" parameterType="java.lang.String">
select *
from manager
where name = #{name ,jdbcType=INTEGER}
</select>
</mapper>
ManagerMapper.java和ManagerMapper.xml文件都都要放在前面所配置的mapper扫描器所设置扫描的包下,不然会抛出异常找不到该注解。
现在写项目的服务层
ManagerService.java
package zhumeng.zyhs.web.service;
import zhumeng.zyhs.ssm.pojo.Manager;
public interface ManagerService {
boolean login(Manager vo);
}
boolean login(Manager vo);
}
服务实现层
UserServiceImpl.java
package zhumeng.zyhs.web.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import zhumeng.zyhs.ssm.mapper.ManagerMapper;
import zhumeng.zyhs.ssm.pojo.Manager;
import zhumeng.zyhs.web.service.ManagerService;
@Service
public class ManagerServiceImp implements ManagerService {
private final ManagerMapper mapper;
@Autowired //调用注解自动注入
public ManagerServiceImp(ManagerMapper mapper) {
this.mapper = mapper;
}
@Override //验证用户名密码 也可在ManagerMapper写在接口中验证
public boolean login(Manager vo) {
Manager manager = mapper.selectByUserName(vo.getName());
if (manager == null) return false;
else if (manager.getPassword().equals(vo.getPassword())) {
vo.setId(manager.getId());
return true;
} else return false;
}
private final ManagerMapper mapper;
@Autowired //调用注解自动注入
public ManagerServiceImp(ManagerMapper mapper) {
this.mapper = mapper;
}
@Override //验证用户名密码 也可在ManagerMapper写在接口中验证
public boolean login(Manager vo) {
Manager manager = mapper.selectByUserName(vo.getName());
if (manager == null) return false;
else if (manager.getPassword().equals(vo.getPassword())) {
vo.setId(manager.getId());
return true;
} else return false;
}
}
最后编写相应的控制器ManagerController.java
package zhumeng.zyhs.web.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import zhumeng.zyhs.ssm.pojo.Manager;
import zhumeng.zyhs.web.service.ManagerService;
@Controller
public class ManagerSetController {
private final ManagerService managerService;
@Autowired
public ManagerSetController(ManagerService managerService) {
this.managerService = managerService;
}
@RequestMapping(value = "/login") //登录
public String skipPacks(Model model, HttpServletRequest request) {
String error = request.getParameter("error");
if (error != null) {
model.addAttribute("error", "账号密码错误");
}
return "/backer/login.jsp";
}
@RequestMapping(value = "/submitLogin") //提交登录
public String submitLogin(Model model, Manager vo, HttpServletRequest request) {
if (managerService.login(vo)) {
request.getSession().setAttribute("manager", vo); //如果登陆成功则把用户信息放入session中 后面可以用来检测用户是否登录
return "success.jsp"; //可直接返回方法、action、和jsp文件
} else {
return "redirect:login.action?error=" + 1; //如果验证不成功则继续返回登录界面
}
}
}
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>登录</title>
</head>
<body>
<form id="login" action="submitLogin.action" method="post">
<div id="input">
<br/>
<h3>登录测试</h3>
<hr/>
<img src="static/icon/user.png" height="40px"/>
<input name="name" type="text" placeholder="用户名" required><br/><br/> //控制器中用的反射 所以这里属性名必须与持久化类中一样一样
<img src="static/icon/password.png" height="40px"/>
<input name="password" type="password" placeholder="密码" required><br/><br/>
</div>
<button type="submit" id="submit">登录</button>
</form>
<br/><br/>
</div>
</body>
<c:if test="${not empty error }">
<script type="text/javascript">
alert('${error}') //调用后端传过来的值 通过键值对的方式
</script>
</c:if>
<script src="http://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>登录成功</title>
</head>
<body>
<h3>登录成功</h3>
</body>
</html>
这只是一个简单的登录测试,其余功能大家可以自由发挥
代码中的Model类是用来给前端JSP传递数据来生成前端html页面数据用的, 实为数据库在代码中的一种映射, 用来承载 如同数据表结构一般信息的存在,且用其在程序中传输 也就是说后端把值传到jsp页面可以用Model类
运行项目后登录流程为