ContextLoaderListener监听器
Spring提供了监听器ContextLoaderListener,实现ServletContextListener接口,
可监听ServletContext的状态,在web服务器的启动,读取Spring的配置文件,
创建Spring的IOC容器。web应用中必须在web.xml中配置。
作用:在服务器启动的时候,加载spring的配置文件,获取spring的ioc容器。
过程:
~通过监听器进行监听服务器的状态,在服务器启动时直接创建springioc的容器,
在springmvc获取ioc容器的时候直接完成自动装配的过程中对controller里面的service自动装配。
原因:
~监听器里面的初始化方法只执行一次,获取ioc容器的时候没必要每次都要获取,
因为创建ioc容器都是通过配置文件创建出来的,通过同一个配置文件创建的ioc没有区别。
~监听器的初始化方法是服务器启动之后第一个执行的方法,比DispatcherServlet初始化时间还早,
就可以通过监听器创建spring的ioc容器,当DispatcherServlet初始化的时候,
就可以完成controller里面的service自动装配。
web.xml
<!-- 监听器 -->
<listener>
<!-- 在服务器启动时加载spring配置文件-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
Spring配置文件默认位置和名称:/WEB-INF/applicationContext.xml
可通过上下文参数自定义Spring配置文件的位置和名称
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:ApplicationContext.xml</param-value>
</context-param>
ssm增删改查分页
引入依赖
<properties>
<!-- 自定义属性,spring版本,统一管理各个依赖的版本 -->
<spring.version>5.3.1</spring.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 事务管理器的类在spring-aspects依赖中 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--mybatis和spring的整合包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- 分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- json格式 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
数据库表
配置web.xml
<!-- 配置spring的编码过滤器 -->
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置处理请求方式的过滤器 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置前端控制器 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 设置springmvc配置文件自定义的位置和名称 -->
<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>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置spring的监听器,在服务器启动时加载spring的配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 设置spring配置spring配置文件自定义的位置和名称 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
springmvc配置文件
<!--扫描控制层组件-->
<context:component-scan base-package="com.atguigu.ssm.controller"></context:component-scan>
<!--配置视图解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
<!-- 配置默认的servlet处理静态资源 -->
<mvc:default-servlet-handler/>
<!-- 开启mvc的注解驱动-->
<mvc:annotation-driven/>
<!-- 配置视图控制器 -->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<mvc:view-controller path="/to/add" view-name="/employee_add"></mvc:view-controller>
<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
<!-- 配置拦截器 有需要在配置 -->
<!-- 异常解析器 有需要在配置 -->
搭建Mybatis环境
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
核心配置文件mybatis-config.xml
<configuration>
<!-- Mybatis核心配置文件中的标签必须要按照指定的顺序配置 -->
<!-- <properties resource="jdbc.properties"></properties>-->
<!-- 设置全局配置 将下划线映射为驼峰 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name=" "/>
</typeAliases>
<!--注释的都让spring管理了-->
<!-- <environments default="development">-->
<!-- <environment id="development">-->
<!-- <transactionManager type="JDBC"/>-->
<!-- <dataSource type="POOLED">-->
<!-- <property name="driver" value="${jdbc.driver}"/>-->
<!-- <property name="url" value="${jdbc.url}"/>-->
<!-- <property name="username" value="${jdbc.username}"/>-->
<!-- <property name="password" value="${jdbc.password}"/>-->
<!-- </dataSource>-->
<!-- </environment>-->
<!-- </environments>-->
<!-- 引入mybatis的映射文件 -->
<!-- <mappers>-->
<!-- <package name=" "/>-->
<!-- </mappers>-->
<!-- 分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
Mapper接口和映射文件
public interface EmployeeMapper {
}
<mapper namespace="com.atguigu.ssm.mapper.EmployeeMapper">
</mapper>
日志文件log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS}
%m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
Spring的配置文件并配置
<!--扫描组件 除控制层-->
<context:component-scan base-package="com.atguigu.ssm">
<!-- 排除控制层扫描 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--引入jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置事务管理器 -->
<!-- 事务管理器在spring-jdbc这个依赖中 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 管理事务,基于连接对象操作的,连接对象交给数据源管理的 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务的注解驱动 将使用的注解@transactionManager标识的方法或类中所有的方法进行事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 整合mybatis -->
<!-- 配置SqlSessionFactoryBean mybatis-spring的依赖提供的 -->
<!-- 可以直接来获取工程bean所提供的的对象 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--
可以通过spring配置核心配置文件也可以在mybatis的核心配置文件配置。
dataSource:数据源 mapperLocations:映射文件路径
configurationProperties:全局配置 plugins:插件
typeAliasesPackage:类型别名所对应的包
-->
<!-- configLocation:设置mybatis核心配置文件路径, -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 设置数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 设置类型别名所对应的包 (实体类别名) -->
<property name="typeAliasesPackage" value="com.atguigu.ssm.pojo"></property>
<!--设置映射文件的路径: 映射文件路径和mapper接口的路径不一致才设置 -->
<!-- <property name="mapperLocations" value="classpath:mappers/*.xml"></property> -->
</bean>
<!--
配置mapper接口的扫描,MapperScannerConfigurer可以把指定的包下面所有的mapper接口
通过SqlSessionFactory所提供的的sqlsession对象来创建mapper接口的代理实现类对象交给ioc管理。
所以直接在service自动装配mapper接口对象就可以
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.atguigu.ssm.mapper"></property>
</bean>
组件
实体类Employee
package com.atguigu.ssm.pojo;
public class Employee {
private Integer empId;
private String empName;
private Integer age;
private String gender;
private String email;
public Employee() {
}
....
}
控制层EmployeeController
@Controller
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
//分页查询
@RequestMapping(value = "/employee/page/{pageNum}",method = RequestMethod.GET)
public String getEmployeePage(@PathVariable("pageNum") Integer pageNum,Model model){
// 调用service层 查询员工数据和处理分页信息
PageInfo<Employee> pageInfo=employeeService.getEmployeePage(pageNum);
// 将分页数据共享到请求域中
model.addAttribute("pageInfo",pageInfo);
// 跳转employee_list.html
return "employee_list";
}
// 查询数据(数据没有分页)
// @RequestMapping(value = "/employee",method = RequestMethod.GET)
// public String getAllEmployee(Model model){
// // 查询所有的员工信息
// List<Employee> list= employeeService.getAllEmployee();
// System.out.println(list);
将员工信息在请求域中共享数据
// model.addAttribute("list",list);
跳转employee_list.html
// return "employee_list";
// }
// 添加数据
@RequestMapping(value = "/employee",method = RequestMethod.POST)
public String AddEmployee(Employee employee){
// 调用service层 把接收浏览器的数据传过去 处理添加数据
employeeService.addEmployee(employee);
// 重定向
return "redirect:/employee/page/1";
}
// 修改查询数据
@RequestMapping(value = "/employee/{empId}")
public String servletupdateEmployee(@PathVariable("empId") Integer empId, Model model){
// 根据id查询所有数据
Employee employee= employeeService.servletupdateEmployee(empId);
// 共享域数据
model.addAttribute("employee",employee);
return "employee_update";
}
// 修改数据
@RequestMapping(value = "/employee",method = RequestMethod.PUT)
public String updateEmployee( Employee employee){
System.out.println("employee="+employee);
employeeService.updateEmployee(employee);
return "redirect:/employee/page/1";
}
// 删除数据
@RequestMapping(value = "/employee/{empId}",method = RequestMethod.DELETE)
public String deleteEmployee(@PathVariable("empId") Integer empId){
employeeService.deleteEmployee(empId);
return "redirect:/employee/page/1";
}
}
service层
public interface EmployeeService {
// 查询所有的员工信息
List<Employee> getAllEmployee();
//获取员工分页的信息
PageInfo<Employee> getEmployeePage(Integer pageNum);
//添加数据
void addEmployee(Employee employee);
//修改之前查询数据
Employee servletupdateEmployee(Integer empId);
//修改数据
void updateEmployee(Employee employee);
//根据id删除数据
void deleteEmployee(Integer empId);
}
@Service
@Transactional
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
@Override
public List<Employee> getAllEmployee() {
// 调用mapper接口 查询所有数据(没有分页)
return employeeMapper.getAllEmployee();
}
@Override
public PageInfo<Employee> getEmployeePage(Integer pageNum) {
// 开启分页功能 当前页的页码(从浏览器传过来的),每页显示的条数
// 本身是拦截器的功能,会对当前查询进行拦截,sql预语句中直接加入limit实现分页功能
PageHelper.startPage(pageNum,4);
// 调用mapper接口 查询所有的员工信息并且分页
List<Employee> list = employeeMapper.getAllEmployee();
// 获取分页相关数据 当前查询的员工集合 , 导航分页 23456,以当前也为原点,显示前面页码和后面页码 (导航分页的页码数)
PageInfo<Employee> page=new PageInfo<>(list,5);
return page;
}
@Override
public void addEmployee(Employee employee) {
// 调用mapper接口 添加数据
employeeMapper.addEmployee(employee);
}
@Override
public Employee servletupdateEmployee(Integer empId) {
// 调用mapper接口 根据id查询数据进行修改
Employee employee= employeeMapper.servletupdateEmployee(empId);
return employee;
}
@Override
public void updateEmployee(Employee employee) {
// 调用mapper接口 修改数据
employeeMapper.updateEmployee(employee);
}
@Override
public void deleteEmployee(Integer empId) {
// 调用mapper接口 删除数据
employeeMapper.deleteEmployee(empId);
}
}
mapper接口和mapper核心配置文件
package com.atguigu.ssm.mapper;
import com.atguigu.ssm.pojo.Employee;
import java.util.List;
public interface EmployeeMapper {
//查询所有员工信息
List<Employee> getAllEmployee();
//添加数据
void addEmployee(Employee employee);
// 修改查询数据
Employee servletupdateEmployee(Integer empId);
//修改数据
void updateEmployee(Employee employee);
//删除数据
void deleteEmployee(Integer empId);
}
<!-- List<Employee> getAllEmployee();-->
<select id="getAllEmployee" resultType="Employee">
select * from t_emp
</select>
<!-- void addEmployee(Employee employee);-->
<insert id="addEmployee">
insert into t_emp values(null,#{empName},#{age},#{gender},#{email})
</insert>
<!--修改 查询数据-->
<!-- servletupdateEmployee-->
<select id="servletupdateEmployee" resultType="Employee">
select * from t_emp where emp_id=#{empId}
</select>
<!-- void updateEmployee(Employee employee, Integer empId); -->
<update id="updateEmployee">
update t_emp set emp_name=#{empName},age=#{age},gender=#{gender},email="email" where emp_id=#{empId};
</update>
<!-- void deleteEmployee(Integer empId);-->
<delete id="deleteEmployee">
delete from t_emp where emp_id=#{empId}
</delete>
浏览器页面
index.html
<h1>index.html</h1>
<!--默认访问第一页-->
<a th:href="@{/employee/page/1}">查询当前员工分页信息</a>
employee_list.html
<head>
<meta charset="UTF-8">
<title>员工列表</title>
<link rel="stylesheet" th:href="@{/static/css/index_work.css}">
</head>
<body>
<div id="app">
<table>
<tr>
<th colspan="6">Employee Info</th>
</tr>
<tr>
<th>emp_id</th>
<th>emp_name</th>
<th>age</th>
<th>sex</th>
<th>email</th>
<th>options</th>
</tr>
<!-- pageInfo.list 共享的是pageInfo对象的list属性表示当前页的数据 -->
<tr th:each="employee: ${pageInfo.list}">
<td th:text="${employee.empId}"></td>
<td th:text="${employee.empName}"></td>
<td th:text="${employee.age}"></td>
<td th:text="${employee.gender}"></td>
<td th:text="${employee.email}"></td>
<td>
<a @click="deleteEmployee" th:href="@{'/employee/'+${employee.empId}}">delete</a>
<a th:href="@{'/employee/'+${employee.empId}}">update</a>
<a th:href="@{/to/add}">add</a>
</td>
</tr>
</table>
<form method="post">
<input type="hidden" name="_method" value="delete">
</form>
<!-- 处理分页 -->
<div style="text-align: center;">
<!-- 如果结果为true,当前这个标识就会显示页面中,false不显示 -->
<!-- hasPreviousPage:是否存在上一页,如果存在,就显示首页和上一页 prePage: 上一页的页码 -->
<a th:if="${pageInfo.hasPreviousPage}" th:href="@{/employee/page/1}">首页</a>
<a th:if="${pageInfo.hasPreviousPage}" th:href="@{'/employee/page/'+${pageInfo.prePage}}">上一页</a>
<!-- 根据数据的条数 序列导航分页的页码 navigatepageNums:导航分页的页码(123...) -->
<span th:each="num : ${pageInfo.navigatepageNums}">
<!-- pageNum:当前页的页码 -->
<a th:if="${pageInfo.pageNum == num}"style="color: red" th:href="@{'/employee/page/'+${num}}" th:text="'['+${num}+']'"></a>
<a th:if="${pageInfo.pageNum != num}" th:href="@{'/employee/page/'+${num}}" th:text="${num}"></a>
</span>
<!-- nextPage:下一页 pages:末页 -->
<a th:if="${pageInfo.hasNextPage}" th:href="@{'/employee/page/'+${pageInfo.nextPage}}">下一页</a>
<a th:if="${pageInfo.hasNextPage}" th:href="@{'/employee/page/'+${pageInfo.pages}}">末页</a>
</div>
</div>
<!--
处理result风格 删除请求必须为delete,但是目前浏览器支持post和get不支持delete,
想要请求可以修改为delete,必须为post请求才能修改
-->
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">
let vue=new Vue({
el:"#app",
date:'',
methods:{
deleteEmployee(){
// 获取form表单
let form=document.querySelectorAll("form")[0];
// 将超链接的href属性值赋值给form表单的action属性
form.action=event.target.href; //event.target 表示当前触发事件的标签
// 表单提交
form.submit();
//阻止超链接的默认行为
event.preventDefault();
}
}
})
</script>
</body>
employee_add.html
<title>add employee</title>
<link rel="stylesheet" th:href="@{/static/css/index_work.css}">
</head>
<body>
<form th:action="@{/employee}" method="post">
<table>
<tr>
<th colspan="2">add employee</th>
</tr>
<td>empName</td>
<td>
<input type="text"name="empName">
</td>
<tr>
<td>email</td>
<td>
<input type="text" name="email">
</td>
</tr>
<tr>
<td>age</td>
<td>
<input type="text" name="age">
</td>
</tr>
</tr>
<tr>
<td>gender</td>
<td>
<input type="radio" name="gender" value="1">male
<input type="radio" name="gender" value="0">female
</td>
</tr>
</tr>
<tr>
<td></td>
<td colspan="2">
<input type="submit" value="add">
</td>
</tr>
</table>
</form>
employee_update.html
<title>update employee</title>
<link rel="stylesheet" th:href="@{/static/css/index_work.css}">
</head>
<body>
<h1>update</h1>
<form th:action="@{/employee}" method="post">
<input type="hidden" name="_method" value="put">
<input type="hidden" name="empId" th:value="${employee.empId}">
<table>
<tr>
<th colspan="2">update employee</th>
</tr>
<tr>
<td>empName</td>
<td>
<input type="text" name="empName" th:value="${employee.empName}">
</td>
</tr>
<tr>
<td>email</td>
<td>
<input type="text" name="email" th:value="${employee.email}">
</td>
</tr>
<tr>
<td>age</td>
<td>
<input type="text" name="age" th:value="${employee.age}">
</td>
</tr>
<tr>
<td>gender</td>
<td>
<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
<input type="radio" name="gender" value="0" th:field="${employee.gender}">female
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="update">
</td>
</tr>
</table>
</form>
</body>