花了点时间作的(比较水)笔记,有可能有漏洞,有不对的,欢迎指出(若是你会看的话)。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()); } }