目录 搜索
前言 简介 概览 使用场景 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 异步的JMS JDBC Web层 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 OJB iBATIS Hibernate JDO UrlFilenameViewController 更新的样例应用 改进的文档 核心技术 IoC(控制反转)容器 简介 基本原理 - 容器和bean 容器 配置元数据 实例化容器 XML配置元数据的结构 多种bean bean的命名 bean的别名 实例化bean 用构造器来实例化 使用静态工厂方法实例化 使用实例工厂方法实例化 使用容器 依赖 注入依赖 构造器注入 构造器参数解析 构造器参数类型匹配 构造参数索引 Setter注入 一些例子 依赖配置详解 直接变量(基本类型、Strings类型等。) idref元素 引用其它的bean(协作者) 内部bean 集合 集合的合并 强类型集合(仅适用于Java5+) Nulls XML配置文件的简写及其他 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容器 了解自己 BeanFactoryAware BeanNameAware bean定义的继承 容器扩展点 用BeanPostProcessor定制bean 使用BeanPostProcessor的Hello World示例 RequiredAnnotationBeanPostProcessor示例 用BeanFactoryPostProcessor定制配置元数据 PropertyPlaceholderConfigurer示例 PropertyOverrideConfigurer示例 使用FactoryBean定制实例化逻辑 The ApplicationContext BeanFactory 还是 ApplicationContext? 利用MessageSource实现国际化 事件 底层资源的访问 ApplicationContext在WEB应用中的实例化 粘合代码和可怕的singleton 以J2EE RAR文件的形式部署Spring ApplicationContext 基于注解(Annotation-based)的配置 @Autowired 基于注解的自动连接微调 CustomAutowireConfigurer @Resource @PostConstruct 与 @PreDestroy 对受管组件的Classpath扫描 @Component和更多典型化注解 自动检测组件 使用过滤器自定义扫描 自动检测组件的命名 为自动检测的组件提供一个作用域 用注解提供限定符元数据 注册一个LoadTimeWeaver 资源 简介 Resource接口 内置 Resource 实现 UrlResource ClassPathResource FileSystemResource ServletContextResource InputStreamResource ByteArrayResource ResourceLoader接口 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 contexts Spring中其他的AspectJ切面 使用Spring IoC来配置AspectJ的切面 在Spring应用中使用AspectJ加载时织入(LTW) 第一个例子 切面 'META-INF/aop.xml' 相关类库(JARS) Spring配置 特定环境的配置 通用Java应用 Tomcat WebLogic 更多资源 Spring AOP APIs 简介 Spring中的切入点API 概念 切入点运算 AspectJ切入点表达式 便利的切入点实现 静态切入点 正则表达式切入点 属性驱动的切入点 动态切入点 控制流切入点 切入点的超类 自定义切入点 Spring的通知API 通知的生命周期 Spring里的通知类型 拦截环绕通知 前置通知 异常通知 后置通知 引入通知 Spring里的Advisor API 使用ProxyFactoryBean创建AOP代理 基础 JavaBean属性 基于JDK和CGLIB的代理 对接口进行代理 对类进行代理 使用“全局”通知器 简化代理定义 使用ProxyFactory通过编程创建AOP代理 操作被通知对象 使用“自动代理(autoproxy)”功能 自动代理bean定义 BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator AbstractAdvisorAutoProxyCreator 使用元数据驱动的自动代理 使用TargetSource 热交换目标源 池化目标源 原型目标源 ThreadLocal目标源 定义新的Advice类型 更多资源 测试 简介 单元测试 Mock对象 JNDI Servlet API Portlet 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 有关的设置 事务传播 required RequiresNew Nested 通知事务操作 结合AspectJ使用 @Transactional 编程式事务管理 使用TransactionTemplate 指定事务设置 使用PlatformTransactionManager 选择编程式事务管理还是声明式事务管理 与特定应用服务器集成 IBM WebSphere BEA WebLogic Oracle 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类 NativeJdbcExtractor JDBC批量操作 使用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容器中创建 SessionFactory The HibernateTemplate 不使用回调的基于Spring的DAO实现 基于Hibernate3的原生API实现DAO 编程式的事务划分 声明式的事务划分 事务管理策略 容器资源 vs 本地资源 在应用服务器中使用Hibernate的注意事项 JDO 建立PersistenceManagerFactory JdoTemplate和JdoDaoSupport 基于原生的JDO API实现DAO 事务管理 JdoDialect Oracle TopLink SessionFactory 抽象层 TopLinkTemplate and TopLinkDaoSupport 基于原生的TopLink API的DAO实现 事务管理 iBATIS SQL Maps 创建SqlMapClient 使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport 基于原生的iBATIS API的DAO实现 JPA 在Spring环境中建立JPA LocalEntityManagerFactoryBean 从JNDI中获取 EntityManagerFactory LocalContainerEntityManagerFactoryBean Tomcat(5.0以上)加载时的织入配置 使用VM代理的全局加载时织入 上下文范围内的加载时织入配置 处理多持久化单元 JpaTemplate 和 JpaDaoSupport 基于原生的JPA实现DAO 异常转化 事务管理 JpaDialect The Web Web MVC framework Web框架 概述 与其他MVC实现框架的集成 Spring Web MVC框架的特点 DispatcherServlet 控制器 AbstractController 和 WebContentGenerator 其它的简单控制器 MultiActionController 命令控制器 处理器映射(handler mapping) BeanNameUrlHandlerMapping SimpleUrlHandlerMapping 拦截器(HandlerInterceptor) 视图与视图解析 视图解析器(ViewResolver) 视图解析链 重定向(Rediret)到另一个视图 RedirectView redirect:前缀 forward:前缀 本地化解析器 AcceptHeaderLocaleResolver CookieLocaleResolver SessionLocaleResolver LocaleChangeInterceptor 使用主题 简介 如何定义主题 主题解析器 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 需要的资源 如何集成Tiles UrlBasedViewResolver类 ResourceBundleViewResolver类 SimpleSpringPreparerFactory 和 SpringBeanPreparerFactory Velocity和FreeMarker 需要的资源 Context 配置 创建模板 高级配置 velocity.properties FreeMarker 绑定支持和表单处理 用于绑定的宏 简单绑定 表单输入生成宏 输入域 选择输入域 重载HTML转码行为并使你的标签符合XHTML XSLT 写在段首 Bean 定义 标准MVC控制器代码 把模型数据转化为XML 定义视图属性 文档转换 小结 文档视图(PDF/Excel) 简介 配置和安装 文档视图定义 Controller 代码 Excel视图子类 PDF视图子类 JasperReports 依赖的资源 配置 配置ViewResolver 配置View 关于报表文件 使用 JasperReportsMultiFormatView 构造ModelAndView 使用子报表 配置子报表文件 配置子报表数据源 配置Exporter的参数 集成其它Web框架 简介 通用配置 JavaServer Faces DelegatingVariableResolver FacesContextUtils Struts ContextLoaderPlugin DelegatingRequestProcessor DelegatingActionProxy ActionSupport Classes Tapestry 注入 Spring 托管的 beans 将 Spring Beans 注入到 Tapestry 页面中 组件定义文件 添加抽象访问方法 将 Spring Beans 注入到 Tapestry 页面中 - Tapestry 4.0+ 风格 WebWork 更多资源 Portlet MVC框架 介绍 控制器 - MVC中的C 视图 - MVC中的V Web作用范围的Bean DispatcherPortlet ViewRendererServlet 控制器 AbstractController 和 PortletContentGenerator 其它简单的控制器 Command控制器 PortletWrappingController 处理器映射 PortletModeHandlerMapping ParameterHandlerMapping PortletModeParameterHandlerMapping 增加 HandlerInterceptors HandlerInterceptorAdapter ParameterMappingInterceptor 视图和它们的解析 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) 访问远程SLSB Accessing EJB 2.x SLSBs versus EJB 3 SLSBs 使用Spring提供的辅助类实现EJB组件 EJB 2.x base classes EJB 3 注入拦截 JMS (Java Message Service) 简介 使用Spring JMS JmsTemplate 连接工厂 目的地管理 消息侦听容器 SimpleMessageListenerContainer DefaultMessageListenerContainer ServerSessionMessageListenerContainer 事务管理 发送消息 使用消息转换器 SessionCallback 和 ProducerCallback 接收消息 同步接收 异步接收 - 消息驱动的POJO SessionAwareMessageListener接口 MessageListenerAdapter 事务中的消息处理 JCA消息端点的支持 JMS命名空间支持 JMX 介绍 将Bean暴露为JMX 创建MBeanServer 重用原有的MBeanServer 延迟初始化的MBean MBean的自动注册 控制注册行为 控制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访问为操作对象 MappingRecordOperation MappingCommAreaOperation 自动生成输出记录 总结 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 beans Groovy beans 通过回调定制Groovy对象 BeanShell beans 场景 Spring MVC控制器的脚本化 Validator的脚本化 Bits and bobs AOP - 通知脚本化bean 作用域 更多的资源 注解和源代码级的元数据支持 简介 Spring的元数据支持 注解 @Required Spring中的其它@Annotations Jakarta Commons Attributes集成 元数据和Spring AOP自动代理 基本原理 声明式事务管理 示例程序 演示案例 介绍 使用动态语言实现的Spring MVC控制器 构建与部署 使用SimpleJdbcTemplate和@Repository实现DAO 域对象 Data Access Object 构建 XML Schema-based configuration Introduction XML Schema-based configuration Referencing the schemas The 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 schema The jms schema The tx (transaction) schema The aop schema The context schema <property-placeholder/> <annotation-config/> <component-scan/> <load-time-weaver/> <spring-configured/> <mbean-export/> The tool schema The beans schema Setting up your IDE Setting up Eclipse Setting up IntelliJ IDEA Integration issues XML parsing errors in the Resin v.3 application server Extensible XML authoring Introduction Authoring the schema Coding a NamespaceHandler Coding a BeanDefinitionParser Registering the handler and the schema 'META-INF/spring.handlers' 'META-INF/spring.schemas' Using a custom extension in your Spring XML configuration Meatier examples Nesting custom tags within custom tags Custom attributes on 'normal' elements Further Resources spring-beans-2.0.dtd spring.tld Introduction The bind tag The escapeBody tag The hasBindErrors tag The htmlEscape tag The message tag The nestedPath tag The theme tag The transform tag spring-form.tld Introduction The checkbox tag The checkboxes tag The errors tag The form tag The hidden tag The input tag The label tag The option tag The options tag The password tag The radiobutton tag The radiobuttons tag The select tag The textarea tag Spring 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。

上一篇: 下一篇: