SpringBoot基础(2)
来源:互联网 发布:参加学术会议 知乎 编辑:程序博客网 时间:2024/06/10 21:39
说明:本文参考http://blog.csdn.net/catoop/article/details/50501664/,在此基础上进行验证与总结。
8.CommandLineRunner
有时候在服务启动时就要做一些事情,如初始化一些参数和配置信息。在Spring Boot中可以通过实现接口CommandLineRunner来实现。
创建实现接口的类:
@Componentpublic class MyStartupRunner implements CommandLineRunner { @Override public void run(String... strings) throws Exception { System.out.println("****************服务启动执行,执行加载数据等操作***********************"); }}
系统启动后会遍历CommandLineRunner接口的实例并运行它们的run方法。如果有多个实例怎么办?哪一个实例先运行?只需要在实例类上添加@Order注解就行,如一个实例添加@Order(value=1),另一个添加@Order(value=2),执行顺序是按value值从小到大。
9.日志打印
在代码中打印日志,以方便获取程序执行的一些信息,这几乎是每个程序员都知道的事。初级程序员喜欢用System.out,但这种一般只是将日志信息打印到标准输出上,无法保存,再则会增加资源的消耗。更好的方式是日志系统:Slf4j+logback(对各日志系统的比较在前面的博客-日志组件中有写)。
Spring Boot记录日志只需两步:
1. src/main/resources 下面创建logback.xml 文件(日志文件配置模板可以参考日志组件这篇博客),或者使用在简单的方法在application配置文件中配置。
2. 在代码中打印日志。
日志打印就是普遍使用的方式,在Spring Boot中没什么特殊,这个没什么好说的。
10.数据源
Jdbc连接数据库
在pom中添加依赖:
<!-- MYSQL --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><!-- Spring Boot JDBC --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency>
在application.properties中配置数据库连接参数:
spring.datasource.url=jdbc:mysql://localhost:3306/testspring.datasource.username=rootspring.datasource.password=rootspring.datasource.driver-class-name=com.mysql.jdbc.Driver
SpringBoot默认使用org.apache.tomcat.jdbc.pool.DataSource。使用以上配置后,springBoot将使用默认的tomcat的连接池,并自动设置好数据库连接参数。
假设有一个UserService,其中listUser方法读取user表中的所有user并以List返回,user中只有id和name两个字段。代码如下(这里为方便,直接在service层操作数据库):
@Servicepublic class UserServiceImpl implements IUserService { @Resource private JdbcTemplate jdbcTemplate; @Override public List<User> listUser() { String sql = "SELECT * FROM user"; return (List<User>) jdbcTemplate.query(sql, new RowMapper<User>() { @Override public User mapRow(ResultSet rs, int i) throws SQLException { User u = new User(); u.setId(rs.getInt("id")); u.setName(rs.getString("name")); return u; } }); }}
可以看到,这里通过jdbcTemplate来做数据库操作,而jdbcTemplate直接注入就可以。通过jdbcTemplate.query方法查询时,第二个参数是一个实现RowMapper接口的匿名类的实例,并且需要实现行映射方法,将返回的行结果映射到实体对象上。
如果想使用其他的连接池如HikariCP(据说是java平台性能最好的连接池),可以在application配置文件中添加一行:spring.datasource.type=com.zaxxer.hikari.HikariDataSource。然后在pom.xml中添加其依赖:
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <!-- 版本号可以不用指定,Spring Boot会选用合适的版本 --></dependency>
即可,其他连接池同理。
JPA连接数据库
将pom.xml中添加的第二个依赖包改为:
<!-- Spring Boot JPA --> <dependency> <groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
当然如果想同时使用JDBC和JPA,只需要将各自的依赖都添加进即可。定义实体类:
@Entity@Table(name="score")public class Score implements Serializable{ private static final long serialVersionUID = 1L; @Id @GeneratedValue private int id; @Column(nullable = false, name="userId") private int userId; @Column(nullable = false) private float sc; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public float getSc() { return sc; } public void setSc(float sc) { this.sc = sc; }}
定义IScoreService接口:
public interface IScoreService extends CrudRepository<Score, Integer>{ @Query("select t from Score t ") List<Score> listScore();}
注意,这里接口要继承CrudRepository,并且不需要实现。因为Spring 会自动为我们继承CrudRepository的接口创建实现类(使用动态代理创建接口实例),我们只需要在使用的时候直接使用注解注入即可:
@Resourceprivate IScoreService scoreService;
Mybatis连接数据库
(1)配置文件方式
在pom.xml中添加依赖:
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId>/*这里只支持1.0.1版本,更高版本不行*/ <version>1.0.1</version></dependency><dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>3.4.0</version></dependency>
创建接口Mapper和对应的Mapper.xml文件。定义相关方法,注意方法名称要和Mapper.xml文件中的sql的id一致。
@Componentpublic interface StudentMapper extends MyMapper<Student> { Student getById(int id); String getNameById(int id);}
public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {}StudentMapper.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="cn.tonghao.mapper.StudentMapper"> <!-- type为实体类Student,包名已经配置,可以直接写类名 --> <resultMap id="stuMap" type="Student"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="sumScore" column="score_sum" /> <result property="avgScore" column="score_avg" /> <result property="age" column="age" /> </resultMap> <select id="getById" resultMap="stuMap" resultType="Student"> SELECT * FROM STUDENT WHERE ID = #{id} </select> <select id="likeName" resultMap="stuMap" parameterType="string" resultType="list"> SELECT * FROM STUDENT WHERE NAME LIKE CONCAT('%',#{name},'%') </select> <select id="getNameById" resultType="string"> SELECT NAME FROM STUDENT WHERE ID = #{id} </select></mapper>
该xml文件放在resources/mapper目录下。
在application中添加:
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml#实体类包名mybatis.type-aliases-package=cn.tonghao.domain
有一个需要注意的地方,在controller中调用service,然后在serviceImpl中调用studentMapper时,StudentMapper接口上要加@Component注解,Spring会通过动态代理自动创建该接口的实例。另外在ServiceImpl类上要显式注明bean的名称,否则会报错(按道理讲当接口只有一个实现类时会自动将该实现类作为bean注入,不用显式指定名称,在jdbc连接数据库的例子中就是如此)。
(2)注解方式这里就不说了,需要在接口方法上通过注解来写sql语句。
(3)集成分页插件
Mybatis提供了拦截器接口,我们可以实现自己的拦截器,将其作为一个plugin装入到sqlSessionFactory中。Spring在注入bean的时候,会把所有实现了Mybatis中Interceptor接口的所有类都注入到sqlSessionFactory中,作为plugin存在。这里使用github上的一个分页插件PageHelper,使用@Bean将PageHelper注册为bean即可,注入的时候PageHelper将被注入到SqlSessionFactory作为插件存在。
@Configurationpublic class MyBatisConfiguration { private static final Logger logger = LoggerFactory.getLogger(MyBatisConfiguration.class); @Bean public PageHelper pageHelper() { logger.info("注册MyBatis分页插件PageHelper"); PageHelper pageHelper = new PageHelper(); Properties p = new Properties(); p.setProperty("offsetAsPageNum", "true"); p.setProperty("rowBoundsWithCount", "true"); p.setProperty("reasonable", "true"); pageHelper.setProperties(p); return pageHelper; }}
调用:
@RequestMapping("/listStudent")public List<Student> listStudent() { PageHelper.startPage(1,2);//pageNum,pageSize return stuService.listStudent();}
11.多数据源自动切换
数据源读取与切换类:
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); }}
@Aspect@Order(-1)// 保证该AOP在@Transactional之前执行@Componentpublic class DynamicDataSourceAspect { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class); @Before("@annotation(ds)") public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable { String dsId = ds.name(); if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) { logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature()); } else { logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature()); DynamicDataSourceContextHolder.setDataSourceType(ds.name()); } } @After("@annotation(ds)") public void restoreDataSource(JoinPoint point, TargetDataSource ds) { logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature()); DynamicDataSourceContextHolder.clearDataSourceType(); }}
public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static List<String> dataSourceIds = new ArrayList<>(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } /** * 判断指定DataSrouce当前是否存在 * * @param dataSourceId * @return * @author SHANHY * @create 2016年1月24日 */ public static boolean containsDataSource(String dataSourceId){ return dataSourceIds.contains(dataSourceId); }}
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class); private ConversionService conversionService = new DefaultConversionService(); private PropertyValues dataSourcePropertyValues; // 如配置文件中未指定数据源类型,使用该默认值 private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource"; // private static final Object DATASOURCE_TYPE_DEFAULT = // "com.zaxxer.hikari.HikariDataSource"; // 数据源 private DataSource defaultDataSource; private Map<String, DataSource> customDataSources = new HashMap<>(); @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); // 将主数据源添加到更多数据源中 targetDataSources.put("dataSource", defaultDataSource); DynamicDataSourceContextHolder.dataSourceIds.add("dataSource"); // 添加更多数据源 targetDataSources.putAll(customDataSources); for (String key : customDataSources.keySet()) { DynamicDataSourceContextHolder.dataSourceIds.add(key); } // 创建DynamicDataSource GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); mpv.addPropertyValue("targetDataSources", targetDataSources); registry.registerBeanDefinition("dataSource", beanDefinition); logger.info("Dynamic DataSource Registry"); } /** * 创建DataSource * @return * @author SHANHY * @create 2016年1月24日 */ @SuppressWarnings("unchecked") public DataSource buildDataSource(Map<String, Object> dsMap) { try { Object type = dsMap.get("type"); if (type == null) type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource Class<? extends DataSource> dataSourceType; dataSourceType = (Class<? extends DataSource>) Class.forName((String) type); String driverClassName = dsMap.get("driver-class-name").toString(); String url = dsMap.get("url").toString(); String username = dsMap.get("username").toString(); String password = dsMap.get("password").toString(); DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url) .username(username).password(password).type(dataSourceType); return factory.build(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** * 加载多数据源配置 */ @Override public void setEnvironment(Environment env) { initDefaultDataSource(env); initCustomDataSources(env); } /** * 初始化主数据源 * * @author SHANHY * @create 2016年1月24日 */ private void initDefaultDataSource(Environment env) { // 读取主数据源 RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource."); Map<String, Object> dsMap = new HashMap<>(); dsMap.put("type", propertyResolver.getProperty("type")); dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name")); dsMap.put("url", propertyResolver.getProperty("url")); dsMap.put("username", propertyResolver.getProperty("username")); dsMap.put("password", propertyResolver.getProperty("password")); defaultDataSource = buildDataSource(dsMap); dataBinder(defaultDataSource, env); } /** * 为DataSource绑定更多数据 * * @param dataSource * @param env * @author SHANHY * @create 2016年1月25日 */ private void dataBinder(DataSource dataSource, Environment env){ RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource); //dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext)); dataBinder.setConversionService(conversionService); dataBinder.setIgnoreNestedProperties(false);//false dataBinder.setIgnoreInvalidFields(false);//false dataBinder.setIgnoreUnknownFields(true);//true if(dataSourcePropertyValues == null){ Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties("."); Map<String, Object> values = new HashMap<>(rpr); // 排除已经设置的属性 values.remove("type"); values.remove("driver-class-name"); values.remove("url"); values.remove("username"); values.remove("password"); dataSourcePropertyValues = new MutablePropertyValues(values); } dataBinder.bind(dataSourcePropertyValues); } /** * 初始化更多数据源 * * @author SHANHY * @create 2016年1月24日 */ private void initCustomDataSources(Environment env) { // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源 RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource."); String dsPrefixs = propertyResolver.getProperty("names"); for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源 Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + "."); DataSource ds = buildDataSource(dsMap); customDataSources.put(dsPrefix, ds); dataBinder(ds, env); } }}
@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface TargetDataSource { String name();}
以上5个类为数据源自动切换的基础类,然后在SpringBootSampleApplication上添加注解:@Import({DynamicDataSourceRegister.class})。在application.xml中添加配置:
# 更多数据源custom.datasource.names=ds1,ds2custom.datasource.ds1.type=com.zaxxer.hikari.HikariDataSourcecustom.datasource.ds1.driver-class-name=com.mysql.jdbc.Drivercustom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNullcustom.datasource.ds1.username=rootcustom.datasource.ds1.password=rootcustom.datasource.ds2.type=com.zaxxer.hikari.HikariDataSourcecustom.datasource.ds2.driver-class-name=com.mysql.jdbc.Drivercustom.datasource.ds2.url=jdbc:mysql:// localhost:3306/test2?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNullcustom.datasource.ds2.username=root custom.datasource.ds2.password=root
仍然以studentService为例:
@Override@TargetDataSource(name="ds1")public List<Student> likeName(String name) { return studentMapper.likeName(name);}
该方法对应sql为:
<select id="likeName" resultMap="stuMap" parameterType="string" resultType="list"> SELECT * FROM STUDENT WHERE NAME like CONCAT('%',#{name},'%')</select>
在likeName方法上指定数据源就可以从不同的数据源中查询了。
- SpringBoot基础(2)
- SpringBoot基础(1)
- SpringBoot基础(3)
- SpringBoot学习(一)SpringBoot基础入门
- SpringBoot基础
- springboot基础
- 【SpringBoot】SpringBoot基础-HelloWord实例
- springboot笔记(2)springboot-Mybatis使用
- Springboot基础篇 (一)启动方式
- SpringBoot 第二季(基础配置学习)
- springboot(2)
- SpringBoot学习(2)
- springboot(2):综合开发
- 零基础myeclipse下springBoot+maven+thymeleaf的开发(2)
- springboot-基础入门
- springboot基础环境搭建
- Springboot基础篇 Controller
- springboot(一)基础概念
- GeoServer基础教程(三):部署发布Shapefile地图数据
- fabirc1.0商业正式版本源码解析7——peer的ChaincodeSupport服务
- 一个字符串的最大回文前缀长度(搜狗2017秋招真题)
- 剑指offer-从尾到头打印链表
- MySQL的读写分离用什么软件比较好?
- SpringBoot基础(2)
- SlidingMenu的使用之一(左右菜单)
- 【theano-windows】学习笔记二——theano中的函数和共享参数
- POJ1305 勾股数组
- 金融安全资讯精选 2017年第五期:WireX 僵尸网络袭击全球,金融企业如何选择安全的云
- 计划总结思考-不定期更新
- kafka集群搭建和使用Java写kafka生产者消费者
- 在ionic3 + angular4 中使用Echart插件
- 线中画箭头