实例用到的框架:springmvc + spring boot + mybatis ,springboot抛弃传统的javaEE项目繁琐的配置,是基于Spring管理对象。
配置文件:相比ssm框架,少了web.xml,springmvc.xml,SqlMapConfig.xml (内部已帮我们配置好了)
application.properties:所有配置集中处理,其实还有中yml方式的配置更简洁(推荐)
mapper文件:没什么好说的,跟ssm一样,位置也一样
项目包结构:
创建spring initializr项目,用maven管理
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.herbert</groupId>
<artifactId>springbootdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springbootdemo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- 核心模块,包括自动配置支持、日志支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- mybatis和jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.36</version>
</dependency>
<!-- 解决Springboot不支持jsp页面,用于jsp编译-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- jsp页面jstl的支持 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- 访问动态页面资源所需的依赖。默认访问/static目录下的静态页面,加了这个之后默认换成/templates目录下 -->
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-thymeleaf</artifactId>-->
<!--</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties:可换成另一种.yml方式的配置,简洁明了。不需要配置其他的,同一位置,替换properties文件即可。
#使用jsp页面时需要配置---原springmvc里的配置信息
#/static放静态资源,/templates放动态资源(.ftl文件,结合freemarker技术生成页面)
#问题1:访问resources文件夹静/动态资源时,是从static或templates下开始,所以这里配置路径不用带/static或/templates
#问题2:Springboot推荐html作为前端页面,默认访问/static目录下的静态资源。若想改成访问/templates下动态资源,需要加thymeleaf依赖坐标
#页面默认前缀目录
spring.mvc.view.prefix=/html/
#响应页面默认后缀
spring.mvc.view.suffix=.html
#数据库
spring.datasource.url=jdbc:mysql://172.16.90.128:3306/organizationframework?characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#实体别名---原sqlmapconfig.xml配置信息
mybatis.typeAliasesPackage=com.herbert.domain
#.mapper文件的扫描---原applicationContext.xml配置信息
mybatis.mapperLocations=classpath:mapper/*.xml
#工程名
server.servlet.context-path=/springbootdemo
#端口号
server.port=8080
#tomcat编码
server.tomcat.uri-encoding=UTF-8
StudentMapper.xml
<?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="com.herbert.dao.StudentMapper" >
<insert id="addStudent" parameterType="student" >
<selectKey resultType="integer" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
<!-- 注意:这里如果不加表后不加属性约束,value后面的值要严格按照表字段顺序插入 -->
insert into t_student(username,password,sno,sex) values (#{username},#{password},#{sno},#{sex})
</insert>
<delete id="delStudent" parameterType="integer">
delete from t_student where id=#{id}
</delete>
<update id="updateStudent" parameterType="integer">
update t_student set sex="女" where id = #{id}
</update>
<select id="queryStudent" parameterType="string" resultType="student">
select * from t_student where sno=#{sno}
</select>
</mapper>
index.html:官方推荐用html展示页面。如果想用jsp,把ssm项目的webapp结构拷过来,把jsp文件按照ssm放置方式放置即可,记得application.properties里边配置默认前后缀。详细参见这位博主:https://blog.youkuaiyun.com/weixin_39885435/article/details/82556112,如若jsp访问方式有静态资源,此时静态资源直接放webapp目录下。
下图为jsp位置实例图:(注意:webapp不要放在resources目录下了!!!)
下方为本次实例index.html页面部分:
在这里我遇到一个比较坑的问题,就是如果method=”post“,会报405的错误。查了很久,网上很多人都说springboot表单不能写post请求,需要换成get,换了之后却是成功了。但是不理解原因,而且get请求不是会把提交信息暴露在链接吗?而且当请求比较长的时候怎么处理?(get有请求长度的限制)
另外就是我忽视了一个点,最开始action里面是用${pageContext.request.contextPath}表示项目路径,殊不知页面已经是html了,这种EL表达式的方式是jsp才有的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<!-- 导入jquery核心类库 -->
<script type="text/javascript" src="../js/jquery-1.8.3.js"></script>
<script type="text/javascript">
// document.getElementById("myForm").submit();
</script>
</head>
<Body>
<form id="myForm" method="get" action="/springbootdemo/student/addStudent">
学号:<input type="text" name="sno" placeholder="请输入学号:">
姓名:<input type="text" name="username" placeholder="请输入姓名:">
密码:<input type="password" name="password">
性别:<input type="radio" name="sex" value="男" checked="checked">男
<input type="radio" name="sex" value="女">女
<input type="submit" value="提交">
</form>
</body>
</html>
接下来展示实例代码:
Student.java
package com.herbert.domain;
/**
* @author Herbert
* @create 2019-06-09 15:12
*/
public class Student {
private Integer id;
private String username;
private String password;
private String sno;
private String sex;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", sno='" + sno + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
controller层:这里注意@controller和@restController的区别
package com.herbert.controller;
import com.herbert.domain.Student;
import com.herbert.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @author Herbert
* @create 2019-06-13 10:13
*/
@Controller
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
/*
注意:访问页面的时候就不能在类上面写@restcontroller,会将"index"当成json数据返回界面的,不会跳转静态页面资源。
因为@restcontroller默认将return后的数据转成json格式返回给请求路径。
@restcontroller = @controller + @responsebody,推荐以后分开写,需要返回json时在方法上加@responsebody
*/
@RequestMapping("/showIndex")
public String showIndex(){
return "index";
}
@RequestMapping(value="/addStudent")
public String addStudent(Student student) {
Integer studentId = studentService.addStudent(student);
System.out.println(studentId);
return "success";
}
@RequestMapping("/delStudent")
public String delStudent(Integer id){
studentService.delStudent(id);
return "success";
}
@RequestMapping("/updateStudent")
public String updateStudent(Integer id) {
studentService.updateStudent(id);
return "success";
}
@RequestMapping("/queryStudent")
@ResponseBody
public Student queryStudent(String sno){ //如果前后形参不一致,括号里可以加@RequestParam(value = "xxxx",required = false)
Student student=studentService.queryStudent(sno);
return student;
}
}
service层:这里注意配置事务在两个地方加注解就行,Spring boot启动类(@EnableTransactionManagement)+serviceimpl类(@transactional)
package com.herbert.service;
import com.herbert.domain.Student;
/**
* @author Herbert
* @create 2019-06-13 10:39
*/
public interface StudentService {
Integer addStudent(Student student);
void delStudent(Integer id);
void updateStudent(Integer id);
Student queryStudent(String sno);
}
package com.herbert.service;
import com.herbert.dao.StudentMapper;
import com.herbert.domain.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Herbert
* @create 2019-06-13 10:39
*/
@Service
@Transactional
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Override
public Integer addStudent(Student student) {
return studentMapper.addStudent(student);
}
@Override
public void delStudent(Integer id) {
studentMapper.delStudent(id);
}
@Override
public void updateStudent(Integer id) {
studentMapper.updateStudent(id);
}
@Override
public Student queryStudent(String sno) {
return studentMapper.queryStudent(sno);
}
}
dao层:mapper接口的扫描是在启动类上加@MapperScan注释,.mapper文件的扫描是在application.properties里配置
package com.herbert.dao;
import com.herbert.domain.Student;
/**
* @author Herbert
* @create 2019-06-13 11:15
*/
public interface StudentMapper {
Integer addStudent(Student student);
void delStudent(Integer id);
void updateStudent(Integer id);
Student queryStudent(String sno);
}
启动类-SpringbootdemoApplication.java
package com.herbert;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* 注意:该启动类默认就是在这个包下,把启动类移到其他包下会报错
*/
@SpringBootApplication
@MapperScan("com.herbert.dao") //扫描整个mapper包,将mapper代理类注入ioc管理,不用那么麻烦到每个接口去加@Mapper注解
@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
junit test:这里区别一下Spring junit test的注解,不要记忆混淆。
这里也遇到一个比较坑的问题,选用官方spring initializr项目模版生成的项目会将启动类和测试类分别放到一个包里,包名为groupID+项目名,这样会导致测试类@autowire service层和dao层的对象(studentService)时,报错说找不到这样的bean。直接上图对比:
原生结构:
本次项目结构:
解决办法:将启动类和测试移到上级一com.herbert目录。注意启动类和测试类结构一定要对应,不然也会报错。具体原因,不是很懂,大体意思让启动类和测试类在代码的上一级结构即可。
package com.herbert;
import com.herbert.domain.Student;
import com.herbert.service.StudentService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootdemoApplicationTests {
@Autowired
private StudentService studentService;
@Test
public void contextLoads() {
Student s = new Student();
s.setUsername("rose");
s.setPassword("jack");
s.setSno("1314520");
s.setSex("female");
Integer studentId = studentService.addStudent(s);
System.out.println("学生ID:"+studentId);
}
}
访问路径:项目名 + 类上@RequestMapping路径(类上可以不加这个注解,这里类似struts框架的@namespace功能)+ 方法上@RequestMapping路径(即value值,依然可以不带.action)
直接访问静态资源:
这里要提一点,springboot的resources下有两个很重要的文件夹,static和templates。
static:存放静态资源,例如图片,js,css各种样式以及html等静态资源。
templates:存放静态资源,例如.ftl(freemarker)等动态资源。
这里主要是为了说路径,上面的静态资源访问路径是localhost:8080/springbootdemo/html/index.html,没有带static目录,说明项目默认访问路径是static,所以以后写路径记得不要带static,这个路径我尝试了很久才访问成功。如果想换成templates为默认资源路径,需要在pom文件里添加一个thymeleaf的依赖,具体坐标上面pom文件也有,被注释掉了。
springboot默认访问路径和端口参见:https://blog.youkuaiyun.com/qq_40087415/article/details/82497668
最后附上实例代码:https://github.com/herbert0601/springbootdemo.git,需要的自行下载。