IOC回顾

IOC

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)

spring实现IOC的思路和方法

spring实现IOC的思路是提供一些配置信息用来描述类之间的依赖关系,然后由容器去解析这些配置信息,继而维护好对象之间的依赖关系。

spring实现IOC的思路大致可以拆分成:

1
2
3
应用程序中提供类和类的依赖关系(属性或者构造方法)
把需要容器管理的对象通过配置信息告诉容器(xml、annotation,javaconfig)
把各个类之间的依赖关系通过配置信息告诉容器

配置这些信息的方法有三种分别是xml,annotation和javaconfig,容器维护这些类和对象的过程称为自动注入,自动注入的方法有两种:构造方法和setter注入。

1
2
3
schemal-based-------xml
annotation-based-----annotation
java-based----java Configuration

spring注入详细配置(字符串、数组等)参考文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-properties-detailed

Constructor-based Dependency Injection

构造方法注入参考文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-constructor-injection

1
2
3
4
5
6
7
8
package x.y;

public class ThingOne {

public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
1
2
3
4
5
6
7
8
9
10
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>

<bean id="beanTwo" class="x.y.ThingTwo"/>

<bean id="beanThree" class="x.y.ThingThree"/>
</beans>

Setter-based Dependency Injection

参考文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-setter-injection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>

<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

自动装配

IOC的注入需要提供的依赖关系,一是类的定义体现依赖关系,二是在配置文件中描述依赖关系。自动装配则把配置文件中描述依赖关系取消了,只要提供好类的依赖关系就可以把对象交给容器管理完成注入。

在实际开发中,描述类之间的依赖关系通常是大篇幅的,如果使用自动装配则省去了很多配置,并且如果对象的依赖发生更新我们可以不需要去更新配置,但是也带来了一定的缺点。

自动装配的优点参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-autowire

缺点参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-autowired-exceptions

自动装配的方法有四种:no/byName/byType/constructor

自动装配的方式参考文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-autowire

20200420222616

从自动装配中排除Bean

在每个bean的基础上,您可以从自动装配中排除一个bean。使用Spring的XML格式,将元素的autowire-candidate属性设置为false。容器使特定的bean定义对自动装配基础结构不可用(包括注释样式配置,例如@Autowired)。

懒加载

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-lazy-init

1
2
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>

或注解形式@Lazy

Spring Bean的作用域

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes

20200420222645

xml方式

1
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

或者直接使用注解方式。

Spring的方法注入可分为两种

查找方法注入:用于注入方法返回结果,也就是说能通过配置方式替换方法返回结果。即我们通常所说的lookup-method注入。

1
2
单例模式的bean只会被创建一次,IoC容器会缓存该bean实例以供下次使用;原型模式的bean每次都会创建一个全新的bean,IoC容器不会缓存该bean的实例。
那么如果现在有一个单例模式的bean引用了一个原型模式的bean呢?如果无特殊处理,则被引用的原型模式的bean也会被缓存,这就违背了原型模式的初衷,这时使用lookup-method注入可以解决该问题。

替换方法注入:可以实现方法主体或返回结果的替换,即我们通常所说的replaced-method注入。

lookup-method注入 @Lookup

** 在Singleton 当中引用了一个Prototype的bean的时候引发的问题: **

1
2
3
4
在Spring的诸多应用场景中bean都是单例形式,当一个单利bean需要和一个非单利bean组合使用或者一个非单利bean和另一个非单利bean组合使用时,我们通常都是将依赖以属性的方式放到bean中来引用,然后以@Autowired来标记需要注入的属性。
但是这种方式在bean的生命周期不同时将会出现很明显的问题,假设单利bean A需要一个非单利bean B(原型),我们在A中注入bean B,每次调用bean A中的方法时都会用到bean B,
我们知道Spring Ioc容器只在容器初始化时执行一次,也就是bean A中的依赖bean B只有一次注入的机会,但是实际上bean B我们需要的是每次调用方法时都获取一个新的对象(原型)
所以问题明显就是:我们需要bean B是一个原型bean,而事实上bean B的依赖只注入了一次变成了事实上的单利bean。

解决方式:去找 或者赋值

1、Method Injection

继承ApplicationContextAware 每次调用方法时用上下文的getBean(name,class)方法去重新获取bean B的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

private ApplicationContext applicationContext;

public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}

protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}

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

2、Lookup Method Injection

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class CommandManager {

public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}

// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}

通过一个注解或xml配置实现

1
2
3
4
5
6
7
8
9
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class CommandManager {

public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}

// 也可以写成 @Lookup("prototypeBean") 来指定需要注入的bean
@Lookup
protected abstract MyCommand createCommand();
}

//或者不用抽象类
@Lookup
public MyCommand createCommand() {
return null;
}

replaced-method注入

主要作用就是替换方法体及其返回值,其实现也比较简单

replace-method注入需实现MethodReplacer接口,并重写reimplement方法。

Lifecycle Callbacks 生命周期回调

初始化回调

实现接口org.springframework.beans.factory.InitializingBean 指定一个方法: void afterPropertiesSet() throws Exception;

或者是配置文件中直接一个初始化方法:

1
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

销毁回调

org.springframework.beans.factory.DisposableBean当包含该接口的容器被销毁时,实现该接口可使Bean获得回调 public void destroy() 。

1
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>

注解方式实现:从Spring 2.5开始,可以使用三种方式来控制Bean生命周期行为:

1、在InitializingBean(初始化bean之后)和 DisposableBean回调接口

2、配置init()和destroy()方法

3、在@PostConstruct(初始化bean之前)和@PreDestroy 注释。您可以结合使用这些机制来控制给定的bean。

如果同一个bean配置的具有不同初始化方法的顺序为:

1
2
3
4
5
6
7
8
用注释的方法 @PostConstruct
afterPropertiesSet()由InitializingBean回调接口定义
定制配置的init()方法

销毁方法的调用顺序相同:
用注释的方法 @PreDestroy
destroy()由DisposableBean回调接口定义
定制配置的destroy()方法

相关注解

1
2
3
4
5
@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常 
@Qualifier:限定符 @Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用
例子:
@Autowired
@Qualifier(注入的类)

depends-on

如果 bean 是另一个的依赖关系,则需要首先注入这个类。

1
2
3
4
5
6
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

生成候选组件的索引

当有很多个类需要扫描,会比较慢,使用如下包来在编译的时候就加入到索引,按照索引的方式去扫描就会很快。

1
2
3
4
5
6
7
8
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>5.2.3.RELEASE</version>
<optional>true</optional>
</dependency>
</dependencies>

java也有一套注解,可以用来开发spring。

20200420222714