Spring IoC容器

一、Bean作用域

用来声明IOC容器中的对象应该处的限定场景或者说该对象的存活空间称作Bean的作用域(Scope)。

IOC容器在对象进入相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。

默认是单例模式,@Scope("singleton")。

  • singleton:全局有且仅有一个实例
  • prototype:每次获取Bean的时候会有一个新的实例
  • request:表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
  • session:表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。
  • application:将单个bean定义限定为ServletContext的生命周期。仅在支持web的Spring应用程序上下文中有效。
  • websocket:将单个bean定义限定为WebSocket的生命周期。仅在支持web的Spring应用程序上下文中有效。
Singleton Bean 和 prototype Bean 的区别

当一个bean被声明为单例bean(singleton)的时候,在处理多次请求的时候Spring容器中只会实例化出一个bean,且后续的请求都会共用这个对象,这个对象将被保存在一个Map集合中。当有请求来时则先从缓存中查看之前有无生成该对象,有的话直接使用这个对象,没有则实例化一个新的对象。而对于原型bean(Prototype)来说,每次请求来时都直接实例化新的bean,没有从缓存中查询获取的过程。

单例Bean的优势
  • 减少了新生成实例的消耗。这个消耗主要体现在 ①Spring在创建实例时会造成性能的消耗。②给对象分配内存也会涉及到一些复杂的算法。
  • 可以快速的获取到bean。在单例下的bean除了第一次生成时需要创建bean外,其余时间都是从缓存(Map)中获取,所以速度快。
  • 减少jvm垃圾回收。对于所有请求只生成一个bean实例,所以垃圾回收自然就少了。
单例Bean的劣势

单例bean的一个很大劣势是是线程不安全的。由于所有请求都共享一个bean实例,且当这个bean有状态的时候在并发的场景下容易出现问题,相比之下原型bean则不会有这样的问题(也有例外,比如这个原型bean被单例bean所依赖),因为原型bean会给每个请求都新创建实例。

什么是有状态Bean和无状态Bean

有状态会话bean   :每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。简单来说,有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。
无状态会话bean   :bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean   并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。简单来说,无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。

不变模式

不变模式只涉及到一个类。一个类的内部状态创建后,在整个生命期间都不会发生变化时,这样的类称为不变类。这种使用不变类的做法叫做不变模式。

不变模式有两种形:一种是弱不变模式,另一种是强不变模式。

弱不变模式

一个类的实例的状态是不可变化的;但是这个类的子类的实例具有可能会变化的状态。这样的类符合弱不变模式的定义。要实现弱不变模式,一个类必须满足下列条件:

  • 所考虑的对象没有任何方法会修改对象的状态;当对象的构造方法将对象的状态初始化之后,对象的状态将不再改变。
  • 所有的属性都应当是私有的。不要声明任何公开的属性,以防客户端对象直接修改任何内部状态。
  • 这个对象所引用的其他对象如果是可变对象的话,必须设法限制外界对这些可变对象的访问,以防止外界修改这些对象。

弱不变模式的缺点:

  • 一个弱不变对象的子对象是可以改变的;换言之,一个弱不变对象的子对象可能是可变的。
  • 这个可变的子对象可能可以修改父对象的状态,从而可能会允许外界修改父对象的状态。

强不变模式

一个类的实例的状态不会改变, 同时它的子类的实例也具有不可变化的状态。这样的类符合强不变模式,一个类必须首先满足弱不变模式所要求的所有条件,并且还要满足下面条件之一:

  • 所考虑的类所有的方法都应该是final的;这个类的子类不能够重写此类的方法;
  • 类本身是final的,那么这个类也就不可能有子类。

不变模式在Java中的应用

  • String类
  • 其他包装类,如Integer、Float、Double、Byte、Long、Short和Character等。

不变模式的优缺点

优点:

  • 不能修改一个不变对象的状态,所以可以避免由此引起的不必要的程序错误,易于维护。
  • 不变对象本身是线程安全的,这样可以省掉处理同步化的开销。

缺点:

一旦需要修改一个对象的状态,就只好创建一个新的同类对象。在需要频繁修改不变对象的环境里,会有大量的不变对象作为中间结果被创建出来再被GC收集,这是一种资源上的浪费。

二、Bean定制

 Spring框架提供了许多接口,可以用来定制bean。

生命周期回调

为了与容器对bean生命周期的管理进行交互,可以实现Spring的InitializationBean 和 DisposableBean接口。容器为前者调用afterPropertiesSet(),为后者调用destroy(),让bean在初始化和销毁bean时执行某些操作。

JSR-250提供的@PostConstruct和@PreDestroy注解通常被认为是Spring应用程序中接收生命周期回调的最佳实践。使用这些注释意味着bean不耦合到Spring特定的接口。

如果您不想使用JSR-250注解,但仍然希望消除耦合,请考虑init method和destroy method bean定义元数据。

ApplicationContextAware 和 BeanNameAware

当ApplicationContext创建实现org.springframework.context.ApplicationContextAware接口,则为实例提供对该ApplicationContext的引用。

public interface ApplicationContextAware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

通过该种方式,bean可以通过ApplicationContext接口或通过将引用强制转换为该接口的已知子类(例如ConfigurableApplicationContext,它公开了附加功能),以编程方式操作创建它们的ApplicationContext。一个用途是对其他bean进行编程式检索。有时这种能力很有用。但是,一般来说,应该避免它,因为它将代码与Spring耦合。ApplicationContext的其他方法提供对文件资源的访问、发布应用程序事件和访问MessageSource。这些附加功能在ApplicationContext的附加功能中进行了描述。

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public final class SpringBeanHelper implements ApplicationContextAware {

    private SpringBeanHelper(){}

    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringBeanHelper.applicationContext == null){
            SpringBeanHelper.applicationContext = applicationContext;
        }
    }

    //通过name获取 Bean.
    public static Object getBean(String name){
        return SpringBeanHelper.applicationContext.getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz){
        return SpringBeanHelper.applicationContext.getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> clazz){
        return SpringBeanHelper.applicationContext.getBean(name, clazz);
    }
}

当ApplicationContext创建实现org.springframework.beans.工厂.BeanNameAware接口,则向类提供对其关联对象定义中定义的名称的引用。

public interface BeanNameAware {
    void setBeanName(String name) throws BeansException;
}

在填充普通bean属性之后,但在初始化回调(如initializegbean、afterPropertiesSet或自定义init方法)之前调用回调。

ApplicationContextAware 和 BeanNameAware在执行顺序上,先执行BeanNameAware接口的setBeanName方法,再执行ApplicationContextAware接口的setApplicationContext方法。

Aware接口列表
  • ApplicationContextAware
  • ApplicationEventPublisherAware
  • BeanClassLoaderAware
  • BeanFactoryAware
  • BeanNameAware
  • BootstrapContextAware
  • LoadTimeWeaverAware
  • MessageSourceAware
  • NotificationPublisherAware
  • ResourceLoaderAware
  • ServletConfigAware
  • ServletContextAware

三、容器扩展点

BeanPostProcessor 

BeanPostProcessor接口定义回调方法,你可以实现这些方法来提供自己的(或覆盖容器的默认)实例化逻辑、依赖关系解析逻辑等。如果你想在Spring容器完成对bean的实例化、配置和初始化之后实现一些自定义逻辑,那么可以插入一个或多个自定义beanoptprocessor实现。

public interface BeanPostProcessor {
    //bean初始化方法调用前被调用
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    //bean初始化方法调用后被调用
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

你可以配置多个BeanPostProcessor实例,并且可以通过设置order属性来控制这些BeanPostProcessor实例的执行顺序。仅当BeanPostProcessor实现有序接口时,才能设置此属性。如果你编写自己的BeanPostProcessor,你也应该考虑实现有序接口。

BeanPostProcessor实例的作用域为容器(ApplicationContext),只对该容器中的bean进行后期处理。

要更改实际的bean定义,您需要使用BeanFactoryPostProcessor。

BeanFactoryPostProcessor

BeanFactoryPostProcessor操作bean配置元数据。

可以配置多个BeanFactoryPostProcessor实例,并且可以通过设置order属性来控制这些BeanFactoryPostProcessor实例的运行顺序。但是,只有在BeanFactoryPostProcessor实现有序接口时才能设置此属性。如果你编写自己的BeanFactoryPostProcessor,也应该考虑实现有序接口。

如果要更改实际的bean实例(即从配置元数据创建的对象),则需要使用BeanPostProcessor(前面在使用BeanPostProcessor定制bean中进行了描述)。而在技术上可以在BeanFactoryPostProcessor中使用bean实例(例如,使用BeanFactory.getBean()),这样做会导致过早的bean实例化,违反标准容器生命周期。这可能会产生负面的副作用,比如绕过bean的后期处理。

ApplicationContext会自动检测部署到其中实现BeanFactoryPostProcessor接口的任何bean。它在适当的时候使用这些bean作为bean工厂的后处理程序。您可以像部署任何其他bean一样部署这些后处理器bean。

FactoryBean

FactoryBean接口是springioc容器实例化逻辑的一个可插拔点。如果您的复杂初始化代码可以更好地用Java表示,而不是(可能)冗长的XML,那么您可以创建自己的FactoryBean,在该类中编写复杂的初始化,然后将自定义的FactoryBean插入容器中。

FactoryBean接口提供了三种方法:

  • Object getObject():返回此工厂创建的对象的实例。实例可以共享,这取决于这个工厂返回的是单例实例还是原型。
  • boolean isSingleton():如果此FactoryBean是singleton,则返回true,否则返回false
  • Class getObjectType():返回由getObject()方法返回的对象类型,如果类型事先未知,则返回null。

FactoryBean的概念和接口在Spring框架中的许多地方都有使用。超过50个FactoryBean接口的实现随Spring一起提供。

当您需要向容器请求一个实际的FactoryBean实例本身,而不是它生成的bean时,在调用ApplicationContext的getBean()方法时,在bean的id前面加上(&)。因此,对于一个id为myBean的给定FactoryBean,在容器上调用getBean(“myBean”)将返回FactoryBean的产品,而调用getBean(“&myBean”)则返回FactoryBean实例本身。

原文地址:https://www.cnblogs.com/myitnews/p/13302006.html