花了点时间做的(比较水)笔记,有可能有漏洞,有不对的,欢迎指出(如果你会看的话)。
首先都是二话不说,先找依赖(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格式):
<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>
在此之前可以先了解一下这两个配置,基本上是必备的,帮我们做了很多事情。
<!-- 配置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"/>
然后就可以尽情的使用了。
自动属性装配
/**
* 这里的属性自动装配,可以装配很多东西,比如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的支持。
@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>
作用:
// 其实返回的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 {
}
}
多个拦截器生命周期:
返回JSON数据
其实早在之前就用到了,还是配置:
<!--<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" 每个周一、周二、周三、周四、周五的10:15触发
使用java类配置Servlet,Spring
@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();
}
}
全局异常捕获,返回json数据
能被包扫描到:
@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);
}
}
AspectJ支持
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());
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。