Spring Framework(三)依赖注入

Dependency Injection,依赖注入。

  • 对象之间依赖关系的管理交给 Spring 维护;
  • 是实现控制反转的方式之一;
  • 可以降低程序之间的耦合(依赖关系)。

Spring 实现的 IoC 容器中,基本类型和对象都可以注入,按配置细节区分:

  • Java 基本类型
  • String
  • bean
  • Spring 自建 bean(未自己申明的 bean,可以通过 getBean 方法获取到)
  • 非 bean(无法通过 getBean 方法获取到)
  • 数组
  • 集合(List、Set、Map)

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ssm</artifactId>
        <groupId>com.fengx</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <!--依赖注入-->
    <artifactId>spring_02</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring-version>5.2.2.RELEASE</spring-version>
    </properties>

    <!--spring-context-->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
    </dependencies>

    <build>

    </build>

</project>

使用 setter 方法演示以上数据类型的注入

bean 的类

package com.fengx.spring_02.datatype;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author: Fengx
 * @date: 2021-10-19
 * @description: bean 类
 **/
public class User {

    /**
     * 基本数据类型
     */
    private double id;

    /**
     * 字符串类
     */
    private String name;

    /**
     * 实体类
     */
    private User wife;

    /**
     * 实体数组
     */
    private User[] friends;

    /**
     * List列表
     */
    private List<User> schoolmates;

    /**
     * Set集合
     */
    private Set<String> favorites;

    /**
     * Map集合
     */
    private Map<String, String> emails;

    public void setId(double id) {
        this.id = id;
    }

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

    public void setWife(User wife) {
        this.wife = wife;
    }

    public void setFriends(User[] friends) {
        this.friends = friends;
    }

    public void setSchoolmates(List<User> schoolmates) {
        this.schoolmates = schoolmates;
    }

    public void setFavorites(Set<String> favorites) {
        this.favorites = favorites;
    }

    public void setEmails(Map<String, String> emails) {
        this.emails = emails;
    }

    /**
     * 重写 Object 类 toString()
     * @return
     */
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", wife=" + wife + ", friends=" + Arrays.toString(friends)
                + ", schoolmates=" + schoolmates + ", favorites=" + favorites + ", emails=" + emails + "]";
    }
}

Spring 配置文件

<?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="wife" class="com.fengx.spring_02.datatype.User">
		<!--基本数据类型-->
		<property name="id" value="2"/>
		<!--字符串-->
		<property name="name" value="wife"/>
	</bean>

	<bean id="fengx_1" class="com.fengx.spring_02.datatype.User">
		<!--基本数据类型-->
		<property name="id" value="1"/>
		<!--字符串-->
		<property name="name" value="Fengx"/>
		<!--对象,ref id 引用 bean 类-->
		<property name="wife" ref="wife"/>
		<!--数组-->
		<property name="friends">
			<list>
				<ref bean="wife"/>
			</list>
		</property>
		<!--list,ref id 引用 bean 类-->
		<property name="schoolmates">
			<list>
				<ref bean="wife"/>
			</list>
		</property>
		<!--set-->
		<property name="favorites">
			<set>
				<value>写代码</value>
				<value>睡觉</value>
		    </set>
		</property>
		<!--map-->
		<property name="emails">
			<map>
				<entry key="公司" value="123@123.com"/>
				<entry key="个人" value="123@abc.com"/>
		    </map>
		</property>
	</bean>
	
<!--	<bean id="specialUser" class="com.fengx.spring_02.datatype.SpecialUser" autowire="byType">-->
<!--	</bean>-->

</beans>

测试代码

package com.fengx.spring_02.datatype;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.Environment;

/**
 * @author: Fengx
 * @date: 2021-10-19
 * @description: 依赖注入,可注入的数据类型
 * 基本类型 double、String、bean、数组、List、Set、Map 的注入
 **/
public class Test {

    @SuppressWarnings("resource")
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("spring-config-01.xml");
        User fengx = (User)factory.getBean("fengx_1");
        System.out.println(fengx);

//        SpecialUser specialUser = (SpecialUser)factory.getBean("specialUser");
//        // Spring 自建 bean,并未在 spring-config-01.xml 配置文件中申明,却可以注入与依赖查找
//        // 通过 autowire="byType" 可以注入成功
//        System.out.println(specialUser.getEnvironment());
//        System.out.println(factory.getBean(Environment.class));
//
//        // 非 bean,可以注入,但无法通过 getBean 方法依赖查找
//        // 通过 autowire="byType" 可以注入成功
//        System.out.println(specialUser.getBeanFactory());
//        // getBean 方法无法获取到 BeanFactory
//        System.out.println(factory.getBean(BeanFactory.class));
    }
}

结果打印

User [id=1.0, name=Fengx, wife=User [id=2.0, name=wife, wife=null, friends=null, schoolmates=null, favorites=null, emails=null], friends=[User [id=2.0, name=wife, wife=null, friends=null, schoolmates=null, favorites=null, emails=null]], schoolmates=[User [id=2.0, name=wife, wife=null, friends=null, schoolmates=null, favorites=null, emails=null]], favorites=[写代码, 睡觉], emails={公司=123@123.com, 个人=123@abc.com}]

上面这个例子演示了基本类型 double、String、bean、数组、List、Set、Map 的注入。

那什么是 Spring 自建的 bean 和 非 bean 呢?这两个概念,看下面这个例子。

新增类 SpecialUser,包含了 BeanFactory 和 Environment 两个类型的属性

package com.fengx.spring_02.datatype;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.env.Environment;

/**
 * @author: Fengx
 * @date: 2021-10-19
 * @description: 包含了 BeanFactory 和 Environment 两个类型的属性
 **/
public class SpecialUser {

    private BeanFactory beanFactory;

    private Environment environment;

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public Environment getEnvironment() {
        return environment;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}

xml 新增一个 bean 配置

<bean id="specialUser" class="com.fengx.spring_02.datatype.SpecialUser" autowire="byType">
</bean>

测试类的 main 方法中新增测试代码

package com.fengx.spring_02.datatype;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.Environment;

/**
 * @author: Fengx
 * @date: 2021-10-19
 * @description: 依赖注入,可注入的数据类型
 * 基本类型 double、String、bean、数组、List、Set、Map 的注入
 **/
public class Test {

    @SuppressWarnings("resource")
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("spring-config-01.xml");
//        User fengx = (User)factory.getBean("fengx_1");
//        System.out.println(fengx);

        SpecialUser specialUser = (SpecialUser)factory.getBean("specialUser");
        // Spring 自建 bean,并未在 spring-config-01.xml 配置文件中申明,却可以注入与依赖查找
        // 通过 autowire="byType" 可以注入成功
        System.out.println(specialUser.getEnvironment());
        System.out.println(factory.getBean(Environment.class));
//
//        // 非 bean,可以注入,但无法通过 getBean 方法依赖查找
//        // 通过 autowire="byType" 可以注入成功
//        System.out.println(specialUser.getBeanFactory());
//        // getBean 方法无法获取到 BeanFactory
//        System.out.println(factory.getBean(BeanFactory.class));
    }
}

打印结果

StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}
StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}

非 bean

package com.fengx.spring_02.datatype;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.Environment;

/**
 * @author: Fengx
 * @date: 2021-10-19
 * @description: 依赖注入,可注入的数据类型
 * 基本类型 double、String、bean、数组、List、Set、Map 的注入
 **/
public class Test {

    @SuppressWarnings("resource")
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("spring-config-01.xml");
//        User fengx = (User)factory.getBean("fengx_1");
//        System.out.println(fengx);

        SpecialUser specialUser = (SpecialUser)factory.getBean("specialUser");
        // Spring 自建 bean,并未在 spring-config-01.xml 配置文件中申明,却可以注入与依赖查找
        // 通过 autowire="byType" 可以注入成功
//        System.out.println(specialUser.getEnvironment());
//        System.out.println(factory.getBean(Environment.class));

        // 非 bean,可以注入,但无法通过 getBean 方法依赖查找
        // 通过 autowire="byType" 可以注入成功x
        System.out.println(specialUser.getBeanFactory());
        // getBean 方法无法获取到 BeanFactory
        System.out.println(factory.getBean(BeanFactory.class));
    }
}

org.springframework.beans.factory.support.DefaultListableBeanFactory@15975490: defining beans [wife,fengx_1,specialUser]; root of factory hierarchy
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.beans.factory.BeanFactory' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1126)
	at com.fengx.spring_02.datatype.Test.main(Test.java:31)

从打印结果可以看出

  • Environment bean 并未在 xml 中配置,但却可以注入与通过 getBean 方法依赖查找,即上面提到的 Spring 自建的 bean
  • BeanFactory 可以被注入,但通过 getBean 方法获取报找不到 bean 的错误,即上面提到的非 bean

依赖注入的实现方式

首先,bean 的配置文件可以通过 xml 和 properties 两种方式。其中 xml 是主流,properties 基本不用,具体实现方式:

  • setter 方法
  • 构造器
  • 接口回调
  • 注解
  • API

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">

    <bean id="fengx_2" class="com.fengx.spring_02.impltype.User">
        <constructor-arg value="1"/>
        <property name="name" value="Fengx"/>
    </bean>

    <bean id="favorites" class="java.lang.String">
        <constructor-arg value="写代码、睡觉"/>
    </bean>

    <!-- 开启注解能力 -->
    <context:component-scan base-package="com.fengx.spring_02"/>

</beans>

bean 的类代码

package com.fengx.spring_02.impltype;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * @author: Fengx
 * @date: 2021-10-19
 * @description: bean 类
 **/
public class User implements ApplicationContextAware {

    /**
     * 构造方法注入
     */
    private final Integer id;
    public User(Integer id) {
        this.id = id;
    }

    /**
     * set 方法注入
     */
    private String name;
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 实现接口 ApplicationContextAware 及其回调方法注入 applicationContextAware
     */
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 使用注解,xml需要开启注解能力
     * 使用 @Autowired 给属性字段注入
     */
    @Autowired
    private String favorites;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", applicationContext=" + applicationContext +
                ", friend=" + favorites +
                '}';
    }
}

测试代码,包含 bean 通过 api 注册与注入

package com.fengx.spring_02.impltype;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: Fengx
 * @date: 2021-10-19
 * @description: 依赖注入的实现方式
 **/
public class Test {

    @SuppressWarnings("resource")
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config-02.xml");
        User fengx = (User)context.getBean("fengx_2");
        System.out.println(fengx);

        // api 构造、注入、组装、注册 bean
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)context.getBeanFactory();
        AbstractBeanDefinition userBeanDefinition = new RootBeanDefinition(User.class);
        // 构造方法注入
        beanFactory.registerBeanDefinition("ApiUser", userBeanDefinition);
        ConstructorArgumentValues argValues = new ConstructorArgumentValues();
        argValues.addIndexedArgumentValue(0, 1);
        // set 方法注入
        userBeanDefinition.setConstructorArgumentValues(argValues);
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue(new PropertyValue("name", "ApiUser"));
        userBeanDefinition.setPropertyValues(propertyValues);
        System.out.println(beanFactory.getBean("ApiUser"));
    }
}

结果打印

{id=1, name='Fengx', applicationContext=org.springframework.context.support.ClassPathXmlApplicationContext@68de145, started on Tue Oct 19 17:41:03 CST 2021, friend=写代码、睡觉}
User{id=1, name='ApiUser', applicationContext=org.springframework.context.support.ClassPathXmlApplicationContext@68de145, started on Tue Oct 19 17:41:03 CST 2021, friend=写代码、睡觉}
  • 注解的使用需要在 xml 里添加 context 的命名空间,context:component-scan 开启注解的能力,设置 bean 扫描路径。
  • setter 方法注入的缺点是可能放大了 bean 的修改权限、如果字段之间有依赖关系与校验逻辑 set 顺序不可控。
  • 构造方法注入的缺点是属性较多时构造方法参数配置较多,顺序易出错、对 null 处理灵活性较差、不利于子类继承与扩展。
  • 与注入的相关的注解除了示例中的 @Autowired,还包括 @Autowired + @Qualifier 指定 bean 名称注入,@Resource(name="") 指定 bean 名称注入,JSR250 规范;@Value 注入字符串、系统信息、配置文件信息、表达式等;@Inject JSR330规范。一般 @Autowired 使用最多,注解的使用都支持在属性字段上,有些也可以用在 setter 方法和构造方法上。
  • 标签还可以指定 autowire,自动绑定(Autowiring)的模式,这个配置也会影响依赖注入的结果。no:默认该值,不进行自动注入,需要手动配置要注入的 bean;byName:根据 bean 名称注入;byType:根据类型注入;constructor:根据构造方法加其参数注入。
  • 网上还有资料提到了静态工厂方法注入和实例工厂方法注入,个人觉得这两个只是 bean 的创建的方式,严格意义上还未到注入的范畴。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值