javascript
SpringMVC学习(二)——快速搭建SpringMVC开发环境(注解方式)
文章目錄
- 說明
- 1、工程搭建
- 2、注解配置
- 2.1、context:annotation-config說明
- 2.2、context:component-scan配置說明
- 2.3、mvc:annotation-driven配置說明
- 3、簡單代碼實現
- 4、文件上傳下載實現
- 4.1 文件上傳jar包依賴
- 4.2 在Spring中添加Bean配置
- 4.3 文件上傳實現
- 4.4 文件下載實現
- 5、攔截器配置
- 5.1 攔截器代碼實現
- 5.2 攔截器配置實現
- 5.3 排除攔截器配置實現
- 6、統一異常配置
- 7、日志集成
說明
本文主要是通過注解方式搭建SpringMVC架構,現在通過注解方式搭建框架更加常見以及便捷,文中如果有不妥之處望各位能夠批評指正,大家共同進步。
碼字不易,轉載請注明出處。需要源碼請自行下載:https://gitee.com/leo825/spring-framework-learning-example.git
1、工程搭建
可以參考《SpringMVC學習(一)——快速搭建SpringMVC開發環境(非注解方式)》搭建一個非注解型的SpringMVC,注解方式就是在非注解方式的基礎之上做的優化。本文也是在這個工程的基礎之上做的修改。
2、注解配置
在開啟注解的時候遇到幾個注解配置:
<context:annotation-config/>
<context:component-scan/>
<mvc:annotation-driven/>
先說明一下這幾個注解的定義,通過他們的定義可以理解他們的用途
2.1、context:annotation-config說明
1、如果想使用@Autowired注解,就必須在Spring容器中聲明AutowiredAnnotationBeanPostProcessor的Bean
2、如果想使用@Resource、@PostConstruct、@PreDestroy等注解,就必須在Spring中聲明CommonAnnotationBeanPostProcessor的Bean
3、如果想使用@PersistenceContext注解,就必須在Spring中聲明PersistenceAnnotationBeanPostProcessor的Bean
4、如果想使用 @Required的注解,就必須在Spring中聲明RequiredAnnotationBeanPostProcessor的Bean
通過上面的描述我們知道,使用<context:annotation-config/>配置就是向Spring容器中注冊上面幾個Bean,并實現@Autowired、@Resource、@PostConstruct、@PreDestroy、@PersistenceContext、@Required等注解的功能。
2.2、context:component-scan配置說明
下面的是官網上對于這個標簽的描述:
Scans the classpath for annotated components that will be auto-registered asSpring beans. By default, the Spring-provided @Component, @Repository,@Service, and @Controller stereotypes will be detected.Note: This tag implies the effects of the 'annotation-config' tag, activating @Required,@Autowired, @PostConstruct, @PreDestroy, @Resource, @PersistenceContext and @PersistenceUnitannotations in the component classes, which is usually desired for autodetected components(without external configuration). Turn off the 'annotation-config' attribute to deactivatethis default behavior, for example in order to use custom BeanPostProcessor definitionsfor handling those annotations.大致意思就是說<context:component-scan/>包含<context:annotation-config/>要做的事情,同時額外支持@Component、@Repository、@Service、@Controller注解.并且、<context:component-scan/>掃描base-package并且在applicationcontext中注冊掃描的beans。因此,如果配置了<context:component-scan/>就不需要再配置<context:annotation-config/>
2.3、mvc:annotation-driven配置說明
根據mvc開頭能看出來,這個配置是是針對SpringMVC的,<mvc:annotation-driven/>配置會向Spring容器中自動注冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個bean,是@Controller分發請求所必須的。并且這個配置提供了數據綁定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,讀寫XML的支持(JAXB),讀寫JSON的支持(Jackson非常主要)。
注意:在筆者測試過程中發現<mvc:annotation-driven/>配置不是必須的,查了原因這個注解驅動主要是配置RequestMappingHandlerMapping和RequestMappingHandlerAdapter。但是springmvc容器啟動是會加載默認的DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter。所以不配置也是不影響項目的啟動運行的。但是看源碼這個兩個默認的實現方法是過時的,因此雖然不影響項目,但是我們一般還是會配置<mvc:annotation-driven/>使用新的代替的方法。
這個標簽的具體實現類是:
org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser通過閱讀類的注釋,可以找到這個類主要向Spring容器中注冊了以下Bean實例
RequestMappingHandlerMapping BeanNameUrlHandlerMapping RequestMappingHandlerAdapter HttpRequestHandlerAdapter SimpleControllerHandlerAdapter ExceptionHandlerExceptionResolver ResponseStatusExceptionResolver DefaultHandlerExceptionResolver這些Bean的作用依次為:
1、前面兩個RequestMappingHandlerMapping、BeanNameUrlHandlerMapping是HandlerMapping接口的實現類,用來處理請求映射的。
- 第一個是處理@RequestMapping注解的。
- 第二個會將Controller類的名稱映射為請求的url。
2、中間三個RequestMappingHandlerAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter是用來處理請求的。就是調用Controller的那個方法來處理請求。
- 第一個是處理@Controller注解的處理器,支持自定義方法參數和返回值。
- 第二個處理繼承HttpRequestHandler的處理器。
- 第三個處理繼承自Controller接口的處理器。
3、后面三個是用來處理異常的解析器(自行查看)。
4、除以上之外,此配置還提供以下支持:
- 支持使用ConversionService實例對表單參數進行類型轉換;
- 支持使用@NumberFormatannotation、@DateTimeFormat注解完成數據類型的格式化;
- 支持使用@Valid注解對Java bean實例進行JSR 303驗證;
- 支持使用@RequestBody和@ResponseBody注解
3、簡單代碼實現
首先在myspringmvc-servlet.xml中添加上面介紹的注解配置:
<!-- 配置SpringMVC支持注解,需要放在context:component-scan前面,否則可能會報404--><mvc:annotation-driven></mvc:annotation-driven><!-- 定義項目掃描包的路徑,并支持注解例如:@Component、@Repository、@Service、@Controller--><context:component-scan base-package="com.leo"></context:component-scan>然后開始編寫一個Controller,并且添加@Controller注解
@Controller public class HelloController{/*** 返回一個ModeAndView* @return* @throws ServletException* @throws IOException*/@RequestMapping(value = "/hello",method = RequestMethod.GET)public ModelAndView hello() throws ServletException, IOException {System.out.println("使用配置實現 hello controller 跳轉到 success");ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("data", "恭喜您,測試成功了");modelAndView.setViewName("success");//跳轉到/WEB-INF/views/success.jspreturn modelAndView;}/*** 返回邏輯視圖“success”* @return* @throws ServletException* @throws IOException*/@RequestMapping(value = "/hello2",method = RequestMethod.GET)public String hello2() throws ServletException, IOException {System.out.println("訪問了 hello2");return "success";}/*** 返回一個字符串* @return* @throws ServletException* @throws IOException*/@RequestMapping(value = "/hello3",method = RequestMethod.GET)@ResponseBodypublic String hello3() throws ServletException, IOException {System.out.println("訪問了 hello3");String hello = "你好 SpringMVC";return hello;} }使用 @Autowired注入UserInfoService
@Controller public class GetUserInfoController {@AutowiredUserInfoService userInfoService;@RequestMapping("/getUserInfoList1")public ModelAndView getUserInfoList1(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {System.out.println("GetUserInfoController獲取用戶信息");ModelAndView modelAndView = new ModelAndView();//訪問數據庫List userInfoList = userInfoService.getUserInfoList();System.out.println(userInfoList);modelAndView.addObject("data", userInfoList);modelAndView.setViewName("success");//跳轉到/WEB-INF/views/success.jspreturn modelAndView;} }如果想使用依賴注入的UserInfoService,首先通過@Service注入到Sping中
@Service public class UserInfoServiceImpl implements UserInfoService {@AutowiredJdbcTemplate jdbcTemplate;@Overridepublic void insertUserInfo(UserInfo userInfo) {jdbcTemplate.execute("INSERT INTO USER_INFO(NAME,GENDER,AGE,REMARKS) VALUES('" + userInfo.getName() + "','" + userInfo.getGender() + "','" + userInfo.getAge() + "','" + userInfo.getRemarks() + "')");}@Overridepublic void deleteUserInfo(Integer id) {DBUtil.getJdbcTemple().execute("DELETE FROM USER_INFO WHERE ID = " + id);}@Overridepublic void updateUserInfo(Integer id, UserInfo newUserInfo) {jdbcTemplate.update("UPDATE USER_INFO SET NAME=?, GENDER=?, AGE=? ,REMARKS=? WHERE ID=?", new Object[]{newUserInfo.getName(),newUserInfo.getGender(),newUserInfo.getAge(),newUserInfo.getRemarks(),id});}@Overridepublic List<UserInfo> getUserInfoList() {List<UserInfo> userInfos = new ArrayList<>();List<Map<String, Object>> results = jdbcTemplate.queryForList("SELECT * FROM USER_INFO");for (Map obj : results) {UserInfo userInfo = new UserInfo();userInfo.setId((Integer) obj.get("ID"));userInfo.setName((String) obj.get("NAME"));userInfo.setGender("0".equals((String) obj.get("GENDER")) ? "女" : "男");userInfo.setAge((String) obj.get("AGE"));userInfo.setRemarks((String) obj.get("REMARKS"));userInfos.add(userInfo);}return userInfos;} }4、文件上傳下載實現
4.1 文件上傳jar包依賴
首先,如果沒有添加jar包依賴的在pom.xml中添加依賴
<!-- SpirngMVC支持文件上傳的工具包 --><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.3</version></dependency>4.2 在Spring中添加Bean配置
其次,需要在myspringmvc-servlet.xml配置文件中添加Bean支持
<!-- 使用文件上傳下載功能需要注入 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 請求的編碼格式,必須和 jsp 的 pageEncoding 屬性一致,默認為ISO-8859-1 --><property name="defaultEncoding" value="utf-8"></property><!-- 上傳最大限制 1M = 1M * 1024 * 1024 = 1048576 byte--><property name="maxUploadSize" value="1048576"></property></bean>4.3 文件上傳實現
通過以上的配置值就可以使用MutipartFile直接接收文件了
/*** 文件上傳** @param multipartFil* @param request* @return* @throws IOException*/@RequestMapping(value = "/upload", method = RequestMethod.POST)public String upload(@RequestParam(value = "file") MultipartFile multipartFil, HttpServletRequest request) throws IOException {if (!multipartFil.isEmpty()) {//文件標簽名System.out.println(multipartFil.getName());//上傳文件名稱System.out.println(multipartFil.getOriginalFilename());//文件大小System.out.println(multipartFil.getSize());//上傳文件的類型System.out.println(multipartFil.getContentType());String fileDirStr = request.getServletContext().getRealPath("/uploadFile");String filename = multipartFil.getOriginalFilename();System.out.println(fileDirStr);File fileDir = new File(fileDirStr);if (!fileDir.exists()) {fileDir.mkdirs();}File file1 = new File(fileDir, filename);multipartFil.transferTo(file1);}return "success";}前端的測試代碼如下:
<form action="${pageContext.request.contextPath}/upload"enctype="multipart/form-data"method="post"><input type="file" name="file" value="請選擇需要上傳的文件" /><br><input type="submit" value="提交"> </form>4.4 文件下載實現
文件下載的方式也有很多,這里提供一下三種測試方法:
/*** 下載文件:* 通過Spring提供的這種對象,這種下載具有局限性,文件太大會導致內存溢出異常** @param request* @param filename* @return* @throws IOException*/@RequestMapping(value = "/download", method = RequestMethod.GET)public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam String filename) throws IOException {String fileDirStr = request.getServletContext().getRealPath("/uploadFile");File file = new File(fileDirStr, filename);byte[] body = FileUtils.readFileToByteArray(file);//設置下載名稱,否則亂碼String downloadFilename = new String(file.getName().getBytes("utf-8"), "iso-8859-1");HttpHeaders httpHeaders = new HttpHeaders();//設置文件類型httpHeaders.add("Content-Disposition", "attchement;filename=" + downloadFilename);ResponseEntity responseEntity = new ResponseEntity(body, httpHeaders, HttpStatus.OK);return responseEntity;}/*** 使用字節流直接輸出,實現類似上面** @param request* @param response* @param filename*/@RequestMapping(value = "/download2", method = RequestMethod.GET)public void download2(HttpServletRequest request, HttpServletResponse response, @RequestParam String filename) throws IOException {//獲取主機文件String fileDirStr = request.getServletContext().getRealPath("/uploadFile");File file = new File(fileDirStr, filename);byte[] outByte = FileUtils.readFileToByteArray(file);//設置下載名稱,否則亂碼String downloadFilename = new String(file.getName().getBytes("utf-8"), "iso-8859-1");response.setHeader("Content-Disposition", "attchement;filename=" + downloadFilename);response.getOutputStream().write(outByte);response.getOutputStream().close();}/*** 普通的輸出輸出流下載** @param request* @param response* @param filename* @throws IOException*/@RequestMapping(value = "/download3", method = RequestMethod.GET)public void download3(HttpServletRequest request, HttpServletResponse response, @RequestParam String filename) throws IOException {//獲取主機文件String fileDirStr = request.getServletContext().getRealPath("/uploadFile");File file = new File(fileDirStr, filename);InputStream inputStream = new FileInputStream(file);//設置下載名稱,否則亂碼String downloadFilename = new String(file.getName().getBytes("utf-8"), "iso-8859-1");response.setHeader("Content-Disposition", "attchement;filename=" + downloadFilename);OutputStream os = response.getOutputStream();byte[] bf = new byte[2048];int len;while ((len = inputStream.read(bf)) > 0) {os.write(bf, 0, len);}os.close();inputStream.close();}5、攔截器配置
5.1 攔截器代碼實現
首先,編寫兩個攔截器繼承自HandlerInterceptorAdapter
第一個攔截器:
第二個攔截器:
public class HandlerInterceptor2 extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("HandlerInterceptor2 preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("HandlerInterceptor2 postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("HandlerInterceptor2 afterCompletion");} }5.2 攔截器配置實現
編寫完上面的攔截器代碼之后,需要將Bean實例注入到Spring中
<!-- 攔截器實現 --><mvc:interceptors><bean class="com.leo.interceptor.HandlerInterceptor1"></bean><bean class="com.leo.interceptor.HandlerInterceptor2"></bean></mvc:interceptors>訪問:http://localhost:8080/springmvc/hello
執行情況如下日志所示:
5.3 排除攔截器配置實現
需要在myspringmvc-servlet.xml配置文件中添加如下配置信息:
<!-- 攔截器實現 --><mvc:interceptors><mvc:interceptor><!-- 攔截對應 /hello 路徑下的所有請求 --><mvc:mapping path="/springmvc/*"/><!-- 除去 /springmvc/hello 這個請求 --><mvc:exclude-mapping path="/springmvc/hello"></mvc:exclude-mapping><bean class="com.leo.interceptor.HandlerInterceptor1"></bean></mvc:interceptor><mvc:interceptor><!-- 所有請求 --><mvc:mapping path="/**"/><bean class="com.leo.interceptor.HandlerInterceptor2"></bean></mvc:interceptor></mvc:interceptors>配置后,再訪問:http://localhost:8080/springmvc/hello
控制臺輸出的日志為如下:
想了解過濾器和攔截器區別的可以參考:《SpringMVC中過濾器和攔截器的區別》
6、統一異常配置
@ControllerAdvice 注解修飾可以處理所有的Controller的異常
@ControllerAdvice public class HandlerException {@ExceptionHandlerpublic ModelAndView handlerAllException(Exception e) {ModelAndView mv = new ModelAndView();mv.addObject("exceptionMsg", e.getMessage());mv.setViewName("error");System.out.println("HelloController.handlerAllException");return mv;}@ExceptionHandler(value = {ArithmeticException.class})public ModelAndView handlerArithmeticException(Exception e) {ModelAndView mv = new ModelAndView();mv.addObject("exceptionMsg", e.getMessage());mv.setViewName("error");System.out.println("HelloController.handlerArithmeticException");return mv;} }當然也可以通過在myspringmvc-servlet.xml配置文件中添加Bean支持實現
<!-- 可以配置使用Spring提供的異常處理類 --><bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><!-- 指定注入異常屬性的key, 默認為 "exception" --><property name="exceptionAttribute" value="ex"></property><property name="exceptionMappings"><props><prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop></props></property></bean>7、日志集成
本章節介紹的是集成log4j2日志框架,首先pom中配置依賴jar
<!--log4j2支持集成日志框架--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.11.2</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.11.2</version></dependency>然后在相應的文件目錄下編寫log4j.xml模板文件,筆者的在resources下
<?xml version="1.0" encoding="UTF-8"?> <!--status:這個用于設置log4j2自身內部的信息輸出,可以不設置,當設置成trace時,你會看到log4j2內部各種詳細輸出。monitorInterval : Log4j2能夠自動檢測修改配置文件和重新配置本身,設置間隔秒數,單位是s,最小是5s. --> <Configuration status="ERROR" monitorInterval="30"><Properties><!-- 配置日志文件輸出目錄 --><Property name="LOG_HOME">/root/bubble/logs</Property><property name="ERROR_LOG_FILE_NAME">/root/bubble/logs</property></Properties><Appenders><!-- Console節點 配置控制臺日志輸出:name:指定Appender的名字.target: SYSTEM_OUT 或 SYSTEM_ERR,一般只設置默認:SYSTEM_OUT.PatternLayout: 輸出格式,不設置默認為:%m%n.--><Console name="Console" target="SYSTEM_OUT"><!-- ThresholdFilter 過濾器:控制臺只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)日志的級別: ALL< Trace < DEBUG < INFO < WARN < ERROR < FATAL < OFF。--><ThresholdFilter level="TRACE" onMatch="ACCEPT" onMismatch="DENY" /><!-- PatternLayout 日志輸出格式模板:%d{yyyy-MM-dd HH:mm:ss.SSS} 日志生成時間。%-5level 日志級別(級別從左顯示5個字符寬度)。%logger{36} 表示logger名字最長36個字符,否則按照句點分割。%L 日志輸出所在行數 日志輸出所在行數[%t] 輸出產生該日志事件的線程名%msg 日志消息%n 是換行符eg:"2017-04-17 16:04:02.312 INFO com.zte.lucene.tools.DateUtils2Joda:424 [main] - Info Message!"--><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36}:%L [%t] - %msg%n" /></Console><!-- RollingFileAppender 定義輸出滾動文件格式。(FileAppender 定義輸出到文件)name : RollingFileAppender的名字,用于與Loggers模塊的綁定fileName : 日志輸出保存的文件路徑filePattern:滿足條件后,日志輸出保存的文件路徑和名稱。(eg: 2017-04/search-2017-04-17-1.log)--><RollingFile name="RollingFileInfo" fileName="${LOG_HOME}/search.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/search-%d{yyyy-MM-dd}-%i.log"><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" /><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36}:%L [%t] - %msg%n" /><!-- Policies定義相關策略:--><Policies><!--TimeBasedTriggeringPolicy 基于時間的滾動策略:該策略主要是完成周期性的log文件封存工作。interval: integer型,指定兩次封存動作之間的時間間隔,默認是1 hour。單位:以日志的命名精度來確定單位,比如yyyy-MM-dd-HH 單位為小時,yyyy-MM-dd-HH-mm 單位為分鐘。關鍵點在于 filePattern后的日期格式.modulate: boolean型,說明是否對封存時間進行調制。若modulate=true,則封存時間將以0點為邊界進行偏移計算。比如:modulate=true,interval=4hours,那么假設上次封存日志的時間為01:00,則下次封存日志的時間為04:00,之后的封存時間依次為08:00,12:00.--><!-- log4j2的按天分日志文件 : search-%d{yyyy-MM-dd}.log --><TimeBasedTriggeringPolicy interval="1" modulate="true" /><!--SizeBasedTriggeringPolicy 基于對log文件大小的判斷策略:當log文件大于設定的閾值時,將觸發封存動作。可設定的log文件大小的單位有bytes、KB、MB或GB。--><SizeBasedTriggeringPolicy size="100 MB" /></Policies><!--DefaultRolloverStrategy(屬性如不設置,則默認為最多同一文件夾下7個文件):fileIndex: String類型,有兩個選擇“max”或“min”。設置為“max”意味著將最新的日志信息封存在序號較大的封存文件中。“min”則相反。max: integer類型,封存文件的序號的最大值。(超過最大值時,將有文件被刪除)min: integer類型,封存文件的序號的起始值。相當于min和max兩個參數設置了一個保存窗口,超出這個窗口的日志文件將會被刪除。--><DefaultRolloverStrategy max="100" /></RollingFile><RollingFile name="RollingFileError"fileName="${ERROR_LOG_FILE_NAME}/search-error.log"filePattern="${ERROR_LOG_FILE_NAME}/$${date:yyyy-MM}/search-error-%d{yyyy-MM-dd}.log"><ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY" /><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36}:%L [%t] - %msg%n" /><Policies><TimeBasedTriggeringPolicy interval="1" modulate="true" /></Policies><DefaultRolloverStrategy max="100" /></RollingFile></Appenders><!--然后定義logger,只有定義了logger并引入的appender,appender才會生效--><Loggers><!--過濾掉spring的一些無用的DEBUG信息--><logger name="org.springframework" level="INFO"></logger><!-- 配置日志的根節點 --><!-- level="all" 只能夠輸出level級別是all及all以上的--><root level="all"><appender-ref ref="Console" /><appender-ref ref="RollingFileInfo" /><appender-ref ref="RollingFileError" /></root></Loggers> </Configuration>然后在web.xml文件中,將日志文件配置到上下文中
<!-- 集成log4j2日志框架 --><context-param><param-name>log4jConfiguration</param-name><param-value>classpath:log4j2.xml</param-value></context-param>完成以上配置就已經集成了log4j2框架。開始測試:
@Controller @RequestMapping("/log") public class LoggerRecordController {//獲取日志private static final Logger LOGGER = LogManager.getLogger();@AutowiredUserInfoService userInfoService;@RequestMapping("/testLog1")@ResponseBodypublic ModelAndView testLog1(@RequestParam("username") String name) {long startTime = System.currentTimeMillis();ModelAndView modelAndView = new ModelAndView();//訪問數據庫List userInfoList = userInfoService.getUserInfoList();LOGGER.info(userInfoList);modelAndView.addObject("data", userInfoList);modelAndView.setViewName("success");//跳轉到/WEB-INF/views/success.jspLOGGER.info("獲取name=" + name + "接口耗時:" + (System.currentTimeMillis() - startTime) + "ms");return modelAndView;} }測試結果,如下所示:
2020-02-09 20:15:59.608 INFO com.leo.controller.LoggerRecordController:38 [http-apr-8080-exec-7] - [UserInfo{id=3, name='曉玲', gender='女', age='22', remarks='工程師'}, UserInfo{id=4, name='曉玲', gender='女', age='24', remarks='工程師'}] 2020-02-09 20:15:59.631 INFO com.leo.controller.LoggerRecordController:41 [http-apr-8080-exec-7] - 獲取name=leo825接口耗時:39ms總結
以上是生活随笔為你收集整理的SpringMVC学习(二)——快速搭建SpringMVC开发环境(注解方式)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringMVC中过滤器和拦截器的区别
- 下一篇: SpringMVC学习(三)——Spri