栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Framework(三)依赖注入

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Framework(三)依赖注入

Dependency Injection,依赖注入。

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

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

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

pom



    
        ssm
        com.fengx
        1.0-SNAPSHOT
    
    4.0.0

    
    spring_02

    
        8
        8
        5.2.2.RELEASE
    

    
    
        
            org.springframework
            spring-context
            ${spring-version}
        
    

    

    


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

bean 的类

package com.fengx.spring_02.datatype;

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


public class User {

    
    private double id;

    
    private String name;

    
    private User wife;

    
    private User[] friends;

    
    private List schoolmates;

    
    private Set favorites;

    
    private Map 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 schoolmates) {
        this.schoolmates = schoolmates;
    }

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

    public void setEmails(Map emails) {
        this.emails = emails;
    }

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

Spring 配置文件




	
		
		
		
		
	

	
		
		
		
		
		
		
		
		
			
				
			
		
		
		
			
				
			
		
		
		
			
				写代码
				睡觉
		    
		
		
		
			
				
				
		    
		
	
	




测试代码

package com.fengx.spring_02.datatype;

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


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;


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 配置



测试类的 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;


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;


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 配置文件




    
        
        
    

    
        
    

    
    


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;


public class User implements ApplicationContextAware {

    
    private final Integer id;
    public User(Integer id) {
        this.id = id;
    }

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

    
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    
    @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;


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 的创建的方式,严格意义上还未到注入的范畴。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/342963.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号