(43). Spring Boot動態數據源(多數據源自動切換)【從零開始學Spring Boot】


在上一篇我們介紹了多數據源,但是我們會發現在實際中我們很少直接獲取數據源對象進行操作,我們常用的是jdbcTemplate或者是jpa進行操作數據庫。那么這一節我們將要介紹怎么進行多數據源動態切換。添加本文實現的代碼之后,只需要配置要數據源就可以直接通過注解使用,在實際使用的時候特別的簡單。那么本章主要分以下幾個步驟進行實戰。

本章大綱 寫道
(1)新建maven java project;
(2)在pom.xml添加依賴包;
(3)編寫啟動類App.java
(4)編寫配置文件application.properties;
(5)動態數據源路由類;
(6)注冊多數據源;
(7)測試類測試;

 

 

       接下來我們看看每一步具體的實現吧:

 

1)新建maven java project;

       新建一個maven project,取名為spring-boot-multi-ds

2)在pom.xml添加依賴包;

       pom.xml文件中加入依賴的庫文件,主要是spring boot基本的,數據庫驅動,spring-jpa支持即可,具體pom.xml文件如下:

<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.kfit</groupId>

  <artifactId>spring-boot-multids</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>jar</packaging>

 

  <name>spring-boot-multids</name>

  <url>http://maven.apache.org</url>

 

  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <!-- jdk版本號,這里需要你本地進行的jdk進行修改,這里angel使用的是1.8的版本. -->

    <java.version>1.8</java.version>

  </properties>

 

 

   <!--

       spring boot 父節點依賴,

       引入這個之后相關的引入就不需要添加version配置,

       spring boot會自動選擇最合適的版本進行添加。

       在這里使用的1.3.3版本,可能目前官方有最新的版本了,大家可以

       使用最新的版本。

     -->

    <parent>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-parent</artifactId>

       <version>1.3.3.RELEASE</version>

    </parent>

 

  <dependencies>

    <!-- 單元測試包,在這里沒有使用到. -->

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <scope>test</scope>

    </dependency>

   

    <!-- spring boot web支持:mvc,aop...

        這個是最基本的,基本每一個基本的demo都是需要引入的。

    -->

    <dependency>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

   

    <!-- mysql驅動.

       我們的demo是多數據源,在這里使用Mysql數據庫.

    -->

    <dependency>

       <groupId>mysql</groupId>

       <artifactId>mysql-connector-java</artifactId>

    </dependency>

   

   

    <!-- spring jpa

       spring jpa中帶有自帶的tomcat數據連接池;

       在代碼中我們也需要用到.

     -->

    <dependency>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-data-jpa</artifactId>

    </dependency>

   

  </dependencies>

</project>

       在上面的配置文件中都有相應的解釋,大家可以自己解讀下。

 

 

3)編寫啟動類App.java

       編寫spring boot的啟動類:

com.kfit.App:

package com.kfit;

 

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

 

/**

 *

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@SpringBootApplication

public class App {

    public static void main(String[] args) {

       SpringApplication.run(App.class, args);

    }

}

 

 

4)編寫配置文件application.properties;

       在這里主要是多數據源和jpa的配置:

src/main/resources/application.properties:

########################################################

###配置文件包括1個主數據源和多個數據源,

###其中主數據源在Spring中的beanName默認為dataSource

###另外幾個數據源的beanName分包為:ds1ds2ds3

###其中datasourcetype屬性可以具體指定到我們需要的數據源上面,

###不指定情況下默認為:org.apache.tomcat.jdbc.pool.DataSource

###當然你也可以把這些數據源配置到主dataSource數據庫中,然后讀取數據庫生成多數據源。當然這樣做的必要性並不大,難不成數據源還會經常變嗎。

########################################################

 

# 主數據源,默認的

#spring.datasource.type=com.zaxxer.hikari.HikariDataSource

spring.datasource.driverClassName=com.mysql.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/test

spring.datasource.username=root

spring.datasource.password=root

 

 

# 更多數據源

custom.datasource.names=ds1,ds2,ds3

#custom.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource

custom.datasource.ds1.driverClassName =com.mysql.jdbc.Driver

custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1

custom.datasource.ds1.username=root

custom.datasource.ds1.password=root

 

#custom.datasource.ds2.type=com.zaxxer.hikari.HikariDataSource

custom.datasource.ds2.driverClassName =com.mysql.jdbc.Driver

custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test

custom.datasource.ds2.username=root

custom.datasource.ds2.password=root

 

#custom.datasource.ds3.type=com.zaxxer.hikari.HikariDataSource

custom.datasource.ds3.driverClassName =com.mysql.jdbc.Driver

custom.datasource.ds3.url=jdbc:mysql://localhost:3306/test

custom.datasource.ds3.username=root

custom.datasource.ds3.password=root

 

 

# 下面為連接池的補充設置,應用到上面所有數據源中

spring.datasource.maximum-pool-size=100

spring.datasource.max-idle=10

spring.datasource.max-wait=10000

spring.datasource.min-idle=5

spring.datasource.initial-size=5

spring.datasource.validation-query=SELECT 1

spring.datasource.test-on-borrow=false

spring.datasource.test-while-idle=true

spring.datasource.time-between-eviction-runs-millis=18800

 

 

 

########################################################

### Java Persistence Api

########################################################

# Specify the DBMS

spring.jpa.database = MYSQL

# Show or not log for each sql query

spring.jpa.show-sql = true

# Hibernate ddl auto (create, create-drop, update)

spring.jpa.hibernate.ddl-auto = update

# Naming strategy

#[org.hibernate.cfg.ImprovedNamingStrategy  #org.hibernate.cfg.DefaultNamingStrategy]

spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.DefaultNamingStrategy

# stripped before adding them to the entity manager)

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

 

 

 

5)動態數據源路由類;

       動態數據源能進行自動切換的核心就是spring底層提供了AbstractRoutingDataSource類進行數據源的路由的,我們主要繼承這個類,實現里面的方法即可實現我們想要的,這里主要是實現方法:determineCurrentLookupKey(),而此方法只需要返回一個數據庫的名稱即可,所以我們核心的是有一個類來管理數據源的線程池,這個類才是動態數據源的核心處理類。還有另外就是我們使用aop技術在執行事務方法前進行數據源的切換。所以這里有幾個需要編碼的類,具體如下:

 

動態數據源上下文>com.kfit.config.datasource.DynamicDataSourceContextHolder

package com.kfit.config.datasource.dynamic;

 

import java.util.ArrayList;

import java.util.List;

 

/**

 * 動態數據源上下文.

 *

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

public class DynamicDataSourceContextHolder {

   

    /*

     * 當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,

     * 所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。

     */

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    /*

     * 管理所有的數據源id;

     * 主要是為了判斷數據源是否存在;

     */

    public static List<String> dataSourceIds = new ArrayList<String>();

 

    /**

     * 使用setDataSourceType設置當前的

     * @param dataSourceType

     */

    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  2016124

     */

    public static boolean containsDataSource(String dataSourceId){

        return dataSourceIds.contains(dataSourceId);

    }

}

 

數據源路由類>com.kfit.config.datasource.dynamic.DynamicDataSource

package com.kfit.config.datasource.dynamic;

 

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

 

/**

 * 動態數據源.

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

public class DynamicDataSource extends AbstractRoutingDataSource {

   

    /*

     * 代碼中的determineCurrentLookupKey方法取得一個字符串,

     * 該字符串將與配置文件中的相應字符串進行匹配以定位數據源,配置文件,即applicationContext.xml文件中需要要如下代碼:(non-Javadoc)

     * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()

     */

    @Override

    protected Object determineCurrentLookupKey() {

       /*

        * DynamicDataSourceContextHolder代碼中使用setDataSourceType

        * 設置當前的數據源,在路由類中使用getDataSourceType進行獲取,

        *  交給AbstractRoutingDataSource進行注入使用。

        */

       return DynamicDataSourceContextHolder.getDataSourceType();

    }

}

 

指定數據源注解類>com.kfit.config.datasource.dynamic.TargetDataSource

package com.kfit.config.datasource.dynamic;

 

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 

/**

 * 在方法上使用,用於指定使用哪個數據源

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Target({ ElementType.METHOD, ElementType.TYPE })

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface TargetDataSource {

    String value();

}

 

切換數據源Advice>com.kfit.config.datasource.dynamic.DynamicDataSourceAspect

package com.kfit.config.datasource.dynamic;

 

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.springframework.core.annotation.Order;

import org.springframework.stereotype.Component;

 

/**

 * 切換數據源Advice

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Aspect

@Order(-10)//保證該AOP@Transactional之前執行

@Component

public class DynamicDataSourceAspect {

   

    /*

     * @Before("@annotation(ds)")

     * 的意思是:

     *

     * @Before:在方法執行之前進行執行:

     * @annotation(targetDataSource)

     * 會攔截注解targetDataSource的方法,否則不攔截;

     */

    @Before("@annotation(targetDataSource)")

    public void changeDataSource(JoinPoint point, TargetDataSourcetargetDataSource) throws Throwable {

       //獲取當前的指定的數據源;

        String dsId = targetDataSource.value();

        //如果不在我們注入的所有的數據源范圍之內,那么輸出警告信息,系統自動使用默認的數據源。

        if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {

            System.err.println("數據源[{}]不存在,使用默認數據源 > {}"+targetDataSource.value()+point.getSignature());

        } else {

            System.out.println("Use DataSource : {} > {}"+targetDataSource.value()+point.getSignature());

            //找到的話,那么設置到動態數據源上下文中。

            DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());

        }

    }

   

    @After("@annotation(targetDataSource)")

    public void restoreDataSource(JoinPoint point, TargetDataSourcetargetDataSource) {

       System.out.println("Revert DataSource : {} > {}"+targetDataSource.value()+point.getSignature());

       //方法執行完畢之后,銷毀當前數據源信息,進行垃圾回收。

        DynamicDataSourceContextHolder.clearDataSourceType();

    }

   

}

 

 

6)注冊多數據源;

       以上都是動態數據源在注入的時候使用的代碼,其實很重要的一部分代碼就是注冊我們在application.properties配置的多數據源,這才是重點,這里我們使用

ImportBeanDefinitionRegistrar進行注冊,具體的代碼如下:

package com.kfit.config.datasource.dynamic;

 

import java.util.HashMap;

import java.util.Map;

 

import javax.sql.DataSource;

 

import org.springframework.beans.MutablePropertyValues;

import org.springframework.beans.PropertyValues;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import org.springframework.beans.factory.support.GenericBeanDefinition;

import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;

import org.springframework.boot.bind.RelaxedDataBinder;

import org.springframework.boot.bind.RelaxedPropertyResolver;

import org.springframework.context.EnvironmentAware;

import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;

import org.springframework.core.convert.ConversionService;

import org.springframework.core.convert.support.DefaultConversionService;

import org.springframework.core.env.Environment;

import org.springframework.core.type.AnnotationMetadata;

 

/**

 * 動態數據源注冊;

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

public class DynamicDataSourceRegister  implements ImportBeanDefinitionRegistrar, EnvironmentAware {

   

    //如配置文件中未指定數據源類型,使用該默認值

    private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";

    private ConversionService conversionService = new DefaultConversionService();

    private PropertyValues dataSourcePropertyValues;

   

    // 默認數據源

    private DataSource defaultDataSource;

   

    private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();

   

    /**

     * 加載多數據源配置

     */

    @Override

    public void setEnvironment(Environment environment) {

       System.out.println("DynamicDataSourceRegister.setEnvironment()");

       initDefaultDataSource(environment);

        initCustomDataSources(environment);

    }

   

    /**

     * 加載主數據源配置.

     * @param env

     */

    private void initDefaultDataSource(Environment env){

       // 讀取主數據源

       RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");

       Map<String, Object> dsMap = new HashMap<String, Object>();

       dsMap.put("type", propertyResolver.getProperty("type"));

        dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));

        dsMap.put("url", propertyResolver.getProperty("url"));

        dsMap.put("username", propertyResolver.getProperty("username"));

        dsMap.put("password", propertyResolver.getProperty("password"));

       

        //創建數據源;

        defaultDataSource = buildDataSource(dsMap);

        dataBinder(defaultDataSource, env);

    }

   

    /**

     * 初始化更多數據源

     *

     * @author SHANHY

     * @create 2016124

     */

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

        }

    }

   

    /**

     * 創建datasource.

     * @param dsMap

     * @return

     */

    @SuppressWarnings("unchecked")

    public DataSource buildDataSource(Map<String, Object> dsMap) {

       Object type = dsMap.get("type");

        if (type == null){

            type = DATASOURCE_TYPE_DEFAULT;// 默認DataSource

        }

        Class<? extends DataSource> dataSourceType;

       

       try {

           dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);

           String driverClassName = dsMap.get("driverClassName").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);

            returnfactory.build();

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       }

       returnnull;

    }

   

    /**

     * DataSource綁定更多數據

     * @param dataSource

     * @param env

     */

    private void dataBinder(DataSource dataSource, Environment env){

       RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);

       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("driverClassName");

            values.remove("url");

            values.remove("username");

            values.remove("password");

            dataSourcePropertyValues = new MutablePropertyValues(values);

        }

        dataBinder.bind(dataSourcePropertyValues);

       

    }

   

 

    @Override

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

       System.out.println("DynamicDataSourceRegister.registerBeanDefinitions()");

       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();

        //添加屬性:AbstractRoutingDataSource.defaultTargetDataSource

        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);

        mpv.addPropertyValue("targetDataSources", targetDataSources);

        registry.registerBeanDefinition("dataSource", beanDefinition);

    }

 

}

這里還有一個步驟很重要,由於我們是使用的ImportBeanDefinitionRegistrar的方式進行注冊的,所以我們需要在App.java類中使用@Import進行注冊,具體改造之后的App.java代碼如下:

package com.kfit;

 

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.Import;

import com.kfit.config.datasource.dynamic.DynamicDataSourceRegister;

 

/**

 *

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@SpringBootApplication

//注冊動態多數據源

@Import({DynamicDataSourceRegister.class})

public class App {

    public static void main(String[] args) {

       SpringApplication.run(App.class, args);

    }

}

 

 

7)測試類測試;

       核心的代碼都編寫完畢了,當然我們肯定是需要編寫代碼進行測試下,我們才放心了。

com.kfit.demo.bean.Demo

package com.kfit.demo.bean;

 

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.Id;

 

/**

 * 測試demo.

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Entity

public class Demo {

    @Id @GeneratedValue

    private longid;

   

    private String name;

 

    public long getId() {

       return id;

    }

 

    public void setId(longid) {

       this.id = id;

    }

 

    public String getName() {

       return name;

    }

 

    publicvoid setName(String name) {

       this.name = name;

    }

 

    @Override

    public String toString() {

       return"Demo [id=" + id + ", name=" + name + "]";

    }

}

 

com.kfit.demo.dao.TestDao

package com.kfit.demo.dao;

 

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.List;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.jdbc.core.RowMapper;

import org.springframework.stereotype.Service;

 

import com.kfit.demo.bean.Demo;

 

@Service

public class TestDao {

   

    @Autowired

    private JdbcTemplate jdbcTemplate;

   

    /**

     * 不指定數據源使用默認數據源

     * @return

     */

    public List<Demo> getList(){

       String sql = "select *from Demo";

        return (List<Demo>) jdbcTemplate.query(sql, new RowMapper<Demo>(){

            @Override

            public Demo mapRow(ResultSet rs, introwNum) throws SQLException {

                Demo demo = new Demo();

                demo.setId(rs.getLong("id"));

                demo.setName(rs.getString("name"));;

                returndemo;

            }

        });

    }

   

     /**

     * 指定數據源

     * 在對應的service進行指定;

     * @return

     * @author SHANHY

     * @create  2016124

     */

    public List<Demo> getListByDs1(){

        /*

         * 這張表示復制的主庫到ds1的,在ds中並沒有此表.

         * 需要自己自己進行復制,不然會報錯:Table 'test1.demo1' doesn't exist

         */

       String sql = "select *from Demo1";

        return (List<Demo>) jdbcTemplate.query(sql, new RowMapper<Demo>(){

 

            @Override

            public Demo mapRow(ResultSet rs, introwNum) throws SQLException {

                Demo demo = new Demo();

                demo.setId(rs.getLong("id"));

                demo.setName(rs.getString("name"));;

                returndemo;

            }

 

        });

    }

}

 

com.kfit.demo.service.TestService :

package com.kfit.demo.service;

 

import java.util.List;

 

import javax.annotation.Resource;

 

import org.springframework.stereotype.Service;

 

import com.kfit.config.datasource.dynamic.TargetDataSource;

import com.kfit.demo.bean.Demo;

import com.kfit.demo.dao.TestDao;

 

@Service

public class TestService {

   

    @Resource

    private TestDao testDao;

   

    /**

     * 不指定數據源使用默認數據源

     * @return

     */

    public List<Demo> getList(){

       returntestDao.getList();

    }

   

    /**

     * 指定數據源

     * @return

     */

    @TargetDataSource("ds1")

    public List<Demo> getListByDs1(){

        returntestDao.getListByDs1();

    }

}

 

com.kfit.demo.controller.TestController:

package com.kfit.demo.controller;

 

import javax.annotation.Resource;

 

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

 

import com.kfit.demo.bean.Demo;

import com.kfit.demo.service.TestService;

 

@RestController

public class TestController {

   

    @Resource

    private TestService testService;

   

    @RequestMapping("/test1")

    public String test(){

//     for(Demo d:testService.getList()){

//         System.out.println(d);

//     }

       for(Demo d:testService.getListByDs1()){

           System.out.println(d);

       }

       return"ok";

    }

   

}

 

好了,測試代碼就這么多了,運行App.java進行測試把,訪問:

http://127.0.0.1:8080/test1 查看控制台的打印。

 

這里需要提醒下,這種方式spring-jpa的方式好像不能自動路由,博主打算在之后的一篇文章介紹spring-jpa多數據源的問題。

 

【Spring Boot 系列博客】

 

0)前言【從零開始學Spring Boot :

http://412887952-qq-com.iteye.com/blog/2291496

 

1spring boot起步之Hello World【從零開始學Spring Boot:

http://412887952-qq-com.iteye.com/blog/2291500

2Spring Boot返回json數據【從零開始學Spring Boot

http://412887952-qq-com.iteye.com/blog/2291508

(15)Spring Boot使用Druid和監控配置【從零開始學Spring Boot】

http://412887952-qq-com.iteye.com/blog/2292362

16Spring Boot使用Druid(編程注入)【從零開始學Spring Boot

http://412887952-qq-com.iteye.com/blogs/2292376

17Spring Boot普通類調用bean【從零開始學Spring Boot】:

http://412887952-qq-com.iteye.com/blog/2292388

......

(35)Spring Boot集成Redis實現緩存機制【從零開始學Spring Boot】

http://412887952-qq-com.iteye.com/blog/2294942

 

(42)Spring Boot多數據源【從零開始學Spring Boot】 編輯

http://412887952-qq-com.iteye.com/blog/2302997

 

更多查看博客:http://412887952-qq-com.iteye.com/

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM