[spring]IoC

来源:互联网 发布:科技部人工智能会议 编辑:程序博客网 时间:2024/05/21 07:36

现实中遇到的问题

我们写的类可以看成一个服务提供者,那么在大部分时候不能再一个类中完成所有的逻辑,也就是说对其他的服务提供者是有依赖的。在实现上当然没有问题:我们在生成自己的类的时候,将依赖的东西全部初始化。

如果有更好的服务提供者来了怎么办?

很简单:在生成自己类的时候,改变依赖的服务的提供者就可以了。

如果在10(或者我也不知道到底哪些)个类中要改成更好的服务提供者怎么办?

这时候就比较麻烦了。那么怎么办呢?把依赖的服务进行硬编码,在修改的时候就需要通过修改这些硬编码来改变依赖的对象,更好的一种办法是:

在类中申明自己需要什么服务,具体由谁来服务由一个框架来选出。

这样就对服务提供者和服务需求进行了解耦。可以看出来spring真是贴心啊,那么:


spring如何注入

spring提供了下面的三种方法进行注入,所谓注入就是说生成一个需要的Bean,其中需要设置的属性、接口都被正确地设置。

1、接口注入:侵入性太强,属于退役状态。

2、setter注入:定义属性的setter方法,在属性很多的时候会有一大串的列表。

3、构造函数:使用构造函数进行注入,如果属性很多时,构造函数会很丑,而且增删属性的时候不好维护。

然后,需要告诉spring我们的类能提供什么服务,也就是能生成些什么样的Bean(BeanDefinition),有点像是一个用来做Bean的模具:

1、直接编码,定义好BeanDefinition,然后注册到BeanFactory中。

2、配置文件,在XML中描述想要注册的Bean,这个方式还是非常方便的。

3、注解方式,配置context:classpath-scanning来扫描个顶路径下的所有的被@Component标注的类,然后自动注册。

那么有了这些模具之后,生成什么样的Bean呢(这里就是用模具来比喻不怎么合适的一个地方了,一提到模具都会想到是批量生产,但Bean的生成并不总是这样):

1、singleton:在IoC容器中共用一个Bean实例(这里和单例模式还是差别很大的),如果你在Bean中定义了一些属性的时候可要注意啦,该实例与BeanFactory共存亡~

2、prototype:在每次getBean的时候都会生成一个新的实例,然后该Bean实例的生命周期就跟BeanFactory没有关系了。

3、request、session、global session,略。

一个Bean被声明为prototype,但是它被注入到singleton的一个Bean中的一个属性,那么多次get该属性会是不同的对象吗?

答案是否定的,在生成singleton类型的Bean的时候已经绑定,之后当然不会再次去初始化~ 如果想在每次get的时候得到不是同一个对象(发挥prototype的威力)可以用spring提供的方法注入:<lookup-method name="methodName" bean="beanId">。这样在每次methodName被执行的时候,就可以得到新的Bean实例。

另一种方法:使用BeanFactoryAware接口,只要能保证每次methodName执行的时候调用了BeanFactory.getBean()就可以实现每次都得到不同的对象。spring在实例化BeanFactoryAware接口的实现类时,会自动将容器本身注入该Bean,这样就得到了BeanFactory,搞定(在methodName方法中调用getBean)。

在spring提供了ObjectFactory,但是它和第二种方法基本上是一样的。

通过这个问题,我们知道了如何去影响生成Bean的过程。那么我们来看下IoC容器处理的流程:

1、容器启动阶段

a、加载meta data。

b、根据meta data生成BeanDefinition,并注册到BeanDefinitionRegistry。

2、在调用getBean的时候,通过解析BeanDefinition的内容来生成Bean实例。


插手容器的启动

spring提供了很方便地注册、生产Bean的方法,但是,有时候我们需要批量地修改Bean的属性(比如给类中的方法加开关),spring提供了一些接口使得我们能干预Bean的整个过程,首先是BeanDefinition。在spring组装好BeanDefinition好之后,我们还可以通过BeanFactoryPostProcessor来对BeanDefiniton进一步加工:

1、可以有多个BeanFactoryPostProcessor。

2、如果在意他们被执行的顺序的话,可以实现Order接口。

3、可以直接在BeanFactory实例上调用postProcessBeanFactory方法。

4、ApplicatonContext会扫描配置中所有的BeanFactoryPostProcessor的实现类并自动执行。

BeanFactoryPostProcessor的定义如下:

public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;}

在spring中提供了一些有用的processor:

a、PropertyPlaceholderConfigurger使得我们能在配置文件使用占位符,占位符的值可以在properties配置文件中统一管理。

b、PropertyOverrideConfigurger可以对容器中配置的任意你想处理的Bean定义的property信息进行覆盖替换。

c、CustomEditorConfigurger并不会修改BeanDefinition,只是辅助性地将后期会用到的信息注册到容器(比如从字符串到具体对象的转换),可以自定义PropertyEditor来解析。


从BeanDefinition到Bean

容器启动后,并不会马上根据BeanDefinition来进行实例化,而是被BeanFactory.getBean触发的,getBean有两种情况下被隐式调用:

1、在实例化对象A的时候,发现其所依赖的B还没有实例化,此时会调用getBean。

2、ApplicationContext启动之后会实例化所有的bean。

spring容器对其所管理的对象全部给予统一的生命周期管理,这些被管理的对象完全摆脱了原来的“new完之后被使用,脱离作用域后被回收”的命运,下面来看每个Bean的经历:

实例化Bean

a、spring采用“策略模式”来决定采用何种方式初始化bean实例,可以通过反射、CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。

b、以BeanWrapper对构造完成的实例进行包裹。

Aware接口

a、在对象实例化完成后,spring会检查该对象实例是否实现了一系列的Aware命名结尾的接口定义。

b、将Aware接口定义中规定的依赖注入给当前对象实例。

BeanFactory类型的容器中定义如下几个Aware:

BeanAware:将bean对应的beanName注入。

BeanClassLoaderAware:将加载bean的classLoader注入。

BeanFactoryAware:BeanFactory将自身注入。

ApplicationContext类型的容器中的Aware接口使用的是BeanPostProcessor的处理方式,有:ResourceLoaderAware、ApplicationeEvent-PublisherAware、MessageSourceAware、ApplicationContextAware。

BeanPostProcessor接口

与BeanFactoryPostProcessor不同的是,BeanFactoryPostProcessor通常会处理容器内所有符合条件的BeanDefinition,而BeanPostProcessor会处理容器内所有符合条件的实例化的对象实例,分别在两个不同的时机执行(前、后)。

InitializingBean和init-method

如果对象实现了InitializingBean,那么此时会调用afterPropertiesSet方法来调整对象(有一定的侵入性),还可以通过init-method属性来设置(可以通过default-init-method统一指定)。

DisposableBean与destroy-method

DisposableBean和destroy-method为对象提供了执行自定义销毁逻辑的机会,如果在spring中注册数据库连接池时会经常用到:在系统退出时连接池应该关闭释放资源。

BeanFactory:我们需要在主程序退出之前(或者其他合适的情况下)调用ConfigurableBeanFactory提供的destroySingletons方法销毁容器中管理的所有singleton类型的对象实例(如果该方法没有被调用,我们就白设置了)。

ApplicationContext:AbstractApplicationContext提供的registerShutdownHook方法通过Runtime类的addShutdownHook的方式来调用对应Bean对象的销毁逻辑(这样在jvm退出前销毁逻辑会被执行)。

END.
原创粉丝点击