SpringBoot初尝试

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
通过这种方式,Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
简单说就是为了实现免 XML 配置的开发体验,让开发者不在需要编写 XML 配置文件。

进一步了解

都知道 Spring 框架功能很强大,项目中大多会使用吧,它带来方便的同时,也有一个麻烦的事,就算创建是一个很简单的项目,我们也要配置很多东西,写一堆的配置文件。
因此就有了 Spring Boot 框架,它的作用很简单,就是帮我们自动配置。Spring Boot 框架的核心就是自动配置,只要存在相应的 jar 包,Spring 就帮我们自动配置。
如果默认配置不能满足需求,我们还可以替换掉自动配置类,使用我们自己的配置。
另外,Spring Boot 还集成了嵌入式的 Web 服务器(所以可以脱离 Tomcat 来运行),系统监控等很多有用的功,让我们快速构建企业及应用程序。

从名字也可以看出,它就是一个启动 spring 项目的一个工具而已,从最根本上来讲,Spring Boot 就是一些库的集合,它能够被任意项目的构建系统所使用。简便起见,该框架也提供了命令行界面,它可以用来运行和测试 Boot 应用。

可以说 Spring 的整个生态系统都使用到了 Groovy 语言,感兴趣的可以了解下
Spring Boot 不是一门新技术。从本质上来说,Spring Boot 就是 Spring,它做了一些对 Spring Bean 的默认配置。

为什么会出现

以前在写 spring 项目的时候,要配置各种 xml 文件,还记得曾经被 ssh 框架支配的恐惧。随着spring3,spring4的相继推出,约定大于配置逐渐成为了开发者的共识,大家也渐渐的从写 xml 转为写各种注解,在spring4的项目里,你甚至可以一行xml都不写。
虽然spring4已经可以做到无xml,但写一个大项目需要茫茫多的包,maven 配置要写几百行,也是一件很可怕的事。
现在,快速开发一个网站的平台层出不穷,nodejs,php 等虎视眈眈,并且脚本语言渐渐流行了起来(Node JS,Ruby,Groovy,Scala等),spring 的开发模式越来越显得笨重。
在这种环境下,spring boot 伴随着 spring4 一起出现了。

可以做什么

spring boot 并不是一个全新的框架,它不是 spring 解决方案的一个替代品,而是 spring 的一个封装。所以,你以前可以用 spring 做的事情,现在用 spring boot 都可以做。
现在流行微服务与分布式系统,springboot 就是一个非常好的微服务开发框架,你可以使用它快速的搭建起一个系统。同时,你也可以使用 spring cloud(Spring Cloud是一个基于Spring Boot实现的云应用开发工具)来搭建一个分布式的网站。

  • 自动配置:针对很多Spring应用程序常见的应用功能,Spring Boot 能自动提供相关配置
  • 起步依赖:告诉Spring Boot需要什么功能,它就能引入需要的库。
  • 命令行界面:这是Spring Boot的可选特性,借此你只需写代码就能完成完整的应用程序,无需传统项目构建。
  • Actuator:让你能够深入运行中的Spring Boot应用程序,一套究竟。

小总结

Spring Boot 是这几年 微服务 概念流行后,Spring 开发的一套快速开发 Spring 应用的框架(它非常年轻)。
它本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序(开箱即用,快速启动)。
也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的 Spring Boot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。

从概念上说,Spring Boot 需要使用到 Spring 框架的各个部分,并且对它们进行了大量的默认约定配置

PS:当你用过 SpringMVC 后再使用 Spring Boot 才能体会到是多么的幸福啊~~

spring boot 相当于腾讯的 Wegame,里面装了自家的各种游戏如 spring data 、spring mvc……
你通过 Wegame 可以轻松 快速 安装、使用(引入、整合) Wegame 旗下的游戏

使用Java配置

一般来说 Spring 中,IOC 推荐使用注解;AOP 推荐使用配置文件来配置;
到了 spring4.x+ 或者 springBoot 时代,官方则推荐使用 Java 配置的方式,可完全替代 XML。

使用 Java 配置最重要的就是两个注解: @Configuration 和 @Bean 注解
带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。
@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean
简单说就是:@Configuration 注解相当于我们之前 Spring 的 XML 配置文件;@Bean 就相当于我们在 XML 中配置的 bean 标签。

1
2
3
4
5
6
7
8
9
@Configuration
// 默认会扫描配置类所在的包
@ComponentScan(basePackages={"com.bfchengnuo.service", "com.bfchengnuo.dao"})
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}

这样就完全不用写配置文件了!上面的配置等同于:

1
2
3
<beans>
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>

注意 id 的名字哦,然后获取 IOC 容器就不是用 ClassPathXmlApplicationContext 而是 AnnotationConfigApplicationContext :

1
2
3
4
5
6
7
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(HelloWorldConfig.class);
HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
helloWorld.setMessage("Hello World!");
helloWorld.getMessage();
}

如果想要在创建对象的时候注入一些参数,那就更简单了,直接用相应的构造函数 new 出来就行了,或者 set 设置,总之你返回的那个对象有相应的属性值就够了,引用其他 bean 也会了吧,这样一来一般是不会写错名字的。

另外还可以使用 @import 注解允许从另一个配置类中加载 @Bean 定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
/*******************分割线******************************/
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}

这样在 AnnotationConfigApplicationContext 只需要传入一个 ConfigB.class 就够了,A 和 B 都可以获取到,其实和 import 标签差不多。
另外,其中也是可以使用 @Autowired 进行注入的,如果涉及到跨类(相当于跨配置文件)的引用,也可以用这个来解决,不要忘了扫描包就好了。
还可以使用 @ImportResource 注解来导入配置文件,达到混用的目的;然后还可以使用 @PropertySource 注解来引入外部配置文件,比如最常用的:@PropertySource(value= {"classpath:jdbc.properties"}) ,然后在 Java 代码中的属性上使用 @Value("${jdbc.url}") 来注入即可

因为使用 @Configuration 注解的类本身也是一个Bean,因为 @Configuration 被 @Component 注解了,因此 @Configuration 注解可以指定 value 属性值,如 “ctxConfig” 就是该Bean的名字,如使用 ctx.getBean("ctxConfig") 将返回该 Bean。

@Bean 注解的属性:
name:指定 Bean 的名字,可有多个,第一个作为 Id,其他作为别名,默认不指定的时候使用方法名作为 ID;
autowire:自动装配,默认 no 表示不自动装配该 Bean,另外还有 Autowire.BY_NAME 表示根据名字自动装配,Autowire.BY_TYPE 表示根据类型自动装配;
initMethod和destroyMethod:指定 Bean 的初始化和销毁方法。
使用 @Bean 注解的方法不能是 private、final 或 static 的

在 Spring Boot项目中推荐使用 @SpringBootConfiguration 替代 @Configuration

入门

官方建议我们使用 Maven 或者 Gradle 来构建项目,然后这里就先用 Maven 了。
虽然 SpringBoot 可以零配置,但是还是建议写一个全局的配置文件,位置是 Resources 下,或者类路径下的 config 也行,但是不建议,名字也是固定写法,application.properties 或者 application.yml ,yml 格式的更简单吧,起码在 IDEA 中是有提示的。
还有一个好消息是 SpringBoot 中默认的编码统一设置为了 UTF-8 ,这就舒爽多了啊,下面就是一个简单的入门栗子,深入研究以后再说

pom文件

我是用 IDEA 建的工程,所以大部分都自动生成了,主要是 parent 是必须的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.bfchengnuo</groupId>
<artifactId>firstspringboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>FirstSpringBoot</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

spring-boot-starter-web 相当于是 web 的快速构建包(自动配置),需要其他技术的导入相应的依赖即可,plugin 可有可无,是为了能使用 mvn 命令启动工程,spring-boot-starter-data-jpa 相当于是对支持 jpa 的 ORM 框架的支持,我用 hibernate 所以可以理解为对 hibernate 的支持

定义Controller

看过 SpringMVC 的都知道,这里的 Controller 和哪里的是一样的,使用起来也很熟悉,其实指的就是 springmvc 中的 handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
public class HelloController {

// 注入自定义属性
@Value("${girl.name}")
private String name;

@Resource
private Girl girl;

@RequestMapping(value = "hello",method = RequestMethod.GET)
public String test() {
return "Hello World! " + name + "; age:" + girl.getAge();
}

// 组合注解,method=get
@GetMapping("hello2")
public String test2() {
return "Hello World! " + name ;
}
}

@RestController 注解在 Spring4.x+ 的版本中加入,相当于 @ResponseBody 和 @Controller 的集合体;再来看看配置的 application.yml 文件(properties 文件也是可以的),其实不配也是完全可以的,这里是为了演示获取配置文件中的自定义属性:

1
2
3
4
5
6
7
8
server:
port: 8080
context-path: /

# 自定义属性
girl:
name: 测试
age: 12

可以配一些 boot 相关的配置,有很多,放在 Github 了,然后还可以配自定义属性(Girl),甚至注入到对象,但是配置属性注入到对象并不推荐使用,还是单个值比较好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component
// 注入自定义的配置(对象)
@ConfigurationProperties(prefix = "girl")
// @PropertySource("classpath:application.yml")
public class Girl {
private String name;
private Integer age;

public String getName() {
return name;
}

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

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

上面就是利用配置文件的值来自动注入对象,但是不推荐使用,主要用到的是 @ConfigurationProperties 注解,prefix 用来确定是用什么前缀的配置注入

在新版的 SpringBoot 中,@ConfigurationProperties 去掉了 localhost 属性,在没指定路劲的情况下,默认在 resource 下的 applications.properties (yml) 中查找,如果找到则返回值,如果没有找到则返回默认值null/0/false…
如果需要指定路径,配合 @PropertySource 和 @Component 注解

启动入口

下面配一个 SpringBoot 的启动入口就可以直接启动了,不需要配 Tomcat、web.xml 之类的,只需要定义一个主函数:

1
2
3
4
5
6
7
8
9
@SpringBootApplication
// @ComponentScan(basePackages="com.bfchengnuo")
public class FirstSpringBootApplication {

public static void main(String[] args) {
// 启动 SpringBoot 所必须的入口
SpringApplication.run(FirstSpringBootApplication.class, args);
}
}

@SpringBootApplication 注解是必须的,主要目的是开启自动配置,这就完成了,如果不是为了测试配置文件,可以更简单,只需要几行代码就可以完成

关于配置文件

可以使用分散配置,比如一份用于开发环境,一份用于生产环境:application-dev.yml ,application-prod.yml ;然后还需要一份主配置文件 application.yml :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 配置 spring 的“环境”,加载那个配置文件
spring:
profiles:
active: dev
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123

jpa:
hibernate:
ddl-auto: update
show-sql: true
server:
tomcat:
uri-encoding: utf-8

主要是前三行,后面顺便配了数据源相关的东西,因为下面要说,配置在主配置文件中的内容全部环境都可以使用;
配置环境,除了使用多文件的方式也可以写在同一份配置文件中,不同的环境之间使用 --- 进行分割就可以了。

数据库操作

这里使用的是 spring 提供的 spring-boot-starter-data-jpa 组件,然后使用的 ORM 框架是 hibernate,先确认以及导入了相关的依赖!
先是实体的定义,使用的全部是 JPA 注解的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Entity
public class Girl {
@Id
@GeneratedValue
private Integer id;
private String name;
@Min(value = 8,message = "你太小了!")
private Integer age;

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;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

非常熟悉了,使用了一个 @Min 注解,这是用来校验的,后面会看到,使用也非常的简单,按照以前的套路还需要写 Dao 层,现在只需要定义一个接口,继承 JpaRepository 就可以了,我把自己定义的这个接口放在了 Repository 包(和命名的最后保持一致比较好吧):

1
2
3
4
public interface GirlRepository extends JpaRepository<Girl,Integer> {
// 扩展接口,通过指定的条件来查询
List<Girl> findByAge (Integer age);
}

泛型第一个就是对应的实体类,第二个是主键的类型,最好指定下;其实接口不用写任何东西就已经支持基本的 CRUD 操作了,如果需要自定义查询条件之类的,那么就需要再扩展,比如上面扩展的根据年龄字段查询,命名就是这么个格式,不要瞎写
下面就是在 Controller 中的使用情况了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@RestController
@RequestMapping("girl")
public class GirlController {

@Resource
private GirlRepository girlRepository;

@GetMapping("getList")
public List<Girl> getList() {
return girlRepository.findAll();
}

@GetMapping("get/{id}")
public Girl getGirl(@PathVariable("id") Integer id) {
return girlRepository.findOne(id);
}

@PostMapping("add")
public Girl add(@RequestParam("name") String name ,
@RequestParam("age") Integer age) {
Girl girl = new Girl();
girl.setAge(age);
girl.setName(name);

System.out.println(name);
return girlRepository.save(girl);
}

@PostMapping("update")
public Girl add(@Valid Girl girl, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
System.out.println(bindingResult.getFieldError().getDefaultMessage());
return null;
}
return girlRepository.save(girl);
}

@GetMapping("getAge/{age}")
public List<Girl> getAge(@PathVariable("age") Integer age) {
return girlRepository.findByAge(age);
}
}

GirlRepository 直接注入就可以了,其他的东西都是 springmvc 里的,应该比较属性了,主要就是 @Valid 注解用来校验的,结果会放到 BindingResult 中

使用AOP

Spring 的精髓啊,怎么能不用一用呢,关于 Spring 中的 AOP 前面已经写的很清楚了,那就直接用了(如有必要,记得导入相应的类):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Aspect
@Component
public class HttpAspect {
public final static Logger LOGGER = LoggerFactory.getLogger(HttpAspect.class);

@Pointcut("execution(public * com.bfchengnuo.firstspringboot.controller.GirlController.*(..))")
public void log() {}

@Before("log()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();

LOGGER.info("url--->{}", request.getRequestURL());
LOGGER.info("method--->{}", request.getMethod());
LOGGER.info("ip--->{}", request.getRemoteAddr());

LOGGER.info("class_method--->{}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
LOGGER.info("args--->{}", joinPoint.getArgs());
}

@AfterReturning(returning = "o", pointcut = "log()")
public void doAfterReturning(Object o) {
// 获取方法执行完后的返回值
if (o != null) {
LOGGER.info("response:{}", o);
}
}
}

不多说,非常简单的切面定义,使用了 slf 的 log4j 日志工具,确实是比较爽的

关于异常

异常用好了也能提高效率,这里定义了一个自定义异常,但并没怎么用,具体如何使用要看需求了,简单说说,毕竟这篇就是简单的初尝试而已:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class GirlException extends RuntimeException {
private Integer code;

public GirlException(String message, Integer code) {
super(message);
this.code = code;
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}
}

需要注意的就是要继承 RuntimeException ,否则 SpringBoot 不会进行回滚事务的,注意:Exception 的构造方法需要一个 message 哦。
既然有异常,那就有相应的处理(捕获)异常的方法:

1
2
3
4
5
6
7
8
9
10
@ControllerAdvice
public class ExceptionHandle {

// 要捕获那个异常
@ExceptionHandler(value = Exception.class)
@ResponseBody // 转成 json....额
public String handle(Exception e) {
return e.getMessage();
}
}

加了 @ControllerAdvice 注解它就会自动捕获规定的异常了,配合 @ExceptionHandler 注解使用,然后就随你怎么干了

关于测试

Spring 中应该是提供了专门的测试注解,这个我还没研究,这里说两种简单的测试,比如,这是 IDEA 自动生成的测试代码,可以借鉴一下:

1
2
3
4
5
6
7
8
9
@RunWith(SpringRunner.class)  // 低层用的还是 Junit,表明是在测试环境下跑
@SpringBootTest // 表明会启动整个工程
public class FirstSpringBootApplicationTests {
@Test
public void contextLoads() {
// 使用断言
// Assert.assertEquals();
}
}

这是最普通的测试了,SpringBootTest 很重要,只有加入了这个测试的时候才运行整个工程。
然后就是 MVC 的测试方法了,我们要测 URL 是不是能用啊:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc // 针对 MVC 的测试,为测试 URL 访问
public class GirlControllerTest {

@Resource
private MockMvc mvc;

@Test
public void getAge() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/getAge/12"))
.andExpect(MockMvcResultMatchers.status().isOk());

// 对返回的内容进行判断断言
// mvc.perform(MockMvcRequestBuilders.get("/getAge/12"))
// .andExpect(MockMvcResultMatchers.content().string("xxxx"));
}

}

也差不多,非常的简单,Spring 都提供给我们 MocMvc 这个类,一切就好说了。

执行 mvn clean package 命令打包的时候会自动进行测试,并显示结果(在命令行);
由于一些原因也可以跳过测试直接打包:mvn clean package -Dmaven.test.skip=true

几种启动方式

启动 SpringBoot 项目常用的就这三种:
第一就是 IDE 直接启动(通过 @SpringBootApplication 配合 SpringApplication.run);
第二种是使用 java 命令来启动:java -jar xxx.jar -spring.profiles.active=dev 后面可以加一些参数,为了得到这个 jar 所以最好先 mvn install 命令来安装下;
第三种是用 mvn 命令来运行:mvn spring-boot:run ,但需要在 pom 文件中导入了相应的插件才行:

1
2
3
4
5
6
7
8
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

对于调试来说,这几种都算是非常方便的,因为内嵌了 tomcat 这种服务器,所以即使单独运行 jar 也是可以的,真正在部署的时候还是用 war 包比较稳!

关于2.0

现在发布了 SpringBoot2.X 版本,虽然还不是正式版,变动有点大,下面来稍微总结下,在 Github 的代码仓库里有一个用 2.0 的例子,可参考。
然后,sb2.x 是基于 spring framwork 5.0 的,所以要 jdk8+,这样才能使用某些特性。
全新特性:web flux 包含:

  • 声明式的函数编程 Java8 的 lambda
  • 响应式编程,Reactive Streams
  • 异步编程,Servlet 3.1 或 Asyc NIO

其他的还没看到,先 TODO….

参考&拓展

http://blog.csdn.net/qq_31655965/article/details/71258191
https://www.tianmaying.com/qa/205
Spring Boot 是什么,不是什么

喜欢就请我吃包辣条吧!

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~