Spring学习笔记:Bean的装配方式

学习内容:Bean的装配方式

1. 装配Bean的概述

Bean的装配可以理解为依赖关系注入,Bean的装配方式,即Bean的依赖注入方式。Spring容器支持多种形式的Bean的装配方式:

  • 在XML中显式配置(基于XML的装配)
  • 在Java的接口和类中实现配置(利用java代码和注解实现装配)
  • 隐式Bean的发现机制和自动装配原则(基于注解的装配和自动装配)
      在显示的开发中,这3中方式都会被用到,并且在学习和工作中常常混合使用,因此需要我们明白这三种方式的优先级,即我们应该如何选择使用哪种方式去把Bean发布到Spring IoC容器中。

第一,基于“约定优于配置”的原则,最优先的应该是通过隐式Bean的发现机制和自动装配的原则。这样的好处是减少程序开发者的决定权,简单又不失灵活。

第二,在没有办法使用自动装配原则的情况下应该优先考虑Java接口和类中实现配置,这样的好处是避免XML配置的泛滥,也更为容易。这种场景典型的例子是一个父类有多个子类,比如学生类有两个子类:男生类和女生类,通过IoC容器初始化一个学生类,容器将无法知道使用哪个子类去初始化,这个时候可以使用Java的注解配置去指定。

第三,在上述方法都无法使用的情况下,那么只能选择XML去配置Spring IoC容器。由于现实开发中常常用到第三方类库,有些类并不是我们开发的,我们无法修改里面的代码,这个时候就通过XML的方式配置使用了。

通俗来讲,当配置的类时你自身正在开发的工程,那么应该考虑Java配置为主,而Java配置又分为自动装配和Bean名称配置。在没有歧义的基础上,优先使用自动装配,这样就可以减少大量的XML配置。如果所需配置的类并不是你的工程开发的,那么建议使用XML的方式。

2. 基于XML的装配

Spring主要提供俩种基于XML的装配:设值注入(Set方式注入)(Setter Injection)和构造器注入(Constructor Injection)
在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter()方法来注入属性值。因此,Set方式注入要求一个Bean必须满足一下要求:

  • Bean类必须提供一个默认的无参构造方法。
  • Bean类必须为需要注入的属性提供对应的setter()方法

设值注入:Spring配置文件中需要使用<bean>元素的子元素<property>来为每个属性注入值。
构造注入:Spring配置文件中需要使用<bean>元素的子元素<constructor-arg>来定义构造方法的参数,可以使用其value属性来设置参数的值。

Set注入和构造器注入详情请见学习笔记“依赖注入”,点这里跳转

3. 基于注解的装配

尽管可以使用XML配置文件来实现Bean的装配工作,但如果Bean过多,就会导致XML配置文件过于臃肿,会对以后的维护和升级工作带来一定困难。故此,Spring提供了对Annotation(注解)技术的支持。

@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!

  • dao 【@Repository
  • service 【@Service
  • controller 【@Controller
    这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

Spring的常用注解:

注解名称说明
@Component可以使用此注解Spring中的Bean,但它只是一个泛化的概率,仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需要将需标注在相应类上即可
@Repository用于将数据访问层(Dao层)的类标识为Spring中的Bean,其功能与@Component相同
@Service作用于业务层(Service层)的类标识为Spring中的Bean,其功能与@Component相同
@Controller作用于控制层(如SpringMVC的Controller)的类标识为Spring中的Bean,其功能与@Component相同
@Autowired用于对Bean的属性变量,属性的setter()方法及构造方法进行标注,配合对应的注解处理器完成Bean的指定配置工作,按照Bean的类型进行装配
@Resource作用与@Autowired一样,区别在于@Autowired默认按照Bean类型装配,而@Resource默认按照Bean实例名称进行装配
@Qualifier与@Autowired注解配合使用,将默认的Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定

在Spring4之后,要使用注解开发,必须要保证AOP包已经导入了
在这里插入图片描述
使用注解需要导入context约束,增加注解的支持!

  • applicationContext.xml
<!--导入context约束
xmlns:context="http://www.springframework.org/schema/context"
注意以下代码要写在xsi:schemaLocation=""引号中
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
-->
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解的支持-->
    <context:annotation-config/>

    <!--此处与XML配置方式有所不同的是,这里不需要配置子元素property-->
    <bean id="user" class="pojo.User"/>

    <!--此处拥有更高效的注解配置方式,对包路径下的所有Bean文件进行扫描,可替换bean,但所有类和xml配置文件得在同一个文件夹中-->
    <!--指定要扫描的包,这个包下的的注解就会生效-->
    <!--    <context:component-scan base-package="pojo"/>-->
</beans>
  • User.java
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Data //lombok的注解
@Component//相当于配置文件中的:<bean id="user" class="pojo.User"/>
@Scope("prototype")//这个是设置作用域的
public class User {
    @Value("pym")//相当于配置文件中bean标签中的:<property name="name" value="pym"></property>
    public String name;
}
  • 测试类
public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user.getName());
    }
}

结果:在这里插入图片描述

4. 自动装配

虽然使用注解方式装配Bean在一定程度上减少了配置文件中的代码量,但也有不少项目是不使用注解开发的,那有没有什么方法既能减少代码量,又能实现Bean的装配呢?当然有。Spring的<bean>元素中包含一个autowired的属性,我们可以设置autowired的属性值来自动装配Bean。

例子:

  • 创建三个实体类:
public class Cat {
    public void shout(){
        System.out.println("喵喵~~");
    }
}

public class Dog {
    public void shout(){
        System.out.println("汪汪~~");
    }
}

@Data //lombok注解
public class People {
    private Cat cat;
    private Dog dog;
    private String name;
    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}
  • applicationContext.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cat" class="pojo.Cat"/>
    <bean id="dog" class="pojo.Dog"/>
    <!--
        byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean的id!
        	(弊端:必须名字相同,但是set方法后面名字是大写,这里用小写可以,但是用大写不行)
        	
        byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!
        	(弊端:必须保证这个类型全局唯一,如果有两个就不行了)
    -->
    <!--byName-->
    <bean id="people" class="pojo.People" autowire="byName">
        <property name="name" value="pym"/>
    </bean>

		<!--byType-->
<!--    <bean id="people" class="pojo.People" autowire="byType">-->
<!--        <property name="name" value="pym"/>-->
<!--    </bean>-->

</beans>
  • 测试类
public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        People people = context.getBean("people", People.class);
        people.getCat().shout();
        people.getDog().shout();

    }
}

结果:
在这里插入图片描述
小结:

  1. 当一个bean节点带有 autowire byName的属性时。
  2. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
  3. 去spring容器中寻找是否有此字符串名称id的对象。如果有,就取出注入;如果没有,就报空指针异常.
  4. 使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。

5. 使用注解实现自动装配

@Autowired

  • @Autowired是按类型自动装配的,不支持id匹配。
  • 需要导入 spring-aop的包!
  • 直接在属性上用即可!也可以在set方式上使用!使用Autowired我们可以不用使用Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在且符合名字(IOC容器中的id和属性名相同)

以4中例子为例:

  • 实体类(Cat、Dog类不变,People做以下修改)
public class People {

//如果显式的定义了AutoWired的required属性为false,说明这个对象可以为null,否则不允许为空
//    @Autowired(required = false)
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
    //使用Autowired我们可以不用使用Set方法了
    public Cat getCat() {
        return cat;
    }
    public Dog getDog() {
        return dog;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
  • applicationContext.xml
<!--导入context约束
xmlns:context="http://www.springframework.org/schema/context"
注意以下代码要写在xsi:schemaLocation=""引号中
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
-->
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解的支持-->
    <context:annotation-config/>

    <bean id="cat" class="pojo.Cat"/>
    <bean id="dog" class="pojo.Dog"/>

	<bean id="people" class="pojo.People"/>
</beans>
  • 测试类
public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        People people = context.getBean("people", People.class);
        people.getCat().shout();
        people.getDog().shout();

    }
}

结果和4中一样。

补充:

@Autowired:自动装配通过类型、名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")
@Nullable:字段标记了这个注解,说明这个字段可以为null
@Resource:自动装配通过名字、类型
@Component:自动装配通过名字、类型

@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用。

测试:
(1)配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!


<bean id="dog1" class="pojo.Dog"/>
<bean id="dog2" class="pojo.Dog"/>
<bean id="cat1" class="pojo.Cat"/>
<bean id="cat2" class="pojo.Cat"/>

(2)在属性上添加Qualifier注解

@Autowired
@Qualifier(value = "cat2")
private Cat cat;

@Autowired
@Qualifier(value = "dog2")
private Dog dog;

@Resource

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

测试一:
实体类:

public class User {
    //如果允许对象为null,设置required = false,默认为true
    @Resource(name = "cat2")
    private Cat cat;
    @Resource
    private Dog dog;
    private String str;
}
  • beans.xml
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>

测试,因为有指定的name属性,所以按该属性进行byName方式查找装配。成功。

测试二:
实体类上只保留注解:

@Resource
private Cat cat;
@Resource
private Dog dog;
  • beans.xml
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>

测试:先进行byName查找,失败;再进行byType查找,成功。

小结
@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。

6. 使用Java的方式配置Spring

现在要完全不使用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能

  • 实体类
//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
@Component
public class User {
    private String name;
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
    public String getName() {
        return name;
    }
    @Value("Serein")
    public void setName(String name) {
        this.name = name;
    }
}
  • 配置文件
import pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//这个也会被Spring容器托管,注册到容器中,因为它自身就是一个@Component,
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml一样
@Configuration
@Import(Config2.class)//相当于Spring配置中的import
public class Config {

    //注册一个bean,就相当于我们之前写的一个bean标签
    //这个方法的名字,就相当于bean标签中的ID属性
    //这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User getUser(){
        return new User();//就是返回要注入到bean的对象
    }
}
  • 测试类
public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载!
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        User getUser = (User) context.getBean("getUser");
        System.out.println(getUser.getName());
    }
}
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值