180531-Spring中JavaConfig知识小结
Sring中JavaConfig使用姿势
去掉xml的配置方式,改成用Java来配置,最常见的就是将xml中的 bean定义, scanner包扫描,属性文件的配置信息读取等
I. 几个基本注解
1. Configuration注解
在javaConfig中注解@Configuration用来代替一个xml文件,可以简单的理解他们的作用是相等的,一般bean的定义也都是放在被这个注解修饰的类中
如一个基本的配置文件如下
@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
private Environment environment;
@Autowired
public void setEnvironment(Environment environment) {
this.environment = environment;
System.out.println("then env: " + environment);
}
@Bean(name="connectionFactory")
public ConnectionFactory connectionFactory() {
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
return factory;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
}
2. Bean 注解
上面的例子中,在方法上添加了@Bean注解,这个就相当于传统的
<bean name="rabbitAdmin" class="org.springframework.amqp.rabbit.core.RabbitAdmin"/>
因此在需要引入rabbitAdmin实例的地方,可以如下使用
a. 属性字段上添加 @Autowired注解
public class RConsumer {
@Autowired
private RabbitAdmin rabbitAdmin;
}
b. 设置方法上添加 @Autowired注解
public class RConsumer {
private RabbitAdmin rabbitAdmin;
@Autowired
public void setRabbitAdmin(RabbitAdmin rabbitAdmin) {
this.rabbitAdmin = rabbitAdmin;
}
}
c. 使用构造器的方式
public class RConsumer {
private RabbitAdmin rabbitAdmin;
public RConsumer(RabbitAdmin rabbitAdmin) {
this.rabbitAdmin = rabbitAdmin;
}
}
上面就是Spring容器支持的几种典型的IoC方式
3. ComponentScan
这个类似于xml中的 <context:component-scan"/> 标签
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
}
上面的这个配置,表示自动扫描包 com.git.hui.rabbit.spring 下面的bean (要求类上添加了 @Component, @Repository, @Service)
那么一个问题来了,如果一个类既被自动扫描加载,又显示定义了bean,会怎样?
package com.git.hui.rabbit.spring;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class TestBean {
private static AtomicInteger count = new AtomicInteger(1);
public TestBean() {
System.out.println("testBean count: " + count.getAndAdd(1));
}
}
对应的JavaConfig
@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig {
@Bean
public TestBean testBean() {
return new TestBean();
}
}
实际测试,发现这个bean只会有一个实例,即输出计数只会有一条,实际查看ApplicationContext中的内容,TestBean的实例,也确实只有一个,如果改成下面这种场景呢
@Bean(name="testBean2")
public TestBean testBean() {
return new TestBean();
}
会有两条记录输出,实际查看容器中的Bean对象,会有两个实例如下

这和我们的预期也是一样的,因为一个类我可能需要多个不同的Bean实例来干一些事情
那么出现这种JavaConfig定义的beanName与自动扫描的冲突的情况会怎样呢?
新增一个NewBean对象,
public class NewBean {
private static AtomicInteger count = new AtomicInteger(1);
public NewBean() {
System.out.println(" newbean count: " + count.getAndAdd(1));
}
}
在JavaConfig中新加一个bean定义,但是BeanName与自动扫描的TestBean重复了
@Bean(name="testBean")
public NewBean newBean() {
return new NewBean();
}
此时发现有意思的事情了,从Spring容器中,将查不到TestBean的实例,但是可以查到NewBean的实例

这个的表现是:
- 当beanName出现冲突时,JavaConfig的优先级会高于自动加载的,导致自动加载的Bean不会被加载到容器内
那么跟着来的一个问题就是如果JavaConfig中定义了两个相同的BeanName的bean呢?
@Bean(name = "testBean2")
public NewBean newBean() {
return new NewBean();
}
@Bean(name = "testBean2")
public TestBean testBean() {
return new TestBean();
}
因为我们TestBean上加了@Component注解,因此容器中至少有一个,但是否会有testBean2这个实例呢? 通过实际查看是没有的,testBean2这个名被 NewBean 占领了

so,表现上看,加上实测,将上面的定义换个位置,得出下面的结论
- 当出现beanName重名时,先定义的Bean占优
然后就是最后一个问题了,当自动扫描时,两个类包不同,但是类名相同,会怎样?
package com.git.hui.rabbit.spring.demo;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class TestBean {
private static AtomicInteger count = new AtomicInteger(1);
public TestBean() {
System.out.println(" demo.TestBean count: " + count.getAndAdd(1));
}
}
实测,会抛出一个异常,在使用xml的配置方式时,经常见到的一个BeanName冲突的异常
org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'testBean' for bean class [com.git.hui.rabbit.spring.demo.TestBean] conflicts with existing, non-compatible bean definition of same name and class [com.git.hui.rabbit.spring.TestBean]
小结:
- JavaConfig 定义的BeanName与自动扫描的BeanName冲突时,JavaConfig的定义的会被实例化
- JavaConfig 中定义了BeanName相同的Bean时,优先定义的有效(这里不抛异常不太能理解)
- 自动扫描的Bean,不支持类名相同,但是包路径不同的场景(会抛异常)
4. Import
在xml配置中,另一个常见的case就是引入另一个xml配置,在JavaConfig中代替的就是Import注解
@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
@Import({DirectConsumerConfig.class, FanoutConsumerConfig.class, TopicConsumerConfig.class})
public class SpringConfig {
}
这个就等同于xml中常见的:
<import resource="service.xml" />
II. 实例测试
1. xml单测姿势
上面说了用JavaConfig代替xml配置的方式,另一个关键的地方就是测试用例的写法了,对于之前的xml,有两种常见的使用姿势
case1: 注解方式
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:*.xml")
public class BeanTest {
}
case2: 主动加载容器方式
private ServiceA serviceA;
@Before
public void init() {
ApplicationContext apc = new ClassPathXmlApplicationContext("classpath:*.xml");
serviceA = (ServiceA) apc.getBean("serviceA");
}
2. JavaConfig单测使用姿势
那么替换成JavaConfig的用法,也有两种
case1: 注解方式,指定内部classes值
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class SprintUnit {
}
case2: 主动加载容器,改为AnnotationConfigApplicationContext
@Test
public void testServiceA() {
ApplicationContext context = new AnnotationConfigApplicationContext(BeansConfiguration.class);
ServiceA serviceA = (ServiceA) context.getBean("serviceA");
serviceA.print();
}
III. 小结
1. 注解映射关系
JavaConfig方式基本上采用的是替换的思路来取代xml,即原xml中的一些东西,可以直接通过注解来代替,如
- @Configuration 修饰类,与传统的xml文件作用相同
- @Bean注解,修饰方法,表示声明一个Bean,与原来的xml中的
<bean>标签作用相同 - @ComponentScan注解,自动扫描包,类似xml中的
<context:component-scan> - @Import注解,与xml中的
<import>标签类似,引入其他的配置信息
2. BeanName重名规则
在实际使用中,有一点需要额外注意,对于beanName相同的情况,通过测试的规则如下(没有看源码,不保证完全准确,仅为测试后得出的依据):
- JavaConfig 定义的BeanName与自动扫描的BeanName冲突时,JavaConfig的定义的会被实例化
- JavaConfig 中定义了BeanName相同的Bean时,优先定义的有效(这里不抛异常不太能理解)
- 自动扫描的Bean,不支持类名相同,但是包路径不同的场景(会抛异常)
3. 测试姿势
最简单的就是修改原来的注解@ContextConfiguration中的值
@ContextConfiguration(classes = SpringConfig.class)
II. 其他
一灰灰Blog: https://liuyueyi.github.io/hexblog
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
声明
尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840
扫描关注

180531-Spring中JavaConfig知识小结的更多相关文章
- spring中JavaConfig相关的注解
在spring3.0中增加配置spring beans的新方式JavaConfig,可以替换spring的applicataion.xml配置.也即@Configuration对等<beans/ ...
- Spring中JavaConfig特性
从Spring3開始,增加了JavaConfig特性.JavaConfig特性同意开发人员不必在Spring的xml配置文件里定义bean,能够在Java Class中通过凝视配置bean,假设你讨厌 ...
- 项目中Spring注入报错小结
之前在做单元测试时采用注解方式进行service对象的注入,但运行测试用例时对象要注入的service对象总是空的,检查下spring配置文件,我要配置的bean类xml文件已经包含到spring要加 ...
- Velocity初探小结--Velocity在spring中的配置和使用
最近正在做的项目前端使用了Velocity进行View层的数据渲染,之前没有接触过,草草过了一遍,就上手开始写,现在又回头细致的看了一遍,做个笔记. velocity是一种基于java的模板引擎技术, ...
- Spring AOP基础知识
Spring AOP使用动态代理技术在运行期织入增强的代码,两种代理机制包括:一是基于JDK的动态代理,另一种是基于CGLib的动态代理.之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的 ...
- Spring中你可能不知道的事(一)
Spring作为Java的王牌开源项目,相信大家都用过,但是可能大家仅仅用到了Spring最常用的功能,Spring实在是庞大了,很多功能可能一辈子都不会用到,今天我就罗列下Spring中你可能不知道 ...
- 4-1 Spring框架基础知识
Spring框架基础知识 1.Spring 框架作用 主要解决了创建对象和管理对象的问题. 自动装配机制 2.Spring 框架 (Spring容器,JavaBean容器,Bean容器,Spring容 ...
- Android app开发知识小结
Android知识小结 这是一个知识的总结,所以没有详解的讲解. 一.分辨率Android中dp长度.sp字体使用.px像素.in英寸.pt英寸1/72.mm毫米 了解dp首先要知道density,d ...
- 轻松了解Spring中的控制反转和依赖注入(一)
我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达170平方米,重达30吨,到现如今的个人笔记本,事物更加轻量功能却更加丰富,这是事物发展过程中的一个趋势,在技术领域中同样也是如此,企业级Ja ...
随机推荐
- 关于webstorm打开项目,文件下方出现了一个小锁的图标,修改文件出现“cannot modify a ready-only directory”的弹窗提示
今天用webstorm打开项目,文件下方出现了一个小锁的图标,修改文件出现“cannot modify a ready-only directory”的弹窗提示 解决办法:运行 sudo chown ...
- 用条件变量实现事件等待器的正确与错误做法--转自陈硕的Blog
用条件变量实现事件等待器的正确与错误做法 TL;DR 如果你能一眼看出 https://gist.github.com/chenshuo/6430925 中的那 8 个 Waiter classes ...
- Razor
什么是Razor 1.Razor是一种将基于服务器的代码添加到网页中的标记语法 2.Razor具有传统ASP.NET标记功能 3.Razor是一种服务器端的标记语法 4.Razor同时支持C#和VB ...
- IOS 文件名获取简洁方式
//这里有一个模拟器沙盒路径(完整路径) NSString* index=@"/Users/junzoo/Library/Application Support/iPhone Simulat ...
- 协作开发中常用的Git命令小结
先提一下最基础的git命令用法: git clone 从远端克隆到本地仓库 git add . (注意add和. 之间有一个空格)将全部改动添加到暂存区 git checkout xxx 撤销更改 ...
- git 的一些基本命令小结
Git是目前世界上最先进的分布式版本控制系统 对于git 的用法,本文并不属于教程,只是总结记录一些平时用的简单命令 git的下载地址:https://git-scm.com/downloads 主要 ...
- 关于js代码位置的第一次总结
最近在学习dom树节点操作时,发现查找结点总是返回null,原因在于将js代码放在了head里,因为页面是从上往下逐行加载,在还未加载相关节点时当然查找不到,返回值为null. 而对于另一句老生常谈的 ...
- MySQL---事务、函数
事务 事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性. delimiter \\ create PROCEDURE p1( OUT ...
- webpack新建项目
记录如何搭建一个最简单的能跑的项目! 1.首先,需要下载安装nodejs环境,可以直接百度搜索nodejs去官网下载符合你操作系统的环境. 安装完nodejs后,在控制台输入命令: npm -vers ...
- 文件后缀与Mime类型对照表
以下是一些文件后缀(扩展名)对应的MIME类型的一个对照表,方便iis中或其他服务器对相应的文件进行解析.有些文件的后缀名没有默认解析就出现上传后无法访问或者下载的问题,这个时候就要设置文件后缀对应的 ...