目录
使用SpringMVC技术开发web程序流程
1.创建web工程 (Maven结构)
2.设置tomcat服务器,加载web工程(tomcat插件)
3.导入坐标(SpringMVC+Servlet)
4.定义处理请求的功能类(UserController)
5.设置请求映射(配置映射关系)
6.将SpringMVC设定加载到Tomcat容器中
SpringMVC入门案例
1.导maven坐标
<packaging>war</packaging>
<dependencies>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
<!-- TomCat插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
2.创建配置类
@Configuration
@ComponentScan("com.YuZai.controller")
public class SpringmvcConfig {
}
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() { //SpringMVC对应的容器对象
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringmvcConfig.class);
return context;
}
@Override
protected String[] getServletMappings() { //请求是由Tomcat处理还是springMVC处理
return new String[]{"/"}; //所有请求都归SpringMVC处理
}
@Override
protected WebApplicationContext createRootApplicationContext() { //spring配置的对应的容器的对象
return null;
}
}
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化web3.0容器的抽象类
AbstractDispatcherServletInitializer提供三个接口方法供用户实现
- protected WebApplicationContext createServletApplicationContext()创建Servlet容器,加载SpringMVC对应的bean放入WebApplicationContext对象范围。
- protected String[] getServletMappings() 设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理。
- protected WebApplicationContext createRootApplicationContext() 如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行。
可以使用它的子类简化开发
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
3.创建控制器
@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody //将Java对象转换为json格式返回
public String save(){
System.out.println("save() Running....");
return "{鱼仔,18,睡觉}";
}
}
SpringMVC执行流程
《启动服务器初始化过程》
1. 初始化web容器
2. 执行getServletConfigClasses()方法,创建WebApplicationContext对象
3. 加载SpringMVC配置文件
4. 加载Spring容器中的Bean
5. 加载Controller
6. 执行getServletMappings()方法,所有请求都将通过SpringMVC
《单次请求过程》
1. 发送请求 http://localhost/book
2. web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
3. 解析请求路径 /book
4. 由 /book配置执行对应的方法 selectAll()
5. 执行selectAll()
6. 检测到有@ResponseBody直接将selectAll()方法的返回值作为响应体返回给请求方
controller控制器
请求映射路径
@RequestMapping
参数传递
GET方式传参
请求路径: http://localhost:80/user/save?name=FangYiZe&pw=666999
Controller控制层代码:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
@ResponseBody //将Java对象转换为json格式返回
public String save(String name, String pw){
System.out.println("save() Running......");
System.out.println("name"+"=>>>"+name);
System.out.println("pw"+"=>>>"+pw);
return "{鱼仔,18,睡觉}";
}
}
运行结果:
save() Running......
name=>>>FangYiZe
pw=>>>666999
POST方式传参
请求路径: http://localhost:80/user/add
PostMan x-www-form-urlencoded Body数据
KEY VALUE
username FangYiZe
password 666999
sex man
Controller控制层代码:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/add",method = RequestMethod.POST)
@ResponseBody
public String add( String username,String password,String sex){
System.out.println("add() Running......");
System.out.println("username"+"=>>>"+username);
System.out.println("password"+"=>>>"+password);
System.out.println("sex"+"=>>>"+sex);
return "add() Running...";
}
}
运行结果:
add() Running......
username=>>>FangYiZe
password=>>>666999
sex=>>>man
POST请求中文乱码处理
POST请求中文乱码处理
为web容器添加过滤器并指定字符集
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
@RequestParam 获取URL参数
传实体类参数
请求URL: http://localhost:80/user/test?age=18&username=喜羊羊&hobby=提出方法&book.bookName=喜羊羊的一生&book.price=99.87
controller控制层代码:
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@RequestMapping(value = "/test",method = RequestMethod.GET)
public void test( User user){
System.out.println("test() Running...");
System.out.println("user=>>>>>"+user);
}
}
传List集合参数
例: POST请求
请求URL: http://localhost:80/user/test02
请求体:
KEY VALUE
likes a666
likes b666
likes c666
controller控制层代码:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/test02",method = RequestMethod.POST)
public void test02(@RequestParam("likes") List<String> listStr){
System.out.println("test02() Running...");
System.out.println("listStr=>>>>>"+listStr);
}
}
运行结果:
test02() Running...
listStr=>>>>>[a666, b666, c666]
[WARNING] No mapping for POST /user/user/test02
@EnableWebMvc
作用: 开启SpringMVC多项辅助功能。
@EnableWebMvc功能之一: 根据类型匹配对应的类型转换器。
参数传递(Json)
导JSON坐标:
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
开启@EnableWebMvc注解
@Configuration
@ComponentScan("com.YuZai.controller")
@EnableWebMvc
public class SpringmvcConfig {
}
Controller控制层代码:
@Controller
@RequestMapping("/user")
public class UserController {
//参数传递(JSON)
@RequestMapping("/test03")
public void test03(@RequestBody List<String> listStr){
System.out.println("test03() Running...");
System.out.println("listStr=>>>>>"+listStr);
}
}
请求URL: http://localhost:80/user/test03
请求体List JSON格式: ["aaa","bbb","ccc"]
运行结果:
test03() Running...
listStr=>>>>>[aaa, bbb, ccc]
POJO实体类的JSON数据
请求URL: http://localhost:80/user/test04
请求体: 嵌套POJO JSON格式
{
"username" : "Jack" ,
"age" : 19,
"hobby" : "唱,跳,rap,篮球",
"book" :{
"bookName" : "投篮的技巧",
"price" : 100.89
}
}
Controller控制层代码:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/test04")
public void test04(@RequestBody User user){
System.out.println("test04() Running...");
System.out.println("user=>>>>>"+user);
}
}
运行结果
test04() Running...
user=>>>>>User{username='Jack', age=19, hobby='唱,跳,rap,篮球', book=Book{bookName='投篮的技巧', price=100.89}}
List<POJO> JSON格式
请求URL: http://localhost:80/user/test05
请求体: [{"username":"迪迦超人","age":19,"hobby":"拯救人类","book":{"bookName":"迪迦在地球的一百万年","price":99.99}},{"username":"青蛙王子","age":16}]
Controller层代码:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/test05")
public void test05(@RequestBody List<User> listObj){
System.out.println("test05() Running...");
System.out.println("listObj=>>>>>"+listObj);
}
}
运行结果:
test05() Running...
listObj=>>>>>[User{username='迪迦超人', age=19, hobby='拯救人类', book=Book{bookName='迪迦在地球的一百万年', price=99.99}}, User{username='青蛙王子', age=16, hobby='null', book=null}]
@RequestBody
参数传递(日期类型)
@DateTimeFormat 指定日期格式
请求URL: http://localhost:80/user/test06?date1=2022/02/02&date2=2022-01-01&date3=2022/03/14 11:35:59
Controller控制成代码:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/test06")
public void test06(Date date1, @DateTimeFormat(pattern = "yyyy-MM-dd") Date date2 , @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") Date date3 ){
System.out.println("test06() Running..." );
System.out.println( "Data1=>>"+ date1 );
System.out.println( "Data2=>>"+ date2 );
System.out.println( "Data3=>>"+ date3 );
}
}
运行结果:
test06() Running...
Data1=>>Wed Feb 02 00:00:00 CST 2022
Data2=>>Sat Jan 01 00:00:00 CST 2022
Data3=>>Mon Mar 14 11:35:59 CST 2022
请求与响应
响应页面
响应数据
文本数据
json数据
响应页面、跳转页面 (直接返回页面名称)
@RequestMapping(value = "/test001",method = RequestMethod.GET)
public String test001(){
System.out.println("test001() Running...");
return "index.jsp";
}
报错:[WARNING] No mapping for GET /pages/books.html
原因: 被SpringMVC拦截了所有请求
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; //这就是问题点
}
}
解决方法: 不走SpringMVC走TomCat (设置对静态资源的访问放行)
@Configuration
public class SpringmvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) { //作用:添加资源的过滤
//当访问 /pages/资源名 时,不通过springMVC,直接访问 /pages/ 下的资源。
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
//同理
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
响应纯文本数据
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/test002",method = RequestMethod.GET)
@ResponseBody
public String test002(){
System.out.println("test002() Running...");
return "response text";
}
}
响应POJO对象
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/test003")
@ResponseBody
public User test003(){
User user = new User();
user.setUsername("小小怪");
user.setHobby("挨揍");
return user;
}
}
响应POJO集合
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/test004")
@ResponseBody
public List<User> test004(){
User user1 = new User();
User user2 = new User();
user1.setUsername("翼龙");
user2.setUsername("霸王龙");
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
return users;
}
}
REST风格
REST(Representational State Transfer) 表现形式状态转换
传统风格资源描述形式
http://localhost/user/getById?id=1
http://locahlost/user/saveUser
REST风格描述形式
http://localhost/user/1
http://localhost/user
优点:
隐藏资源的访问路径,无法通过地址得知对资源是可何种操作
简化书写
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
http://localhost/users 查询全部用户信息 GET (查询)
http://lcoalhost/users/1 查询指定用户信息 GET (查询)
http://localhost/users 添加用户信息 POST (新增/保存)
http://localhost/users 修改用户信息 PUT (修改/更新)
http://localhost/users/1 删除用户信息 DELETE (删除)
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范。
描述模块的名称通常是用复数,也就加s的格式描述,表示此类资源,而非单个资源,例如: users、books...
根据REST风格对资源进行访问称为RESTful。
REST 案例
案例 GET带参
URL: http://localhost:80/user/9999
Controller:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public void testA(@PathVariable int id){
System.out.println("testA Running... ");
System.out.println("id =>>> " +id );
}
}
运行结果:
testA Running...
id =>>> 9999
-----------------------------------------------------------------
案例 POST请求 带请求体 ({"username":"大鲨鱼","age":998,"hobby":"大吃特吃" })
URL: http://localhost:80/user
Controller:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(method = RequestMethod.POST)
public void testC(@RequestBody User user){
System.out.println("执行添加操作,增加" +user);
}
}
运行结果:
执行添加操作,增加User{username='大鲨鱼', age=998, hobby='大吃特吃', book=null}
-----------------------------------------------------------------
案例 PUT请求 带请求体 ({"username":"大大怪","age":112,"hobby":"敢作敢当" })
URL: http://localhost:80/user
Controller:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(method = RequestMethod.PUT)
public void testE(@RequestBody User user){
System.out.println("执行修改操作,修改"+user);
}
}
运行结果:
执行修改操作,修改User{username='大大怪', age=112, hobby='敢作敢当', book=null}
接收参数的三种方式
至此,接收参数的方式一共有三种,分别是: @RequestBody、@RequestParam、@PathVariable,第一种是接收请求体参数,第二种是接收URL传参例: http://localhost:80/user/test?age=18&username=喜羊羊
, 第三种是接收路径参数例: http://localhost:80/user/1
。
RESTful快速开发
@RestController
@RequestMapping("/user002")
public class User002Controller {
@GetMapping("/{id}") //测试URL:http://localhost:80/user002/1
public User selectById(@PathVariable int id ){
User user = new User();
user.setUsername("暴龙");
return user;
}
@PostMapping //测试URL:http://localhost:80/user002 请求体: {"username":"暴龙战士","hobby":"炫饭"}
public void addUser(@RequestBody User user){
System.out.println( "添加新成员=>>>"+user );
}
@DeleteMapping //测试URL:http://localhost:80/user002?id=9
public void deleteUser(@RequestParam int id){
System.out.println("删除成员=>>>"+id);
}
@PutMapping("/{id}") //测试URL:http://localhost:80/user002/6
public void updateUser(@RequestBody User user,@PathVariable int id){
System.out.println( "修改id为"+id+"的成员=>>>"+user );
}
}
静态资源访问放行
记得将此配置类加载到SpringMVC容器中
@Configuration
public class SpringmvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) { //作用:添加资源的过滤
//当访问 /pages/资源名 时,不通过springMVC,直接访问 /pages/ 下的资源。
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
//同理
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
SSM整合
SSM整合流程
1.创建工程
2.SSM整合
Spring
SpringConfig
MyBatis
MybatisConfig
JdbcConfig
jdbc.properties
SpringMVC
ServletConfig
SpringMvcConfig
3.功能模块
表与实体类
dao(接口+自动代理)
service(接口+实现类)
业务层接口测试(整合JUnit)
controller
表现层接口测试(PostMan)
整合配置
<packaging>war</packaging>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- Spring整合jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- Spring整合Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- MyBatis-Spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</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.9.0</version>
</dependency>
</dependencies>
<!-- TomCat插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
// 此类是Spring配置类。
@Configuration
@ComponentScan({"com.YuZai.service"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MyBatisConfig.class})
@EnableTransactionManagement //开启事务
public class SpringConfig {
}
resources/jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/SpringMVCSSM_db
jdbc.username=root
jdbc.password=0000000
com/YuZai/config/JdbcConfig.java
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean //将返回值加载到Spring的IOC容器
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
//事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
//Spring整合MyBatis
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ //dataSource由Spring自动装配
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.YuZai.domain");
return factoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.YuZai.dao");
return msc;
}
}
com/YuZai/config/ServletConfig.java
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
com/YuZai/config/SpringMvcConfig.java
@Configuration
@ComponentScan("com.YuZai.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
功能模块开发
domain
public class Book {
private Integer id ;
private String type;
private String bookName;
private String description;
构造器方法...
getter,setter方法....
toString....
}
dao
// 增删改查
public interface BookDao {
//利用mybatis的自动代理写实现
@Select("SELECT * FROM tab_book WHERE id=#{id}") // #{方法的形参名}
public Book selectById(Integer id);
@Select("SELECT * FROM tab_book ")
public List<Book> selectAll();
@Insert("INSERT tab_book( TYPE,book_name,description ) VALUES ( #{type},#{bookName},#{description} ); ") //#{POJO的属性}
public void addBook(Book book);
@Update("UPDATE tab_book SET TYPE=#{type},book_name=#{bookName},description=#{description} WHERE id=#{id} ; ") //#{POJO的属性}
public void updateBook(Book book);
@Delete("DELETE FROM tab_book WHERE id=#{id} ;") //#{方法的形参名}
public void deleteBook(Integer id);
}
service
// 业务层接口
@Transactional
public interface BookService {
public Book selectById(Integer id);
public List<Book> selectAll();
public boolean addBook(Book book);
public boolean updateBook(Book book);
public boolean deleteBook(Integer id);
}
service.impl
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public Book selectById(Integer id) {
Book book = bookDao.selectById(id);
return book;
}
@Override
public List<Book> selectAll() {
List<Book> books = bookDao.selectAll();
return books;
}
@Override
public boolean addBook(Book book) {
bookDao.addBook(book);
return true;
}
@Override
public boolean updateBook(Book book) {
bookDao.updateBook(book);
return true;
}
@Override
public boolean deleteBook(Integer id) {
bookDao.deleteBook(id);
return true;
}
}
controller
package com.YuZai.controller;
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookServiceImpl bookService;
@GetMapping
public List<Book> selectAll(){ // 测试URL: http://localhost:80/books
List<Book> books = bookService.selectAll();
return books;
}
@GetMapping("/{id}")
public Book selectById(@PathVariable Integer id){
Book book = bookService.selectById(id);
return book;
}
@PostMapping // 测试URL: http://localhost:80/books post 请求体:{"type":"动漫类","bookName":"迪迦爱学习","description":"超励志"}
public boolean addBook(@RequestBody Book book){
boolean b = bookService.addBook(book);
return b;
}
@PutMapping // 测试URL:http://localhost:80/books Put 请求体:{"type":"动漫类","bookName":"打大怪将军","description":"超腻害","id":"4"}
public boolean updateBook(@RequestBody Book book){
boolean b = bookService.updateBook(book);
System.out.println(book);
return b;
}
@DeleteMapping("/{id}") //测试URL:http://localhost:80/books/5 DELETE
public boolean deleteBook(@PathVariable Integer id){
boolean b = bookService.deleteBook(id);
return b;
}
}
接口测试
//Spring整合Junit
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
@Autowired
private BookServiceImpl bookService;
@Test
public void TestSelectById(){
Book book = bookService.selectById(1);
System.out.println(book);
}
@Test
public void TestSelectAll(){
List<Book> books = bookService.selectAll();
System.out.println(books);
}
}
表现层数据封装
后端统一数据模型传给前端的。
设置统一数据返回结果类:
public class Result{
private Object data; //封装数据
private Integer code; //状态码
private String message; //消息
}
需要注意的是: 这就是前后端通讯的协议,没有固定格式,Result类中的字段并不固定,根据需要增减,提供构造方法。
public class Code {
public static final Integer SELECT_OK = 20011;
public static final Integer ADD_OK = 20021;
public static final Integer DELETE_OK = 20031;
public static final Integer UPDATE_OK = 20041;
public static final Integer SELECT_ERR = 20010;
public static final Integer ADD_ERR = 20020;
public static final Integer DELETE_ERR= 20030;
public static final Integer UPDATE_ERR = 20040;
}
//表现层
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookServiceImpl bookService;
@GetMapping
public Result selectAll(){ // 测试URL: http://localhost:80/books GET
List<Book> books = bookService.selectAll();
System.out.println(books);
Integer code = books.size() != 0 ? Code.SELECT_OK : Code.SELECT_ERR;
String msg = books.size() != 0 ? "" : "查询数据失败,请重试!";
return new Result(books,code,msg);
}
@GetMapping("/{id}")
public Result selectById(@PathVariable Integer id){ // 测试URL: http://localhost:80/books/1 GET
Book book = bookService.selectById(id);
Integer code = book != null ? Code.SELECT_OK : Code.SELECT_ERR;
String msg = book != null ? "" : "查询数据失败,请重试!";
return new Result(book,code,msg);
}
@PostMapping // 测试URL: http://localhost:80/books post 请求体:{"type":"动漫类","bookName":"迪迦爱学习","description":"超励志"}
public Result addBook(@RequestBody Book book){
boolean b = bookService.addBook(book);
return new Result(b,b?Code.ADD_OK:Code.ADD_ERR);
}
}
异常处理
程序开发中不可避免会遇到异常。
程序正常运行时理应返回统一格式的Result封装数据。
但若是出现异常返回给前端的就是一堆html代码。
异常得分类处理:
出现异常现象的常见位置与常见诱因如下:
框架内部抛出的异常 :因使用不合规导致
数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
表现层抛出的异常: 因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
工具类拋出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
解决思想:
数据层、业务层、表现层均出现异常,异常处理代码书写在哪一层?
解:全部往上抛,抛到表现层进行处理。
表现层每个方法都得进行异常处理,代码书写量巨大,如何解决?
解:AOP思想。
以上 异常分类处理、统一表现层处理、AOP处理的操作,SpringMVC已经做了封装。
----- 异常处理器!
异常处理器
集中的、统一的处理项目中出现的异常。
package com.YuZai.controller;
//异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
return new Result(null,null,"后台出错了");
}
}
项目异常分类
业务异常(BusinessException)
规范的用户行为产生的异常
不规范的用户行为操作产生的异常
系统异常(SystemException)
项目运行过程中可预计且无法避免的异常
其他异常(Exception)
编程人员未预期到的异常
项目异常处理方案
业务异常(BusinessException)
◆发送对应消息传递给用户, 提醒规范操作
系统异常(SystemException)
◆发送固定消息传递给用户,安抚用户.
◆发送特定消息给运维人员,提醒维护
◆记录日志
其他异常(Exception)
◆发送固定消息传递给用户,安抚用户
◆发送特定消息给编程人员,提醒维护(纳入预期范围内)
◆记录日志
自定义业务型异常
package com.YuZai.exception;
public class BusinessException extends RuntimeException{
private Integer code;
public Integer getCode() {return code; }
public void setCode(Integer code) { this.code = code; }
public BusinessException(String message, Integer code) {
super(message);
this.code = code;
}
public BusinessException(String message, Throwable cause, Integer code) {
super(message, cause);
this.code = code;
}
}
自定义系统型异常
package com.YuZai.exception;
public class SystemException extends RuntimeException{
private Integer code;
public Integer getCode() { return code; }
public void setCode(Integer code) { this.code = code; }
public SystemException(String message, Integer code) {
super(message);
this.code = code;
}
public SystemException(String message, Throwable cause, Integer code) {
super(message, cause);
this.code = code;
}
}
自定义异常编码
public class Code {
public static final Integer SYSTEM_ERR = 50001;
public static final Integer BUSINESS_ERR = 60001;
public static final Integer SYSTEM_UNKNOW_ERR = 59999;
}
模拟激活异常
package com.YuZai.service.impl;
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public Book selectById(Integer id) {
//模拟业务异常 测试URL:http://localhost:80/books/200
if(id==200){
throw new BusinessException("禁止违规操作!",Code.BUSINESS_ERR);
}
//模拟未知类异常 测试URL:http://localhost:80/books/300
if (id==300){
throw new ArithmeticException();
}
//将可能出现的异常进行包装,转换成自定义异常 测试URL:http://localhost:80/books/1
try {
System.out.println(2/0);
}catch (ArithmeticException e){
throw new SystemException("系统繁忙,请稍后重试!",e, Code.SYSTEM_ERR);
}
Book book = bookDao.selectById(id);
return book;
}
}
异常处理器 拦截并处理异常
package com.YuZai.controller;
@RestControllerAdvice
public class ProjectExceptionAdvice {
/**
* 处理系统类异常
* @param ex
* @return
*/
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException ex){
//记录日志
//通知运维
//发邮件给开发,将ex对象发给开发
//return信息给前端
return new Result(null,ex.getCode(),ex.getMessage());
}
/**
* 业务类异常
* @param ex
* @return
*/
@ExceptionHandler(BusinessException.class)
public Result doBusinessException(BusinessException ex){
//返回信息给前端
return new Result(null,ex.getCode(),ex.getMessage());
}
/**
* 其他类异常
* @param ex
* @return
*/
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
//记录日志
//通知运维
//发邮件给开发,将ex对象发给开发
//return信息给前端
return new Result(null,Code.SYSTEM_UNKNOW_ERR,"系统维护,请稍后再试! ");
}
}
异常处理器效果对比
{
"data": null,
"code": 50001,
"message": "系统繁忙,请稍后重试!"
}
{
"data":
{
"id": 1,
"type": "搞笑类",
"bookName": "懒羊羊考试",
"description": "小孩子喜欢看"
},
"code": 20011,
"message": ""
}
拦截器
拦截器是一种动态拦截方法调用的机制
作用:在指定的方法调用前后执行预先设定后的代码
阻止原始方法的执行
拦截器与过滤器区别
归属不同:Filter属于Servlet技术,Interceptor属于SprinigMVC技术
拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
入门案例: 步骤一
// 拦截器 (为SpringMVC服务,得加载到SpringMvc容器中)
package com.YuZai.controller.interceptor;
@Component
public class ProjectInterceptor implements HandlerInterceptor {
@Override // preHandle()表示拦截原始操作之前运行的代码
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle() Running...");
return true; // 如果返回false,表示终止原始操作的进行,自然的后面的postHandle()、afterCompletion()也不会执行。
}
@Override // postHandle()表示拦截原始操作之后运行的代码
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle() Running...");
}
@Override //
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion() Running...");
}
}
入门案例:步骤二
package com.YuZai.config;
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); //测试URL:http://localhost/pages/books.html
}
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
//进行具体的拦截器配置
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
}
}
简化配置类开发小技巧
SpringMvcSupport
SpringMvcConfig
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); //测试URL:http://localhost/pages/books.html
}
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
}
}
@Configuration
@ComponentScan({"com.YuZai.controller","com.YuZai.config"})
@EnableWebMvc
public class SpringMvcConfig {
}
可以变为:
缺点: 入侵性较强,因为实现了接口,此程序就和Spring强绑定了(这个类跟Spring的API关联在一起了)
@Configuration
@ComponentScan({"com.YuZai.controller","com.YuZai.config"})
@EnableWebMvc
public class SpringMvcConfigF implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
}
拦截器的执行流程:
拦截器参数
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
参数:
request:请求对象
response:响应对象
handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装 (可以这么说:拿到了handler对象就可以操作原始执行的那个方法)
返回值:
返回值为false,被拦截的处理器将不执行。
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
参数:
ModelAndView (现在的开发模式已经不使用它了) 如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整。
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
参数:
Exception (完全可以通过SpringMVC替换这里进行的操作)如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理。
当配置多个拦截器时,形成拦截器链
// 当配置多个拦截器时,形成拦截器链
com/YuZai/controller/interceptor/ProjectInterceptor02.java
@Component
public class ProjectInterceptor02 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle02() Running...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle02() Running...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion02() Running...");
}
}
com/YuZai/config/SpringMvcSupport.java
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Autowired
private ProjectInterceptor02 projectInterceptor02;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
registry.addInterceptor(projectInterceptor02).addPathPatterns("/books","/books/*");
}
}
拦截器链的运行顺序
preHandle: 与配置顺序相同,必定运行
postHandle: 与配置顺序相反,可能不运行
afterCompletion:与配置顺序相反,可能不运行
例:
preHandle() Running...
preHandle02() Running...
postHandle02() Running...
postHandle() Running...
afterCompletion02() Running...
afterCompletion() Running...