Spring Boot 简介
Spring Boot主要是设计用来简化spring开发的。 详细的可以看这里
入门: Hello World
下面我们一步一步的搭建一个spring boot应用, 并实现我们经典的 Hello World。
Maven 配置
在pom.xml
里, 添加
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>
即引入了spring boot。 但是为了我们下面开发的Hello world应用, 还要添加其他依赖的jar:
<!-- 配置用于web项目的starter -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- 将应用打包为jar -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
到这里maven的配置已经完成了。 后面会说spring boot的各种starter。
编写代码
写一个最简单的rest接口:
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
@RestController
@EnableAutoConfiguration
public class Example {
@RequestMapping("/")
String home() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Example.class, args);
}
}
然后直接右键-运行, 然后访问http://localhost:8080
, 即可看到页面输出了Hello World !
从这个例子可以看出来, 使用spring boot 确实可以大大简化我们的程序开发。 但是上面的例子很简单很简单, 仅仅为了让大家对boot的便利性有个直观的认识。
实际项目中我们会用到很多其他组件, 比如我们Datasource
,Velocity
, Mybatis
,Filter
, Listener
,jsp/html页面等。 下面我们一一举例来说。
修改端口
修改端口比较简单, 我们在resources下面建一个config文件夹, 里面新建一个application.properties
文件, 在里面即可配置端口
# change server port
server.port=9090
除了application.properties
文件, spring boot可以使用yml配置文件, 我们在config
目录下建一个application.yml
文件,输入:
server:
port: 9090
也可以改变端口。
添加DataSource
添加数据源也很简单, 首先我们需要添加mysql-connector以及boot-starter-jdbc的依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
然后要在application.properties
文件里面添加以下配置:
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.MySQL.jdbc.Driver
spring.datasource.url=jdbc:MySQL://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
Spring Boot 就会自动创建一个Datasource, 我们在需要的地方直接使用@Autowire
注解注入就可以了。
Spring Boot默认的数据源是使用的tomcat-jdbc, 如果想更换其他的数据源, 比如Druid数据源, 只需要加入Druid的依赖, 然后在application.properties
中再添加一行:
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
就可以了。 其他的数据源的配置, 比如最大连接数什么的,
可以参考Spring Boot 所有配置
添加Mybatis
添加Mybatis相对来说复杂一点,Mybatis官方有对Spring Boot的支持, 源码可以在Github找到。
假如我们有一个city表, 有 id,name,postcode, province等属性。
我总结出来有三种方式可以添加Mybatis支持():
-
注解形式。这是一种比较简单的方式, 我们直接在接口上面添加
@Mapper
注解, 在方法上面配置@select
即可, 示例如下:@Mapper public interface CityMapper { @Select("select * from city where id=#{id}") City findCity(@Param("id") String id); }
然后在对应的service里面直接注入
CityMapper
即可. -
xml配置方式。
相对于上面的方式,这种方式更加灵活, 毕竟我们使用mybatis主要就是为了将SQL与代码分离以及动态SQL特性,如果使用注解的话这两个优势都没了,还不如用JPA。
所以我们需要自定义mybatis-config.xml
, 自定义CityMapper.xml
等配置文件。首先我们先看一下用这种方法, Mapper 应该怎么写:
@Mapper public interface CityMapper { City findCity(@Param("id") String id); }
可以看到与第一种方式相比, 仅仅移除了
@select
注解, 以分离SQL与代码。然后我们在
resources
目录下创建mybatis-config.xml
, 内容如下:<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <mappers> <mapper resource="mybatis/mappers/CityMapper.xml"/> </mappers> </configuration>
然后在
application.properties
配置文件添加一行:mybatis.config-location=classpath:mybatis-config.xml
然后在
resources
目录下创建新目录mybatis/mappers
用于存放mybatis的mapper文件。 然后再此目录下创建CityMapper.xml
, 内容如下:<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qunar.adam.boot.mybatis.xml.CityMapper"> <select id="findCity" resultType="com.qunar.adam.boot.mybatis.City"> select * from city where id = #{id} </select> </mapper>
到这里mybatis也就添加完成了, 在需要的地方注入
CityMapper
即可。 -
代码配置方式。 上面的两种方式都是mybatis官网给出的方案。 详见这里。
第一种方式, 抹杀了mybatis的最主要的优势, 显然我们不会用它。 第二种方式, 虽然灵活了, 但是还是有点繁琐。 每次增加一个Mapper,
都需要将xml所在路径添加到mybatis-config.xml
。 那么能不能像普通的spring与mybatis结合的那种方式, 只要配置一下mybatis的xml所在目录就可以呢?
答案也是肯定的, 这个时候就需要用代码配置了。我们回忆一下在普通的spring应用中是怎么集成mybatis的: 首先我们配置一个
SqlSessionFactoryBean
, 给它赋予datasource和mapperLocations属性就可以了, 就像这样:<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations"> <list> <value>classpath*:mybatis/mappers/*.xml</value> </list> </property> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/> </bean>
同样, 我们在spring boot里面也可以这么干, 只不过是用代码的形式:
@Configuration @EnableTransactionManagement public class MybatisConfig { private static final Logger logger = LoggerFactory.getLogger(MybatisConfig.class); /** * xml 配置文件的位置 */ @Value("${boot.mybatis.mapperfile.location}") private String mapperFolder; @Bean public MybatisResource mybatisResource() throws IOException { ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + mapperFolder + "/**/*.xml"; Resource[] resources = resourcePatternResolver.getResources(pattern); return new MybatisResource(resources); } @Autowired @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource, MybatisResource resource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(resource.getMapperResources()); return sqlSessionFactoryBean; } }
我们来分析一下上面的代码, 首先我们在spring context里面注册了一个
MybatisResource
来指示mybatis配置文件的目录, 然后手动创建了SqlSessionFactoryBean
并将他注册到spring中,
这样一来我们就又可以像以前那样使用mybatis了。 其中MybatisResource
是自定义的一个类。 源码如下:public class MybatisResource { private Resource[] mapperResources; public MybatisResource(){} public MybatisResource(Resource[] mapperResources){ this.mapperResources = mapperResources; } public Resource[] getMapperResources() { return mapperResources; } public void setMapperResources(Resource[] mapperResources) { this.mapperResources = mapperResources; } }
当然, 别忘了在Mapper的interface上面加上
@Mapper
注解。
添加Servlet,Filter与Listener
在普通web项目中很容易就能添加filter以及listener, 那么在spring boot中应该怎么操作呢? 其实也简单, spring boot有3种方式来添加。
-
Spring Boot 自动添加, 我们只需要正常实现一个
Filter
或Listener
, 然后给这个实现类加上@Service
注解交给spring就可以了。
对于Filter
, boot会自动匹配路径/*
. -
如果想要更灵活一点的, 可以使用
ServletRegistrationBean
,FilterRegistrationBean
,ServletListenerRegistrationBean
来通过代码注册。 这种方式稍微繁琐一些
不过灵活。 首先我们肯定还是先正常实现Servlet
,Filter
,Listener
, 然后新建一个WebConfig
的类(类名随意), 代码如下:@Configuration public class WebConfig { /** * 注册Servlet * @return */ @Bean public ServletRegistrationBean servletRegistrationBean() { ServletRegistrationBean bean = new ServletRegistrationBean(); bean.setName("mine");//设置servlet名称 bean.setLoadOnStartup(1);// bean.setServlet(new MyServlet1());//servlet的实现 bean.setUrlMappings(Collections.singletonList("/mine"));//url mapping return bean; } /** * 注册Filter * @return */ @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setName("mine-filter");//filter 名称 bean.setFilter(new MyFilter());//filter的实现 bean.setUrlPatterns(Collections.singletonList("/mine"));//url mapping return bean; } }
这样就注册了
Servlet
,Filter
与Listener
. -
使用注解来注册。 Spring Boot 提供了
@WebServlet
,@WebFilter
,and @WebListener
来注册。 这种方式要求我们在启动类上面加上@ServletComponentScan
注解。
首先还是正常实现Servlet
,Filter
与Listener
, 然后我们在Servlet
上面添加注解@WebServlet(urlPatterns = "/mine")
,
在Filter
上面添加@WebFilter(urlPatterns = "/mine")
, 在Listener
上面添加@WebListener
, 最后在启动类上面添加@ServletComponentScan
即可。 既方便又灵活。
关于JSP
Spring Boot 是不推荐使用JSP的, 因为内嵌的tomcat对JSP支持并不好, 有一些已知的限制,
这其中就提到了如果使用内嵌的tomcat,并且将spring boot打成jar包运行, 是不支持jsp的, 因为tomcat内部存在文件路径匹配的硬编码。 而如果把spring boot打成war包,
运行在tomcat容器内,则是支持jsp的。 关于如何设置JSP, 参考这里的例子, 就不展开了。
关于静态内容
默认情况下, Spring Boot 将classpath下面的/static
or /public
or /resources
or /META-INF/resources
作为静态资源目录, 我们可以在Maven项目中的resources
目录下
创建上面的目录存放静态资源。 例如我们的目录结构如下:
|src/main/
|- java/
|- resources/
|- static/
|- html/
|- index.html
|- js/
|- hello.js
那么要访问index.html的地址就是http://localhost:8080/html/index.html
, 也就是说静态资源目录是相对于http访问的根/
路径, 不过可以通过配置修改,
在application.properties
添加如下配置
spring.mvc.static-path-pattern=/mystatic/**
那么访问地址就对应的变成了http://localhost:8080/mystatic/html/index.html
。
添加Redis
在Maven中添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后在application.properties
添加配置:
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=123
如果是sentinel模式, 则添加如下配置:
spring.redis.sentinel.master= # Name of Redis server.
spring.redis.sentinel.nodes= # Comma-separated list of host:port pairs.
其他redis配置信息可以参考SpringBoot所有配置
配置完成后即可在需要的地方注入RedisTemplate
来操作Redis。
其他如集成Mongodb
,ES
,AMQP
与此差不多。
集成Dubbo
下面重点来了, 上面说的其实大部分官方文档都有。 那么dubbo作为一个国产组件, spring boot是没有做对它的支持的, 那么应该怎么集成到boot里面呢?
我们知道dubbo支持xml配置、注解配置、API配置。 所以第一种思路就是使用dubbo的API配置结合Sprring Boot的@Configuration
代码配置。 不过这种方式比较繁琐, Service多了很麻烦。
用简化一些的方法, github上面有个同学实现了一个spring-boot-starter-dubbo
, 代码在这里,
代码实现比较简单,用起来也很简单,具体的使用方法可以看这个文档写的很清晰, 这里就不重复了。
关于 spring boot starters
上面我们也用到了一些starters, 比如spring-boot-starter-web
,spring-boot-starter-jdbc
,
大家应该也能大概感觉到这个starter是个什么东西。 其实starter是一个空jar包, 仅仅就是一组依赖的集合,用于方便简单的为spring boot应用添加功能组件。 比如starter-jdbc
为应用添加访问数据库的能力, starter-data-redis提供了访问redis的能力, starter-data-web提供了web组件,作为web应用运行。 关于所有的sttarter,
可以参考这里的官方文档
编写自己的AutoConfiguration
我们上面介绍的各种starter, 其实它们都有一个auto-configuration的实现, 比如redis有RedisAutoConfiguration
, mongodb有MongoAutoConfiguration
...
有了这些auto-configuration, spring boot就有了自动配置的能力。 那么我们怎么写自己的auto-configuration呢?
本质上来说, auto-configuration 还是实现了标准的@Configuration
, 然后@Conditional
用于约束自动配置是否应该生效, 什么情况下生效。 一般来说, auto-configuration常用的
注解是@ConditionalOnClass
与 @ConditionalOnMissingBean
。 第一个注解的意思是只有classpath下存在此Class的时候才生效, 第二个注解表示没有此Class的实例的时候才生效。
这样就保证了能创建bean但不会重复创建, 所以用户可以重写默认的自动配置。
-
注册auto-configuration。
spring boot 通过检测jar包内的
META-INF/spring.factories
文件来检测auto-configuration,
自定义的auto-configuration应该配置在org.springframework.boot.autoconfigure.EnableAutoConfiguration
下面:org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\ com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
另外还可以使用
@AutoConfigureAfter
or@AutoConfigureBefore
来指定在某个类之前或者之后配置。 -
使用
@Conditional
进行约束。-
检测类是否存在的
@ConditionalOnClass
与@ConditionalOnMissingClass
。 这两种注解可以在value属性传入目标Class对象。 由于注解
是使用ASM(字节码库)来解析的, 所以即使实际运行的classpath不存在目标Class对象也可以正常工作。 -
检测bean是否存在的
@ConditionalOnBean
和@ConditionalOnMissingBean
。 这两个注解用于检测spring是否存在指定的bean, 可以用value
属性按照type识别或者使用name
属性
按照bean name识别。使用这两个注解需要注意, bean是否存在是在当前执行的节点判断的, 也就是有顺序问题。 所以推荐的做法是, 把这两个注解仅仅用在auto-configuration上面。
-
检测Spring上下文环境是否存在指定属性的
@ConditionalOnProperty
注解。默认情况下只要属性存在并且不是false
都会认为存在。 -
检测资源是否存在的
@ConditionalOnResource
。 -
检测是否为web项目的
@ConditionalOnWebApplication
和@ConditionalOnNotWebApplication
。 如果一个应用使用了WebApplicationContext
,
或者定义了StandardServletEnvironment
就被认为是web项目。 -
检测SpEL表达式的
@ConditionalOnExpression
注解。
-
-
创建自己的starter
一个完整的Spring Boot starter包含两个模块:
autoconfigure
模块, 主要包含auto-configure的代码。starter
模块, 主要是定义一些依赖关系。
当然两个模块也可以合并为一个。
部署
jar包部署
打成jar包比较简单, 在maven中添加依赖:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.test.adam.boot.filter3.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
其中configuration
标签可以省略如果项目中只有一个main方法。 然后使用mvn package -DskipTests
即可打成可运行的jar, 使用java -jar -server xxxx.jar
运行就行了。
war包部署
除了支持打成可运行jar包之外, Spring Boot还支持构建war包。 详细步骤请参考官方文档
关于Profile
Spring Boot配置文件支持profile, 我们可以创建三个application-dev.properties
,application-test.properties
,application-pro.properties
来分别对应本地、
测试环境、线上三个profile, 其中跟环境有关的比如数据源、redis等信息分别配置在各个环境对应的配置文件里。 把公用的配置写在application.properties
文件里。 启动jar的时候,
通过参数 -Dspring.profiles.active=dev
来启用对应的配置文件。
原创文章,转载请注明出处。 如发现文章有误, 请联系作者
Q.E.D.