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

     

     

  • 相关阅读:
    什么是HTTP
    通过递归法解决阶梯问题(n个台阶,上楼可以一步上1阶,也可以一步上2阶,一共有多少种上楼的方法)
    在Intelli Idea中使用plantuml(plantuml时序图的使用)
    Java中if(boolean)与if(boolean=true)的区别
    实现一个Servlet程序
    退出mysql的编辑模式
    mysql数据库基本操作命令行
    通过mysql命令查看mysql服务实例支持的搜索引擎
    Mac环境下使用终端启动Mysql,并进行mysql数据库的连接
    路飞学城Python-Day4
  • 原文地址:https://www.cnblogs.com/zhangcaihua/p/12838787.html
Copyright © 2020-2023  润新知