学习笔记:JavaEE开发的颠覆者SpringBoot实战(四)spring MVC基础

资源信息都来自网络,本人只是记录作为个人笔记

一.概述

MVC:Model+View+Controller(数据模型+视图+控制器)

三层架构:Presentation tier+Application tier+Data tier(展示层+应用层+数据访问层)

在spring mvc里,有个专门的类叫Model,用来和V之间的数据交互,传值;V指的是试图页面,包含jsp,freeMarker,Velocity,Thymeleaf,Tile等;C当然就是控制器(Spirng MVC的注解@Controller类)

二.项目快速搭建

1.构建maven项目

pom.xml

<dependencies>
    <dependency>
        <groupId></groupId>
        <artifactId>javaee-web-api</artifactId>
        <version>7.0</version>
        <scope>provied</scope>
    </dependency>

    <!--spring mvc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.18.RELEASE</version>
    </dependency>

    <!--other web-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>

    <!--spring and transactions-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.1.8.RELEASE</version>
    </dependency>

    <!--slf4j和LogBack-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.26</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.26</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>1.2.3</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins<groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</verison>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>    
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.3</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
            </configruation>    
        </plugin>
    </plugins>
</build>

2.日志配置

/*
Logback 配置文件的语法非常灵活。正因为灵活,所以无法用 DTD 或 XML schema 进行定义。尽管如此,可以这样描述配置文件的基本结构:以<configuration>开头,后面有零个或多个<appender>元素,有零个或多个<logger>元素,有最多一个<root>元素。
*/
<?xml version="1.0" encoding="UTF-8" ?>

/*
scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
*/
<configuration scan="true" scanPeriod="1 seconds">
    <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
        <resetJUL>true</resetJUL>
    </contextListener>

    <jmxConfigurator/>

    /*
    负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名
    */
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender" >
        <encoder>
            <pattern>logbak:%d{HH:mm:ss.SSS} %logger{36} - %msg%n </pattern>
        </encoder>
    </appender>
    
    /*
    用来设置某一个包或具体的某一个类的日志打印级别、以及指定<appender>。<loger>仅有一个name属性,一个可选的level和一个可选的addtivity属性。可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个loger
    name: 用来指定受此loger约束的某一个包或者具体的某一个类。
    level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前loger将会继承上级的级别。
       addtivity: 是否向上级loger传递打印信息。默认是true。同<loger>一样,可以包含零个或多个                              <appender-ref>元素,标识这个appender将会添加到这个loger。
    */
    <logger name="org.springframework.web" level="DEBUG" />

    /*
    它也是<loger>元素,但是它是根loger,是所有<loger>的上级。只有一个level属性,因为name已经被命名为"root",且已经是最上级了。
    */
    <root level="info">
        <appender-ref ref="console" />
    </root>

</configuration>

3.演示页面

在src/main/resources下建立views目录,并在此目录下新建index.jsp

<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head></head>
<body>
    <pre>
        welcome to spring mvc world
    </pre>
</body>
</html>

4.spring mvc配置

@Configuration
@EnableWebMvc //开启一些默认配置,如ViewResolver或者MeaaageConverter等
@ComponentScan("com.shm.springdemo")
public class MyMvcCofig{
    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return viewResolver; 
    }
}

5.web配置

/**
WebApplicationInitializer是spring提供用来配置servlet3.0+的接口,从而实现了替代web.xml的位置。实现此接口将会自动被SpringServletContainerInitializer(用来启动Servlet3.0容器)获取到。
*/
public class WebInitializer implements WebApplicationInitializer{
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        //注册配置类,并作关联
        AnnotationConfigWebApplication ctx = new AnnotationConfigWebApplication();
        ctx.register(MyMvcConfig.class);
        ctx.setServletContext(servletContext);

        //注册Spring MVC的DispatcherServlet
        Dynamic servlet = servletContext.addServlet("dispatcher",new DispatcherServlet(ctx));
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
    }
}

6.控制器

@Controller
public class HelloController{
    @RequestMapping("/index")
    public String hello(){
        return "index";
    }
}

三.Spring MVC的常用注解

  • @Controller  注解在类上,表明这个类是spring mvc里的controller,将其声明为spring的一个bean,DiapathcServlet会自动扫描注解了此注解的类并将web请求映射到注解了@RequestMapping的方法上。
  • @RequestMapping  是用来映射web请求,处理类和方法的。可注解在类和方法上
  • @ResponseBody  支持将返回值放在response体内,而不是返回一个页面。可注解在返回值前或者方法上。
  • @RequestBody  允许request的参数在request体内,而不是在直接连接在地址后面。注解在参数前。
  • @PathVariable 用来接收路劲参数。注解在参数前。
  • @RestController 是一个组合注解,组合了@Controller和@ResponseBody。

添加jackson及其以来获得对象和json的转换

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.3</verison>    
</dependency>
@Controller //1 声明此类事一个控制器
@RequestMapping("/anno")  //2 映射此类的访问路径是/anno
public class DemoAnnoController{

    @RequestMapping(produces="text/plain;charset=URF-8") //3 未标注路径,故使用类级别的路径。procuces可定制返回的response的媒体类型和字符集。
    public @ResponseBody String index(HttpServletRequest request){ //4 接受HttpServletRequest 作为参数
        return "url:"+request.getRequestURL+" can access";
    }

    @RequestMapping(value="/pathvar/{str}",produces="text/plain;charset=URF-8") //5 接受路径参数,并在方法参数前结合@PathVariable 使用。
    public @ResponseBody String demoPathVar(@PathVariable String str,HttpServletRequest request){
        return "url:"+request.getRequestURL+" can access,str:"+str;
    }

    @RequestMapping(value="/requestParam",produces="text/plain;charset=URF-8") //6 常规的request参数获取
    public @ResponseBody String passRequestParam(Long id,HttpServletRequest request){
        return "url:"+request.getRequestURL+" can access,id:"+id;
    }

    @RequestMapping(value="/obj",produces="application/json;charset=URF-8") //7 解释参数到对象
    @ResponseBody //8 也可以用在方法上
    public String passObj(DemoObj obj,HttpServletRequest request){
        return "url:"+request.getRequestURL+" can access,obj id:"+obj.getId()+"obj name:"+obj.getName();
    }

    @RequestMapping(value={"/name1","name2"},produces="text/plain;charset=URF-8") //9映射不同的路径到相同的方法
    public @ResponseBody String remove(HttpServletRequest request){
        return "url:"+request.getRequestURL+" can access";
    }


}

四.spring mvc基本配置

Spring MVC的定制配置需要我们实现接口WebMvcConfigurer,并在此类使用@EnableWebMvc注解,来开启对spring mvc的配置支持,我们实现相应的方法,来完成我们的常用配置。

1.静态资源映射

在src/main/resources下建立assets/js目录

配置:

@Configuration
@EnableWebMvc //开启一些默认配置,如ViewResolver或者MeaaageConverter等
@ComponentScan("com.shm.springdemo")
public class MyMvcCofig implements WebMvcConfigurer{ //实现WebMvcConfigurer接口,重写其方法可以springmvc进行配置
    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return viewResolver; 
    }

    @Verride
    public void addResourceHandlers(ResourceHanlerRegistry registry){
        registry.addResourceHandler("/assets/**").addResourceLocations("calsspath:/assets/"); //addResourceLocations指的是文件存放的目录,addResourceHandler指的是对外暴露的访问路径
    }    

}

2.拦截器配置

拦截器(Interceptor)实现对每一个请求处理前后进行的相关业务处理,类似于servlet的Filter

可让普通的Bean实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter类来实现自定义拦截器

通过重写WebMvcConfigurer的addInterceptors方法来注册自定义的拦截器。

拦截器:

public class DemoInterceptor extends HandlerInterceptorAdapter{ //继承HandlerInterceptorAdapter来实现自定义拦截器

    @Verride
    public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{ //重写preHandle方法,在请求发生前
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime",startTime)
    }

    @Verride
    public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModeAndView modelAndView)  throws Exception{ //重写postHandle方法,在请求完成后执行
        long startTime = (Long)request.getAttribute("startTime");
        request.removeAttribute("startTime");
        long endTime = System.currentTimeMills();
        System.out.println("本次请求处理时间为:"+new Long(endTime-startTime)+"ms");
        request.setAttribute("handlingTime",endTime-startTime)
    }


}

配置:

@Bean //配置拦截器的bean
public DemoInterceptor demoInterceptor(){
    return new DemoInterceptor ();
}

@Overide //重写addInterceptor方法,注册拦截器
public void addInterceptors(InterceptorRegistry registry){
    registry.addInterceptor(demoInterceptor());
}

3.@ControllerAdvice 全局控制

通过@ControlerAdvice,我们可以将对于控制器的全局配置放在同一个位置,注解了@Controller的类的方法可以使用@ExceptionHandler,@InitBinder,@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内的方法有效。

  • @ExceptionHandler  用于全局处理控制器里的异常。
  • @InitBinder 用来设置WebDataBinder,自动绑定前台请求参数到model中。
  • @ModelAttribute 本来的作用是绑定键值对到model里,此处是让全局的@RequestMapping都能获得在此处设置的键值对。

定制ControllerAdvice

@ControllerAdvice  //1 声明一个控制器建言,组合了@Component
public class ExceptionHandleAdvice{

    @ExceptionHandler(value=Exception.class)  //2在此定义全局处理,value属性可过滤拦截的条件
    pbulic ModelAndView exception(Exception exception,WebRequest request){
        ModelAndView modelAndView = ModelAndView("error"); //error页面
        modelAndView.addObject("""errorMessage",exception.getMessage());
        return modelAndView;
    }

    @ModelAttribute //3 注解将键值对添加到全局,所有注解了@RequestMapping的方法可获的此键值对
    public void addAttributes(Model model){
        model.addAttribute("msg","额外信息");
    }

    @InitBinder //4 注解定制WebDataBinder
    public void initBinder(WebDataBinder webDataBinder){
        webDataBinder.setDisalloweFields("id"); //5 忽略request参数中的id
    }

}

控制器

@Controller
public class AdviceController{

    @RequestMapping("/advice") 
    public String getSomething(@ModelAttribute("msg") String msg,DemoObj obj){
        throw new IllegalArgumentException("非常抱歉,参数有误:"+"来自@ModelAttribute"+msg);
    }   

}

异常页面

<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head></head>
<body>
    <pre>
        ${errorMessage}
    </pre>
</body>
</html>

四.其他配置

1.快捷的ViewController

@Override
public void addViewControllers(ViewControllerRegistry registry){
    registry.addViewController("index").setViewName("/index");
}

 

2.路径匹配参数配置

路径参数如果带“.”的话,“.”后面的4值将被沪铝,入股哦让其不被忽略,配置

@Verride
pbulic void configurePathMatch(PathMatchCOnfigurer configurer){
    configurer.setUseSuffixPatternMatch(false);
}

3.更多配置

 

五.spring mvc的高级配置

1.文件上传配置

spring mvc通过配置要给MultipartResolver来上传文件。

在spring的控制器中,通过MultipartFile file来接收文件,通过MultipartFile[] files接收多个文件上传。

依赖:

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</verison>    
</dependency>
<!--非必须,可简化I/O操作-->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.3</verison>    
</dependency>

页面:

<div>
    <form action="upload" enctype="multipart/form-data" method="post">
        <input type="file" name="file" /> <br/>
        <input type="submit" value="上传" />
    </form>
</div>

配置:

@Bean
public MultipartResolver multipartResolver(){
    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    multipartResolver.setMaxUploadSize(1024*1024); //限制上传文件大小1M,单位B
    multipartResolver.setDefaultEncoding("utf-8"); //中文乱码处理
    return multipartResolver;
}

控制器

@Controller
public class uploadController{
    @RequestMapping(value="/upload",method=RequestMethod.POST)
    public @ResponseBody String upload(MultipartFile){
        FileUtils.writeByteArrayToFile(new File("D:/upload/"+file.getOriginalFilename()),file.getBytes());
        return "ok";
    }
}

 

 

2.自定义HttpMessageConverter

HttpMessageConverter是用来处理request和response里的数据的。spring为我们内置了大量的HttpMessageConverter,列如,MappingJackson2HttpMessageConverter,StringHttpMessageConverter。

3.服务器端推送技术

当客户端向服务端发送请求,服务端会抓住这个请求不放,等有数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求,周而复始。

六.spring mvc的测试

1.测试依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.8.RELEASE</verison>
    <scope>test</scope>   <!--test说明这些包的存活是在test周期,发布时将不包含这些jar包--> 
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</verison>  
    <scope>test</scope>  
</dependency>

2.演示服务

@Service
public class DemoService{
    public String saySomething(){
        return "hello";
    }
}


/*********************************************************************/

@Controller
public class NormalController{
    @Autowired    
    DemoService demoService;
    
    @RequestMapping("/normal")
    public String testPage(Model model){
        model.addAttribut("msg",demoService.saySomething());
        return "page";    
    }

}

/**********************************************************************/

@RestController
public class MyTestController{
    @Autowired
    DemoService demoService;
    
    @RequestMapping(value="/testRest",produces="text/plain;charset=utf-8")
    public @ResponseBody String testRest(){
        return demoService.saySomething();
    }


}

3.在src/main/resources/vies下新建页面page.jsp

4.测试用例

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContestConfiguration(classes={MyMvcConfig.class}) //springmvc配置java文件
@WebAppConfiguration("src/main/resources") //声明是一个WebAppConfiguration,他的属性指定的是web资源的位置
public class TestControllerTests{
    private MockMvc mockMvc; //模拟mvc对象
    @Autowired
    private DmeoService demoService;
    @Autowired
    WebApplicationContext wac;
    @@Autowired
    MockHttpSession session;
    @Autowired
    MockHttpServletRequest request;

    @Before  //测试前,做的初始化工作
    public void setup(){
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();//初始化mvc对象
    }

    @Test
    public void testNormalController(){
        mockMvc.perform(get("/normal")) //模拟向“/normal”进行get请求
                .addExpect(status().isOk()) //预期控制返回状态为200
                .addExpect(view().name("page")) //预期view的名称
                .addExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp")) //预取也买你转向的真正路径
                .addExpect(model().attribute("msg",demoService.saySomething())); //预取model里的值是方法返回的值“hello”
    }

    
    @Test
    public void testRestConteroller{
        mockMvc.perform(get("/normal")) //模拟向“/normal”进行get请求
                .addExpect(status().isOk()) //预期控制返回状态为200
                .addExpect(content().contentType("text/plain;charset=UTF-8")) //预期返回值的媒体类型
                .addExpect(content().string(demoService.saySomething())); //预取返回值内容为方法返回的值“hello”
    }


}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值