参数能spring 动态调用方法决定调用哪个实现类 spring的

相关文章推荐
在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,从而实现定时任务的动态配置,下面就通过一个简单的示例,来实现这个功能。
一、新建一个springboot工程,并添加依赖
介绍SpringBoot定时任务,利用@Scheduled注解实现,可以通过配置applicationContext.xml来实现单线程定时任务和多线程定时任务。...
通过动态添加JobDetail实例到Scheduler中,实现可配置动态式的定时任务作业
首先实现Job接口的 Myjob,通过包全名(com.example.demo.job.MyJob1),利用...
1. 定时任务实现方式
定时任务实现方式:
Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个...
我的项目是采用的idea+gradle+springboot项目构建,下面是springboot实现定时任务所需要的jar包
//定时任务使用
compile group: 'org.quart...
如下两篇文章结合,可在Spring Boot中实现Quartz动态定时任务,全程无XML配置文件。
Quartz结合Spring Boot源码:
Spring框架自3.0版本起,自带了任务调度功能,好比是一个轻量级的Quartz,而且使用起来也方便、简单,且不需要依赖其他的JAR包。秉承着Spring的一贯风格,Spring任务调度的实现同时支...
以前框架使用quartz框架执行定时调度问题、
老大说这配置太麻烦、每个调度都需要多加在spring的配置中、
能不能减少配置的量从而提高开发效率、
最近看了看spring的 scheduled...
Spring配置文件xmlns加入
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation中加入
使用spring @scheduled注解可以方便的设定定时任务,但是对于定时参数需要变化的情况就会很不方便,如果要实现更改定时参数,就要停止服务,更改参数,重新部署。
对于这种需求, 可以利用Ta...
他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)如何更好地学习dubbo源代码
很荣幸,作为这样一款业界使用率和好评率出众的RPC框架的维护者,今天这个文章主要是想帮助那些热爱开源的同学,更好的来研究dubbo的源代码。
一、Dubbo整体架构
1、Dubbo与Spring的整合
Dubbo在使用上可以做到非常简单,不管是Provider还是Consumer都可以通过Spring的配置文件进行配置,配置完之后,就可以像使用spring bean一样进行服务暴露和调用了,完全看不到dubbo api的存在。
很荣幸,作为这样一款业界使用率和好评率出众的RPC框架的维护者,今天这个文章主要是想帮助那些热爱开源的同学,更好的来研究dubbo的源代码。
一、Dubbo整体架构
1、Dubbo与Spring的整合
Dubbo在使用上可以做到非常简单,不管是Provider还是Consumer都可以通过Spring的配置文件进行配置,配置完之后,就可以像使用spring bean一样进行服务暴露和调用了,完全看不到dubbo api的存在。这是因为dubbo使用了spring提供的可扩展Schema自定义配置支持。在spring配置文件中,可以像、这样进行配置。META-INF下的spring.handlers文件中指定了dubbo的xml解析类:DubboNamespaceHandler。像前面的被解析成ServiceConfig,被解析成ReferenceConfig等等。2、jdk spi扩展
由于Dubbo是开源框架,必须要提供很多的可扩展点。Dubbo是通过扩展jdk spi机制来实现可扩展的。具体来说,就是在META-INF目录下,放置文件名为接口全称,文件中为key、value键值对,value为具体实现类的全类名,key为标志值。由于dubbo使用了url总线的设计,即很多参数通过URL对象来传递,在实际中,具体要用到哪个值,可以通过url中的参数值来指定。
Dubbo对spi的扩展是通过ExtensionLoader来实现的,查看ExtensionLoader的源码,可以看到Dubbo对jdk spi做了三个方面的扩展:
(1)jdk spi仅仅通过接口类名获取所有实现,而ExtensionLoader则通过接口类名和key值获取一个实现;
(2)Adaptive实现,就是生成一个代理类,这样就可以根据实际调用时的一些参数动态决定要调用的类了。
(3)自动包装实现,这种实现的类一般是自动激活的,常用于包装类,比如Protocol的两个实现类:ProtocolFilterWrapper、ProtocolListenerWrapper。3、url总线设计
Dubbo为了使得各层解耦,采用了url总线的设计。我们通常的设计会把层与层之间的交互参数做成Model,这样层与层之间沟通成本比较大,扩展起来也比较麻烦。因此,Dubbo把各层之间的通信都采用url的形式。比如,注册中心启动时,参数的url为:
registry://0.0.0.0:9090?codec=registry&transporter=netty
这就表示当前是注册中心,绑定到所有ip,端口是9090,解析器类型是registry,使用的底层网络通信框架是netty。
二、Dubbo启动过程
Dubbo分为注册中心、服务提供者(provider)、服务消费者(consumer)三个部分。1、注册中心启动过程
注册中心的启动过程,主要看两个类:RegistrySynchronizer、RegistryReceiver,两个类的初始化方法都是start。
RegistrySynchronizer的start方法:
(1)把所有配置信息load到内存;
(2)把当前注册中心信息保存到数据库;
(3)启动5个定时器。
5个定时器的功能是:
(1)AutoRedirectTask,自动重定向定时器。默认1小时运行1次。如果当前注册中心的连接数高于平均值的1.2倍,则将多出来的连接数重定向到其他注册中心上,以达到注册中心集群的连接数均衡。
(2)DirtyCheckTask,脏数据检查定时器。作用是:分别检查缓存provider、数据库provider、缓存consumer、数据库consumer的数据,清除脏数据;清理不存活的provider和consumer数据;对于缓存中的存在的provider或consumer而数据库不存在,重新注册和订阅。
(3)ChangedClearTask,changes变更表的定时清理任务。作用是读取changes表,清除过期数据。
(4)AlivedCheckTask,注册中心存活状态定时检查,会定时更新registries表的expire字段,用以判断注册中心的存活状态。如果有新的注册中心,发送同步消息,将当前所有注册中心的地址通知到所有客户端。
(5)ChangedCheckTask,变更检查定时器。检查changes表的变更,检查类型包括:参数覆盖变更、路由变更、服务消费者变更、权重变更、负载均衡变更。
RegistryReceiver的start方法:启动注册中心服务。默认使用netty框架,绑定本机的9090端口。最后启动服务的过程是在NettyServer来完成的。接收消息时,抛开dubbo协议的解码器,调用类的顺序是
NettyHandler-》NettyServer-》MultiMessageHandler-》HeartbeatHandler-》AllDispatcher-》
DecodeHandler-》HeaderExchangeHandler-》RegistryReceiver-》RegistryValidator-》RegistryFailover-》RegistryExecutor。
2、provider启动过程
provider的启动过程是从ServiceConfig的export方法开始进行的,具体步骤是:
(1)进行本地jvm的暴露,不开放任何端口,以提供injvm这种形式的调用,这种调用只是本地调用,不涉及进程间通信。
(2)调用RegistryProtocol的export。
(3)调用DubboProtocol的export,默认开启20880端口,用以提供接收consumer的远程调用服务。
(4)通过新建RemoteRegistry来建立与注册中心的连接。
(5)将服务地址注册到注册中心。
(6)去注册中心订阅自己的服务。3、consumer启动过程
consumer的启动过程是通过ReferenceConfig的get方法进行的,具体步骤是:
(1)通过新建RemoteRegistry来建立与注册中心的连接。
(2)新建RegistryDirectory并向注册中心订阅服务,RegistryDirectory用以维护注册中心获取的服务相关信息。
(3)创建代理类,发起consumer远程调用时,实际调用的是InvokerInvocationHandler。
三、实际调用过程
consumer端发起调用时,实际调用经过的类是:
1、consumer:
InvokerInvocationHandler-》MockClusterInvoker(如果配置了Mock,则直接调用本地Mock类)-》FailoverClusterInvoker(负载均衡,容错机制,默认在发生错误的情况下,进行两次重试)-》RegistryDirectory$InvokerDelegete-》ConsumerContextFilter-》FutureFilter-&DubboInvoker
2、provider:NettyServer-》MultiMessageHandler-》HeartbeatHandler-》AllDispatcher-》DecodeHandler-》HeaderExchangeHandler-》DubboProtocol.requestHandler-》EchoFilter-》ClassLoaderFilter-》GenericFilter-》ContextFilter-》ExceptionFilter-》TimeoutFilter-》MonitorFilter-》TraceFilter-》实际service。
四、Dubbo使用的设计模式1、工厂模式ServiceConfig中有个字段,代码是这样的:private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Dubbo里有很多这种代码。这也是一种工厂模式,只是实现类的获取采用了jdk spi的机制。这么实现的优点是可扩展性强,想要扩展实现,只需要在classpath下增加个文件就可以了,代码零侵入。另外,像上面的Adaptive实现,可以做到调用时动态决定调用哪个实现,但是由于这种实现采用了动态代理,会造成代码调试比较麻烦,需要分析出实际调用的实现类。2、装饰器模式Dubbo在启动和调用阶段都大量使用了装饰器模式。以Provider提供的调用链为例,具体的调用链代码是在ProtocolFilterWrapper的buildInvokerChain完成的,具体是将注解中含有group=provider的Filter实现,按照order排序,最后的调用顺序是EchoFilter-》ClassLoaderFilter-》GenericFilter-》ContextFilter-》ExceptionFilter-》
TimeoutFilter-》MonitorFilter-》TraceFilter。
更确切地说,这里是装饰器和责任链模式的混合使用。例如,EchoFilter的作用是判断是否是回声测试请求,是的话直接返回内容,这是一种责任链的体现。而像ClassLoaderFilter则只是在主功能上添加了功能,更改当前线程的ClassLoader,这是典型的装饰器模式。3、观察者模式Dubbo的provider启动时,需要与注册中心交互,先注册自己的服务,再订阅自己的服务,订阅时,采用了观察者模式,开启一个listener。注册中心会每5秒定时检查是否有服务更新,如果有更新,向该服务的提供者发送一个notify消息,provider接受到notify消息后,即运行NotifyListener的notify方法,执行监听器方法。4、动态代理模式Dubbo扩展jdk spi的类ExtensionLoader的Adaptive实现是典型的动态代理实现。Dubbo需要灵活地控制实现类,即在调用阶段动态地根据参数决定调用哪个实现类,所以采用先生成代理类的方法,能够做到灵活的调用。生成代理类的代码是ExtensionLoader的createAdaptiveExtensionClassCode方法。代理类的主要逻辑是,获取URL参数中指定参数的值作为获取实现类的key。
本文为云栖社区原创内容,未经允许不得转载,如需转载请发送邮件至yqeditor@list.;如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:yqgroup@ 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
用云栖社区APP,舒服~
【云栖快讯】中办国办印发《推进互联网协议第六版(IPv6)规模部署行动计划》加快推进基于 IPv6 的下一代互联网规模部署,计划指出2025年末中国 IPv6 规模要达到世界第一,阿里云也第一时间宣布了将全面提供IPv6服务,那么在全面部署 IPV6 前,你需要了解都在这儿&&
阿里云函数计算(Function Compute)是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器...
阿里云机器学习是基于阿里云分布式计算引擎的一款机器学习算法平台。用户通过拖拉拽的方式可视化的操作组件来进行试验,...
基于阿里聚安全的核心技术,为移动应用(APP)提供全生命周期的安全服务,其能够准确发现应用的安全漏洞,恶意代码,...
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
社区之星年度评选,投票可抽奖
Loading...详解 Spring 3.0 基于 Annotation 的依赖注入实现
使用 @Repository、@Service、@Controller 和 @Component 将类标识为 Bean
Spring 自 2.0 版本开始,陆续引入了一些注解用于简化 Spring 的开发。@Repository 注解便属于最先引入的一批,它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean。具体只需将该注解标注在 DAO 类上即可。同时,为了让 Spring 能够扫描类路径中的类并识别出 @Repository 注解,需要在 XML 配置文件中启用 Bean 的自动扫描功能,这可以通过 &context:component-scan/& 实现。如下所示: // 首先使用 @Repository 将 DAO 类声明为 Bean
package bookstore.
@Repository
public class UserDaoImpl implements UserDao{ …… }
// 其次,在 XML 配置文件中启动 Spring 的自动扫描功能
&beans … &
&context:component-scan base-package=”bookstore.dao” /&
&/beans&如此,我们就不再需要在 XML 中显式使用 &bean/& 进行 Bean 的配置。Spring 在容器初始化时将自动扫描 base-package 指定的包及其子包下的所有 class 文件,所有标注了 @Repository 的类都将被注册为 Spring Bean。为什么 @Repository 只能标注在 DAO 类上呢?这是因为该注解的作用不只是将类识别为 Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。 Spring 本身提供了一个丰富的并且是与具体的数据访问技术无关的数据访问异常结构,用于封装不同的持久层框架抛出的异常,使得异常独立于底层的框架。Spring 2.5 在 @Repository 的基础上增加了功能类似的额外三个注解:@Component、@Service、@Constroller,它们分别用于软件系统的不同层次:
@Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。
@Service 通常作用在业务层,但是目前该功能与 @Component 相同。
@Constroller 通常作用在控制层,但是目前该功能与 @Component 相同。通过在类上使用 @Repository、@Component、@Service 和 @Constroller 注解,Spring 会自动创建相应的 BeanDefinition 对象,并注册到 ApplicationContext 中。这些类就成了 Spring 受管组件。这三个注解除了作用于不同软件层次的类,其使用方式与 @Repository 是完全相同的。另外,除了上面的四个注解外,用户可以创建自定义的注解,然后在注解上标注 @Component,那么,该自定义注解便具有了与所 @Component 相同的功能。不过这个功能并不常用。当一个 Bean 被自动检测到时,会根据那个扫描器的 BeanNameGenerator 策略生成它的 bean 名称。默认情况下,对于包含 name 属性的 @Component、@Repository、 @Service 和 @Controller,会把 name 取值作为 Bean 的名字。如果这个注解不包含 name 值或是其他被自定义过滤器发现的组件,默认 Bean 名称会是小写开头的非限定类名。如果你不想使用默认 bean 命名策略,可以提供一个自定义的命名策略。首先实现 BeanNameGenerator
接口,确认包含了一个默认的无参数构造方法。然后在配置扫描器时提供一个全限定类名,如下所示: &beans ...&
&context:component-scan
base-package="a.b" name-generator="a.SimpleNameGenerator"/&
与通过 XML 配置的 Spring Bean 一样,通过上述注解标识的 Bean,其默认作用域是"singleton",为了配合这四个注解,在标注 Bean 的同时能够指定 Bean 的作用域,Spring 2.5 引入了 @Scope 注解。使用该注解时只需提供作用域的名称就行了,如下所示: @Scope("prototype")
@Repository
public class Demo { … }如果你想提供一个自定义的作用域解析策略而不使用基于注解的方法,只需实现 ScopeMetadataResolver 接口,确认包含一个默认的没有参数的构造方法。然后在配置扫描器时提供全限定类名: &context:component-scan base-package="a.b"
scope-resolver="footmark.SimpleScopeResolver" /&使用 @PostConstruct 和 @PreDestroy 指定生命周期回调方法
Spring Bean 是受 Spring IoC 容器管理,由容器进行初始化和销毁的(prototype 类型由容器初始化之后便不受容器管理),通常我们不需要关注容器对 Bean 的初始化和销毁操作,由 Spring 经过构造函数或者工厂方法创建的 Bean 就是已经初始化完成并立即可用的。然而在某些情况下,可能需要我们手工做一些额外的初始化或者销毁操作,这通常是针对一些资源的获取和释放操作。Spring 1.x 为此提供了两种方式供用户指定执行生命周期回调的方法。
第一种方式是实现 Spring 提供的两个接口:InitializingBean 和 DisposableBean。如果希望在 Bean 初始化完成之后执行一些自定义操作,则可以让 Bean 实现 InitializingBean 接口,该接口包含一个 afterPropertiesSet() 方法,容器在为该 Bean 设置了属性之后,将自动调用该方法;如果 Bean 实现了 DisposableBean 接口,则容器在销毁该 Bean 之前,将调用该接口的 destroy() 方法。这种方式的缺点是,让 Bean 类实现 Spring 提供的接口,增加了代码与 Spring 框架的耦合度,因此不推荐使用。
第二种方式是在 XML 文件中使用 &bean& 的 init-method 和 destroy-method 属性指定初始化之后和销毁之前的回调方法,代码无需实现任何接口。这两个属性的取值是相应 Bean 类中的初始化和销毁方法,方法名任意,但是方法不能有参数。示例如下: &bean id=”userService”
class=”bookstore.service.UserService”
init-method=”init” destroy-method=”destroy”&
Spring 2.5 在保留以上两种方式的基础上,提供了对 JSR-250 的支持。JSR-250 规范定义了两个用于指定声明周期方法的注解:@PostConstruct 和 @PreDestroy。这两个注解使用非常简单,只需分别将他们标注于初始化之后执行的回调方法或者销毁之前执行的回调方法上。由于使用了注解,因此需要配置相应的 Bean 后处理器,亦即在 XML 中增加如下一行: &context:annotation-config /&比较上述三种指定生命周期回调方法的方式,第一种是不建议使用的,不但其用法不如后两种方式灵活,而且无形中增加了代码与框架的耦合度。后面两种方式开发者可以根据使用习惯选择其中一种,但是最好不要混合使用,以免增加维护的难度。使用 @Required 进行 Bean 的依赖检查
依赖检查的作用是,判断给定 Bean 的相应 Setter 方法是否都在实例化的时候被调用了。而不是判断字段是否已经存在值了。Spring 进行依赖检查时,只会判断属性是否使用了 Setter 注入。如果某个属性没有使用 Setter 注入,即使是通过构造函数已经为该属性注入了值,Spring 仍然认为它没有执行注入,从而抛出异常。另外,Spring 只管是否通过 Setter 执行了注入,而对注入的值却没有任何要求,即使注入的 &null/&,Spring 也认为是执行了依赖注入。
&bean& 标签提供了 dependency-check 属性用于进行依赖检查。该属性的取值包括以下几种:
none -- 默认不执行依赖检查。可以在 &beans& 标签上使用 default-dependency-check 属性改变默认值。
simple -- 对原始基本类型和集合类型进行检查。
objects -- 对复杂类型进行检查(除了 simple 所检查类型之外的其他类型)。
all -- 对所有类型进行检查。旧版本使用 dependency-check 在配置文件中设置,缺点是粒度较粗。使用 Spring2.0 提供的 @Required 注解,提供了更细粒度的控制。@Required 注解只能标注在 Setter 方法之上。因为依赖注入的本质是检查 Setter 方法是否被调用了,而不是真的去检查属性是否赋值了以及赋了什么样的值。如果将该注解标注在非 setXxxx() 类型的方法则被忽略。为了让 Spring 能够处理该注解,需要激活相应的 Bean 后处理器。要激活该后处理器,只需在 XML 中增加如下一行即可。 &context:annotation-config/&
当某个被标注了 @Required 的 Setter 方法没有被调用,则 Spring 在解析的时候会抛出异常,以提醒开发者对相应属性进行设置。使用 @Resource、@Autowired 和 @Qualifier 指定 Bean 的自动装配策略自动装配是指,Spring 在装配 Bean 的时候,根据指定的自动装配规则,将某个 Bean 所需要引用类型的 Bean 注入进来。&bean& 元素提供了一个指定自动装配类型的 autowire 属性,该属性有如下选项:
no -- 显式指定不使用自动装配。
byName -- 如果存在一个和当前属性名字一致的 Bean,则使用该 Bean 进行注入。如果名称匹配但是类型不匹配,则抛出异常。如果没有匹配的类型,则什么也不做。
byType -- 如果存在一个和当前属性类型一致的 Bean ( 相同类型或者子类型 ),则使用该 Bean 进行注入。byType 能够识别工厂方法,即能够识别 factory-method 的返回类型。如果存在多个类型一致的 Bean,则抛出异常。如果没有匹配的类型,则什么也不做。
constructor -- 与 byType 类似,只不过它是针对构造函数注入而言的。如果当前没有与构造函数的参数类型匹配的 Bean,则抛出异常。使用该种装配模式时,优先匹配参数最多的构造函数。
autodetect -- 根据 Bean 的自省机制决定采用 byType 还是 constructor 进行自动装配。如果 Bean 提供了默认的构造函数,则采用 byType;否则采用 constructor 进行自动装配。当使用 byType 或者 constructor 类型的自动装配的时候,自动装配也支持引用类型的数组或者使用了泛型的集合,这样,Spring 就会检查容器中所有类型匹配的 Bean,组成集合或者数组后执行注入。对于使用了泛型的 Map 类型,如果键是 String 类型,则 Spring 也会自动执行装配,将所有类型匹配的 Bean 作为值,Bean 的名字作为键。
我们可以给 &beans& 增加 default-autowire 属性,设置默认的自动封装策略。默认值为"no"。如果使用自动装配的同时,也指定了 property 或者 constructor-arg 标签,则显式指定的值将覆盖自动装配的值。目前的自动封装不支持简单类型,比如基本类型、String、Class,以及它们的数组类型。
在按类型匹配的时候 ( 可能是 byType、constructor、autodetect),同一个类型可能存在多个 Bean,如果被注入的属性是数组、集合或者 Map,这可能没有问题,但是如果只是简单的引用类型,则会抛出异常。解决方法有如下几种:
取消该 Bean 的自动装配特性,使用显式的注入。我们可能不希望某个 Bean 被当作其他 Bean 执行自动封装时的候选对象,我们可以给该 &bean& 增加 autowire-candidate="false"。(autowire-candidate 属性和 autowire 属性相互独立,互不相干。某个 Bean 可以将 autowire-candidate 设置为 false,同时使用 autowire 特性。) 另外,我们可以设置 &beans& 的 default-autowire-candidates 属性,可以在该属性中指定可以用于自动装配候选 Bean 的匹配模式,比如 default-autowire-candidates="*serv,*dao",这表示所有名字以 serv 或者 dao 结尾的 Bean 被列为候选,其他则忽略,相当于其他 Bean 都指定为 autowire-candidate="false",此时可以显式为 &bean& 指定 autowire-candidate="true"。在 &bean& 上指定的设置要覆盖 &beans& 上指定的设置。
如果在多个类型相同的 Bean 中有首选的 Bean,那么可以将该 &bean& 的 primary 属性设置为 "true" ,这样自动装配时便优先使用该 Bean 进行装配。此时不能将 autowire-candidate 设为 false。
如果使用的是 Java 5 以上版本,可以使用注解进行更细粒度的控制。使用 @Autowired 和 @Qualifier 注解执行自动装配使用 @Autowired 注解进行装配,只能是根据类型进行匹配。@Autowired 注解可以用于 Setter 方法、构造函数、字段,甚至普通方法,前提是方法必须有至少一个参数。@Autowired 可以用于数组和使用泛型的集合类型。然后 Spring 会将容器中所有类型符合的 Bean 注入进来。@Autowired 标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。@Autowired 标注作用于普通方法时,会产生一个副作用,就是在容器初始化该 Bean 实例的时候就会调用该方法。当然,前提是执行了自动装配,对于不满足装配条件的情况,该方法也不会被执行。当标注了 @Autowired 后,自动注入不能满足,则会抛出异常。我们可以给 @Autowired 标注增加一个 required=false 属性,以改变这个行为。另外,每一个类中只能有一个构造函数的 @Autowired.required() 属性为 true。否则就出问题了。如果用 @Autowired 同时标注了多个构造函数,那么,Spring 将采用贪心算法匹配构造函数 ( 构造函数最长 )。@Autowired 还有一个作用就是,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。当容器中存在多个 Bean 的类型与需要注入的相同时,注入将不能执行,我们可以给 @Autowired 增加一个候选值,做法是在 @Autowired 后面增加一个 @Qualifier 标注,提供一个 String 类型的值作为候选的 Bean 的名字。举例如下: @Autowired(required=false)
@Qualifier("ppp")
public void setPerson(person p){}
@Qualifier 甚至可以作用于方法的参数 ( 对于方法只有一个参数的情况,我们可以将 @Qualifer 标注放置在方法声明上面,但是推荐放置在参数前面 ),举例如下: @Autowired(required=false)
public void sayHello(@Qualifier("ppp")Person p,String name){}
我们可以在配置文件中指定某个 Bean 的 qualifier 名字,方法如下: &bean id="person" class="footmark.spring.Person"&
&qualifier value="ppp"/&
如果没有明确指定 Bean 的 qualifier 名字,那么默认名字就是 Bean 的名字。通常,qualifier 应该是有业务含义的,例如 "domain","persistent" 等,而不应该是类似 "person" 方式。
我们还可以将 @Qualifier 标注在集合类型上,那么所有 qualifier 名字与指定值相同的 Bean 都将被注入进来。
最后,配置文件中需要指定每一个自定义注解的属性值。我们可以使用 &meta& 标签来代替 &qualifier/& 标签,如果 &meta& 标签和 &qualifier/& 标签同时出现,那么优先使用 &qualifier& 标签。如果没有 &qualifier& 标签,那么会用 &meta& 提供的键值对来封装 &qualifier& 标签。示例如下: &bean class="footmark.HelloWorld"&
&qualifier type="MovieQualifier"&
&attribute key="format" value="VHS"/&
&attribute key="genre" value="Comedy"/&
&/qualifier&
&bean class="footmark.HelloWorld"&
&meta key="format" value="DVD"/&
&meta key="genre" value="Action"/&
@Autowired 注解对应的后处理注册与前面相似,只需在配置文件中增加如下一行即可: &context:annotation-config/&如果 @Autowired 注入的是 BeanFactory、ApplicationContext、ResourceLoader 等系统类型,那么则不需要 @Qualifier,此时即使提供了 @Qualifier 注解,也将会被忽略;而对于自定义类型的自动装配,如果使用了 @Qualifier 注解并且没有名字与之匹配的 Bean,则自动装配匹配失败。使用 JSR-250 中的 @Resource 和 @Qualifier 注解
如果希望根据 name 执行自动装配,那么应该使用 JSR-250 提供的 @Resource 注解,而不应该使用 @Autowired 与 @Qualifier 的组合。
@Resource 使用 byName 的方式执行自动封装。@Resource 标注可以作用于带一个参数的 Setter 方法、字段,以及带一个参数的普通方法上。@Resource 注解有一个 name 属性,用于指定 Bean 在配置文件中对应的名字。如果没有指定 name 属性,那么默认值就是字段或者属性的名字。@Resource 和 @Qualifier 的配合虽然仍然成立,但是 @Qualifier 对于 @Resource 而言,几乎与 name 属性等效。
如果 @Resource 没有指定 name 属性,那么使用 byName 匹配失败后,会退而使用 byType 继续匹配,如果再失败,则抛出异常。在没有为 @Resource 注解显式指定 name 属性的前提下,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。此时 name 属性不需要指定 ( 或者指定为""),否则注入失败;如果使用了 @Qualifier,则该注解将被忽略。而对于用户自定义类型的注入,@Qualifier 和 name 等价,并且不被忽略。&bean& 的 primary 和 autowire-candidate 属性对 @Resource、@Autowired 仍然有效。使用 @Configuration 和 @Bean 进行 Bean 的声明
虽然 2.0 版本发布以来,Spring 陆续提供了十多个注解,但是提供的这些注解只是为了在某些情况下简化 XML 的配置,并非要取代 XML 配置方式。这一点可以从 Spring IoC 容器的初始化类可以看出:ApplicationContext 接口的最常用的实现类是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,以及面向 Portlet 的 XmlPortletApplicationContext 和面向 web 的 XmlWebApplicationContext,它们都是面向 XML 的。Spring 3.0 新增了另外两个实现类:AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext。从名字便可以看出,它们是为注解而生,直接依赖于注解作为容器配置信息来源的 IoC 容器初始化类。由于 AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 web 版本,其用法与后者相比几乎没有什么差别,因此本文将以 AnnotationConfigApplicationContext 为例进行讲解。AnnotationConfigApplicationContext 搭配上 @Configuration 和 @Bean 注解,自此,XML 配置方式不再是 Spring IoC 容器的唯一配置方式。两者在一定范围内存在着竞争的关系,但是它们在大多数情况下还是相互协作的关系,两者的结合使得 Spring IoC 容器的配置更简单,更强大。之前,我们将配置信息集中写在 XML 中,如今使用注解,配置信息的载体由 XML 文件转移到了 Java 类中。我们通常将用于存放配置信息的类的类名以 “Config” 结尾,比如 AppDaoConfig.java、AppServiceConfig.java 等等。我们需要在用于指定配置信息的类上加上 @Configuration 注解,以明确指出该类是 Bean 配置的信息源。并且 Spring 对标注 Configuration 的类有如下要求:
配置类不能是 final 的;
配置类不能是本地化的,亦即不能将配置类定义在其他类的方法内部;
配置类必须有一个无参构造函数。AnnotationConfigApplicationContext 将配置类中标注了 @Bean 的方法的返回值识别为 Spring Bean,并注册到容器中,受 IoC 容器管理。@Bean 的作用等价于 XML 配置中的 &bean/& 标签。示例如下: @Configuration
public class BookStoreDaoConfig{
public UserDao userDao(){ return new UserDaoImpl();}
public BookDao bookDao(){return new BookDaoImpl();}
Spring 在解析到以上文件时,将识别出标注 @Bean 的所有方法,执行之,并将方法的返回值 ( 这里是 UserDaoImpl 和 BookDaoImpl 对象 ) 注册到 IoC 容器中。默认情况下,Bean 的名字即为方法名。因此,与以上配置等价的 XML 配置如下: &bean id=”userDao” class=”bookstore.dao.UserDaoImpl”/&
&bean id=”bookDao” class=”bookstore.dao.BookDaoImpl”/&
@Bean 具有以下四个属性:
name -- 指定一个或者多个 Bean 的名字。这等价于 XML 配置中 &bean& 的 name 属性。
initMethod -- 容器在初始化完 Bean 之后,会调用该属性指定的方法。这等价于 XML 配置中 &bean& 的 init-method 属性。
destroyMethod -- 该属性与 initMethod 功能相似,在容器销毁 Bean 之前,会调用该属性指定的方法。这等价于 XML 配置中 &bean& 的 destroy-method 属性。
autowire -- 指定 Bean 属性的自动装配策略,取值是 Autowire 类型的三个静态属性。Autowire.BY_NAME,Autowire.BY_TYPE,Autowire.NO。与 XML 配置中的 autowire 属性的取值相比,这里少了 constructor,这是因为 constructor 在这里已经没有意义了。@Bean 没有直接提供指定作用域的属性,可以通过 @Scope 来实现该功能,关于 @Scope 的用法已在上文列举。
下面讲解基于注解的容器初始化。AnnotationConfigApplicationContext 提供了三个构造函数用于初始化容器。
AnnotationConfigApplicationContext():该构造函数初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其 register() 方法注册配置类,并调用 refresh() 方法刷新容器。
AnnotationConfigApplicationContext(Class&?&... annotatedClasses):这是最常用的构造函数,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的 Bean 自动注册到容器中。
AnnotationConfigApplicationContext(String... basePackages):该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的 Spring Bean,将其注册到容器中。它不但识别标注 @Configuration 的配置类并正确解析,而且同样能识别使用 @Repository、@Service、@Controller、@Component 标注的类。除了使用上面第三种类型的构造函数让容器自动扫描 Bean 的配置信息以外,AnnotationConfigApplicationContext 还提供了 scan() 方法,其功能与上面也类似,该方法主要用在容器初始化之后动态增加 Bean 至容器中。调用了该方法以后,通常需要立即手动调用 refresh() 刷新容器,以让变更立即生效。
需要注意的是,AnnotationConfigApplicationContext 在解析配置类时,会将配置类自身注册为一个 Bean,因为 @Configuration 注解本身定义时被 @Component 标注了。因此可以说,一个 @Configuration 同时也是一个 @Component。大多数情况下,开发者用不到该 Bean,并且在理想情况下,该 Bean 应该是对开发者透明的。@Configuration 的定义如下所示: @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
在一般的项目中,为了结构清晰,通常会根据软件的模块或者结构定义多个 XML 配置文件,然后再定义一个入口的配置文件,该文件使用 &import/& 将其他的配置文件组织起来。最后只需将该文件传给 ClassPathXmlApplicationContext 的构造函数即可。针对基于注解的配置,Spring 也提供了类似的功能,只需定义一个入口配置类,并在该类上使用 @Import 注解引入其他的配置类即可,最后只需要将该入口类传递给 AnnotationConfigApplicationContext。具体示例如下: @Configuration
@Import({BookStoreServiceConfig.class,BookStoreDaoConfig.class})
public class BookStoreConfig{ … }混合使用 XML 与注解进行 Bean 的配置
设计 @Configuration 和 @Bean 的初衷,并不是为了完全取代 XML,而是为了在 XML 之外多一种可行的选择。由于 Spring 自发布以来,Spring 开发小组便不断简化 XML 配置,使得 XML 配置方式已经非常成熟,加上 Spring 2.0 以后出现了一系列命名空间的支持,使得 XML 配置方式成为了使用简单、功能强大的 Bean 定义方式。而且,XML 配置的一些高级功能目前还没有相关注解能够直接支持。因此,在目前的多数项目中,要么使用纯粹的 XML 配置方式进行 Bean 的配置,要么使用以注解为主,XML 为辅的配置方式进行 Bean 的配置。
之所以会出现两者共存的情况,主要归结为三个原因:其一,目前绝大多数采用 Spring 进行开发的项目,几乎都是基于 XML 配置方式的,Spring 在引入注解的同时,必须保证注解能够与 XML 和谐共存,这是前提;其二,由于注解引入较晚,因此功能也没有发展多年的 XML 强大,因此,对于复杂的配置,注解还很难独当一面,在一段时间内仍然需要 XML 的配合才能解决问题。除此之外,Spring 的 Bean 的配置方式与 Spring 核心模块之间是解耦的,因此,改变配置方式对 Spring 的框架自身是透明的。Spring 可以通过使用 Bean 后处理器 (BeanPostProcessor) 非常方便的增加对于注解的支持。这在技术实现上非常容易的事情。
要使用混合配置方式,首先需要判断以哪一种配置方式为主。对这个问题的不同回答将会直接影响到实现的方式。然而大可不必为此伤脑筋,因为不论是以 XML 为主,还是以注解为主,配置方式都是简单而且容易理解的。这里不存在错误的决定,因为仅仅是表现方式不一样。我们首先假设以 XML 配置为主的情况。
对于已经存在的大型项目,可能初期是以 XML 进行 Bean 配置的,后续逐渐加入了注解的支持,这时我们只需在 XML 配置文件中将被 @Configuration 标注的类定义为普通的 &bean&,同时注册处理注解的 Bean 后处理器即可。示例如下: // 假设存在如下的 @Configuration 类:
package bookstore.
import bookstore.dao.*;
@Configuration
public class MyConfig{
public UserDao userDao(){
return new UserDaoImpl();
此时,只需在 XML 中作如下声明即可:
&beans … &
&context:annotation-config /&
&bean class=”demo.config.MyConfig”/&
&/beans&由于启用了针对注解的 Bean 后处理器,因此在 ApplicationContext 解析到 MyConfig 类时,会发现该类标注了 @Configuration 注解,随后便会处理该类中标注 @Bean 的方法,将这些方法的返回值注册为容器总的 Bean。对于以上的方式,如果存在多个标注了 @Configuration 的类,则需要在 XML 文件中逐一列出。另一种方式是使用前面提到的自动扫描功能,配置如下: &context:component-scan base-package=”bookstore.config” /&
如此,Spring 将扫描所有 demo.config 包及其子包中的类,识别所有标记了 @Component、@Controller、@Service、@Repository 注解的类,由于 @Configuration 注解本身也用 @Component 标注了,Spring 将能够识别出 @Configuration 标注类并正确解析之。
对于以注解为中心的配置方式,只需使用 @ImportResource 注解引入存在的 XML 即可,如下所示: @Configuration
@ImportResource(“classpath:/bookstore/config/spring-beans.xml”)
public class MyConfig{
// 容器的初始化过程和纯粹的以配置为中心的方式一致:
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(MyConfig.class);
……结束语从 2.0 版本开始,Spring 的每一次更新都会提供更多新的注解供开发者使用。这满足了注解爱好者的胃口。但是正如前面所说,Spring 提供更多的注解并不是为了有朝一日取代 XML 配置方式,而是为了给开发者多一种选择。两种声明 Bean 的方式各有特色,XML 方式更加灵活,并且发展的相对成熟,这种配置方式为大多数 Spring 开发者熟悉;注解方式使用起来非常简洁,但是尚处于发展阶段。我们很难说两种配置方式孰优孰劣,但是如果能够灵活搭配两种方式,一定能够进一步提升开发效率。
相关主题:提供了对 Spring 3.0 的全方位讲解,是学习 Spring 3.0 的最佳资料。:详细讲述了 Spring 2.5 基于注释 IoC 功能的使用。:在这里可以和来自世界各地的 Spring 爱好者交流 Spring 技术。访问 developerWorks 获得丰富的 how-to 信息、工具和项目更新以及 ,帮助您用开放源码技术进行开发,并将它们与 IBM 产品结合使用。
添加或订阅评论,请先或。
有新评论时提醒我
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Open sourceArticleID=487709ArticleTitle=详解 Spring 3.0 基于 Annotation 的依赖注入实现publish-date=

我要回帖

更多关于 spring 动态调用bean 的文章

 

随机推荐