Spring Boot - 集成Web開發(自定義Servlet、Filter、Interceptor)

Spring Boot - 集成Web開發

 使用Spring Boot進行web開發十分簡單,其中包括常用的接口json返回、servlet、filters、interceptor、log等,在這之前我們已經瞭解了Spring Boot配置文件和基本的使用,接下來我們就來看一下Spring Boot是如何集成Web開發的。

1.Json接口開發

 我們在沒有使用Spring Boot之前首先需要引入jackjson對應的依賴,配置spring掃描,然後在對應Controller和接口上加上@Controller以及@ResponseBody註解,這是我們習慣開發接口的方式。現在利用Spring Boot去開發接口,只需保證接口類處於入口類掃描範圍內,Controller加上@RestController註解就可以提供一個接口服務。

application.properties
server.port=8080
server.servlet.context-path=/springboot-basic
User.java
package com.springboot.repository.entity;

/**
 * @author hzk
 * @date 2018/12/19
 */
public class User {

    private Integer id;

    private String name;

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

UserController .java
package com.springboot.controller;

import com.springboot.repository.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author hzk
 * @date 2018/12/19
 */
@RestController
public class UserController {

    @GetMapping(value = "/user/{id}/{name}")
    public User user(@PathVariable(name = "id") Integer id, @PathVariable("name") String name){
        return new User(id,name);
    }
}


SpringbootBasicApplication.java
package com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

/**
 * 所需掃描的類必須寫在啓動類同級或者子級目錄下
 * @author hzk
 * @date 2018/12/18
 */
@SpringBootApplication
public class SpringbootBasicApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootBasicApplication.class, args);
	}

}

 簡單幾步就完成了一個Json接口開發,請求http://localhost:8080/springboot-basic/user/88/jack可以看到和我們預期效果一致。

運行結果
{
	id: 88,
	name: "jack"
}

2.集成使用Jsp

 通常有些項目採用jsp作爲視圖開發,在沒有使用Spring Boot之前我們需要在spring的xml配置文件中配置對應的視圖屬性,這裏我們也需要配置spring mvc的視圖屬性,不過通過簡單的配置就能基本實現jsp的集成使用。

application.properties
#激活加載application-x.properties文件 此時取application-qa.properties 該命名格式約定俗成
#若application.properties主配置文件和激活配置文件中存在相同配置屬性,則優先取激活配置文件中屬性,例:主配置文件和激活配置文件中都配置了啓動端口號,則使用激活配置文件中端口號
spring.profiles.active=qa

#spring.http.encoding.charset=UTF-8
#spring.http.encoding.force=true
#spring.http.encoding.enabled=true

#配置mvc視圖
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
ViewController.java
package com.springboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author hzk
 * @date 2018/12/19
 */
@Controller
public class ViewController {

    @RequestMapping(value = "/index")
    public String index(Model model){
        model.addAttribute("msg","spring boot集成jsp");
        return "index";
    }
}

index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
${msg}
</body>
</html>

 請求http://localhost:8080/springboot-basic/index就能跳轉到我們的index頁面,和沒有使用Spring Boot時效果一樣。

3.自定義Servlet

 在我們使用Spring框架之後很少再去自己編寫Servlet服務,但是這裏我們也介紹一下在Spring Boot下如何去編寫實現一個Servlet服務,這裏主要有兩種實現方式,首先我們看下目錄結構。

- java
	- com
		- springboot
			- config
				- ServletConfig.java
			- servlet
				- MyServlet.java
				- MyServlet2.java
			- SpringbootApplication.java
- resources
3.1 使用Servlet3註解實現

 首先我們要編寫一個Servlet服務,並且添加上@WebServlet註解。

MyServlet.java
package com.springboot.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 第一種方式@WebServlet註解加上主程序@ServletComponentScan註解掃描
 * @author hzk
 * @date 2018/12/26
 */
@WebServlet(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().print("my servlet doGet ..");
        resp.getWriter().flush();
        resp.getWriter().close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

 編寫好了Servlet服務之後,只需在入口類上配置Servlet掃描@ServletComponentScan,訪問http://localhost:8080/springboot-basic/myServlet即可。

SpringbootBasicApplication.java
package com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

/**
 * 所需掃描的類必須寫在啓動類同級或者子級目錄下
 * @author hzk
 * @date 2018/12/18
 */
@SpringBootApplication
@ServletComponentScan(basePackages = "com.springboot.servlet")
public class SpringbootBasicApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootBasicApplication.class, args);
	}

}
3.2 編寫Servlet配置類

 同樣我們要先編寫一個Servlet服務。

MyServlet2.java
package com.springboot.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 第二種方式添加ServletConfig自定義配置文件 使用springboot提供的方式手動註冊servlet
 * @author hzk
 * @date 2018/12/26
 */
public class MyServlet2 extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().print("my servlet2 doGet..");
        resp.getWriter().flush();
        resp.getWriter().close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

 編寫好Servlet服務後,在config包下新建一個ServletConfig配置類用於註冊Servlet,需加上@Configuration註解纔有效。

ServletConfig.java
package com.springboot.config;

import com.springboot.filter.MyFilter2;
import com.springboot.servlet.MyServlet2;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;

import javax.servlet.Filter;

/**
 * springboot不使用xml文件,@Configuration可以替代xml配置文件
 * 該方式類似於添加xml文件配置
 * @author hzk
 * @date 2018/12/26
 */
@Configuration
public class ServletConfig {

    @Bean
    public ServletRegistrationBean<MyServlet2> myServletRegistrationBean(){
        return new ServletRegistrationBean<>(new MyServlet2(), "/myServlet2");
    }
}

 配置完之後啓動,訪問http://localhost:8080/springboot-basic/myServlet2即可。

4.自定義Filter過濾器

 我們在項目中經常會使用filter去做一些公共處理,比如跨域請求、排除有XSS威脅的字符、權限驗證等等。我們會根據業務需求自定義Filter,和Servlet的實現方式一樣有兩種,我們看一下目錄結構。

- java
	- com
		- springboot
			- config
				- ServletConfig.java
			- filter
				- MyFilter.java
				- MyFilter2.java
			- SpringbootApplication.java
- resources
4.1 使用@WebFilter註解實現

 首先我們要編寫一個自定義Filter類去實現Filter接口重寫其中doFilter方法,然後使用@WebFilter註解配置過濾器過濾範圍

MyFilter.java
package com.springboot.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * 第一種方式@WebFilter註解加上主程序@ServletComponentScan註解掃描
 * @author hzk
 * @date 2018/12/26
 */
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("MyFilter into...");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

 通過這種方式編寫的自定義Filter過濾器,需要在入口類上配置Servlet掃描@ServletComponentScan,和之前Servlet第一種實現一樣,若配置多個可以用逗號分隔。

SpringbootBasicApplication.java
package com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

/**
 * 所需掃描的類必須寫在啓動類同級或者子級目錄下
 * @author hzk
 * @date 2018/12/18
 */
@SpringBootApplication
@ServletComponentScan(basePackages = {"com.springboot.servlet","com.springboot.filter"})
public class SpringbootBasicApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootBasicApplication.class, args);
	}

}
4.2 編寫Filter配置類

 同第一種一樣我們要編寫一個自定義Filter,區別是這裏不需要加上@WebServlet註解。

MyFilter2.java
package com.springboot.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * 第二種方式添加ServletConfig自定義配置文件 使用springboot提供的方式手動註冊filter
 * @author hzk
 * @date 2018/12/26
 */
public class MyFilter2 implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("MyFilter2 into...");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

 編寫好自定義Filter過濾器後,編寫一個配置類,我們使用開始的ServletConfig,通過實例化FilterRegistrationBean便可以將MyFilter2這個過濾器註冊到Spring容器中加入到過濾器鏈中。

ServletConfig.java
package com.springboot.config;

import com.springboot.filter.MyFilter2;
import com.springboot.servlet.MyServlet2;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;

import javax.servlet.Filter;

/**
 * springboot不使用xml文件,@Configuration可以替代xml配置文件
 * 該方式類似於添加xml文件配置
 * @author hzk
 * @date 2018/12/26
 */
@Configuration
public class ServletConfig {

    @Bean
    public ServletRegistrationBean<MyServlet2> myServletRegistrationBean(){
        return new ServletRegistrationBean<>(new MyServlet2(), "/myServlet2");
    }

    @Bean
    public FilterRegistrationBean<MyFilter2> myFilterRegistration(){
        FilterRegistrationBean<MyFilter2> filterRegistrationBean = new FilterRegistrationBean<>(new MyFilter2());
        String[] urlPatterns = {
                "/*"
        };
        filterRegistrationBean.addUrlPatterns(urlPatterns);
        return filterRegistrationBean;
    }

    /**
     * 等同web.xml配置CharacterEncodingFilter
     * 注意:若想使用自定義編碼過濾器,則spring.http.encoding.enabled=false纔可生效
     * @return
     */
   /* @Bean
    public FilterRegistrationBean<CharacterEncodingFilter> filterRegistrationBean(){
        FilterRegistrationBean<CharacterEncodingFilter> filterFilterRegistrationBean = new FilterRegistrationBean<CharacterEncodingFilter>();
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);
        filterFilterRegistrationBean.setFilter(characterEncodingFilter);
        filterFilterRegistrationBean.addUrlPatterns("*//*");
        return filterFilterRegistrationBean;
    }*/

}

5.自定義Interceptor攔截器

 我們知道Filter和Interceptor表面上比較有很多相似之處,但是也有十分大的差異化。比如Filter依賴Servlet容器而Interceptor則不需要、Filter基於函數回調而Interceptor基於反射、Filter不可以使用Spring容器資源而Interceptor可以等,具體細節差異各位可以查詢書籍或者網上的資料。這裏可以參考這幾篇博客內容,寫的通俗易懂也比較詳細。
過濾器(Filter)和攔截器(Interceptor)的區別-xiaodanjava
過濾器(Filter)和攔截器(Interceptor)的區別-軍子
SpringMVC的攔截器(Interceptor)和過濾器(Filter)的區別與聯繫
 在瞭解過Interceptor攔截器後我們來看下Spring Boot中我們應該如何去實現,這裏照例給出目錄結構。

- java
	- com
		- springboot
			- config
				- WebConfig.java
			- interceptor
				- AuthInterceptor.java
			- SpringbootApplication.java
- resources

 我們需要先編寫一個自定義interceptor去實現HandlerInterceptor重寫其中的方法,這裏我們定義一個AuthInterceptor權限驗證攔截器。

AuthInterceptor.java
package com.springboot.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 權限驗證攔截器
 * @author hzk
 * @date 2018/12/26
 */
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("AuthInterceptor into...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

 要使這個攔截器生效,我們需要有一個自定義配置類去註冊攔截器,該類需要繼承WebMvcConfigurationSupport 實現addInterceptors方法將自定義攔截器註冊Web容器中。

WebConfig.java
package com.springboot.config;

import com.springboot.interceptor.AuthInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * @author hzk
 * @date 2018/12/26
 */
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {

        //需要攔截的路徑
        String[] pathPatterns = {
                "/api/**"
        };
        //需要放行的路徑
        String[] excludePathPatterns = {
                "/api/config"
        };

        registry.addInterceptor(new AuthInterceptor()).addPathPatterns(pathPatterns).excludePathPatterns(excludePathPatterns);
    }
}

6.RESTFull實現

 首先在實現RESTFull之前,我們需要弄明白什麼是RESTFull?

  1. RESTFULL是一種互聯網軟件架構設計風格,並不是標準,只是提出了一組客戶端和服務器交互時的架構理念和設計原則,基於該理念和原則設計的接口可以更簡潔,更有層次
  2. 如果一個架構符合REST原則,就稱之爲RESTFull架構
    比如我們訪問一個http接口:http://localhost:8080/api/user?id=88&name=jack
    採用RESTFull風格則http地址爲:http://localhost:8080/api/user/88/jack

 我們用Spring Boot實現RESTFull風格接口時,主要有一個核心的註解@PathVariable,這個註解可以幫我們獲取url中的數據並且設定接口風格,例如我們下面這個接口就簡單實現了RESTFull風格接口的開發。

UserController.java
package com.springboot.controller;

import com.springboot.repository.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author hzk
 * @date 2018/12/19
 */
@RestController
public class UserController {

    @GetMapping(value = "/user/{id}/{name}")
    public User user(@PathVariable(name = "id") Integer id, @PathVariable("name") String name){
        return new User(id,name);
    }
}

 這個接口的請求地址我們用http://localhost:8080/user/88/jack就可以請求成功並且返回正確的數據,相比我們以前的請求方式更加簡便,我們還可以通過@PostMapping@PutMapping@DeleteMapping註解控制接口的請求方式,這些知識RESTFull風格的冰山一角,更多細節的東西需要大家自己根據所需去了解。

7.熱部署插件

 在我們實際開發中,我們每次修改了代碼邏輯功能或者頁面調整都需要去重啓我們的應用,從而無形中降低了我們開發的效率,但是Spring Boot給我們提供了一個熱部署的插件,加入了這個插件之後當我們代碼進行了修改之後無需重新啓動就會自動加載新的內容。加入熱部署插件很簡單,只需要我們加入一個依賴,但是需要注意的是實際使用中可能會出現一些問題,比如有時候無法生效,仍然需要手動重啓服務。

pom.xml
<!-- springboot 熱部署-->
<!--實際開發中,我們修改代碼邏輯需要重啓應用,無形降低了開發效率。我們可以通過熱部署插件,服務會自動加載新的內容,大大提高了開發效率,
實際使用中可能會出現一些問題,比如有時候無法生效,仍然需要手動重啓服務。-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-devtools</artifactId>
	<optional>true</optional>
</dependency>

8.SpringBoot默認日誌LOGO關閉

 當我們每次啓動Spring Boot項目的時候,我們都會看到日誌中出現下面這個logo。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.1.RELEASE)

 雖然這個logo的可有可無對項目都沒有任何影響,但是可能考慮到有些同學強迫症不想要看到這個logo,Spring Boot的開發團隊也是考慮地十分細心,給我們提供了方法可以將他關閉,只需要在入口類啓動項目的時候稍作修改就可以關閉這個logo在日誌中輸出。

 public static void main( String[] args )
    {
        SpringApplication springApplication = new SpringApplication(SpringbootJavaApplication.class);
		springApplication.setBannerMode(Banner.Mode.OFF);
		springApplication.run(args);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章