Spring Setter注入原理

本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。欢迎转载,转载请注明出处(https://blog.csdn.net/feng_xiaoshi/article/details/105832004),谢谢。

简介

本文讨论 Setter 注入方式的原理,通过了解 Setter 注入方式的原理,来加深对 Spring IOC 的认识。通过本文将了解如何使用 autowire属性的 byTypebyName 执行依赖注入。以及手动模式的 API 配置元信息的案例。

前期工作
引入依赖

pom.xml 中添加 Spring 的库依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>         
</dependency>
创建依赖类

有一个依赖于实际上处理业务逻辑的服务的应用程序类:
IndexApp.java

public class IndexApp {
    private IndexService service;
...
}

IndexService 是一个接口:
IndexService.java

public interface IndexService {
    public String serve();
...    
}

该接口可以有多种实现。 一个潜在的实现 DefaultIndexServiceImpl:
DefaultIndexServiceImpl.java

public class IndexService implements IService {
    @Override
    public String serve() {
        return "Hello World";
    }
}

设计上,IndexApp 是一个高级组件,它依赖于称为 IndexService 的低级组件。
本质上,将 IndexAppIndexService 的具体实现的关联关系实现脱钩,而和 IndexService 的接口进行关联,因为该实现可能会因各种因素而有所不同。

模式

Setter 的实现方式有手动和自动两种。

手动模式

依赖注入是一种通过外部容器提供对象的依赖关系的技术。 因此需要通过外部化配置来进行指定和约束需要装配的内容和规则。
主要有以下三种手动方式进行约束。

  • XML 资源配置元信息
  • Java 注解配置元信息
  • API 配置元信息
XML 资源配置元信息

XML

XML 的配置中将依赖项连接在一起:
bean-setter.xml

<bean
  id="defaultIndexService"
  class="com.feng.di.spring.DefaultIndexServiceImpl" />
      
<bean
  id="indexApp"
  class="com.feng.di.spring.IndexApp" >
    <property name="service" ref="defaultIndexService" />
</bean>   

可以看出,XML 配置创建 DefaultIndexServiceImpl 的实例并为其分配 ID 。默认情况下,bean 是单例。与此同时创建 IndexApp 的实例,在这个 bean 中,使用 setter 方法注入另一个 bean

测试代码:

/**
 * 基于 XML 资源的依赖 Setter 方法注入示例
 */
public class XmlDependencySetterInjectionDemo {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(beanFactory);

        String xmlResourcePath = "classpath:META-INF/bean-setter.xml";
        // 加载 xml 资源,并生成 BeanDefinition 资源
        xmlReader.loadBeanDefinitions(xmlResourcePath);
        IndexApp indexApp = beanFactory.getBean(IndexApp.class);
    }
}
Java 注解配置元信息

Spring Annotation

从Spring 2.5开始,可以使用注释配置依赖项注入。因此,可以使用相关类,方法或字段声明上的注释,而不是使用XML来描述bean的连接,而是可以将bean配置移入组件类本身。
注释注入在XML注入之前执行。因此,对于通过两种方法连接的属性,后一种配置将覆盖前者。
默认情况下,Spring容器中的注释接线未打开。因此,在使用基于注释的连接之前,需要在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"
   xmlns:context = "http://www.springframework.org/schema/context"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <context:annotation-config/>

</beans>

一旦配置了<context:annotation-config />,就可以开始注释代码,以指示Spring应该自动将值连接到属性,方法和构造函数中。
下面是一些注释列表:

序号注释与说明
1@Required批注适用于bean属性设置器方法。
2@Autowired批注可以应用于bean属性设置器方法,非设置器方法,构造函数和属性。
3@Qualifier注释和@Autowired可以通过指定将要连接的确切bean来消除混淆。
4Spring支持基于JSR-250的注释,包括@ Resource,@ PostConstruct和@PreDestroy注释。
Service 类例子

假设 向用户发送电子邮件和短息消息。对于依赖项注入,需要为服务提供一个基类。因此,MessageService 是用于发送消息的单一方法声明的接口。


package com.feng.spring.di.services;

public interface MessageService {

	boolean sendMessage(String msg, String rec);
}

现在, 有实际的实现类来发送电子邮件和短信消息。


package com.feng.spring.di.services;

public class EmailService implements MessageService {

	public boolean sendMessage(String msg, String rec) {
		System.out.println("Email Sent to "+rec+ " with Message="+msg);
		return true;
	}
}

package com.feng.spring.di.services;

public class SmsService implements MessageService {

	public boolean sendMessage(String msg, String rec) {
		System.out.println("Sms message Sent to "+rec+ " with Message="+msg);
		return true;
	}
}
Component 类例子

为上述 Service 编写一个消费者类。


package com.feng.spring.di.consumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

import com.feng.spring.di.services.MessageService;

@Component
public class MyApplication {

	// 基于变量的依赖注入
	//@Autowired
	private MessageService service;
	
//	基于构造参数的 dependency injection	
//	@Autowired
//	public MyApplication(MessageService svc){
//		this.service=svc;
//	}
	
	@Autowired
	public void setService(MessageService svc){
		this.service=svc;
	}
	
	public boolean processMessage(String msg, String rec){
		//验证,日志等信息
		return this.service.sendMessage(msg, rec);
	}
}

关于 MyApplication 类的注意点:

  • @Component 注释被添加到该类中,以便当 Spring 框架扫描组件时,该类将被视为组件。@Component 批注只能应用于类,其保留策略是运行时。
  • @Autowired 注解用于让 Spring 知道需要自动装配。这可以应用于字段,构造函数和方法。这个注释使我们能够在组件中实现基于构造函数,基于字段或基于方法的依赖注入。
配置类

对于基于注释的配置,我们需要编写一个Configurator类,该类将用于将实际的实现bean注入到组件属性中。


package com.feng.spring.di.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.feng.spring.di.services.EmailService;
import com.feng.spring.di.services.MessageService;

@Configuration
@ComponentScan(value={"com.feng.spring.di.consumer"})
public class DIConfiguration {

	@Bean
	public MessageService getMessageService(){
		return new EmailService();
	}
}
  • @Configuration 注解用于让 Spring 知道它是 Configuration 类。
  • @ComponentScan 注解与 @Configuration 注解一起使用以指定要查找组件类的包。
  • @Bean 批注用于使 Spring 框架知道该方法应用于使 Bean 实现注入到 Component 类中。

编写一个简单的程序来测试基于注释的 Spring Dependency Injection 示例:


package com.feng.spring.di.test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.journaldev.spring.di.configuration.DIConfiguration;
import com.journaldev.spring.di.consumer.MyApplication;

public class ClientApplication {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DIConfiguration.class);
		MyApplication app = context.getBean(MyApplication.class);
		
		app.processMessage("Hi Pankaj", "pankaj@abc.com");
		
		// 关闭上下文
		context.close();
	}
}

AnnotationConfigApplicationContextAbstractApplicationContext 抽象类的实现,用于在使用注释时将服务自动装配到组件。AnnotationConfigApplicationContext 构造函数将 Class 作为参数,用于获取 Bean 实现以注入组件类。

getBean(Class) 方法返回 Component 对象,并使用配置自动装配对象。上下文对象是资源密集型的,因此在完成操​​作后应将其关闭。
程序运行输出以下信息:

May 02, 2020 9:14:01 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@17f052a3: startup date [Sat May 02 21:14:01 CST 2020]; root of context hierarchy
Email Sent to pankaj@abc.com with Message=Hi Pankaj
May 02, 2020 9:14:02 PM org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@17f052a3: startup date [Sat May 02 21:14:01 CST 2020]; root of context hierarchy
API 配置元信息

API 配置元信息是通过构造 BeanDefinition 来实现的,代码如下:
ApiDependencySetterInjectionDemo.java

package com.feng.spring.di;

import com.feng.spring.di.consumer.MyApplication;
import com.feng.spring.di.services.EmailService;
import com.feng.spring.di.services.MessageService;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

public class ApiDependencySetterInjectionDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApiDependencySetterInjectionDemo.class);

        // 生成 MyApplication 的 BeanDefinition
        BeanDefinition beanDefinition = createMyApplicationBeanDefinition();
        // 注册 MyApplication的Bean
        context.registerBeanDefinition("myApplication", beanDefinition);
        
        MyApplication myApplication = context.getBean(MyApplication.class);
        myApplication.processMessage("hello bean definition", "abc@outlook.com");
        context.close();
    }

    private static BeanDefinition createMyApplicationBeanDefinition(){
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyApplication.class);
        builder.addPropertyReference("service", "messageService");
        return builder.getBeanDefinition();
    }

    @Bean(name = "messageService")
    public MessageService getMessageService(){
        return new EmailService();
    }
}

输出信息如下:

May 02, 2020 10:06:19 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@238e0d81: startup date [Sat May 02 22:06:19 CST 2020]; root of context hierarchy
Email Sent to abc@outlook.com with Message=hello bean definition
May 02, 2020 10:06:19 PM org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@238e0d81: startup date [Sat May 02 22:06:19 CST 2020]; root of context hierarchy
自动模式
  • byName
  • byType

下面通过示例来解释自动模式:
Order.java

package com.feng.spring.di.beans;

public class Order {

    private String item;

    private String price;

    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    public String getPrice() {
        return price;
    }
    public void setPrice(String price) {
        this.price = price;
    }
}

PaymentGateway.java

package com.feng.spring.di.beans;

public class PaymentGateway {

    private Order order;

    public void setOrder(Order ord){
        this.order = ord;
    }

    public Order getOrder() {
        return order;
    }

    @Override
    public String toString(){
        return "ordering "+this.order.getItem()+" | price: "+this.order.getPrice();
    }
}

基于xml的配置文件,注入依赖项的常规方法:
spring-autowriting-conventional.xml

<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-3.0.xsd">

    <bean id="paymentGateway" class="com.feng.spring.di.beans.PaymentGateway" >
        <property name="order" ref="orderBean" />
    </bean>

    <bean id="orderBean" class="com.feng.spring.di.beans.Order" >
        <property name="item" value="Java Book" />
        <property name="price" value="RS 225" />
    </bean>
</beans>

基于 byType 模式的自动注入:
spring-autowriting-by-type.xml

<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-3.0.xsd">

    <bean id="paymentGateway" class="com.feng.spring.di.beans.PaymentGateway"
          autowire="byType"/>

    <bean id="orderBean" class="com.feng.spring.di.beans.Order" >
        <property name="item" value="Java Book" />
        <property name="price" value="RS 225" />
    </bean>
</beans>

基于 byName 模式的自动注入:
spring-autowriting-by-name.xml

<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-3.0.xsd">

    <bean id="paymentGateway" class="com.feng.spring.di.beans.PaymentGateway"
          autowire="byName"/>

    <bean id="order" class="com.feng.spring.di.beans.Order" >
        <property name="item" value="Java Book" />
        <property name="price" value="RS 225" />
    </bean>
</beans>

测试类
AutoWiringDependencyInjectDemo.java

package com.feng.spring.di;

import com.feng.spring.di.beans.PaymentGateway;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AutoWiringDependencyInjectDemo {

    public static void main(String a[]){
        String confFile = "spring-autowriting-by-name.xml";
        ConfigurableApplicationContext context
                = new ClassPathXmlApplicationContext(confFile);
        PaymentGateway paymentGateway = (PaymentGateway) context.getBean("paymentGateway");
        System.out.println(paymentGateway.toString());
    }
}

自动注入通过 byTypebyName ,一般在 XML 配置文件中用的较多。

总结

在本文中,举例说明了如何使用 Spring Framework 使用基于 XML 的配置注入依赖项的示例,通过上面的5个案例,分别通过手动模式和自动模式介绍了 Setter 依赖注入的方式,通过对手动模式的 API 配置元信息,能够更多的理解它底层的一个设计初衷,比如通过 BeanDefinitionBuilder#addPropertyReference 来达到引入相关依赖的目的。

参考资料

Spring Setter-based Dependency Injection(基于Spring Setter的依赖注入)

XML-Based Injection in Spring(Spring中基于XML的注入)

Spring Dependency Injection(Spring 依赖注入)

Spring - Dependency Injection example with annotation(Spring-带有注释的依赖注入示例

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值