慢慢来比较快,虚心学技术
前言:创建应用对象之间协作关系的行为通常称为装配( wiring ),这也是依赖注入( DI )的本质
Spring提供三种Bean装配机制:
- 在 XML 中进行显式配置。
- 在 Java 中进行显式配置
- 隐式的 bean 发现机制和自动装配
一、什么是自动化装配?
很显然,通过前两篇文章的描述,我们可以发现,Spring的配置方式可以很简洁,也可以也很复杂,JavaConfig配置方式使用大量的注解替代了XML中的配置,那么,基于JavaConfig的基础之上,是否可以再次封装或简化配置呢?
Spring实现了纯注解配置的自动化隐式装配,所谓的隐式装配就是不需要像XML和JavaConfig一样去为每一个Bean创建
其中包含两个概念:
- 组件扫描( component scanning ): Spring 会自动发现应用上下文中所创建的 bean 。
- 自动装配( autowiring ): Spring 自动满足 bean 之间的依赖。【DI】
二、如何实现自动化装配
ⅰ.四个基本注解:
@Autowired :标记于属性,方法等,自动装配的关键注解,依赖注入的表现,该注解可以自动寻找并从Spring容器中提取使用该注解的bean并注入到对应的属性中去
@Component :标记于类,标明当前类是一个可被扫描的组件
@ComponentScan :标记于配置类,开启组件注解扫描
@Configuration :标记于配置类,标明当前类是一个配置类
ⅱ.基本实现:
①定义基本接口CDPlayer
public interface CDPlayer {
/**
* 定义方法播放CD
* @param
*
* @return void
*
* @author lai.guanfu 2019/2/27
* @version 1.0
**/
void playCD();
}
②定义基本类CDBean
@Data//Data是lobok的注解,自动添加setter和getter方法等
@Component//将当前类定义为可扫描的组件
public class CDBean {
/**
* 定义CD名
*/
private String title="The World!";
/**
* 定义CD作者
*/
private String author="Mr.D";
}
③定义实现类CDPlayerImpl,并将CDBean作为属性注入
@Component//将当前类定义为可扫描的组件
public class CDPlayerImpl implements CDPlayer {
/**
* 从Spring容器注入CDBean-----依赖注入
*/
@Autowired
private CDBean cdBean;
@Override
public void playCD() {
System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
}
}
④定义配置类CDConfig,并开启注解扫描
@ComponentScan()//开启注解扫描
@Configuration//指定当前类为配置类
public class CDConfig {}
⑤编写测试
此处使用Spring提供的测试类SpringJUnit4ClassRunner帮助测试,以便在测试开始的时候自动创建 Spring 的应用上下文
而@ContextConfiguration可以指定创建上下文的加载方式以及配置的位置等
@RunWith(SpringJUnit4ClassRunner.class)//辅助创建Spring应用上下文
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {CDConfig.class})//指定创建上下文的加载方式以及配置的位置
public class AppTest
{
/**
* 从Spring容器注入CdPlayer-----依赖注入
*/
@Autowired
private CDPlayer cdPlayer;
/**
* Rigorous Test :-)
*/
@Test
public void play()
{
//调用playCD方法测试是否自动装配及依赖注入成功
this.cdPlayer.playCD();
}
}
⑥测试结果,自动装配成功,依赖注入成功
正在播放:The World! by Mr.D
ⅲ.关注点:
① 提供另一种测试方式:通过上一篇文章中提及的AnnotationConfigApplicationContext上下文实现进行测试
public static void main(String[] args) {
/**
* 注解配置实现,同时指定配置类的位置,否则无法读取配置进行扫描,默认只有Spring自带的组件
*/
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CDConfig.class);
/**
*获取应用上下文中的所有Bean名称
*/
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String className : beanDefinitionNames){
System.out.println(className);
}
/**
*从上下文(容器)中获取对应的Bean
*/
CDPlayer cdPlayer = applicationContext.getBean(CDPlayer.class);
/**
*调用具体方法测试是否成功
*/
cdPlayer.playCD();
}
测试结果:自动装配成功,依赖注入成功
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
CDConfig
app//当前测试类的名字
CDBean
CDPlayerImpl
正在播放:The World! by Mr.D
② 为装配的Bean命名
Spring 应用上下文中所有的 bean 都会给定一个 ID,默认是类名的首字母小写命名,我们可以为装配的Bean装配一个不一样的ID,在 @Component注解中有一个属性value,该值可指定bean在Spring容器中的ID,其在@Component注解的源码定义如下:
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
我们可以给CDPlayerImpl指定一个别的ID:cdPlayer
@Component(value = "cdPlayer")
public class CDPlayerImpl implements CDPlayer {
@Autowired
private CDBean cdBean;
@Override
public void playCD() {
System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
}
}
此时执行上一测试代码结果中,CDPlayerImpl在容器中的ID类名将变成cdPlayer
另:可以使用@Name代替@Component注解为Bean命名,但是为了避免概念模糊,建议使用@Component注解
③ 设置组件扫描的基础包
上述代码中,配置类CDConfig仅能扫描自身所在包及其子包的组件,能否指定扫描某个或多个包及其子包中的组件呢?
我们看到@ComponentScan注解的源码中定义的几个属性:
public @interface ComponentScan {
/**
* Alias for {@link #basePackages}.
* Allows for more concise annotation declarations if no other attributes
* are needed — for example, {@code @ComponentScan("org.my.pkg")}
* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
*/
@AliasFor("basePackages")
String[] value() default {};
/**
* Base packages to scan for annotated components.
*
{@link #value} is an alias for (and mutually exclusive with) this
* attribute.
*
Use {@link #basePackageClasses} for a type-safe alternative to
* String-based package names.
*/
@AliasFor("value")
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages} for specifying the packages
* to scan for annotated components. The package of each class specified will be scanned.
*
Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*/
Class>[] basePackageClasses() default {};
}
从源码得知,我们可以通过配置basePackages和basePackageClasses属性来指定配置扫描的范围,其中:
basePackages:指定组件扫描的包路径(使用{}包含的数组)
@ComponentScan(basePackages = {"com.my.spring","com.my.test"})//指定扫描com.my.spring包和com.my.test包及其子包下的组件
@Configuration
public class CDConfig {}
basePackageClasses:指定以该类数组所在包及其子包为组件扫描范围
@ComponentScan(basePackageClasses = {App.class})//指定扫描App.class所在包及其子包下的组件
@Configuration
public class CDConfig {}
注:通常,为了防止因包路径更改以及业务实体类更改等因耦合而产生的问题,我们通常会使用一个不具备任何业务意义的空接口作为扫描包的类
④自动注入注解@Autowired的使用
可以用在属性,构造函数,setter以及任何一个普通方法中
@Component(value = "cdPlayer")
public class CDPlayerImpl implements CDPlayer {
//1.在属性注入
@Autowired
private CDBean cdBean;
public CDPlayerImpl(){super()};
//2.在构造函数中注入
@Autowired
public CDPlayerImpl(CDBean cdBean){super()};
//3.在setter方法中注入
@Autowired
public void setCDBean(CDBean cdBean){this.cdBean = cdBean};
//4.在普通方法中使用
@Autowired
@Override
public void playCD(CDBean cdBean) {
System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
}
}
总结
1.自动化装配使用全注解方式替代XML和JavaConfig显式装配方式,方便简洁
2.自动化装配依赖于几个特殊注解:@Autowired,@Component,@ComponentScan和@Configuration
3.@Component注解可以将类定义为装配的组件,同时可以为改组件另起别名(ID)
4.@Configuration将使用该注解的当前类标注为配置类,@ComponentScan开启自动扫描,默认扫描当前配置类所在的包及其子包中含有或使用了@Component注解的类,可通过属性指定扫描范围
5.@Autowired注解可以用在属性,构造函数,setter以及任何一个普通方法中,是Spring依赖注入的核心注解
参考文档
【1】《Spring 实战(第 4 版)》·Craig Walls