Spring Boot入門(四)自動配置

Spring Boot自動配置是一個運行時的過程,考慮衆多因素,決定Spring配置應該使用哪個,而不該用哪個。

Spring Boot自動配置需要考慮:
Spring的JdbcTemplate是否在Classpath中?若有,並且有DataSource的Bean,則自動配置一個JdbcTemplate的Bean。
Thymeleaf是否在Classpath中?若有,則配置Thymeleaf的模板解析器、視圖解析器以及模板引擎。
Spring Security是否在Classpath中?若有,則進行一個基本的Web安全設置。
每當應用程序啓動時,Spring Boot的自動配置都會進行重新配置。

專注應用程序功能

  1. 定義領域模型
    簡單模型,屬性+get/set
import javax.persistence.*;
@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String reader;
    private String isbn;
    private String title;
    private String author;
    private  String description;

    public Long getId() {
        return id;
    }

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

    public String getReader() {
        return reader;
    }

    public void setReader(String reader) {
        this.reader = reader;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

  1. 定義倉庫接口
    將實體類對象持久化到數據庫的倉庫當中
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ReadingListRepository extends JpaRepository<Book, Long> {
List<Book> findByReader(String reader);//用戶自定義方法
}

因爲使用了Spring Data JPA,所以只需定義一個接口,擴展Spring Data JPA的JpaRepository接口。
通過接口擴展,ReadingListRepository直接繼承了多個執行常用持久化操作的方法。當然,繼承的接口,Spring Data在應用程序啓動後,該接口在運行時會自動實現。

  1. 創建web界面
    使用Spring MVC處理HTTP請求:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.List;

@Controller
@RequestMapping("/")
public class ReadingListController {
    private ReadingListRepository readingListRepository;

    @Autowired
    public ReadingListController(ReadingListRepository readingListRepository) {
        this.readingListRepository = readingListRepository;
    }

    @RequestMapping(value = "/{reader}", method = RequestMethod.GET)
    public String readersBooks(@PathVariable("reader") String reader, Model model) {
        //根據{reader}從倉庫取出Book列表
        List<Book> readingList = readingListRepository.findByReader(reader);
        if (readingList != null) {
            //用books鍵將列表存放在model中(model爲一個Map)
            model.addAttribute("books", readingList);
        }
        //返回"readingList"作爲視圖名
        return "readingList";
    }

			
	//將正文數據綁定到一個Book對象上,將該對象的reader屬性設置爲讀者姓名
	//通過倉庫的save()保存修改後的Book對象,最後進行重定向
	@RequestMapping(value="/{reader}", method=RequestMethod.POST)
	@ResponseBody
	public String addToReadingList(@PathVariable("reader") String reader, @ResponseBody Book book) {
						
				book.setReader(reader);
				readingListRepository.save(book);
				return "redirect:/{reader}";
	}
}

@Controller:掃描並自動將其處理器(此處爲ReadingListController)註冊爲Spring應用程序上下文中的一個Bean。
@RequestMapping:將處理器的方法映射到URL路徑上

呈現閱讀列表的Thymeleaf模板:
在src/main/resources/templates裏創建一個名爲readingList.html的文件

<html>
	<head>
		<title>Reading List</title>
		<link rel="stylesheet" th:href="@{/style.css}"></link>
	</head>
	<body>
		<h2>Your Reading List</h2>
		<div th:unless="${#lists.isEmpty(books)}">
			<dl th:each="book : ${books}">
				<dt class="bookHeadline">
					<span th:text="${book.title}">Title</span> by
					<span th:text="${book.author}">Author</span>
					(ISBN: <span th:text="${book.isbn}">ISBN</span>)
				</dt>
				<dd class="bookDescription">
					<span th:if="${book.description}"
					th:text="${book.description}">Description</span>
					<span th:if="${book.description eq null}">
					No description available</span>
				</dd>
			</dl>
		</div>
		<div th:if="${#lists.isEmpty(books)}">
			<p>You have no books in your book list</p>
		</div>
		<hr/>
		<h3>Add a book</h3>
		<form method="POST">
			<label for="title">Title:</label>
			<input type="text" name="title" size="50"></input><br/>
			<label for="author">Author:</label>
			<input type="text" name="author" size="50"></input><br/>
			<label for="isbn">ISBN:</label>
			<input type="text" name="isbn" size="15"></input><br/>
			<label for="description">Description:</label><br/>
			<textarea name="description" cols="80" rows="5">
			</textarea><br/>
			<input type="submit"></input>
		</form>
		</body>
</html>

該頁面模板有兩部分組成:上方是讀者的閱讀列表中的圖書清單,下方是一個表單,讀者可添加新書。

另外,引入的style.css文件,位於src/main.resources/static中:

body {
background-color: #cccccc;
font-family: arial,helvetica,sans-serif;
}
.bookHeadline {
font-size: 12pt;
font-weight: bold;
}
.bookDescription {
font-size: 10pt;
}
label {
font-weight: bold;
}

省略數據庫配置(在配置文件中配相應的DataSource)
綜上就是一個完整的web應用程序了。

在嚮應用程序加入Spring Boot時,有個名爲spring-boot-autoconfigure的jar文件,包含了很多的配置類。每個配置類都在應用程序的Classpath裏。這些配置類有用於Spring Data JPA的配置,有用於Spring MVC的配置,和其他配置。用戶可以選擇是否要使用他們。
這些配置文件採用條件配置(Spring4.0新特性),允許配置存在於應用程序中,但在滿足特定條件前忽略該配置。
在Spring中要編寫自己的條件,只需實現Condition接口,並覆蓋它的matches()方法。
如,下面的條件類只有在Classpath裏存在JdbcTemplate時纔會生效:

public class jdbcTemplateConditon implements Conditon{
	@Override
	public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata){
			try{
					context.getClassLoader().loadClass("org.springframework.jdbc.core.JdbcTemplate");
					return true;
			}catch(Exception e){
					return false;
			}
	}
}

當用Java來聲明Bean時,可以使用這個自定義條件類:

@Conditional(JdbcTemplateConditon.class)
public MyService myService{
	...
}

在該例子中,只有當JdbcTemplateCondition類條件成立時,纔會創建MyService這個Bean,即MyService Bean創建的條件是Classpath裏有JdbcTemplate。否則,這個Bean聲明將被忽略。
在Spring Boot的自動配置類中,DataSourceAutoConfiguration添加了@Configuration註解,從其他配置類裏導入了一些額外配置,還自定義了一下Bean。另外,添加了@ConditionalOnClass註解,要求Classpath裏必須要有DataSource和EmbeddedDatabaseType。若他們不存在,DataSourceAutoConfiguration提供的配置會都被忽略。
DataSourceAutoConfiguration內嵌了JdbcTemplateConfiguration類,自動配置了一個JdbcTemplate Bean。
JdbcTemplateConfiguration使用了@Conditional註解,判斷DataSourceAvailableCondition條件是否成立–是否有一個DataSource Bean或自動配置創建一個。假設有DataSource Bean,使用了@Bean註解的jdbcTemplate()方法會配置一個JdbcTemolate Bean。另外使用了@ConditionalOnMissingBean註解,因此,只有在不存在JdbcOperations(JdbcTemplate實現接口)類型的Bean時,會創建JdbcTemplate Bean。
以上,說明了Spring Boot如何利用條件化配置實現自動配置。

自動裝配會做出一下決策:(以上述例子爲例)
因爲Classpath裏有H2,所以會創建一個嵌入式H2數據庫Bean,他的類型爲javax.sql.DataSource,JPA實現(Hibernate)需要它來訪問數據庫。
因爲Classpath裏有Hibernate(Spring Data JPA傳遞引入的)的實體管理器,所以自動配置會配置與Hibernate相關的Bean,包括Spring的LocalContainerEntityManagerFactoryBean和JpaVendorAdapter。
因爲Classpath裏有Spring Data JPA,所以他會自動配置爲根據倉庫的接口創建倉庫實現。
因爲Classpath裏有Spring Data JPA,所以它會自動配置爲Spring MVC的視圖,包括一個Thymeleaf的模板解析器、模板引擎及視圖解析器。視圖解析器會解析相對於Classpath根目錄/templates目錄裏的模板。
因爲Classpath裏有Spring MVC,所以會配置Spring的DispatcherServlet並啓用Spring MVC。
因爲這時一個Spring MVC Web應用,所以會註冊一個資源處理器,把相對於Classpath根目錄/static目錄下的靜態內容提供出來。
因爲Classpath裏有Tomcat(通過web起步依賴傳遞),所以會啓動一個嵌入式Tomcat容器。


參考文獻:Spring Boot實戰 ,丁雪豐 (譯者)

發佈了32 篇原創文章 · 獲贊 9 · 訪問量 8277
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章