【培训】DAY17 Spring IOC

28 篇文章 0 订阅
28 篇文章 1 订阅

简介

Spring IoC(Inversion of Control)容器功能:通过配置元数据(Configuration Metadata)描述 Bean 与 Bean 之间的依赖关系,利用 Java 反射功能实例化 Bean,并建立各个Bean 之间的依赖关系。
IoC容器控制对象的生命周期,所有类的创建、销毁都由 IoC容器来执行,也就是说控制对象生命周期的不再是使用方,而是IoC容器。之前是由使用方自身来控制的,现在交由IoC容器,所以叫控制反转。
ApplicationContext接口代表Spring IoC容器,它继承BeanFactory接口,并通过它来实例化、配置、组装bean。ApplicationContext主要实现类有:ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、AnnotationConfigApplicationContext等。
Spring是一个轻量级框架,主要由IoC及AOP组成。IoC自动装配的方式有byName、byType等。

术语

IoC - IoC为控制反转,即一个对象依赖其他对象时,不需要自己去创建、配置被依赖的对象,控制权不在自己,而在于Spring IoC容器。IoC(Inversion of Control)也被称作为依赖注入(Dependency Injection - DI)。
Bean - 由Spring IoC容器管理的,构成应用程序的这些对象叫做Bean。Bean被Spring容器实例化、管理。一个Bean即为应用程序的一个简单对象。Bean和Bean之间的依赖关系通过Spring IoC容器管理。Bean在 Spring容器中有两种管理方式,原型模式和单例模式,默认使用单例模式。
Configuration Metadata - Spring IoC容器解析配置元数据,通过它Spring IoC容器才会知道怎么实例化、配置、组装对象。

Configuration Metadata包含多种格式:

  • XML-based metadata - 基于XML文件的配置
  • Annotation-based configuration - 基于注解的配置
  • Java-based configuration - 基于Java的配置

优点

  1. 降低了使用资源双方的依赖程度,也就是我们说的耦合度。
  2. 享受单例的好处,效率高,不浪费系统资源。
  3. 便于单元测试,方便切换mock组件。
  4. 便于进行AOP操作,对于使用者是透明的。
  5. 资源集中管理,实现资源的可配置和易管理。
  6. 能更好的实现面向接口编程。

详解

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:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">
      <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
      <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean defimitions go here -->
    <!-- 更多的Bean在这里定义 -->
</beans>

The id attribute is a string that identifies the individual bean definition.

id属性是标识单个bean定义的字符串。

The class attribute defines the type of the bean and uses the fully qualified classname.

class属性定义bean的类型并使用完全限定的类名。

XML配置 - id、name属性

id、name属性值必须在容器中唯一,id为bean对象的唯一标识符,name为当前bean的别名,id和name都可用来定位bean。
name属性值可以配置多个,以“,”、“;”或空格分隔。id和name属性值不是必填的,如果两个都没配置,则spring会自动生成一个唯一标识,如:com.xxx.User#0。

XML配置 - 基于构造函数的依赖注入

优点:1. 构造期即创建一个完整、合法的对象。2. 不需要写繁琐的setter方法的。3. 在构造函数中决定依赖关系的注入顺序。
Setter注入和构造函数注入的区别:1. 注入依赖关系注入时机不同。2. 构造注入方式依赖对象先注入。
构造函数的参数解析是通过参数的类型来匹配的。假如ThingTwo和ThingThree没有继承关系,下面的配置可以正常工作,开发者不需要去定义< constructor-arg>元素的索引或类型信息。

public class TestOne {
    public TestOne(TestTwo testTwo, TestThree testThree) {}
}
<beans>
	<bean id="testOne" class="com.test.spring.test.TestOne">
    <constructor-arg index="0" ref="testTwo"/>
    <constructor-arg index="1" ref="testThree"/>
  </bean>

  <bean id="testTwo" class="com.test.spring.test.TestTwo"/>
  <bean id="testThree" class="com.test.spring.test.TestThree"/></beans>

当定义构造函数参数时,类型不确定时,需要配置type属性来实现类型匹配或者使用index指定参数索引值。

package com.test.spring.test;

public class ExampleBean {
    private int years;
    private String ultimateAnswer;
    
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
  <bean id="exampleBean" class="com.test.spring.test.ExampleBean">
    <constructor-arg type="int" value="88"/>
    <constructor-arg type="java.lang.String" value="66"/>
  </bean>

或者

  <bean id="exampleBean" class="com.test.spring.test.ExampleBean">
    <constructor-arg index="0" value="88"/>
    <constructor-arg index="1" value="66"/>
  </bean>

通过构造器的参数名来匹配参数(推荐方案)。使用此方法的两种场景:

  1. 编译时启用debug,Apache Maven Compiler Plugin默认启动了该配置项。配置如下:

      <bean id="exampleBean" class="com.test.spring.test.ExampleBean">
        <constructor-arg name="years" value="88"/>
        <constructor-arg name="ultimateAnswer" value="66"/>
      </bean>
    
  2. 编译时不启用debug,则可使用@ConstructorProperties代替。如下:

    package com.test.spring.test;
    
    import java.beans.ConstructorProperties;
    
    public class ExampleBean { 
        @ConstructorProperties({"years", "ultimateAnswer"})
        public ExampleBean(int years, String ultimateAnswer) {
            this.years = years;
            this.ultimateAnswer = ultimateAnswer;
        }
    }
    
XML配置 - 基于Setter的依赖注入
  <bean id="exampleBean" class="com.test.spring.test.ExampleBean">
    <property name="beanOne">
      <ref bean="anotherExampleBean"/>
    </property>

    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
  </bean>

  <bean id="anotherExampleBean" class="com.test.spring.test.AnotherBean"/>
  <bean id="yetAnotherBean" class="com.test.spring.test.YetAnotherBean"/>

注:基于Setter的依赖注入,属性必须提供set方法,无所谓get方法。对于复杂的依赖关系,setter注入更简洁、直观。

package com.test.spring.test;

public class ExampleBean {
    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}
XML配置 - 基本类型及字符串配置

当Bean的属性为基本类型或字符串时,可直接使用标签的value属性进行配置。

  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="user" value="root"></property>
    <property name="password" value="123456"></property>
    <property name="jdbcUrl"
      value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"></property>
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
  </bean>

可以使p-namespace用来简化配置。

<?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:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close"
    p:user="root"
    p:password="123456"
    p:jdbcUrl="jdbc:mysql://localhost:3306/test"
    p:driverClass="com.mysql.jdbc.Driver"/>
</beans>
XML配置 - 集合
  <bean>
    <property name="adminEmails">
      <props>
        <prop key="admin">admin@test.com</prop>
        <prop key="support">support@test.com</prop>
        <prop key="development">development@test.com</prop>
      </props>
    </property>
    <property name="someList">
      <list>
        <value>a list element followed by a reference</value>
        <ref bean="dataSource"/>
      </list>
    </property>
  </bean>
  <bean>
    <property name="someMap">
      <map>
        <entry key="an entry" value="just some string"/>
        <entry key="a ref" value-ref="dataSource"/>
      </map>
    </property>
    <property name="someSet">
      <set>
        <value>just some string</value>
        <ref bean="dataSource"/>
      </set>
    </property>
  </bean>
XML配置 - Null或空字符串
空字符串
  <bean class="com.test.spring.test.ExampleBean">
    <property name="integerProperty">
      <null/>
    </property>
  </bean>

等于

exampleBean.setEmail("");
Null值
  <bean class="com.test.spring.test.ExampleBean">
    <property name="integerProperty" value=""/>
  </bean>

等于

exampleBean.setEmail(null);
XML配置 - 配置java.util.Properties
  <bean id="propertyPlaceholderConfigurer"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="properties">
      <value>
        jdbc.driver.className=com.mysql.jdbc.Driver
        jdbc.url=jdbc:mysql://localhost:3306/test
      </value>
    </property>
  </bean>
XML配置 - Static Factory Method实例化Bean

class属性表示指定类中包含静态工厂方法,factory-method属性表示静态工厂方法的方法名。

  <bean id="clientService" class="com.test.spring.test.ClientService"
    factory-method="clientService"/>
package com.test.spring.test;

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService clientService() {
        return clientService;
    }
}
XML配置 - Instance Factory Method实例化Bean

通过bean的工厂方法来实例化bean。factory-bean指定依赖的工厂bean,factory-method指定bean的什么方法用于创建对象,此方法为非静态方法。

  <bean id="defaultServiceLocator" class="com.test.spring.test.DefaultServiceLocator">

  </bean>

  <bean id="clientService"
    factory-bean="defaultServiceLocator"
    factory-method="createClientServiceInstance"/>
package com.test.spring.test;

public class DefaultServiceLocator {
    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}
XML配置 - 一个工厂类中可包含多个工厂方法
  <bean id="defaultServiceLocator" class="com.test.spring.test.DefaultServiceLocator">

  </bean>

  <bean id="clientService"
    factory-bean="defaultServiceLocator"
    factory-method="createClientServiceInstance"/>

  <bean id="accountService"
    factory-bean="defaultServiceLocator"
    factory-method="createClientServiceInstance"/>
package com.test.spring.test;

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public static AccountService getAccountService() {
        return accountService;
    }
}

Bean Scopes
作用域描述
singleton(默认)每一个Spring IoC容器都拥有唯一的一个实例对象
prototype一个Bean定义可以创建任意多个实例对象
request一个HTTP请求会产生一个Bean对象,也就是说,每一个HTTP请求都有自己的Bean实例。只在基于web的Spring ApplicationContext中可用
session限定一个Bean的作用域为HTTPsession的生命周期。同样,只有基于web的Spring ApplicationContext才能使用
application限定一个Bean的作用域为ServletContext的生命周期。同样,只有基于web的Spring ApplicationContext可用
websocket限定一个Bean的作用域为WebSocket的生命周期。同样,只有基于web的Spring ApplicationContext可用
Lifecycle callbacks

实现Spring的InitializingBean和DisposableBean接口的类,容器会自动调用此Bean的生命周期回调函数,对应的回调函数为afterPropertiesSet(Bean创建后调用)、destroy(Bean销毁前调用)。
JSR-250的@PostConstruct和@PreDestroy注解可以达到同样的效果。

Initialization callbacks

InitializingBean接口允许Bean在所有的依赖 配置完成后执行初始化操作。InitializingBean定义了一个接口:void afterPropertiesSet() throws Exception;
建议开发者不要使用InitializingBean接口的,因为这样代码会与Spring耦合。建议使用@PostConstruct注解,或通过配置bean的特定方法来实现(如下图)。基于XML的配置时,开发者可以使用init-method属性来指定一个没有参数的方法。使用Java配置的开发者可以使用@Bean之中的initMethod属性,如下:

  <bean id="exampleBean" class="com.test.spring.test.AnotherExampleBean"/>
package com.test.spring.test;

import org.springframework.beans.factory.InitializingBean;

public class AnotherExampleBean implements InitializingBean {
    public void afterPropertiesSet() throws Exception {}
}

或者

  <bean id="exampleBean" class="com.test.spring.test.ExampleBean" init-method="init"/>
package com.test.spring.test;

public class ExampleBean {
    public void init() {}
}
Destruction callbacks

实现了DisposableBean接口的Bean,可以在Bean销毁前执行特定的逻辑(在destroy方法中释放资源等)。DisposableBean定义了一个接口:void destroy() throws Exception;
建议不用DisposableBean接口,因为会与Spring耦合。建议使用@PreDestroy注解,或通过配置bean的特定方法来实现。当基于XML配置时,可配置Bean标签的destroy-method属性。基于Java配置时,可以配置@Bean的destroyMethod来实现。

  <bean id="exampleBean" class="com.test.spring.test.AnotherExampleBean"/>
package com.test.spring.test;

import org.springframework.beans.factory.InitializingBean;

public class AnotherExampleBean implements InitializingBean {
    public void afterPropertiesSet() throws Exception {}
}

或者

  <bean id="exampleBean" class="com.test.spring.test.ExampleBean" destroy-method="cleanup"/>
package com.test.spring.test;

public class ExampleBean {
    private void cleanup() {}
}
Aware interfaces
接口名描述
ApplicationContextAware声明的ApplicationContext
ApplicationEventPlulisherAwareApplicationContext中的事件发布器
BeanClassLoaderAware加载Bean使用的类加载器
BeanFactoryAware声明的BeanFactory
BeanNameAwareBean的名字
BootstrapContextAware容器运行的资源适配器BootstrapContext,通常仅在JCA环境下有效
LoadTimeWeaverAware加载期间处理类定义的weaver
MessageSourceAware解析消息的配置策略
NotificationPublisherAwareSpring JMX通知发布器
PortletConfigAware容器当前运行的PortletConfig,仅在web下的Spring ApplicationContext中可见
PortletContextAware容器当前运行的PortletContext,仅在web下的Spring ApplicationContext中可见
ResourceLoaderAware配置的资源加载器
ServletConfigAware容器当前运行的ServletConfig,仅在web下的Spring ApplicationContext中可见
ServletContextAware容器当前运行的ServletContext,仅在web下的Spring ApplicationContext中可见
Annotation-based configuration

@Required - 适用于property setter方法,只能配置在方法上。表明此属性在配置期间必须设值,没有设值则抛异常。
@Autowired - 自动注入依赖的Bean,可用于构造函数、方法、参数、字段、注解类型。此为按类型查找bean。如果此类型在容器中存在多个bean,而你只需要一个,则你需要通过特殊手段(@Primary、@Qualifier)去过滤出你想要的bean。

Autowired也可用于集合,如:

  1. @Autowired private UserTemplate[] userTemplates
  2. @Autowired(required = false)表示容器中没有候选bean时也不会报错
  3. @Autowired private Set< UserTemplate> userTemplates

@Autowired private Map<String, UserTemplate> userTemplates

Autowired原理:
  1. 从容器中找出指定类型的所有bean,把这些候选bean的name都统计出来。
  2. 遍历所有候选bean的name,验证他们是否为合格的候选bean。此判断的依据为:是否使用@Qualifier。如果没有使用@Qualifier则所有的bean都是合格的,如果使用了@Qualifier则只有@Qualifier value值相同的才是合格的候选bean(创建bean时Qualifier Name默认为bean name,所以可不配置。但使用方(依赖方)需配置@Qualifier value值)。
  3. 筛选出合格的候选bean name后,判断此时集合的大小。如果为0则报NoSuchBeanDefinitionException异常。如果整好等于1,则使用此bean。
  4. 当合格的候选bean大于1时,则首先去找@Primary bean,有则使用它,没有则找出候选bean 中name与@Autowired对象名(如:field name)相同的bean,如果还没匹配则抛NoUniqueBeanDefinitionException异常。

注:源码请参考DefaultListableBeanFactory类doResolveDependency方法

@Primary - 当容器中相同类型有多个bean时,可用Primary表明哪个bean享有优先权。
@Qualifier - 当容器中相同类型有多个bean时,可用Qualifier指定使用哪些bean,多个bean的Qualifier值可以相同。
@Resource -

  • @Resource没配置name属性
    1. 按规约获取默认名,@Resource置于field上则取field name,@Resource置于property setter方法上则取property name。
    2. 如果容器中包含此默认名的bean,则找出此bean并判断此bean的类型是否是你需要的。
    3. 如果容器中没有此默认名的bean,则使用备选方案,按类型查找bean(和Autowired判断逻辑一样)。
  • @Resource已配置name属性
    1. 不管容器中是否包含此name的bean,都依据此name值及类型去容器中找出唯一的bean。

注:源码请参考CommonAnnotationBeanPostProcessor类的autowireResource方法

Java-based configuration

Spring基于Java配置的核心在于@Configuration(用于类)、@Bean(用于方法)。@Bean注解和< bean>元素扮演相同的角色,@Configuration类似于< beans>。

package com.test.spring.test;

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

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

等于

<beans>
  <bean id="myService" class="com.test.spring.test.MyServiceImpl"/>
</beans>
@Bean

@Bean是一个方法级的注解,与XML的< bean/>元素功能相同。该注解支持一些< bean/>上的属性,如:init-method, destroy-method, autowiring 和 name。 你可以在@Configuration或@Component类里使用@Bean注解。
要声明一个bean,可以使用@Bean注解到一个方法上。默认bean name与方法名相同。

package com.test.spring.test;

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

@Configuration
public class AppConfig {
    @Bean
    public TransferSerivceImpl transferSerivce() {
        return new TransferSerivceImpl();
    }
}

等于

<beans>
  <bean id="transferSerivce" class="com.test.spring.test.TransferSerivceImpl"/>
</beans>
Lifecycle Callbacks

默认情况下,基于Java配置的Bean,包含public close/shutdown 方法,会被自动的置为destruction callback(应用关闭时自动执行close/shutdown方法)。如果你不需要此默认特性,你可以配置成空字符@Bean(destroyMethod="")。

package com.test.spring.test;

public class TestOne {
    public void init() {}
}
package com.test.spring.test;

public class TestTwo {

    public void cleanup() {

    }
}
package com.test.spring.test;

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

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public TestOne testOne() {
        return new TestOne();
    }

    @Bean(destroyMethod = "cleanup")
    public TestTwo testTwo() {
        return new TestTwo();
    }
}
@Scope
package com.test.spring.test;

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

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {

    }
}
自定义Bean Name
package com.test.spring.test;

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

@Configuration
public class AppConfig {
    @Bean(name = "myThing")
    public Thing thing() {
        return new Thing();
    }
}
package com.test.spring.test;

import javax.activation.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean(name = {"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
    public DataSource dataSource() {

    }
}
Bean之间的依赖
package com.test.spring.test;

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

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl;
    }
}

等于

package com.test.spring.test;

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

@Configuration
public class AppConfig {

    @Bean
    public TransferSerivce transferSerivce(AccountRepository accountRepository) {
        return new TransferSerivce(accountRepository);
    }

    @Bean
    public TestOne testOne() {
        return new TestOne(testTwo());
    }

    @Bean
    public TestTwo testTwo() {
        return new TestTwo();
    }
}
Bean Description - 常用于JMX
package com.test.spring.test;

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

@Configuration
public class AppConfig {
    @Bean
    @Description("Provides a basic example of a bean")
    public Thing thing() {
        return new Thing();
    }
}
@Import
package com.test.spring.test;

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

@Configuration
public class ConfigA {
    @Bean
    public A a() {
        return new A();
    }
}
package com.test.spring.test;

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

@Configuration
@Import(ConfigA.class)
public class ConfigB {
    @Bean
    public B b() {
        return new B();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值