SpringBoot使用及原理浅尝
一、前言
最近微服务很热,而SpringBoot以轻量级和内嵌tomcat,方便启动调试为微服务越来越被采用,而现在前沿的技术的demo一般都也使用SpringBoot编写。
二、启动
启动时序图
解析配置文件.png
上面流程启动从SpringApplication的run方法开始,到解析application.properties结束。
三、创建应用程序上下文和嵌入式tomcat
上面配置文件解析完毕后紧接着就是创建应用程序上下文
创建spring容器和tomcat.png
四、ConfigurationProperties Bean的创建
SpringBoot中不建议使用XML配置,所以比较常用的是使用ConfigurationProperties注解创建bean从application.properties中加载配置项。
@ConfigurationProperties(prefix = "mysql")
public class MySqlProperties {
private String driverClassName;
private String url;
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
private String userName;
private String passWord;
}
而application.properties中配置为:
mysql.driverClassName = com.mysql.jdbc.Driver
mysql.url = jdbc:mysql://localhost:3306/users
mysql.userName = ***
mysql.passWord = ***
五、 事务配置
Spring中使用XML配置的事务管理,那么springboot中如何进行配置对应的东西那?
5.1 SpringBoot中 DataSource的配置
首先数据源属性Bean
@ConfigurationProperties(prefix = "mysql")
public class MySqlProperties {
private String driverClassName;
private String url;
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
private String userName;
private String passWord;
}
然后创建DataSource的bean
@Configuration
@EnableConfigurationProperties(MySqlProperties.class)
@ConditionalOnClass(TDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
// 三、设置扫描器
@MapperScan(basePackages = "com.zlx.demo.web.speech2.mapper", sqlSessionFactoryRef = "sqlSessionFactory1")
public class MysqlAutoConfiguration {
@Autowired
private MySqlProperties properties;
// 一、创建数据源
@Primary
@Bean(name = "dataSource")
public DataSource dataSource() throws TddlException {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(properties.getDriverClassName());
dataSource.setUrl(properties.getUrl());
dataSource.setUsername(properties.getUserName());
dataSource.setPassword(properties.getPassWord());
return dataSource;
}
}
5.2 SpringBoot中 sqlSessionFactory的配置
Spring中XML配置知道要创建个sqlSessionFactory对象并且要进行属性配置,所以下面首先讲下mybaits的属性配置:
@ConfigurationProperties(prefix="mybatis")
public class MybatisProperties
{
public static final String MYBATIS_PREFIX = "mybatis";
private String configLocation;
private String[] mapperLocations;
private String typeAliasesPackage;
private String typeHandlersPackage;
private boolean checkConfigLocation = false;
private ExecutorType executorType;
private Properties configurationProperties;
@NestedConfigurationProperty
private Configuration configuration;
public String getConfigLocation()
{
return this.configLocation;
}
public void setConfigLocation(String configLocation)
{
this.configLocation = configLocation;
}
@Deprecated
public String getConfig()
{
return this.configLocation;
}
@Deprecated
public void setConfig(String config)
{
this.configLocation = config;
}
public String[] getMapperLocations()
{
return this.mapperLocations;
}
public void setMapperLocations(String[] mapperLocations)
{
this.mapperLocations = mapperLocations;
}
public String getTypeHandlersPackage()
{
return this.typeHandlersPackage;
}
public void setTypeHandlersPackage(String typeHandlersPackage)
{
this.typeHandlersPackage = typeHandlersPackage;
}
public String getTypeAliasesPackage()
{
return this.typeAliasesPackage;
}
public void setTypeAliasesPackage(String typeAliasesPackage)
{
this.typeAliasesPackage = typeAliasesPackage;
}
public boolean isCheckConfigLocation()
{
return this.checkConfigLocation;
}
public void setCheckConfigLocation(boolean checkConfigLocation)
{
this.checkConfigLocation = checkConfigLocation;
}
public ExecutorType getExecutorType()
{
return this.executorType;
}
public void setExecutorType(ExecutorType executorType)
{
this.executorType = executorType;
}
public Properties getConfigurationProperties()
{
return this.configurationProperties;
}
public void setConfigurationProperties(Properties configurationProperties)
{
this.configurationProperties = configurationProperties;
}
public Configuration getConfiguration()
{
return this.configuration;
}
public void setConfiguration(Configuration configuration)
{
this.configuration = configuration;
}
public Resource[] resolveMapperLocations()
{
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList();
if (this.mapperLocations != null) {
for (String mapperLocation : this.mapperLocations) {
try
{
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
}
catch (IOException localIOException) {}
}
}
return (Resource[])resources.toArray(new Resource[resources.size()]);
}
}
同样这些配置想从application.properties中进行配置,
然后创建sqlsessionfactory的代码:
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration
{
private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider)
{
this.properties = properties;
this.interceptors = ((Interceptor[])interceptorsProvider.getIfAvailable());
this.resourceLoader = resourceLoader;
this.databaseIdProvider = ((DatabaseIdProvider)databaseIdProvider.getIfAvailable());
}
@PostConstruct
public void checkConfigFileExists()
{
if ((this.properties.isCheckConfigLocation()) && (StringUtils.hasText(this.properties.getConfigLocation())))
{
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
}
}
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource)
throws Exception
{
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
factory.setConfiguration(this.properties.getConfiguration());
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
return factory.getObject();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory)
{
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
}
return new SqlSessionTemplate(sqlSessionFactory);
}
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware
{
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
{
MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try
{
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
for (String pkg : packages) {
MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
}
}
scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(packages));
}
catch (IllegalStateException ex)
{
MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
}
}
public void setBeanFactory(BeanFactory beanFactory)
throws BeansException
{
this.beanFactory = beanFactory;
}
public void setResourceLoader(ResourceLoader resourceLoader)
{
this.resourceLoader = resourceLoader;
}
}
@Configuration
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class})
public static class MapperScannerRegistrarNotFoundConfiguration
{
@PostConstruct
public void afterPropertiesSet()
{
MybatisAutoConfiguration.logger.debug("No {} found.", MapperFactoryBean.class.getName());
}
}
}
如果自己没有注入的话,springboot会通过上面代码进入注入,其中注解 @ConditionalOnMissingBean
说明IOC中不存在该类别bean的时候才创建。如果我们需要自己创建则如下代码:
@Bean(name = "sqlSessionFactory1")
@Primary
public SqlSessionFactory sqlSessionFactoryBean1() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource1());
sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations(new String[]{"classpath:mapper/*.xml"}));
return sqlSessionFactoryBean.getObject();
}
public Resource[] resolveMapperLocations(String[] mapperLocations) {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList();
if (mapperLocations != null) {
for (String mapperLocation : mapperLocations) {
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException localIOException) {
}
}
}
return (Resource[]) resources.toArray(new Resource[resources.size()]);
}
5.3 SpringBoot中MapperScannerConfigurer配置
Spring中XML配置知道这个主要是配置扫描mapper路径然后生成数据库操作代理类,那么在SpringBoot中只需要一个简单的注解:
image.png
5.4 SpringBoot中事务管理器配置
Spring中XML配置知道有注解式和xml配置式,SpringBoot不推荐XML配置,所有讲下注解。之前xml开启事务管理器需要创建DataSourceTransactionManager对象,现在Springboot中只需要:
然后只需要在需要进行事务管理的bo上面加上@Transactional即可,很方便的有没有-
screenshot.png
5.4.1 SpringBoot中多数据源使用
@EnableTransactionManagement
@RestController
@SpringBootApplication
public class DemoApp implements TransactionManagementConfigurer
{
@RequestMapping("/")
String home() {
return "Hello Demo!";
}
@Resource(name="txManager1")
private PlatformTransactionManager txManager1;
// 实现接口 TransactionManagementConfigurer 方法,其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager1;
}
@Bean
public StartRunner startRunner(){
return new StartRunner();
}
public static void main( String[] args )
{
SpringApplication.run(DemoApp.class, args);
}
}
数据源一配置:
//三、设置扫描器
// 三、设置扫描器
@MapperScan(basePackages = "com.zlx.demo.web.speech2.mapper", sqlSessionFactoryRef = "sqlSessionFactory1")
public class MysqlAutoConfiguration {
@Autowired
private MySqlProperties properties;
// 一、创建数据源
@Primary
@Bean(name = "dataSource1")
public DataSource dataSource1() throws TddlException {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(properties.getDriverClassName());
dataSource.setUrl(properties.getUrl());
dataSource.setUsername(properties.getUserName());
dataSource.setPassword(properties.getPassWord());
return dataSource;
}
// 二、创建SqlSessionFactory
@Bean(name = "sqlSessionFactory1")
@Primary
public SqlSessionFactory sqlSessionFactoryBean1() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource1());
sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations(new String[] { "classpath:mapper/*.xml" }));
return sqlSessionFactoryBean.getObject();
}
// 四、 创建事务管理器
@Bean(name = "txManager1")
@Primary
public PlatformTransactionManager txManager1(@Qualifier("dataSource1") DataSource dataSource) {
System.out.println("-----------dataource-----" + dataSource.toString());
return new DataSourceTransactionManager(dataSource);
}
// 创建SqlSessionTemplate
@Bean("sqlSessionTemplate1")
@Primary
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) {
System.out.println("-----------sqlSessionFactory-----" + sqlSessionFactory.toString());
return new SqlSessionTemplate(sqlSessionFactory);
}
public Resource[] resolveMapperLocations(String[] mapperLocations) {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList();
if (mapperLocations != null) {
for (String mapperLocation : mapperLocations) {
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException localIOException) {
}
}
}
return (Resource[]) resources.toArray(new Resource[resources.size()]);
}
数据源二配置:
//三、设置扫描器
@MapperScan(basePackages = "com.zlx.demo.web.business.mapper", sqlSessionFactoryRef = "sqlSessionFactory1")
public class MysqlAutoConfiguration2 {
@Autowired
private MySqlProperties properties;
// 一、创建数据源
@Primary
@Bean(name = "dataSource2")
public DataSource dataSource1() throws TddlException {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(properties.getDriverClassName());
dataSource.setUrl(properties.getUrl());
dataSource.setUsername(properties.getUserName());
dataSource.setPassword(properties.getPassWord());
return dataSource;
}
// 二、创建SqlSessionFactory
@Bean(name = "sqlSessionFactory2")
@Primary
public SqlSessionFactory sqlSessionFactoryBean1() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource1());
sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations(new String[] { "classpath:mapper/*.xml" }));
return sqlSessionFactoryBean.getObject();
}
// 四、 创建事务管理器
@Bean(name = "txManager2")
@Primary
public PlatformTransactionManager txManager1(@Qualifier("dataSource1") DataSource dataSource) {
System.out.println("-----------dataource-----" + dataSource.toString());
return new DataSourceTransactionManager(dataSource);
}
// 创建SqlSessionTemplate
@Bean("sqlSessionTemplate2")
@Primary
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) {
System.out.println("-----------sqlSessionFactory-----" + sqlSessionFactory.toString());
return new SqlSessionTemplate(sqlSessionFactory);
}
public Resource[] resolveMapperLocations(String[] mapperLocations) {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList();
if (mapperLocations != null) {
for (String mapperLocation : mapperLocations) {
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException localIOException) {
}
}
}
return (Resource[]) resources.toArray(new Resource[resources.size()]);
}
另外SqlSessionTemplate是对SqlSessionFactory的一个包装,这里每个数据源也配置了一个,如果想使用它的话,只需要修改@mapperscan,设置sqlSessionTemplateRef替换sqlSessionFactoryRef
六、总结
SpringBoot简化了开发,节省了时间,是未来的趋势,但是对其原理也要知其然也要知其所以然。