Java编程配置思路详解

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

事务类

import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.itdragon.dao.ITDragonDao; @Service
public class ITDragonServer { @Autowired(required=false)
private ITDragonDao itdragonDao; public List<Map<String,Object>> findAll() {
return itdragonDao.findAll();
} @Transactional
public void updateNameById(String name, Long id) {
itdragonDao.updateUserNameById(name, id);
System.out.println(0/0); // 事务异常
}
}

完整代码,请异步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();
}
}

自定义的条件判断组件注入类

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata; /**
* 自定义的条件判断组件注入
* @author itdragon
*
*/
public class ITDragonCustomCondition implements Condition{ /**
* 判断注册的bean中是否含有指定的bean
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取bean的注册类
BeanDefinitionRegistry registry = context.getRegistry();
return registry.containsBeanDefinition("scopeTopicBean"); // 有则加载conditionalBean
} }

自定义Bean的工厂类

import org.springframework.beans.factory.FactoryBean;
import com.itdragon.entity.ITDragonEntity;
/**
* 自定义Bean的工厂类
* @author itdragon
*
*/
public class ITDragonFactoryBean implements FactoryBean<ITDragonEntity>{ public ITDragonEntity getObject() throws Exception {
System.out.println("^^^^^^^^^^^^^^^^^^^^^FactoryBean Create Bean");
return new ITDragonEntity(); // 创建对象并返回到容器中
} public Class<?> getObjectType() {
return ITDragonEntity.class;
} public boolean isSingleton() {
return false; // 设置多实例,true则为单例
} }

三、属性赋值

属性赋值步骤:

第一步:通过注解PropertySource引入外部文件。可以引入多个,若担心文件不存在,可以通过参数ignoreResourceNotFound设置忽略

第二步:通过注解Value从外部文件中获取值,一般采用${}格式,还支持#{} SpEL表达式,也可以直接传字符串。如果想接收一些复杂的值,比如集合,可以考虑使用注解ConfigurationProperties,后续会详细介绍两者的优缺点。

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的格式整理!我都被自己吓到了。不仅如此,我还在抢设计师的饭碗,自己动手设计效果图。在制作效果图的过程中,发现了很多不合理的设想。比如之前打算在主卧的左上角放一个电脑桌,墙上打几个小格子。可在实际的效果图中,和床的位置有了冲突,重新改了方案。不然又是一个无用功,不仅耗时,耗力,还耗钱。爸妈和女票就是客户,而我就一直处于改!改!改!的阶段。体验了一把前端工程师的辛酸。(往左边移一点,往右边移一点,移多了,换个颜色试试,o(≧口≦)o)

我是谁?我在哪?我在做什么?

我是一名程序员,我在学习的道路上,我在做能提高自己的事情!

Java编程配置思路详解的更多相关文章

  1. Java网络编程和NIO详解8:浅析mmap和Direct Buffer

    Java网络编程与NIO详解8:浅析mmap和Direct Buffer 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NI ...

  2. Java网络编程和NIO详解9:基于NIO的网络编程框架Netty

    Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...

  3. Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理

    Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...

  4. Java网络编程和NIO详解6:Linux epoll实现原理详解

    Java网络编程和NIO详解6:Linux epoll实现原理详解 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO h ...

  5. Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO

    Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO Java 非阻塞 IO 和异步 IO 转自https://www.javadoop.com/post/nio-and-aio 本系 ...

  6. Java网络编程和NIO详解开篇:Java网络编程基础

    Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...

  7. Java网络编程和NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector

    Java网络编程与NIO详解4:浅析NIO包中的Buffer.Channel 和 Selector 转自https://www.javadoop.com/post/nio-and-aio 本系列文章首 ...

  8. Java网络编程和NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型

    Java网络编程与NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型 知识点 nio 下 I/O 阻塞与非阻塞实现 SocketChannel 介绍 I/O 多路复用的原理 事件选择器与 ...

  9. Java网络编程和NIO详解3:IO模型与Java网络编程模型

    Java网络编程和NIO详解3:IO模型与Java网络编程模型 基本概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32 ...

随机推荐

  1. JavaScript控制输入框只能输入非负正整数

    1.问题背景 问题:一个输入框,输入的是月份,保证输入的内容只能是非负正整数 2.JavaScript代码 function checkMonth() { $("month").k ...

  2. Defeat the Enemy UVALive - 7146

      Long long ago there is a strong tribe living on the earth. They always have wars and eonquer other ...

  3. jQuery.isPlainObject()的作用

    jQuery.isPlainObject()函数用于判断指定参数是否是一个纯粹的对象. 所谓"纯粹的对象",就是该对象是通过"{}"或"new Obj ...

  4. freemarker基本数据类型(十一)

    freemarker基本数据类型 1.基本数据类型 (1)字符串 (2)数字 (3)布尔值 (4)日期 2.展示示例 <html> <head> <meta http-e ...

  5. RobotFramework自动化测试框架-DatabaseLibrary库的使用(对数据库的操作)

    在自动化过程中,我们经常需要连接不同的数据库,并且对数据库进行很多不同的操作,RobotFramework中,提供了DatabaseLibrary这个库来操作数据库,我们可以按照官网中的说明来安装Da ...

  6. 1.4 如何在main()方法之前执行输出“hello world”

    public class Test{ static{ System.out.println("hello world"); } public static void main(St ...

  7. angular路由模块(二)

    上一章写的是如何创建一个简单的路由,这一样我们来看看如何创建一个路由模块.angular的思想就是(模块,组件,子组件.....). 我们在src/app目录下创建一个跟路由模块app-routing ...

  8. 无需安装Oracle Client连接Oracle数据库

    介绍 当我们采用 ODP.NET 检索Oracle 数据库的时候,Oracle客户端是必须安装.假如当时电脑上没有安装Oracle客户端,就不能这么用了,这时候Oracle.ManagedDataAc ...

  9. Java接口-----代理模式(Proxy)

    public static void main(String[] args) { // TODO Auto-generated method stub ProxySubject a = new Pro ...

  10. FJUT2017寒假训练二题解

    A题 题意:让你找出唯一的一个四位数,满足对话时的要求. 思路:因为是4位数,可以直接从1000-9999遍历一遍,判断是否有唯一的数能满足所有条件,如果不是唯一的或者没有满足条件的数就输出Not s ...