SpringMVC
SpringMVC简介
SpringMVC是一种基于Java实现MVC模型的轻量级web框架
优点
使用简单,开发便捷
灵活性强
入门案例
1、创建web工程(maven结构)
2、设置tomcat服务器,加载web工程(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>
3、导入坐标(SpringMVC+Servlet)
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency>
4、定义处理请求的功能类(UserController)
@Controller public class UesrController { public String save(){ System.out.println("user save ..."); return "{'info':'springMVC'}"; } }
5、设置请求映射(配置映射关系)
为UserController添加注解
@Controller public class UesrController { @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'info':'springMVC'}"; } }
6、将SpringMVC设定加载到tomcat服务器
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //加载SpringMVC对应的容器对象 protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } protected String[] getServletMappings() {return new String[]{"/"};} //加载Spring配置对应的容器对象 protected WebApplicationContext createRootApplicationContext() {return null;} }
入门案例工作流程分析
启动服务器初始化过程
1、服务器启动,执行ServletContainersInitConfig类,初始化web容器
2、执行createServletApplicationContext方法,创建WebApplicationContext对象
3、加载SpringMvcConfig
4、执行@ComponentScan加载对应的bean
5、加载UserController,每个@RequestMapping的名称对应一个具体的方法
6、执行getServletMappings方法,定义所有的请求都通过SpingMVC
单次请求过程
1、发送请求localhost/save
2、web容器发现所有请求都经过SpingMVC,将请求交给SpringMVC处理
3、解析请求路径/save
4、由/save匹配执行对应的方法save()
5、执行save()
6、检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方
请求与响应
请求参数
普通参数:url地址传参,地址参数名与形参变量名相同(名称不匹配在参数前使用@RequestParam("地址参数名")),定义形参即可接收
POJO参数:请求参数名与形参对象属性名相同
数组参数:请求参数名与形参对象属性名相同且请求参数为多个
集合保存普通参数:请求参数名与形参对象属性名相同且请求参数为多个,使用@RequestParam绑定参数关系
接收请求中的json数据
开启自动转换json数据支持
@Configuration @ComponentScan("com.itheima.controller") //开启json数据类型自动转换 @EnableWebMvc public class SpringMvcConfig { }
接收json数据
参数前加@RequestBody
@RequestMapping("/listPojoParamForJson") @ResponseBody public String listPojoParamForJson(@RequestBody List<User> list){ System.out.println("list pojo(json)参数传递 list ==> "+list); return "{'module':'list pojo for json param'}"; }
日期类型参数传递
接收参数时,根据不同的日期格式设置不同的接收方式
//使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd @RequestMapping("/dataParam") @ResponseBody public String dataParam(Date date, @DateTimeFormat(pattern="yyyy-MM-dd") Date date1, @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){ System.out.println("参数传递 date ==> "+date); System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1); System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2); return "{'module':'data param'}"; }
响应
响应页面:直接返回页面名称
//返回值为String类型,设置返回值为页面名称,即可实现页面跳转 @RequestMapping("/toJumpPage") public String toJumpPage(){ System.out.println("跳转页面"); return "page.jsp"; }
响应文本数据
//返回值为String类型,设置返回值为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解 @RequestMapping("/toText") @ResponseBody public String toText(){ System.out.println("返回纯文本数据"); return "response text"; }
响应json数据
//返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解 @RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO(){ System.out.println("返回json对象数据"); User user = new User(); user.setName("dashadiao"); user.setAge(15); return user; }
REST风格
REST简介
REST:表现形式转换
优点:
隐藏资源的访问行为
书写简化
REST风格访问资源时使用行为动作区分对资源进行了何种操作
入门案例
1、设定http请求动作
@RequestMapping(value = "/users",method = RequestMethod.POST) @ResponseBody public String save(){ System.out.println("user save..."); return "{'module':'user save'}"; }
2、设定请求参数(路径变量)
//@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同 @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id){ System.out.println("user delete..." + id); return "{'module':'user delete'}"; }
快速开发
1、将相同的路径前缀提到类前
@Controller @ResponseBody @RequestMapping("/books") public class BookController {
2、将@Controller和@ResponseBody合并为@RestController
@RestController //使用@RestController注解替换@Controller与@ResponseBody注解,简化书写 @RequestMapping("/books") public class BookController {
3、将每个方法的前的@RequestMapping替换为@{行为}Mapping
@PostMapping //使用@PostMapping简化Post请求方法对应的映射配置 @DeleteMapping("/{id}") //使用@DeleteMapping简化DELETE请求方法对应的映射配置 @PutMapping //使用@PutMapping简化Put请求方法对应的映射配置 @GetMapping("/{id}") //使用@GetMapping简化GET请求方法对应的映射配置
SSM整合(注解)
1、创建Maven的web项目
补全项目结构
2、添加依赖
pom.xml添加SSM所需要依赖的jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
配置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>
3、创建项目包结构
config目录存放相关配置类
controller编写Controller类
dao存放Dao接口,使用Mapper接口代理方式没有实现类包
service存放的是Service接口,impl存放接口的实现类
resources存放配置文件,如Jdbc.properties
webapp目录可以存放静态资源
test/java存放测试类
4、创建SpringConfig类
@Configuration用来代替xml配置文件
@ComponentScan({"com.my.service"})设定要扫描的包
@PropertySource("classpath:jdbc.properties")配置数据源
@Import({JdbcConfig.class,MyBatisConfig.class})将类注入到容器中
@EnableTransactionManagement开启声明式事务
@Configuration @ComponentScan({"com.my.service"}) @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MyBatisConfig.class}) @EnableTransactionManagement public class SpringConfig { }
5、创建JdbcConfig配置类
属性值来源Jdbc.properties文件
public DataSource dataSource()返回数据源
public PlatformTransactionManager transactionManager(DataSource dataSource)事务管理器
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 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; } }
6、创建MybatisConfig配置类
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource)返回SqlSessionFactoryBean对象,设置扫描包
public MapperScannerConfigurer mapperScannerConfigurer()扫描映射
public class MyBatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTypeAliasesPackage("com.my.domain"); return factoryBean; } @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.my.dao"); return msc; } }
7、创建jdbc.properties
提供要连接数据库的信息
MySQL8驱动:com.mysql.cj.jdbc.Driver
MySQL5驱动:com.mysql.jdbc.Driver
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false jdbc.username=root jdbc.password=123456789
8、创建SpringMVC配置类
@EnableWebMvc是使用Java 注解快捷配置Spring Webmvc的一个注解。
@Configuration @ComponentScan({"com.my.controller","com.my.config"}) @EnableWebMvc public class SpringMvcConfig { }
9、创建Web项目入口配置类
protected Class[] getRootConfigClasses():配置SpringConfig
protected Class[] getServletConfigClasses():配置SpringMvcConfig
protected String[] getServletMappings():设置拦截路径
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } }
10、创建数据库及表
create database ssm_db character set utf8; use ssm_db; create table tbl_book( id int primary key auto_increment, type varchar(20), name varchar(50), description varchar(255) )
11、编写模块类
public class Book { private Integer id; private String type; private String name; private String description; //添加get/set方法 }
12、编写Dao接口
public interface BookDao { @Insert("insert into tbl_book values(null,#{type},#{name},#{description})") public void save(Book book); @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}") public void update(Book book); @Delete("delete from tbl_book where id = #{id}") public void delete(Integer id); @Select("select * from tbl_book where id = #{id}") public Book getById(Integer id); @Select("select * from tbl_book") public List<Book> getAll(); }
13、编写Service接口和实现类
@Transactional注解在代码执行出错的时候能够进行事务的回滚。
bookDao在Service中注入的会提示一个红线提示IDEA在检测依赖关系的时候,没有找到适合的类注入,所以会提示错误提示 但是程序运行的时候,代理对象就会被创建,框架会使用DI进行注入,所以程序运行无影响。
@Transactional public interface BookService { public boolean save(Book book); public boolean update(Book book); public boolean delete(Integer id); public Book getById(Integer id); public List<Book> getAll(); }
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; public boolean save(Book book) { bookDao.save(book); return true; } public boolean update(Book book) { bookDao.update(book); return true; } public boolean delete(Integer id) { bookDao.delete(id); return true; } public Book getById(Integer id) { return bookDao.getById(id); } public List<Book> getAll() { return bookDao.getAll(); } }
14、编写Contorller类
@RestController @RequestMapping("/books") public class BookController { @Autowired private BookService bookService; @PostMapping public Result save(@RequestBody Book book) { boolean flag = bookService.save(book); return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR,flag); } @PutMapping public Result update(@RequestBody Book book) { boolean flag = bookService.update(book); return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag); } @DeleteMapping("/{id}") public Result delete(@PathVariable Integer id) { boolean flag = bookService.delete(id); return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag); } @GetMapping("/{id}") public Result getById(@PathVariable Integer id) { Book book = bookService.getById(id); Integer code =book!=null?Code.GET_OK:Code.GET_ERR; String msg = book !=null?"":"数据查询失败请重试"; return new Result(code,book,msg); } @GetMapping public Result getAll() { List<Book> books = bookService.getAll(); Integer code = books!=null?Code.GET_OK:Code.GET_ERR; String msg = books!=null?"":"数据查询失败请重试"; return new Result(code,books,msg); } }
15、表现层与前端数据传输协议定义
对于前端来说,如果后台能够返回一 个统一的数据结果,前端在解析的时候就可以按照一种方式进行解析。开发就会变得更加简单。
为了封装返回的结果数据:创建结果模型类,封装数据到data属性中
为了封装返回的数据是何种操作及是否操作成功:封装操作结果到code属性中
操作失败后为了封装返回的错误信息:封装特殊消息到message(msg)属性中
public class Result { private Object data; private Integer code; private String msg; public Result() {} public Result(Integer code, Object data) { this.data = data; this.code = code; } public Result(Integer code, Object data, String msg){ this.data=data; this.code=code; this.msg=msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
public class Code { public static final Integer SAVE_OK = 20011; public static final Integer DELETE_OK = 20021; public static final Integer UPDATE_OK = 20031; public static final Integer GET_OK = 20041; public static final Integer SAVE_ERR = 20010; public static final Integer DELETE_ERR = 20020; public static final Integer UPDATE_ERR = 20030; public static final Integer GET_ERR = 20040; }
16、异常处理
异常的种类及出现异常的原因:
框架内部抛出的异常:因使用不合规导致
数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
1. 各个层级均出现异常,异常处理代码书写在哪一层?
所有的异常均抛出到表现层进行处理
2. 异常的种类很多,表现层如何将所有的异常都处理到呢?
异常分类
3. 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决?
AOP
@RestControllerAdvice:Rest风格开发的控制器增强类定义上方,为Rest风格开发的控制器类做增强。此注解自带@ResponseBody注解与@Component注解,具备对应的功能
@ExceptionHandler:专用于异常处理的控制器方法上方,设置指定异常的处理方案,功能等同于控制器方法, 出现异常后终止原始控制器执行,并转入当前方法执行。此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(Exception.class) public Result doException(Exception ex){ System.out.println("处理异常"); return new Result(404,null,"存在异常"); } @ExceptionHandler(SystemException.class) public Result doSystemException(SystemException ex){ //记录日志 //发送消息给运维 //给开发人员发邮件 return new Result(ex.getCode(),null,ex.getMessage()); } @ExceptionHandler(BusinessException.class) public Result doBusinessException(BusinessException ex){ return new Result(ex.getCode(),null,ex.getMessage()); } }
public class SystemException extends RuntimeException { private Integer 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 Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } }
public class BusinessException extends RuntimeException { private Integer 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; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } }
拦截器
拦截器概念
一种动态拦截方法调用机制,在SpringMVC中拦截控制器方法的执行
作用:
在指定方法前后执行预先定义的功能
阻止原始方法的执行
入门案例
1、创建一个拦截器类(定义在controller.interceptor包下),实现HandlerInterceptor接口
public class ProjectInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle..."); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("preHandle..."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion..."); } }
2、加载拦截器
类前添加注解@Component,检查SpringMvcConfig类是否能扫描到拦截器
3、在SpringMvcSupport类中重写addInterceptors方法
检查SpringMvcConfig类是否能扫描到SpringMvcSupport类
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Autowired private ProjectInterceptor projectInterceptor; @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books"); } }