花了點時間作的(比較水)筆記,有可能有漏洞,有不對的,歡迎指出(若是你會看的話)。html
首先都是二話不說,先找依賴(Gradle):前端
// spring系列 // 這個jar文件包含Spring框架基本的核心工具類,Spring其它組件要都要使用到這個包裏的類,是其它組件的基本核心,固然你也能夠在本身的應用系統中使用這些工具類。 compile group: 'org.springframework', name: 'spring-core', version: '4.2.5.RELEASE' // 這個jar文件是全部應用都要用到的,它包含訪問配置文件、建立和管理bean以及進行Inversion of Control / Dependency Injection(IoC/DI)操做相關的全部類。 // 若是應用只需基本的IoC/DI支持,引入spring-core.jar及spring- beans.jar文件就能夠了。 compile group: 'org.springframework', name: 'spring-beans', version: '4.2.5.RELEASE' // 提供在基礎IOC功能上的擴展服務,此外還提供許多企業級服務的支持,有郵件服務、任務調度、JNDI定位,EJB集成、遠程訪問、緩存以及多種視圖層框架的支持。 compile group: 'org.springframework', name: 'spring-context', version: '4.2.5.RELEASE' // 對JDBC 的簡單封裝 compile group: 'org.springframework', name: 'spring-jdbc', version: '4.2.5.RELEASE' // 爲JDBC、Hibernate、JDO、JPA等提供的一致的聲明式和編程式事務管理 compile group: 'org.springframework', name: 'spring-tx', version: '4.2.5.RELEASE' // 包含Web應用開發時,用到Spring框架時所需的核心類,包括自動載入WebApplicationContext特性的類、Struts與JSF集成類、文件上傳的支持類、Filter類和大量工具輔助類 compile group: 'org.springframework', name: 'spring-web', version: '4.2.5.RELEASE' // 這個jar文件包含Spring MVC框架相關的全部類。包含國際化、標籤、Theme、視圖展示的FreeMarker、JasperReports、Tiles、Velocity、 XSLT相關類。 // REST Web服務和Web應用的視圖控制器的實現, 固然,若是你的應用使用了獨立的MVC框架,則無需這個JAR文件裏的任何類。 compile group: 'org.springframework', name: 'spring-webmvc', version: '4.2.5.RELEASE' // 對於單元測試和集成測試的簡單封裝 compile group: 'org.springframework', name: 'spring-test', version: '4.2.5.RELEASE' // Spring提供的對AspectJ框架的整合 compile group: 'org.springframework', name: 'spring-aspects', version: '4.2.5.RELEASE'
而後SpringMVC主要仍是要以來Servlet,因此須要進行配置web.xml(順帶配置一個過濾器,讓SpringMVC全部操做都是UTF-8格式):java
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="false"> <!-- UTF-8 編碼 防止插入數據庫的數據亂碼 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- springMVC配置 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- Spring 配置文件所在的位置 --> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-*.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
在此以前能夠先了解一下這兩個配置,基本上是必備的,幫咱們作了不少事情。web
<!-- 配置SpringMVC --> <!-- 開啓SpringMVC註解模式 --> <!--簡化配置 (1)自動註冊DefaultannotationHandlerMapping,AnnotationMethodhandlerAdapter (2)默認提供了一系列的功能:數據綁定,數字和日期的轉換format @NumberFormat,@DataTimeFormat,xml,json的讀寫支持 --> <mvc:annotation-driven /> <!-- servlet-mapping 映射路徑:"/" --> <!-- 靜態資源默認Servlet配置 1:加入對靜態資源的處理:js,gif,png 2:容許使用"/"作總體配置映射 --> <mvc:default-servlet-handler/> <!-- 後面的xml和類基本上都要自動包掃描,讓Spring認識 --> <content:component-scan base-package="com.test.all"/>
而後就能夠盡情的使用了。spring
/** * 這裏的屬性自動裝配,能夠裝配不少東西,好比Bean對象,還有普通的字符串,當用戶請求中帶有同名的參數時,會自動把結果賦值到上面。 * 而後還可使用原生的Servlet對象,Spring也會自動注入,支持: * HttpServletRequest * HttpServletResponse * HttpSession * Writer * Reader * Locale * InputStream * OutputStream * java.security.Principal * 其中Write(Reader)和OutputStream(InputStream)和在Servlet同樣,不能同時使用 **/ public BaseDTO<User> doLogin(User user, @RequestParam(value = "login_token") String login_token, HttpServletResponse response) { }
之因此Spring可以自動裝配屬性(傳入的參數都是String,可是咱們接收到的能夠是Integer等類型),是由於Spring寫好了一些默認的數據類型轉換器,然而爲了知足要求,咱們也能夠自定義轉換器:
先寫好一個類型轉換器類:數據庫
// 這個類的做用是把String轉換成Timestamp public class JsMillisecondsToDate implements Converter<String, Timestamp> { @Override public Timestamp convert(String source) { long millis = Long.parseLong(source); return new Timestamp(millis); } }
而後在Spring文件中配置:編程
<!-- 註冊轉換器 --> <mvc:annotation-driven conversion-service="jsMillisecondsToDate"/> <!-- ====== 自定義類型轉換器 ======= --> <bean id="jsMillisecondsToDate" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.test.conver.JsMillisecondsToDate"/> </set> </property> </bean>
以後的話Spring就有了String -> Timestamp的支持,能夠自動的將String裝配成Timestamp的支持。json
@RequestMapping(value = "/submitText", method = RequestMethod.POST) @ResponseBody public BaseDTO submitText(@RequestParam("title") String title, @RequestParam("date") Timestamp date, BindingResult bindingResult) { return null; } // 傳入的json能夠是這樣的(date是一個時間戳):{"title" : "初音將來的平常", "date", "122354864"} // Spring會用咱們定義的轉換器去轉換,若是轉換出錯了,會把錯誤結果放入BindingResult對象中,能夠檢查發生了什麼問題。
<!-- ====== 試圖解析器 包含了thymeleaf和jsp,若是你只用jsp,只須要最後一段就行,order改成1 ========== --> <!-- thymeleaf 配置 --> <bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver"> <constructor-arg ref="servletContext"/> <!-- --> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML5"/> <!-- Template cache is set to false (default is true). --> <property name="cacheable" value="false"/> </bean> <bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"> <property name="templateResolver" ref="templateResolver"/> </bean> <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> <property name="viewClass" value="org.thymeleaf.spring4.view.ThymeleafView"/> <property name="templateEngine" ref="templateEngine"/> <property name="characterEncoding" value="utf-8"/> <!-- jsp 和 html 分別解析 --> <property name="viewNames" value="html/*"/> <!--order越小,優先級越高 --> <property name="order" value="1"/> </bean> <!-- 配置jsp顯示ViewResolver --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".jsp"/> <!-- jsp 和 html 分別解析 --> <property name="viewNames" value="jsp/*"/> <property name="order" value="2"/> </bean>
做用:api
// 其實返回的html視圖是/WEB-INF/html/welcome.html @RequestMapping(value = {"/welcome.html", "/"}, method = RequestMethod.GET) public String welcome() { return "html/welcome"; }
使用jsr303標準,而後加入緩存
// hibernate 數據校驗框架 compile group: 'org.hibernate', name: 'hibernate-validator', version: '5.1.0.Final'
Spring配置:
<!-- ====== 數據驗證配置 ===== --> <!-- 配置校驗器 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <!-- 校驗器,使用hibernate校驗器 --> <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/> <!-- 指定校驗使用的資源文件,在文件中配置校驗錯誤信息,若是不指定則默認使用classpath下面的ValidationMessages.properties文件 --> <property name="validationMessageSource" ref="messageSource"/> </bean> <!-- 校驗錯誤信息配置文件 國際化 --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <!-- 資源文件名 存放在classpath下 --> <property name="basename" value="i18n.i18n"/> <!-- 資源文件編碼格式 --> <property name="fileEncodings" value="utf-8"/> <!-- 對資源文件內容緩存時間,單位秒 --> <property name="cacheSeconds" value="120"/> </bean>
bean的設置
public class User { private Integer uid; // 這些屬性 能夠查看jsr303的文檔 // message裏的是驗證不經過時你要顯示的消息 // 我用了消息國際化顯示成這樣{message.username.length} 在上述配置中又 @NotBlank(message = "{message.username.length}") @Size(min = 1, message = "{message.username.length}") private String username; @NotBlank(message = "{message.password.length}") @Size(min = 8, message = "{message.password.length}") private String password; // no check private String email; // 省略getter setter }
驗證數據,只須要在要驗證的對象上加上@Validated註解,當驗證不經過時會把錯誤結果給到BindingResult中,按照需求給用戶進行提示。
@RequestMapping(value = "/doLogin", method = RequestMethod.POST) @ResponseBody public BaseDTO<User> doLogin(@Validated User user, BindingResult bindingResult){ if (bindingResult.hasErrors()) { StringBuilder stringBuilder = new StringBuilder(); for (FieldError fieldError : bindingResult.getFieldErrors()) { stringBuilder.append(fieldError.getDefaultMessage()).append(","); } stringBuilder.deleteCharAt(stringBuilder.length() - 1); throw new UserException(stringBuilder.toString(), 0); } return null; }
首先定義一個校驗分組:
public interface ValidGroupLogin { //接口中不須要定義任何方法,僅僅是對不一樣的校驗規則進行分組 //此分組只校驗用戶名和密碼 }
先是在bean上設好分組
@NotBlank(message = "{message.username.length}", groups = ValidGroupLogin.class)
而後在controller中使用
@Validated(groups = ValidGroupLogin.class)
Spring配置:
<!-- 國際化 --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <!-- 國際化信息所在的文件名 --> <property name="basename" value="i18n.i18n"/> <property name="defaultEncoding" value="utf-8"/> <!-- 若是在國際化資源文件中找不到對應代碼的信息,就用這個代碼做爲名稱 --> <property name="useCodeAsDefaultMessage" value="true"/> </bean> <!-- SessionLocalReslover --> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> <property name="defaultLocale" value="CHINESE"/> </bean> <!-- LocalChangeInterceptor --> <!-- 國際化操做攔截器 若是採用基於(請求/Session/Cookie)則必需配置 --> <mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/> </mvc:interceptors>
文件所在位置
文件的內容(一份中文,一份英文,一份默認也是中文),其中數據校驗也使用的這些個文件
中文:
Title=歡迎 Content=內容 Footer=歡迎捧場 GoHome=前往主頁 message.username.length=用戶名不能爲空 message.password.length=密碼要大於等於8位 message.uid.error=參數錯誤 message.uid.length=參數長度過長
英文:
Title=Welcome Content=content Footer=Welcome to join GoHome=Go message.username.length=username must be not null message.password.length=password more than 8 length must message.uid.error=param error message.uid.length=param so long
thymeleaf中能夠這樣使用:
<div class="container"> <div class="jumbotron"> <h1 class="font-effect-shadow-multiple" th:text="#{meaageTitle}"></h1> <p class="text-info" th:text="#{Content}"></p> <p class="text-warning" th:text="#{Footer}"></p> <a class="btn btn-primary btn-lg" href="/home.html" role="button" th:text="#{GoHome}"></a> </div> <div class="text-right"> <a class="btn btn-success" href="/welcome.html?locale=en_US">English</a> <a class="btn btn-success" href="/welcome.html?locale=zh_CN">中文</a> </div> </div>
老樣子Spring配置:
<!-- ===== 上傳和下載 ===== --> <!-- 上傳和下載 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 字節 --> <property name="maxUploadSize" value="104857600"/> <!-- 4096k --> <property name="maxInMemorySize" value="4096"/> </bean>
前端:
<form enctype="multipart/form-data" action="/uploadHeadImage" method="post"> <input type="hidden" name="username" value="${user_info.uid}"/> <input class="form-control" id="choosePic" type="file" name="file" value="選擇"/> <input class="btn btn-warning" type="submit" value="上傳"/> </form>
控制器層:multipartFile就是用戶上傳的文件,MultipartFile[] multipartFiles能夠變成多文件上傳。
@RequestMapping(value = "/uploadHeadImage", method = RequestMethod.POST) @ResponseBody public BaseDTO uploadHeadImage(@RequestParam("file") MultipartFile multipartFile, @RequestParam(value = "uid") Integer uid) { // 保存到指定路徑 multipartFile.transferTo(orginFile); return null; }
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.test.all.SuperSpringFilter"/> </mvc:interceptor> </mvc:interceptors>
攔截器類,繼承HandlerInterceptor:
public class SuperSpringFilter implements HandlerInterceptor { public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { // 權限校驗,不是全部人均可以操做數據庫 if ("/api/v2/text/checkText".equals(httpServletRequest.getRequestURI())) { if (httpServletRequest.getSession().getAttribute("uid").equals(1)) { } } httpServletRequest.setCharacterEncoding("UTF-8"); return true; } public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { if ("/welcome.html".equals(httpServletRequest.getRequestURI())) { httpServletResponse.setDateHeader("Expires", System.currentTimeMillis() + 2000 * 3600); } } public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
多個攔截器生命週期:
其實早在以前就用到了,仍是配置:
<!--<mvc:annotation-driven/> --> <!-- 像前面說過,這個配置 能夠自動配置JSON返回,就能夠不用那一大串代碼 --> <!--另外在SPringMVC3.0以後依賴的包版本(jackson)要在2.0版本之上才行--> <!-- 使用jackson處理 --> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> </list> </property> </bean> <!-- 其實默認的直接使用 <mvc:annotation-driven/> 就能夠了 由於json格式的數據默認就是utf-8編碼的 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter"/> </list> </property> </bean>
而後再須要返回json的地方加上@ResponseBody註解就能夠了
其實吧,直接用HttpServletResponse寫出數據流就好了,但既然用了框架,那就來折騰一下:
先在Spring設置一下返回字符串的編碼格式:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <!--json的編碼格式也是在這裏設置--> <!--<ref bean="mappingJacksonHttpMessageConverter"/>--> <ref bean="stringHttpMessageConverter"/> </list> </property> </bean> <!-- 返回數據的編碼方式 --> <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=utf-8</value> <value>application/json;charset=utf-8</value> </list> </property> </bean>
而後在Controller寫一個下載的方法:
@RequestMapping(value = "/download/{fileName}/", method = RequestMethod.GET) public ResponseEntity<byte[]> springDownload(@PathVariable String fileName, HttpServletRequest request) { String url = request.getSession().getServletContext().getRealPath("/") + "static/" + fileName; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentDispositionFormData("attachment", fileName); try { File file = new File(url); return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK); } catch (IOException e) { throw new RuntimeException(e); } }
首先確保類是被Spring掃描獲得的(@Componet),而後加上一個@EnableScheduling註解:
@Componet @EnableScheduling public class MeiZiTu { // 表示天天8點運行一次 @Scheduled(cron = "0 0 8 * * ?") public void upDay() { System.out.println("8點啦"); } }
一些表達式的例子:
CRON表達式 含義 "0 0 12 * * ?" 天天中午十二點觸發 "0 15 10 ? * *" 天天早上10:15觸發 "0 15 10 * * ?" 天天早上10:15觸發 "0 15 10 * * ? *" 天天早上10:15觸發 "0 15 10 * * ? 2005" 2005年的天天早上10:15觸發 "0 * 14 * * ?" 天天從下午2點開始到2點59分每分鐘一次觸發 "0 0/5 14 * * ?" 天天從下午2點開始到2:55分結束每5分鐘一次觸發 "0 0/5 14,18 * * ?" 天天的下午2點至2:55和6點至6點55分兩個時間段內每5分鐘一次觸發 "0 0-5 14 * * ?" 天天14:00至14:05每分鐘一次觸發 "0 10,44 14 ? 3 WED" 三月的每週三的14:10和14:44觸發 "0 15 10 ? * MON-FRI" 每一個周1、周2、周3、周4、週五的10:15觸發
@Configuration public class DruidConfiguration implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { DispatcherServlet servlet = new DispatcherServlet(); } @Bean public User user() { return new User(); } }
能被包掃描到:
@ControllerAdvice public class DefaultExceptionHandle { private static final Logger logger = LogManager.getLogger(DefaultExceptionHandle.class); // 通用異常 @ExceptionHandler(value = Exception.class) @ResponseBody public BaseDTO jsonErrorHandler(Exception e) { logger.error(e); return new BaseDTO(ResultEnums.UNKNOW_ERROR, false); } // 用戶異常 @ExceptionHandler(value = UserException.class) @ResponseBody public BaseDTO makeUserEception(UserException ue) { logger.error(ue); return new BaseDTO(ue.getStatus(), ue.getMessage(), false); } }
Spring AOP 處理Http請求的記錄:
Spring配置文件的使用:
<!-- ======= 啓用@AspectJ支持 ======= --> <!-- 爲了在Spring配置中使用@AspectJ切面, 你首先必須啓用Spring對@AspectJ切面配置的支持, 並確保自動代理(autoproxying)的bean是否能被這些切面通知。 自動代理是指Spring會判斷一個bean是否使用了一個或多個切面通知, 並據此自動生成相應的代理以攔截其方法調用,而且確保通知在須要時執行。 --> <aop:aspectj-autoproxy/>
使用:
@Aspect @Component public class HttpAspect { private static final Logger logger = LogManager.getLogger(HttpAspect.class); // Pointcut表示要切哪一個點, *表示全部方法, ..表示任意參數 // UserController下的全部方法進行aop攔截,這樣咱們能夠在方法的聲明週期中執行日誌記錄。 @Pointcut("execution(public * com.test.controller.base.UserController.*(..))") public void log() { } // Before表示在方法執行以前 @Before("log()") public void before(JoinPoint joinPoint) { // 記錄http請求,url,method,client-ip,請求的方法,請求的參數 logger.debug("Before====================="); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); logger.debug("url={}", request.getRequestURL()); logger.debug("method={}", request.getMethod()); logger.debug("ip={}", request.getRemoteAddr() + ":" + request.getRemoteHost()); // 類名 logger.debug("class_method={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); // 參數 logger.debug("class_args={}", joinPoint.getArgs()); } // After 表示在方法執行後 @After("log()") public void after() { logger.debug("After===================== \n\n"); } // 返回的值 @AfterReturning(returning = "o", pointcut = "log()") public void afterReturning(Object o) { logger.debug("response={}", o.toString()); } }