spring 框架说明文档学习记录(3.4)

时间:2022-06-30 05:42:01
Bean范围
当我们创建一个bean定义时,我们创建了一个根据定义来实例化class的规则。

我们不仅能够控制各种依赖和配置,也包括从bean定义创建的实例的范围

spring framework支持六种范围

singleton  :  (默认)每一个spring容器中,一个bean定义只有一个对象实例

prototype  :   一个bean定义有任意多个对象实例

request  :  一个bean定义的范围对应一个HTTP request的生命周期,也就是每一个HTTP请求都有自己的bean实例,仅在web相关的ApplicationContext中有效

session  :  一个bean定义的范围对应一个HTTP session的生命周期,仅在web相关的ApplicationContext中有效

application  :  一个bean定义的范围对应一个ServletContext的生命周期,仅在web相关的ApplicationContext中有效
websocket  :  一个bean定义的范围对应一个Websocket的生命周期,仅在web相关的ApplicationContext中有效


注意:Spring 3.0起,我们可以使用线程范围


sigleton

spring 框架说明文档学习记录(3.4)

spring的单例与GOF的单例模式不同,GOF中单例是每个ClassLoader中有且只有一个class的实例,而Spring的单例,可以描述为一个容器一个实例。也就是说,如果你为某个类定义一个bean,然后该容器根据该bean定义创建一个且只有一个该class的实例。

spring的单例为默认模式

<bean id="accountService" class="com.foo.DefaultAccountService"/>
<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>

原型模式

bean部署的原型模式,每一次对指定bean的请求,都会创建一个新的bean实例。

spring 框架说明文档学习记录(3.4)

<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
与其他模式对比,Spring不会管理原型模式的整个生命周期,容器实例化、配置、组合一个原型对象,将它交给调用者,然后就不再记录该原型对象实例。

因此,尽管无论任何模型,生命周期中的回调函数都会被调用,但在原型模式下,配置好的生命周期析构函数不会被调用。调用端代码必须清理原型模式的对象,回收原型对象占用的资源。


Request、session、application、Websocket模式

初始化web模式的配置

1,使用spring web mvc,无需做特殊设置,DispatcherServlet已经显式相关状态

2,使用servlet 2.5,不使用DispatcherServlet的情况下,需要注册org.springframework.web.context.request.RequestContextListener监听器。

3,使用servlet3.0+,类似的使用WebApplicationInitializer接口。

<web-app>
  ...
  <listener>
    <listener-class>
      org.springframework.web.context.request.RequestContextListener
    </listener-class>
  </listener>
  ...
</web-app>

如果监听器设置有问题,可以使用Spring的RequestContextFilter过滤器替代

<web-app>
  ...
  <filter>
    <filter-name>requestContextFilter</filter-name>
    <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>requestContextFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  ...
</web-app>

Request 模式

xml配置

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
java注解配置

@RequestScope
@Component
public class LoginAction {
// ...
}


Session模式

XML配置

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
java注解配置

@SessionScope
@Component
public class UserPreferences {
// ...
}


Application模式

xml配置

<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>

java注解配置

@ApplicationScope
@Component
public class AppPreferences {
// ...
}

各范围bean的依赖

Spring IOC 容器不仅管理beans的实例化,同时也管理bean的集成与依赖。如果你想将一个HTTP请求范围的bean注入另一个更长生命周期范围的bean中,你可以选择在HTTP请求范围的bean中注入AOP代理。

<?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
  <!-- an HTTP Session-scoped bean exposed as a proxy -->
  <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
    <!-- instructs the container to proxy the surrounding bean -->
    <aop:scoped-proxy/>
  </bean>
  <!-- a singleton-scoped bean injected with a proxy to the above bean -->
  <bean id="userService" class="com.foo.SimpleUserService">
    <!-- a reference to the proxied userPreferences bean -->
    <property name="userPreferences" ref="userPreferences"/>
  </bean>
</beans>

定制范围

bean范围机制可扩展,你可以定义自己的范围,或者重新定义已存在的范围,尽管后一种被认为是坏习惯,并且你不能覆盖内嵌的单例和原型范围。

创建定制范围

实现org.springframework.beans.factory.config.Scope接口

使用定制范围

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
<bean id="..." class="..." scope="thread">
也可以通过CustomScopeConfigurer类来使用

<?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
  <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
      <map>
        <entry key="thread">
          <bean class="org.springframework.context.support.SimpleThreadScope"/>
        </entry>
      </map>
    </property>
  </bean>
  <bean id="bar" class="x.y.Bar" scope="thread">
    <property name="name" value="Rick"/>
    <aop:scoped-proxy/>
  </bean>
  <bean id="foo" class="x.y.Foo">
    <property name="bar" ref="bar"/>
  </bean>
</beans>

生命周期中的回调

初始化的回调

org.springframework.beans.factory.InitializingBean接口允许一个bean在所有需要的属性都被容器设置好之后执行初始化工作。InitializingBean接口指定了一个方法:

void afterPropertiesSet() throws Exception;

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
  public void afterPropertiesSet() {
    // do some initialization work
  }
}
与通过xml的配置是相同的

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
  public void init() {
    // do some initialization work
  }
}

析构函数的回调
实现org.springframework.beans.factory.DisposableBean接口,可以使一个bean在包含它的容器释放它后获取一个回调。DisposableBean指定了一个方法。
void destroy() throws Exception;
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
  public void destroy() {
    // do some destruction work (like releasing pooled connections)
  }
}
与通过xml的配置是相同的

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
  public void cleanup() {
    // do some destruction work (like releasing pooled connections)
  }
}
默认的初始化和析构的回调
我们可以配置容器,去查找每一个bean的初始化和析构的回调函数
配置如下
public class DefaultBlogService implements BlogService {
  private BlogDao blogDao;
  public void setBlogDao(BlogDao blogDao) {
    this.blogDao = blogDao;
  }
  // this is (unsurprisingly) the initialization callback method
  public void init() {
    if (this.blogDao == null) {
      throw new IllegalStateException("The [blogDao] property must be set.");
    }
  }
}
<beans default-init-method="init">
  <bean id="blogService" class="com.foo.DefaultBlogService">
    <property name="blogDao" ref="blogDao" />
  </bean>
</beans>
default-destroy-method与之类似

多种机制的结合

Spring 2.5后,我们有三种控制bean生命周期行为的方式

1,InitializingBean和DisposableBean回调接口;

2,定制init()和destroy()方法;

3,@PostConstruct和@PreDestroy注解;

这几种机制可以混合使用

如果一个bean上有多种机制同时存在,且指定的不是同一个方法,则初始化回调按照如下顺序依次执行

    @PostConstruct注解的方法

    InitializingBean回调接口定义的afterPropertiesSet()方法

    一个配置的init()方法

析构函数回调按照同样顺序

    @PreDestroy注解的方法

    DisposableBean回调接口定义的destroy()方法

    一个配置的destroy()方法


启动和关闭回调回调

Lifecycle为任何有生命周期需求的对象定义了关键的方法

public interface Lifecycle {
  void start();
  void stop();
  boolean isRunning();
}
任何实现了该接口的spring对象,在ApplicationContext接收到开始或结束信号时,它会传递该信号给该context中所有Lifecycle的实现 。通过委托给 LifecycleProcessor 来做这个工作。 注意 LifecycleProcessor 自身扩展了Lifecycle接口。它也增加了两个其他的方法来与上下文交互,使得可以刷新和关闭。

public interface LifecycleProcessor extends Lifecycle {
  void onRefresh();
  void onClose();
}

非web环境下,合理的关闭Spring IOC 容器

定义一个关闭钩子关闭容器,可以使容器合理的调用bean的destroy方法

我们可以通过嗲用ConfigurableApplicationContext接口中定义的registerShutdownHook()方法,来注册一个关闭钩子

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
  public static void main(final String[] args) throws Exception {
    ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
    new String []{"beans.xml"});
    // add a shutdown hook for the above context...
    ctx.registerShutdownHook();
    // app runs here...
    // main method exits, hook is called prior to the app shutting down...
  }
}

ApplicationContextAware和BeanNameAware

当一个ApplicationContext创建一个实现了org.springframework.context.ApplicationContextAware接口的对象实例时,这个实例会拥有该ApplicationContext的一个引用

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

当一个ApplicationContext创建一个实现了org.springframework.beans.factory.BeanNameAware接口的对象实例时,这个实例会拥有该对象名称的一个引用

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

其他的意识接口

ApplicationContextAware

ApplicationEventPublisherAEwvaenrte

BeanClassLoaderAware

BeanFactoryAware

BeanNameAware

BootstrapContextAware

LoadTimeWeaverAware

MessageSourceAware

NotificationPublisherAwareSpring

ResourceLoaderAware

ServletConfigAware

ServletContextAware