wordpress 分页不出来,昆明网站快速优化排名,室内设计效果图的软件,毫州网站建设bean的生命周期 挂钩到bean的创建
通过了解初始化的时间#xff0c;bean可以检查是否满足其所需的所有依赖项。
尽管Spring可以帮助我们检查依赖项#xff0c;但它几乎是一种全有或全无的方法#xff0c;并且不会提供任何机会来将其他逻辑应用于依赖项的解析过程中。
假设…bean的生命周期 挂钩到bean的创建
通过了解初始化的时间bean可以检查是否满足其所需的所有依赖项。
尽管Spring可以帮助我们检查依赖项但它几乎是一种全有或全无的方法并且不会提供任何机会来将其他逻辑应用于依赖项的解析过程中。
假设一个bean有四个被声明为setter的依赖项其中两个是必需的还有一个bean在没有提供依赖项的情况下提供合适的默认值。
通过使用初始化回调bean可以检查它所需的依赖项触发异常或根据需要提供默认值。
一个bean不能在其构造函数中执行这些检查因为Spring无法为所需的依赖项提供值。
Spring的初始化回调函数在Spring完成提供依赖项之后调用并执行你所要求的依赖项检查。
不限于使用初始化回调来检查依赖项可以在回调中做任何想要做的事情但是它对于我们描述的目的来说非常有用。
在创建bean时执行方法
接收初始化回调的一种方法是在bean上指定一个方法作为初始化方法并告诉Spring将此方法用作初始化方法。
指定回调方法只是在bean的标记的init-method属性中指定名称的一种情况。
/*** MyBean 类*/
Bean(initMethod init)
public class MyBean {/*** 初始化方法*/public void init(){System.out.println(MyBean created!);}
}
初始化方法的唯一限制是不能接收任何参数。可以定义任何返回类型虽然返回值会被Spring忽略甚至可以使用静态方法但也不能接收任何参数。
当使用静态初始化方法时这种机制带来的好处就被否定了因为无法访问任何bean的状态来加以验证。
如果bean正在使用静态状态作为节约内存的机制同时正在使用静态初始化方法来验证状态那么应考虑将静态状态移至实例状态并使用非静态初始化方法。
虽然也可以使用Spring的单例管理功能来实现相同的效果。
实现InitializingBean接口
Spring中定义的InitializingBean接口允许在bean代码中定义希望bean接收的Spring已经完成配置的通知。
使用初始化方法的方式相同通过实现InitializingBean接口可以创造机会来检查bean配置以确保它是有效的并提供任何默认值。
InitializingBean接口定义了一个方法即afterPropertiesSet(),它的作用与前一节介绍的init)方法的作用相同。
以下代码片段重新实现了前面的示例但此时使用InitializingBean接口替换了初始化方法
public class MyBean implements InitializingBean {/*** 在属性设置之后执行的操作** throws Exception 如果在设置属性时发生异常*/Overridepublic void afterPropertiesSet() throws Exception {System.out.println(MyBean created!);}
}
使用JSR-250 PostConstruct注解
/*** 我的Bean类*/
public class MyBean {/*** 在PostConstruct注解的方法在对象创建完成后自动执行** throws Exception 异常*/PostConstructpublic void afterPropertiesSet() throws Exception {System.out.println(MyBean created!);}
}
该程序与使用init-method方法相同只是在init()方法前应用了PostConstruct注解。
了解解析顺序
所有初始化机制都可以在同一个bean实例上使用。 在这种情况下Spring首先调用使用了PostConstruct注解的方法然后调用afterPropertiesSet(),最后调用配置文件中指定的初始化方法。
该顺序是由一个技术原因决定的可以注意到在bean创建过程中主要完成以下步骤
首先调用构造函数来创建bean。注入依赖项(调用setter)。现在bean已经存在并且提供了依赖项预初始化的BeanPostProcessor基础结构bean将被查询以查看它们是否想从创建的bean中调用任何东西。这些都是特定于Spring的基础架构bean,它们在创建后执行bean修改操作。 PostConstruct注解由CommonAnnotationBeanPostProcessor注册所以该bean将调用使用了PostConstruct注解的方法。 该方法在bean被构建之后在类被投入使用之前且在bean的实际初始化之前(即在afterPropertiesSet)和init-method之前)执行。InitializingBean的afterPropertiesSet()方法在注入依赖项后立即执行。如果BeanFactory设置了提供的所有Bean属性并且满足BeanFactoryAware和ApplicationContextAware,将会调用afterPropertiesSet()方法。最后执行init-method属性这是因为它是bean的实际初始化方法。
如果你有一个在特定方法中执行某些初始化操作的bean,同时在使用Spring时需要添加更多的初始化代码那么理解不同类型bean的初始化顺序是非常有用的。
挂钩到bean的销毁
当使用封装了DefaultListableBeanFactory接口的ApplicationContext实现可以通过调用ConfigurableBeanFactory.destroySingletons()向BeanFactory发出信号告知销毁所有单例实例。
在应用程序关闭时执行此操作并允许清理bean可能保持打开的任何资源从而使应用程序可以正常关闭。此外在该回调中还可以将存储在内存中的任何数据刷新到持久存储库中并允许bean关闭可能已启动的长时间运行的任何进程。
为了让bean接收到destroySingletons()被调用的通知存在三种选择这些选择类似于用来接收初始化回调的机制。销毁回调通常与初始化回调一起使用。在许多情况下在初始化回调中创建并配置资源然后在销毁回调中释放资源。
在bean被销毁时执行一个方法
如果想要指定一个在bean被销毁时调用的方法只需要在bean的标记的destroy-method属性中指定该方法的名称即可。 Spring在销毁bean的单例实例之前会调用该方法(对于那些有原型作用域的bean,Spring不会调用此方法)。
/*** 自定义Bean类用于在应用程序停止时被销毁*/
Bean(destroyMethod destroy)
public class MyBean {/*** 销毁方法用于在应用程序停止时执行清理操作*/public void destroy(){System.out.println(MyBean destroy!);}
}
这里通过使用destroymethod属性指定destroy()方法作为销毁回调。
实现DisposableBean接口
与初始化回调一样Spring也提供了一个接口(即DisposableBean),你的bean可以实现该接口作为接收销毁回调的机制。DisposableBean接口定义了一个方法destroy(),该方法在bean被销毁之前被调用。
public class MyBean implements DisposableBean {/*** 销毁Bean方法执行与销毁相关的操作。*/Overridepublic void destroy(){System.out.println(MyBean destroy!);}
}
使用JSR-250 PreDestroy注解
义在bean销毁之前所调用方法的第三种方式是使用JSR-250生命周期注解PreDestroy,它与PostConstruct注解相反。
以下代码片段是DestructiveBean的一个版本它在同一个类中同时使用PostConstruct和PreDestroy来执行程序的初始化和销毁操作。
public class MyBean implements DisposableBean {/*** 在Bean被销毁前执行的回调方法。*/PreDestroypublic void destroy(){System.out.println(MyBean destroy!);}
}
了解解析的顺序
创建bean的情况一样可以在同一bean实例上使用所有机制来进行bean销毁。这种情况下Spring首先调用用PreDestroy注解的方法然后调用DisposableBean.destroy),最后调用XML定义中配置的destroy)方法。
使用关闭钩子
在Spring中销毁回调函数的唯一缺点是它们不会自动触发需要记住在应用程序关闭之前调用AbstractApplicationContext.destroy()。
当应用程序作为servlet运行时可以简单地在servlet的destroy()方法中调用destroy()。
但是在独立的应用程序中事情并不那么简单尤其是在应用程序中存在多个退出点时。
幸运的是有一个可行的解决方案。Java允许创建一个关闭钩子(shutdown hook),它是在应用程序关闭之前执行的一个线程。
这是调用AbstractApplicationContext所有具体的ApplicationContext实现都扩展了AbstractApplicationContext的destroy()方法的一种理想方式。
利用此机制的最简单方法是使用AbstractApplicationContext的registerShutdownHook()方法。该方法自动指示Spring注册底层JVM运行时的关闭钩子。bean的声明和配置和之前一样唯一改变的是main()方法添加对ctx.registerShutdownHook的调用同时删除对ctx.destroy()或close()的调用。
让Spring感知bean
相对于依赖查找作为实现控制反转机制的依赖注入的最大亮点之一是bean不需要知道正在管理它们的容器是如何实现的。
对于使用构造函数注入或setter注入的bean而言Spring容器与Google Guice或PicoContainer所提供的容器是相同的。
但是在某些情况下可能需要一个使用依赖注入来获取其依赖项的bean,以便出于某种其他原因而与容器进行交互。
比如一个用来自动配置关闭钩子的bean,它需要访问ApplicationContext。
在其他情况下bean可能想知道它的名称是什么(即在当前ApplicationContext中分配的bean名称),以便可以根据名称进行一些额外的处理。
也就是说此功能真正用于Spring内部使用。为bean名称提供某种业务含义通常是一个糟糕的主意并且可能导致配置问题因为必须人为地操纵bean名称以支持其业务含义。
但是我们发现能够让bean在运行时找到它的名称对于日志记录来说是非常有用的。
假设有许多在不同配置下运行的相同类型的bean。
此时在日志消息中可以包含bean名称以便当出现错误时帮助区分哪些bean发生了错误而哪些bean正常工作。
使用BeanNameAware接口
想要获取自己名称的bean可以实现BeanNameAware接口它有一个方法setBeanName(String)。
在完成bean的配置之后且在调用任何生命周期回调(初始化回调或销毁回调)之前Spring会调用setBeanName()方法。
大多数情况下setBeanName()的实现仅仅是一行代码它将容器传入的值存储在字段中供以后使用。
public class MyBean implements BeanNameAware {// 每一个实例的名称private String name;Overridepublic void setBeanName(String name) {// 将给定的名称赋值给实例的名称this.name name;}
}
使用ApplicationContextAware接口
通过使用ApplicationContextAware接口bean可以获得对配置它们的ApplicationContext实例的引用。
创建此接口的主要原因是为了允许bean在应用程序中访问Spring的ApplicationContext,例如使用getBean()以编程方式获取其他Spring bean。
但是应该避免这种做法并使用依赖注入为bean提供协作者。如果在可以使用依赖注入时使用了基于查找的getBean()方法来获得依赖项那么将会为bean添加不必要的复杂性。
ApplicationContext并不仅仅用于查找bean它可以执行许多其他任务。
正如你在前面看到的其中一项任务是销毁所有单例但在销毁之前会依次通知每个单例。
在前面已经介绍了如何创建一个关闭钩子来确保在应用程序关闭之前指示ApplicationContext销毁所有单例。
可以使用ApplicationContextAware接口创建一个bean,该bean可以在ApplicationContext中配置并自动创建和配置关闭钩子bean。
/*** MyBean 类是一个实现了 ApplicationContextAware 接口的 Java 类。*/
public class MyBean implements ApplicationContextAware {/*** setApplicationContext 方法用于设置 ApplicationContext。** param applicationContext 应用程序上下文* throws BeansException 如果设置应用程序上下文时发生错误则抛出 BeansException 异常*/Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {}
}
使用FactoryBean
在使用Spring时会面临一个问题如何创建并注入不能简单地使用new运算符创建的依赖项。
为了解决这个问题Spring提供了FactoryBean接口该接口充当不能使用标准Spring语义创建和管理的对象的适配器。通常使用FactoryBean创建不能通过使用new运算符创建的bean,例如通过静态工厂方法访问的bean,尽管情况并非总是如此。
简而言之FactoryBean是一个bean,可以作为其他bean的工厂。
FactoryBean像任何普通bean一样在ApplicationContext中配置但是当Spring使用FactoryBean接口来满足依赖或查找请求时它并不返回FactoryBean。相反它调用FactoryBean.getObject()方法并返回调用的结果。
FactoryBean在Spring中的使用效果很好最明显的用途是创建事务代理以及从JNDI上下文中自动获取资源。
但是FactoryBean不仅仅用于构建Spring的内部组件当构建自己的应用程序时你会发现它们也非常有用因为它们允许通过使用IoC来管理更多的资源。
FactoryBean示例MessageDigestFactoryBean
一般来说所开发的项目需要进行某种密码处理通常包括生成存储在数据库中的消息摘要或者用户密码的哈希值。在Java中MessageDigest类提供了创建任意数据摘要的功能。
MessageDigest本身是抽象的通过调用MessageDigest.getlnstance()并传入想要使用的摘要算法的名称来获得具体的实现。
例如如果想使用MD5算法创建一个摘要可以使用下面的代码来创建MessageDigest实例 MessageDigest md5 MessageDigest.getInstance(“MD5”); 如果想用Spring来管理MessageDigest对象的创建那么在没有FactoryBean的情况下可以采用的最好方法是使用bean上的属性algorithmName,然后使用初始化回调来调用MesageDigestgetlnstance();而如果使用FactoryBean,则可以将相关逻辑封装在一个bean中。
然后需要MessageDigest实例的任何bean都可以简单地声明属性messageDigest,并使用FactoryBean来获取实例。下面的代码片段中的FactoryBean实现了上述功能
/*** 消息摘要工厂类*/
public class MessageDigestFactoryBean implements FactoryBeanMessageDigest,InitializingBean {private MessageDigest messageDigest;Overridepublic MessageDigest getObject() throws Exception {return messageDigest;}Overridepublic ClassMessageDigest getObjectType() {return MessageDigest.class;}Overridepublic void afterPropertiesSet() throws Exception {messageDigestMessageDigest.getInstance(md5);}
}
pring调用getObject()方法来检索由FactoryBean创建的对象。该对象将被传递给使用FactoryBean作为协作者的其他bean。
在上述代码片段中可以看到MesageDigestFactoryBean传递了一个在InitializingBean.afterPropertiesSet()回调中创建的用于存储MessageDigest实例的副本。
getObjectType()方法允许告诉Spring FactoryBean所返回对象的类型。
如果事先不知道返回类型(例如FactoryBean根据配置创建不同类型的对象具体的类型只有在FactoryBean初始化后才能确定),那么对象类型可以为null,但如果指定了类型那么 Spring可以使用该类型实现自动装配。
在这个示例中将MessageDigest作为类型返回(此时返回的是一个类但也可以尝试返回一个接口并让FactoryBean实例化具体的实现类不过只有在必要的情况下才会这么做),因为我们不知道将返回什么样的具体类型(但这并不重要因为所有的bean都会使用MessageDigest来定义它们的依赖项)。
通过使用isSingleton()属性可以告知Spring FactoryBean是否正在管理一个单例实例。
请记住通过设置FactoryBean的标记的singleton属性可以告诉Spring FactoryBean的单例状态而不是所返回的对象。接下来看看在应用程序中如何使用FactoryBean。
Beanpublic MessageDigestFactoryBean shaDigest(){MessageDigestFactoryBean factoryone new MessageDigestFactoryBean();return factoryone;}直接访问FactoryBean
假设Spring可以自动满足由FactoryBean生成的对象对FactoryBean的引用那么你可能会问是否可以直接访问FactoryBean。答案是肯定的。访问FactoryBean很简单在调用getBean()时用“”符号作为bean名称的前缀即可。
JavaBean PropertyEditor
如果还不完全熟悉JavaBean概念可以看看PropertyEditor,它是一个接口它将属性值从其本机类型表示形式转换为字符串。
最初该接口的设计目的是允许将属性值作为字符串值输入到编辑器中并将它们转换为正确的类型。但是由于PropertyEditor本身就是轻量级的类因此在许多设置中都可以找到它们包括Spring。
因为基于Spring的应用程序中很大一部分属性值都在BeanFactory配置文件中开始生命周期所以它们基本上都是Strings。但是用这些值设置的属性可能并不是字符串类型的。因此为了避免人为创建String类型的属性Spring允许定义PropertyEditor以实现基于字符串的属性值到正确的类型的转换。
下图显示了属于spring-beans包的PropertyEditor的完整列表可以使用任何智能Java编辑器查看此列表。 它们都扩展了java.beans.PropertyEditorSupport,并且可用来将字符串文字隐式转换为要注入bean中的属性值因此这里使用BeanFactory预先注册了它们。 创建自定义PropertyEditor
虽然内置的PropertyEditor涵盖了属性类型转换的一些标准情况但有时可能需要创建自己的PropertyEditor来支持应用程序中所使用的一个类或一组类。
Spring完全支持注册自定义PropertyEditor;唯一的缺点是java.beans.PropertyEditor接口有很多方法其中很多方法与当前的任务(即当前转换属性类型的任务)无关。
值得庆幸的是JDK5或更新的版本提供了其PropertyEditor可以扩展的PropertyEditorSupport类该类只有一个方法setAsText)。接下来用一个简单的示例演示如何实现一个自定义属性编辑器。
假设有一个FullName类它只有两个属性firstName和lastName,定义如下所示 为了简化应用程序配置开发一个自定义编辑器将带有空格分隔符的字符串分别转换为FullName类的名字和姓氏。以下代码片段描述了自定义属性编辑器的实现 该编辑器很简单。它扩展了JDK的PropertyEditorSupport类并实现了setAsText)方法。
在该方法中简单地将String分隔成一个以空格作为分隔符的字符串数组。
之后实例化FullName类的一个实例将空格字符之前的字符串作为第一个名称并将空格字符之后的字符串作为姓氏传递。
最后通过调用带结果的setValue()方法返回转换后的值。
如果想要在应用程序中使用NamePropertyEditor,需要在Spring的ApplicationContext中注册该编辑器。
以下配置示例显示了CustomEditorConfigurer和NamePropertyEditor的ApplicationContext配置。 应用程序事件
BeanFactory中不存在的ApplicationContext的另一个功能是通过使用ApplicationContext作为代理发布和接收事件的能力。
使用应用程序事件事件是派生自ApplicationEvent的类而ApplicationEvent类又派生自java.util.EventObject。
任何bean都可以通过实现ApplicationListener接口来监听事件当配置时ApplicationContext会自动注册实现此接口的任何bean作为监听器。
事件是通过使用ApplicationEventPublisher.publishEvent()方法发布的所以发布类必须可以访问ApplicationContext(它扩展了ApplicationEventPublisher接口) Environment和PropertySource抽象
想要设置活动配置文件需要访问Environment接口。
Environment接口是一个抽象层用于封装正在运行的Spring应用程序的环境除配置文件外Environment接口封装的其他关键信息都是属性。
属性用来存储应用程序的底层环境配置例如应用程序文件夹的位置、数据库连接信息等。
Spring中的Environment和PropertySource抽象功能帮助开发人员访问来自运行平台的各种配置信息。
在抽象环境中所有系统属性、环境变量和应用程序属性都由Environment接口提供Spring启动ApplicationContext时将填充该接口。 而对于PropertySource抽象Spring将按照以下默认顺序访问属性
运行JVM的系统属性环境变量应用程序定义的属性
在现实生活中很少需要直接与Environment接口进行交互但会以${}的形式使用一个属性占位符,例如${application.home}),并将解析后的值注入Spring bean中。接下来看一下该过程是如何完成的。假设有一个类用来存储从属性文件加载的所有应用程序属性。以下所示的是AppProperty类