com.atguigu.mvc
SpringMVC_demo01
1.0-SNAPSHOT
war
8
8
org.springframework
spring-webmvc
5.3.1
ch.qos.logback
logback-classic
1.2.3
javax.servlet
javax.servlet-api
3.1.0
provided
org.thymeleaf
thymeleaf-spring5
3.0.12.RELEASE
2.3.1 注册SpringMVC的前端控制器DispatcherServlet
①. 默认配置方式
springMVC
org.springframework.web.servlet.DispatcherServlet
springMVC
/
②. 扩展配置方式
SpringMVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springMVC.xml
1
SpringMVC
/
③. 注:
@Controller
public class HelloController{
}
在请求控制器中创建处理请求的方法
//@RequestMapping注解:处理请求和控制器方法之间的映射关系
//@RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
//localhost:8080/springMVC/
@RequestMapping("/")
public String index() {
//设置视图名称
return "index";
}
在主页index.html中设置超链接
首页
首页
HelloWorld
在在请求控制器中创建处理请求的方法
@RequestMapping("/hello")
public String HelloWorld() {
return "target";
}
@Controller
@RequestMapping("/test")
public class RequestMappingController {
//此时请求映射所映射的请求的请求路径为:/test/testRequestMapping
@RequestMapping("/testRequestMapping")
public String testRequestMapping(){
return "success";
}
}
测试@RequestMapping的value属性-->/testRequestMapping
测试@RequestMapping的value属性-->/test
@RequestMapping(
value = {"/testRequestMapping", "/test"}
)
public String testRequestMapping(){
return "success";
}
测试@RequestMapping的value属性-->/test
@RequestMapping(
value = {"/testRequestMapping", "/test"},
method = {RequestMethod.GET, RequestMethod.POST}
)
public String testRequestMapping(){
return "success";
}
测试@RequestMapping的params属性-->/test
@RequestMapping(
value = {"/testRequestMapping", "/test"},
method = {RequestMethod.GET, RequestMethod.POST},
params = {"username","password!=123456"}
)
public String testRequestMapping(){
return "success";
}
注:
若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,
此时页面会报错400:Parameter conditions "username, password!=123456" not
met for actual request parameters: username={admin}, password={123456}
测试路径中的占位符-->/testRest
@RequestMapping("/testRest/{id}/{username}")
public String testRest(
@PathVariable("id") String id,
@PathVariable("username") String username
){
System.out.println("id:"+id+",username:"+username);
return "success";
}
//最终输出的内容为-->id:1,username:admin
@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:"+username+",password:"+password);
return "success";
}
测试获取请求参数-->/testParam
@RequestMapping("/testParam")
public String testParam(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}
注:
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串
数组或者字符串类型的形参接收此请求参数
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果
@RequestMapping("/testParam")
public String testParam(
@RequestParam(
value="user_name",
required=false,
defaultValue="hehe") String username,
String password,
String[] hobby
){
System.out.println("username:"+username+",password:"+password+",hobby:"+Arrays.toString(hobby));
return "success";
}
@RequestMapping("/testPojo")
public String param(User user){
System.out.println(user);
return "success";
}
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='男',email='[email protected]'}
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceResponseEncoding
true
CharacterEncodingFilter
/*
//使用servletAPI向Request域对象共享数据
@RequestMapping("/testRequestByServletAPI")
public String testServletAPI(HttpServletRequest request){
request.setAttribute("testRequestScope","hello,servletAPI");
return "success";
}
//获得Request域中的数据
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
/*
* ModelAndView有Model和View的功能
* Model主要用于向请求域共享数据
* View主要用于设置视图,实现页面跳转
*/
ModelAndView mav = new ModelAndView();
//处理模型数据:即向请求域request共享数据
mav.addObject("testRequestScope","hello,ModelAndView");
//设置视图名称,实现页面跳转
mav.setViewName("success");
return mav;
}
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("testRequestScope","hello,Model");
return "success";
}
@RequestMapping("/testMap")
public String testMap(Map map){
map.put("testRequestScope", "hello,Map");
return "success";
}
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("testRequestScope", "hello,ModelMap");
return "success";
}
public interface Model{}
public class ModelMap extends LinkedHashMap {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
//向session域共享数据
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope", "hello,session");
return "success";
}
//在html页面获得共享的数据
//向application域共享数据
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("testApplicationScope", "hello,application");
return "success";
}
//在html页面获得共享的数据
@RequestMapping("/testHello")
public String testHello(){
return "hello";
}
SpringMVC.xml文件中的视图解析器配置:
返回的解析结果是:
/WEB-INF/templates/hello.html
然后把这个路径下的html页面通过转发的方式实现跳转
最终的结果是在浏览器上呈现出hello.html页面
@RequestMapping("/testForward")
public String testForward(){
return "forward:/testHello";
}
//创建InternalResourceView视图,将前缀"forward:"去掉,
//然后把"/testHello"请求进行转发,最终的结果是访问下面的控制器方法,是同一个请求
@RequestMapping("/testHello")
public String testHello(){
}
@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/testHello";
}
//创建RedirectView视图,将前缀"redirect:"去掉,
//然后把"/testHello"请求进行重定向,就是客户端重新发一次请求:/testHello
@RequestMapping("/testHello")
public String testHello(){
}
SpringMVC.xml配置文件中配置view-controller:
REST:Representation State Transfer,表现层资源状态转移
7.1.1 资源
7.1.2 资源的表述
7.1.3 状态转移
HiddenHttpMethodFilter
org.springframework.web.filter.HiddenHttpMethodFilter
HiddenHttpMethodFilter
/*
注:
目前为止,SpringMVC中提供了两个过滤器:
CharacterEncodingFilter和HiddenHttpMethodFilter
在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter
原因:
在CharacterEncodingFilter中通过request.setCharacterEncoding(encoding)方法设置字符集
request.setCharacterEncoding(encoding)方法要求前面不能有任何获取请求参数的操作
而HiddenHttpMethodFilter恰恰有一个获取请求方式的操作:
String paramValue = request.getParameter(this.methodParam);
和传统CRUD一样,实现对员工信息的增删改查操作
1>准备实体类
public class Employee {
private Integer id;
private String lastName;
private String email;
//1 male, 0 female
private Integer gender;
...
}
2>准备dao模拟数据
@Repository
public class EmployeeDao {
private static Map employees = null;
static{
employees = new HashMap();
employees.put(1001, new Employee(1001, "E-AA", "[email protected]", 1));
employees.put(1002, new Employee(1002, "E-BB", "[email protected]", 1));
employees.put(1003, new Employee(1003, "E-CC", "[email protected]", 0));
employees.put(1004, new Employee(1004, "E-DD", "[email protected]", 0));
employees.put(1005, new Employee(1005, "E-EE", "[email protected]", 1));
}
private static Integer initId = 1006;
public void save(Employee employee){
if(employee.getId() == null){
employee.setId(initId++);
}
employees.put(employee.getId(),employee);
}
public Collection getAll(){
return employees.values();
}
public Employee get(Integer id){
return employees.get(id);
}
public void delete(Integer id){
employees.remove(id);
}
}
1>配置view-controller
2>创建页面
Title
首页
访问员工信息
1>控制器方法
@RequestMapping(value = "/employee", method = RequestMethod.GET)
public String getEmployeeList(Model model){
Collection employeeList = employeeDao.getAll();
model.addAttribute("employeeList", employeeList);
return "employee_list";
}
2>页面
Employee Info
Employee Info
id
lastName
email
gender
options(add)
delete
update
1>创建处理Delete请求方式的表单
2>删除超链接绑定点击事件
①.引入vue.js
②.删除超链接
delete
③.通过vue处理点击事件
3>控制器方法
@RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
public String deleteEmployee(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/employee";
}
Add Employee
@RequestMapping(value = "/employee", method = RequestMethod.POST)
public String addEmployee(Employee employee){
employeeDao.save(employee);
return "redirect:/employee";
}
1>修改超链接
update
2>控制器方法
@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
public String getEmployeeById(@PathVariable("id") Integer id, Model model){
Employee employee = employeeDao.get(id);
model.addAttribute("employee", employee);
return "employee_update";
}
3>html页面
Update Employee
@RequestMapping(value = "/employee", method = RequestMethod.PUT)
public String updateEmployee(Employee employee){
employeeDao.save(employee);
return "redirect:/employee";
}
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
输出结果:
requestBody:username=admin&password=123456
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity requestEntity){
System.out.println("requestHeader:"+requestEntity.getHeaders());
System.out.println("requestBody:"+requestEntity.getBody());
return "success";
}
输出结果:
requestHeader:[host:"localhost:8080",connection:"keep-alive",content-
length:"27",cache-control:"max-age=0",sec-ch-ua:""Not A;Brand";v="99",
"Chromium";v="90","GoogleChrome";v="90"",sec-ch-ua-mobile:"?0",upgrade-
insecure-requests:"1",origin:"http://localhost:8080",user-
agent:"Mozilla/5.0(Windows NT 10.0; Win64; x64)AppleWebKit/537.36(KHTML,
likeGecko)Chrome/90.0.4430.93 Safari/537.36"]
requestBody:username=admin&password=123
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "success";
}
结果:浏览器页面显示success
//也可以使用传统的方式
//响应浏览器数据
@RequestMapping("/testResponse")
public void testResponse(HttpServletResponse response) throws IOException {
response.getWriter().print("hello,response");
}
com.fasterxml.jackson.core
jackson-databind
2.12.1
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
return new User(1001,"admin","123456",23,"男");
}
浏览器的页面中展示的结果:
{"id":1001,"username":"admin","password":"123456","age":23,"sex":"男"}
1>请求超链接
2>通过vue和axios处理点击事件
3>控制器方法
@RequestMapping("/testAjax")
@ResponseBody
public String testAjax(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "hello,ajax";
}
#########文件上传需要添加依赖
commons-fileupload
commons-fileupload
1.3.1
#########在SpringMVC的配置文件中添加配置
1>html文件
测试文件上传和下载
文件下载(当前工程的环境):从服务器将文件下载到客户端、浏览器端
测试下载图片1.jpg
文件上传(当前工程的环境):从客户端、浏览器端将文件上传到服务器
二者底层用的都是文件复制的过程。
2>请求控制器
@Controller
public class FileUpAndDownController {
//ResponseEntity自定义一个响应报文去响应浏览器
//文件的下载
@RequestMapping("/testDown")
public ResponseEntity testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext(表示的是当前的整个工程)对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径,只有知道文件在服务器中的位置,才可以通过io流进行文件的复制
//servletContext.getRealPath:该方法是获取当前服务器的部署路径(就是工程部署到tomcat服务器的路径)
//如果里面有字符串,获取的就是当前字符串所对应的文件在服务器中的路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
System.out.println(realPath);
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
//is.available():获取当前的输入流对应的文件的所有字节数
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap headers = new HttpHeaders();
//键是固定的,值是设置要下载的方式以及为下载的文件所设置的默认的名字(固定结构,除了1.jpg),attachment代表是以附件的方式下载文件
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象,bytes就是响应体,headers就是响应头
ResponseEntity responseEntity = new ResponseEntity<>(bytes, headers,statusCode);
//关闭输入流
is.close();
return responseEntity;
}
//文件的上传
@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
//1.获取上传的文件的文件名
String fileName = photo.getOriginalFilename();
//2.处理文件重名问题
//获取上传的文件的后缀(通过lastIndexOf获取最后一个.的索引位置,然后通过subString从索引位置开始截取)
String suffixName = fileName.substring(fileName.lastIndexOf("."));
//UUID:32位的随机序列,防止文件重名导致文件覆盖
fileName = UUID.randomUUID().toString() + suffixName;
//3.获取服务器中photo文件目录的路径
ServletContext servletContext = session.getServletContext();
//上传的位置(photo文件目录下)
String photoPath = servletContext.getRealPath("photo");
File file = new File(photoPath);
//判断photopath所对应的文件路径是否存在
if(!file.exists()) {
//如果不存在,则创建目录
file.mkdir();
}
//File.separator:文件分隔符/
String finalPath = photoPath + File.separator + fileName;
//实现上传功能
photo.transferTo(new File(finalPath));
return "success";
}
}
@Component
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("FirstInterceptor-->preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("FirstInterceptor-->postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("FirstInterceptor-->afterCompletion");
}
}
@Component
public class SecondInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("SecondInterceptor-->preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("SecondInterceptor-->postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("SecondInterceptor-->afterCompletion");
}
}
error
//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
//@ExceptionHandler用于设置所标识方法处理的异常
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public String testException(Exception exception, Model model){
//ex表示当前请求处理中出现的异常对象
model.addAttribute("ex",exception);
return "error";
}
}
//error.html
Title
出现错误!
//web工程的初始化类,用来设置servlet上下文,等同于web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定spring的配置类
* @return
*/
@Override
protected Class>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 指定SpringMVC的配置类
* @return
*/
@Override
protected Class>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/**
* 指定DispatcherServlet的映射规则,即url-pattern
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 添加过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
//编码过滤器
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceResponseEncoding(true);
//
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
}
}
@Configuration
public class SpringConfig {
//ssm整合之后,spring的配置信息写在此类中
}
//用来代替SpringMVC的配置文件:SpringMVC.xml
@Configuration
//1.扫描组件
@ComponentScan("com.atguigu.mvc")
//2.开启MVC的注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
//3.配置Thymeleaf的视图解析器
//3.1配置生成模板解析器
//@Bean:加上此注解的方法的返回值可以作为IOC容器中的一个Bean
@Bean
public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
// ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext的方法获得
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(webApplicationContext.getServletContext());
//视图前缀
templateResolver.setPrefix("/WEB-INF/templates/");
//视图后缀
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
//3.2生成模板引擎并为模板引擎注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
//3.3生成视图解析器并为解析器注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setOrder(1);
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
//4.default-servlet-handler:配置默认的servlet,用于开放对静态资源的访问:静态资源不能被SpringMVC处理,所以需要开放default-servlet,对静态资源进行处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//代表默认的servlet可用
configurer.enable();
}
//5.配置视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
}
//6.配置文件上传解析器
@Bean
public MultipartResolver multipartResolver(){
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
//7.配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//创建拦截器对象
TestInterceptor testInterceptor = new TestInterceptor();
registry.addInterceptor(testInterceptor).addPathPatterns("/**").excludePathPatterns("/");
}
//8.配置异常处理解析器
@Override
public void configureHandlerExceptionResolvers(List resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.ArithmeticException","error");
exceptionResolver.setExceptionMappings(properties);
exceptionResolver.setExceptionAttribute("exception");
resolvers.add(exceptionResolver);
}
}