Spring04--依赖注入,bean 的作用域和自动装配

本文详细介绍了Spring框架中的依赖注入,包括构造器注入和setter注入,并探讨了何时选择哪种方式。此外,还讲解了p命名和c命名空间注入,以及Bean的多种作用域,如Singleton和Prototype。最后,文章讨论了Bean的自动装配,包括ByName和ByType两种方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

一、依赖注入

基于构造函数或基于setter的DI?
由于可以混合使用基于构造函数的DI和基于设置程序的DI,因此,将构造函数用于强制性依赖项,将setter方法或配置方法用于可选性依赖项是一个很好的经验法则。请注意,可以 在setter方法上使用@Required批注,以使该属性成为必需的依赖项。但是,最好使用带有参数的程序验证的构造函数注入。

Spring团队通常提倡构造函数注入,因为它可以让您将应用程序组件实现为不可变的对象,并确保不存在必需的依赖项null。此外,构造函数注入的组件始终以完全初始化的状态返回到客户端(调用)代码。附带说明一下,大量的构造函数自变量是一种不好的代码味道,这表明该类可能承担了太多的职责,应将其重构以更好地解决关注点分离问题。

Setter注入主要应仅用于可以在类中分配合理的默认值的可选依赖项。否则,必须在代码使用依赖项的任何地方执行非空检查。setter注入的一个好处是,setter方法使该类的对象在以后可以重新配置或重新注入。因此,通过JMX MBean进行管理是用于setter注入的引人注目的用例。

使用最适合特定班级的DI风格。有时,在处理您没有源代码的第三方类时,将为您做出选择。例如,如果第三方类未公开任何setter方法,则构造函数注入可能是DI的唯一可用形式。

1.构造器注入:

基于构造函数的DI是通过容器调用具有多个参数(每个参数代表一个依赖项)的构造函数来完成的。调用static带有特定参数的工厂方法来构造Bean几乎是等效的,并且本次讨论也将构造函数和static工厂方法的参数视为类似

2.set方式注入

基于设置器的DI是通过在调用无参数构造函数或无参数static工厂方法以实例化您的bean之后,在您的bean上调用setter方法来完成的。

Set注入:要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写。

环境搭建:

地址类:

public class Address {
    private String address;
    ...
    //getter/setter/tiString
}

学生类:

package com.shy.pojo;
import java.util.*;
public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String girl;//null
    private Properties info;
    ...
    //getter/setter/tiString
}

beans.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="address" class="com.shy.pojo.Address">
        <property name="address" value="ssss"/>
    </bean>

    <bean id="student" class="com.shy.pojo.Student">
        <!--第一种,普通值注入-->
        <property name="name" value="shy1"/>
        <!--第二种,bean注入-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>book1</value>
                <value>book2</value>
                <value>book3</value>
            </array>
        </property>
        <!--list注入-->
        <property name="hobbys">
            <list>
                <value>hhh</value>
                <value>ppp</value>
                <value>ccc</value>
            </list>
        </property>
        <!--map注入-->
        <property name="card">
            <map>
                <entry key="饭卡" value="12345"/>
                <entry key="公交卡" value="12321"/>
                <entry key="啪啪卡" value="11011"/>
            </map>
        </property>
        <!--set注入-->
        <property name="games">
            <set>
                <value>blhx</value>
                <value>bh3</value>
                <value>ys</value>
            </set>
        </property>
        <!--null注入-->
        <property name="girl">
            <null/>
        </property>
        <!--Properties注入-->
        <property name="info">
            <props>
                <prop key="学号">1999</prop>
                <prop key="性别"></prop>
                <prop key="姓名">shy</prop>
            </props>
        </property>
    </bean>

</beans>

测试类

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getName());
    }
}

依赖性解析过程

容器执行bean依赖项解析,如下所示:

使用ApplicationContext描述所有bean的配置元数据创建和初始化。可以通过XML,Java代码或注释来指定配置元数据。

对于每个bean,其依赖项都以属性,构造函数参数或static-factory方法的参数的形式表示(如果使用它而不是普通的构造函数)。实际创建Bean时,会将这些依赖项提供给Bean。

每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。

每个值的属性或构造函数参数都将从其指定的格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring能够以String类型提供值转换成所有内置类型,比如int, long,String,boolean,等等。

二、p命名和c命名注入

User.java :【注意:这里没有有参构造器!】

 public class User {
     private String name;
     private int age;
     public void setName(String name) {
         this.name = name;
     } 
     public void setAge(int age) {
         this.age = age;
     }
     @Override
     public String toString() {
         return "User{" +
                 "name='" + name + '\'' +
                 ", age=" + age +
                 '}';
    }
 }

1、P命名空间注入 : 需要在头文件中加入约束文件

 <!--导入约束 : xmlns:p="http://www.springframework.org/schema/p"-->
 
 <!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
 <bean id="user" class="com.shy.pojo.User" p:name="shy" p:age="111"/>

2、c 命名空间注入 : 需要在头文件中加入约束文件

 导入约束 : xmlns:c="http://www.springframework.org/schema/c"
 <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
 <bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>

发现问题:爆红了,刚才我们没有写有参构造!

解决:把有参构造器加上,这里也能知道,c 就是所谓的构造器注入!

测试代码:

 @Test
 public void test02(){
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
     User user = (User) context.getBean("user");
     System.out.println(user);
 }

三、Bean的作用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象 .

Singleton

当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:

 <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

Prototype

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:

<!--方式一--> 
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
<!--方式二--> 
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>

Request

当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>

针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。

Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

作用域描述
1.单例模式:默认

2.原型模式:每次从容器中get的时候,都会产生一个新的对象

剩下的只能在web开发中使用到

四、Bean 的自动装配

1.自动装配是Spring满足bean依赖的一种方式!

2.Spring会在上下文中自动寻找,并自动给bean装配属性

在Spring中有三种自动装配的方式

  • 在xml中显式配置
  • 在java中显式配置
  • 隐式的自动装配bean

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;

自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。

测试

dog类

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

cat类

public class Cat {
    public void shout(){
        System.out.println("miao");
    }
}

person类

package com.shy.pojo;
public class Person {
    private Cat cat;
    private Dog dog;
    private String name;
    public Cat getCat() {
        return cat;
    }
    public void setCat(Cat cat) {
        this.cat = cat;
    }
    public Dog getDog() {
        return dog;
    }
    public void setDog(Dog dog) {
        this.dog = dog;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}

ByName:自动在容器上下文中查找,和自己对象set的方法后的值对应的beanid,必须保证所有的beanid唯一,并且需要和自动注入的属性的set方法一致。

<bean id="cat" class="com.shy.pojo.Cat"/>
<bean id="dog" class="com.shy.pojo.Dog"/>
<bean id="person" class="com.shy.pojo.Person" autowire="byName">
   <property name="name" value="shy"/>
</bean>

ByType:自动在容器上下文中查找,和自己对象属性类型相同的bean!必须保证类型全局唯一

<bean id="cat" class="com.shy.pojo.Cat"/>
<bean class="com.shy.pojo.Dog"/>
<bean id="person" class="com.shy.pojo.Person" autowire="byType">
   <property name="name" value="shy"/>
</bean>

测试方法

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = (Person) context.getBean("person");
        person.getDog().shout();
        person.getCat().shout();
    }
}

当一个bean节点带有 autowire byName的属性时。

  1. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
  2. 去spring容器中寻找是否有此字符串名称id的对象。
  3. 如果有,就取出注入;如果没有,就报空指针异常。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值