花了点时间做的(比较水)笔记,有可能有漏洞,有不对的,欢迎指出(如果你会看的话)。

首先都是二话不说,先找依赖(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());
    }

}

喵了个咪
171 声望6 粉丝

backend,初音,舰娘.