SpringMVC 配置和基本使用

花了點時間作的(比較水)筆記,有可能有漏洞,有不對的,歡迎指出(若是你會看的話)。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 {

    }
}

多個攔截器生命週期:

返回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"    每一個周1、周2、周3、周4、週五的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());
    }

}
相關文章
相關標籤/搜索