SpringBoot是快速開發企業應用的一條捷徑,尤其是在Spring4以後,項目完全不需要xml配置以來,讓開發者感到巨大的流暢感。
我們使用Eclipse搭建一個基於maven的SpringBoot項目,不瞭解的可以看一下《用maven搭建springboot環境》。
新建maven項目,把需要的源包都建好,可以刪掉web.xml文件,然後打開pom文件。這裏我們的依賴要比上面文章裏的多一些。
首先刪除自動生成的Junit依賴,我們的單元測試雖然也是Junit,不過SpringBoot會自動添加依賴。
然後按照上面那篇文章添加parent
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.2.6.RELEASE</version> </parent>
這裏使用的是最新的126發佈版,所以不需要像文章中提到的那樣增加下面的庫
<!-- 使用 Spring repositories --> <!-- (我們使用的是SNAPSHOT版本,如果用RELEASE版本下面的偶不用寫) --> <repositories> <repository> <id>spring-snapshots</id> <url>http://repo.spring.io/snapshot</url> <snapshots><enabled>true</enabled></snapshots> </repository> <repository> <id>spring-milestones</id> <url>http://repo.spring.io/milestone</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <url>http://repo.spring.io/snapshot</url> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <url>http://repo.spring.io/milestone</url> </pluginRepository> </pluginRepositories>
接下來添加我們這裏需要的幾個依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.hateoas</groupId> <artifactId>spring-hateoas</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
第一個是SpringBoot的單元測試依賴;第二個是最主要的,用來開發webapp的web依賴;第三個是SpringBoot開發Restful服務的HATEOAS依賴;第四個是管理員使用的接口依賴。
然後我們新建一個Model
public class Greeting extends ResourceSupport {
private long gid;
private String content;
@JsonCreator
@JsonIgnoreProperties(ignoreUnknown = true)
public Greeting(@JsonProperty(value = "gid") long gid, @JsonProperty(value = "content") String content) {
this.gid = gid;
this.content = content;
}
這裏只列出了類定義和構造器,你需要自己添加getter和setter方法。
這裏的註解不是必須的,你可以都刪掉。他們的作用可以自己看一下javadoc。
然後建一個controller
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
@RestController
public class GreetingController {
private static final String template = "Hello, %s";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "world") String name) {
Greeting greeting = new Greeting(counter.incrementAndGet(), String.format(template, name));
GreetingController methodOn = methodOn(GreetingController.class);
// GreetingController methodOn = DummyInvocationUtils.methodOn(GreetingController.class);
Greeting greeting2 = methodOn.greeting(name);
ControllerLinkBuilder linkTo = linkTo(greeting2);
Link withSelfRel = linkTo.withSelfRel();
greeting.add(withSelfRel);
return greeting;
}
@RequestMapping("/show")
public Greeting show(){
RestTemplate template = new RestTemplate();
Greeting greeting = template.getForObject("http://localhost:8080/greeting?name=323", Greeting.class);
System.err.println(greeting);
return greeting;
}
}
@restController註解表明裏面的每個action都返回的不是view。
然後我們使用內置的Tomcat服務器部署,這個服務器的性能和你本機下載的綠色版是一樣的
@SpringBootApplication
public class Application implements CommandLineRunner{
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(Application.class, args);
System.out.println("hohoho");
String[] names = context.getBeanDefinitionNames();
Arrays.sort(names);
for (String string : names) {
System.err.println(string);
}
}
@Override
public void run(String... args) throws Exception {
RestTemplate template = new RestTemplate();
Greeting greeting = template.getForObject("http://localhost:8080/greeting?name=323", Greeting.class);
System.err.println(greeting);
}
}
接下來我們分步驗收我們的成果(就算成果吧)。
首先運行main方法,在瀏覽器裏訪問http://localhost:8080/greeting,你應該能看到你想看到的;然後訪問http://localhost:8080/greeting?name=WhoAmI,應該和你預料的也一樣,都是返回的JSON串。每個串裏都有當前的訪問路徑,這就是HATEAOS的作用,人們認爲調用服務的時候路徑拼接不對很影響開發效率就非要加上這個。
我們已經驗證了web依賴和hateaos依賴工作正常,接下來看一下actuator。不知道你注意到沒有,應用啓動的時候輸出了很多類似
Mapped "{[/greeting],methods=[GET]}" onto ...
Mapped "{[/env],methods=[GET]}" onto ...
Mapped "{[/info],methods=[GET]}" onto ...
Mapped "{[/health],methods=[GET]}" onto ...
的東西,是程序輸出的而非我們打印的。
也行看到第一行你就會明白我們可以嘗試訪問http://localhost:8080/env 、 http://localhost:8080/health 、 http://localhost:8080/info等路徑。actuator提供了很多管理員工具,更多的可以去官網瞭解。
接下來看一下單元測試。
在我們的GreetingController上面右鍵-新建-JUnit Test Case,把測試類的目錄改到src/test/java下面。然後修改裏面的代碼,因爲我們用不到它默認的測試方法
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
public class GreetingControllerTest {
private MockMvc mvc;
@Before
public void setUp() {
mvc = MockMvcBuilders.standaloneSetup(new GreetingController()).build();
}
@Test
public void getHello() throws Exception {
ResultActions actions = mvc.perform(MockMvcRequestBuilders.get("/greeting").accept(MediaType.APPLICATION_JSON));
actions.andExpect(status().isOk());
// actions.andExpect(content().string(equalTo("Hello world")));
}
}
這裏面用到了幾個靜態導入,eclipse對靜態導入的處理簡直令人髮指,你可以copy進去:
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
運行它,應該可以通過。然後把最下面一行的註釋去掉,也就是增加一個新斷言,認爲返回的內容是Hello world。這次一定通不過了。
最後我們看一下主運行文件裏面的那個run方法
@Override
public void run(String... args) throws Exception {
RestTemplate template = new RestTemplate();
Greeting greeting = template.getForObject("http://localhost:8080/greeting?name=323", Greeting.class);
System.err.println(greeting);
}
看到上面的複寫註解就知道它來自哪裏了是吧,沒錯,就是實現的那個接口CommandLineRunner(所以完全可以不實現)。實現了這個接口的類(需要是一個bean,不過@SpringBootApplication註解有這個功能)在成功運行後會調用這個run方法。
我們在run中把返回的json重新組裝成Greeting實例。爲了更清晰,你最好給Greeting類增加
@Override public String toString()
這個能成功的關鍵是構造器的參數中使用了@JsonProperty註解,並且使用了value參數。你可以刪掉試一下效果。
既然是web service,所以我們的最後一步就是打開CORS訪問,不然異步訪問不了。我不太明白爲什麼一個隨時隨地可以同步訪問的地址在異步的時候非要限制。新建一個FIlter
class SimpleCORSFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
這個類實現了javax.servlet.Filter接口。既然我們沒有了web.xml文件,怎麼才能讓它攔截請求呢?我們可以給這個類加上自動配置的註解,比如org.springframework.stereotype.Component或org.springframework.stereotype.Repository。