我对IoC(控制反转)/DI(依赖注入)的理解:
原来我们写java代码时,在A类中调用B类,则直接在A类中new一个B类对象即可调用B中的方法。这在平时的课程作业里显得非常合理而且通俗易懂,但是,接触了一个java项目之后才发现,尽管是一个小型的java项目,其中的类也是非常多的,算上javabean、工具类等少说也有十几个类。若要仍然使用new方法在一个类中调用另一个类,那类的关系将会错综复杂并且要一个一个地打开java文件才能理得清。若有一个容器盛放好了所有的类对象,并且帮我们理清了他们的关系就太好了,这个容器就是Spring的应用上下文或Bean工厂。另一方面,根据面向对象原则,一个类只需要做好自己的职责工作就够了,要使用别人的职责时,也只管把别人喊过来调用即可,不应该承担生产别人的功能。在学习spring之前,我们使用工厂模式,把生产这一功能交给工厂类来做,这样做可以使调用者类只管知道怎么生产工厂即可,但是我们还想让调用者什么都不用生产,有一样东西帮调用者生产好放在那里,调用者只管拿来用即可。这就是Spring的IoC原理,Spring容器将调用者需要的所有类对象生产好放在容器里面,并且将这些对象之间的依赖关系注入到这些对象中,调用者要某个对象只要从容器中拿就行了,不用创建对象,也不用管对象是否依赖其他对象。
太口语化了,因为我也是初学者。。。
下面来介绍Spring是如何装配Bean的。
首先创建一下本文需要模拟的项目场景
这是一个CD磁带接口,模拟所有的CD:
package soundsystem;
//CD接口
public interface CompactDisc {
void play();
}
Spring有三种装配Bean的方式:
一、隐式的bean发现机制和自动装配
自动化装配Bean也分两种角度:组件扫描和自动装配,组件扫描是扫描项目中有哪些需要创建Bean的类,自动装配是自动根据扫描结果将依赖关系装配到spring容器中。
1)组件扫描
步骤:
①创建可被发现的Bean(创建一个具体的CD光盘)
调用者需要使用哪些类对象,就让那个对象注入到spring容器中被调用者使用。
具体方法:在类签名前面添加@Component注解
package soundsystem;
import org.springframework.stereotype.Component;
//@Component注解,为了告诉Spring为该类创建Bean
//括号中内容为该Bean设置id,若无后面的括号参数,则spring默认为该Bean配置的id就为类名首字母小写
@Component("sgtPeppers")
public class SgtPeppers implements CompactDisc{
private String title="Sgt.Pepper's Lonely Hearts Club Band";
private String artist="The Beatles";
@Override
public void play() {
System.out.println("Playing "+title+" by "+artist);
}
}
可以使用Java依赖注入规范中的@Named注解来代替@Component
②组件扫描在默认情况下是不启用的,因此我们要让Spring容器的组件扫描启动起来(生产那个CD光盘)
第一种方式是通过java配置
package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//告诉spring这个java文件是配置文件
@Configuration
//@ComponentScan告诉Spring该配置文件使用的是组件扫描
//括号中参数为组件扫描需要扫描的基础包包名
@ComponentScan("soundsystem")
public class CDPlayerConfig {
}
为了清晰表明你想扫描的是基础包,可用以下参数
@ComponentScan(basePackages="soundsystem")
若想扫描多个基础包,可用数组表示
@ComponentScan(basePackages={"......","......"})
如果注解后面没有括号表明具体的基础包,则spring扫描当前配置文件所在的包及其子包
上面的方法的参数都是用String类型的值表示,这种类型编译时不会检查字符串是否正确,是类型不安全的,因此spring提供以下方法扫描基础包:指定包中所含的类或接口
@ComponentScan(basePackages={CDPlayer.class,DVDPlayer.class})
第二种方式是通过XML配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:Context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
//报错了,不知为何
<context:component-scan base-package="soundsystem"/>
</beans>
③编写测试程序查看SgtPepper对象是否创建成功(光盘是否生产成功)
package soundsystem;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
//测试开始时自动创建Spring应用上下文
@RunWith(SpringJUnit4ClassRunner.class)
//告诉Spring配置文件的位置,这里使用了javaconfig的配置文件
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
//自动装配
@Autowired
private CompactDisc cd;
@Test
public void cdShouldNotBeNull(){
System.out.println(cd==null?"Null":"notNull");
}
}
输出:
五月 10, 2018 8:26:56 下午 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
信息: Could not instantiate TestExecutionListener class [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their dependencies) available.
五月 10, 2018 8:26:57 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.GenericApplicationContext@3c5a99da: startup date [Thu May 10 20:26:57 CST 2018]; root of context hierarchy
notNull
五月 10, 2018 8:26:58 下午 org.springframework.context.support.GenericApplicationContext doClose
信息: Closing org.springframework.context.support.GenericApplicationContext@3c5a99da: startup date [Thu May 10 20:26:57 CST 2018]; root of context hierarchy
说明创建Bean成功
下面是播放器接口,模拟所有的播放器
package soundsystem;
public interface MediaPlayer {
void play();
}
2)自动装配
创建CD播放器类,该类也是要在spring中创建Bean实例的,并且CD播放器需要一张CD光盘才能正常播放,将之前在spring中创建好的CD光盘自动注入到这个CD播放器中
在构造方法上自动依赖注入
package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer{
private CompactDisc cd;
//自动依赖注入,可用于构造方法、setter方法和其他任意方法上
@Autowired
public CDPlayer(CompactDisc cd){
this.cd=cd;
}
public void play() {
cd.play();
}
}
如果没有spring中没有满足这个依赖关系的Bean(CompactDisc)存在,则在创建应用上下文时会抛出异常,为避免异常,可设置如下:
@Autowired(required=false) 此时,若无匹配的Bean存在,这个CDPlayer Bean就会处于未装配状态,若代码中不进行null检查,可能造成空指针异常
如果有多个Bean都满足匹配,也会抛出异常
可用@Inject代替@Autowired
二、java中显式配置
三、XML中显式配置