目录搜索
前言简介概览使用场景Spring 2.0和 2.5的新特性简介控制反转(IoC)容器新的bean作用域更简单的XML配置可扩展的XML编写Annotation(注解)驱动配置在classpath中自动搜索组件面向切面编程(AOP)更加简单的AOP XML配置对@AspectJ 切面的支持对bean命名pointcut( bean name pointcut element)的支持对AspectJ装载时织入(AspectJ load-time weaving)的支持中间层在XML里更为简单的声明性事务配置对Websphere 事务管理的完整支持JPA异步的JMSJDBCWeb层Spring MVC合理的默认值Portlet 框架基于Annotation的控制器Spring MVC的表单标签库对Tiles 2 支持对JSF 1.2支持JAX-WS支持其他动态语言支持增强的测试支持JMX 支持将Spring 应用程序上下文部署为JCA adapter计划任务对Java 5 (Tiger) 支持移植到Spring 2.5改变支持的JDK版本Spring 2.5的Jar打包XML配置Deprecated(淘汰)的类和方法Apache OJBiBATISHibernateJDOUrlFilenameViewController更新的样例应用改进的文档核心技术IoC(控制反转)容器简介基本原理 - 容器和bean容器配置元数据实例化容器XML配置元数据的结构多种beanbean的命名bean的别名实例化bean用构造器来实例化使用静态工厂方法实例化使用实例工厂方法实例化使用容器依赖注入依赖构造器注入构造器参数解析构造器参数类型匹配构造参数索引Setter注入一些例子依赖配置详解直接变量(基本类型、Strings类型等。)idref元素引用其它的bean(协作者)内部bean集合集合的合并强类型集合(仅适用于Java5+)NullsXML配置文件的简写及其他XML-based configuration metadata shortcuts使用p名称空间配置属性组合属性名称使用depends-on延迟初始化bean自动装配(autowire)协作者将bean排除在自动装配之外依赖检查方法注入Lookup方法注入自定义方法的替代方案Bean的作用域Singleton作用域Prototype作用域Singleton beans和prototype-bean的依赖其他作用域初始化web配置Request作用域Session作用域global session作用域作用域bean与依赖选择创建代理的类型自定义作用域创建自定义作用域使用自定义作用域定制bean特性生命周期回调初始化回调析构回调缺省的初始化和析构方法组合生命周期机制在非web应用中优雅地关闭Spring IoC容器了解自己BeanFactoryAwareBeanNameAwarebean定义的继承容器扩展点用BeanPostProcessor定制bean使用BeanPostProcessor的Hello World示例RequiredAnnotationBeanPostProcessor示例用BeanFactoryPostProcessor定制配置元数据PropertyPlaceholderConfigurer示例PropertyOverrideConfigurer示例使用FactoryBean定制实例化逻辑The ApplicationContextBeanFactory 还是 ApplicationContext?利用MessageSource实现国际化事件底层资源的访问ApplicationContext在WEB应用中的实例化粘合代码和可怕的singleton以J2EE RAR文件的形式部署Spring ApplicationContext基于注解(Annotation-based)的配置@Autowired基于注解的自动连接微调CustomAutowireConfigurer@Resource@PostConstruct 与 @PreDestroy对受管组件的Classpath扫描@Component和更多典型化注解自动检测组件使用过滤器自定义扫描自动检测组件的命名为自动检测的组件提供一个作用域用注解提供限定符元数据注册一个LoadTimeWeaver资源简介Resource接口内置 Resource 实现UrlResourceClassPathResourceFileSystemResourceServletContextResourceInputStreamResourceByteArrayResourceResourceLoader接口ResourceLoaderAware 接口把Resource作为属性来配置Application context 和Resource 路径构造application context创建 ClassPathXmlApplicationContext 实例 - 简介Application context构造器中资源路径的通配符Ant风格的pattern潜在的可移植性classpath*: 前缀其他关于通配符的说明FileSystemResource 说明校验,数据绑定,BeanWrapper,与属性编辑器简介使用Spring的Validator接口进行校验从错误代码到错误信息Bean处理和BeanWrapper设置和获取属性值以及嵌套属性内建的PropertyEditor实现注册用户自定义的PropertyEditor使用PropertyEditorRegistrars使用Spring进行面向切面编程(AOP)简介AOP概念Spring AOP的功能和目标AOP代理@AspectJ支持启用@AspectJ支持声明一个切面声明一个切入点(pointcut)切入点指示符(PCD)的支持组合切入点表达式共享通用切入点定义示例声明通知前置通知后置通知(After returning advice)异常通知(After throwing advice)最终通知(After (finally) advice)环绕通知通知参数(Advice parameters)访问当前的连接点传递参数给通知确定参数名处理参数通知顺序引入(Introduction)切面实例化模型例子基于Schema的AOP支持声明一个切面声明一个切入点声明通知前置通知后置通知异常通知最终通知环绕通知通知参数通知顺序引入切面实例化模型Advisor例子AOP声明风格的选择Spring AOP还是完全用AspectJ?Spring AOP中使用@AspectJ还是XML?混合切面类型代理机制理解AOP代理以编程方式创建@AspectJ代理在Spring应用中使用AspectJ在Spring中使用AspectJ进行domain object的依赖注入@Configurable对象的单元测试Working with multiple application contextsSpring中其他的AspectJ切面使用Spring IoC来配置AspectJ的切面在Spring应用中使用AspectJ加载时织入(LTW)第一个例子切面'META-INF/aop.xml'相关类库(JARS)Spring配置特定环境的配置通用Java应用TomcatWebLogic更多资源Spring AOP APIs简介Spring中的切入点API概念切入点运算AspectJ切入点表达式便利的切入点实现静态切入点正则表达式切入点属性驱动的切入点动态切入点控制流切入点切入点的超类自定义切入点Spring的通知API通知的生命周期Spring里的通知类型拦截环绕通知前置通知异常通知后置通知引入通知Spring里的Advisor API使用ProxyFactoryBean创建AOP代理基础JavaBean属性基于JDK和CGLIB的代理对接口进行代理对类进行代理使用“全局”通知器简化代理定义使用ProxyFactory通过编程创建AOP代理操作被通知对象使用“自动代理(autoproxy)”功能自动代理bean定义BeanNameAutoProxyCreatorDefaultAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator使用元数据驱动的自动代理使用TargetSource热交换目标源池化目标源原型目标源ThreadLocal目标源定义新的Advice类型更多资源测试简介单元测试Mock对象JNDIServlet APIPortlet API单元测试支持类通用工具类Spring MVC集成测试概览使用哪个支持框架通用目标上下文管理及缓存测试fixtures依赖注入事务管理集成测试支持类JDBC测试支持常用注解JUnit 3.8遗留支持上下文管理及缓存测试fixture依赖注入字段级别(Field Level)注入事务管理JUnit 3.8 遗留支持类Java 5+ 专有支持使用注解的事务相关测试JPA支持类Spring TestContext Framework主要的抽象上下文管理和缓存测试fixture的依赖注入事务管理TestContext支持类JUnit 3.8支持类JUnit 4.4支持类定制JUnit 4.4运行器TestNG支持类TestContext框架注解支持PetClinic示例更多资源中间层数据访问事务管理简介动机关键抽象使用资源同步的事务高层次方案低层次方案TransactionAwareDataSourceProxy声明式事务管理理解Spring的声明式事务管理实现第一个例子回滚为不同的bean配置不同的事务语义<tx:advice/> 有关的设置使用 @Transactional@Transactional 有关的设置事务传播requiredRequiresNewNested通知事务操作结合AspectJ使用 @Transactional编程式事务管理使用TransactionTemplate指定事务设置使用PlatformTransactionManager选择编程式事务管理还是声明式事务管理与特定应用服务器集成IBM WebSphereBEA WebLogicOracle OC4J常见问题的解决方法对一个特定的 DataSource 使用了错误的事务管理器更多的资源DAO支持简介一致的异常层次一致的DAO支持抽象类使用JDBC进行数据访问简介选择一种工作模式Spring JDBC包结构利用JDBC核心类控制JDBC的基本操作和错误处理JdbcTemplate类一些示例查询(SELECT)更新(INSERT/UPDATE/DELETE)其他操作JdbcTemplate 的最佳实践NamedParameterJdbcTemplate类SimpleJdbcTemplate类DataSource接口SQLExceptionTranslator接口执行SQL语句执行查询更新数据库获取自动生成的主键控制数据库连接DataSourceUtils类SmartDataSource接口AbstractDataSource类SingleConnectionDataSource类DriverManagerDataSource类TransactionAwareDataSourceProxy类DataSourceTransactionManager类NativeJdbcExtractorJDBC批量操作使用JdbcTemplate进行批量操作使用SimpleJdbcTemplate进行批量操作通过使用SimpleJdbc类简化JDBC操作使用SimpleJdbcInsert插入数据使用SimpleJdbcInsert来获取自动生成的主键指定SimpleJdbcInsert所使用的字段使用SqlParameterSource提供参数值使用SimpleJdbcCall调用存储过程声明SimpleJdbcCall使用的参数如何定义SqlParameters使用SimpleJdbcCall调用内置函数使用SimpleJdbcCall返回的ResultSet/REF Cursor用Java对象来表达JDBC操作SqlQuery类MappingSqlQuery类SqlUpdate类StoredProcedure类SqlFunction类参数和数据处理的基本原则为参数设置SQL类型信息处理BLOB 和 CLOB对象在IN语句中传入一组参数值处理复杂类型的存储过程调用使用ORM工具进行数据访问简介Hibernate资源管理在Spring容器中创建 SessionFactoryThe HibernateTemplate不使用回调的基于Spring的DAO实现基于Hibernate3的原生API实现DAO编程式的事务划分声明式的事务划分事务管理策略容器资源 vs 本地资源在应用服务器中使用Hibernate的注意事项JDO建立PersistenceManagerFactoryJdoTemplate和JdoDaoSupport基于原生的JDO API实现DAO事务管理JdoDialectOracle TopLinkSessionFactory 抽象层TopLinkTemplate and TopLinkDaoSupport基于原生的TopLink API的DAO实现事务管理iBATIS SQL Maps创建SqlMapClient使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport基于原生的iBATIS API的DAO实现JPA在Spring环境中建立JPALocalEntityManagerFactoryBean从JNDI中获取 EntityManagerFactoryLocalContainerEntityManagerFactoryBeanTomcat(5.0以上)加载时的织入配置使用VM代理的全局加载时织入上下文范围内的加载时织入配置处理多持久化单元JpaTemplate 和 JpaDaoSupport基于原生的JPA实现DAO异常转化事务管理JpaDialectThe WebWeb MVC framework Web框架概述与其他MVC实现框架的集成Spring Web MVC框架的特点DispatcherServlet控制器AbstractController 和 WebContentGenerator其它的简单控制器MultiActionController命令控制器处理器映射(handler mapping)BeanNameUrlHandlerMappingSimpleUrlHandlerMapping拦截器(HandlerInterceptor)视图与视图解析视图解析器(ViewResolver)视图解析链重定向(Rediret)到另一个视图RedirectViewredirect:前缀forward:前缀本地化解析器AcceptHeaderLocaleResolverCookieLocaleResolverSessionLocaleResolverLocaleChangeInterceptor使用主题简介如何定义主题主题解析器Spring对分段文件上传(multipart file upload)的支持介绍使用MultipartResolver在表单中处理分段文件上传使用Spring的表单标签库配置form标签input标签checkbox标签checkboxes标签radiobutton标签radiobuttons标签password标签select标签option标签options标签textarea标签hidden标签errors标签处理异常惯例优先原则(convention over configuration)对控制器的支持:ControllerClassNameHandlerMapping对模型的支持:ModelMap(ModelAndView)对视图的支持:RequestToViewNameTranslator基于注解的控制器配置建立dispatcher实现注解支持使用@Controller定义一个控制器使用@RequestMapping映射请求使用@RequestParam绑定请求参数到方法参数使用@ModelAttribute提供一个从模型到数据的链接使用@SessionAttributes指定存储在会话中的属性自定义WebDataBinder初始化使用@InitBinder自定义数据绑定配置一个定制的WebBindingInitializer更多资源集成视图技术简介JSP和JSTL视图解析器'Plain-old' JSPs versus JSTL 'Plain-old' JSP与JSTL帮助简化开发的额外的标签Tiles需要的资源如何集成TilesUrlBasedViewResolver类ResourceBundleViewResolver类SimpleSpringPreparerFactory 和 SpringBeanPreparerFactoryVelocity和FreeMarker需要的资源Context 配置创建模板高级配置velocity.propertiesFreeMarker绑定支持和表单处理用于绑定的宏简单绑定表单输入生成宏输入域选择输入域重载HTML转码行为并使你的标签符合XHTMLXSLT写在段首Bean 定义标准MVC控制器代码把模型数据转化为XML定义视图属性文档转换小结文档视图(PDF/Excel)简介配置和安装文档视图定义Controller 代码Excel视图子类PDF视图子类JasperReports依赖的资源配置配置ViewResolver配置View关于报表文件使用 JasperReportsMultiFormatView构造ModelAndView使用子报表配置子报表文件配置子报表数据源配置Exporter的参数集成其它Web框架简介通用配置JavaServer FacesDelegatingVariableResolverFacesContextUtilsStrutsContextLoaderPluginDelegatingRequestProcessorDelegatingActionProxyActionSupport ClassesTapestry注入 Spring 托管的 beans将 Spring Beans 注入到 Tapestry 页面中组件定义文件添加抽象访问方法将 Spring Beans 注入到 Tapestry 页面中 - Tapestry 4.0+ 风格WebWork更多资源Portlet MVC框架介绍控制器 - MVC中的C视图 - MVC中的VWeb作用范围的BeanDispatcherPortletViewRendererServlet控制器AbstractController 和 PortletContentGenerator其它简单的控制器Command控制器PortletWrappingController处理器映射PortletModeHandlerMappingParameterHandlerMappingPortletModeParameterHandlerMapping增加 HandlerInterceptorsHandlerInterceptorAdapterParameterMappingInterceptor视图和它们的解析Multipart文件上传支持使用 PortletMultipartResolver处理表单里的文件上传异常处理Portlet应用的部署整合使用Spring进行远程访问与Web服务简介使用RMI暴露服务使用RmiServiceExporter暴露服务在客户端链接服务使用Hessian或者Burlap通过HTTP远程调用服务为Hessian和co.配置DispatcherServlet使用HessianServiceExporter暴露你的bean在客户端连接服务使用Burlap对通过Hessian或Burlap暴露的服务使用HTTP Basic认证使用HTTP调用器暴露服务Exposing the service object在客户端连接服务Web Services使用JAX-RPC暴露基于servlet的web服务使用JAX-RPC访问web服务注册JAX-RPC Bean映射注册自己的JAX-RPC 处理器使用JAX-WS暴露基于servlet的web服务使用JAX-WS暴露单独web服务使用Spring支持的JAX-WS RI来暴露服务使用JAX-WS访问web服务使用XFire来暴露Web服务JMS服务端配置客户端配置对远程接口不提供自动探测实现在选择这些技术时的一些考虑Enterprise Java Beans (EJB) 集成简介访问EJB概念访问本地的无状态Session Bean(SLSB)访问远程SLSBAccessing EJB 2.x SLSBs versus EJB 3 SLSBs使用Spring提供的辅助类实现EJB组件EJB 2.x base classesEJB 3 注入拦截JMS (Java Message Service)简介使用Spring JMSJmsTemplate连接工厂目的地管理消息侦听容器SimpleMessageListenerContainerDefaultMessageListenerContainerServerSessionMessageListenerContainer事务管理发送消息使用消息转换器SessionCallback 和 ProducerCallback接收消息同步接收异步接收 - 消息驱动的POJOSessionAwareMessageListener接口MessageListenerAdapter事务中的消息处理JCA消息端点的支持JMS命名空间支持JMX介绍将Bean暴露为JMX创建MBeanServer重用原有的MBeanServer延迟初始化的MBeanMBean的自动注册控制注册行为控制Bean的管理接口MBeanInfoAssembler接口使用源码级元数据使用JDK 5.0的注解源代码级的元数据类型AutodetectCapableMBeanInfoAssembler接口用Java接口定义管理接口使用MethodNameBasedMBeanInfoAssembler控制Bean的ObjectName从Properties读取Properties使用MetadataNamingStrategy<context:mbean-export/>元素JSR-160连接器服务器端连接器客户端连接器基于Burlap/Hessian/SOAP的JMX通过代理访问MBean通知为通知注册监听器发布通知更多资源JCA CCI简介配置CCI连接器配置在Spring中配置ConnectionFactory配置CCI连接使用一个 CCI 单连接使用Spring的 CCI访问支持记录转换CciTemplate类DAO支持自动输出记录生成总结直接使用一个CCI Connection接口和Interaction接口CciTemplate 使用示例建模CCI访问为操作对象MappingRecordOperationMappingCommAreaOperation自动生成输出记录总结MappingRecordOperation 使用示例MappingCommAreaOperation 使用示例事务Spring邮件抽象层简介使用Spring邮件抽象MailSender 和 SimpleMailMessage 的基本用法使用 JavaMailSender 和 MimeMessagePreparator使用MimeMessageHelper发送附件和嵌入式资源(inline resources)附件内嵌资源使用模板来创建邮件内容一个基于Velocity的示例Spring中的定时调度(Scheduling)和线程池(Thread Pooling)简介使用OpenSymphony Quartz 调度器使用JobDetailBean使用 MethodInvokingJobDetailFactoryBean使用triggers和SchedulerFactoryBean来包装任务使用JDK Timer支持类创建定制的timers使用 MethodInvokingTimerTaskFactoryBean类最后:使用TimerFactoryBean来设置任务SpringTaskExecutor抽象TaskExecutor接口TaskExecutor类型使用TaskExecutor动态语言支持介绍第一个示例定义动态语言支持的bean公共概念<lang:language/> 元素Refreshable bean内置动态语言源文件理解dynamic-language-backed bean上下文中的构造器注入JRuby beansGroovy beans通过回调定制Groovy对象BeanShell beans场景Spring MVC控制器的脚本化Validator的脚本化Bits and bobsAOP - 通知脚本化bean作用域更多的资源注解和源代码级的元数据支持简介Spring的元数据支持注解@RequiredSpring中的其它@AnnotationsJakarta Commons Attributes集成元数据和Spring AOP自动代理基本原理声明式事务管理示例程序演示案例介绍使用动态语言实现的Spring MVC控制器构建与部署使用SimpleJdbcTemplate和@Repository实现DAO域对象Data Access Object构建XML Schema-based configurationIntroductionXML Schema-based configurationReferencing the schemasThe util schema<util:constant/>Setting a bean property or constructor arg from a field value<util:property-path/>Using <util:property-path/> to set a bean property or constructor-argument<util:properties/><util:list/><util:map/><util:set/>The jee schema<jee:jndi-lookup/> (simple)<jee:jndi-lookup/> (with single JNDI environment setting)<jee:jndi-lookup/> (with multiple JNDI environment settings)<jee:jndi-lookup/> (complex)<jee:local-slsb/> (simple)<jee:local-slsb/> (complex)<jee:remote-slsb/>The lang schemaThe jms schemaThe tx (transaction) schemaThe aop schemaThe context schema<property-placeholder/><annotation-config/><component-scan/><load-time-weaver/><spring-configured/><mbean-export/>The tool schemaThe beans schemaSetting up your IDESetting up EclipseSetting up IntelliJ IDEAIntegration issuesXML parsing errors in the Resin v.3 application serverExtensible XML authoringIntroductionAuthoring the schemaCoding a NamespaceHandlerCoding a BeanDefinitionParserRegistering the handler and the schema'META-INF/spring.handlers''META-INF/spring.schemas'Using a custom extension in your Spring XML configurationMeatier examplesNesting custom tags within custom tagsCustom attributes on 'normal' elementsFurther Resourcesspring-beans-2.0.dtdspring.tldIntroductionThe bind tagThe escapeBody tagThe hasBindErrors tagThe htmlEscape tagThe message tagThe nestedPath tagThe theme tagThe transform tagspring-form.tldIntroductionThe checkbox tagThe checkboxes tagThe errors tagThe form tagThe hidden tagThe input tagThe label tagThe option tagThe options tagThe password tagThe radiobutton tagThe radiobuttons tagThe select tagThe textarea tagSpring 2.5开发手册中文化项目声明致谢参与人员项目历程
文字

24.3. 定义动态语言支持的bean

这一节描述了如何针对Spring所支持的动态语言定义受Spring所管理的bean。

请注意本章不会解释这些支持的动态语言的语法和用法。例如,如果你想在你的某个应用中使用Groovy来编写类,我们假设你已经了解Groovy这门语言。如果你需要了解和动态语言本身有关的更多细节,请参考本章末尾第 24.6 节 “更多的资源”一节。

24.3.1. 公共概念

使用dynamic-language-backed bean要经过以下步骤:

  1. 编写针对动态语言源码的测试代码(这是理所应当的事情)

  2. 然后编写动态语言源码 :)

  3. 在XML配置文件中使用相应的<lang:language/>元素定义dynamic-language-backed bean。当然你也可以使用Spring API,以编程的方式来定义---本章并不会涉及到这种高级的配置方式,你可以直接阅读源码来获得相应的指示)。注意这是一个迭代的步骤。每一个动态语言的源文件至少对应一个bean定义(同一个动态语言的源文件当然可以在多个bean定义中引用)。

前面两步(测试并编写动态语言源文件)超出了本章的范畴。请参考你所选动态语言相关的语言规范或者参考手册,并继续开发你的动态语言的源文件。不过你应该首先阅读本章的剩下部分,因为Spring(动态语言支持)对动态语言源文件的内容有一些(小小的)要求。

24.3.1.1. <lang:language/> 元素

最后一步包括如何定义dynamic-language-backed bean定义,每一个要配置的bean对应一个定义(这和普通的Javabean配置没有什么区别)。但是,对于容器中每一个需要实例化和配置的类,普通的Javabean配置需要指定全限定名,对于dynamic language-backed bean则使用<lang:language/>元素取而代之。

每一种支持的语言都有对应的<lang:language/>元素

  • <lang:jruby/>(JRuby)

  • <lang:groovy/>(Groovy)

  • <lang:bsh/>(BeanShell)

对于配置中可用的确切的属性和子元素取决于具体定义bean的语言(后面和特定语言有关的章节会揭示全部内幕)。

24.3.1.2. Refreshable bean

Spring对动态语言支持中最引人注目的价值在于增加了对 'refreshable bean' 特征的支持。

refreshable bean是一种只有少量配置的dynamic-language-backed bean。dynamic-language-backed bean 可以监控底层源文件的变化,一旦源文件发生改变就可以自动重新加载(例如开发者编辑文件并保存修改)。

这样就允许开发者在应用程序中部署任意数量的动态语言源文件,并通过配置Spring容器来创建动态语言源文件所支持的bean(使用本章所描述的机制)。以后如果需求发生改变,或者一些外部因素起了作用,这样就可以简单的编辑动态语言源文件,而这些文件中的变化会反射为bean的变化。而这些工作不需要关闭正在运行的应用(或者重新部署web应用)。dynamic-language-backed bean能够自我修正,从已改变的动态语言源文件中提取新的状态和逻辑。

注意

注意,该特征默认值为off(关闭)

下面让我们看一个例子,体验一下使用refreshable bean是多么容易的事情。首先要启用refreshable bean特征,只需要在bean定义的 <lang:language/>元素中指定一个附加属性。假设我们继续使用前文中的 例子,那么只需要在Spring的XML配置文件中进行如下修改以启用refreshable bean:

<beans>

    <!-- this bean is now 'refreshable' due to the presence of the 'refresh-check-delay' attribute -->
    <lang:groovy id="messenger"
          refresh-check-delay="5000" <!-- switches refreshing on with 5 seconds between checks -->
          script-source="classpath:Messenger.groovy">
        <lang:property name="message" value="I Can Do The Frug" />
    </lang:groovy>

    <bean id="bookingService" class="x.y.DefaultBookingService">
        <property name="messenger" ref="messenger" />
    </bean>

</beans>

这就是所有你需要做的事情。 'messenger' bean定义中的'refresh-check-delay'属性指定了刷新bean的时间间隔,在这个时间段内的底层动态语言源文件的任何变化都会刷新到对应的bean上。通过给该属性赋一个负值即可关闭该刷新行为。注意在默认情况下,该刷新行为是关闭的。如果你不需要该刷新行为,最简单的办法就是不要定义该属性。

运行以下应用程序可以体验refreshable特征:请执行接下来这段代码中的'jumping-through-hoops-to-pause-the-execution'小把戏。System.in.read()的作用是暂停程序的执行,这个时候去修改底层的动态语言源文件,然后程序恢复执行的时候触发dynamic-language-backed bean的刷新。

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

    public static void main(final String[] args) throws Exception {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        Messenger messenger = (Messenger) ctx.getBean("messenger");
        System.out.println(messenger.getMessage());
        // pause execution while I go off and make changes to the source file...
        System.in.read();
        System.out.println(messenger.getMessage());
    }
}

假设对于这个例子,所有调用Messenger实现中getMessage()方法的地方都被修改:比如将message用引号括起来。下面是我在程序执行暂停的时候对Messenger.groovy源文件所做的修改:

package org.springframework.scripting

class GroovyMessenger implements Messenger {

    private String message = "Bingo"

    public String getMessage() {
        // change the implementation to surround the message in quotes
        return "'" + this.message + "'"
    }

    public void setMessage(String message) {
        this.message = message
    }
}

在这段程序执行的时候,在输入暂停之前的输出是I Can Do The Frug。在修改并保存了源文件之后,程序恢复执行,再次调用dynamic-language-backed MessengergetMessage()方法的结果为'I Can Do The Frug' (注意新增的引号)。

有一点很重要,如果上述对脚本的修改发生在'refresh-check-delay'值的时间范围内并不会触发刷新动作。同样重要的是,修改脚本并不会马上起作用,而是要到该动态语言实现的bean的相应的方法被调用时才有效。只有动态语言实现的bean的方法被调用的时候才会检查底层源文件是否修改了。刷新脚本产生的任何异常(例如发生编译错误,或者脚本文件被删除了)都会在调用的代码中抛出一个致命异常。

前面描述的refreshable bean的行为并会作用于使用<lang:inline-script/>元素定义的动态语言源文件(请参考第 24.3.1.3 节 “内置动态语言源文件”这一节)。而且它作用于那些可以检测到底层源文件发生改变的bean。例如,检查文件系统中的动态语言源文件的最后修改日期。

24.3.1.3. 内置动态语言源文件

Spring动态语言支持还提供了直接在bean定义中直接嵌入动态语言源码的功能。通过<lang:inline-script/>元素,可以在Spring的配置文件中直接定义动态语言源文件。下面的例子或许可以将嵌入脚本特征表达的更清楚:

<lang:groovy id="messenger">
    <lang:inline-script>
package org.springframework.scripting.groovy;

import org.springframework.scripting.Messenger

class GroovyMessenger implements Messenger {

    String message
}
    </lang:inline-script>
    <lang:property name="message" value="I Can Do The Frug" />
</lang:groovy>

直接在Spring的配置文件中定义动态语言源码的是否是最佳实践这个问题先放在一边,<lang:inline-script/>元素在某些场景下还是相当有用的。例如,给Spring MVC的Controller快速添加一个Spring Validator实现。如果采用内置源码的方式只需要片刻时间就可以搞定(请参见第 24.4.2 节 “Validator的脚本化”这一节的示例)。

下面这个例子是一个基于JRuby的bean,这个例子直接在Spring的XML配置文件中定义了源码,并使用了inline: 符号。(注意其中使用 &lt;符号来表示'<'字符。这个例子中如果在内置源码周围定义一个<![CDATA[]]>就更好了。)

<lang:jruby id="messenger" script-interfaces="org.springframework.scripting.Messenger">
    <lang:inline-script>
require 'java'

include_class 'org.springframework.scripting.Messenger'

class RubyMessenger &lt; Messenger

 def setMessage(message)
  @@message = message
 end

 def getMessage
  @@message
 end
 
end
    </lang:inline-script>
    <lang:property name="message" value="Hello World!" />
</lang:jruby>

24.3.1.4. 理解dynamic-language-backed bean上下文中的构造器注入

关于Spring动态语言支持有一个要点必须引起注意:目前对dynamic-language-backed bean还不可能提供构造器参数的支持(也就是说对于dynamic-language-backed bean的构造器注入无效)。 只是为了将构造器和属性的特殊处理100%说清楚,下面混合了代码和配置的例子是无法运作的。

// from the file 'Messenger.groovy'
package org.springframework.scripting.groovy;

import org.springframework.scripting.Messenger

class GroovyMessenger implements Messenger {

    GroovyMessenger() {}

    // this constructor is not available for Constructor Injection
    GroovyMessenger(String message) {
        this.message = message;
    }

    String message

    String anotherMessage
}
<lang:groovy id="badMessenger"
    script-source="classpath:Messenger.groovy">

    <!-- this next constructor argument will *not* be injected into the GroovyMessenger -->
    <!--     in fact, this isn't even allowed according to the schema -->
    <constructor-arg value="This will *not* work" />
    
    <!-- only property values are injected into the dynamic-language-backed object -->
    <lang:property name="anotherMessage" value="Passed straight through to the dynamic-language-backed object" />

</lang>

实际上这种局限性并没有表现的那么明显,因为setter注入的方式是开发人员更青睐的方式(至于哪种注入方式更好,这个话题我们还是留到以后再讨论吧)。

24.3.2. JRuby beans

来自JRuby官方网页...

JRuby是Ruby语言的纯Java实现。

Spring一直以来的崇尚的哲学是提供选择性,因此Spring动态语言支持特征也支持使用JRuby语言定义的bean。JRuby语言当然基于Ruby语言,支持内置正则表达式,块(闭包),以及其他很多特征,这些特征对于某些域问题提供了解决方案,可以让开发变的更容易。

Spring对JRuby动态语言支持的有趣的地方在于:对于<lang:ruby>元素'script-interfaces'属性指定的接口,Spring为它们创建了JDK动态代理实现(这也是你使用JRuby实现的bean,必须为该属性指定至少一个接口并编程实现的原因)。

首先我们看一个使用基于JRuby的bean的可工作的完整示例。下面是使用JRuby实现的Messenger接口(本章之前定义过了,为了方便你阅读,下面重复定义该接口)。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();
}
require 'java'

class RubyMessenger
    include org.springframework.scripting.Messenger

    def setMessage(message)
        @@message = message
    end

    def getMessage
        @@message
    end
end

# this last line is not essential (but see below)
RubyMessenger.new

下面是Spring的XML配置,其内容定义了RubyMessenger(JRuby bean)的实例。

<lang:jruby id="messageService"
            script-interfaces="org.springframework.scripting.Messenger"
            script-source="classpath:RubyMessenger.rb">
    
    <lang:property name="message" value="Hello World!" />

</lang:jruby>

注意JRuby源码的最后一行('RubyMessenger.new')。在Spring动态语言支持的上下文之下使用JRuby的时候,我们鼓励你实例化并返回一个JRuby类的实例。如果你打算将其作为你的JRuby源码的执行结果,并将其作为dynamic-language-backed bean,只需要简单的实例化你的JRuby类就可以达到这样的效果,如下面源文件的最后一行:

require 'java'

include_class 'org.springframework.scripting.Messenger'

# class definition same as above...

# instantiate and return a new instance of the RubyMessenger class
RubyMessenger.new

如果你忘记了这点,并不代表以前所有的努力白费了,不过Spring会以反射的方式扫描你的JRuby的类型表示,并找出一个类,然后进行实例化。这个过程的速度是相当快的,可能你永远都不会感觉到延迟,但是只需要象前面的例子那样在你的JRuby的脚本最后添加一行就可以避免这样的事情,何乐而不为呢?如果不提供这一行,或者如果Spring在你的JRuby脚本中无法找到可以实例化的类,JRuby的解释器执行源码结束后会立刻抛出ScriptCompilationException异常。下面的代码中可以立刻发现一些关键的文本信息,这些文本信息标识了导致异常的根本原因(如果Spring容器在创建的 dynamic-language-backed bean的时候抛出以下异常, 在相应的异常堆栈中会包括以下文本信息,希望这些信息能够帮助你更容易定位并矫正问题):

org.springframework.scripting.ScriptCompilationException: Compilation of JRuby script returned ''

为了避免这种错误,将你打算用作JRuby-dynamic-language-backed bean(如前文所示)的类进行实例化,并将其返回。请注意在JRuby脚本中实际上可以定义任意数目的类和对象,重要的是整个源文件应该返回一个对象(用于Spring的配置)。

第 24.4 节 “场景” 这一节提供了一些场景,在这些场景下也许你会打算采用基于JRuby的bean.

24.3.3. Groovy beans

来自Groovy官方网页...

Groovy是一门针对Java 2平台的敏捷的动态语言,Python、Ruby、Smalltalk这类语言中不少受人喜爱的特征都被囊括其中,并以Java风格的语法展现给Java开发者。

如果你是以从上到下的方式一直读到这一章,你应该已经看到了一个Groovy-dynamic-language-backed bean的示例。接下来我们来看另外一个例子(还是选自Spring的测试套件)。

注意

Groovy需要1.4以上的JDK。

package org.springframework.scripting;

public interface Calculator {

    int add(int x, int y);
}

下面是使用Groovy实现的Calculator接口。

// from the file 'calculator.groovy'
package org.springframework.scripting.groovy

class GroovyCalculator implements Calculator {

    int add(int x, int y) {
        x + y
    }
}
<-- from the file 'beans.xml' -->
<beans>
    <lang:groovy id="calculator" script-source="classpath:calculator.groovy"/>
</beans>

最后是一个小应用程序,用于测试上面的配置。

package org.springframework.scripting;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void Main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        Calculator calc = (Calculator) ctx.getBean("calculator");
        System.out.println(calc.add(2, 8));
    }
}

运行上面的程序最终输出结果(毫无疑问)为10。(令人激动的例子,是吧?记住我们的目的是为了阐述概念。更复杂的例子请参考动态语言的示例项目,或者参考本章最后列出的第 24.4 节 “场景”)。

有一点很重要,那就是你不要 在一个Groovy源文件中定义两个以上的class。虽然Groovy允许这样做,但是是一个很不好的实践,为了保持一致性,你应该尊重标准的Java规范(至少作者是这样认为的):一个源文件只定义一个(public)类。

24.3.3.1. 通过回调定制Groovy对象

GroovyObjectCustomizer接口实际上是一个回调, 它允许你将附属的创建逻辑添加到创建Groovy bean的过程中。 例如,该接口的实现能够调用任何需要的初始化方法, 或者设置一些缺省的属性值,或者指定自定义的MetaClass

public interface GroovyObjectCustomizer {

   void customize(GroovyObject goo);
}

Spring框架将会初始化Groovy bean示例,然后将已经创建的GroovyObject 对象传到到指定的GroovyObjectCustomizer接口。 通过GroovyObject你可以做任意想做的事情。大部分 人也许都想通过该回调传入一个定制的MetaClass,下面你将看到一个这样的例子。

public final class SimpleMethodTracingCustomizer implements GroovyObjectCustomizer {

   public void customize(GroovyObject goo) {
      DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) {

         public Object invokeMethod(Object object, String methodName, Object[] arguments) {
            System.out.println("Invoking '" + methodName + "'.");
            return super.invokeMethod(object, methodName, arguments);
         }
      };
      metaClass.initialize();
      goo.setMetaClass(metaClass);
   }
}

完整讨论Groovy的元编程已经超出了本参考手册的范畴。建议直接查阅Groovy参考手册的有关章节, 或者通过在线查询:目前有大量的关于这方面的文章。 如果你使用Spring 2.0的命名空间支持功能,使用GroovyObjectCustomizer将会变得非常简单。

<!-- define the GroovyObjectCustomizer just like any other bean -->
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer" />

   <!-- ... and plug it into the desired Groovy bean via the 'customizer-ref' attribute -->
   <lang:groovy id="calculator"
      script-source="classpath:org/springframework/scripting/groovy/Calculator.groovy"
      customizer-ref="tracingCustomizer" />

如果你没有使用Spring 2.0的命名空间支持,你仍然可以使用GroovyObjectCustomizer的功能。

<bean id="calculator" class="org.springframework.scripting.groovy.GroovyScriptFactory">
      <constructor-arg value="classpath:org/springframework/scripting/groovy/Calculator.groovy"/>
      <!-- define the GroovyObjectCustomizer (as an inner bean) -->
      <constructor-arg>
         <bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer" />
      </constructor-arg>
</bean>

<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>

24.3.4. BeanShell beans

来自BeanShell官方网页...

BeanShell是一个用Java实现的小型免费嵌入式Java源码解释器,支持动态语言特征。BeanShell动态执行标准的Java语法,并进行了扩展,带来一些常见的脚本的便利,如在Perl和JavaScript中的宽松类型、命令、方法闭包等等。

和Groovy相比,基于BeanShell的bean定义需要的配置要多一些。Spring对BeanShell动态语言支持的有趣的地方在于:对于<lang:bsh>元素的'script-interfaces'属性指定的接口,Spring为它们创建了JDK动态代理实现(这也是你使用BeanShell实现的bean,必须为该属性指定至少一个接口并编程实现的原因)。这意味着所有调用 BeanShell-backed对象的方法,都要通过JDK动态代理调用机制。

首先我们看一个使用基于BeanShell的bean的可工作的完整示例。它实现了本章之前定义的Messenger接口(为了方便阅读,下面重复定义该接口)。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();
}

Here is the BeanShell 'implementation' (the term is used loosely here) of the Messenger interface.

下面是BeanShell的实现的Messenger 接口。

String message;

String getMessage() {
    return message;
}

void setMessage(String aMessage) {
    message = aMessage;
}

下面的Spring XML定义了上述“类”的一个“实例”(这里对术语的使用非常的随意)。

<lang:bsh id="messageService" script-source="classpath:BshMessenger.bsh"
    script-interfaces="org.springframework.scripting.Messenger">

    <lang:property name="message" value="Hello World!" />
</lang:bsh>

第 24.4 节 “场景”一节中提供了一些场景,在这样的场景下你也许打算采用基于BeanShell的bean。

上一篇:下一篇: