Spring开发一个简单的starter——c3p0自动配置

Posted 依姿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring开发一个简单的starter——c3p0自动配置相关的知识,希望对你有一定的参考价值。

上篇文章写了spring boot自动配置原理,现在尝试自己开发一个starter,供给spring boot完成自动配置。

在这里我们就用c3p0连接池为例,c3p0是一个比较老的连接池,在远程仓库也没有对应的starter。所以在这里的目的就是开发一个简单的c3p0的starter,达到的效果就是像使用过的driud连接池一样,在yml文件指定一些配置信息,数据源就可以自动装配好了。

在创建项目之前,先说明一下spring官方对自定义starter命名的规范和约定,spring boot官方的starter的命名规范是“spring-boot-starter-模块名”,如tomcat:spring-boot-starter-tomcat。那么自定义的starter命名规范是“模块名-spring-boot-starter”,如mybatis:mybatis-spring-boot-starter。自定义跟官方的区别就是模块名放在了后面,这样做的目的就是有区别于官方的starter。

打开idea新建一个普通的maven项目,命名为c3p0-spring-boot-starter。

1、pom.xml添加依赖:

<properties>
    <java.version>1.8</java.version>
    <spring.boot.version>2.2.4.RELEASE</spring.boot.version>
</properties>

<dependencies>
    <!-- 添加c3p0连接池依赖 -->
    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>
    <!-- 添加自动配置的依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
        <version>${spring.boot.version}</version>
    </dependency>
    <!-- 将@ConrigurationProperties注解的类属性注入到元数据,
         再idea中如果有元数据则可以提供良好的代码智能提示功能 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <version>${spring.boot.version}</version>
    </dependency>
</dependencies>

2、添加自动配置类

根据spring boot的约定,创建一个org.c3p0.spring.boot.autoconfigure的package:

在包中创建C3p0DatasourceAutoConfigure类,这个也是c3p0的核心自动配置类。在类之上,也要添加一些注解,首先添加@Configuration标识这是一个配置类,然后就是添加条件注解,如下代码:

@Configuration
/**
 * c3p0里面的数据源的类就是ComboPooledDataSource
 * 当ComboPooledDataSource存在classpath时,则初始化当前配置类
 */
@ConditionalOnClass(ComboPooledDataSource.class)
public class C3p0DatasourceAutoConfigure {
}

3、读取yml配置文件的配置信息并松散绑定

spring boot是在properties或者yml文件中进行配置的,所以自动配置就要把配置文件中的配置信息绑定到类中,在这里新建一个C3p0DatasourceProperties用于yml的松散绑定。这个类中的属性就是连接池所需要的属性,这里简单列举几条。

/**
 * 松散绑定,prefix表明在yml的前缀
 */
@ConfigurationProperties(prefix = "spring.datasource.c3p0")
public class C3p0DatasourceProperties {

    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private Integer minPoolSize;
    private Integer maxPoolSize;
    private Integer initialPoolSize;

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

    public Integer getMinPoolSize() {
        return minPoolSize;
    }

    public void setMinPoolSize(Integer minPoolSize) {
        this.minPoolSize = minPoolSize;
    }

    public Integer getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(Integer maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public Integer getInitialPoolSize() {
        return initialPoolSize;
    }

    public void setInitialPoolSize(Integer initialPoolSize) {
        this.initialPoolSize = initialPoolSize;
    }
}

写完这些后,会发现在C3p0DatasourceProperties上的@ConfigurationProperties注解中有报红,原因是当我们使用这个注解标注一个类的时候,spring默认是不会将这个类纳入ioc容器管理的,除非在类上加入@Component注解,当然还有另外一个方式,就是在配置类上加@EnableConfigurationProperties注解:

@Configuration
/**
 * 当ComboPooledDataSource存在classpath时,则初始化当前配置类
 */
@ConditionalOnClass(ComboPooledDataSource.class)
/**
 * 将带有@ConfigurationProperties注解的类纳入spring容器管理
 */
@EnableConfigurationProperties(C3p0DatasourceProperties.class)
public class C3p0DatasourceAutoConfigure {
}

4、编写核心自动配置类

接下来就是要配置数据源了:

@Configuration
/**
 * 当ComboPooledDataSource存在classpath时,则初始化当前配置类
 */
@ConditionalOnClass(ComboPooledDataSource.class)
/**
 * 将带有@ConfigurationProperties注解的类纳入spring容器管理
 */
@EnableConfigurationProperties(C3p0DatasourceProperties.class)
public class C3p0DatasourceAutoConfigure {

    @Autowired
    private C3p0DatasourceProperties properties;

    @Bean
    /**
     * 如果容器中不存在Datasource时,则创建Bean实例
     */
    @ConditionalOnMissingBean
    public DataSource dataSource(){
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(properties.getDriverClassName());
            dataSource.setJdbcUrl(properties.getUrl());
            dataSource.setUser(properties.getUsername());
            dataSource.setPassword(properties.getPassword());
            dataSource.setMinPoolSize(properties.getMinPoolSize());
            dataSource.setMaxPoolSize(properties.getMaxPoolSize());
            dataSource.setInitialPoolSize(properties.getInitialPoolSize());
            return dataSource;
        } catch (PropertyVetoException e) {
            e.printStackTrace();
            throw new RuntimeException("error. " + e);
        }
    }
}

5、创建factories文件

spring boot的自动配置原理就是扫描META-INF目录下的spring.factories文件,拿到里面的完整类名并交由spring管理,所以在自定义starter也要有这样一个目录和文件。

在resources目录下新建META-INF目录,里面新建一个spring.factories文件。

spring.factories:

# \\斜杠表示换行符
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\
  org.c3p0.spring.boot.autoconfigure.C3p0DatasourceAutoConfigure

factories文件的key指定的是org.springframework.boot.autoconfigure.EnableAutoConfiguration,也就是开启自动配置的注解的完整类名,value就是指定自定义starter的核心自动配置类的完整类名。

这样,c3p0的starter就算开发完成。

6、打包

starter最终是要打成jar包使用的,如果要在本地使用,那么最简单的方法就是打成jar包后安装在本地仓库中。

在idea中,打开maven视图层,运行项目的install命令,这样前面的所有命令都会执行:

当控制台报时,说明打包并安装成功,可以在其他项目中使用了。

7、测试

新建一个spring boot项目,添加依赖:

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

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- 依赖c3p0的starter -->
    <dependency>
        <groupId>edu.nf</groupId>
        <artifactId>c3p0-spring-boot-starter</artifactId>
        <version>1.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

在yml文件中添加一些基本配置

spring:
  datasource:
    c3p0:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8
      username: root
      password: root
      min-pool-size: 5
      max-pool-size: 20
      initial-pool-size: 5

测试数据源:

@SpringBootTest
class Ch08ApplicationTests {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    void testDatasource() {
        ComboPooledDataSource dataSource = (ComboPooledDataSource) jdbcTemplate.getDataSource();
        System.out.println("min-pool-size: " + dataSource.getMinPoolSize());
        List<Map<String, Object>> list = jdbcTemplate.queryForList("select city_name from city_info limit 0,5");
        for (Map<String, Object> map : list) {
            for (String key : map.keySet()){
                System.out.println(map.get(key));
            }
        }
    }

}

 

 

 

以上是关于Spring开发一个简单的starter——c3p0自动配置的主要内容,如果未能解决你的问题,请参考以下文章

在spring开发中,c3p0.jar是啥东西,有啥作用,跟我们数据库驱动sqljdbc4.jar有啥不同

018-Spring Boot Starter开发

手写一个简单的Starter

spring-boot整合dubbo:Spring-boot-dubbo-starter

记 SpringBoot starter的开发过程

开发一个属于自己的Spring Boot Starter