本文讲解spirng三种注入方式(构造函数注入,setter注入,注解注入)中的构造函数注入。
所有例子,都是以注解的方式注册bean。
关于构造函数方式注入,spring官网的说明地址为:Spring官网之构造函数注入
单参数且单实现类这是一种最简单的以构造函数的注入方式注入依赖,只要在构造函数中添加所依赖类型的参数即可,spring会匹配对应类型的bean进行注入,由于只有一个对应类型的实现类,因此能准确地找到bean进行注入。
我们看以下例子:
public interface GoPlay {
public void havePlay();
}
import org.springframework.stereotype.Component;
@Component
public class GoPlayImpl implements GoPlay {
@Override
public void havePlay() {
System.out.println("\n\nsingle play\n\n");
}
}
import org.springframework.stereotype.Component;
@Component
public class PlayController {
private GoPlay goPlay;
public PlayController(GoPlay goPlay) {
this.goPlay = goPlay;
}
public void play(){
goPlay.havePlay();
}
}
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PlayControllerTest {
@Resource
PlayController playController;
@After
public void tearDown() throws Exception {
}
@Test
public void play() {
playController.play();
}
}
//有一些自动生成的函数没有删除
single play
说明成功地将对应的bean以构造函数的方式注入。
单构造函数参数依赖的类型,有多个实现类时,就不能直接像上面的例子一样,只定义接口的类型了:
以下方式是错误的:
public MorePlayContorller(MorePlay morePlay) { morePlay.someOnePlay(); }
需要写明所引用的bean的名称,否则spring根据type匹配到两个bean,就会报错。
看下实际的例子:
public interface MorePlay {
public void someOnePlay();
}
import org.springframework.stereotype.Component;
@Component
public class MorePlayImplFirstOne implements MorePlay {
@Override
public void someOnePlay() {
System.out.println("\n\nFirst one play.\n\n");
}
}
import org.springframework.stereotype.Component;
@Component
public class MorePlayImplSecondOne implements MorePlay {
@Override
public void someOnePlay() {
System.out.println("\n\nSecond one play.\n\n");
}
}
import org.springframework.stereotype.Component;
@Component
public class MorePlayContorller {
private MorePlay morePlay;
public MorePlayContorller(MorePlay morePlay) {
morePlay.someOnePlay();
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MorePlayContorllerTest {
@Resource MorePlayContorller morePlayContorller;
@Test
public void play() {
morePlayContorller.play();
}
}
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.springbootexample.oneArgument.MultiImplementation.MorePlay' available: expected single matching bean but found 2: morePlayImplFirstOne,
很显然,直接就懵圈了,我找到了两个,你是想要哪一个?实际上,这种方式编译都过不了。
@Component
public class MorePlayContorller {
private MorePlay morePlay;
public MorePlayContorller(@Qualifier("morePlayImplFirstOne") MorePlay morePlay) {
this.morePlay = morePlay;
}
public void play(){
morePlay.someOnePlay();
}
}
方式二:构造函数中的属性名与所要注入的bean名称一致
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class MorePlayContorller {
private MorePlay morePlay;
public MorePlayContorller(@Qualifier("morePlayImplFirstOne") MorePlay morePlay) {
this.morePlay = morePlay;
}
public void play(){
morePlay.someOnePlay();
}
}
First one play
当具体业务场景中,需要依赖于某接口的所有实现类时,可以使用list注入,构造函数方式注入,同样也可以注入list。
接口和实现类,我们继续沿用MorePlay及其实现。
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class ListPlayControler {
private List<MorePlay> goPlays;
public ListPlayControler(List<MorePlay> goPlays) {
this.goPlays = goPlays;
}
public void listPlay(){
goPlays.forEach(goPlay -> goPlay.someOnePlay());
}
}
listPlay方法会执行GoPlay接口所有实现类对方法havePlay()的重写。list的注入方式易于业务的扩展,封装的代码不会因为扩展了一个新的实现类而发生改动,完全遵循了设计模式的原则。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ListPlayControlerTest {
@Resource private ListPlayControler listPlayControler;
@Test
public void listPlay() {
listPlayControler.listPlay();
}
}
First one play.
Second one play.
需要依赖多接口的场景很多,这个时候仍然可以使用构造函数的注入方式。
import com.example.springbootexample.oneArgument.MultiImplementation.MorePlay;
import com.example.springbootexample.oneArgument.SingleImplementation.GoPlay;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class PlayMoreArugumentContoller {
private GoPlay goPlay;
private MorePlay morePlay;
public PlayMoreArugumentContoller(GoPlay goPlay, @Qualifier("morePlayImplSecondOne") MorePlay morePlay) {
this.goPlay = goPlay;
this.morePlay = morePlay;
}
public void playAll(){
goPlay.havePlay();
morePlay.someOnePlay();
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PlayMoreArugumentContollerTest {
@Resource private PlayMoreArugumentContoller playMoreArugumentContoller;
@Test
public void playAll() {
playMoreArugumentContoller.playAll();
}
}
single play
Second one play.
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。