spring建议通过注解配置,替代原xml配置方式。
使用配置类替代xml配置的优势大体:
1,xml配置维护容易出错而且不易检查,java配置类基于java语法检查,对于java程序员更友好,易于维护;
2,注解配置,简洁、清晰,代码都内聚于java代码,理解和代码开发都更连贯;
主要缺点是:
1,结构没有xml文件结构清晰,分散不易于整体阅读;
2,部分注解不易于实现配置,建议仍然采用xml配置实现。
spring采用配置类的注解主要包括:
@ComponentScan 配置用于@Configuration 类的组件扫描路径等。 提供与xml配置上的的元素相同的功能;
@bean 指明方法生成由Spring容器管理的bean。替换xml配置中的bean标签;
spring中和spring容器相关注解包括如下:
@component 指示带注释的类是“组件”。当使用基于注释的配置和类路径扫描时,此类被视为自动检测的候选类,自动装载进入容器。
@Service 指示带注释的类是“服务”,作为独立于模型的接口提供的操作,没有封装状态。行为类组件,应该是无状态bean。(具体概念参见领域驱动设计基本概念中的服务)
@Controller 指示带注释的类是“控制器”,在springMVC中的控制器。也是一个组件。
@Repository 指示带注释的类是“存储库”,封装存储、检索和模拟对象集合的搜索行为的机制。持久层操作。也是一个组件。(概念有别于dao层;具体参见领域驱动设计基本概念中的仓储)(实现传统2ee模式(如“数据访问对象”)的团队也可以将这种原型应用于DAO类,但是在这样做之前,应该注意理解数据访问对象和DDD风格仓储之间的区别。这个注释是一个通用的原型,单个团队可能会适当地缩小它们的语义和使用范围。)
@Conditional 指示组件只有在符合注册条件时才有资格注册。 用于修饰组件、bean等;
@Primary 当有多个组件或者bean有资格被依赖注入时,无法判断注入的bean。如果候选人中存在一个主bean,则该bean将是自动设置的值。 被@Primary修饰的component、bean被作为主bean被spring容器优先采用。
@Lazy 指示是否要延迟初始化bean,如延迟,则在第一次被使用时初始化加载。默认 true。
@scope 当作为类型级注释与@Component一起使用时,@Scope指示要用于带注释类型的实例的作用域范围的名称。 spring容器在不指定scope的情况下默认是单例的。可选:prototype、request、session、global session;
@Import 指示要导入的一个或多个@Configuration类。 允许导入@Configuration类、ImportSelector、ImportBeanDefinitionRegistrar 实现。向spring容器中注册bean。
@PostConstruct 自定义初始化函数,在bean创建后执行;对应有自定义的销毁函数@PreDestroy,在销毁前执行;
组件赋值注解包括如下:
在使用spring的过程中,大多数功能相关组件直接通过配置类或者注解完成往spring容器中注册,整个过程对spring容器的框架对象是无感知的。但是基于某些特殊需求,我们需要使用spring容器的框架对象组件,那么spring提供了基于Aware的方式的扩展,可以获取spring容器的框架对象组件。
Aware 标记超级接口,表示bean有资格通过回调样式的方法由特定框架对象的Spring容器通知。实际可获取的框架对象组件,通过Aware的子接口来决定。 (相对常用如下:)
BeanFactoryAware 获得spring的bean Factory;该接口由希望获取beanFactory的bean实现。
ApplicationContextAware 当前的applicationContext从而调用容器的服务;该接口由希望获取applicationContext的bean实现。(ApplicationContext接口集成了MessageSource接口、ApplicationEventPublisher接口和ResourceLoader接口,因此当Bean实现ApplicationContextAware的时候就可以得到Spring容器的所有服务。)
MessageSourceAware 用于得到message source从而得到文本信息。
ApplicationEventPublisherAware 用于获取ApplicationEventPublisher,用于发布事件;
ResourceLoaderAware 获取ResourceLoader资源加载器,可以获得外部资源文件;
看下面的代码就知道为什么可以通过实现接口,获得对应spring框架对象组件:
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
BeanPostProcessor:spring创建bean时的工厂方法模式中,通过BeanPostProcessor暴露出来两个钩子函数,通过自定义实现该工厂钩子函数,实现需要的扩展,如:检查注解或者用代理包装bean等;
两个具体钩子:
/**
* 在参数中的bean已经实例化,依赖注入等完成后,但是在自定义初始化方法(类似@PostConstruct 修饰的自定义初始化函数)完成初始化之前调用。返回该bean或者被代理包装过的bean;
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* 在参数中的bean已经实例化,依赖注入等完成后,但是在自定义初始化方法(类似@PostConstruct 修饰的自定义初始化函数)完成初始化之后调用。返回该bean或者被代理包装过的bean;
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
BeanPostProcessor有很多子接口,可以通过扩展实现,具体参见BeanPostProcessor的继承实现结构。
具体实现方式、代码,参照:java注解(java Annotation) 注解修饰类生效方式一。
BeanFactoryPostProcessor 允许自定义修改应用程序上下文的bean定义,调整上下文基础bean工厂的bean属性值。
比 BeanPostProcessor更强大,BeanPostProcessor用于bean在实例化后扩展;而BeanFactoryPostProcessor 用于在bean实例化之前读取bean的定义(bean的元数据),并可以修改它,使bean按照新的定义信息初始化bean;
可以按照需要定义很多个BeanFactoryPostProcessor 实例,注册进入spring容器,在spring bean factory初始化完成后,会通知所有processor执行。
BeanFactoryPostProcessor继承结构,有很多实现可以用于扩展,这里只就BeanFactoryPostProcessor和典型子接口BeanDefinitionRegistryPostProcessor进一步描述;
/**
* 允许自定义修改应用程序上下文的bean定义,调整上下文基础bean工厂的bean属性值。
*/
public interface BeanFactoryPostProcessor {
/**
* 在bean factory的标准初始化都装载完成后,在bean实例化之前,允许修改应用程序上下文的内部bean factory。可以重载,也可以添加。
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
/**
* 扩展标准的beanfactorypostProcessor SPI,允许在常规beanfactorypostProcessor检测开始之前注册进一步的bean定义。 (从BeanFactoryPostProcessor继承,可以同时实现两个接口)
*/
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* 修改应用程序上下文的内部bean定义注册表,在其标准的初始化之后。所有常规bean定义都将被加载,但是还没有bean被实例化。这允许在下一个后处理阶段开始之前进一步添加bean定义。在 beanfactorypostProcessor之前。
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
AOP:Aspect Oriented Programming 面向切面编程;采用动态代理,在程序运行期间将切面(某段代码,实现通用性功能)织入到指定方法上运行(含前置、后置、返回、异常、环绕五种通知类型,确定了代码织入到目标方法的方式)。通过这种方式,业务逻辑行为和切面处理逻辑独立开来,业务逻辑行为将注意力集中在具体的业务逻辑上,对切面代码和业务逻辑行为代码进行了良好的解耦。
采用spring AOP机制实现功能扩展的实现例子参见:java注解 注解修饰类生效方式二。
在这里简要解析一下AOP是如何在spring框架中是如何生效,以便更好理解spring AOP(spring AOP的实现比较复杂,这里描述整体实现的骨架):
spring在容器准备好后,创建bean,创建bean的过程如下:
1,创建bean实例;
2,给bean属性赋值,注入等;
3,初始化bean:
3.1,处理Aware接口类型实现的方法回调;
3.2,调用BeanPostProcessor的初始化前置钩子函数;
3.3,调用自定义的初始化方法;(@PostConstruct注解等)
3.4,调用BeanPostProcessor的初始化后置钩子函数;
AnnotationAwareAspectJAutoProxyCreator的钩子函数实现中,完成了如下处理,给目标对象的方法做了代理:
1,获取所有拦截器,找到哪些切面是要通知到当前bean方法的,并排序;
2,将bean保存在advisedBeans中;
3,通过proxyFactory动态创建代理;
4,给容器中返回当前增强了的代理对象;该代理对象会进入spring容器。
目标方法执行时:
1,方法调用时,代理对象执行方法;调用CglibAopProxy.intercept();
2,职责链的方式进入每一个拦截器依次执行切面功能;
3,实际执行就成了:
3.1 前置通知切面依次执行;
3.2 目标对象方法执行;
3.3 后置通知切面依次执行;
3.4 返回通知切面依次执行;
3.5 异常通知切面依次执行。
SPI就是:Service Provider Interface 服务提供者接口。基本含义就是,框架提供接口定义;而不同插件厂商提供具体的实现方案。实际使用时,根据配置,获取实现方案加载、实例化。这样就实现了模块插件的可插拔。SPI在各种框架中是重要的扩展机制,例如常见的dubbo、spring等都提供了自身框架支持的SPI机制。
jdk SPI机制,采用约定的方式,把所有扩展服务实现放在META-INF/services/目录下,读取定义的属性配置文件,获取实际实现类,然后装载、实例化。
在spring提供的SPI机制中,是通过处理器的形式加载,在spring容器准备好,加载了各种自身的processor和外部定义的processor后,refresh方法中,调用了AbstractApplicationContext的refresh方法;如下附代码,在准备好bean工厂,设置好所有beanPostProcessors后,通过invokeBeanFactoryPostProcessors 调用postProcessors;这样就把插件引入了spring。(例如:mybatis插件就是通过BeanDefinitionRegistryPostProcessor引入。)
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 为刷新、设置其启动日期和活动标志;以及初始化property sourse的初始化上下文准备此上下文。
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 配置beanFactory的上下文环境,Classloader、BeanPostProcessor等。
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//设置了postProcessors;
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//执行BeanFactoryPostProcessors,会调用各种BeanFactoryPostProcessor的实现方法;同时会调用 processConfigBeanDefinitions(registry) --> processDeferredImportSelectors;EnableAutoConfiguration注解中import的ImportSelector被调用执行;
invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor.
registerBeanPostProcessors(beanFactory);
// 初始化 message source.
initMessageSource();
// 初始化应用程序事件广播。如果上下文中没有定义任何消息,则使用simpleapplicationEv多发广播
initApplicationEventMulticaster();
// 初始化themesource、内嵌的servlet 容器.
onRefresh();
//将实现了ApplicationListener的bean放入listeners;
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 完成beanFactory的初始化,初始化所有剩余的单例bean。
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成刷新,调用LifecycleProcessor的onRefresh方法;并发布contextrefreshedevent事件;
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
spring还有一些其它扩展方式,比如:基于ApplicationListener进行事件监听扩展等,基本方式类似BeanPostProcessor。基于理解spring启动和容器中各种处理过程后,就可以参照spring中的实现方式,进行自定义的扩展。良好的运用spring注解和扩展能让产品代码优雅且功能强大。