SpringBoot 虽然提供了很多优秀的 starter 帮助我们快速开发, 可实际生产环境的特殊性, 我们依然需要对默认整合配置做自定义操作, 提高程序的可控性, 虽然你配的不一定比官方提供的 starter 好上周因为工作和装修的事情, 导致博客没有正常更新, 害怕停更会让人懒惰起来, 挤了一点时间写了一篇内容比较简单的文章后面闲谈一下我是如何从装修小白到入门的经历
技术: Configuration,ComponentScan,PropertySource,EnableTransactionManagement,Bean,Value
说明: 文中只贴出了配置代码, 完整的测试代码在 github 上
源码: https://github.com/ITDragonBlog/daydayup/tree/master/Spring/itdragon-spring-anno
文章目录结构:
一 Java 编程配置
在 Spring4.x 之前, 应用的基本配置中一般使用 xml 配置的方式, 而在业务逻辑中建议使用注解的方式可在 Spring4.x 以后, 官方便开始推荐使用 Java 的编程配置来代替 xml 配置, 这又是为什么? 这两种配置又有什么优缺点呢?
Java 编程配置和 xml 配置
xml 配置优点: 对于我们这些老一辈的程序员来说 (_),xml 非常亲切, 使用简单, 容易扩展, 修改应用配置参数不需要重新编译
xml 配置缺点: 配置文件的读取和解析需要耗时, xml 配置文件内容太多会显得很臃肿, 不方便管理
Java 编程配置优点: 相对于 xml 配置而言, 其结构更清晰, 可读性更高, 同时也节省了解析 xml 耗时
Java 编程配置缺点: 修改应用配置参数需要重新编译其实并不是一个大的问题, 实际生成环境中, 应用配置完成后一般都不会也不敢去随意修改
两者各有千秋, 考虑到 Spring4.x 和 SpringBoot 都在推荐使用 Java 编程配置的方式, 那我们也应该顺应时代潮流, 你可以不用, 但你应该要懂!
Java 编程配置步骤
第一步: 创建配置类, 在类名上添加注解 Configuration, 告知 Spring 这是一个配置类, 其作用类似 xml 文件
第二步: 加载外部配置文件, 在类名上添加注解 PropertySource, 指定 properties 文件的读取路径
第三步: 获取应用配置属性值, 在属性变量上添加注解 Value, 通过 ${} 表达式获取配置文件中参数
第四步: 依赖注入, 在方法上添加 Bean 注解, 也可以用 FactoryBean
第一步和第四步的语法, 文章第二部分会详细介绍第二步和第三步的语法, 文章第三部分会详细介绍
- import javax.sql.DataSource;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.PropertySource;
- import org.springframework.jdbc.core.JdbcTemplate;
- import org.springframework.jdbc.datasource.DataSourceTransactionManager;
- import org.springframework.transaction.PlatformTransactionManager;
- import org.springframework.transaction.annotation.EnableTransactionManagement;
- import com.mchange.v2.c3p0.ComboPooledDataSource;
- /**
- * Spring 配置类
- * 配置数据源, 事务管理, bean, 自动扫描包
- * @author itdragon
- */
- @Configuration // 声明该类为配置类
- @PropertySource({"classpath:propertySource.properties"}) // 引入外部文件
- @ComponentScan("com.itdragon") // 配置自动扫描包的路径
- @EnableTransactionManagement // 开启基于注解的事务管理功能
- public class ApplicationContextConfig {
- @Value("${DATA_USER}")
- private String DATA_USER;
- @Value("${DATA_PAWD}")
- private String DATA_PAWD;
- @Value("${DATA_DRIVER}")
- private String DATA_DRIVER;
- @Value("${DATA_JDBC_URL}")
- private String DATA_JDBC_URL;
- @Bean // 数据源
- public DataSource dataSource() throws Exception{
- ComboPooledDataSource dataSource = new ComboPooledDataSource();
- dataSource.setUser(DATA_USER);
- dataSource.setPassword(DATA_PAWD);
- dataSource.setDriverClass(DATA_DRIVER);
- dataSource.setJdbcUrl(DATA_JDBC_URL);
- return dataSource;
- }
- @Bean // jdbc 模板
- public JdbcTemplate jdbcTemplate() throws Exception{
- JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
- return jdbcTemplate;
- }
- @Bean // 事务管理
- public PlatformTransactionManager transactionManager() throws Exception{
- return new DataSourceTransactionManager(dataSource());
- }
- }
事务测试代码, 请异步 github
二组件注入
Bean 注解类似 xml 文件中的 < bean > 标签, 其中被 Bean 注解修饰的方法名对应 < bean > 标签中的 id, 也可以通过 Bean 注解的 value 属性设置 id 的值在 SpringBoot 底层代码中被大量使用
若希望容器启动后创建对象, 并在使用后从容器中直接获取, 则什么都不需要做, 因为 Spring 默认是单实例, 即容器启动后创建对象, 并保存到容器中, 使用时再从容器中获取
若希望容器启动后不创建对象, 而是在使用时再创建, 继而保存到容器中, 下次使用再从容器中获取, 可以通过懒加载的方式实现, 即使用 Lazy 注解修饰
若希望容器启动后不创建对象, 而是在每次使用时创建, 则采用多实例的方式, 即使用 Scope 注解, 参数的值为 prototype, 即 @Scope("prototype")
若希望容器启动后根据条件选择需要注入的 Bean, 可以使用注解 Conditional 判断, SpringBoot 的底层打量使用了该注解
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.ComponentScan.Filter;
- import org.springframework.context.annotation.Conditional;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.FilterType;
- import org.springframework.context.annotation.Lazy;
- import org.springframework.stereotype.Controller;
- import com.itdragon.entity.ITDragonEntity;
- import com.itdragon.server.ITDragonServer;
- /**
- * 知识点二: 配置自动扫描包路径
- * 一注解 ComponentScan 的 value 值设置自动扫描包的路径
- * 二注解 ComponentScan 的 excludeFilters 值设置扫描排除的规则
- * 1) 通过注解 @Filter 设置排除的类型, type=ANNOTATION 表示按照注解包含排除 classes 是具体的注解, 如 Controller,Server,Repository
- * 三注解 ComponentScan 的 includeFilters 值设置扫描加入的规则
- * 1) 通过设置 useDefaultFilters=false 关闭 Spring 默认扫描全部的功能, 使 includeFilters 生效
- *
- * 知识点三:@Filter 常用的拦截类型
- * 一 FilterType.ANNOTATION: 按照注解
- * 二 FilterType.ASSIGNABLE_TYPE: 按照给定的类型, 包括其子类和实现类
- * 三 FilterType.CUSTOM: 使用自定义规则
- *
- * 第一个 ComponentScan 注解表示在指定包下不扫描通过 Controller 注解修饰的类和 ITDragonServer 类及其子类和实现类
- * 第二个 ComponentScan 注解表示在指定包下只扫描通过 Controller 注解修饰的类
- * 第三个 ComponentScan 注解表示在指定包下根据自定义拦截规则, 不扫描满足规则的类
- */
- @Configuration@ComponentScan(value = "com.itdragon", excludeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {
- Controller.class
- }),
- @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
- ITDragonServer.class
- })
- })
- //@ComponentScan(value="com.itdragon",includeFilters={@Filter(type=FilterType.ANNOTATION,classes={Controller.class})},useDefaultFilters=false)
- //@ComponentScan(value="com.itdragon",excludeFilters={@Filter(type=FilterType.CUSTOM,classes={ITDragonCustomTypeFilter.class})})
- public class ITDragonConfig {
- /**
- * 知识点一: 配置 bean
- * 一注解 Bean 的 value 值表示 bean 的 id
- * 二注解 Bean 的 value 值未设置, 则方法名表示 bean 的 id
- */
- @Bean(value = "itdragonBean") public ITDragonEntity itdragonEntity() { // 方法名很重要, 类似 xml 的 id 名, 也可以通过 @bean 的 value 值重定义
- return new ITDragonEntity("itdragon", "configuration-password", 25);
- }
- /**
- * 知识点四: Scope 属性
- * @Scope, 调整作用域, 默认单实例
- * singleton: 单实例: ioc 容器启动后创建对象放到 ioc 容器中, 需要是从容器中获取
- * prototype: 多实例: ioc 容器启动后每次获取对象时都要创建对象
- * request: 同一次请求创建一个实例
- * session: 同一个 session 创建一个实例
- *
- * 知识点五: 懒加载
- * 针对单实例而言, 在容器启动后不创建对象, 在第一次使用 Bean 时创建对象可以理解为单实例的一种补充
- *
- *$/ @Scope("prototype")
- @Lazy
- @Bean
- public ITDragonEntity scopeTopicBean() {
- System.out.println("^^^^^^^^^^^^^^^^^^^^^Create Bean");
- return new ITDragonEntity("scopeBean", "singleton-prototype-request-session", 25);
- }
- /**
- * 知识点六: Conditional 条件判断
- * 满足条件才会注册 bean, 可以修饰在类上, 管理整个类下的组件注入
- */
- @Bean@Conditional({
- ITDragonCustomCondition.class
- }) public ITDragonEntity conditionalBean() {
- return new ITDragonEntity("conditionalBean", "Conditional-Condition-CustomCondition", 25);
- }
- /**
- * 知识点七: FactoryBean 工厂 Bean
- * FactoryBean 默认通过调用 getObject 创建的对象, 通过调用 isSingleton 设置单实例和多实例
- */
- @Bean public ITDragonFactoryBean itdragonFactoryBean() {
- return new ITDragonFactoryBean();
- }
- }
三属性赋值
属性赋值步骤:
第一步: 通过注解 PropertySource 引入外部文件可以引入多个, 若担心文件不存在, 可以通过参数 ignoreResourceNotFound 设置忽略
第二步: 通过注解 Value 从外部文件中获取值, 一般采用 ${} 格式, 还支持 #{} SpEL 表达式, 也可以直接传字符串
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.PropertySource;
- import com.itdragon.entity.ITDragonEntity;
- /**
- * 知识点一: 引入外部文件, 并从文件中获取值
- * @PropertySource 引入外部文件, 支持多个, 如果文件不存在会报错, 可以通过设置参数 ignoreResourceNotFound=true 忽略
- * @Value 从外部文件中获取值, 支持 spel 表达式,#{},${},string
- * @author itdragon
- */
- @Configuration
- @PropertySource(value={"classpath:propertySource.properties","classpth:xxx.properties"},ignoreResourceNotFound=true)
- public class ITDragonConfigValue {
- @Value("${ACCOUNT}") // 从配置文件获取数据
- private String ACCOUNT;
- @Value("${PASSWORD}")
- private String PASSWORD;
- @Value("${AGE}")
- private Integer AGE;
- @Value("ITDragon") // 普通赋值
- private String str;
- @Value("#{(1+2-3)/4*5}") // 算术运算
- private String operator;
- @Value("#{1>2 || 2 <= 3}") // 关系运算
- private Boolean comparison;
- @Value("#{systemProperties['java.version']}") // 系统配置: os.name
- private String systemProperties;
- @Value("#{T(java.lang.Math).abs(-18)}") // 表达式
- private String mapExpression;
- @Bean("valueBean")
- public ITDragonEntity itdragonEntity() {
- System.out.println("^^^^^^^^^^^^^^^^^^^^ str :" + str);
- System.out.println("^^^^^^^^^^^^^^^^^^^^ operator :" + operator);
- System.out.println("^^^^^^^^^^^^^^^^^^^^ comparison :" + comparison);
- System.out.println("^^^^^^^^^^^^^^^^^^^^ systemProperties :" + systemProperties);
- System.out.println("^^^^^^^^^^^^^^^^^^^^ mapExpression :" + mapExpression);
- return new ITDragonEntity(ACCOUNT, PASSWORD, AGE);
- }
- }
四闲谈学习
这里并不是介绍如何学习一门技术, 而是论养成一个学习习惯的重要性大一时期, 因为担心找不到工作而报了一个线上培训机构经常被洗脑, 其中一句话而我印象深刻 ---- "让优秀成为一种习惯" 听得我热血沸腾花了五六千大洋报名, 后来才发现网上有免费的个人不建议参加培训
这钱花的还算值得,"让优秀成为一种习惯", 这句话对我的影响很大, 从大学到工作, 每当遇到陌生的知识, 并没有选择逃避它而是抽时间从网上找资料, 去学习, 整理, 实践直到弄懂它可我万万没有想到, 这种学习的精神竟然用到了装修上可能学习已经是我的一个习惯了吧!
开始, 我是一个装修小白, 不知道什么是全包半包清包; 不知道什么是硬装软装; 也不知道装修的流程; 不知道水电线橱柜洁具的品牌选择, 不知道挂机柜机风管机中央空调的优缺点; 不知道封阳台的利弊; 更不知道一个装修效果图要七八千面对这些未知的领域, 我寸步难行我清楚的知道: 如果你不懂, 你就是砧板上的鱼肉, 任人宰割
现在, 我通过在百度, 知乎, 兔巴兔等平台上找答案, 并把内容用 Markdown 的格式整理! 我都被自己吓到了不仅如此, 我还在抢设计师的饭碗, 自己动手设计效果图在制作效果图的过程中, 发现了很多不合理的设想庆幸问自己设计了一套效果图, 不然又有很多无用功, 耗时, 耗力, 耗钱爸妈和女票就是客户, 而我就一直处于改! 改! 改! 的阶段体验了一把前端工程师的辛酸
我是谁? 我在哪? 我在做什么?
我是一名程序员, 我在学习的道路上, 我在做能提高自己的事情!
来源: https://www.cnblogs.com/itdragon/p/8658946.html