Spring04-自动装配

Spring 自动装配

Spring会自发得在容器中寻找bean所需的依赖,主要从以下两方面来实现
1.组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
2.自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

新建Cat、Dog、User类

public class Cat {
    String name;

    public void setName(String name) {
        this.name = name;
    }

    public void shout(){
        System.out.println(name+"miaomiaomiao");
    }
}
public class Dog {
    String name;

    public void setName(String name) {
        this.name = name;
    }

    public void shout(){
        System.out.println(name+"wangwangwang");
    }
}
public class User {

    Dog dog;
    Cat cat;

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public void show(){
        dog.shout();
        cat.shout();
    }
}

可以看出User类依赖Dog和Cat类
配置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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cat" class="com.jojo.Cat">
        <property name="name" value="小猫"></property>
    </bean>

    <bean id="dog" class="com.jojo.Dog">
        <property name="name" value="小狗"></property>
    </bean>

    <bean id="user" class="com.jojo.User" autowire="byName">
    </bean>
</beans>

可以看到user的bean中并没有给它注入属性,只是多加了一个autowire参数

进行测试

public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        User user = applicationContext.getBean("user",User.class);
        user.show();
    }
    
输出:
小狗wangwangwang
小猫miaomiaomiao

可以看出属性已经成功注入了

autowire有两种方式,byName和byType

byName

修改配置文件中autowire为byName

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cat" class="com.jojo.Cat">
        <property name="name" value="小猫"></property>
    </bean>

    <bean id="dog" class="com.jojo.Dog">
        <property name="name" value="小狗"></property>
    </bean>

    <bean id="user" class="com.jojo.User" autowire="byName">
    </bean>
</beans>

此时Spring容器使用User类中的属性,例如cat的set方法setCat,根据set方法中小写的cat去Spring容器中寻找与之相匹配的id,若能寻找到,则注入,若未寻找到,则会报空指针异常
例如我们把setCat改为setCat2

public void setCat2(Cat cat) {
        this.cat = cat;
 }

此时去执行测试

@org.junit.Test
    public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        User user = applicationContext.getBean("user",User.class);
        user.show();
    }

输出:
	
	小狗wangwangwang

java.lang.NullPointerException
	at com.jojo.User.show(User.java:18)
	at Test.test(Test.java:11)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

但,若是将xml中cat的id改为cat2

 <bean id="cat2" class="com.jojo.Cat">
        <property name="name" value="小猫"></property>
    </bean>

再次进行测试

输出:
	
小狗wangwangwang
小猫miaomiaomiao
byType

byType 通过类型在Spring容器中寻找与之相符合的bean并进行注入,若容器中存在多个相同类型的bean,注入时则会报错
依旧以User类为例,修改配置xml

 <bean id="user" class="com.jojo.User" autowire="byType"></bean>

进行测试

    public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        User user = applicationContext.getBean("user",User.class);
        user.show();
    }

输出结果

小狗wangwangwang
小猫miaomiaomiao

若此时修改setCat为setCat2,进行测试

public void setCat2(Cat cat) {
        this.cat = cat;
    }
 
输出:
	
小狗wangwangwang
小猫miaomiaomiao

可见,修改set方法名对于byType并没有影响
此时,若在xml中新增一个Dog的bean

 <bean id="dog" class="com.jojo.Dog">
        <property name="name" value="小狗"></property>
</bean>

<bean id="dog2" class="com.jojo.Dog">
        <property name="name" value="小狗"></property>
</bean>

进行测试,则报错
NoUniqueBeanDefinitionException

因此,使用byType时,需要确保Spring容器中具有唯一类型的bean

使用注解的方式进行自动装配

1.配置Spring约束

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

2.开启注解支持

<context:annotation-config></context:annotation-config>

3.修改User代码,删除set方法

public class User {
    
    @Autowired
    Dog dog;
    @Autowired
    Cat cat;
    
    public void show(){
        dog.shout();
        cat.shout();
    }
}

进行测试

输出
	
nullwangwangwang
nullmiaomiaomiao

因为cat与dog属性中的name都未注入,因此输出为null,但是可以看到user中的dog与cat属性已成功注入了
注意
@AutoWired(required=false) 注入的对象可以为null
示例如下
在xml配置中删除cat的bean信息

 <bean id="dog" class="com.jojo.Dog"></bean>
    <bean id="user" class="com.jojo.User"></bean>

在User类中添加@AutoWired(required=false)

@Autowired(required = false)
    Cat cat;

输出结果

nullwangwangwang

java.lang.NullPointerException
	at com.jojo.User.show(User.java:14)
	at Test.test(Test.java:11)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

可以看到,虽然required=false,但是仍然会报出空指针

@AutoWired自动装配时,先byType后byName,可能有些博文都说@AutoWired是byType的,并且需要和@Qualifier一起使用,才能达到同时识别byType和byName,但是经过测试,实际上@AutoWired是既支持byType,也支持byName的

修改xml配置

修改xml配置
<bean id="dog" class="com.jojo.Dog"></bean>
    <bean id="dog2" class="com.jojo.Dog"></bean>
    <bean id="cat" class="com.jojo.Cat"></bean>
    <bean id="cat2" class="com.jojo.Cat"></bean>
    <bean id="user" class="com.jojo.User"></bean>
    
先前byType的例子可以知道,如果@AutoWired是byType的,那么此时应该会报错NoUniqueBeanDefinitionException

但是测试可正常输出结果
wangwangwang
miaomiaomiao

修改xml配置

<bean id="dog2" class="com.jojo.Dog">
        <property name="name" value="狗2"></property>
    </bean>
    <bean id="dog" class="com.jojo.Dog">
        <property name="name" value="狗1"></property>
    </bean>
    <bean id="cat" class="com.jojo.Cat">
        <property name="name" value="猫1"></property>
    </bean>
    <bean id="cat2" class="com.jojo.Cat">
        <property name="name" value="猫2"></property>
    </bean>
    <bean id="user" class="com.jojo.User"></bean>
输出
狗1wangwangwang
猫1miaomiaomiao

由此可见,@AutoWired实际上是先byType,后byName,而非单纯的byType

@Resource

@Resource是jdk自带的注解,先byName,后byType
@Resource可以指定name,如@Resource(name=“cat”)

修改User类
public class User {

    @Resource
    Dog dog;
    @Resource
    Cat cat;


    public void show(){
        dog.shout();
        cat.shout();
    }
}

进行测试

输出
狗1wangwangwang
猫1miaomiaomiao

可见@Resource注解也生效了

修改User代码
public class User {

    @Resource(name = "dog2")
    Dog dog;
    @Resource(name = "cat2")
    Cat cat;


    public void show(){
        dog.shout();
        cat.shout();
    }
}


输出
狗2wangwangwang
猫2miaomiaomiao

小结

@Autowired与@Resource异同:

1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值