spring-framework-中文


13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 1/514 Spring框架参考文档 作者 杆 约翰逊 , 于尔根· Hoeller , 基思 唐纳德 , 科林 Sampaleanu , 罗伯 Harrop , 托马斯 里斯贝里 , Alef Arendsen , 达伦 戴维 森 , 他 Kopylenko , 马克 波拉克 , 蒂埃里 Templier , 欧文 Vervaet , 波西亚 董建华 , 本 硬朗 , 艾德里安 Colyer , 约翰 刘易斯 , 损失 Leau , 马克 费舍尔 , 山姆 Brannen , Ramnivas Laddad , 罗本 Poutsma , 克里斯 梁 , 塔里克· Abedrabbo , 安迪 克莱 门特 , 戴夫 Syer , 奥利弗 Gierke , 雷森 Stoyanchev , 菲利普 韦伯 3 2 2释放 版权©2004 - 2013 本文件的复印件可以供你自己使用和 分配给别人,只要你不收取任何费用这样的 册,进一步提供,每个副本包含这个版权 注意,是否分布在打印或电 子。 表的内容 即Spring框架的概述 1。 介绍Spring框架 1.1。 依赖注入和控制反转 1.2。 模块 1.2.1。 核心容器 1.2.2。 数据访问/集成 1.2.3。 web 1.2.4。 AOP和仪表 1 2 5。 测试 1.3。 使用场景 1.3.1。 依赖关系管理和命名约定 Spring依赖和依靠弹簧 Maven依赖管理 艾薇依赖管理 1.3.2。 日志 不使用通用日志 使用SLF4J 使用Log4j 二世。 什么是新的在春季3 2。 新特性和增强功能在Spring框架3.0 2.1。 Java 5 2.2。 改进文档 2.3。 新的文章和教程 2.4。 新模块的组织和构建系统 2.5。 概述的新特性 2 5 1。 核心api更新Java 5 2 5 2。 弹簧表达式语言 2 5 3。 这个控制反转(IoC)容器 基于Java bean的元数据 元数据定义bean组件内 2 5 4。 通用类型转换系统和字段格式 系统 2 5 5。 数据层 2 5 6。 Web层 综合REST支持 @MVC添加 2 5 7。 声明式模型验证 2 5 8。 早期支持Java EE 6 2 5 9。 支持嵌入式数据库 3。 新特性和增强功能在Spring框架3.1 3.1。 缓存抽象 3.2。 Bean定义概要文件 3.3。 环境抽象 3.4。 PropertySource抽象 3.5。 代码等价的Spring的XML名称空间 3.6。 支持Hibernate 4. x 3.7。 还是和TestContext框架支持@ configuration类和bean 定义概要文件 3.8。 c:名称空间更简洁的构造函数注入 3.9。 支持对非标准JavaBeans注射 setter 3.10。 支持基于Servlet的配置Servlet 3 容器 3.11。 3 MultipartResolver支持Servlet 翻译级别 专 家 进 阶 入 门 全文翻译 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 2/514 3.12。 JPA会引导没有 persistence . xml 3.13。 新HandlerMethod-based支持类注释的控制器 处理 3.14。 “消费”和“生产”条件 @RequestMapping 3.15。 Flash属性和 RedirectAttributes 3.16。 URI模板变量增强 3.17。 @Valid 在 @RequestBody 控制器方法参数 3.18。 @RequestPart 注释 控制器方法参数 3.19。 UriComponentsBuilder 和 UriComponents 4。 新特性和增强功能在Spring框架3.2 4.1。 支持Servlet 3异步请求处理的基础 4.2。 Spring MVC的测试框架 4.3。 内容协商改进 4.4。 @ControllerAdvice 注释 4.5。 矩阵变量 4.6。 抽象基类基于代码的Servlet 3 +容器 初始化 4.7。 ResponseEntityExceptionHandler 类 4.8。 对泛型的支持的 RestTemplate 在 @RequestBody 参数 4.9。 杰克逊JSON 2和相关的改进 4.10。 瓷砖3 4.11。 @RequestBody 改进 4.12。 HTTP补丁方法 4.13。 排除模式映射的拦截器 4.14。 使用注射点和元注释bean定义方法 4.15。 最初支持JCache 0.5 4.16。 支持 @DateTimeFormat 没有 Joda时间 4.17。 日期和时间格式化全球 4.18。 新的测试功能 4.19。 整个框架的改进并发性 4.20。 新的基于gradle构建和搬到GitHub 4.21。 精制Java SE 7 / OpenJDK 7支持 三世。 核心技术 5。 IoC容器 5.1。 介绍Spring IoC容器和豆类 5.2。 集装箱概述 5 2 1。 配置元数据 5.2.2。 实例化一个集装箱 组成的基于xml的配置元数据 5 2 3。 使用集装箱 5.3。 Bean概述 5 3 1。 命名bean 混淆一个bean外的bean定义 5 3 2。 实例化bean 与构造函数实例化 与静态工厂方法实例化 使用工厂方法实例化一个实例 5.4。 依赖性 5.4.1之前。 依赖注入 无参依赖注入 setter建立依赖注入 依赖解析过程 依赖注入的例子 5 4 2。 依赖关系和配置细节 直值(原语, 字符串 ,所以 ) 引用其他豆类(合作者) 内豆 集合 空,空字符串值 XML快捷与p名称空间 XML快捷与c名称空间 复合属性名 第5.4.3。 使用 取决于 5 4 4。 延迟初始化的 bean 5 4 5。 自动装配的合作者 局限性和缺点的自动装配 不包括一个bean从自动装配 5 4 6。 方法注入 查找方法注入 任意方法替代 5.5。 Bean范围 5.5.1。 singleton范围 5 5 2。 原型范围 5 5 3。 单例bean与原型bean的依赖项 5 5 4。 请求、会话和全球会话作用域 初始web配置 请求范围 会话范围 通迅订阅Search Documentation 所有 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 3/514 全球会话范围 范围bean作为依赖项 5 5 5。 定制范围 创建一个自定义的范围 使用一个自定义的范围 5.6。 定制一个bean的性质 5.6.1。 生命周期回调 初始化回调 破坏回调 默认的初始化和销毁方法 结合生命周期机制 启动和关闭回调 关闭Spring IoC容器优雅地在非web 应用 5 6 2。 ApplicationContextAware 和 BeanNameAware 5 6 3。 其他 意识到 接口 5.7。 Bean定义继承 5.8。 容器扩展点 5 8 1。 定制bean使用 BeanPostProcessor 例如:你好世界, BeanPostProcessor 风格 示例: RequiredAnnotationBeanPostProcessor 5 8 2。 自定义配置元数据与一个 BeanFactoryPostProcessor 示例: 来完成 示例: PropertyOverrideConfigurer 5 8 3。 自定义实例化逻辑与一个 FactoryBean 5.9。 基于注解的容器配置 5 - 9 - 1。 @ required 5 9 2。 @ autowired 5 9 3。 基于注解的自动装配与限定词微调 5 9 4。 CustomAutowireConfigurer 5 9 5。 @ resource 5 9 6。 @PostConstruct 和 @PreDestroy 5.10。 类路径扫描和管理组件 5 10 1。 component 和进一步的刻板印象 注释 5 10 2。 自动检测和注册的bean类 定义 5 10 3。 使用过滤器来定制扫描 5 10 4。 元数据定义bean组件内 5 10 5。 命名个组件 5 10 6。 一个组件提供的范围 5 10 7。 元数据与注释提供限定符 5.11。 使用JSR 330标准注释 5 11 1。 依赖注入与 @ inject 和 @ named 5 11 2。 @ named :一个标准相当于 component 注释 5 11 3。 的标准方法的局限性 5.12。 java容器配置 5 12 1。 基本概念: @ bean 和 @ configuration 5 12 2。 Spring容器实例化使用 所 结构简单 建筑容器以编程方式使用 寄存器(类< ? >…) 使组件扫描与 扫描(String……) 支持web应用程序时 AnnotationConfigWebApplicationContext 5 12 3。 使用 @ bean 注释 声明一个bean 接收生命周期回调 bean指定范围 定制bean命名 Bean混淆 5 12 4。 使用 @ configuration 注释 注入国米bean依赖 查找方法注入 进一步的信息关于基于java的配置工作 内部 5 12 5。 构成基于java的配置 使用 @ import 注释 结合Java和XML配置 5.13。 登记 LoadTimeWeaver 5.14。 附加功能的 ApplicationContext 5 14 1。 国际化使用 MessageSource 5 14 2。 标准和定制的事件 5 14 3。 方便的访问底层的资源 5 14 4。 方便 ApplicationContext 为web应用程序的实例化 5 14 5。 部署一个弹簧ApplicationContext作为J2EE RAR文件 5.15。 BeanFactory 5 15 1。 BeanFactory 或 ApplicationContext 吗? 5 15 2。 胶水代码和邪恶的单例 6。 资源 6.1。 介绍 6.2。 这个 资源 接口 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 4/514 6.3。 内置 资源 实现 6 3 1。 UrlResource 6 3 2。 ClassPathResource 6.3.3这时。 FileSystemResource 6.3.4得到。 ServletContextResource 6 3 5。 InputStreamResource 6 3 6。 ByteArrayResource 6.4。 这个 ResourceLoader 6.5。 这个 ResourceLoaderAware 接口 6.6。 资源 作为依赖项 6.7。 应用程序上下文和 资源 路径 6 7 1。 构建应用程序上下文 构建 ClassPathXmlApplicationContext 实例——快捷键 6 7 2。 通配符在应用程序上下文构造函数资源路径 ant是基于模式 这个 classpath *: 前缀 其他笔记有关通配符 6 7 3。 FileSystemResource 警告 7。 验证、数据绑定、类型转换 7.1。 介绍 7.2。 验证使用Spring的 验证器 接口 7.3。 解决代码错误消息 7.4。 Bean操纵和 BeanWrapper 7 4 1。 设置和获取基本和嵌套的属性 7.4.2。 内置 属性编辑器 实现 注册附加自定义 PropertyEditors 7.5。 弹簧3类型转换 7.5.1。 转换器SPI 7.5.2。 ConverterFactory 7 5 3。 GenericConverter ConditionalGenericConverter 7 5 4。 ConversionService API 7.5.5。 配置一个ConversionService 7 5 6。 使用ConversionService编程 7.6。 弹簧3字段格式 7 6 1。 格式化器SPI 之前。 注解驱动的格式 格式注释API 7 6 3。 FormatterRegistry SPI 7 6 4。 FormatterRegistrar SPI 7 6 5。 配置格式在Spring MVC 7.7。 配置一个全球日期和时间格式 7.8。 弹簧3验证 7 8 1。 概述了jsr - 303 API Bean验证 7 8 2。 配置一个Bean验证实现 注入一个验证器 配置自定义约束 额外的配置选项 7 8 3。 配置DataBinder 7 8 4。 Spring MVC 3验证 触发controller输入验证 配置一个验证器使用Spring MVC 配置一个jsr - 303验证器使用Spring MVC 8。 春天表达式语言(?) 8.1。 介绍 8.2。 特性概述 8.3。 表达式求值的表达式使用Spring接口 夹带了本条件8.3.1。 EvaluationContext接口的 类型转换 8.4。 表达式支持定义bean定义 8 4 1。 基于XML配置 8 4 2。 基于注解的配置 8.5。 语言参考 8.5.1皆是如此。 字面表达式 8.5.2。 属性、数组、列表、地图、索引器 8 5 3。 内联列表 8 5 4。 阵列结构 8 5 5。 方法 8 5 6。 运营商 关系运算符 逻辑运算符 数学运算符 8 5 7。 分配 8 5 8。 类型 8 5 9。 构造函数 8 5 10。 变量 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 5/514 #这和# root变量 8 5 11。 功能 8 5 12。 Bean引用 8 5 13。 三元操作符(是以if - then - else) 8 5 14。 猫王运营商 8 5 15。 安全导航操作符 8 5 16。 集合选择 8 5 17。 收集投影 8 5 18。 表达式模板 8.6。 使用的类的实例 9。 面向方面的编程与弹簧 9.1。 介绍 9 1 1。 AOP概念 9 1 2。 Spring AOP功能和目标 9 1 3。 AOP代理 9.2。 @ aspectj支持 9 2 1。 启用@ aspectj支持 使@ aspectj支持Java配置 使@ aspectj支持使用XML配置 9 2 2。 声明一个方面 9 2 3。 声明一个切入点 支持切入点指示器 结合切入点表达式 分享共同切入点定义 例子 编写好的切入点 9 2 4。 声明建议 建议之前 回国后的建议 在投掷的建议 (最后)后建议 Around通知 建议参数 建议订购 9 2 5。 介绍 9 2 6。 方面的实例化模型 9 2 7。 例子 9.3。 基于AOP支持 如果。 声明一个方面 9 3 2。 声明一个切入点 9 3 3。 声明建议 建议之前 回国后的建议 在投掷的建议 (最后)后建议 Around通知 建议参数 建议订购 9 3 4。 介绍 9 3 5。 方面的实例化模型 9 3 6。 顾问 9 3 7。 例子 9.4。 选择要使用的AOP声明风格 9.4.1。 Spring AOP和AspectJ完整吗? 上装。 @ aspectj或XML为Spring AOP吗? 9.5。 混合方面类型 9.6。 代理机制 9 6 1。 理解AOP代理 9.7。 @ aspectj编程创建代理 9.8。 使用AspectJ和Spring应用程序 9 8 1。 使用AspectJ域对象依赖注入与 春天 单元测试 @Configurable 对象 使用多种应用程序上下文 9 8 2。 其他弹簧方面对AspectJ 9 8 3。 使用Spring IoC配置AspectJ方面 9 8 4。 装入时编织与AspectJ在Spring框架 第一个例子 方面 ” meta - inf / aop xml ” 需要的库(jar) Spring配置 特定于环境的配置 9.9。 进一步的资源 10。 Spring AOP api 10.1。 介绍 10.2。 切入点API在春天 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 6/514 10 2 1。 概念 10 2 2。 操作切入点 10 2 3。 AspectJ切入点表达式 10 2 4。 便利切入点实现 静态切入点 动态切入点 10 2 5。 切入点超类 10 2 6。 定制切入点 10.3。 建议API在春天 10 3 1。 建议生命周期 10 3 2。 建议类型在春天 拦截around通知 建议之前 抛出建议 回国后的建议 介绍的建议 10.4。 顾问API在春天 10.5。 使用ProxyFactoryBean创建AOP代理 10 5 1。 基本 10 5 2。 JavaBean属性 10 5 3。 JDK和cglib建立代理 10 5 4。 代理接口的 10 5 5。 代理类 10 5 6。 使用“全球”顾问 10.6。 简洁代理定义 10.7。 以编程方式创建AOP代理与ProxyFactory 10.8。 操纵建议对象 10.9。 使用“自动代理”设施 10 - 9 - 1。 火狐的一个插件bean定义 BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator AbstractAdvisorAutoProxyCreator 10 9 2。 使用元数据驱动的汽车代理 10.10。 使用TargetSources 10 10 1。 热可切换目标来源 10 10 2。 池目标来源 10 10 3。 原型目标来源 10 10 4。 ThreadLocal 目标来源 10.11。 定义新的 建议 类型 10.12。 进一步的资源 11。 测试 11.1。 介绍弹簧测试 11.2。 单元测试 11 2 1。 模拟对象 环境 JNDI Servlet API Portlet API 11 2 2。 单元测试支持类 通用实用工具 Spring MVC 11.3。 集成测试 11 3 1。 概述 11 3 2。 目标的集成测试 上下文管理和缓存 依赖注入的测试夹具 事务管理 支持类的集成测试 11 3 3。 JDBC测试支持 11 3 4。 注释 弹簧测试注释 标准注释支持 弹簧JUnit测试注释 11 3 5。 春天还是和TestContext框架 关键抽象 上下文管理 依赖注入的测试夹具 测试请求和会话作用域的豆子 事务管理 还是和TestContext框架支持类 11 3 6。 Spring MVC的测试框架 服务器端测试 端REST测试 11 3 7。 宠物诊所的例子 11.4。 进一步的资源 四。数据访问 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 7/514 12。 事务管理 12.1。 介绍Spring框架事务管理 12.2。 优势的Spring框架的事务支持模型 12 2 1。 全局事务 12 2 2。 本地事务 12 2 3。 Spring框架是一致的编程模型 12.3。 了解Spring框架事务抽象 12.4。 同步资源交易 12 4 1。 高级同步方法 12 4 2。 低级同步方法 12 4 3。 TransactionAwareDataSourceProxy 12.5。 声明式事务管理 12 5 1。 了解Spring框架的声明性事务 实现 12 5 2。 声明式事务实现的例子 12 5 3。 一个声明式事务回滚 12 5 4。 配置不同的事务性语义不同 bean 12 5 5。 < tx:建议/ > 设置 12 5 6。 使用 transactional transactional 设置 多个事务经理与 transactional 自定义快捷注释 12 5 7。 事务传播 需要 RequiresNew 嵌套 12 5 8。 建议事务性操作 12 5 9。 使用 transactional 与 AspectJ 12.6。 编程式事务管理 12 6 1。 使用 TransactionTemplate 指定事务设置 12 6 2。 使用 PlatformTransactionManager 12.7。 选择编程和声明式事务 管理 12.8。 特定于应用服务器的集成 12 8 1。 IBM WebSphere 12 8 2。 BEA WebLogic服务器 12 8 3。 甲骨文OC4J 12.9。 解决常见问题 12 - 9 - 1。 使用错误的事务管理器为一个特定的 数据源 12.10。 进一步的资源 13。 DAO支持 13.1。 介绍 13.2。 一致的异常层次结构 13.3。 注释用于配置刀或存储库类 14。 数据访问与JDBC 14.1。 介绍Spring框架JDBC 14 1 1。 选择一个JDBC数据库访问方法 14 1 2。 包的层次结构 14.2。 使用JDBC核心类来控制基本JDBC加工 错误处理 14 2 1。 JdbcTemplate JdbcTemplate类的例子使用 JdbcTemplate 最佳实践 14 2 2。 NamedParameterJdbcTemplate 14 2 3。 SQLExceptionTranslator 14 2 4。 执行语句 14 2 5。 运行查询 14 2 6。 更新数据库 14 2 7。 检索自动生成的键 14.3。 控制数据库连接 14 3 1。 数据源 14 3 2。 DataSourceUtils 14 3 3。 SmartDataSource 14 3 4。 AbstractDataSource 14 3 5。 SingleConnectionDataSource 14 3 6。 DriverManagerDataSource 14 3 7。 TransactionAwareDataSourceProxy 14 3 8。 DataSourceTransactionManager 14 3 9。 NativeJdbcExtractor 14.4。 JDBC批处理操作 14 4 1。 基本批处理操作与JdbcTemplate 14 4 2。 批处理操作与对象的列表 14 4 3。 与多个批次的批处理操作 14.5。 简化JDBC操作与SimpleJdbc类 14 5 1。 插入数据使用SimpleJdbcInsert 14 5 2。 自动生成的键SimpleJdbcInsert检索使用 14 5 3。 指定一个SimpleJdbcInsert列 14 5 4。 使用SqlParameterSource提供参数值 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 8/514 14 5 5。 与SimpleJdbcCall调用一个存储过程 14 5 6。 显式地声明参数来使用 SimpleJdbcCall 14 5 7。 如何定义SqlParameters 14 5 8。 使用SimpleJdbcCall调用一个存储功能 14 5 9。 返回结果集/ REF光标从一个SimpleJdbcCall 14.6。 作为Java对象建模JDBC操作 14 6 1。 SqlQuery 14 6 2。 MappingSqlQuery 14 6 3。 SqlUpdate 14 6 4。 StoredProcedure 14.7。 常见问题与参数和数据值处理 14 7 1。 提供SQL类型信息参数 14 7 2。 处理BLOB和CLOB对象 14 7 3。 传入的值列表的条款 14 7 4。 处理复杂类型为存储过程调用 14.8。 嵌入式数据库支持 14 8 1。 为什么使用嵌入式数据库吗? 14 8 2。 创建一个嵌入式数据库实例使用Spring的XML 14 8 3。 以编程方式创建一个嵌入式数据库实例 14 8 4。 扩展嵌入式数据库支持 14 8 5。 使用HSQL 14 8 6。 使用H2 14 8 7。 使用Derby 14 8 8。 测试数据访问逻辑与嵌入式数据库 14.9。 初始化数据源 14 - 9 - 1。 初始化数据库实例使用Spring的XML 初始化的其他组件的依赖 数据库 15。 对象关系映射(ORM)数据访问 15.1。 介绍ORM和春天 15.2。 一般ORM集成考虑 15 2 1。 资源和事务管理 15 2 2。 异常翻译 15.3。 hibernate 15 3 1。 SessionFactory 设置在一个春天 容器 15 3 2。 实现DAOs基于普通Hibernate 3 API 15 3 3。 声明式事务划分 15 3 4。 程序性事务界定 15 3 5。 事务管理策略 15 3 6。 比较本地定义的容器管理和资源 15 3 7。 虚假的应用服务器与Hibernate的警告 15.4。 JDO 15 4 1。 PersistenceManagerFactory 设置 15 4 2。 实现DAOs基于JDO API的平原 15 4 3。 事务管理 15 4 4。 JdoDialect 15.5。 JPA 15 5 1。 三个选项设置在一个春天的JPA环境 LocalEntityManagerFactoryBean 获得一个 会 从 JNDI LocalContainerEntityManagerFactoryBean 处理多个持久性单元 15 5 2。 实现DAOs基于普通JPA 15 5 3。 事务管理 15 5 4。 JpaDialect 15.6。 iBATIS SQL映射 15 6 1。 设置 SqlMapClient 15.6.2。 使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport 15 6 3。 实现DAOs基于普通iBATIS API 16。 编组XML使用O / X映射器 16.1。 介绍 16.2。 Marshaller和解组程序 16 2 1。 Marshaller 16 2 2。 解组程序 16 2 3。 XmlMappingException 16.3。 使用信号员和解组程序 16.4。 XML的基于配置 16.5。 JAXB 16 5 1。 Jaxb2Marshaller XML的基于配置 16.6。 蓖麻 16 6 1。 使用CastorMarshaller 16 6 2。 映射 XML的基于配置 16.7。 XMLBeans 16 7 1。 XmlBeansMarshaller XML的基于配置 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 9/514 16.8。 JiBX 16 8 1。 JibxMarshaller XML的基于配置 16.9。 XStream 16 9 1。 XStreamMarshaller 诉网络 17。 Web MVC框架 17.1。 介绍Spring Web MVC框架 17 1 1。 Spring Web MVC的特点 17 1 2。 可插入性的MVC实现 17.2。 这个 DispatcherServlet 17 2 1。 特殊的Bean类型 WebApplicationContext 17 2 2。 DispatcherServlet默认配置 17 2 3。 DispatcherServlet处理顺序 17.3。 实现控制器 17 3 1。 定义一个控制器, controller 17 3 2。 映射请求 @RequestMapping 新的支持类 @RequestMapping 方法在Spring MVC 3.1 URI模板模式 URI模板用正则表达式模式 路径模式 模式包含占位符 矩阵变量 消耗品媒体类型 可生产的媒体类型 请求参数和头的值 17 3 3。 定义 @RequestMapping 处理程序 方法 支持方法参数类型 支持方法返回类型 绑定请求参数方法参数 @RequestParam 映射与@RequestBody请求主体 注释 映射响应的身体 @ResponseBody 注释 使用 HttpEntity < ? > 使用 @ModelAttribute 在一个 方法 使用 @ModelAttribute 在一个 方法参数 使用 @SessionAttributes 存储模型 属性在HTTP会话请求之间 重定向和flash属性指定 处理 “应用程序/ x-www-form-urlencoded” 数据 映射cookie的值与@CookieValue注释 映射属性与@RequestHeader请求头 注释 方法参数和类型转换 定制 WebDataBinder 初始化 支持“last - modified”响应头方便 内容缓存 17 3 4。 异步请求处理 异常处理异步请求 拦截异步请求 配置为异步请求处理 17 3 5。 测试控制器 17.4。 处理程序映射 17日4 1。 拦截请求 HandlerInterceptor 17.5。 解决意见 17 5 1。 解决视图和 ViewResolver 接口 17 5 2。 链接ViewResolvers 17 5 3。 重定向到视图 RedirectView 这个 重定向: 前缀 这个 转发: 前缀 17 5 4。 ContentNegotiatingViewResolver 17.6。 使用flash属性 17.7。 建筑 URI 年代 17.8。 使用场所 17 8 1。 AcceptHeaderLocaleResolver 17 8 2。 CookieLocaleResolver 17 8 3。 SessionLocaleResolver 17 8 4。 LocaleChangeInterceptor 17.9。 使用主题 17 9 1。 概述主题 17 9 2。 定义主题 17 9 3。 主题解析器 17.10。 春天的多部分(文件上传)支持 17 10 1。 介绍 17 10 2。 使用 MultipartResolver 与 Commons FileUpload 17 10 3。 使用 MultipartResolver 与 Servlet 3.0 17 10 4。 处理一个文件上传表单中 17 10 5。 处理一个文件上传请求从编程的客户 17.11。 处理异常 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 10/514 17日11 1。 HandlerExceptionResolver 17日11 2。 @ExceptionHandler 17日11 3。 处理标准的Spring MVC例外 17日11 4。 注释业务异常与 @ResponseStatus 17日11 5。 自定义默认Servlet容器的错误页面 17.12。 约定优于配置支持 17 12 1。 控制器 ControllerClassNameHandlerMapping 17 12 2。 该模型 ModelMap ( ModelAndView ) 17 12 3。 视图- RequestToViewNameTranslator 17.13。 ETag支持 17.14。 基于代码的Servlet容器初始化 17.15。 配置Spring MVC 17日15 1。 启用MVC Java配置或MVC XML名称空间 17日15 2。 定制提供的配置 17日15 3。 配置拦截器 17日15 4。 配置内容协商 17日15 5。 配置视图控制器 17日15 6。 配置服务资源 17日15 7。 mvc:默认servlet处理程序 17日15 8。 更多的Spring Web MVC资源 17日15 9。 高级定制与MVC Java配置 17日15 10。 高级定制与MVC名称空间 18。 视图技术 18.1。 介绍 18.2。 JSP & JSTL 18 2 1。 视图解析器 18 2 2。 “普通的jsp和JSTL 18 2 3。 额外的标签促进发展 18 2 4。 使用Spring标记库的形式 配置 这个 形式 标签 这个 输入 标签 这个 复选框 标签 这个 复选框 标签 这个 radiobutton 标签 这个 radiobuttons 标签 这个 密码 标签 这个 选择 标签 这个 选项 标签 这个 选项 标签 这个 Textarea 标签 这个 隐藏 标签 这个 错误 标签 HTTP方法转换 HTML5标签 18.3。 瓷砖 18 3 1。 依赖性 18 3 2。 如何将瓷砖 UrlBasedViewResolver ResourceBundleViewResolver SimpleSpringPreparerFactory 和 SpringBeanPreparerFactory 18.4。 速度& FreeMarker 18 4 1。 依赖性 18 4 2。 上下文配置 18 4 3。 创建模板 18 4 4。 高级配置 速度属性 freemarker 18 4 5。 绑定支持和形式处理 bind宏 简单绑定 表单输入生成宏 HTML转义和XHTML合规 18.5。 XSLT 18 5 1。 我的第一个单词 Bean定义 标准MVC控制器代码 模型数据转换为XML 定义视图属性 文档转换 18 5 2。 总结 18.6。 文档视图(PDF / Excel) 18 6 1。 介绍 18 6 2。 配置和设置 文档视图定义 控制器代码 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 11/514 子类化Excel的观点 子类化对于PDF的观点 18.7。 jasperreports 18 7 1。 依赖性 18 7 2。 配置 配置 ViewResolver 配置 视图 年代 关于报告文件 使用 JasperReportsMultiFormatView 18 7 3。 填充 ModelAndView 18 7 4。 处理子报告 配置子报告文件 配置子报告数据来源 18 7 5。 出口国参数配置 18.8。 饲料的观点 18.9。 XML编组视图 18.10。 JSON映射视图 19。 与其他web框架集成 19.1。 介绍 19.2。 常见的配置 19.3。 JavaServer Faces 1.1和1.2 19 3 1。 DelegatingVariableResolver(JSF 1.1/1.2) 19 3 2。 SpringBeanVariableResolver(JSF 1.1/1.2) 19 3 3。 SpringBeanFacesELResolver(JSF 1.2 +) 19 3 4。 FacesContextUtils 19.4。 Apache Struts 1。 倍和2.倍 19日4 1。 ContextLoaderPlugin DelegatingRequestProcessor DelegatingActionProxy 19日4 2。 ActionSupport类 19.5。 网络系统2. x 19.6。 Tapestry 3。 倍和4.倍 19日6 1。 注入spring管理bean 依赖注入Spring bean到Tapestry页面 组件定义文件 添加抽象访问器 依赖注入Spring bean到Tapestry页面- Tapestry 4。 x风格 19.7。 进一步的资源 20。 Portlet MVC框架 20.1。 介绍 20 1 1。 控制器- C在MVC 20 1 2。 视图- V在MVC 20 1 3。 web范围bean 20.2。 这个 DispatcherPortlet 20.3。 这个 ViewRendererServlet 20.4。 控制器 20 4 1。 基类AbstractController 和 PortletContentGenerator 20 4 2。 其他简单的控制器 20 4 3。 命令控制器 20 4 4。 PortletWrappingController 20.5。 处理程序映射 20 5 1。 PortletModeHandlerMapping 20 5 2。 ParameterHandlerMapping 20 5 3。 PortletModeParameterHandlerMapping 20 5 4。 添加 HandlerInterceptor 年代 20 5 5。 HandlerInterceptorAdapter 20 5 6。 ParameterMappingInterceptor 20.6。 观点和解决他们 20.7。 多部分(文件上传)支持 20 7 1。 使用 PortletMultipartResolver 20 7 2。 处理一个文件上传表单中 20.8。 处理异常 20.9。 基于注解的控制器配置 20 - 9 - 1。 设置调度程序注释支持 20 9 2。 定义一个控制器, controller 20 9 3。 映射请求 @RequestMapping 20 9 4。 支持处理程序方法参数 20 9 5。 绑定请求参数方法参数 @RequestParam 20 9 6。 提供一个链接的数据模型 @ModelAttribute 20 9 7。 指定属性存储在会话 @SessionAttributes 20 9 8。 定制 WebDataBinder 初始化 定制数据绑定和 @InitBinder 配置一个自定义 WebBindingInitializer 20.10。 Portlet应用程序部署 VI。集成 21。 使用Spring Remoting和web服务 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 12/514 21.1。 介绍 21.2。 暴露服务使用RMI 21 2 1。 出口服务使用 RmiServiceExporter 21 2 2。 连接的服务在客户端 21.3。 使用麻绳或麻袋远程调用服务通过HTTP 21日3 1。 布线的 DispatcherServlet 对于 黑森和co。 21日3 2。 暴露你的bean使用 HessianServiceExporter 21日3 3。 连接的服务在客户端 21日3 4。 用粗麻布 21日3 5。 应用HTTP基本身份验证服务暴露通过 黑森或麻袋 21.4。 使用HTTP调用程序的公开服务 21 4 1。 公开服务对象 21 4 2。 连接的服务在客户端 21.5。 Web服务 21 5 1。 基于servlet的web服务公开使用jax - rpc 21 5 2。 访问web服务使用jax - rpc 21 5 3。 注册jax - rpc Bean映射 21 5 4。 注册你自己的jax - rpc处理程序 21 5 5。 基于servlet的web服务公开使用jax - ws 21 5 6。 出口独立的web服务使用jax - ws 21 5 7。 导出web服务使用jax - ws国际扶轮的春天 支持 21 5 8。 访问web服务使用jax - ws 21.6。 jms 21日6 1。 服务器端配置 21日6 2。 客户端配置 21.7。 还没有实现自动识别为远程接口 21.8。 考虑当选择技术 21.9。 在客户端访问RESTful服务 21 - 9 - 1。 RestTemplate 使用URI 处理请求和响应头 21日9 2。 HTTP消息转换 StringHttpMessageConverter FormHttpMessageConverter ByteArrayHttpMessageConverter MarshallingHttpMessageConverter MappingJackson2HttpMessageConverter(或MappingJacksonHttpMessageConverter与杰克逊1. x) SourceHttpMessageConverter BufferedImageHttpMessageConverter 22。 Enterprise JavaBeans(EJB)集成 22.1。 介绍 22.2。 ejb访问 22日2 1。 概念 22日2 2。 访问当地SLSBs 22日2 3。 访问远程SLSBs 22日2 4。 访问EJB 2。 x SLSBs与EJB 3 SLSBs 22.3。 使用Spring的EJB实现支持类 22 3 1。 EJB 2。 x基类 22 3 2。 EJB 3注入拦截器 23。 JMS(Java消息服务) 23.1。 介绍 23.2。 使用Spring JMS 23 2 1。 JmsTemplate 23 2 2。 连接 缓存消息传递资源 SingleConnectionFactory CachingConnectionFactory 23 2 3。 目的地管理 23 2 4。 消息侦听器容器 SimpleMessageListenerContainer DefaultMessageListenerContainer 23 2 5。 事务管理 23.3。 发送 消息 23日3 1。 使用消息转换器 23日3 2。 SessionCallback 和 ProducerCallback 23.4。 接收消息 23日4 1。 同步接收 23日4 2。 异步接收消息驱动pojo, 23日4 3。 这个 SessionAwareMessageListener 接口 23日4 4。 这个 MessageListenerAdapter 23日4 5。 在事务处理消息 23.5。 支持JCA消息端点 23.6。 JMS名称空间支持 24。 JMX 24.1。 介绍 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 13/514 24.2。 出口你的豆子JMX 24 2 1。 创建一个 MBeanServer 24 2 2。 重用现有的 MBeanServer 24 2 3。 延迟初始化的mbean 24 2 4。 自动注册mbean 24 2 5。 控制登记行为 24.3。 控制管理接口的豆子 24 3 1。 这个 MBeanInfoAssembler 接口 24 3 2。 使用源代码级别的元数据(JDK 5.0注释) 24 3 3。 源代码级别的元数据类型 24 3 4。 这个 AutodetectCapableMBeanInfoAssembler 接口 24 3 5。 使用Java接口定义管理接口 24 3 6。 使用 MethodNameBasedMBeanInfoAssembler 24.4。 控制 ObjectName 年代为你豆 24 4 1。 阅读 ObjectName 年代从 属性 24 4 2。 使用 MetadataNamingStrategy 24 4 3。 配置基于注解的MBean的出口 24.5。 jsr - 160连接器 24日5 1。 端连接器 24日5 2。 端连接器 24日5 3。 JMX在麻袋/黑森/ SOAP 24.6。 通过代理访问mbean 24.7。 通知 24 7 1。 通知监听器注册 24 7 2。 发布通知 24.8。 进一步的资源 25。 JCA CCI 25.1。 介绍 25.2。 配置CCI 25 2 1。 连接器配置 25 2 2。 ConnectionFactory 配置在 春天 25 2 3。 配置CCI连接 25 2 4。 使用单一CCI连接 25.3。 使用Spring的CCI访问支持 25 3 1。 记录转换 25 3 2。 这个 CciTemplate 25 3 3。 DAO支持 25 3 4。 自动输出记录生成 25 3 5。 总结 25 3 6。 使用CCI 连接 和 交互 直接 25 3 7。 例子 CciTemplate 使用 25.4。 作为操作对象建模CCI访问 25 4 1。 MappingRecordOperation 25 4 2。 MappingCommAreaOperation 25 4 3。 自动输出记录生成 25 4 4。 总结 25 4 5。 例子 MappingRecordOperation 使用 25 4 6。 例子 MappingCommAreaOperation 使用 25.5。 交易 26。 邮件 26.1。 介绍 26.2。 使用 26日2 1。 基本 MailSender 和 SimpleMailMessage 使用 26日2 2。 使用 JavaMailSender 和 MimeMessagePreparator 26.3。 使用JavaMail MimeMessageHelper 26日3 1。 发送附件和内联资源 附件 内联资源 26日3 2。 创建电子邮件内容使用模板库 一个速度的基础例子 27。 任务执行和调度 27.1。 介绍 27.2。 春天 TaskExecutor 抽象 27 2 1。 TaskExecutor 类型 27 2 2。 使用 TaskExecutor 27.3。 春天 TaskScheduler 抽象 27日3 1。 这个 触发 接口 27日3 2。 触发 实现 27日3 3。 TaskScheduler 实现 27.4。 注释支持调度和异步 执行 27日4 1。 使调度注释 27日4 2。 @Scheduled注释的 27日4 3。 @Async注释的 27日4 4。 遗嘱执行人资格与@Async 27.5。 任务名称空间 27日5 1。 “调度”元素 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 14/514 27日5 2。 “执行人”元素 27日5 3。 “计划任务”元素 27.6。 使用石英调度器 27 6 1。 使用JobDetailBean 27 6 2。 使用 MethodInvokingJobDetailFactoryBean 27 6 3。 连接使用触发和工作 SchedulerFactoryBean 28。 动态语言支持 28.1。 介绍 28.2。 第一个例子 28.3。 定义bean,动态语言支持 28日3 1。 常见的概念 这个 < lang:语言/ > 元素 可刷新的豆子 内联动态语言源文件 理解构造函数注入上下文中的动态语言支持bean 28日3 2。 JRuby bean 28日3 3。 Groovy bean 通过回调Groovy对象的定制 28日3 4。 BeanShell bean 28.4。 场景 28 4 1。 照本宣科的Spring MVC控制器 28 4 2。 脚本验证器 28.5。 零零碎碎 28 5 1。 AOP——建议脚本bean 28 5 2。 范围 28.6。 进一步的资源 29。 缓存抽象 29.1。 介绍 29.2。 理解抽象的缓存 29.3。 声明基于注解的缓存 29日3 1。 @Cacheable 注释 缺省密钥生成 自定义键生成申报 条件缓存 可用缓存 ? 评估上下文 29日3 2。 @CachePut 注释 29日3 3。 @CacheEvict 注释 29日3 4。 @Caching 注释 29日3 5。 启用缓存注释 29日3 6。 使用自定义注释 29.4。 声明性xml缓存 29.5。 配置缓存存储 29日5 1。 JDK ConcurrentMap 的 缓存 29日5 2。 ehcache基于 缓存 29日5 3。 gemfire基于 缓存 29日5 4。 处理缓存没有后备存储器 29.6。 插入不同的后端缓存 29.7。 我如何设置TTL /创科实业/驱逐政策/ XXX特性? 七世。 附录 a .经典弹簧的使用 背书的。 经典ORM使用 一个1 1。 hibernate 这个 hibernatetemplate 实现基于spring dao没有回调 一个1 2。 JDO JdoTemplate 和 JdoDaoSupport 一个1 3。 JPA JpaTemplate 和 JpaDaoSupport a。 经典Spring MVC 由。 JMS使用 一个3 1。 JmsTemplate 一个3 2。 异步消息接收 一个3 3。 连接 3 4。 事务管理 b .经典Spring AOP使用 责任。 切入点API在春天 b 1 1。 概念 b 1 2。 操作切入点 b 1 3。 AspectJ切入点表达式 实施了。 便利切入点实现 静态切入点 动态切入点 b 1 5。 切入点超类 b 1 6。 定制切入点 b 2。 建议API在春天 b 2 1。 建议生命周期 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 15/514 b 2 2。 建议类型在春天 拦截around通知 建议之前 抛出建议 回国后的建议 介绍的建议 b 3。 顾问API在春天 b 4。 使用ProxyFactoryBean创建AOP代理 b 4 1。 基本 b 4 2。 JavaBean属性 b 4 3。 JDK和cglib建立代理 b 4 4。 代理接口的 b 4 5。 代理类 b 4 6。 使用“全球”顾问 b 5。 简洁代理定义 b 6。 以编程方式创建AOP代理与ProxyFactory b 7。 操纵建议对象 b 8。 使用“火狐的一个插件”设施 b 8 1。 火狐的一个插件bean定义 BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator AbstractAdvisorAutoProxyCreator b 8 2。 使用元数据驱动的汽车代理 b 9。 使用TargetSources b 9 1。 热可切换目标来源 b 9 2。 池目标来源 b 9 3。 原型目标来源 b 9 4。 ThreadLocal 目标来源 b 10。 定义新的 建议 类型 b 11。 进一步的资源 c .迁移到Spring Framework 3.1 c 1。 组件扫描对“org”基础包 d .迁移到Spring Framework 3.2 d 1。 新可选依赖 d 2。 EHCache支持搬到spring上下文的支持 d 3。 内联的弹簧asm jar d 4。 明确CGLIB依赖不再需要 d 5。 对OSGi用户 d 6。 MVC Java配置和MVC的名称空间 d 7。 解码的URI变量值 d 8。 HTTP补丁方法 d 9。 瓷砖3 d 10。 Spring MVC测试独立项目 d 11。 弹簧测试依赖关系 d 12。 公共API的变化 d 12 1。 JDiff报告 d 12 2。 随着 e . XML的基于配置 e 1。 介绍 e 2。 XML的基于配置 e 2 1。 引用模式 e 2 2。 这个 util 模式 < util:常数/ > < util:属性路径/ > < util:属性/ > < util:列表/ > < util:地图/ > < util:设置/ > e 2 3。 这个 JEE 模式 < jee:jndi查找/ > (简单的) < jee:jndi查找/ > (与单JNDI环境设置) < jee:jndi查找/ > (有多个JNDI环境设置) < jee:jndi查找/ > (复杂的) < jee:local-slsb / > (简单的) < jee:local-slsb / > (复杂的) < jee:remote-slsb / > e 2 4。 这个 朗 模式 e 2 5。 这个 jms 模式 e 2 6。 这个 TX (事务)模式 e 2 7。 这个 aop 模式 e 2 8。 这个 上下文 模式 <属性占位符/ > <注释配置/ > <组件扫描/ > <加载时间韦弗/ > < spring配置/ > 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 16/514 < mbean出口/ > e 2 9。 这个 工具 模式 e 2 10。 这个 JDBC 模式 e 2 11。 这个 缓存 模式 e 2 12。 这个 bean 模式 f .可扩展的XML编写 f 1。 介绍 f 2。 创作模式 f 3。 编码一个 NamespaceHandler 中。 编码一个 BeanDefinitionParser 四。 注册处理程序和模式 f 5 1。 “meta - inf / spring处理程序” f 5 2。 “meta - inf / spring模式” f 6。 使用一个自定义的扩展在Spring的XML配置 有为。 丰满的例子 f 7 1。 自定义标签内嵌套自定义标记 f 7 2。 自定义属性在“正常”的元素 f 8。 进一步的资源 g .弹簧tld g 1。 介绍 g 2。 这个 绑定 标签 g 3。 这个 escapeBody 标签 g 4。 这个 hasBindErrors 标签 g 5。 这个 htmlEscape 标签 g 6。 这个 消息 标签 g 7。 这个 nestedPath 标签 g 8。 这个 主题 标签 g 9。 这个 变换 标签 g 10。 这个 url 标签 g 11。 这个 eval 标签 h .弹簧形式tld 1。 介绍 h 2。 这个 复选框 标签 h 3。 这个 复选框 标签 h 4。 这个 错误 标签 h 5。 这个 形式 标签 h 6。 这个 隐藏 标签 h 7。 这个 输入 标签 h 8。 这个 标签 标签 h 9。 这个 选项 标签 h 10。 这个 选项 标签 h 11。 这个 密码 标签 h 12。 这个 radiobutton 标签 h 13。 这个 radiobuttons 标签 h 14。 这个 选择 标签 h 15。 这个 Textarea 标签 PartA我。 Spring框架的概况 Spring框架是一个轻量级的解决方案和一个潜在的 一站式为构建企业级应用程序。 然而, 春天是模块化的,允许你只使用那些 你需要的部分, 无需引入其余。 您可以使用IoC容器, Struts在上,但是你也可以只使用 Hibernate集成代码 或 JDBC抽象层 。 春天 框架支持声明式事务管理、远程访问 你的逻辑通过RMI或web服务,以及各种选项 坚持你的数据。 它提供了一个功能全 面的 MVC框架 ,并允许您 整合 aop 透明地进 你的软件。 春天是设计为非侵入性的,这意味着你的域 逻辑代码通常没有依赖框架本身。 在 你的集成层(比如数据访问层),一些 依赖于数据 访问技术和Spring库将 存在。 然而,它应该很容易隔离这些依赖项的 其余的代码库。 这个文档是一个参考指南,Spring框架功能。 如果你有任何请求,评论,或问题在这个文件, 请张贴在用户邮件列表或支持论坛 http://forum.springsource.org/ 。 1。 一个介绍Spring框架 Spring框架是一个Java平台,提供全面的 基础设施支持开发Java应用程序。 弹簧处理 基础设施可以让你专注于你的应用程序。 春天使您能够构建应用程序从一个​​普通旧式Java objectsa​​ (pojo)和应用企业服务非侵入性的方法来pojo。 这 功能适用于Java SE的编程模型和完全和部分 Java EE。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 17/514 背景 一个​​ 问题是,什么方面的控制 [他们]反相吗? 一 个​​ 马丁提出一个问题 关于控制反转(IoC)于 2004年在他的网站。 福勒建议 重命名原则,让 它更加不言自明,想出了 依赖注入 。 为深入了解国际奥委会和DI,指福勒的文章中 http://martinfowler.com/articles/injection.html 。 你的例子,作为应用程序开发人员,可以使用弹簧 平台优势: 将一个Java方法中执行数据库事务没有 必须处理事务api。 让一个本地Java方法远程过程无需交易 与远程api。 让一个本地Java方法无需管理操作 处理JMX api。 让一个本地Java方法一个消息处理程序而无需交易 与JMS api。 1.1一个依赖注入和控制反转 Java应用程序——一个松散的术语,历经了从运行 限制到n层端企业应用程 序, 通常由对象来协作形式应用 适当的。 因此,在一个应用程序对象有 依 赖性 在每个其他。 尽管Java平台提供了一个丰富的应用程序 开发功能,它缺乏意味着组织基础 构建块成一个连贯的整体,留下这任务架构师和 开发人员。 真的,你可以使 用设计模式如 工厂 , 抽象工厂 , 建设者 , 装饰 ,和 服务定位器 撰写各种类 和 对象实例的应用程序。 然而,这些模式 只是简单的说:最佳实践提供了一 个名称,描述的是什么 模式确实,应用,问题,地址等等。 模式是形式化的最 佳实践 你必须实现 自己 在您的应用程序。 Spring框架 控制反转 (IoC) 组件的解决了这个问题通过提供一种形式化的方法 不同的组件组合成一个完整的工作程序准备好 使用。 这个 Spring框架概括形式化的设计模式是一流的 对象,可以整合到您自己的应用程序(年代)。 无数 组织和机构使用 Spring框架在这种方式 工程师健壮, 维护 应用程序。 1.2一个模块 Spring框架是由功能组织成大约20 模块。 这些模块被分组到核心容器、数据 访问/集成、网络、AOP(面向方面编程), 仪器仪 表和测试,见下图。 Spring框架的概述 1.2.1A核心容器 这个 核心 容器 由核心、豆类、上下文和 表达式语言模块。 这个 核心和 bean 模块提供的基本部分 框架,包括国际奥委会和依赖注入特性。 这个 BeanFactory 是一个复杂的实现的 工厂 模式。 它使得不需要编程单件和 允许您配置和规范的解耦 从你的实际程序逻辑依赖关系。 这个 上下文 模块构建在提供的坚实的基础 核心和豆类 模块:它是一种手段来访问对象在一个框架风格的方式 这是类似于一个 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 18/514 JNDI注册表。 上下文模块继承它 从bean模块功能并添加国际化支持 (例如,使用资源包)、事件传播, 资源加载,透明,创建上下 文 例子,一个servlet容器。 上下文模块还支持Java EE 功能,如EJB,JMX,和基本的远程控制。 这个 ApplicationContext 接口是 焦点 上下文的模块。 这个 表达式 语言 模块提供 一个强大的表达式语言查询和操作一个对象 图在运行时。 它是一个扩展的统一表达式语言 (统一 EL)按JSP 2.1规范。 语言 支持设置和获取属性值,属性赋值, 方法调用上下文的访问数组、集合和 索引器、逻辑、算术运算符、 命名变量,和 检索对象的名字从Spring的IoC容器。 它还 支持列表投影和选择以及常见的列表 聚合。 1.2.2A数据访问/集成 这个 数据访问/集成 层由 JDBC,ORM,OXM、JMS和事务模块。 这个 JDBC 模块提供 一个JDBC抽象层,消除了需要做乏味的JDBC代码 和解析数据库供应商的特定错误代码。 这个 orm 模块 提供集成层为流行的对象-关系映射api, 包括 JPA , JDO , hibernate ,和 iBatis 。 使用ORM包可以使用 所有这 些框架的O / r映射结合所有其他的 功能弹簧提供,如简单的声明性事务 前面提到的管理特性。 这个 OXM 模块提供了一个抽象 层,支持对象/ XML映射实现JAXB,Castor, XMLBeans,JiBX和XStream。 Java消息传递服务( jms )模块 包含功能,为生产和消费信息。 这个 事务 模块支持 编程式和声明式事务管理类 实现特定的接口和 你所有的pojo(传统 Java对象) 。 1.2.3A网络 这个 web 层由网络, web servlet、web struts,portlet的web模块。 春天的 web 模块提供了基本 以网络为中心的集成特性,比如多部分文件上传 功能和初始化的IoC容器使用servlet 听众和一个 面向web的应用程序上下文。 它还包含了 Spring的web部件远程支持。 这个 web servlet 模块包含弹簧的 模型-视图-控制器( MVC ) 实现为web应用程序。 Spring的MVC框架提供了一个 清洁分离 域模型代码和web表单,并整合 与所有的其他功能,Spring框架。 这个 web struts 模块包含支持 类为集成一个经典的Struts web层在一个春天 应用程序。 注意,这个支持现在是春天的废弃 3.0。 考虑迁移应用程序到Struts 2.0和它的春天 集成或Spring MVC的解决方案。 这个 web portlet 模块提供了MVC 实现用于portlet环境和反映 web servlet模块的功能。 1.2.4A AOP和仪表 春天的 aop 模块 提供了一个 AOP联盟 兼容的面向方面的 编程实现允许您定义,例如, 方法拦截器和切入点来干净代码解耦 实 现了功能,应该分开。 使用源代码级别的 元数据功能,您还可以将行为信息 到你的代码,在相似的方式。 净的属性。 单独的 方面 模块提供 整合与AspectJ。 这个 仪表 模块提供类 仪表支持和类加载器的实现中使用 某些应用程序服务器。 1.2.5A测试 这个 测试 模块支持的测试 弹簧组件使用JUnit和TestNG。 它提供了一致的加载 春天ApplicationContexts和缓存的上下文。 它还 为模拟对象,您可以使用它来测试你的代码 隔离。 1.3一个使用场景 前面描述的构建块使弹簧一个逻辑 选择在许多场景中,从applet向成熟的企业 应用程序使用Spring的事务管理功能和 web框架 集成。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 19/514 典型成熟的Spring web 应用 春天的 声明 事务管理功能 使web应用程序完全 事务,就像它是如果你使用EJB容器管理的 事务。 你所有的自定义业务逻辑可 以实现 简单的pojo和管理的Spring的IoC容器。 额外的服务 包括支持发送电子邮件和验证,是独立的 web层,它可以让你选择 在哪里执行验证规则。 春天的ORM支持是集成了JPA,Hibernate,JDO和iBatis; 例如,当使用Hibernate,您可以继续使用您现有 的 Hibernate映射文件和标准 SessionFactory 配置。 形式 控制器无缝集成的web层与域模型, 删除需要 actionform 或其他 类 HTTP参数值转换为你的域模型。 春天中间层使用第三方web 框架 有时情况不允许你完全切换到一个 不同的框架。 Spring框架并 不 强迫你使用一切在它,它不是一个 孤注一掷的 解决方案。 现有前端建立 与网络系统、Struts、挂毯、或其他UI框架可以集成 与一个基于Spring的中间层,它允许您使用弹簧 事务特性。 你只需要连接您的业务逻辑使用 一个 ApplicationContext 和使用 WebApplicationContext 整合您的web 层。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 20/514 Remoting使用场景 当你需要通过web服务访问现有的代码,你可以 使用Spring的 黑森- , 麻袋, , Rmi - 或 JaxRpcProxyFactory 类。 启用远程访 问现有的应用程序不是 困难的。 ejb -包装现有的pojo Spring框架还提供了一个 访问和 抽象层 为Enterprise JavaBeans,使您可以重用 你现有的pojo并包装在使用无状态会话bean 可伸缩的,故障安全的web应用程序可能需要声明 安全。 1.3.1A依赖管理和命名约定 依赖关系管理和依赖注入是不同的 的事情。 让那些漂亮的春天的特征到您的应用程序(如 依赖注入)你需要组装所有的图书馆 需要(jar 文件),让他们到您在运行时类路径中,并可能在 编译时间。 这些依赖关系没有虚拟组件 注入,但物理资源在一个文件系 统(通常)。 这个 过程的依赖关系管理涉及到定位这些资源, 存储它们并将它们添加到类路径。 依赖关系可以直接 (如我的应用 程序依赖于Spring运行时)或间接(如我 应用取决于 commons-dbcp 取决于 共享池 )。 间接依赖关系也被称为 “传递”,这是 那些依赖关系难以确定 和管理。 如果你打算使用弹簧你需要得到一个jar的副本 库组成件弹簧,你需要。 为了让这个 春天是容易打包为一组模块分开 尽可能多 的依赖关系,例如如果你不想 编写一个web应用程序中,您不需要spring web模块。 参考 春天在本指南库模块我们使用速记命 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 21/514 名 公约 弹簧- * 或 春天- * . jar, 其中“*” 代表了模块的短名称(如。 spring核心 , spring-webmvc , spring jms 等等)。 实 际的 jar文件的名字,你可能会以这种形式使用(见下文)或者它可能 不,通常它也有一个版本号在文件名称(如。 spring核心3 0 0 释放罐 )。 一般来说,春天发布了4个不同的工件 的地方: 在社区下载站点 http://www.springsource.org/download/community 。 在这里你找到所有的弹簧罐捆扎在一起成为 一个zip文件 为容易的下载。 这里的名字从版本3.0罐 形式 org.springframework。* - <版本> . jar 。 Maven中央,这是默认的存储库,Maven 查询,并且不需要任何特殊配置使用。 许多公共库,弹簧取决于也 可以从Maven中 央和一大段弹簧 社区使用Maven的依赖管理,所以这是 方便他们。 这里的名字是形式的罐子 春天- * - <版本> . jar 和 Maven groupId是 org.springframework 。 企业Bundle库(电子束记录),它是由 SpringSource和主机所有的库集成 春天。 两个Maven和Ivy存储器可用来所有 Spring jar和他们的依赖关系,以及大量的其他 公共库,人们用在应用程序与弹簧。 两 完整版本,还里程碑和开发快照 在这里部署。 jar文件的名称是在同一个表格 社区下载 ( org.springframework。* - <版本> . jar ), 依赖性也在这个“长”形式,与外部 库 (不是从SpringSource)有前缀 com springsource 。 看到 faq 为更多的信息。 在一个公共Maven存储库托管在Amazon S3作为 开发快照和里程碑版本(一份期末 版本也在这里举行)。 这个jar文件名称 相同的 形式是Maven中央,所以这是一个有用的地方去 开发版本的Spring使用与其他库部署 在Maven中央。 所以你首先需要决定如何管理你的 依赖性:大多数人使用一个自动化的系统像Maven或者常春藤,但是 你也可以手动下载所有的 罐子自己。 当 获得与Maven和Ivy春天你然后决定哪个地方 你会得到它。 一般来说,如果你关心OSGi,使用电子束重熔, 因为它 的房屋构件的兼容OSGi所有春天的 依赖关系,如Hibernate和Freemarker。 如果OSGi并不重要 你,任何一个地方工作,虽然有 一些利弊之间 他们。 一般来说,选择一个地方或另一个用于你的项目,不要 混合。 这是特别重要,因为工件必然电子束重熔 使用 不同的命名约定比Maven中央工件。 1.1为多。 一个比较的Maven中央和SpringSource电子束重熔 存储库 特性 Maven中央 电子束重熔 OSGi兼容 没有明确的 是的 数量的工件 成千上万的;所有种类 数以百计的;那些春天集成了 一致的命名惯 例 没有 是的 命名约 定:GroupId 各不相同。 新工件经常使用的域名,例如。 org.slf4j。 老年人通常只使用工件的名字,例 如。 log4j。 域名的起源或主要包根,如。 org.springframework 命名约定:与 ArtifactId设 为 各不相同。 一般项目或模块名称、使用 连字 符“-”分隔符,例如,logj4 spring核心。 束符号名称,来自主要的包 根,如 org.springframework.beans。 如果jar必须 打补 丁,确保OSGi合规然后com。 springsource是 附加 的,例如com springsource org apache log4j 命名约定:版本 各不相同。 许多新工件使用规则。 米或m m m。 X( m =数字,X =文本)。 老年人使用。一 些既不答。 订购 被定义,但不能经常依赖,所以 不严格吗 可靠的。 OSGi版本号m m m。 X,例如。3 0 0 rc3。 文本 预 选赛上强加了字母排序的版本 相同的数值。 出版 通常通过rsync或源代码控制自动更新。 项目 作者可以上传个人JIRA jar。 手册(JIRA SpringSource处理) 质量保证 根据政策。 精度是责任的 作者。 广泛的对OSGi manifest,Maven POM和常春藤 元 数据。 QA团队执行的春天。 托管 Contegix。 由Sonatype与几个 镜子。 S3 SpringSource资助。 搜索实用程序 各种 http://www.springsource.com/repository 集成与 SpringSource 工具 集成通过STS Maven的依赖 管理 广泛的集成通过STS Maven,袋鼠, CloudFoundry 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 22/514 Spring依赖和依靠弹簧 虽然Spring提供了集成和支持一个巨大的 范围内的企业和其他外部工具,它特意保持 其强制性依赖一个绝对最小:你不应该 查 找和下载(甚至自动)大量的jar 图书馆为了使用弹簧为简单的用例。 基本 依赖注入只有一个强制性的外部依赖, 并对日志记录 (参见后面的一个更详细的描述 日志记录选项)。 接下来我们大纲的基本步骤需要配置一个 应用程序依赖于春天,首先与Maven然后用 艾薇。 在所有情况下,如果有不清楚的地 方,请参阅文档 你的依赖关系管理系统,或看一些示例代码- 弹簧本身使用Ivy来管理依赖当它正在建设, 我们的样品主要是使用 Maven。 Maven依赖管理 如果您正在使用Maven的依赖管理你甚至不 需要提供日志依赖明确。 例如,要 创建一个应用程序上下文和使用依赖注入来 配 置一个应用程序,你会看起来像Maven的依赖 这个: org.springframework spring-context 3.0.0.RELEASE runtime 这是它。 注意范围可以声明为运行时如果你 不需要编译与弹簧的api,这是典型的案例 对于基本依赖注入的用例。 我们使用Maven中央命名约定在示例 以上,因此,适用于Maven中央或SpringSource S3 Maven 存储库。 使用S3 Maven存储 库(例如里程碑或 开发者快照),您需要指定存储库位置 您的Maven配置。 对于完整版本: com.springsource.repository.maven.release http://repo.springsource.org/release/ false 为里程碑: com.springsource.repository.maven.milestone http://repo.springsource.org/milestone/ false 和快照: com.springsource.repository.maven.snapshot http://repo.springsource.org/snapshot/ true 利用电子束记录您需要SpringSource使用一个不同的 命名约定的依赖性。 名字通常是容易 猜,比如在本例中,它是: org.springframework org.springframework.context 3.0.0.RELEASE runtime 你还需要声明库的位置 明确(只有URL是重要的): com.springsource.repository.bundles.release http://repository.springsource.com/maven/bundles/release/ 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 23/514 如果你管理你的依赖手工中的URL 库声明以上不是浏览,但有一个用户 接口在 http://www.springsource.com/repository 可 以用来搜索并下载依赖关系。 它也有 方便的片段的Maven和Ivy的配置,你可以复制和 如果您使用的是那些粘贴工具。 艾薇依赖管理 如果你喜欢使用 艾薇 管理依赖 然后有类似的名称和配置选项。 配置艾薇指SpringSource的电子束重熔添加 以下你解析器 ivysettings.xml : 上面的XML也是无效的,因为线太长——如果 你复制粘贴然后删除额外的行结束的中间 url模式。 一旦艾薇配置为看看电子束记录添加一个依赖项是 容易的。 只是拉起包的详细信息页面上的问题 存储库浏览器,你会发现一个 常春藤片段,为你准备好 包括在你的依赖关系部分。 例如(在 流 ): 1.3.2A日志 日志是一个非常重要的依赖,因为它是春天) 唯一强制性外部依赖,b)每个人都喜欢看一些 输出从他们使用的工具,和c)弹簧集成 了很多 其它的工具都还做了一个选择的日志 依赖项。 的一个目标应用程序开发人员通常是 有统一的日志配置在一个中心位置 对整个 应用程序,包括所有的外部组件。 这是更加困难 比它可能是由于有如此多的选择的日志 框架。 在春天的强制性日志依赖性是Jakarta Commons 日志记录API(JCL)。 我们编译也使对JCL JCL 日志 对类对象可见扩展 Spring框架。 它是重要的用户,所有版本的春天 使用相同的日志库:迁移是容易因为向后 兼容性是与应用程序保存甚至扩展 Spring。 我们这样做是在春天的一个模块依赖 明确在 通用日志 (规范化实现 的JCL),然后让所有其他模块依赖,在编译 时间。 如果您正在使用Maven例如,和想知道你挑选 依赖的 通用日志 ,那么它就是来自于 春天和专门从中央模块称为 spring核心 。 的好处 通用日志 是你 不需要什么要让你的应用程序的工作。 它有一个运行时 发现算法,寻找其他日志框架在好 已知在类路径 的地方,使用一个它认为是适当的 (或者你可以告诉它哪一个如果你需要)。 如果没什么 可你很漂亮的日志只是从JDK (java跑龙 套。 日志记录或7月短)。 你会发现你的春天 应用程序和记录到控制台的幸福好了大多数 的情况,这很重要。 不使用通用日志 不幸的是,运行时发现算法 通用日志 ,虽然方便了终端用户,是 有问题的。 如果时间可以倒转,开始春天了 作为一个新项目将使 用不同的日志依赖性。 这个 第一选择应该是简单的日志立面为Java( SLF4J ),它也使用了很多 其它的工具,人们使用弹簧在他们 应用程序。 关掉 通用日志 很简单:只是要吗 当然这不是在运行时类路径中。 在Maven条款排除 的依赖,因此,Spring依赖项 是说,你只需要 做一次。 org.springframework spring-context 3.0.0.RELEASE runtime 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 24/514 commons-logging commons-logging 现在这个应用程序可能是破碎的,因为没有 实现的类路径上的JCL API,所以解决这一新的一个 必须提供。 在接下来的部分中,我 们向您展示如何提供一个 选择实施JCL SLF4J作为一个例子,使用 使用SLF4J SLF4J是一个更清洁和更有效的在运行时依赖比 通用日志 因为它使用编译时绑定 而不是运行时发现的其他日志框架它 集成 了。 这也意味着,你必须更明确的关于什么 你想发生在运行时,并宣布它或配置它 因此。 SLF4J提供绑定很多常见的日志框架, 所以你通常可以选择一个你已经使用,并结合, 配置和管理。 SLF4J提供绑定很多常见的日志框架, 包括JCL,它也确实相反:桥梁在其他 日志框架和本身。 所以使用SLF4J与弹簧你需要 取代 通用日志 依赖与SLF4J-JCL 桥。 一旦你做了,然后从内部日志调用春天 将被翻译成日志调用SLF4J API,所以如果其他吗 图书馆 在你的应用程序使用该API,然后你有一个单独的位置 配置和管理日志。 一种常见的选择可能是春天SLF4J桥,然后 提供显式绑定从SLF4J Log4J。 你需要提供4 依赖性(和排除现有的 通用日志 ): 这座 桥,SLF4J API,绑定到Log4J,Log4J 实现本身。 在Maven这样的你也会这么做的 org.springframework spring-context 3.0.0.RELEASE runtime commons-logging commons-logging org.slf4j jcl-over-slf4j 1.5.8 runtime org.slf4j slf4j-api 1.5.8 runtime org.slf4j slf4j-log4j12 1.5.8 runtime log4j log4j 1.2.14 runtime 这可能看起来像一个许多依赖关系只是为了得到一些 日志记录。 嗯,就是这样,但它 是 可选的,它 应表现出比香草 通用日志 与 类加载器问题的方面,特别是如果你是在一个严格的容器 像一个OSGi平台。 据说也有性能优势 因为绑定是在编译时不运行 时。 一个更普遍的选择在SLF4J用户,它使用更少的步骤 和产生更少的依赖,是直接绑定 logback 。 这消除了额外的 绑定的步骤,因 为Logback SLF4J直接实现,所以你只需要 依靠两个库不是四( jcl-over-slf4j 和 logback )。 如果你这样做,你可能还需要排除 slf4j-api依赖从其他外部依赖(不是弹簧), 因为你只需要一个版本的API类路径上。 使用Log4j 许多人使用 log4j 作为一个记录 框架配置和管理的目的。 它是有效的 和完善的,事实上它就是我们使用在运行时当我们 构建 和测试弹簧。 春天还提供了一些实用程序 Log4j配置和初始化,所以它有一个可选的编译时 在一些模块依赖Log4j。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 25/514 Java SE和Java EE的支持 Spring框架是现在基于Java 5和Java 6是完全 支持。 此外,春天是兼容J2EE 1.4和Java EE 5,而 同时引 入一些早期支持Java EE 6。 使Log4j工作默认JCL依赖性 ( 通用日志 )你所需要做的就是把Log4j的 类路径,和为它提供一个配置文件 ( log4j . properties 或 log4j xml 在根 在类路径中)。 所以对于Maven用户这是你的依赖 声明: org.springframework spring-context 3.0.0.RELEASE runtime log4j log4j 1.2.14 runtime 这里有一个样品log4j。 属性用于日志记录的 控制台: log4j.rootCategory=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n log4j.category.org.springframework.beans.factory=DEBUG 运行时容器与本机JCL 许多人运行他们的Spring应用程序在一个容器 本身提供了一个实现JCL。 IBM Websphere Application 服务器(是)是原型。 这常常导致问题, 不幸的是没有银弹的解决方案;简单地排除 通用日志 从你的应用程序是不够的 大多数情况下。 要清楚这一点:报告的问题通常是不 与JCL本身,或即使有 通用日志 :而 他们都与绑定 通用日志 到另一个 框架(经常Log4J)。 这 可能会因为 通用日志 改变他们的方式运行时 发现旧版本之间(1.0)发现在一些 容器和现代版本,大多数人现在使用(1.1)。 春天 不使用任何不寻常的部分的JCL API,所以没有什么 在那里中断,但只要弹簧或应用程序试图做的 任何日志你可以发现绑定到 Log4J不是 工作。 在这种情况下是最简单的事情就是转化 类加载器层次结构(IBM称之为“父母最后”)这样 应用程序控制着JCL依赖,而不是容 器。 这 选择并不总是开放的,但也有很多其他的建议 在公共领域对替代方法,和你的里程 可能取决于确切的版本和特性集的吗 集装箱。 PartA II。 一个什么新弹簧3 2。 一个新特性和增强功能在Spring框架3.0 如果你一直使用Spring框架对于一些时间,你会 知道春天已经经历了两个重大修改:Spring 2.0中发布 2006年10月,Spring 2.5,2007年11月发布。 这是现在的时间为 第三个检修导致Spring Framework 3.0。 2.1 Java 5 整个框架代码已被修改,以利用Java 5的特性(如泛型、可变参数和其他语言的改进。 我们有 尽了最大努力仍然保持向后兼容的 代码。 我们现在有 坚持使用泛型集合和地图,坚持使用泛型 FactoryBeans,也一致的解决方法在桥梁 Spring AOP API。 通用 ApplicationListeners自动接收 特定事件类型只有。 所有的回调接口如 TransactionCallback和HibernateCallback声明一个 通用的结果值 现在。 总的来说,核心代码库的春天是现在刚修订和 优化了Java 5。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 26/514 Note: The spring.jar artifact that contained almost the entire framework is no longer provided. Spring的抽象TaskExecutor已经更新为关闭 整合与Java 5的Java跑龙套。 并发的设施。 我们提供 一流的支持Callables和期 货现在,以及 ExecutorService适配器,ThreadFactory集成等。这是 符合jsr - 236(并发实用程序对Java EE 6)至于 可能的。 此 外,我们提供支持异步方法 调用通过使用新@Async注释(或EJB 3.1的 asynchronous注释)。 2.2一个改进的文档 Spring参考文档还明显被 更新,以反映所有的变更和新特性对于Spring框架 3.0。 而所做的一切努力都以确保没有错误 这个文 档,一些错误可能不过已经爬了进去。 如果你 点任何拼写错误或更严重的错误,你可以抽出几个周期 在午餐,请把错误的注意弹 簧 团队的 抚养一个 问题 。 2.3一个新的文章和教程 有许多优秀的文章和教程,展示如何得到 开始使用Spring框架3特性。 读他们的 弹簧文档 页面。 样品已经被改进和更新,以利用新特性在春天 框架3。 另外,样品已经搬出了源代码树到一个专门的SVN 库 可以在: https://anonsvn.springframework.org/svn/spring-samples/ 因此,样品都不再分布式和弹簧 框架3,需要单独下载从存储库 上面提到的。 然而,这个文档将继续参考 一些样品(特别是宠物诊 所)来说明各种特性。 注意 为更多的信息在Subversion(SVN或短),请参见项目主页: http://subversion.apache.org/ 2.4新模块的组织和构建系统 该框架模块已经修改,现在管理 一个源代码树分别与每个模块jar: org.springframework.aop org.springframework.beans org.springframework.context org.springframework.context.support org.springframework.expression org.springframework.instrument org.springframework.jdbc org.springframework.jms org.springframework.orm org.springframework.oxm org.springframework.test org.springframework.transaction org.springframework.web org.springframework.web.portlet org.springframework.web.servlet org.springframework.web.struts We are now using a new Spring build system as known from Spring Web Flow 2.0. This gives us: Ivy-based "Spring Build" system consistent deployment procedure consistent dependency management consistent generation of OSGi manifests 2.5 Overview of new features This is a list of new features for Spring Framework 3.0. We will cover these features in more detail later in this section. 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 27/514 Spring Expression Language IoC enhancements/Java based bean metadata General-purpose type conversion system and field formatting system Object to XML mapping functionality (OXM) moved from Spring Web Services project Comprehensive REST support @MVC additions Declarative model validation Early support for Java EE 6 Embedded database support 2.5.1 Core APIs updated for Java 5 BeanFactory interface returns typed bean instances as far as possible: T getBean(Class requiredType) T getBean(String name, Class requiredType) Map getBeansOfType(Class type) Spring's TaskExecutor interface now extends java.util.concurrent.Executor: extended AsyncTaskExecutor supports standard Callables with Futures New Java 5 based converter API and SPI: stateless ConversionService and Converters superseding standard JDK PropertyEditors Typed ApplicationListener 2.5.2 Spring Expression Language Spring introduces an expression language which is similar to Unified EL in its syntax but offers significantly more features. The expression language can be used when defining XML and Annotation based bean definitions and also serves as the foundation for expression language support across the Spring portfolio. Details of this new functionality can be found in the chapter Spring Expression Language (SpEL). The Spring Expression Language was created to provide the Spring community a single, well supported expression language that can be used across all the products in the Spring portfolio. Its language features are driven by the requirements of the projects in the Spring portfolio, including tooling requirements for code completion support within the Eclipse based SpringSource Tool Suite。 下面是一个示例,说明了表达式语言可以 用于配置数据库设置的一些性质 这个功能也可以如果你喜欢配置 你的组件使用注释: @Repository public class RewardsTestDatabase { @Value("#{systemProperties.databaseName}") public void setDatabaseName(String dbName) { … } @Value("#{strategyBean.databaseKeyGenerator}") public void setKeyGenerator(KeyGenerator kg) { … } } 2.5.3A的控制反转(IoC)容器 基于Java bean的元数据 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 28/514 一些核心的功能 JavaConfig 项目已经被添加到Spring框架现在。 这意味着 下面的注释是现在直接支持: @ configuration @ bean @DependsOn @Primary @Lazy @ import @ImportResource 这个元素包含一个@ value 下面的示例是一个Java类提供基本配置 使用新的JavaConfig特点: package org.example.config; @Configuration public class AppConfig { private @Value("#{jdbcProperties.url}") String jdbcUrl; private @Value("#{jdbcProperties.username}") String username; private @Value("#{jdbcProperties.password}") String password; @Bean public FooService fooService() { return new FooServiceImpl(fooRepository()); } @Bean public FooRepository fooRepository() { return new HibernateFooRepository(sessionFactory()); } @Bean public SessionFactory sessionFactory() { // wire up a session factory AnnotationSessionFactoryBean asFactoryBean = new AnnotationSessionFactoryBean(); asFactoryBean.setDataSource(dataSource()); // additional config return asFactoryBean.getObject(); } @Bean public DataSource dataSource() { return new DriverManagerDataSource(jdbcUrl, username, password); } } 得到这个工作你需要添加以下组件 扫描进入你的最小应用程序上下文XML文件。 或者你可以引导一个 @ configuration 类直接使用 所 : public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); FooService fooService = ctx.getBean(FooService.class); fooService.doStuff(); } 看到 SectionA 5 12 2,一个​​Spring容器实例化使用 所 一个​​ 完全信息 所 。 元数据定义bean组件内 @ bean 也支持带注释的方法 在弹簧组件。 他们使工厂bean定义 容器。 看到 bean定义元数据在 组件 为更多的信息 2.5.4A通用类型转换系统和字段格式 系统 一个通用 类型转换 系统 介绍了。 该系统目前使用? 类型转换,也可以被用于一个Spring容器和DataBinder当 绑定bean属性 值。 此外,一个 格式化程序 SPI 介绍了格式字段值。 这SPI提供 一个更简单和更健壮的替代JavaBean PropertyEditors用于客户端 环境如Spring MVC。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 29/514 2.5.5A数据层 对象到XML的映射功能(OXM)从Spring Web 服务项目已经搬到核心的Spring框架现在。 这个 功能是发现的 org.springframework.oxm 包。 更多的信息的使用 OXM 模块中可以找到 编组XML使用O / X 映射器 一章。 2.5.6A Web层 最令人兴奋的新功能是支持Web层 构建RESTful web服务和web应用程序。 也有一些 新的注释,可以用在任何web应用程序。 综合REST支持 服务器端支持构建RESTful应用程序一直 作为一个扩展现有的注解驱动的MVC web 框架。 客户端支持提供的 RestTemplate 类的精神,其他 模板类,比如 JdbcTemplate 和 JmsTemplate 。 服务器和客户端都休息 功能利用 HttpConverter 年代,以促进 之间的转换对象和他们的代表在HTTP请求 和响应。 这个 MarshallingHttpMessageConverter 使用 这个 对象到XML的映射 功能提到 早期。 参考的章节 MVC 和 RestTemplate的 更多 信息。 @MVC添加 一个 MVC 名称空间被引入,这极大地简化了Spring MVC的配置。 额外的注释(如 @CookieValue 和 @RequestHeaders 已添加。 看到 映射的cookie的值 @CookieValue注释 和 映射属性与 请求头 @RequestHeader注释的 为更多的信息。 2.5.7A声明式模型验证 几个 验证改进 , 包括JSR 303支持,使用Hibernate Validator作为默认提供者。 2.5.8A早期支持Java EE 6 我们提供支持异步方法调用通过 使用新@Async注释(或EJB 3.1的asynchronous 注释)。 JSR 303,JSF 2.0,JPA 2.0等 2.5.9A支持嵌入式数据库 方便支持 嵌入式Java数据库 引擎 ,包括HSQL,H2,德比,现在提供。 3。 一个新特性和增强功能在Spring框架3.1 这是一个列表的新功能,为Spring Framework 3.1。 一个数量的特性 没有专门的参考文档,但确实有完整的Javadoc。 在这样 的 情况下,给出了完全限定的类名。 看到也 AppendixA C, 迁移到Spring Framework 3.1 3.1一个缓存抽象 ChapterA 29日 缓存抽象 缓存抽象 (SpringSource团队博客) 3.2一个Bean定义概要文件 XML配置文件 (SpringSource团队博客) 引入@Profile (SpringSource团队博客) 看到org.springframework.context.annotation.Configuration Javadoc 看到org.springframework.context.annotation.Profile Javadoc 3.3一个环境抽象 环境抽象 (SpringSource团队博客) 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 30/514 看到org.springframework.core.env。 环境Javadoc 3.4一个PropertySource抽象 统一物业管理 (SpringSource团队博客) 看到org.springframework.core.env。 环境Javadoc 看到org.springframework.core.env。 PropertySource Javadoc 看到org.springframework.context.annotation.PropertySource Javadoc 3.5代码等价的Spring的XML名称空间 基于代码的等价物,流行的Spring XML名称空间的元素 <上下文:组件扫描/ >,< tx:注解驱动/ > 和< mvc:注解驱动>已经开发 出来,大多数在 形式的 @Enable 注释。 这些都是 设计用于结合春天的 @ configuration 类,这在 介绍了Spring框架3.0。 看到org.springframework.context.annotation.Configuration Javadoc 看到org.springframework.context.annotation.ComponentScan Javadoc 看到 org.springframework.transaction.annotation.EnableTransactionManagement Javadoc 看到 org.springframework.cache.annotation。 EnableCaching Javadoc 看到org.springframework.web.servlet.config.annotation.EnableWebMvc Javadoc 看到org.springframework.scheduling.annotation.EnableScheduling Javadoc 看到org.springframework.scheduling.annotation.EnableAsync Javadoc 看到 org.springframework.context.annotation.EnableAspectJAutoProxy Javadoc 看到 org.springframework.context.annotation.EnableLoadTimeWeaving Javadoc 看到 org.springframework.beans.factory.aspectj.EnableSpringConfigured Javadoc 3.6一个支持Hibernate 4. x 看到在新的类的Javadoc org.springframework.orm。 hibernate4包 3.7一个还是和TestContext框架支持@ configuration类和bean 定义概要文件 这个 @ContextConfiguration 注释现在支持提供 @ configuration 类配置 春天 还是和TestContext 。 此外,一个新的 @ActiveProfiles 注释已经 介绍了支持声明式配置的活跃bean 定义概要文件在 ApplicationContext 集成测试。 春天 3.1平方米:测试与@ configuration类和配置文件 (SpringSource团队博客) 看到 SectionA 11 3 5,一个​​春天还是和TestContext Frameworka​​ 看到 一个​章节​上下文配置使用带注释的classesa​​ 和 org.springframework.test.context.ContextConfiguration Javadoc 看到 org.springframework.test.context.ActiveProfiles Javadoc 看到 org.springframework.test.context.SmartContextLoader Javadoc 看到 org.springframework.test.context.support.DelegatingSmartContextLoader Javadoc 看到 org.springframework.test.context.support.AnnotationConfigContextLoader Javadoc 3.8一个c:名称空间更简洁的构造函数注入 一个​章节​XML与c-namespacea​​快捷方式 3.9一个支持对非标准JavaBeans注射 setter Spring Framework 3.1之前,为了对产权注入 方法必须严格符合javabean属性签名规则, 即任何“setter”方法必须返回 void。 现在可以 在Spring XML指定setter方法,返回任何对象类型。 这 是有用的在考虑设计api方法链接,在哪里 setter方法返 回一个引用“本”。 3.10一个支持Servlet 3基于代码的配置Servlet 容器 新 WebApplicationInitializer 构建在Servlet 3.0的 ServletContainerInitializer 支持 提供一个程序化的替代传统的web . xml。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 31/514 看到org.springframework.web.WebApplicationInitializer Javadoc Diff从春天的 温室参考应用 演示迁移 从web。 xml WebApplicationInitializer 3.11一个支持Servlet 3 MultipartResolver 看到 org.springframework.web.multipart.support.StandardServletMultipartResolver Javadoc 3.12一个JPA会引导没有 persistence . xml 在标准JPA,持久性单元得到定义通过 meta - inf / persistence . xml 文件在特定的jar文件 这将反过来得到搜索吗 entity 类。 在许多情况下,持久性。 xml不包含超过一个单位的名字 和依赖于违约和/或外部设置为所有其他问题 (如数据源使用,等等)。 出于这个原因,Spring框架 3.1提供了一个替代方案: LocalContainerEntityManagerFactoryBean 接受一个 “packagesToScan”属性,指定基础包扫描 entity 类。 这是类似于 AnnotationSessionFactoryBean 的财产 相同的名称为 土著Hibernate的设置,同时弹簧的 组件扫描功能,定期Spring bean。 实际上,这 允许xml自由JPA设置仅仅是在牺牲指定一个 基地 包对于实体扫描:一个特别好的适合春天 应用程序依赖于组件扫描为Spring bean, 甚至可能引导使用基于Servlet 3.0 初始 化器。 3.13新HandlerMethod-based支持类注释的控制器 处理 Spring Framework 3.1引入了一组新的支持类 处理请求使用带注释的控制器: RequestMappingHandlerMapping RequestMappingHandlerAdapter ExceptionHandlerExceptionResolver 这些类是替代现有的: DefaultAnnotationHandlerMapping AnnotationMethodHandlerAdapter AnnotationMethodHandlerExceptionResolver 新类是回应了许多要求 让注释控制器支持类更多的定制和开放 对扩展。 而以前可以配置一个自定义注释 控制器方法参数解析 器,新的支持类 可以定制加工为任何受支持的方法参数或返回吗 值类型。 看到 org.springframework.web.method.support.HandlerMethodArgumentResolver Javadoc 看到 org.springframework.web.method.support.HandlerMethodReturnValueHandler Javadoc 第二个显著的区别是引入一个 HandlerMethod 抽象代表一个 @RequestMapping 法。 这种抽象是使用 在新支持类的 处理 程序 实例。 例如一个 HandlerInterceptor 可以 把 处理程序 从 对象 到 HandlerMethod 和获得目标 控制器方法,其注释 等。 新类是默认启用的MVC名称空间和通过 基于java的配置通过 @EnableWebMvc 。 这个 现有类持续可用但使用新的 类是推 荐的前进。 看到 一个​章节​新的支持类 @RequestMapping 方法在一个​​Spring MVC 3.1 对于额外的 细节和一个列表的功能可能无法与新 的支持类。 3.14“消费”和“生产”条件 @RequestMapping 改进支持指定媒体类型所消耗的方法 通过 ”内容类型的 头以及 可生产的类型指定的通过 “接受” 头。 看到 一个​章节​消耗 品媒体Typesa​​ 和 一个​章节Typesa​​​可生产的媒体 3.15一个Flash属性和 RedirectAttributes Flash属性现在可以被存储在一个 FlashMap 并保存在HTTP会话生存 一个重定向。 概述的一般支持flash的属性 在Spring MVC看到 SectionA 17.6,一个​​attributesa​​使用闪光灯 。 在注释的控制器,一个 @RequestMapping 方法可以添加闪光 属性声明一个方法的参数类型 RedirectAttributes 。 这个方法 参数 现在还可以被用来得到精确控制属性用于 一个重定向的场景。 看到 一个​章节​指定attributesa​​重定向和闪光 为更多的细 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 32/514 节。 3.16一个URI模板变量增强 URI模板变量从当前请求中使用更多 的地方: URI模板变量是使用除了请求 当一个请求参数绑定 @ModelAttribute 方法 参数。 @PathVariable方法参数值合并成 模型渲染之前,除了视图生成的内容 一个自动化的时尚如JSON序列化或XML 编组。 一个重定向为URI字符串可以包含占位符变量 (如。 “重定向:/博客/ {年} / {月}” )。 当 扩大占位符,URI模板变量 当前请 求被自动考虑。 一个 @ModelAttribute 方法 参数可以实例化一个URI模板变量提供 有一个注册转换器或属性编辑器将从 一个字符串到 目标对象类型。 3.17一个 @Valid 在 @RequestBody 控制器方法参数 一个 @RequestBody 方法参数可以 标注 @Valid 调用自动 验证类似于支持 @ModelAttribute 方法参数。 一个结果 MethodArgumentNotValidException 处理 DefaultHandlerExceptionResolver 和结果在一个 400年 响应代码。 3.18一个 @RequestPart 注释 控制器方法参数 这个新的注释提供了访问的内容 “多部分/格式数据“请求部分。 看到 SectionA 17 10 5,一个​​处理文件上传请求从编程 clientsa​​ 和 SectionA 17.10,一个​​弹簧的多部分(文件上传)supporta​​ 。 3.19一个 UriComponentsBuilder 和 UriComponents 一个新的 UriComponents 类已添加, 这是一个不可变的容器组件的URI提供吗 访问所有包含URI组件。 一个新的 UriComponentsBuilder 类也 提供帮助创建 UriComponents 实例。 在一起的两个类给细粒度控制所有 方面的准备一个URI 包括建设、扩张 从URI模板变量,和编码。 在大多数情况下,新类可以作为一个更灵活 替代现有的 UriTemplate 特别是 UriTemplate 依赖那些 同一类内部。 一个 ServletUriComponentsBuilder 子类 提供静态工厂方法复制信息 一个Servlet请求。 看到 SectionA 17.7,一个​​建筑 URI sa​​ 。 4。 一个新特性和增强功能在Spring框架3.2 这一节将介绍什么是新的在Spring框架3.2。 看到也 AppendixA D, 迁移到Spring Framework 3.2 4.1一个支持Servlet 3基于异步请求处理 Spring MVC的编程模型现在提供明确的Servlet 3 异步支持。 @RequestMapping 方法可以 返回一个: java util并发调用 到 完整的处理在一个单独的线程管理任务执行人 在Spring MVC。 org.springframework.web.context.request.async.DeferredResult 完成处理在稍后的时间从一个线程不了解 一个​ Spring MVC​例如,在应对一些外部事件(JMS, AMQP等)。 org.springframework.web.context.request.async.AsyncTask 包装 可调用的 和定制 超时值或任务执行器使用。 看到 SectionA 17 3 4,一个​Processinga​​​异步请求 。 4.2一个Spring MVC的测试框架 一流的支持Spring MVC应用程序测试用 流利的API和没有一个Servlet容器。 服务器端测试涉及使用 的 DispatcherServlet 而 客户端休息 测试依赖 RestTemplate 。 看到 SectionA 11 3 6,一个​​测试Frameworka​​Spring MVC 。 4.3一个内容协商改进 一个 ContentNegotiationStrategy 现在 用于解决从传入请求媒体类型 请求。 可用的实现是基于文件扩展名, 查询参数,“接 受”标题,或一个固定的内容类型。 等效选项以前只能在 ContentNegotiatingViewResolver但现在遍及。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 33/514 ContentNegotiationManager 是中央 类使用在配置选项内容协商。 更多细节见 SectionA 17 15 4,一个​​配置内容 Negotiationa​​ 。 引入 ContentNegotiationManger 还使选择性后缀模式匹配为传入的请求。 有关详细信息,请参见的Javadoc RequestMappingHandlerMapping.setUseRegisteredSuffixPatternMatch 。 4.4一个 @ControllerAdvice 注释 类标注 @ControllerAdvice 可以包含 @ExceptionHandler , @InitBinder ,和 @ModelAttribute 方法和那些将 适用于 @RequestMapping 方法在 控制器层次相对于控制器层次在其中 他们宣布。 @ControllerAdvice 是 组件注释允许实现类自 动 通过类路径扫描。 4.5一个矩阵变量 一个新的 @MatrixVariable 注释添加 支持提取矩阵变量从请求URI。 更多 细节见 一个​章节Variablesa​​​矩阵 。 4.6一个抽象基类,用于基于Servlet 3 +容器 初始化 一个抽象的基类的实现 WebApplicationInitializer 接口是 提供简化基于代码注册DispatcherServlet和 过滤器映射到它。 这 个新类命名 AbstractDispatcherServletInitializer 和它的 子类 AbstractAnnotationConfigDispatcherServletInitializer 可以 使用基于java的Spring配置。 更多细节见 SectionA 17.14,一个​​initializationa​​基于代码的Servlet容器 。 4.7一个 ResponseEntityExceptionHandler 类 一个方便的基类的 @ExceptionHandler 方法处理 标准的Spring MVC异常并返回 ResponseEntity 这允许定制和 编写响应 HTTP消息转换器。 这可以作为一种 替代 DefaultHandlerExceptionResolver , 这确实相同的但返回吗 ModelAndView 相 反。 看到修改后的 SectionA 17.11,一个​​处理exceptionsa​​ 包括 信息定制默认Servlet容器的错误 页面。 4.8对泛型的支持的 RestTemplate 在 @RequestBody 参数 这个 RestTemplate 现在可以读一个HTTP吗 回应一个泛型类型(如。 列表<帐户> )。 有 三个新 交换() 方法,它接受 ParameterizedTypeReference ,一个新类 使捕获和传递泛型类型信息。 支持这个特性, HttpMessageConverter 是延长 GenericHttpMessageConverter 添加一个方法 阅读内容给指定的参数化的 类型。 新 接口实现的 MappingJacksonHttpMessageConverter 也会被一个 新 Jaxb2CollectionHttpMessageConverter 这可以 阅读读一个通用的 收集 在 泛型类型是JAXB注释类型 @XmlRootElement 或 @XmlType 。 4.9一个杰克逊JSON 2和相关的改进 杰克逊JSON 2图书馆现在支持。 由于包装 变化在杰克逊的图书馆,有单独的类在Spring MVC 为好。 那些是 MappingJackson2HttpMessageConverter 和 MappingJackson2JsonView 。 其他相关 配置的改进包括支持漂亮的印刷等 一个 JacksonObjectMapperFactoryBean 为了方便 定制的 ObjectMapper 在XML 配置。 4.10一个瓷砖3 现在支持瓷砖3除了瓷砖2. x。 配置 它应该非常类似于瓷砖2配置,即 结合 TilesConfigurer , TilesViewResolver 和 TilesView 除了使用 tiles3 而不是 tiles2 包。 还请注意,除了版本号变化,瓷砖 依赖关系也发生了变化。 你需要有一个子集或全部 瓦片请求api , 瓷砖api , 瓦片核心 , 瓷砖 servlet , 瓷砖jsp , 瓷砖el 。 4.11一个 @RequestBody 改进 一个 @RequestBody 或一个 @RequestPart 参数可以遵循 通过一个 错误 参数成为可能 处理验证错误(由于一个 @Valid 注 释)内的本地 @RequestMapping 法。 @RequestBody 现在还支持一个必需的 国旗。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 34/514 4.12一个HTTP补丁方法 HTTP请求方法 补丁 现在可能用在吗 @RequestMapping 方法以及 RestTemplate 结合Apache HttpComponents HttpClient 4.2或更高版本。 JDK HttpURLConnection 不支持 补丁 法。 4.13一个排除模式映射的拦截器 拦截器现在支持URL模式映射到被排除在外。 MVC 名称空间和MVC JavaConfig两暴露这些选项。 4.14一元注释使用注射点和bean定义方法 为3.2,春天允许 @ autowired 和 这个元素包含一个@ value 作为元注释, 如建立自定义注入注解结合特定的限定符。 类似地, 您可以构建定制的 @ bean 定义 注释 @ configuration 类, 如结合特定的限定符,@Lazy,@Primary等等。 4.15一个初始支持JCache 0.5 Spring提供了一种缓存管理器适配器为JCache、建筑与JCache 0.5 预览版。 全JCache支持是在明年,以及Java EE 7最后。 4.16一个支持 @DateTimeFormat 没有 Joda时间 这个 @DateTimeFormat 注释可以 现在被使用而无需依赖Joda时间图书馆。 如果Joda 时间不存在JDK SimpleDateFormat 将 被用来解析和打印日期模式。 当Joda时间是现在 将继续被用于优先 SimpleDateFormat 。 4.17全球日期和时间格式 现在可以定义全局格式的时候将会使用 解析和打印日期和时间类型。 看到 SectionA 7.7,一个​​配置全球formata​​日期与时间 对 于 细节。 4.18一个新的测试功能 除了上述的包容的 Spring MVC的测试框架 在 这个 弹簧测试 模块, 春天 还是和TestContext框架 已被修订,支持吗 集成测试 web应用程序和配置应用程序 与上下文初始化上下文。 为进一步的细节,请参考 以下。 配置和 加载一个 WebApplicationContext 在集成测试 配置 上下文层次 在集成测试 测试 请求和 会话作用域bean 改进 Servlet API 模拟 配置测试应用程序上下文与 ApplicationContextInitializers 4.19一个并发细化整个框架 Spring Framework 3.2包括微调的并发数据结构 在许多部分的框架,最小化锁和普遍提高 安排高并发创建范围/原型豆子。 4.20新gradle基础构建和搬到GitHub 建筑和导致从未简单的框架与 我们搬到一个基于gradle构建系统和源控制在GitHub上。 看到 从源代码构建 部分的自述和 贡 献者指南 完整的细节。 4.21一个精制Java SE 7 / OpenJDK 7支持 最后但并非最不重要的是,Spring框架3.2附带精制Java 7的支持 在框架以及通过升级第三方依赖关系: 具体来说,CGLIB 3.0,ASM 4.0(两个来作为内联依赖关系与 春天现在)和AspectJ 1.7支持(下一个现有的AspectJ 1.6支持)。 PartA三世。 一个核心技术 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 35/514 这部分的参考文档涵盖了所有这些 技术是绝对不可或缺的春天 框架。 首先在这些是Spring框架的反转 控制(IoC)容器。 彻底治疗的Spring框架的 IoC容器是紧随其后的是春天的全面覆盖的 面向方 面的编程(AOP)技术。 Spring框架已经 自己的AOP框架,它在概念上很容易理解, 成功地解决了80%的甜蜜点的AOP的要求吗 Java企业编程。 覆盖Spring的集成与AspectJ(目前 富有-从功能——当然,最成熟的AOP 实现在Java企业空间)也提供了。 最后,采用测试驱动开发(TDD) 软件开发方法当然是倡导的春天 团队,所以覆盖率Spring的支持集成测试 覆盖(与单元测试的最 佳实践)。 春季团队 发现,正确使用IoC当然会让两个单位和 集成测试容易(在的存在和setter方法 适当的构造函数的类会使得 他们更容易连接在一起 在一个测试而无需注册和设置服务定位器 诸如此类的)…… 这一章专门测试将有希望 说服你的这种。 ChapterA 5, IoC容器 ChapterA 6, 资源 ChapterA 7, 验证、数据绑定、类型转换 ChapterA 8, 春天表达式语言(?) ChapterA 9, 面向方面的编程与弹簧 ChapterA 10, Spring AOP api ChapterA 11, 测试 5。 一个IoC容器 5.1一个介绍Spring IoC容器和豆类 这一章介绍了Spring框架的实现 控制反转(IoC) [ 1 ] 原理。 国际奥委会也被称为 依赖 注入 (DI)。 这是一个过程,即对象定义他 们 依赖关系,即其他对象合作,只有通过 构造函数参数,参数到一个工厂方法或属性 上设置的对象实例构造或后回来吗 工厂方 法。 容器然后 注入 那些 在创建bean的依赖项。 这一过程基本上是这个 逆,因此得名 控制反转 (IoC), bean本身的控制实例化 或位置的 通过使用直接建设依赖的类,或一个机制这样的 随着 服务定位器 模式。 这个 org.springframework.beans 和 org.springframework.context 包是基础 Spring框架的IoC容器。 这个 BeanFactory 接口提供了一个先进的 配置机制能够处理任何类型的对象。 ApplicationContext 是一个接头界面的 BeanFactory。 它会更 容易集成 与Spring的AOP功能;信息资源处理(用于 国际化),事件发布;和应用程序层具体 上下文如 WebApplicationContext 在web应用程序中使用。 简而言之, BeanFactory 提供 配置框架和基本功能,以及 ApplicationContext 添加更多 特定的功能。 这个 ApplicationContext 是一个完整的超集 的 BeanFactory ,是专门用于 在这一章在描述Spring的IoC容器。 对于 更多的信息在 使用 BeanFactory 相反 的 ApplicationContext, 指 SectionA 5.15,一个​​BeanFactorya​​的 。 在春天,对象,形成的主干应用程序和 这由Spring IoC 容器 是 称为 bean 。 一个bean是一个对象,是 实例化,装配,否则一个 Spring IoC容器管理的。 否则,一个bean是一个简单的许多对象在您的应用程序。 豆, 依赖性 其中,是 反映在 配置元数据 使用 集装箱。 5.2一个集装箱概述 接口 org.springframework.context.ApplicationContext 代表了Spring IoC容器和负责实例化, 配置和组装上述豆子。 容器 被 它的指令对象实例化、配置和组装 通过读取配置元数据。 配置元数据是 用XML表示,Java注释,或Java代码。 它允许您 表 达的对象构成应用程序和丰富 这样的对象之间的相互依赖关系。 的几种实现 ApplicationContext 接口提供 与春天的开箱即用。 在独立应用程序中是很常见的 创建一个实例, ClassPathXmlApplicationContext 或 FileSystemXmlApplicationContext 。 虽然XML被传统的格式 定义配置元数据你可以 通知容器来使用 Java注释或代码的元数据格式,提供少量 XML配置声明支持这些额外的 元数据格式。 在大多数应用程序场景,明确用户代码不需要 实例化一个或多个实例的Spring IoC容器。 例如, 在一个web应用程序场景中,一 个简单的八(左右)行样板 J2EE web描述符中的XML web . xml 文件的 应用程序通常会足够了(参见 SectionA 5 14 4,一个​​方便 ApplicationContext 为web applicationsa​​实例化 )。 如果你正在使用 SpringSource工具套件 eclipse动力开发环境 或 Spring Roo 这 样板配置可以很容易地创建几个鼠标点击或 击键。 下面的图是如何工作的高级视图春天。 你 应用程序类是结合配置元数据,以便之后 这个 ApplicationContext 创建和初始化, 你有一个完全配置和可执行的系统或应用程序。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 36/514 Spring IoC容器 5.2.1A配置元数据 作为前面的图表显示,Spring IoC容器消耗 形式的 配置元数据 ,这个配置 元数据代表如何作为应用程序开发人员告诉Spring 容 器实例化、配置和组装中的对象 应用程序。 配置元数据通常是在一个简单而提供 直观的XML格式,这是本章的大部分用来传达 关键的概念和特性Spring IoC容器。 注意 基于xml的元数据是 不 唯一允许 形式的配置元数据。 Spring IoC容器本身 完全 脱离了格式,这 配置元数 据实际上是写。 信息使用其他形式的元数据与春天 集装箱,请参阅: 基于注解的 配置 :Spring 2.5引入了支持 基于注解的配置元数据。 基于java的配置 : 从Spring 3.0,提供的许多特性 Spring JavaConfig 项目 成为Spring框架核心的一部分。 因此你 可以定 义bean类的外部应用程序使用Java 而不是XML文件。 使用这些新特性,请参阅 @ configuration , @ bean, @ import 和 @DependsOn 注释。 Spring配置由至少一个,通常更多 比一个bean定义的容器必须管理。 基于xml的 配置元数据显示这些bean配置为 < bean / > 元素在一个顶级 < bean / > 元素。 这些bean定义对应于实际的对象构成 您的应用程序。 通常你定义服务层对象,数据 访问对象(DAOs)、表示对象(如Struts 行动 情况下,基础设施对象 比如Hibernate SessionFactories 、JMS 队列 ,等等。 通常是 没有配置细粒度的域对象的容器,因为它 通常是负责DAOs和业务逻辑创建和 负载的域对象。 然而,你可以使用Spring的集成 AspectJ来配置对象创建的无法控制 一个 IoC容器。 看到 使用 AspectJ依赖注入与春天的域对象 。 下面的例子显示了xml的基本结构 配置元数据: 这个 id 属性是一个字符串,你使用 识别个人的bean定义。 这个 类 属性定义了类型的bean和使用完全限定 类名。 id属性的值 是指合作 对象。 XML为指协作对象没有显示在 这个例子,看到 依赖性 为更多的信息。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 37/514 5.2.2A实例化一个集装箱 实例化一个Spring IoC容器是直截了当的。 这个 位置路径或路径提供给一个 ApplicationContext 构造函数是 实际资源字符 串,使容器加载配置 元数据从各种各样的外部资源,如本地文件 系统,从Java 类路径 ,等等。 ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); 注意 在你了解Spring的IoC容器,您可能想知道 更多关于春天的 资源 抽象,如前文所述 ChapterA 6, 资源 ,这 提 供了一个方便的机制来阅读InputStream从 位置中定义的URI语法。 特别是, 资源 路径用于构造 应用程序 上下文所描述的那样 SectionA 6.7,一个​​应用程序上下文和 资源 pathsa​​ 。 下面的例子显示了服务层对象 (services . xml) 配置文件: 下面的例子显示了数据访问对象 daos xml 文件: 在前面的示例中,服务层包含的类 PetStoreServiceImpl 和两个数据访问对象 类型的 SqlMapAccountDao 和 SqlMapItemDao 基于 iBatis 对象/关系映射框架。 这个 财产 名称 元素指的JavaBean属性的名称,和 这个 Ref 元素指的是另 一个bean的名称 定义。 这种关联id和ref元素表达了 协作对象之间的依赖关系。 对于细节的配置 对象的依赖项,见 依赖性 。 组成的基于xml的配置元数据 它可以有用bean定义跨越多个XML文件。 通常每个单独的XML配置文件中代表一个逻辑 层或模块在您的体系结构。 您可以使用应用程序上下文构造函数加载bean 定义所有这些XML片段。 这个构造函数 多个 资源 位置, 如上一节所示。 或者, 使用一个或多个 退运 <进口/ > 元素加载 从另一个档案或bean定义文件。 例如: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 38/514 在前面的示例中,外部加载bean定义 从三个文件, services . xml , messageSource.xml ,和 themeSource.xml 。 所有的位置 路径指的是相对于 定义文件做进口,所以 services . xml 必须在同一目录或 类路径位置文件做进口,而 messageSource.xml 和 themeSource.xml 必须在一个 资源 下面的位置的位置 导入文件。 正如您可以看到的,一个领先的削减是忽视,但鉴于 这些路 径是相对而言的,它是更好的形式不使用斜杠 在所有。 文件的内容被导入,包括顶部 水平 < bean / > 元素,必须是有效的XML bean定义根据弹簧模式或DTD。 注意 它是可能的,但不推荐,参考文件 父目录使用一个相对“. . /“路径。 这样创建一个 依赖一个文件,是当前应 用程序之外。 在 特别的,这种引用是不推荐用于“类路径:“url (例如,“类路径:. . / services . xml”),运行 时 解决程序选择“最近的“类路径根,然后 看着它的父目录。 类路径配置的变化可能 导致一个不同的选择, 不正确的目录。 你总是可以使用完全限定的资源位置代替 相对路径:例如,“文件:C:/ config /服务。 xml”或 “类路径:/ config / services . xml”。 然而,要知道你是 耦合应用程序的配置特定的绝对 位置。 它通常是更好的保持 一个间接的 这样的绝对位置,例如,通过" $ {… }“占位符 这是解决在运行时对JVM系统属性。 5.2.3A使用集装箱 这个 ApplicationContext 是 界面一个先进的工厂能保持一个注册的 不同的bean及其依赖项。 使用方法 T getBean(字符串 名称、类< T > requiredType) 你可以 您的bean检索实例。 这个 ApplicationContext 使您能够 读取bean定义和访问他们如下: // create and configure beans ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); // retrieve configured instance PetStoreServiceImpl service = context.getBean("petStore", PetStoreServiceImpl.class); // use configured instance List userList = service.getUsernameList(); 你使用 getBean() 检索实例的 你的豆子。 这个 ApplicationContext 接口有一些其他方法来检索bean,但最好你的 应用程序 代码不应该使用它们。 事实上,您的应用程序代码 应该没有调用吗 getBean() 方法在 所有,因此不依赖于Spring api在所有。 例如,Spring的 与web框架集成提供了依赖注入 各种web框架类,如控制器和jsf托管 豆子。 5.3一个Bean概述 Spring IoC容器管理一个或多个 bean 。 这些bean创建配置元数据,你供应 的容器,例如,在形式的XML < bean / > 定义。 在集装箱本身,这些bean定义表示为 BeanDefinition 对象,它包含 (在其他信息)下面的元数据: 一个包限定类名: 通常 实际实现类定义的bean。 豆行为配置元素,这些国家如何Bean 应该表现在容器(范围、生命周期回调,所以呢 出)。 引用其他bean所需的bean来做它 工作;这些引用也被称为 合作者 或 依赖性 。 其他配置设置来设置在新创建的对象, 例如,连接数的使用在一个bean管理 连接池,或大小限制的池。 这个元数据转换成一组属性,弥补每个bean 定义。 5.1为多。 一个bean定义 财产 解释…… 类 SectionA 5 3 2,一个​beansa​​​实例化 名称 SectionA 5 3 1,一个​​命名beansa​​ 范围 SectionA 5.5,一个​scopesa​​​Bean 构造函数参数 SectionA 5.4.1之前,一个​injectiona​​​依赖性 属性 SectionA 5.4.1之前,一个​injectiona​​​依赖性 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 39/514 Bean命名约定 该公约是使用标准Java公约实例 当bean字段名 称命名。 即,bean名称开始 小写字母,骆驼包装 从那时起。 这样的例子 的名字是(没有引号) “accountManager” , “accountService” , “userDao” , ”loginController” ,等等。 一直让你配置命名豆子更容易阅读 和理解,如果 您正在使用Spring AOP它帮助很多 应用建议一 套bean相关的名字。 自动装配模式 SectionA 5 4 5,一个​​collaboratorsa​​自动装配 延迟初始化模式 SectionA 5 4 4,一个​​延迟初始化 beansa​​ 初始化方法 一个​章节callbacksa​​​初始化 破坏方法 一个​章节callbacksa​​​破坏 除了bean定义,包含信息 创建一个特定的bean, ApplicationContext 实现也 允许注册的现有对象以外的创建 容器,由用户。 这是通过访问ApplicationContext的 BeanFactory通过方法 getBeanFactory() 哪一个 返回BeanFactory实现 DefaultListableBeanFactory 。 DefaultListableBeanFactory 支持这 登记通过方法 registerSingleton(. .) 和 registerBeanDefinition(. .) 。 然而,典型的 应用程序的工作仅仅与豆类通过元数据定义的bean 定义。 5.3.1A命名豆 人各有一个或多个标识符。 这些标识符必须 独特的容器托管bean。 通常只有一个bean 一个标识符,但如果它需要不止一个, 额外的可以 认为是别名。 在基于xml的配置元数据,您使用 id 和/或 名称 属性来 指定bean标识符(年代)。 这个 id 属性 允许您指定一个id。传统究竟这 些名字是 字母数字(' myBean”、“fooService”等等),但可能特殊字符 为好。 如果你想介绍其他别名bean,您可以 还指定它 们 名称 属性,隔开 一个逗号( , ),分号( ; ),或 白色的空间。 作为一个历史的注意,在Spring 3.1版本之前, id 属性是一个类型的 xsd:ID ,可能的字符限制。 截至 3.1,现在 xsd:string 。 注意,bean id 独特性是由容器仍然执行,虽然不再通过XML 解析器。 你不需要提供一个名称或id为bean。 如果没有名字 或id提供明确,容器生成一个惟一的名称 这豆。 然而,如果你想要将这个 bean的名字,通过 使用 Ref 元素或 服务定位器 风格的查找, 你必须提供一个名称。 动机不提供一个名称 有关使用 内豆 和 自 动装配 合作者 。 混淆一个bean外的bean定义 在bean定义本身,您可以提供超过一个名称 bean,通过结合使用一个指定的 名称 id 属性,和任意数量的其他名称 名称 属性。 这些名字可以等效 别名 来同样的bean,并且是有用的对于某些情况,如 允许每个组件在一个应用程 序来引用一个常见 通过使用bean名称依赖特定于该组件 本身。 指定所有别名定义的bean实际上是不 总是足够的,然而。 它有时需要介绍 一个 别名为bean定义的其他地方。 这是普遍的情形 在大型系统中,各子系 统之间的配置是分裂, 每个子系统有它自己的一组对象定义。 在基于xml的 配置元数据,您可以使用 <别名/ > 元素来完成这个。 在这种情况下,bean在同一个集装箱得名 fromName ,也可以在使用这个别名 定义,被称为 toName 。 例如,配置元数据为一个可参考的子系统 到一个数据源通过名字“subsystemA-dataSource。 配置 元数据子系统B可以引用数 据源通过名称 “subsystemB-dataSource’。 在编写应用程序,该应用程序使用的主要 这两个子系统的主要应用是指数据源 通过名字“myapp数据源”。 有这三个名字参考 相同的对象添加到MyApp配置元数据以下 别名定义: 现在每个组件和主应用程序可以参考 数据源名称通过一个独特的和保证不发生冲突 与其他任何定义(有效地创建一个名称空 间),然而他们 指相同的bean。 5.3.2A实例化bean 一个bean定义实质上是一个创建一个或多个配方 对象。 容器看着菜谱命名豆当问, 和使用配置元数据封装的bean定义 创建 (或获得)一个实际对象。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 40/514 内部类名称 如果你想配置一个bean定义 静态 嵌套类,您必 须使用 二进制 内部类的名称。 例如,如果你有一个类称为 foo 在 com . example 包,这 foo 类有一个 静态 内在 类称 为 酒吧 的值 ”类的 属性在一个bean定义会 被…… com例子foo $酒吧 注意使用 $ 字符的名称 将内部类的名字从外部 类名。 如果你使用基于xml的配置元数据,您指定类型 (或类)的对象也被实例化的 类 属性的 < bean / > 元素。 这 类 属性,它的内部 是一个 类 财产 在一个 BeanDefinition 实例,通常是 强制性的。 (例外,看到 一个​章节​实例使用一个实例工厂methoda​​ 和 SectionA 5.7,一个​​inheritancea​​Bean定义 )。 你使用 类 财产的方式有两种: 一般来说,指定bean类来构建的 情况下,容器本身直接创建bean调用 它的构造函数反映地,有点相当于Java代码使用 这个 新 操作符。 指定实际的类包含 静态 工厂方法时,就会到 创建对象,较常见的情况下,容器 调用一个 静态 , 工厂 方法在类来创建bean。 对象类型返回 的调用 静态 工厂方法可能 是同一类或另一个类完全。 与构造函数实例化 当你创建一个bean的构造函数方法,所有正常 类是可用的和兼容弹簧。 也 就是说,类 正在开发不需要实现任何特定的接口或 是在一个特定的方式进 行编码。 简单地指定bean类 应该足够了。 然而,这取决于你用什么类型的 奥委会对 特定bean,您可能需要一个默认构造函数(空)。 Spring IoC容器可以管理几乎 任何 你想要它来管理类;它不是 限于管理真 正的javabean。 大多数Spring用户更喜欢实际 只有一个默认的 javabean(无参数)构造函数和 适当的setter和getter方法模仿中的属性 集 装箱。 你也可以有更多的异国情调的非bean样式类 你的集装箱。 例如,如 果您需要使用一个遗留的连接 池,绝对不符合JavaBean规范, 春天可以管理 它。 与基于xml的配置元数据可以指定您的bean 类如下: 有关机构提供的参数 构造函数(如果需要)和设置对象实例属性之后 构造的对象,看到 注入 依赖性 。 与静态工厂方法实例化 当定义一个bean,您创建与静态工厂方法, 你使用 类 属性来指定类 包含 静态 工厂方法和一个 属性命名 工厂方法 指定名称 工 厂方法本身。 你应该能够调用这个方法 (和可选的参数作为稍后描述)并返回一个活对象, 这后来被视为如果它已经创造了通过 吗 构造函数。 一个用于这样一个bean定义是调用 静态 工厂在遗留代码。 下面的bean定义指定bean将 由调用工厂方法。 该定义没有指定 的类型(类)返回的对象,只有类包含 工厂方法。 在这个例子 中, createInstance除外() 方法必须是一个 静态 法。 public class ClientService { private static ClientService clientService = new ClientService(); private ClientService() {} public static ClientService createInstance() { return clientService; } } 了解详细机制提供(可选参数) 工厂方法和设置对象实例属性的后 对象返回工厂,看到 依赖性和 详细配置 。 使用工厂方法实例化一个实例 通过一个类似实例化 静态工厂 方法 ,实例化一个实例工厂方法调用 非静态的方法从容器中现有的bean创建一个 新的bean。 使用这种机制,离开 类 属性空, 工厂bean 属性,指定bean的名称在当前(或 父/祖先)容器,包含实例方法 被调用来创建对象。 的 名称设置工厂方法 本身与 工厂方法 属性。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 41/514 public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private DefaultServiceLocator() {} public ClientService createClientServiceInstance() { return clientService; } } 一个工厂类也可以持有一个以上的工厂方法 所示: public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); private DefaultServiceLocator() {} public ClientService createClientServiceInstance() { return clientService; } public AccountService createAccountServiceInstance() { return accountService; } } 这种方法表明,工厂bean本身是可以控制的 和配置通过依赖注入(DI)。 看到 依赖性和 详细配置 。 注意 在春天的文档, 工厂bean 指一个bean中配置Spring容器, 将创建对象通过一个吗 实例 或 静态 工厂方法。 相比之下, FactoryBean (注意 资本化)指的是一个spring特定 FactoryBean 。 5.4一个依赖 一个典型的企业应用程序不包含一个对象(或 豆在春天的说法)。 即使是最简单的应用程序有几个 对象,共同展示最终用户看到 作为一个连贯的 应用程序。 下一节将解释如何去定义一个数量的 bean定义,独自站到一个完全实现应用程序 对象合作,以达到 一个目标。 5.4.1A依赖注入 依赖注入 (DI)是一个过程,即 对象定义他们的依赖性,这是他们的工作,其他的对象 只能通过构造函数参数,参数到一个工厂方法, 或属性设置对象实例,构建了之后 或返回工厂方法。 容器然后 注入 这些依赖项当它创建bean。 这一过程基本上是反,因此得 名 控制反转 (IoC)的bean本身 控制实例化或位置的依赖自己 通过使用直接建设类,或 服务 定位器 模式。 代码是清洁与DI原理和去耦是更有效的 当对象提供它们的依赖项。 对象不 查找它的依赖性,不知道位置或类的 依赖关系。 这 样,你的类更容易测试,特别是 当依赖在接口或抽象基类,它 允许存根或模拟实现用于单元测试。 存在于两个主要的变体迪, 无参依赖 注入 和 setter基于 依赖注入 。 无参依赖注入 无参 DI是完成的 容器调用构造函数与一些参数,每个 代表一个依赖项。 调用一个 静态 工厂 方法与特定的参数来构造bean是 近 等价的,这个讨论治疗参数构造函数和 一个 静态 工厂方法类似。 以下 示例显示了一个类,只能依赖注入与 构造函数注入。 注意,没有什么 特殊 关于这个类,它是一个POJO,没有 依赖于容器特定的接口,基类或 注释。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 42/514 public class SimpleMovieLister { // the SimpleMovieLister has a dependency on a MovieFinder private MovieFinder movieFinder; // a constructor so that the Spring container can 'inject' a MovieFinder public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually 'uses' the injected MovieFinder is omitted... } 构造函数参数的分辨率 构造函数参数分辨率匹配发生使用 参数的类型。 如果没有潜在的歧义存在的构造函数 参数定义的bean,那么顺序 构造函数的 参数中定义的bean定义的顺序 这些参数是提供给适当的构造函数当吗 这个bean被实例化。 考虑下面的类: package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } } 没有潜在的歧义存在,假设 酒吧 和 巴兹 类 不相关的继承。 因此下面的配置工作 很好,你不需要指定构造函数的参数指标 和/ 或类型显式的 < constructor - arg / > 元素。 当另一个bean引用,类型是已知的,匹配 可以发生(的情况也是前面的例子)。 当一个简单的 类型被使用,如 <值>真<值> ,春天 不能 确定值的类型,因此不能匹配的类型没有 帮助。 考虑下面的类: package examples; public class ExampleBean { // No. of years to the calculate the Ultimate Answer private int years; // The Answer to Life, the Universe, and Everything private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } } 构造函数参数类型匹配 在前面的方案中,容器 可以 利用类型匹配与简单类型如果你 显式地指定类型的构造函数的参数使用 类型 属性。 例如: 构造函数参数的指数 使用 指数 属性来指定显式 构造函数参数的指数。 例如: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 43/514 无参或setter建立迪吗? 因为你可以混合使用两种,构造函数——和 setter基于DI,它是一个 好的经验法则使用构造 函数参数为强制性 可选依赖依赖和setter。 注 意,使用 的 @ required 注释一个setter可以用 来制造setter要求 依赖关系。 春季团队一般主张setter注入,因为 大量的构造 函数参数可以得到笨拙,特别是 当属性是可选 的。 Setter方法也使对象的 类易于重构或重新 注入后。 管理 通过 JMX mbean 是一个引人注 目的使用 案例。 一些纯粹主义者支持注射无参。 供应所有 对象 依赖意味着对象总是返回给客户机 (打电话)代 码在一个完全初始化状态。 缺点是 的对象变得 不大受重组和 重新注入。 除了解决歧义多简单 值,指定一个索引解决歧义在一个构造函数 有相同类型的两个参数。 注意, 指数是 0基础 。 构造函数参数的名字 在Spring 3.0中,您还可以使用构造函数的参数 名称值消歧: 记住,使这项工作从盒子里你的代码 必须被编译和调试标记,以便Spring可以启用 查找参数的名字从构造函数。 如果你不能编 译 你的代码与调试标记(或不愿意)可以使用 @ConstructorProperties JDK注释明确命名您的构造函数参数。 这个 样例类必 须看起来如下: package examples; public class ExampleBean { // Fields omitted @ConstructorProperties({"years", "ultimateAnswer"}) public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } } setter建立依赖注入 setter基于 DI是完成的 容器调用setter方法在你的bean调用之后 无参数构造函数或无参数 静态 工厂 方法来实例化bean。 下面的示例显示了一个类,它只能 依赖注入的使用纯setter注入。 这个类是 传统的Java。 这是一个POJO,没有依赖于容器 具体 的接口,基类或注释。 public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder; // a setter method so that the Spring container can 'inject' a MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually 'uses' the injected MovieFinder is omitted... } 这个 ApplicationContext 支持 构造函数,对bean的setter基于DI它管理。 它还 支持基于setter经过一些依赖性都已经DI注射 通过构造函数的方法。 你配置中的依赖关系 形式的一个 BeanDefinition ,你可以用它 与 属性编辑器 实例转换 属性从一种格 式到另一个。 然而,大多数Spring用户不 使用这些类直接(编程),而是用一个 XML定义文件,然后在内部转换成的实例 这些类,用 于加载整个Spring IoC容器 实例。 依赖解析过程 容器执行bean依赖决议如下: 1. 这个 ApplicationContext 创建 在初始化配置元数据,描述所有的 豆 子。 配置元数据可以被指定通过XML、Java代码或 注释。 2. 对于每个bean,它所依赖的形式表达 属性,构造函数参数或参数 静态工 厂方法如果您使用的不是正常的 构造函数。 这些依赖项提供了豆, 当 bean实际上是创建了 。 3. 每个属性或构造函数的参数是一个实际的定义 值来设置,或引用的另一 个bean 集装箱。 4. 每个属性或构造函数的参数,它是一个值 从其指定的格式转换到实际的 类型的 属性或构造函数参数。 默认情况下弹簧可以转换一个 值在字 符串格式提供给所有内置类型,如 int , 长 , 字符串 , 布尔 等。 Spring容器的配置每个bean验证的 集装箱被创建,包括验证bean是否参考 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 44/514 使用DI,让最适合一个特定的类。 有时,当处理的 第三方类,你不 有来源,选择了你。 一个遗产类 不得 揭露任何setter方法,构造函数注入是唯一 的 可用DI。 循环依赖 如果你使用主要构造函数注入,它是可能的 创建 一个加入不能解析循环依赖的场景。 例如:A类需要B类的一个实例通过 构造函数注 入,B类需要一个类的一个实例 通过构造函数注 入。 如果你配置bean类的一种 和B注入到对 方,Spring IoC容器检测 这个循环引用在运行时, 并抛出了一个 BeanCurrentlyInCreationException 。 一个可能的解决方案是编辑源代码的一些类 通 过设置配置而不是构造函数。 另外, 避免构造函 数注入和使用setter注入只有。 在其他 话说,虽 然不推荐,您可以配置循环 依赖关系与setter注 入。 与 典型 案例(没有圆 依赖关系),一个圆形的依赖 关系和bean B部队bean 一个bean注入其他之 前被完全 初始化本身(经典鸡/蛋场景)。 属性指的是有效的豆子。 然而,该bean的属性本身 没有设置到豆吗 实际上 是创建 。 豆子是单例范围并设置为可预先实例化( 默认)是由容器创建。 范围被定义 在 SectionA 5.5,一个​scopesa​​​Bean 否则,创建了bean 只有当 它是要求。 创建bean可能导致一个图 要创建的bean,作为bean的依赖项 和它所依赖的 依赖性(等等)创建和分配。 通常你能够信任弹簧做正确的事。 它检测 配置问题,如引用不存在的豆类 和 循环依赖,在容器加载时。 弹簧设置属性 和解析依赖项尽可能晚,当 bean实际上是 创建的。 这意味着一个Spring容器加载正确的 稍后可以生 成一个异常当你请求一个对象是否有 问题,对象创建或它的一个依赖项。 例如, bean抛出一个异常结果的缺失或无效 财产。 这可能会推迟一些配置 的可见性 问题是为什么 ApplicationContext 默认实现单例bean实例化之 前。 在成本 前期的一些时间和内存来创建这些bean之前 实际需要,你发现 时配置问题 ApplicationContext 被创建,不迟。 你仍然可以覆盖这个缺省 行为,这样单例bean 将懒惰的初始化,而不是预先实例化。 如果没有循环依赖存在,当一个或更多的合作 豆子被注入依赖的bean,每个 bean合作 是 完全 配置之前被注入 依赖bean。 这意味着如果bean一依 赖的bean B,Spring IoC容器完全配置bean B之前 调用bean的setter方法 a。换句话说,这个bean 实例化(如果不是预先实例化单例),其依赖关系 集, 和相关的生命周期方法(如 配置初始化 方法 或 InitializingBean 回调方法 )被调用。 依赖注入的例子 下面的示例使用基于xml的配置元数据 setter基于DI。 一个春天的一小部 分的XML配置文件 指定一些bean定义: public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } } 在前面的示例中,要匹配的制定者是宣布的 属性中指定的XML文件。 下面的例子使用 无参迪: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 45/514 public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherBean; this.beanTwo = yetAnotherBean; this.i = i; } } 构造函数的参数中指定的bean定义将 用作参数的构造函数 ExampleBean 。 现在考虑这个例子的变种,而不是使用一个 构造函数,春天是告诉调用 静态 工厂 方法返回对象的一个实例: public class ExampleBean { // a private constructor private ExampleBean(...) { ... } // a static factory method; the arguments to this method can be // considered the dependencies of the bean that is returned, // regardless of how those arguments are actually used. public static ExampleBean createInstance ( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); // some other operations... return eb; } } 参数 静态 工厂方法是 提供通过 < constructor - arg / > 元素, 完全一样,如果一个构造函数实际上已经被使用。 这个类型的 类返回了工厂方法不需要的 同一类型的类,该类包含 静态 工厂方法,虽然在这个例子中它是。 一个实例(非静态) 工厂方法将被 用于一个本质上相同的时装(一边 从使用 工厂bean 属性而不是 这个 类 属性),所以细节将不会 在这里讨论。 5.4.2A依赖性和配置细节 正如上一节中提到的,您可以定义bean属性 和构造函数参数作为引用其他托管bean (合作者),或作为值定义内联。 Spring的基 于xml的 配置元数据支持元素类型在它 <属性/ > 和 < constructor - arg / > 元素对于这个 目的。 直值(原语, 字符串 ,所以 ) 这个 价值 属性的 <属性/ > 元素指定一个属性或 构造函数参数作为一个人类可读的字符串表示。 正如前面提到的 , JavaBeans PropertyEditors 是用来转换这些吗 字符串值从一个 字符串 实际的类型的 房地产或参数。 下面的例子使用了 p名称空间 更简洁的XML配置。 前面的XML更简洁的;然而,输入错误发现 运行时,而不是设计时间,除非你使用一个IDE如 intellij idea 或 SpringSource工具 套 件 (STS),支持自动属性完成当你 创建bean定义。 这样的IDE援助是高度 推荐。 您还可以配置一个 java util属性 实例: jdbc.driver.className=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb Spring容器内的文本转换 <价值/ > 元素到一个 java util属性 实例通过使用 JavaBeans 属性编辑器 机制。 这 是一个不错的 捷径,是为数不多的地方之一的弹簧组是怎么做的呢 支持使用嵌套的 <价值/ > 元素 在 价值 属性风格。 这个 idref 元素 这个 idref 元素是一个简单的错误证明方式 通过 id (字符串值——不是一个参考) 另一个bean在集装箱到 < constructor - arg / > 或 <属性/ > 元素。 上面的bean定义片段 到底 等效(在运行时),下面的代码片段: 第一种形式比第二个,因为使用 idref 标记允许容器来验证 在部署时 这个引用,命名 豆确实存在。 在第二个变化,没有验证 执行 的值传递到 targetName 财产的 客户 bean。 输入错误只是发现(与大多数 可能致命的结果) 客户 bean是 其实实例化。 如果 客户 bean是一个 原型 豆,这个错误 和由此产生的异常可能只被发现后很长时间 容器部署。 此外,如果引用bean是在相同的XML单位, bean的名字是bean id ,你可以使用 当地 属性,它允许XML解析器本身 验证bean id 之前,在XML文档解析时间。 一个共同的地方(至少在版本早于Spring 2.0) 在< idref / >元素带来的价值是在配置吗 的 AOP拦截器 在一个 ProxyFactoryBean bean定义。 使用 < idref / >元素当你指定拦截器名称 防止你拼错一个拦截器id。 引用其他豆类(合作者) 这个 Ref 元素是最后的元素在 < constructor - arg / > 或 <属性/ > 定义元素。 在这里你设置 指定属性的值的一个bean的 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 47/514 引用 另一个bean(一个合作者)容器来管理。 引用的 豆是一种依赖的bean的财产将被设置,它是 初始化前的随需应变属性设 置。(如果 合作者是一个单例bean,它可能被初始化了 容器)。 最终所有引用另一个对象的一个引用。 范围和验证取决于您指 定id /名称的 其他对象通过 bean , 当地的, 或 父 属性。 指定目标bean通过 bean 属性的 < ref / > 标签是最一般的 形式,允许创建一个引用到任何bean在相同的 容器或父容器,不管 它是在相同的 xml文件。 的价值 bean 属性可能是 一样 id 目标bean的属性,或作为 的一个值 名称 属性的目标 bean。 指定目标bean通过 当地 属性利用能力的XML解析器来验证XML id 在同一个文件的引用。 的价值 当地 属性必须是一样的 id 目标bean的属性。 XML解析器 问题一个错误如果没有找到匹配的元素在同一个文件中。 作为 这样,使用当地的变体是最好的 选择(为了了解 错误尽可能早的)如果目标bean是在相同的XML 文件。 指定目标bean通过 父 属性创建一个引用bean,是在一个父容器的 当前的容器。 的价值 父 属性可能是相同的或 id 属性 目标 bean,或是一个值 名称 目标bean的属性,和目标bean必须在父母 容器的当前。 你使用这个bean引用主要变体 当你有一个层 次结构的容器,你想把现有的 豆在父容器与代理,将会有相同的名称 父bean。 class="org.springframework.aop.framework.ProxyFactoryBean"> 内豆 一个 < bean / > 元素在 <属性/ > 或 < constructor - arg / > 元素定义了一个所谓的 内在bean 。 一种内在的bean定义不需要定义id或名称; 忽略这些值容器。 也忽略了 范围 国旗。 内在bean是 总是 匿名和他们 总是 创建 外部bean。 这是 不 可能将内心的豆子进 其他的合作豆子比封闭的bean。 集合 在 <列表/ > , <设置/ > , <地图/ > ,和 <道具/ > 元素,设置属性和 参数的Java 收集 类型 列表 , 集 , 地图 ,和 属性 ,分别。 administrator@example.org support@example.org development@example.org a list element followed by a reference 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 48/514 just some string 地图的价值关键或价值,或一组值,也可以 再任何下列元素: bean | ref | idref | list | set | map | props | value | null 收集合并 在Spring 2.0中,容器支持 合并 的集合。 应用程序开发人员 可以定义一个父样式 <列表/ > , <地图/ > , <设置/ > 或 <道具/ > 元素,和有孩子的风格 <列表/ > , <地图/ > , <设置/ > 或 <道具/ > 元素继承和重载值从父集合。 这 是,孩子集合的值合并 的结果吗 父母和孩子的元素集合,有孩子的 集合的元素中指定的值覆盖父 收集。 这部分讨论了亲子bean合并 机制。 父母和孩子的读者不熟悉的bean定义 可能希望阅读吗 相关部门 在继续之前。 下面的例子演示了收集合并: administrator@example.com support@example.com sales@example.com support@example.co.uk 注意使用 合并= true 属性 这个 <道具/ > 元素的 adminEmails 财产的 孩子 bean定义。 当 孩子 豆是解决和实例化 容器,生 成的实例有一个 adminEmails 属性 集合,包含结果的合并的孩子的 adminEmails 收集与父母的 adminEmails 收集。 administrator=administrator@example.com sales=sales@example.com support=support@example.co.uk 孩子 属性 集合的值设置 继承了所有的财产元素父 <道具/ > 和孩子的价值 支持 值将覆盖在父的价值 收集。 这个合并行为适用类似 <列表/ > , <地图/ > ,和 <设置/ > 集合类型。 在特定的情况下 的 <列表/ > 元素,语义 相关 列表 集 合类型, 是,的概念 下令 收藏的价值, 是维护;父母的价值观之前所有的子列表的 值。 在的情况下 地图 , 集 ,和 属性 集合类型, 没有 订购的存在。 因此没有订购语义实际上影响了 集合类型,构成相关的 地图 , 集 ,和 属性 实现类型, 容器内部使用。 限制收集合并 你不能合并不同的集合类型(如 地图 和一个 列表 ),如果你试图这样做 一个适当的 异常 抛出。 这个 合并 属性必须被指定在降 低, 继承、儿童定义;指定 合并 属性在一个父集合定义是冗余的,不会 导致所需的合并。 合并功能是只提供 在Spring 2.0和以 后。 强类型集合(Java 5 +只有) 在Java 5和以后,您可以使用强类型集合(使用 泛型类型)。 也就是说,它可以声明一个 收集 的类型,这样它只能 包含 字符串 元 素(例如)。 如果你 使用Spring依赖项注入一个强类型的吗 收集 成一个bean,您可以采取 利用弹簧的类型转换支持这样的元素 你的强类型 收集 实例转换为适当的类型添加之前 到 收集 。 public class Foo { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 49/514 private Map accounts; public void setAccounts(Map accounts) { this.accounts = accounts; } } 当 账户 财产的 foo 豆是准备注入,泛型 元素类型信息的强类型 Map < String,浮动> 可以通过 反射。 因此弹簧的类型转换基 础设施识别 各种价值元素的类型 浮 和字符串值 9.99, 2.75 ,和 3.99 转化为 实际 浮 类型。 空,空字符串值 春天 把空的参数属性等为空 字符串 。 下面的基于xml的配置 元数据代码片段设置电子邮件属性的空洞 字符串 值(“”) 前面的例子是相当于以下Java代码: exampleBean.setEmail(" ") 。 这个 < null / > 元素处理 空 值。 例如: 上面的配置相当于以下Java代码: exampleBean.setEmail(空) 。 XML快捷与p名称空间 在p名称空间允许您使用 bean 元素的属性,而不是嵌套 <属性/ > 元素,来描述你的财产 价值观和/或合作豆子。 Spring 2.0,后来支持可扩展的配置格式 使用名称空间的 ,这是基于XML 模式定义。 这个 bean 配置格式 在本章中讨论的定义 在一个XML模式文档。 然而, 没有定义的p名称空间在XSD文件和仅存在于 春天的核心。 下面的例子显示了两个XML片段,解决 同样的结果:第一次使用标准XML格式和第二次使用 p名称空间。 该示例显示了一个属性在p名称空间称为电子邮件 该bean定义。 这告诉春天包含一个属性 宣言。 正如前面提到的,p名称空间 没有 模式定义,所以你可以设置属性的名称的 属性名。 下一个示例包括两个bean定义,都有 另一个bean的引用: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 50/514 正如您可以看到的,这个例子不仅包括一个属性值 使用p名称空间,但也使用一个特殊的格式申报 属性引用。 而第一个bean定 义使用 <属性名=“配偶”ref = "简" / > 创建 从bean的引用 约翰 到bean 简 ,第二个bean定义使用 p:配偶ref =“简” 作为 一个属性来做准确的 同样的事情。 在这种情况下 配偶 是属性名, 而 ref 部分表明这不是一个 直值而是一个引用到另一个 bean。 注意 不像的p名称空间灵活为标准的XML格式。 对于 示例中,格式为声明属性引用的冲突 属性,最终在 Ref ,而标 准 XML格式不。 我们建议你选择你的方法 仔细和沟通这你的团队成员,以避免 产生的XML文档,使用这三 种方法在相同的 时间。 XML快捷与c名称空间 类似 一个​章节​XML与p-namespacea​​快捷方式 , c名称空间 ,新引进的Spring 3.1中, 允许使用内联属性进行配置,构造函数参数 而非嵌套 constructor - arg 元素。 让我们回顾的示例 一个​章节​injectiona​​无参的依赖 与 C 命名空间: <-- 'traditional' declaration --> <-- 'c-namespace' declaration --> 这个 C: 名称空间使用相同的约定的 P: 一个(落后 ref 对于bean引用) 构造函数参数的设置由他们的名字。 和一样好,这需要宣 称即使它没有定义在XSD模式 (但它存在在Spring核心)。 为罕见的情况下,构造函数参数名称不可以(通常如果字节码编译没有调试信息),一个人可以 使用回退到参数指标: <-- 'c-namespace' index declaration --> 注意 由于XML语法,该指数表示法需要领先的存在 _ 作为XML属性名称不能开始 与一些(尽管一些IDE允许它)。 在实践中,构造函数分辨率 机制 是相当有效的匹配参数 除非一个人真的需要,我们建议使用名称符号都会你的配置。 复合属性名 您可以使用复合或嵌套的属性名当你设置bean 属性,只要所有组件的路径除了最后的 属性名不 空 。 考虑以下 bean定义。 这个 foo bean有一个 弗雷德 属性,它有一个 鲍勃 属性,它有一个 萨米 财产,最后 萨米 属性被设置为值 123年 。 为了工作, 弗 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 51/514 雷德 财产的 foo ,和 鲍勃 财产的 弗雷德 不得 空 在bean被构建,或一个 NullPointerException 抛出。 5.4.3A使用 取决于 如果一个bean是一个依赖另一个通常意味着一个bean 设置属性的另一个。 通常你完成这个的 < ref / > 元素 在基于xml的配 置元数据。 然而,有时 bean之间的依赖关系不直接的;例如,一个静态的 初始化器在一个类需要被触发,比如数据库驱动程序 登 记。 这个 取决于 属性可以显式 力一个或多个bean被初始化之前使用这个bean 元素初始化。 下面的例子使用了 取决于 属性 来表达依赖于一个 单一的豆: 表达依赖于多个bean,提供一个列表的bean的名字 的值的 取决于 属性,用逗号隔开, 空格、分号、用作有效的分隔符: 注意 这个 取决于 属性在bean定义 可以同时指定一个初始化时间依赖和,对于吗 singleton bean 只是,一个相应 的破坏时间依赖性。 依赖豆子, 定义一个 取决于 关系与一个给定的bean 首先被破坏之前,给定的bean本身 遭到破坏。 因此 取决于 还可以控制关闭 订单。 5.4.4A延迟初始化的 bean 默认情况下, ApplicationContext 实现急切地创建和配置所有 singleton bean作为 初始化过程。 通常,这是以前的实例化 可 取的,因为错误的配置或周围的环境 立即被发现,而不是数小时甚至数天之后。 当 这种行为是 不 可取的,可以防止 前实例化一 个单例bean标记bean定义 延迟初始化的。 一个懒惰的初始化bean告诉IoC容器来 创建一个bean实例当它是第一个请求,而不 是在 启动。 在XML中,这种行为的控制 懒init 属性 < bean / > 元素,例如: 如果之前使用的配置 ApplicationContext ,bean命名 懒惰 不是急切地预先实例化的时候吗 ApplicationContext 是启动,而 这 个 不是懒惰 豆是急切地预先实例化。 然而,当一个延迟初始化的bean是一个单例对象的依赖 bean, 不 延迟初始化的, ApplicationContext 创建 延迟初始化的bean 在启动,因为它必须满足单例的 依赖关系。 延迟初始化的bean注入到一个单例bean 在其他地方,不是懒惰的初始化。 您还可以控制延迟初始化在集装箱的水平 使用 默认懒init 属性 < bean / > 元素,例如: 5.4.5A自动装配的合作者 Spring容器可以 自动装配 关系 在合作豆子。 你可以让春天解决合作者 (其他bean)自动为您的bean通过检查的内容 这个 ApplicationContext 。 自动装配有 以下优点: 自动装配可以显著减少需要指定属性 或构造函数参数。 (其他机制如豆的模板 讨论其他 本章 在这方面也有价值。) 自动装配可以更新一个配置对象演变。 对于 例子,如果你需要添加一个依赖关系,一个类,这种依赖性 可以满足自动而不需 要重新修改吗 配置。 因此可以在自动装配特别有用 发展,没有否定选择切换到明确的 布线当代码库会变得更加稳定。 当使用基于xml的配置元数据 [ 2 ] ,你指定的bean定义自动装配模式的 自动装配 属性的 < bean / > 元素。 自动装配的功能 五个模式。 你指定自动装配 每 bean,因此 可以选择哪些自动装配。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 52/514 5.2为多。 一个自动装配模式 模式 解释 没有 (默认)没有自动装配。 Bean引用必须 定义通过 Ref 元素。 更改默认 不建议设置为较大的部署,因为 指定显 式地给了更大的控制权和合作者 清晰。 在某种程度上,它文档的结构 系统。 别名 自动装配的属性名。 春天寻找一个bean 具有相同名称的属性,需要autowired的。 对于 例子,如果一个 bean定义设置为自动装配的名字,和它 包含一个 主 属性(即,它有一个 setMaster(. .) 方法),弹簧查找 bean 定义命名 主 ,使用它 设置此属性。 byType 允许一个属性是autowired的如果整整一个bean 属性的类型存在于容器。 如果超过一个 存在,一个致命的 异常被抛出,这表明你可能 不使用 byType 自动装配的bean。 如果 没有匹配的豆子,什么也不会发生;财产不 是 设置。 构造函 数 类似于 byType ,但应用 到构造函数参数。 如果有不准确的一个bean 构造函数参数类型的容器,一个致命错 误 提高。 与 byType 或 构造函数 自动装配模式,你可以线阵列和类型的集合。 在这种情况下 所有 自动装配的候选人在容器内, 匹配预期 的类型提供了满足依赖关系。 你可以 如果预期强类型的地图自动装配关键类型 字符串 。 一个地图将包括autowired的价值观 所有bean实例匹配预期的类型,和地图键将 包含相应的bean的名字。 你可以结合自动装配行为检查依赖性,这是 自动装配完成后进行。 局限性和缺点的自动装配 自动装配工作最好当它用于跨一个项目。 如果不使用自动装配在一般情况下,它可能是困惑 开发人员使用它来线只有一个或两 个bean定义。 考虑的限制和缺点的自动装配: 显式的依赖在 财产 和 constructor - arg 设置总是覆盖 自动装配。 你不能自动装配所谓 简单 属性如原语, 字符串 ,和 类 (和数组这样的简单属性)。 这个限制是 通过设计。 自动装配并不完全像显式布线。 不过,由于 指出在上面的表中,春天是小心避免猜测在 模棱两可的情况,可能意想不到的结 果, 你的spring管理对象之间的关系不再是 明确地描述。 布线信息可能不能使用的工具,可能 从Spring容器生成文档。 多个bean定义在容器内可能匹配 指定的类型setter方法或构造函数的参数 autowired的。 为数组、集合或地图,这是不一 定的 一个问题。 然而对于依赖关系,期望一个单一值,这 歧义是不随意解决。 如果没有独特的bean定义 可用,就抛出一个 异常。 在后一种情况下,你有几种选择: 放弃自动装配支持显式布线。 避免自动装配为一个bean定义通过设置它 自动装配的候选人 属性来 假 在下一节中描述。 指定一个bean定义的 主要 候选人通过设置 主要 属性的 < bean / > 元素 真正的 。 如果您使用的是Java 5或更高版本,实现更多 细粒度的控制可提供基于注解的配置, 中描述的 SectionA 5.9,一个​​ configurationa​​基于注解的容器 。 不包括一个bean从自动装配 每个bean上基础上,你可以排除一个bean从自动装配。 在 Spring的XML格式,设置 自动装配的候选人 属性的 < bean / > 元 素 假 ;容器使特定bean 定义不能自动装配的基础设施(包括 注释风格配置如 @ autowired )。 你也可以限制自动装配候选人基于模式匹配 对bean名称。 顶级 < bean / > 元素接受一个或多个模式在它 默认自动装配候选 人 属性。 例如, 自动装配的候选人地位来限制任何bean的名字结尾 库, 提供的值*存储库。 到 提供多种模式,定义在一个以逗 号分隔的列表。 一个 显式值 真正的 或 假 对于一个bean定义 自动装配的候选人 属性 总是优先,并且这样的豆子,模式匹配规 则 不适用。 这些技巧是很有用的,你再也不想咖啡豆是 其他bean注入由自动装配。 这并不意味着一个 排除bean本身不能被配置使用自动 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 53/514 你可以阅读更多关于动机的方法注入 这个博客 条目 。 装配。 相反, bean本身不是一个候选人对自动装配其他豆类。 5.4.6A方法注入 在大多数应用程序场景,大多数豆类的容器 单件 。 当一个 单例bean需要与另一个单例bean,或者一个 非单体bean需要与另一 个单体豆, 你通常处理依赖通过定义一个bean的属性 其他的。 一个问题会在bean的生命周期是不同的。 假设singleton豆一 个需要使用非单体(原型)bean B, 也许在每个方法调用在a .容器仅创建 单例bean一次,因此只有得到一个机会来设置 属性。 容 器不能提供bean与一个新实例的一个 每次一豆B需要。 一个解决办法是放弃一些控制反转。 你可以 让豆一个意识到容器 通过 实施 ApplicationContextAware 接口,通过 做 getBean(" B ")调用容器 要求(一个典型的新)bean B 实例一个需要每次bean。 下面是一个例子 方法: // a class that uses a stateful Command-style class to perform some processing package fiona.apple; // Spring-API imports import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class CommandManager implements ApplicationContextAware { private ApplicationContext applicationContext; public Object process(Map commandState) { // grab a new instance of the appropriate Command Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } protected Command createCommand() { // notice the Spring API dependency! return this.applicationContext.getBean("command", Command.class); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } 前是不可取的,因为业务代码是意识到 和耦合到Spring框架。 方法注射,有些先进的 Spring IoC容器的特点,允许这个用例的处 理 一个干净的 时尚。 查找方法注入 查找方法注入容器的能力覆盖 方法 容器托管bean ,返回 查找结果另一个 名叫豆在容器。 查找 通常涉及一个原型bean在描述的场景 前一节。 Spring框架实现了这个方法注入 通过使用字节码生成从CGLIB来生成 动态 子类,覆盖了 法。 注意 对于这个动态子类化工作,春天的类 容器将子类不能 最后 ,和 方法不能被覆盖 最后 要么。 同时, 测试一个 类,它有一个 文摘 方法需要 你自己和子类的类提供一个存根实现 的 文摘 法。 最后,对象 被目标的方法注 射不能序列化。 像春天的 3.2不需要添加CGLIB到您的类路径中,因为 CGLIB类打包在org。 springframework和分布式 在spring核心JAR。 这样做既方便 以避免潜在的冲突与其他项目使用不同的 版本的CGLIB。 看 commandManager 类 之前的代码片段,你看到Spring容器将 动态覆盖的实施 createCommand() 法。 你 commandManager 类将不会有任何的春天 依赖关系,可以看到在返工的例子: package fiona.apple; // no more Spring imports! public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 54/514 command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); } 在客户端类中包含的方法注射( commandManager 在这种情况下),方法 需要签名的注入以下表格: [abstract] theMethodName(no-arguments); 如果这个方法是 文摘 , 动态生成子类实现了该方法。 否则, 动态生成子类覆盖混凝土方法定义在 原来的类。 例如: bean标识为 commandManager 调用它的 自己的方法 createCommand() 每当它需要一个 的新实例 命令 bean。 你必须 小心部署 命令 bean作为原型,如果 这实际上是所需要的。 如果它是部署为一个 singleton ,同样的 实例的 命令 bean返回每 个 时间。 提示 感兴趣的读者也可以找到 ServiceLocatorFactoryBean (在 org.springframework.beans.factory.config 包) 使用的。 ServiceLocatorFactoryBean中使用的方法是 类似于另一个实用程序类, ObjectFactoryCreatingFactoryBean ,但它允许 您指定您自己的查询接口而不是一个 spring特定查询界 面。 参考这些javadoc 类以及这 博客条目 了解更多信息 ServiceLocatorFactoryBean。 任意方法替代 不太有用的形式的方法注入比查找方法注入 是能够代替任意方法在一个托管bean 另一个方法的实现。 用户可以安全地忽略其 余的 节,直到功能实际上是 需要。 与基于xml的配置元数据,您可以使用 替换方法 元素替换现有的方法 实现与另一个部署bean。 考虑以下 类,有一个方法,我们想 要computeValue覆盖: public class MyValueCalculator { public String computeValue(String input) { // some real code... } // some other methods... } 一个类实现 org.springframework.beans.factory.support.MethodReplacer 接口提供了新方法定义。 /** meant to be used to override the existing computeValue(String) implementation in MyValueCalculator */ public class ReplacementComputeValue implements MethodReplacer { public Object reimplement(Object o, Method m, Object[] args) throws Throwable { // get the input value, work with it, and return a computed result String input = (String) args[0]; ... return ...; } } 该bean定义部署原始类和指定 方法覆盖会看起来像这样: String 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 55/514 您可以使用一个或多个包含 <自变量类型/ > 元素在 <替换方法/ > 元素来表示 方法签名的方法被覆盖。 签名的 参数是必须 的,如果方法重载和多 变异存在于类。 为方便起见,这些类型的字符串 子字符串的参数可以是一个完全限定类型名称。 对于 的 例子,下面的所有比赛 以 : java.lang.String String Str 因为数量的参数通常是足够的区分 在每个可能的选择,这个快捷方式可以节省很多的打字,通过 允许您只输入字符串,将匹配的最 短的一个 参数类型。 5.5一个Bean范围 当你创建一个bean定义,您创建一个 配方 创建实际的类的实例 定义的bean定义。 认为一个bean定义是一个配方 很重要,因为 它意味着,与一个类,你可以创建很多吗 对象实例从单一配方。 你不仅可以控制各种依赖关系和配置 值将被插入一个对象,创建于一个 特定的bean定义,但也 范围 的 从一个特定的对象创建 的bean定义。 这种方法是强大的 和灵活,你可以 选择 的范围 您创建的对象通过配置而不必再烤 一个对象的范围在Java类级 别。 bean可以被定义 部署在一个数量的范围:盒子之外,Spring框架 支持5个范围,其中三只提供如果你使用 理解网络 ApplicationContext 。 以下范围的支持从盒子里。 您还可以创建 一个自定义的范围。 5.3为多。 一个Bean范围 范围 描述 singleton (默认)范围一个bean定义到一个 对象实例/ Spring IoC容器。 原型 范围一个bean定义任意数量的对象 实例。 请求 一个bean定义范围的生命周期 单一的HTTP请求;也就是说,每个HTTP请求有自己的实例 一个bean创建了 后面的一个bean定义。 只有 有效的上下文中的理解网络弹簧 ApplicationContext 。 会话 范围一个bean定义的生命周期 HTTP 会话 。 只有有效的 上下文理解网络的春天 ApplicationContext 。 全球会话 一个bean定义范围的生命周期 全球HTTP 会话 。 通常只有 有效使用时,在一个portlet上下文。 只有有 效的上下文中的一个 理解网络春天 ApplicationContext 。 线程级的豆子 在Spring 3.0中,一个 线程范围 是可用的, 但不是默认注册。 有关更多信息,请参见 文档 SimpleThreadScope 。 请示如何注册这个或 任何其他自定义范围,见 一个​​章节使用一个自定义scopea​​ 。 5.5.1A singleton范围 只有一个 共享 一个单例实例bean 管理,所有请求bean id或id匹配的bean 在一个特定的定义结果被返回的bean实例 Spring 容器。 换句话说,当你定义一个bean定义和它是 作用域作为一个单例,Spring IoC容器创建 到底 一个 对象的实例定义的bean定义。 这单实例存储在缓存这样的单例豆类, 所有后续请求和引用 这名叫 bean返回缓存的对象。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 56/514 春天是一个单例bean的概念不同于单例 模式中定义的四人帮(GoF)模式的书。 “四人帮” 单例对象的范围将这样一个 和 只 有一个 一个特定的类的实例被创建 每 类加载器 。 春天的范围 单例是最好的形容 每集装箱和每 bean 。 这意味着如果你定 义一个bean为一个特定的 类在一个Spring容器,然后Spring容器创建一个 且只有一个 类的实例定义的 bean定义。 单例的范 围是默认的范围 春天 。 定义一个bean作为一个单独的XML,你会 写,譬如: 5.5.2A原型范围 单体的,原型范围部署结果。豆 创建一个新的bean实例 每次请求 对该特定bean是由。 即,bean注入另一个 bean或你要求它 通过 getBean() 方法调用 在容器上。 作为一个规则,使用原型范围为所有状态 豆类和singleton范围为无状态bean。 下面的图展示了弹簧原型范围。 一个数据访问对象(DAO)不是通常配置为一个 原型,因为典型的刀不持有任何会话状态; 这只是 作者更容易重用核心的单例 图。 下面的例子定义了一个bean作为原型在XML: 与其他范围,弹簧不管理完成 生命周期的一个原型bean:容器实例化、配置和 否则组装一个原型对象,并把它递给客户, 没有进一 步的记录原型实例。 因此,尽管 初始化 生命周期回调方法被称为 在所有对象无论范围,对于原型、配置 破坏 生命周期回调 不 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 57/514 称为。 客户机代码必须清理 原型作用域对象和释放昂贵的资源 原型bean(s)控股。 让Spring容器释放 持有的资源原型作用域 的豆子,尝试使用一个自定义的 豆后处理器 ,这 持有bean,需要清理。 在某些方面,Spring容器的角色对于一个 原型作用域bean是一个替代Java 新 操作符。 所有的生命周期管理过去这一点必须处 理 客户端。 (有关的生命周期bean在Spring容器, 看到 SectionA 5.6.1,​​生命周期callbacksa​​ )。 5.5.3A单例bean与原型bean依赖关系 当你使用单例范围bean与依赖的原型 豆子,要知道 在实例化时解析依赖项 时间 。 因此如果你依赖注入一个原型作用域bean 成一个单例作用域的bean,一个新的原型bean实例化和 然后依赖注入到单例bean。 原型实例 是有史以来唯一实例提供给 singleton范围 bean。 然而,假设您希望singleton范围bean来获得一个新的 bean的实例在运行时反复原型作用域。 你不能 依赖注入一个原型作用域 bean到你的单例bean, 因为注射只发生 一旦 ,当 Spring容器实例化bean是singleton和解决和 其依赖项注入。 如果你需要一 个新实例的原型bean 在运行时不止一次看到 SectionA 5 4 6,一个​​injectiona​​方法 5.5.4A请求、会话和全球会话作用域 这个 请求 , 会话 ,和 全球会话 范围 只有 如果你使用一个理解网络可用春天 ApplicationContext 实现(如 XmlWebApplicationContext )。 如果你使用这些范围 与常规Spring IoC容器如 ClassPathXmlApplicationContext ,你得到 一个 IllegalStateException 抱怨一个未知的 豆范围。 初始web配置 支持bean的作用域的 请求 , 会话 ,和 全球会话 水平 (web范围bean),一些次要的初始配置之前,需要进行 你定义你的豆子。 (这个初始设置 不 所需的标准范围,singleton和原型)。 你如何完成这取决于您的特定初始设置 Servlet环境. . 如果你访问范围bean中Spring Web MVC,实际上,在 一个请求所处理的春天 DispatcherServlet ,或 DispatcherPortlet ,然后 没有特殊设置 必要的: DispatcherServlet 和 DispatcherPortlet 已经暴露所有相关 状态。 如果你使用一个Servlet 2.4 + web容器,处理的请求 Spring的DispatcherServlet之外(例如,当使用JSF或 Struts),您需要添加以 下 javax.servlet.ServletRequestListener 到 在您的web应用程序的声明 web . xml 文件: ... org.springframework.web.context.request.RequestContextListener ... 如果你使用一个年长的web容器(Servlet 2.3),使用提供的 javax.servlet.Filter 实现。 这个 以下代码片段的XML配置必须包含 在 web . xml 文件的web应用程序,如果你想 访问web范围豆子在请求之外的春天的 DispatcherServlet在Servlet 2.3容器。 (过滤器映射 取决于周围的web应用程序的配置,所以你必须 适当的改变)。 .. requestContextFilter org.springframework.web.filter.RequestContextFilter requestContextFilter /* ... DispatcherServlet , RequestContextListener 和 RequestContextFilter 所有做完全一样的 东西,即HTTP请求对象绑定到 线 程 这是服务请求。 这使得 豆子是请求和会话范围内可用的更低 调用链。 请求范围 考虑下面的bean定义: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 58/514 Spring容器创建一个新的实例 loginAction bean通过使用 loginAction bean定义每个HTTP 请求。 这是, loginAction bean 是作用域在 HTTP请求级别。 你可以改变内部状态 实例创建尽可能多的你想要的,因为其他实例 创建相同的 loginAction bean定义 不会看到这些变化在状态;他们是特别的一个 单个请求。 当请求完成加工,bean 作用域是这个请求就会被丢弃。 会话范围 考虑下面的bean定义: Spring容器创建一个新的实例 userPreferences bean通过使用 userPreferences bean定义的生命周期 单一的HTTP 会话 。 换句话说, userPreferences 豆是有效的范围 HTTP 会话 水平。 与 会 豆子,你可以改变内部 状态的实例创建尽可能多的你想要 的,知道 其他HTTP 会话 实例 也使用相同的实例被创建 userPreferences bean定义看不到这些 状态的变化,因为他们是特定于 单个HTTP 会话 。 当HTTP 会话 最终丢弃,豆吗 这是局限于特定的HTTP 会话 也被丢弃。 全球会话范围 考虑下面的bean定义: 这个 全球会话 范围是相似的 标准HTTP 会话 范围( 上面所描述的 ),和 仅适用于基于portlet的web应用程序的上下文。 这个 portlet规范定义了一个全局的概念 会话 这是所有portlet之间共享 建立了一个单个portlet的web应用程序。 bean定义 全球 会话 范围是作用域(或约束) 一辈子的全球portlet 会话 。 如果你写一个标准的基于servlet的web应用程序,您定义 一个或多个bean有 全球会话 范围, 标准HTTP 会话 范围使用,和 没有 错误了。 范围bean作为依赖项 Spring IoC容器实例化管理不仅你的 对象(豆子),而且连接的合作者(或 依赖关系)。 如果你想注入(例如)一个HTTP请求 作用域 bean到另一个bean,您必须注入AOP代理代替 作用域的bean。 这是,你需要注入一个代理对象,暴露了 相同的公共接口的作用 域对象,但也可以 获取真正的、目标对象相关的范围(例如, 一个HTTP请求)和委托方法调用到真正的对象。 注意 你 不 需要使用 < aop:作用域的代理/ > 结合豆类 这是作用域作为 单件 或 原型 。 配置在下面的例子只有一线,但它 是重要的理解 一个​​ 为什么 一个​​ 以及 一个​​ 如何 一个​​ 它背后。 创建这样一个代理,你插入一个孩子 < aop:作用域的代理/ > 元素到一个限定了作用域的bean 定义。 看到 一个​​章节选择类型 的代理创建​​ 和 AppendixA E, XML的基于配置 )。 为什么定义bean的作用域在吗 请求 , 会话 , globalSession 和自定义范围 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 59/514 的水平要求 < aop:作用域的代理/ > 元素? 让我们检查 以下单例bean定义和对比它与你需要的 定义为上述范围。 (以下 userPreferences bean定义目前是 不完整。) 在前面的示例中,单例bean userManager 被注射到HTTP引用 会话 范围bean userPreferences 。 这里的要点是, userManager 豆是一个单例:这将是 实例化 正好一次 每集装箱,和它的 依赖项(在本例中只有一个, userPreferences bean)也 只有一次注射。 这意味着 userManager bean只会 操作完全相同 userPreferences 对象, 这就是,一个,原来的注射。 这是 不 你想要的行为当 注射到一个长期寿命较短的作用域bean范围豆, 例如注射一个HTTP 会话 作用域合作作为。豆 成单例 bean的依赖。 相反,你需要一个单身 userManager 对象,和生命周期中的一个HTTP 会话 ,你需要一个 userPreferences 对象, 该对象是特定于说HTTP 会话 。 因此容器创建一个 对象,暴露了完全相同的公共接口 userPreferences 类(理想情况下一个对 象 是 userPreferences 实例)可以获取真正的 userPreferences 对象的范围界定机制 (HTTP请求, 会话 等等)。 这个 这个代理 对象容器注入到 userManager 豆,这是不知道这个 userPreferences 引用是一个代理。 在这个 例如,当一个 userManager 实 例 调用一个方法依赖注入 userPreferences 对象,它实际上是调用 方法在代理。 然后,代理获取真正需要的 userPreferences 对象(在本例中) HTTP 会话 ,代表该方法 调用到检索的实际 userPreferences 对象。 因此你需要以下,正确和完整、配置 当注入 请求- , 会话—— , 和 globalSession-scoped bean到合作 对象: 选择类型的代理创建 默认情况下,Spring容器创建一个代理,一个bean 这是明显的 < aop:作用域的代理/ > 元素, 一个 基于cglib代理创建类 。 注意:CGLIB代理只有截距公共方法 电话! 不叫非公有制方法在这样一个代理;他们吗 将不会被委托给目标对象的作用域。 或者,您可以配置Spring容器创建 标准JDK基于接口的代理这样的作用域豆类, 指定 假 的值 代理目标类 属性的 < aop:作用域 的代理/ > 元素。 使用JDK 基于接口的代理意味着您不需要额外的 图书馆在你的应用程序的类路径效果这样的代理。 然而,这 也意味着类的作用域bean必须 实现至少一个界面,和 所有 合作者的作用域bean注入必须参考 bean通过它的一个接口。 对于更详细的信息关于选择基于类的或 基于接口的代理,看 SectionA 9.6,一个​​mechanismsa​​代理 。 5.5.5A自定义范围 在Spring 2.0中,bean范围界定机制是可扩展的。 你可以 定义自己的范围,甚至重新定义现有的范围,虽然 后者被认为是糟糕的 实践和你 不能 覆盖内置 singleton 和 原型 作用域。 创建一个自定义的范围 将您的自定义范围(s)到Spring容器,你 需要实现 org.springframework.beans.factory.config.Scope 接口,它是这一节中描 述。 对于一个想法如何 实现自己的范围,请参阅 范围 实现,提供Spring框架本身和 这个 范围Javadoc ,这解释了需要实现的方 法 在更多的细节。 这个 范围 界面有四个方法来获取 对象的范围,删除它们从范围,并允许他们 摧毁了。 以下方法返回对象从底层的范围。 会话作用域实现,例如,返回 会话范围内的bean(如果它不存在,该方法返回一个新的 bean的 实例之后,将它绑定到会话为未来 引用)。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 60/514 Object get(String name, ObjectFactory objectFactory) 以下方法删除对象从底层的范围。 会话作用域实现例如,删除会话范围内 从底层的会话bean。 应该返回的对象,但你 可以返回 null如果对象与指定的名字不是吗 发现。 Object remove(String name) 下面的方法注册回调的范围应该 当它被销毁或执行规定的对象范围 被摧毁。 请参考Javadoc或弹簧范围实现 更多的信息在破 坏回调。 void registerDestructionCallback(String name, Runnable destructionCallback) 下面的方法获得对话标识 潜在的范围。 这个标识符是为每一个范围不同。 对于一个 会话作用域实现,这个标识符可以会话 标 识符。 String getConversationId() 使用一个自定义的范围 在编写和测试一个或多个自定义 范围 实现,你需要做的 Spring容器意识到你的新范围(年代)。 下面的方法是 中央方法来注册 一个新的 范围 与Spring容器: void registerScope(String scopeName, Scope scope); 该方法中声明的 ConfigurableBeanFactory 接口,它 可以在大多数的混凝土 ApplicationContext 实现 船与Spring BeanFactory属性。通过 的第一个参数 套色定位观察仪(. .) 方法是唯一的名称与范围关联;这样的例子 名字在Spring容器本身 singleton 和 原型 。 第 二个参数 套色定位观察仪(. .) 方法是一个实际的实例 定制 范围 实现 你想注册和使用。 假设您编写自定义 范围 实现,然后注册 它如下。 注意 下面的例子使用 SimpleThreadScope 哪一个 包含在春天,但不是默认注册。 这个 指令将同样的您自己的 自定义 范围 实现。 Scope threadScope = new SimpleThreadScope(); beanFactory.registerScope("thread", threadScope); 然后,您创建的bean定义范围规则的遵守 您的自定义 范围 : 用一个自定义的 范围 的实现, 你是不限于程序性登记的范围。 你可以 也做 范围 登记 通过声明,使用 CustomScopeConfigurer 类: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 61/514 注意 当你把< aop:作用域的代理/ >在 FactoryBean 实现,它是 工厂bean本身是作用域,而不是返回的对象 getObject() 。 5.6一个定制bean的性质 5.6.1A生命周期回调 可以与容器的管理bean的生命周期,你 可以实现春天 InitializingBean 和 DisposableBean 接口。 这个 容器调用 afterPropertiesSet() 为 前和 destroy() 后者允许 bean来执行特定操作在初始化和销毁 你的豆子。 提示 jsr - 250 @PostConstruct 和 @PreDestroy 注释通常 被认为是最佳实践在现代接受生命周期回调 Spring 应用程序。 使用这些注释意味着您的bean是不 耦合弹簧特定的接口。 详情见 SectionA 5 9 6,一个​​ @PostConstruct 和 @PreDestroy 一个​​ 。 如果你不想使用jsr - 250注释但你仍然 想消除耦合考虑使用init方法和销毁方法 对象定义元数据。 在内部,Spring框架使用 BeanPostProcessor 实现 处理任何回调接口可以找到并调用合适的 方法。 如果你需要定制功能或其 他生命周期行为弹簧 不提供开箱即用的,您可以实现吗 BeanPostProcessor 你自己。 更多 信息,请参阅 SectionA 5.8,一个​​容 器扩展Pointsa​​ 。 除了初始化和销毁回调, spring管理对象也可以实现 生命周期 接口,以便这些对象 可以参与启动和关闭过程驱动的吗 容器的自 己的生命周期。 生命周期回调接口描述 部分。 初始化回调 这个 org.springframework.beans.factory.InitializingBean 接口允许一个bean来执行初始化工作毕竟 必要的属性在bean已 经设定的容器。 这个 InitializingBean 接口指定了一个 单一的方法: void afterPropertiesSet() throws Exception; 建议您不要使用 InitializingBean 接口,因为它 不必要的代码来春夫妻。 另外,使用 @PostConstruct 注释 或指定一个POJO 初始化方法。 对于基于xml的配置元数据, 你使用 init方法 属性指定的名字 的方法有一个空白无参数签名。 例如, 以下定义: public class ExampleBean { public void init() { // do some initialization work } } … 完全一样… public class AnotherExampleBean implements InitializingBean { public void afterPropertiesSet() { // do some initialization work } } … 但没有几个春天的代码。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 62/514 破坏回调 实施 org.springframework.beans.factory.DisposableBean 接口允许一个bean来得到一个回调当容器包含 它被摧毁。 这个 DisposableBean 接口指定了一个简单的方法: void destroy() throws Exception; 建议您不要使用 DisposableBean 回调接口,因为 它不必要的代码来春夫妻。 另外,使用 @PreDestroy 注释 或指定一个 泛型 方法,支持bean定义。 与基于xml的 配置元数据,您使用 销毁方法 属性 < bean / > 。 例如, 以下定义: public class ExampleBean { public void cleanup() { // do some destruction work (like releasing pooled connections) } } … 完全一样… public class AnotherExampleBean implements DisposableBean { public void destroy() { // do some destruction work (like releasing pooled connections) } } … 但没有几个春天的代码。 默认的初始化和销毁方法 当你写回调函数,初始化和销毁方法做 不使用spring特定 InitializingBean 和 DisposableBean 回调接口,你 通常写方法等名称 init() , 初始化() , dispose() ,所以 在。 理想情况下,名称这样的生命周期回调方法 标准化的跨项目以便所有开发人员使用相同 的方法 名称和确保一致性。 您可以配置Spring容器 看 指定的初始化和销毁的回调方法上的名字 每一 bean。 这意味着,你,作为一个应用程序 开发人员,可 以编写应用程序类和使用一个初始化 调称为 init() ,无需配置 一个 init方法=“init” 属性与每个bean 定义。 Spring IoC容器 调用该方法当bean 创建(并符合标准的生命周期回调契约 前面描述的)。 这个特性还执行一致的命名 公约初始化和销毁方法回 调。 假设你的初始化回调方法的命名 init() 并摧毁回调方法的命名 destroy() 。 你的类将像中的类 下面的例子。 public class DefaultBlogService implements BlogService { private BlogDao blogDao; public void setBlogDao(BlogDao blogDao) { this.blogDao = blogDao; } // this is (unsurprisingly) the initialization callback method public void init() { if (this.blogDao == null) { throw new IllegalStateException("The [blogDao] property must be set."); } } } 的存在 默认init方法 属性 在顶级 < bean / > 元素属性 使Spring IoC容器识别方法被称为 INIT 在bean作为初始化方法的回 调。 当一个bean创建并组装,如果有这样一个bean类 法,它是在适当的时候调用。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 63/514 你配置回调的销毁方法同样(XML), 通过使用 默认销毁方法 属性 顶级 < bean / > 元素。 在现有的bean类已经回调方法,是吗 命名不大会,您可以覆盖默认的 指定(XML),方法名使用 init方法 和 销毁方法 属性的< bean / >本身。 Spring容器保证配置的初始化 回调后立即调用bean提供所有 依赖关系。 因此,初始化的回调是呼吁生豆 参考,这意味着AOP拦 截器等等都没有 应用到bean。 一个目标bean是完全创建 第一 , 然后 AOP代理( 例)以其拦截器链是应用。 如果目标bean和 代理是单独定义,你的代码甚至可以与其进行交互 生目标bean,绕过代理。 因此,这将是不一致的, 应用拦截器到init方法,因为这 样做会夫妇 目标bean的生命周期与其代理/拦截器和离开 奇怪的语义代码交互时直接向原始目标 bean。 结合生命周期机制 在Spring 2.5中,您有三个选项控制bean 生命周期行为: InitializingBean 和 DisposableBean 回调 接口;自定义 init() 和 destroy() 方法; @PostConstruct 和 @PreDestroy 注释 。 你可以 结合这些机制来控制一个bean。 注意 如果多个生命周期机制配置为一个bean,并 每个机制都配置了一个不同的方法名称,然后每个 配置的方法是 执行在下面列出的顺序。 然而,如果 配置相同的方法名,例如, init() 对于一个初始化方法——超过 其中的一 个生命周期机制,该方法被执行一次, 在前一节中解释。 多个生命周期机制配置为相同的bean, 不同的初始化方法,被称为如下: 的方法 @PostConstruct afterPropertiesSet() 所定义的 InitializingBean 回调 接口 一个自定义配置 init() 方法 破坏的方法被称为以相同的顺序: 的方法 @PreDestroy destroy() 所定义的 DisposableBean 回调 接口 一个自定义配置 destroy() 方法 启动和关闭回调 这个 生命周期 接口定义了 基本方法的任何对象都有自己的生命周期需求 (如启动和停止一些背景过程): public interface Lifecycle { void start(); void stop(); boolean isRunning(); } 任何spring管理对象可以实现该接口。 然后,当 本身的ApplicationContext启动和停止,它会级联这些 调用所有生命周期实现 定义在这个上下文。 它 这是否通过指派给一个吗 LifecycleProcessor : public interface LifecycleProcessor extends Lifecycle { void onRefresh(); void onClose(); } 注意, LifecycleProcessor 是 本身的延伸 生命周期 接口。 它还添加了其他两个方法来应对上下文 被刷新和关闭。 启动和关闭的顺序调用可能是重要的。 如果一个 “依赖”关系存在任何两个物体之间的依赖 侧将开始 在 它的依赖,它将 停止 之前 它的依赖。 然而,有时 直接依赖关系是未知的。 你可能只知道对象的一个 某些类型的对象之前,应该开始另一个类型。 在 那些 情况下, SmartLifecycle 接口 定义了另一个选择,即 getPhase() 方法定义在它的超接口相反, 阶段性 。 public interface Phased { int getPhase(); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 64/514 } public interface SmartLifecycle extends Lifecycle, Phased { boolean isAutoStartup(); void stop(Runnable callback); } 在启动时,这些对象与最低的阶段开始前, 当停止,后面是相反的顺序。 因此,一个对象,该对象 实现 SmartLifecycle 和他 getPhase()方法返回 整数最小值 将 最早开始和最后停止。 在另一端的 谱,相位值 integer . max_value之间 将 表明该对象应 该开始于去年和停止第一 (可能是因为它依赖于其他进程运行)。 当 考虑到相位值,同样重要的是知道的 默认的阶段对于任 何“正常” 生命周期 对象,没有实现 SmartLifecycle 将是0。 因此,任何 负相位值将显示一个对象应该开始之前 这些标准组件 (和停止在他们之后),反之亦然,任何 积极的相位值。 正如你可以看到停止法所定义的 SmartLifecycle 接受一个回调。 任何 实现 必须 调用这个回调的run() 方法之后,关闭过程完 成实现的。 这 使异步关闭在必要时由于默认 实施 LifecycleProcessor 界面, DefaultLifecycleProcessor ,会等待 其超时的值 对象组在每个阶段 调用这个回调。 每阶段的默认超时时间是30秒。 你 可以覆盖默认的生命周期处理器实例,通过定义一个豆 吗 命名为“lifecycleProcessor”范围内。 如果你只是想 修改超时,然后定义以下会 足够的: 正如前面提到的, LifecycleProcessor 接口定义了回调方法的更新和关闭 背景和。 后者将只是把关机过程好像 停止()被称为明 确,但它会发生当上下文 是关闭的。 “刷新”回调另一方面使另一个 特性的 SmartLifecycle 豆子。 当 上下文是刷新(毕竟已 经实例化的对象, 初始化),将调用的回调,而那时 缺省生命周期处理器会检查返回的布尔值 每个 SmartLifecycle 对象的 isAutoStartup() 法。 如果“真正的”,然后, 对象将开始在这一点上,而不是等待一个显式的 调用上下文的或自己的start()方法 (不像 上下文刷新,上下文的开始并不自动发生对于一个 标准上下文实现)。 “相”的价值以及任何 “依赖”关系将决定启动顺 序相同 方法如上所述。 关闭Spring IoC容器优雅地在非web 应用 注意 本节仅适用于非web应用程序。 春天的 网络 ApplicationContext 实现已经有代码后关闭Spring IoC 集装 箱优雅地在有关web应用程序关闭 下来。 如果您正在使用Spring的IoC容器在非web应用程序 环境;例如,在一个富客户端桌面环境;你 注册一个关机钩与JVM。 这样做 可以确保一个优雅 关闭并调用相关的破坏方式单例bean 所以,所有资源被释放。 当然,您还必须配置 和实现这些破坏回调正 确。 注册一个关机钩,你所说的 registerShutdownHook() 方法中声明 在 AbstractApplicationContext 类: import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public final class Boot { public static void main(final String[] args) throws Exception { AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(new String []{"beans.xml"}); // add a shutdown hook for the above context... ctx.registerShutdownHook(); // app runs here... // main method exits, hook is called prior to the app shutting down... } } 5.6.2A ApplicationContextAware 和 BeanNameAware 当一个 ApplicationContext 创建一个 类,实现了 org.springframework.context.ApplicationContextAware 接口,类提供一个 参考的 ApplicationContext 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 65/514 public interface ApplicationContextAware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; } 因此大豆可以编程操作了 ApplicationContext 创造了他们, 通过 ApplicationContext 界面, 或者通过铸造引用到一个已知的 子类这个接口,例如 ConfigurableApplicationContext ,它公开了 额外的功能。 一个使用将是程序化的检索 其他豆类。 有时 这种能力是有用的;然而,在一般的你 应该避免使用它,因为它夫妇代码以春季和不遵循 控制反转的风格,合作者是提供给豆子 作 为属性。 其他方法的ApplicationContext提供 文件资源,发布应用程序事件,和访问 MessageSource。 这些额外的功能描述 SectionA 5.14,一个​​附加的功能 ApplicationContext 一个​​ Spring 2.5的,自动装配是另一个替代获得 参考 ApplicationContext 。 这个 “传统” 构造函数 和 byType 自动装配模式(详 见 SectionA 5 4 5,一个​​collaboratorsa​​自动装配 )可以提供一个依赖的类型 ApplicationContext 对于一个构造函数 参数或 setter方法参数,分别。 更多的灵活性, 包括自动装配领域的能力和多参数的方法, 使用新的基于注解的自动装配功能。 如果你 这样做了, ApplicationContext autowired成是 域、构造参数或方法参数,是期待 ApplicationContext 如果字段类型, 构造函 数或方法有问题 @ autowired 注释。 更多 信息,请参阅 SectionA 5 9 2,一个​​ @ autowired 一个​​ 。 当创建一个类,它实现了ApplicationContext的 org.springframework.beans.factory.BeanNameAware 接口,类提供一个名称 的引用定义在 它相关的对象定义。 public interface BeanNameAware { void setBeanName(string name) throws BeansException; } 调用回调后的人口正常bean属性但 在一个初始化的回调如 InitializingBean 年代 afterPropertiesSet 或一个自定义的init方 法。 5.6.3A其他 意识到 接口 除了 ApplicationContextAware 和 BeanNameAware 上面所讨论的,春天 提供一系列的 意识到 接口, 让豆子来指示容器,他 们需要一个特定的 基础设施 依赖项。 最重要的 意识到 接口是总结如下,作为 一般来说,这个名字是一个好迹象的依赖 类型: 为多5 4 a 意识到 接口 名称 注入依赖 解释…… ApplicationContextAware 宣布 ApplicationContext SectionA 5 6 2,一个​​ ApplicationContextAware 和 BeanNameAware 一个​​ ApplicationEventPublisherAware 事件发布者的封闭 ApplicationContext SectionA 5.14,一个​​附加的功能 ApplicationContext 一个​​ BeanClassLoaderAware 类加载器来加载bean使用 类。 SectionA 5 3 2,一个​beansa​​​实例化 BeanFactoryAware 宣布 BeanFactory SectionA 5 6 2,一个​​ ApplicationContextAware 和 BeanNameAware 一个​​ BeanNameAware 声明bean的名称 SectionA 5 6 2,一个​​ ApplicationContextAware 和 BeanNameAware 一个​​ BootstrapContextAware 资源适配器 BootstrapContext 容器运行 在。 通常只在JCA意识 ApplicationContext 年代 ChapterA 25, JCA CCI LoadTimeWeaverAware 定义 韦弗 加工 类定义在加载时间 SectionA 9 8 4,一个​​装入时编织与 AspectJ在春季Frameworka​​ MessageSourceAware 配置策略来解决信息( 支持参数化和 国际 化) SectionA 5.14,一个​​附加的功能 ApplicationContext 一个​​ NotificationPublisherAware Spring JMX通知出版商 SectionA 24.7,一个​​Notificationsa​​ PortletConfigAware 电流 PortletConfig 容器运行在。 有效只 ChapterA 20, Portlet MVC框架 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 66/514 有在理解网络弹簧 ApplicationContext PortletContextAware 电流 PortletContext 容器运行在。 有效 只有在理解网络弹簧 ApplicationContext ChapterA 20, Portlet MVC框架 ResourceLoaderAware 配置的装载机为低级的 资源 ChapterA 6, 资源 ServletConfigAware 电流 ServletConfig 容器运行在。 有效只 有在理解网络弹簧 ApplicationContext ChapterA 17, Web MVC框架 ServletContextAware 电流 ServletContext 容器运行在。 有效 只有在理解网络弹簧 ApplicationContext ChapterA 17, Web MVC框架 请再次注意,使用这些接口的关系你的代码到春天 API和不遵循控制反转的风格。 因此,他们是 推荐用于基础设施,需要编程访问 bean 容器。 5.7一个Bean定义继承 一个bean定义可以包含大量的配置信息, 包括构造函数参数,属性值和特定容器 信息,如初始化方法,静态工厂方法的名字, 等 等。 一个孩子bean定义继承配置数据从一个家长 定义。 孩子定义可以覆盖一些值,或添加其他人, 作为需要。 父母和孩子使 用bean定义可以节省很多 打字。 实际上,这是一个形式的模板。 如果您使用一个 ApplicationContext 接口编程,孩子bean定义为代表的 ChildBeanDefinition 类。 大多数用户不工作 他们在 这一水平,相反配置bean定义 在类似的声明 ClassPathXmlApplicationContext 。 当你使用 基于xml的配置元数据,您显示孩 子的bean定义 使用 父 属性,指定父bean 作为这个属性的值。 一个孩子bean定义使用bean类从父定义 如果没有指定,但是也可以重写它。 在后一种情况下, 孩子bean类必须兼容父,也就是 说,它必须 接受父母的财产价值。 一个孩子bean定义继承的构造函数的参数值,属性 值,方法覆盖父,选项来添加新的 值。 任何初始化方法,破坏的方法,和/或 静态 工厂方法设置,您指定将 覆盖相应的父设置。 其余设置 总是 来自 孩子的定义: 取决于 , 自动装配 模式 , 依赖检查 , singleton , 范围 , 懒惰 INIT 。 前面的例子明确标志着父的bean定义为 摘要通过使用 文摘 属性。 如果父 定义并没有指定一个类,明确标记父bean 定义为 文 摘 是必需的,如下: 父豆不能被实例化在它自己的,因为它是 不完整的,而且它也是明确标记为 文摘 。 当一个定义是 文摘 喜欢这个,它是可用的只 有作为一个纯粹 模板的bean定义,作为父母对孩子的定义 定义。 尝试使用这样一个 文摘 父bean 在它自己的,称之为一个ref 属性的另一个bean或做 一个明确的 getBean() 调用父bean id,返回一个错误。 同样,容器的内部 preInstantiateSingletons() 方法忽略了bean 定义为抽象的定义。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 67/514 注意 ApplicationContext pre实例化所有 单例对象默认情况下。 因此,它是重要的(至少对 单例bean),如果你有 一个(父)bean定义你 打算只使用作为模板,这个定义指定了一个类, 你必须确保设置 文摘 属性 真正的 ,否则 应用程序上下文将 实际上(试图)预实例化的 文摘 bean。 5.8容器扩展点 通常,应用程序开发人员不需要子类 ApplicationContext 实现类。 相反,Spring IoC容器可以延长插入 实现特殊的集成接口。 接下来的几个部分 描述这些集成接口。 5.8.1A定制bean使用 BeanPostProcessor 这个 BeanPostProcessor 接口定义了 回调方法 那你可以实现来提供 你自己的(或覆盖容器的默认)实例化的逻辑, 依赖性解析 逻辑,等等。 如果你想实现一些 自定义逻辑Spring容器实例化后完成, 配置和初始化一个bean,您可以插入一个或 更多的 BeanPostProcessor 实现。 您可以配置多个 BeanPostProcessor 情况下,您可以控制顺序这些 BeanPostProcessor 年代执行通过设置 秩序 财产。 你可 以设置这个属性只是如果 BeanPostProcessor 实现了 下令 接口;如果你写你自己的 BeanPostProcessor 你应该考虑 实施 下 令 界面太。 对于 进一步的细节,请咨询的Javadoc BeanPostProcessor 和 下令 接口。 参见下面的说明上 编程式注册 BeanPostProcessors 注意 BeanPostProcessor 年代操作bean(或对象) 实例 ;也就是说,Spring IoC容器 实例化一个bean实例和 然后 BeanPostProcessor 年代做他们的工作。 BeanPostProcessor 年代的范围被 每集装箱 。 这是唯一相关的如果你是 使用容器层次结构。 如果你定 义一个 BeanPostProcessor 在一个容器,它 将 只有 豆子,后处理 集装箱。 换句话说,bean中定义的一个集 装箱不是 后期处理的 BeanPostProcessor 定义在另一个 容器,即使两个容器都属于同一个层次。 改变实际的bean定义(即。 , 蓝图 定义bean),您需要使用一个不是 BeanFactoryPostProcessor 所述 在 SectionA 5 8 2,一个​​定制配置元数据与一个 BeanFactoryPostProcessor 一个​​ 。 这个 org.springframework.beans.factory.config.BeanPostProcessor 接口由完全两个回调方法。 当这样一个类 注册为后处 理与容器,每个bean实例 这是由容器、后处理器得到一个回调的 容器既 之前 容器初始化 方法(如InitializingBean的 afterPropertiesSet() 和任何宣布init方法)被称为以及 在 任何bean初始化回调。 后处理器可以 任何动作与bean实例,包括忽 略了回调 完全。 一个豆后处理器通常检查回调 接口或可能包装一个bean与一个代理。 一些Spring AOP 基础设施类都实现 为bean后处理器在秩序 提供代理包装逻辑。 一个 ApplicationContext 自动检测 任何bean中定义的 配置元数据而实现 BeanPostProcessor 接口。 这个 ApplicationContext 寄存器这些bean作为 后处理器,这样他们可以被称为后在bean创建。 豆后处理器可以部署在容器就像任 何其他 豆子。 编程方式注册 BeanPostProcessors 而推荐方法 BeanPostProcessor 登记是通过 ApplicationContext 自动检测(如上所述),它也是 可以注册他 们 编程 反对 ConfigurableBeanFactory 使用 addBeanPostProcessor 法。 这可能是有用的 当需要评估 条件逻辑注册之前,甚至 抄袭bean后处理器在上下文层次结构中。 注意 然而, BeanPostProcessors 添加 编程 不尊重 下令 接口 。 这是 秩序的 登记 ,决定了执行顺序。 还请注意 这 BeanPostProcessors 注册 以 编程方式总是加工前注册通过 自动检测、不管任何明确的顺序。 BeanPostProcessors 和AOP 汽车代理 类,实现了 BeanPostProcessor 接口是 特殊 并以不同的方式对待 集装箱。 所有 BeanPostProcessors 和 豆类,他们直接引用 是 在启动时实例化的一部分,特殊的启动阶段 ApplicationContext 。 接下来,所有 BeanPostProcessors 被注册在一个 排序的时尚和适用于所有进一步的豆子在容器。 因为AOP汽车代理被 实现为一个 BeanPostProcessor 本身,既不 BeanPostProcessors 还是豆子他们参考 直接有资格获得汽车 代理,因此没有方面编织 到他们。 任何这样的bean,您应该看到一个信息日志信息: 一个​​ 豆foo没有资格获得处理的所有 BeanPostProcessor 接口(例如:为不合格 汽车代理) 一个​​ 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 68/514 请注意,如果你有豆子连接到你的 BeanPostProcessor 使用自动装配或 @ resource (可能跌回自动装配), 春天可能获得意想不到的豆子当搜索类型匹配依赖候选人, 因而使他们得到汽车代理或其他类型的bean后 处理。 例如,如果你有一个依赖项标注 @ resource 如果该字段/ setter的名字不直接对应于声明的名称 bean和 没有名字的属性是使用,然后春天将访问其他豆类为匹配他们的类型。 下面的例子展示了如何编写,注册和使用 BeanPostProcessors 在一个 ApplicationContext 。 例如:你好世界, BeanPostProcessor 风格 这第一个示例演示了基本的用法。 这个例子显示了一个 定制 BeanPostProcessor 实现 调用 toString() 每个bean的方法 因为 它是由容器和打印生成的字符串 系统控制台。 下面找到自定义 BeanPostProcessor 实现类 定义: package scripting; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.BeansException; public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor { // simply return the instantiated bean as-is public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; // we could potentially return any object reference here... } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("Bean '" + beanName + "' created : " + bean.toString()); return bean; } } 注意, InstantiationTracingBeanPostProcessor 只是 定义。 它甚至没有一个名字,因为它是一个bean可以 是依赖注入就像任 何其他bean。 (前面的 配置还定义了一个bean是由一个Groovy脚本。 这个 动态语言支持Spring 2.0中详细章节的题目为 ChapterA 28日 动态语言支持 )。 以下简单的Java应用程序执行前面的代码和 配置: 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("scripting/beans.xml"); Messenger messenger = (Messenger) ctx.getBean("messenger"); System.out.println(messenger); } } 前应用程序的输出类似 以下: Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 69/514 org.springframework.scripting.groovy.GroovyMessenger@272961 示例: RequiredAnnotationBeanPostProcessor 使用回调接口或注释联同一个 定制 BeanPostProcessor 实现 是一个普遍的方法扩展Spring IoC容器。 一个例子是 春天的 RequiredAnnotationBeanPostProcessor 一个​​一 BeanPostProcessor 实现 船只与春天分布,确保JavaBean 在bean的属性标 记为(任意)注解 实际上(配置为)依赖注入与价值。 5.8.2A定制配置元数据与一个 BeanFactoryPostProcessor 接下来,我们将扩展点看 org.springframework.beans.factory.config.BeanFactoryPostProcessor 。 这个接口的语义是相似 的 BeanPostProcessor ,其中一个主要的 的区别: BeanFactoryPostProcessor 年代操作 bean配置元数据 ;也就是说,Spring IoC 容器允许 BeanFactoryPostProcessors 阅读 配置元数据和潜在地改变它 之前 容器实例化bean的任何其他 比 BeanFactoryPostProcessors 。 您可以配置多个 BeanFactoryPostProcessors ,你可以控制的顺序 这些 BeanFactoryPostProcessors 执行的 设置 秩序 财 产。 但是,你只能设置 这个属性如果 BeanFactoryPostProcessor 实现了 下令 接口。 如果你写你自己的 BeanFactoryPostProcessor ,你应该 考虑实施 下令 接口 太。 咨询的Javadoc BeanFactoryPostProcessor 和 下令 接口为更 多的细节。 注意 如果你想改变实际的bean 实例 (即。 ,创建的对象从配置元数据),然后你 而是需要使用 BeanPostProcessor (上面所描述的在 SectionA 5 8 1,一个​​定制bean使用 BeanPostProcessor 一个​​ )。 而 它在技术上是可能的工作与bean实例在一个 BeanFactoryPostProcessor (如。 ,使用 BeanFactory.getBean() ),这样做的原因 过早的bean实例化,违反了标准集装箱的生命周期。 这可能导致负 面影响比如绕过bean邮报 处理。 同时, BeanFactoryPostProcessors 是作用域 每集装箱 。 这是唯一相关的如果你是 使用容器层次结构。 如果你定义一个 BeanFactoryPostProcessor 在一个 容器,它将 只有 被应用到bean 定义在容器。 在一个 容器的Bean定义 不会的后续处理 BeanFactoryPostProcessors 在另一个容器,甚至 如果两个容器都属于同 一个层次。 一个bean工厂后处理器是当它是自动执行 宣布在一个 ApplicationContext , 为了申请更改配置元数据定义 集装箱。 弹簧包 括一系列预定义的bean工厂 后处理器,如 PropertyOverrideConfigurer 和 来完成 。 一个自定义 BeanFactoryPostProcessor 也可以使用, 例如,注册自定义属性编辑器。 一个 ApplicationContext 自动 检测到任何bean的部署到它实现 BeanFactoryPostProcessor 接口。 它 使用这些bean作为 bean工厂后处理器,在 适当的时间。 你可以将这些部署后处理器bean作为你 将任何其他bean。 注意 与 BeanPostProcessor 年代,你通常 不需要配置吗 BeanFactoryPostProcessor 年代 对于延迟初始化。 如果没有其他bean引用 豆(工厂)后处理程序 , 后处理器将不会得到实例化,在所有。 因此,标记它 延迟初始 化将被忽略, 豆(工厂)后处理程序 将 实例化急切地即使你设置 默认懒init 属性 真正的 在声明的 < bean / > 元素。 示例: 来完成 你使用 来完成 到 外部化属性值从一个bean定义在一个单独的 文件使用标准的Java 属性 格式。 这样做使人部署一个应用程 序来定制 与环境相关的属性如数据库url和密码, 没有风险的复杂性或修改主XML定义文件 或文件的容器。 考虑下面的基于xml的配置元数据片段, 一个 数据源 用占位符 值被定义。 这个例子显示了属性的配置 外部 属性 文件。 在运 行时,一个 来完成 应用于 元数据,将取代一些属性的数据源。 值 来取代被指定为 占位符 表单的 $ { property - name }这是蚂 蚁/ log4j / JSP EL风格。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 70/514 实际的值来自另一个文件在标准的Java 属性 格式: jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root 因此,字符串 $ { jdbc用户名} 代替 在运行时的价值' sa ',这同样适用于其他占位符 值,匹配键在属性文件。 这个 来完成 检查 在 大多数属性和属性占位符的bean定义。 此外,占位符前缀和后缀可以定制。 与 上下文 名称空间中引入弹簧 2.5,可以配置属性占位符和一个专用的 配置元素。 一个或多个位置可以提供作为一个 逗号分 隔列表 位置 属性。 这个 来完成 不仅 查找的属性 属性 文件 你指定。 默认情况下它还检查对Java 系统 属性如果不能找到属性 在指定的属性文 件。 您可以定制这个行为通过设置 systemPropertiesMode configurer财产与 以下三个支持整数价值: 从来没有 (0):从不检查系统属性 撤退 (1):检查系统属性如果无法在指定的属性文件。 这是默认的。 覆盖 (2):检查系统属性之前,首先在指定的属性文件。 这允许系统属性重写任何其他财产的来源。 咨询的Javadoc 来完成 为更多的信息。 类名称替换 您可以使用 来完成 代替 类的名字,有时候是有用的,当你必须选择一个 特定的实现类在运行时。 例如: classpath:com/foo/strategy.properties custom.strategy.class=com.foo.DefaultStrategy 如果类不能在运行时解析为一个有效的类, 分辨率的bean失败当它即将被创建,这是 在 preInstantiateSingletons() 阶段 的 ApplicationContext 对于一个 非懒惰init bean。 示例: PropertyOverrideConfigurer 这个 PropertyOverrideConfigurer ,另一个bean 工厂后处理器,类似 来完成 ,但与 后者,原始的定义可以有默认值或没有 在所 有bean属性的值。 如果一个覆盖 属性 文件没有的条目 某些bean属性,默认的上下文定义使用。 注意,该bean定义 不 意识到 被覆盖,所以它不会立即明显的从XML 定义文件,覆盖configurer正在被使用。 在案件的 多个 PropertyOverrideConfigurer 实例 定义不同的值相同的bean属性,最后一个 赢了,由于覆盖机制。 属性文件配置线采取这种格式: beanName.property=value 例如: dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb 这个示例文件可以使用集装箱的定义 包含一个bean名为 数据源 ,这已经 司机 和 url 属性。 复合属性名也支持,只要每一个 组件的路径除了最后的财产被重载 已非空(可能是初始化的构造函数)。 在这个 例子…… foo.fred.bob.sammy=123 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 71/514 是注释比XML配置弹簧吗? 介绍了基于注解的配置 的问题:这种方法是更好 的比XML。 简短的回答 是 这取决于 。 长一点 的回答,每个方法 有其自身的优缺点,通常由开发 人员决定吗 这策略适合她更好。 由于它们定义 的方法, 注释提供了大量的上下文在他们的声明 中,导致 更短更简洁的配置。 然而,XML擅长连 接 组件不碰他们的源代码或重新编译它们。 一 些 开发人员更喜欢具有布线接近源而其他人认 为 带注释的类,不再是pojo,此外, 配置变得分散, 难以控制。 无论选择,春天可以容纳两种风格,甚至混合 他们 在一起。 值得指出的是,通过它 JavaConfig 选 项,春天允许注释 用于非侵入性的方式,不接触目 标组件 源代码和这方面的工具,所有配置风格 支 持的 SpringSource工具套件 。 … 这个 萨米 财产的 鲍勃 财产的 弗雷德 财产 的 foo bean设置为标量值 123年 。 注意 指定覆盖值总是 文字 值;他们不是翻译成bean的引用。 本公约 也适用于原始的值在XML bean定义 指定 一个bean引用。 与 上下文 名称空间中引入弹簧 2.5,可以配置属性覆盖与一个专用的 配置元素: 5.8.3A定制实例化逻辑与一个 FactoryBean 实现 org.springframework.beans.factory.FactoryBean 接口的对象的 本身 工厂 。 这个 FactoryBean 接口是一个点 可插入性到Spring IoC容器实例化的逻辑。 如果你 有复杂的初始化代码,更好的表达在Java 作为吗 反对(可能)详细数量的XML,您可以创建自己的 FactoryBean 、编写复杂的 在这个类的初始化,然后塞你的自定义 FactoryBean 进入容器。 这个 FactoryBean 接口提供了 三个方法: 对象的getObject() :返回一个实例 这个工厂的对象创建。 可能的实例 共享,取决于这个工厂返回单例对象或 原型。 布尔isSingleton() :返回 真正的 如果这 FactoryBean 返回单例对象, 假 否则。 类getObjectType() :返回对象 类型返回的 getObject() 方法或 空 如果类型是无法提前知道。 这个 FactoryBean 概念和接口 是用在许多地方在Spring框架;超过50个吗 的实现 FactoryBean 接口与弹簧本身的船。 当你需要问一个容器对于一个实际的 FactoryBean 实例本身而不是bean 它产生,前言bean的id与&符号 ( & )当调用 getBean() 方法 ApplicationContext 。 所以对于一个给定的 FactoryBean id的 myBean ,调用 getBean(“myBean”) 在集 装箱返回的产品 FactoryBean ;然而,调用 getBean(“&myBean”) 返回 FactoryBean 实例 本身。 5.9基于注解的容器的配置 一个替代XML设置是由基于注解的 配置依赖于字节码元数据组件连接 而 不是尖括号声明。 而不是使用XML来描述 豆布线,开发人员移动配置成组 件类 通过使用注释的本身相关的类、方法或字段 宣言。 如前所述在 一个​ 章节​例子: RequiredAnnotationBeanPostProcessor 一个​​ ,使用一个 BeanPostProcessor 结合 注释是一个普遍的方法扩展Spring IoC容器。 对于 例,Spring 2.0引入强制要求的可能性 属性与 @ required 注释。 Spring 2.5能跟随 相同的一般方法来驱动Spring的依赖项注入。 本质上, @ autowired 注释 提供了同样的功能描述 SectionA 5 4 5,一个​​ collaboratorsa​​自动装配 但是随着越来越多的细粒度控制和 更广泛的适用 性。 Spring 2.5还增加了支持jsr - 250注释 如 @PostConstruct ,和 @PreDestroy 。 Spring 3.0添加支持 jsr - 330(依赖注入Java)中包含注释 javax。 注射包如 @ inject 和 @ named 。 这些注释的细节中可以找到 相关部门 。 注意 注释注入执行 之前 XML注入,因此后者配置 将覆盖前通 过这两种方法的属性连接。 像往常一样,您可以将这些看作个人bean定义,但是 他们也可以隐式注册通过包括下面的标签在一个 基于xml的Spring配置(注 意包容的 上下文 名称空间): (隐式注册后处理器包括 AutowiredAnnotationBeanPostProcessor , CommonAnnotationBeanPostProcessor , PersistenceAnnotationBeanPostProcessor ,如 以及上述 RequiredAnnotationBeanPostProcessor )。 注意 <上下文:注释配置/ > 只希望 注释在豆子在同一个应用程序上下文 定义。 这意味着,如果你把 <上下文:注 释配置/ > 在一个 WebApplicationContext 对于一个 DispatcherServlet ,它只检查 @ autowired 豆子在 你的控制器,和 不是你的服务。 看到 SectionA 17.2,一个​​了 DispatcherServlet 一个​​ 更多 信息。 5.9.1A @ required 这个 @ required 注释适用于 bean属性的setter方法,像下面的例子: public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... } 这个注释仅仅表明,受影响的bean属性必须 被填充在配置时,通过一个明确的财产价值 一个bean定义或通过自动装配。 容器抛 出一个异常 如果受影响的bean属性没有被填充的,这允许 热切的和明确的故障,避免 NullPointerException 年代以后或类似。 这是 还是建议你把断言到bean类本身,因为 例,变成一个init方法。 这样做需要强制执行这些引用 和价值观甚至当你使用类外 的容器。 5.9.2A @ autowired 正如预期的那样,您可以应用 @ autowired 注释“传统” setter方法: public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... } 注意 JSR 330的@ inject注释可以用来代替弹簧的 @ autowired 注释在下面的例子。 看到 这里 为更多的细节 你也可以应用注释方法具有任意名称 和/或多个参数: public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 73/514 你可以申请 @ autowired 到 构造函数和字段: public class MovieRecommender { @Autowired private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... } 也可以提供 所有 bean的一个 特定类型的 ApplicationContext 通过添加注释字段或方法,预计的数组 该类型: public class MovieRecommender { @Autowired private MovieCatalog[] movieCatalogs; // ... } 这同样适用于类型集合: public class MovieRecommender { private Set movieCatalogs; @Autowired public void setMovieCatalogs(Set movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... } 即使输入地图可以autowired只要预期的主要类型 字符串 。 映射的值将包含所有的咖啡豆 预期的类型和键将包含相应的 bean 名称: public class MovieRecommender { private Map movieCatalogs; @Autowired public void setMovieCatalogs(Map movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... } 默认情况下,自动装配失败每当 零 候选人bean是可用的,默认行为是把注释 方法、构造函数和字段指示 需要 依赖关系。 这种 行为可以被改变 如下图所示。 public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required=false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... } 注意 只有 一个带注释的构造函数/类 可以 标记为 需要 ,但多个非必需 构造函数可以得到注释。 在这种情况下, 每一个被认为是在 候选人和弹簧使用 贪婪的 构造函数的依赖可以满足,那是构造函数 有最大数量的参数。 @ autowired ' s 需要 属性是推荐的 @ required 注释。 这个 需要 属性表示该属性 不需要自动装配的目 的,房地产是忽略了如果它 不能autowired的。 @ required ,在 另一方面,更强,它执行财产设定的 任何方式 支持的容器。 如果没有值是注射,一个 相应的发生异常。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 74/514 您还可以使用 @ autowired 对于 接口是众所周知的可分解的依赖关系: BeanFactory , ApplicationContext , 环境 , ResourceLoader , ApplicationEventPublisher ,和 MessageSource 。 这些接口和他们 扩展接口,如 ConfigurableApplicationContext 或 ResourcePatternResolver ,自动 解决,没有特殊设置必要的。 public class MovieRecommender { @Autowired private ApplicationContext context; public MovieRecommender() { } // ... } 注意 @ autowired , @ inject , @ resource ,和 这个元素包含一个@ value 注释是由一个 春天 BeanPostProcessor 实现 这反过来意味着你 不能 应用这些注释在你自己的 BeanPostProcessor 或 BeanFactoryPostProcessor 类型(如果有的话)。 这些 类型必须是“有线”明确通过XML或使用一个春天 @ bean 法。 5.9.3A微调自动装配与限定词基于注解的 因为自动装配的类型可能导致多个候选人,这是 常常需要有更多的控制的选择过程。 一个方法 完成这是Spring qualifier 注 释。 你可以将 限定符与特定的参数值,缩小组类型 匹配,这样一个特定的bean是为每个参数选择。 在 简单的情况下,这可能是 一个纯描述性的值: public class MovieRecommender { @Autowired @Qualifier("main") private MovieCatalog movieCatalog; // ... } 这个 qualifier 注释也可以 构造函数参数指定个人或方法参数: public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("main") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... } 相应的bean定义如下所示。 bean 限定符值“主要”是有线与构造函数参数,是 合格的用相同的值。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 75/514 对于一个后备匹配,该bean的名字被认为是一个默认的限定符 值。 因此你可以定义bean id“主要”代替 嵌套的限定符元素,导 致相同的匹配结果。 然而, 虽然您可以使用本公约来引用特定bean的名字, @ autowired 根本上是关于 与可选的语义限定符 受类型驱动注入。 这意味着 限定符的值,即使bean名称撤退,总有缩小 语义在组类型匹配;他们不语义表达 引用一个独特的 bean id。良好的限定符值是“主要”或 “中东”或“持久”,表达一个特定的组件的特点 这是独立于bean id,这可能是自动生 成的情况 一个匿名的bean定义类似于前面的 的例子。 限定符也适用于类型集合,正如上面所讨论的,对 个例子, 集< MovieCatalog > 。 在这种情况下,所有 匹配的豆子根据宣布限定 符作为注射 收集。 这意味着限定符不必是独特的;他们 而仅仅构成过滤标准。 例如,您可以定义 多个 MovieCatalog bean与 相同的限定符 值“行动”,所有这些将被注入到一个 集< MovieCatalog > 标注 @ qualifier(“行动”) 。 提示 如果你打算表达注解驱动喷射的名字,不要 主要使用 @ autowired ,即使是 技术能力指一个bean名称通过 qualifier 值。 相反,使用 jsr - 250 @ resource 注释,这是 语义定义,确定具体的目标组件的 独特的名字,声 明的类型被无关的匹配 过程。 作为一个特定结果的语义差异,豆类, 是自己定义为一个集合或地图类型不能被注入 通过 @ autowired ,因 为类型匹配 没有被正确适用于他们。 使用 @ resource 对于这样的豆子,指 特定的集合或地图由独特的名 字,豆 @ autowired 适用于油田, 构造函数,进行方法,允许通过缩小 限定符注释在参数水平。 相比之下, @ resource 只支持字段 和bean属性setter方法的一个参数。 作为一个 因此,坚持限定符如果你注入目标是 构造函数或方法进行。 您可以创建自己的自定义限定符注释。 简单地定义一个 注释和提供 qualifier 注释在您的定义: @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Genre { String value(); } 然后你可以提供自定义修饰符在autowired的字段和 参数: public class MovieRecommender { @Autowired @Genre("Action") private MovieCatalog actionCatalog; private MovieCatalog comedyCatalog; @Autowired public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) { this.comedyCatalog = comedyCatalog; } // ... } 接下来,为候选人提供信息的bean定义。 你 可以添加 <限定符/ > 标签的子元素 < bean / > 标签,然后指定 类型 和 价值 以 匹配您的自定义 限定符注释。 是与类型的完全限定 类名的注释。 或者,作为一个方便的如果没有风险的 存在冲突的名称,您可 以使用短的类名。 两 方法是通过以下例子。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 76/514 在 SectionA 5.10,一个​​类路径扫描和管理​​componentsa ,你将看到一个 基于注解的替代提供限定符元数据在XML。 具体地说, 看到 SectionA 5 10 7,一个​​提供元数据与annotationsa​​限定词 。 在某些情况下,它可能不足以使用注释没有 值。 这可能是有用的注释是一个更通用的 目的和可以应用在几个不同类型的依赖关 系。 例如,您可以提供一个 离线 目录, 将搜索网络连接可用时没有。 首先定义 简单的注释: @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Offline { } 然后添加注释的字段或属性是 autowired的: public class MovieRecommender { @Autowired @Offline private MovieCatalog offlineCatalog; // ... } 现在只需要一个预选赛的bean定义 类型 : 您还可以定义定制的限定符注释命名接受 属性除了或而不是简单的 价值 属性。 如果多个属性值然后 指定一个字段或参数 autowired的,一个bean定义必须 匹配 所有 这样的属性值被认为是一个 自动装配的候选人。 作为一个例子,考虑下面的注释 定义: @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface MovieQualifier { String genre(); Format format(); } 在这种情况下 格式 是一个枚举: public enum Format { VHS, DVD, BLURAY } 这个字段是加注autowired的定义修饰符和 包括两个属性的值: 流派 和 格式 。 public class MovieRecommender { @Autowired @MovieQualifier(format=Format.VHS, genre="Action") private MovieCatalog actionVhsCatalog; @Autowired @MovieQualifier(format=Format.VHS, genre="Comedy") private MovieCatalog comedyVhsCatalog; @Autowired 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 77/514 @MovieQualifier(format=Format.DVD, genre="Action") private MovieCatalog actionDvdCatalog; @Autowired @MovieQualifier(format=Format.BLURAY, genre="Comedy") private MovieCatalog comedyBluRayCatalog; // ... } 最后,该bean定义应该包含匹配的限定符 值。 这个例子还说明bean 元 属性可能用于代替 <限定符/ > 子元素。 如果可用, < 限定符/ > 和它的属性优先, 但是自动装配机制回落在值中提供 < meta / > 标记如果没有这样的限定符是礼物, 在过去的两个 bean定义在下面的例子。 5.9.4A CustomAutowireConfigurer 这个 CustomAutowireConfigurer 是 BeanFactoryPostProcessor 这使您 注册自己的自定义限定符注释类型,即使它们 没有 标注春天的 qualifier 注释。 example.CustomQualifier 特定的实现 AutowireCandidateResolver 这是激活 对于应用程序上下文依赖于Java版本。 在版本 早于Java 5注释的限定符, 不支持 因此自动装配候选人仅根据 自动装配的候选人 每个bean定义的价值一样 通过任何 默认自动装配候选人 模式(s) 上可 用 < bean / > 元素。 在Java 5或 后来的存在 qualifier 任何自定义注释的注释和注册 CustomAutowireConfigurer 也将扮 演一个 的角色。 不管Java版本,当多个bean符合 自动装配的候选人,确定一个“主”候选人的 相同的:如果整整一个bean定义的候选人中有一个 主要 属性设置为 真正的 ,它 将被选择。 5.9.5A @ resource 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 78/514 春天也支持注射使用jsr - 250 @ resource 注释字段或bean 属性的setter方法。 这是一个常见的模式在Java EE 5和6, 在JSF 托管bean示例1.2或jax - ws 2.0的端点。 Spring支持 这种模式也为spring管理对象。 @ resource 取一个名称属性,和 默认情况下春天,的值作为解释bean名称注射。 换句话说,它遵循 则 语义, 证明在这个例子: public class SimpleMovieLister { private MovieFinder movieFinder; @Resource(name="myMovieFinder") public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } } 如果没有名字是显式地指定,默认的名称源自 字段名或setter方法。 在案件的一个领域,它需要的字段 名字,以防setter方法,它需 要bean属性名。 所以 下面的例子将bean名称”movieFinder” 注入它的setter方法: public class SimpleMovieLister { private MovieFinder movieFinder; @Resource public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } } 注意 这个名字提供注释被解析为bean名称 这个 ApplicationContext 的 CommonAnnotationBeanPostProcessor 是意识到。 这个 通过JNDI名称可以解决如果你配置Spring的 SimpleJndiBeanFactory 明确。 然而,建议你依赖默认行为和 简单地使用Spring的JNDI查找功能来保护级 别的 间接寻址。 专属的案例 @ resource 没有显式指定使用名字,类似 @ autowired , @ resource 发现一个主要类型匹配 而不是一个特定的 命名bean,并解决了著名的可分解的 依赖性: BeanFactory , ApplicationContext, ResourceLoader, ApplicationEventPublisher ,和 MessageSource 接口。 因此在接下来的例子中, customerPreferenceDao 场第一个寻找一个bean customerPreferenceDao命名,然后回落到一个主 类型匹配 类型 customerPreferenceDao 。 “上下文”字段 是基于已知的可分解的注入依赖类型 ApplicationContext 。 public class MovieRecommender { @Resource private CustomerPreferenceDao customerPreferenceDao; @Resource private ApplicationContext context; public MovieRecommender() { } // ... } 5.9.6A @PostConstruct 和 @PreDestroy 这个 CommonAnnotationBeanPostProcessor 不仅 认识到 @ resource 注解,但 还jsr - 250 生命周期 注释。 引入 Spring 2.5,支持这些注释提供了另一个 替代中描述 初始化 回调 和 破坏 回调 。 只要 CommonAnnotationBeanPostProcessor 注 册 在春天 ApplicationContext ,一个 法携带这些注释是在同一点的调用 生命周期作为相应的春天生命周期接口方法或 显式声 明回调方法。 在下面的示例中,缓存会 在初始化和清除来预装在毁灭。 public class CachingMovieLister { @PostConstruct public void populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache() { // clears the movie cache upon destruction... } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 79/514 } 注意 对于细节的影响结合各种生命周期 机制,看到 一个​​章节结合生命周期mechanismsa​​ 。 5.10类路径扫描和管理组件 大多数的例子在这一章使用XML指定的配置 元数据产生每个 BeanDefinition 在Spring容器。 前一节 ( SectionA 5.9,一个​​ configurationa​​基于注解的容器 )演示了如何提供 很多配置元数据通过源代码级别的注释。 甚至 在那些例子,然而,“基 地”bean定义明确 在XML文件中,而注释只驱动依赖 注射。 本节描述一个选项为隐式检测 候选组件 通过扫描类路径。 候选 人组件类,与一个过滤条件和 有一个相应的bean定义注册容器。 这 消除了需要使用XML来执行bean注册,相反你可以 使用注 释(例如component),AspectJ表达式,或者你的类型 自定义过滤条件来选择哪个类将bean 定义注册容器。 注意 从Spring 3.0,提供的许多特性 Spring JavaConfig 项目 是Spring框架核心的一部分。 这允许您 定义bean 使用Java,而不是使用传统的XML文件。 把 一看 @ configuration , @ bean , @ import ,和 @DependsOn 注释的例子 使用这些新特性。 5.10.1A component 和进一步的刻板印象 注释 在Spring 2.0和以后, @Repository 注释标记任何 类,它实现了角色或 原型 (也 被称为数据访问对象或刀)的存储库。 在使用的 这个标志是自动翻译中描述的异常 SectionA 15 2 2,一个​​translationa​​除外 。 Spring 2.5引入了进一步构造型注解: component , @ service ,和 controller 。 component 是一个通用的原型吗 spring管理 组件。 @Repository , @ service ,和 controller 是专门化的 component 为更具体的用例,因为 例,在持久性、服务和表示层, 分别。 因此,你可以标注你的组件类 component ,但他们通过标注 @Repository , @ service ,或 controller 相反,你的类是更 多 正确地适合加工的工具或联想方面。 对于 的例子,这些构造型注解使理想目标为切入点。 它 也有可能的是, @Repository , @ service ,和 controller 可能携带额外的语义 在未来版本的Spring框架。 因此,如果你选择 之间使用 component 或 @ service 为你的服务层, @ service 显然是更好的选择。 同样,如上所述, @Repository 是 已经支持作为一个标记为自动异常在 你。翻译 持久层。 5.10.2A自动检测类和注册的bean 定义 春天可以自动检测的原型类和寄存器 相应 BeanDefinition 年代的 ApplicationContext 。 例如, 以下两个类都有资格获得这 种自动: @Service public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } } @Repository public class JpaMovieFinder implements MovieFinder { // implementation elided for clarity } 自动侦测到这些类和寄存器对应的豆子,你 需要包括以下元素在XML的基本方案 元素是一种常见的父母包了两个类。 (另外, 你 可以指定一个以逗号分隔的列表,包括父包的 每个类)。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 80/514 提示 使用 <上下文:组件扫描> 隐式 使功能的 <上下文:注释配置> 。 通常没有必要包括 <上下文:注释配置> 元 素当使用 <上下文:组件扫描> 。 注意 扫描的classpath包需要存在 相应的目录条目在类路径中。 当您构建jar 与蚂蚁,确保你做的 不 激活 文件只 有开关JAR任务。 此外, AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 都 包括隐式在使用组件扫描 元件。 这意味着 这两个组件 和 有线 在一起,没有任何bean配置元数据中提供的 XML。 注意 你可以禁用注册 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 通过 包括 注释配置 属性与一个 值为false。 5.10.3A使用过滤器来定制扫描 默认情况下,类标注 component , @Repository , @ service , controller ,或一个自定义注释, 本身是标注 component 是 只 有发现候选人组件。 但是,您可以修改和扩展 这种行为只是通过应用自定义过滤器。 在 包括过滤器 或 排他过滤器 子元素的 组件扫描 元素。 每个过滤器 元素要求 类型 和 表达式 属性。 下表描述了 过滤选项。 5.5为多。 一个过滤器类型 滤波器类 型 示例表达式 描述 注释 org.example.SomeAnnotation 一个注释出席类型在目标水平 组件。 可转让的 org.example.SomeClass 一个类(或接口),目标组件 可转让的(扩展/执行)。 AspectJ org的例子. . *服务+ AspectJ表达式类型相匹配的目标 组件。 Regex org \例子\违约。* 一个正则表达式表达与目标组件 类名。 定制 org.example.MyTypeFilter 一个自定义实现的 org.springframework.core.type .TypeFilter 接 口。 下面的例子显示了XML配置忽略所有 @Repository 注释和使用“存根” 存储库相反。 注意 你也可以禁用默认的过滤器通过提供 使用默认的过滤器= " false " 的属性 <组件扫描/ >元素。 这将禁用 自动生效 检测类标注 component , @Repository , @ service ,或 controller 。 5.10.4A定义bean组件内的元数据 弹簧组件也可以贡献的bean定义元数据 集装箱。 你这么做同样的 @ bean 注释 用于定义bean内的元数据 @ configuration 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 81/514 带注释的类。 这是一个简单的例子: @Component public class FactoryMethodComponent { @Bean @Qualifier("public") public TestBean publicInstance() { return new TestBean("publicInstance"); } public void doWork() { // Component method implementation omitted } } 这个类是一个弹簧组件包含特定于应用程序的代码 包含在其 doWork() 法。 然而,它 也贡献一个bean定义,有一个工厂方法指 该方法 publicInstance() 。 这个 @ bean 注释标识工厂方法和 其他bean定义属性,比如限定符值通过 qualifier 注释。 其他 方法级 可以指定注释是 scope , @Lazy ,定义修饰符注释。 Autowired的 支持字段和方法如之前讨论的,额外的 支持自动装配 的 @ bean 方法: @Component public class FactoryMethodComponent { private static int i; @Bean @Qualifier("public") public TestBean publicInstance() { return new TestBean("publicInstance"); } // use of a custom qualifier and autowiring of method parameters @Bean protected TestBean protectedInstance(@Qualifier("public") TestBean spouse, @Value("#{privateInstance.age}") String country) { TestBean tb = new TestBean("protectedInstance", 1); tb.setSpouse(tb); tb.setCountry(country); return tb; } @Bean @Scope(BeanDefinition.SCOPE_SINGLETON) private TestBean privateInstance() { return new TestBean("privateInstance", i++); } @Bean @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public TestBean requestScopedInstance() { return new TestBean("requestScopedInstance", 3); } } 这个例子的autowires 字符串 方法 参数 国家 的价值 年龄 另一个bean属性命名 privateInstance 。 一个春天的表达式语言元 素 定义了该属性的值通过符号 # { <表达式> } 。 对于 这个元素包含一个@ value 注释, 一个表达式解析器是预先配置的寻找 bean名称当 解析表达式的文本。 这个 @ bean 方法在一个弹簧组件 经过不同的处理比同行在春天 @ configuration 类。 不同的是, component 类是不增 强,CGLIB 拦截的调用方法和字段。 CGLIB代理是 方法调用方法或字段中 @ configuration 类 @ bean 方法 创建bean引用 协作对象的元数据。 方法 不 与普通的Java调用语义。 相比之下, 调用一个方法或字段在一个 component 类 @ bean 方法 已经 标准Java 语义。 5.10.5A命名个组件 当一个组件是作为扫描过程,它的 bean的名字是生成的 BeanNameGenerator 策略已知, 扫描仪。 默认情况下,任何弹簧构造 型注解 ( component , @Repository , @ service ,和 controller ),其中包含一个 名称 值从而提供名字 相应的bean定义。 如果这样的注释不包含 名称 值或 检测到任何其他组件(如自定义过滤器所发现的), 默认的bean名称生成器返回小写形式的确 立 类名。 例如,如果以下两个组件检测到, 名字会myMovieLister和movieFinderImpl: @Service("myMovieLister") public class SimpleMovieLister { // ... } @Repository 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 82/514 public class MovieFinderImpl implements MovieFinder { // ... } 注意 如果你不想依赖默认bean命名策略,你 可以提供一个自定义的bean命名策略。 首先,实现 BeanNameGenerator 接口, 一定要包括一个默认的无参数构造函数。 然后,提供 完全限定类名当配置扫 描: 作为一般规则,考虑指定名字和注释 每当其他组件可以使显式地引用它。 在 另一方面,自动生成的名字是足够每当容器 负责布 线。 5.10.6A提供一个组件范围 与spring管理组件在一般情况下,默认的和最 常见的范围是singleton。个组件 然而,有时 你需要其他范围,用一个新的Spring 2.5提供 scope 注释。 仅仅提供名称 范围内的注释: @Scope("prototype") @Repository public class MovieFinderImpl implements MovieFinder { // ... } 注意 提供一个自定义的策略范围解析而不是 依靠基于注解的方法,实现了 ScopeMetadataResolver 界面, 而且 一定要包括一个默认的无参数构造函数。 然后,提供 完全限定类名当配置扫描: 当使用某些单体范围,可能需要 生成代理的范围对象。 描述的推理 一个​章节​bean作为dependenciesa​​范围 。 为了这个目的, 一个 范围代理 属性是可用的 组件扫描的元素。 三个可能的值是:不,接口和 targetClass。 例如,下面的配置会导致 标准JDK动 态代理: 提供元数据与5.10.7A预选赛注释 这个 qualifier 注释是讨论 在 SectionA 5 9 3,一个​​微调与qualifiersa​​基于注解的自动装配 。 这个例子 在这部分演示使用 qualifier 注释和定义修饰符 注释提供细粒度控制当你解决自动装配 候选人。 因为这些例子是基于XML bean定义, 限定符元 数据提供候选人bean定义使用 这个 预选赛 或 元 子元素 的 bean 元素在XML。 当依赖 类路径扫描自动组件,你提供 限定符 元数据与类型水平上加注解的候选类。 这个 以下三个例子证明这种技术: @Component @Qualifier("Action") public class ActionMovieCatalog implements MovieCatalog { // ... } @Component @Genre("Action") public class ActionMovieCatalog implements MovieCatalog { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 83/514 // ... } @Component @Offline public class CachingMovieCatalog implements MovieCatalog { // ... } 注意 与大多数基于注解的替代品,请记住 注释的元数据绑定类定义本身,而 使用XML允许多个bean 相同的 类型 提供元数据的变化他们的限定符, 因为这是每个元数据提供而不是 每个类。 5.11一个使用JSR 330标准注释 从Spring 3.0,弹簧提供了支持jsr - 330标准注释(依赖注入)。 这些注释是扫描一样弹簧注释。 你只需要有相关的罐子在您的类 路径下。 注意 如果您正在使用Maven javax.inject 工件可用 在标准Maven存储库 ( http://repo1.maven.org/maven2/javax/inject/javax.inject/1/ )。 你可以添加以下依赖你的文件pom . xml中: javax.inject javax.inject 1 5.11.1A依赖注入与 @ inject 和 @ named 而不是 @ autowired , @javax.inject.Inject 可以用如下: import javax.inject.Inject; public class SimpleMovieLister { private MovieFinder movieFinder; @Inject public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... } 与 @ autowired ,可以使用 @ inject 在类级别,字段级,方法级和构造函数参数的水平。 如果你想使用限定名称,应该注入的依 赖, 你应该使用 @ named 注释如下: import javax.inject.Inject; import javax.inject.Named; public class SimpleMovieLister { private MovieFinder movieFinder; @Inject public void setMovieFinder(@Named("main") MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... } 5.11.2A @ named :一个标准相当于 component 注释 而不是 component , @javax.inject.Named 可以用如下: import javax.inject.Inject; import javax.inject.Named; @Named("movieListener") public class SimpleMovieLister { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 84/514 全@ configuration vs“简装版”@Beans模 式? 当 @ bean 方法内部声明 类 不 标注 @ private MovieFinder movieFinder; @Inject public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... } 这是非常常见的使用 component 没有 指定组件的名称。 @ named 可以用在类似的方式: import javax.inject.Inject; import javax.inject.Named; @Named public class SimpleMovieLister { private MovieFinder movieFinder; @Inject public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... } 当使用 @ named ,可以使用 组件扫描在完全相同的方式在使用Spring注释: 5.11.3A限制的标准方法 当使用标准的注释,重要的是要知道 一些重要的特性是不提供如下表所示: 5.6为多。 一个春天的注释与标准注释 春天 javax.inject。 * javax。 注入限制/评论 @ autowired @ inject @ inject没有“要求”属性 component @ named 一个​​ scope(“单”) @ singleton jsr - 330默认范围是喜欢春天的 原型 。 然而,为了保持符合春天的一般违约, 一个 jsr - 330 bean声明在Spring容器是一个 singleton 默认情况下。 为了使用一个 范围以外的其他 singleton ,你应该使用Spring的 scope 注释。 javax.inject 还提供了一个 scope 注释。 然而,这是只用于创建您自己的注释。 qualifier @ named 一个​​ 这个元素包含 一个@ value 一个​​ 没有等效 @ required 一个​​ 没有等效 @Lazy 一个​​ 没有等效 5.12一个基于java的容器的配置 5.12.1A基本概念: @ bean 和 @ configuration 在春天的中央工件的新java配置支持 @ configuration 注释的类和 @ bean 带注释的方法。 这个 @ bean 注释被用来显示 方法实例化、配置和初始化一个新对象的 管理 Spring IoC容器。 对于那些熟悉Spring的 < bean / > XML配置的 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 85/514 configuration 他们被称为被 处理在一 个“lite”模式。 例如,bean方法中声明 component 或者甚至在一个 平原旧 类 将被 视为“简装版”。 与全面 @ configuration ,清淡的 @ bean 方法不能轻易宣布国米bean 依赖关系。 通常 一 @ bean 方法不应该 调用另一个 @ bean 方法当操作在 “建兴”模式。 只有使用 @ bean 方法在 @ configuration 类是推荐的方法 确保‘满 载’模式总是使用。 这将防止相同的 @ bean 不小心被调用的方法 多次和有助于减少微妙的 错误,很难追踪 当操作在“精简”模式。 @ bean 注释扮演同样的角色 < bean / > 元素。 您可以使用 @ bean 带注释的方法 任何春天 component ,然而,他们是大多数 通常用于 @ configuration 豆子。 注释一个类与 @ configuration 表明,其主要用途是作为一个来源的bean 定义。 此外, @ configuration 国米bean类允许 依赖关系被定义为简单 的调用其他 @ bean 方法在同一个班。 尽可能简单的 @ configuration 类将如下: @Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } } 这个 AppConfig 级以上就相当于这个 第二年春天 < bean / > XML: 这个 @ bean 和 @ configuration 注释将深入讨论下面的小节。 不过,首先,我们会 覆盖的各种方法创建一个spring容器使用 基于java的 配置。 5.12.2A Spring容器实例化使用 所 下面的部分文档春天的 所 ,新的Spring 3.0中。 这种多用途的 ApplicationContext 实现 不仅能够接受 @ configuration 类 输入,但也平原 component 类和类 jsr - 330元数据注释。 当 @ configuration 类是提供作为输入, 这个 @ configuration 类本身注册为bean 定义,和所有的声明 @ bean 方法在 类也 登记为bean定义。 当 component 和jsr - 330提供的类, 他们被注册为bean定义和假设DI 元数据如 @ autowired 或 @ inject 用在这些类在哪 里 必要的。 结构简单 在同样的方式,使用Spring XML文件作为输入当 实例化一个 ClassPathXmlApplicationContext , @ configuration 课程可能 被用作输入当 实例化一个 所 。 这允许完全使用xml免费Spring容器: public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); } 正如上面提到的, 所 并不局限于 工作只有 @ configuration 类。 任何 component 或jsr - 330带注释的类可以提供 作为输入 到构造函数。 例如: public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); } 上述假设 MyServiceImpl , Dependency1 和 Dependency2 使用 Spring依赖项注入等注释 @ autowired 。 建筑容器以编程方式使用 寄存器(类< ? >…) 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 86/514 一个 所 可能是 使用无参数构造函数实例化,然后配置使用 寄存器() 法。 这种方法尤其 当通过编程建立一个有用 所 。 public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(AppConfig.class, OtherConfig.class); ctx.register(AdditionalConfig.class); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); } 使组件扫描与 扫描(String……) 经验丰富的弹簧用户将熟悉以下 常用的XML声明从春天的 背景: 名称空间 在上面的例子中, com acme 包将 扫描,寻找任何 component 注释 类,这些类将被登记为Spring bean定义 在容器内。 所 暴 露了 扫描(String……) 方法以允许相同的 组件扫描 功能: public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("com.acme"); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); } 注意 记住, @ configuration 类 元注释与 component ,所以他们 组件扫描的候选人! 在上面的示例中,假设 AppConfig 是宣布在 com acme 包(或任何包下面,它 会捡起在叫吗 扫描() ,和 在 refresh() 所有的 @ bean 方法将处理和登记为bean定义中 容器。 支持web应用程序时 AnnotationConfigWebApplicationContext 一个 WebApplicationContext 的变体 所 可与 AnnotationConfigWebApplicationContext 。 这 实现可用于当配置弹簧 ContextLoaderListener servlet侦听器,Spring MVC DispatcherServlet 等等。下面是一个 web . xml 片段,配置一个典型的 Spring MVC web应用程序。 注意使用 contextClass 上下文参数和初始化: contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext contextConfigLocation com.acme.AppConfig org.springframework.web.context.ContextLoaderListener dispatcher org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 87/514 contextConfigLocation com.acme.web.MvcConfig dispatcher /app/* 5.12.3A使用 @ bean 注释 @ bean 是方法级注释和 直接模拟的XML < bean / > 元素。 这个 注释支持一些属性提供 < bean / > ,如: init方法 , 销毁方 法 , 自动装配 和 名称 。 您可以使用 @ bean 注释在 @ configuration 注释或在一个 component 带注释的类。 声明一个bean 声明一个bean,只是标注的方法 @ bean 注释。 你使用这种方法 注册一个bean定义在一个 ApplicationContext 的 类型指定 为方法的返回值。 默认情况下,该bean 的名字是相同的方法名称。 以下是一个简单的 的例子 @ bean 方法声明: @Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } } 前面的配置就是相当于以下 Spring XML: 既使一个bean声明命名 transferService 可用的 ApplicationContext ,绑定到一个对象 类型的实例 TransferServiceImpl : transferService -> com.acme.TransferServiceImpl 接收生命周期回调 任何类定义 @ bean 注释支持 常规的生命周期回调函数,可以使用 @PostConstruct 和 @PreDestroy jsr - 250的注解,请参阅 jsr - 250 注释 为进一步的细节。 常规的弹簧 生命周期 回调也完全支持。 如果一个bean 实现 InitializingBean , DisposableBean , 或 生命周期 ,各自的方法叫 的 集装箱。 标准组 *意识到 接口如 BeanFactoryAware , BeanNameAware , MessageSourceAware , ApplicationContextAware ,和 所以在也完全支持。 这个 @ bean 注释支持 指定任意的初始化和销毁的回调方法, 就像春天的XML的 init方法 和 销毁方法 属性 bean 元素: public class Foo { public void init() { // initialization logic } } public class Bar { public void cleanup() { // destruction logic } } @Configuration public class AppConfig { @Bean(initMethod = "init") public Foo foo() { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 88/514 return new Foo(); } @Bean(destroyMethod = "cleanup") public Bar bar() { return new Bar(); } } 当然,对于 foo 以上,这将是 同样有效的调用 init() 方法直接在 施工: @Configuration public class AppConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.init(); return foo; } // ... } 提示 当你工作直接在Java中,你可以做任何你喜欢的和 你的对象,并不总是需要依靠容器 生命周期! bean指定范围 使用 scope 注释 你可以指定你的bean定义 @ bean 注释应该有一个特定的 范围。 你可以使用任何标准的范围中指定的 Bean范围 部分。 默认范围是 singleton ,但你可以 覆盖这个与 scope 注释: @Configuration public class MyConfiguration { @Bean @Scope("prototype") public Encryptor encryptor() { // ... } } scope和作用域的代理 Spring提供了方便的工作方式与作用域依赖关系 通过 范围 代理 。 最简单的方法来创建这样一个代理在使用 XML配置是 < aop:作用域的代理/ > 元素。 配置您的Java bean与一个scope注释 提供了同等的支持与proxyMode属性。 默认的是 没有代 理( ScopedProxyMode.NO ),但您可以指定 ScopedProxyMode.TARGET_CLASS 或 ScopedProxyMode.INTERFACES 。 如果你港口的作用域内的代理从XML实例参考 文档(参见前面的链接)到我们 @ bean 使用Java,它看起来像 以下: // an HTTP Session-scoped bean exposed as a proxy @Bean @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public UserPreferences userPreferences() { return new UserPreferences(); } @Bean public Service userService() { UserService service = new SimpleUserService(); // a reference to the proxied userPreferences bean service.setUserPreferences(userPreferences()); return service; } 定制bean命名 默认情况下,配置类使用 @ bean 方法的名称的名称 产生的bean。 这个功能可以被覆盖,然而,由于 名称 属性。 @Configuration public class AppConfig { @Bean(name = "myFoo") public Foo foo() { return new Foo(); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 89/514 } } Bean混淆 作为讨论 SectionA 5 3 1,一个​​命名beansa​​ ,有时候 希望给一个bean多个名称,否则称为 Bean混淆 。 这个 名称 属性的 @ bean 注释接受一个字符串 数组为这个目的。 @Configuration public class AppConfig { @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" }) public DataSource dataSource() { // instantiate, configure and return DataSource bean... } } 5.12.4A使用 @ configuration 注释 @ configuration 是一个类级别注释 表明一个对象是一个源的bean定义。 @ configuration 类声明bean通过 公共 @ bean 带注释的方法。 调用 @ bean 方法 @ configuration 类还可以用来 定义国米bean依赖关系。 看到 SectionA 5 12 1,一个​​基 本概念: @ bean 和 @ configuration 一个​​ 对于 一个总体介绍。 注入国米bean依赖 当 @ bean 年代都依赖一个 另一个,表示依赖关系很简单,有一个bean 方法调用另一个: @Configuration public class AppConfig { @Bean public Foo foo() { return new Foo(bar()); } @Bean public Bar bar() { return new Bar(); } } 在上面的例子中, foo bean接收参考 到 酒吧 通过构造函数注入。 注意 这个方法的声明国米bean依赖只能当 这个 @ bean 方法内部声明的 @ configuration 类。 你不能申报 国米bean依赖使用普通 component 类。 查找方法注入 正如前面提到的, 查找方法注入 是一种先进的功能,你应该吗 使用很少。 它是有用的情况下,有一个单例范围bean有一个 依赖 一个原型作用域bean。 使用Java为这种类型的 配置提供了一种自然的方式实现此模式。 public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); } 使用java配置支持,您可以创建的一个子类 commandManager 在抽象的 createCommand() 方法被覆盖的方式 它看起来新 (原型)命令对象: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 90/514 @Bean @Scope("prototype") public AsyncCommand asyncCommand() { AsyncCommand command = new AsyncCommand(); // inject dependencies here as required return command; } @Bean public CommandManager commandManager() { // return new anonymous implementation of CommandManager with command() overridden // to return a new prototype Command object return new CommandManager() { protected Command createCommand() { return asyncCommand(); } } } 进一步的信息关于基于java的配置工作 内部 下面的示例显示了一个 @ bean 注释 方法被称为两次: @Configuration public class AppConfig { @Bean public ClientService clientService1() { ClientServiceImpl clientService = new ClientServiceImpl(); clientService.setClientDao(clientDao()); return clientService; } @Bean public ClientService clientService2() { ClientServiceImpl clientService = new ClientServiceImpl(); clientService.setClientDao(clientDao()); return clientService; } @Bean public ClientDao clientDao() { return new ClientDaoImpl(); } } clientDao() 被称为一旦在吗 clientService1() 一旦在 clientService2() 。 因为这个方法创建一个新的 实例的 ClientDaoImpl 并返回它,你会 通常期望拥有两个实例(一个用于每个服务)。 这绝对 将有问题的:在春天,实例化bean有一个吗 singleton 默认 范围。 这是神奇的 来自:所有 @ configuration 类子类在 启动时间与 CGLIB 。 在子类,孩子 方法检查容器首先对于任何缓存 (作用域)bean之前 调用父方法和创建一个新的实例。 注意,在春天 3.2,不需要添加到您的类路径中,因为,CGLIB CGLIB类已经重 新打包在org。 springframework和包括 直接在spring核心JAR。 注意 这种行为可能是不同的根据你的范围 bean。 我们正在谈论单身在这里。 注意 存在一些限制因为CGLIB动态 添加特性在启动时间: 配置类不应该是最后的 他们应该有一个不带参数的构造函数 5.12.5A构成基于java的配置 使用 @ import 注释 一样 <进口/ > 元素用于 在Spring XML文件来帮助在模块化配置, @ import 注释允许加载 @ bean 定义从另一个配置 类: @Configuration public class ConfigA { public @Bean A a() { return new A(); } } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 91/514 @Configuration @Import(ConfigA.class) public class ConfigB { public @Bean B b() { return new B(); } } 现在,而不是需要同时指定 ConfigA.class 和 ConfigB.class 当实例化背景下,只有 ConfigB 需要 提供 明确: public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class); // now both beans A and B will be available... A a = ctx.getBean(A.class); B b = ctx.getBean(B.class); } 这种方法简化了容器实例化,因为只有一个类 需要处理,而不需要开发人员记住 一个潜在的大量的 @ configuration 类 在施工 期间。 注入依赖进口 @ bean 定义 上面的例子是有效的,但过于简单。 在大多数实际 场景,豆子会彼此依赖跨 配置类。 当使用XML,这不是一个问题,本身, 因为没 有编译器的参与,和一个可以简单地声明 ref = " someBean” 并且相信春天会工作 在容器初始化。 当然,当使用 @ configuration 类,Java编译器的地方 约束的配置模型,引用其他 bean必须有效的Java语法。 幸运的是,解决这个问题很简单。 记住, @ configuration 类是最终只是另一个 豆在容器——这意味着他们可以利用 @ autowired 注入元数据就像任何其他 豆! 让我们考虑一个更真实的场景中与几个 @ configuration 类,每个取决于豆子 宣布在 其他: @Configuration public class ServiceConfig { private @Autowired AccountRepository accountRepository; public @Bean TransferService transferService() { return new TransferServiceImpl(accountRepository); } } @Configuration public class RepositoryConfig { private @Autowired DataSource dataSource; public @Bean AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource); } } @Configuration @Import({ServiceConfig.class, RepositoryConfig.class}) public class SystemTestConfig { public @Bean DataSource dataSource() { /* return new DataSource */ } } public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class); // everything wires up across configuration classes... TransferService transferService = ctx.getBean(TransferService.class); transferService.transfer(100.00, "A123", "C456"); } 完全资格进口咖啡豆,易于导航 在上面的场景中,使用 @ autowired 作品 好,并提供所需的模块化,但确定到底 autowired的bean定义声明仍有点吗 模棱两可 的。 例如,作为一个开发人员查看 ServiceConfig ,你怎么知道的确切位置 AccountRepository @ autowired bean声明吗? 这 不是显式的代码,这可能是刚刚好。 记得 , SpringSource工具套件 提供的工具,可以渲染 图表表明一切都是有线——这可能是 所有的你 需要。 同时,您的Java IDE可以很容易地找到所有声明和使用 的 AccountRepository 式,将很快 显示你的位置 @ bean 方法 返回类型。 这种模糊性的情况下是不能接受的,你想 直接导航从在IDE从一个吗 @ configuration 类到另一个,可以考虑 自动装配配置类本 身: @Configuration public class ServiceConfig { private @Autowired RepositoryConfig repositoryConfig; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 92/514 public @Bean TransferService transferService() { // navigate 'through' the config class to the @Bean method! return new TransferServiceImpl(repositoryConfig.accountRepository()); } } 在上面的情况,它是完全明确的地方 AccountRepository 被定义。 然而, ServiceConfig 现在是紧耦合的吗 RepositoryConfig ,这是一种折衷。 这种紧密 耦合可以稍微缓解通过使用基于接口的或 摘要基于类的 @ configuration 类。 考虑以下: @Configuration public class ServiceConfig { private @Autowired RepositoryConfig repositoryConfig; public @Bean TransferService transferService() { return new TransferServiceImpl(repositoryConfig.accountRepository()); } } @Configuration public interface RepositoryConfig { @Bean AccountRepository accountRepository(); } @Configuration public class DefaultRepositoryConfig implements RepositoryConfig { public @Bean AccountRepository accountRepository() { return new JdbcAccountRepository(...); } } @Configuration @Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config! public class SystemTestConfig { public @Bean DataSource dataSource() { /* return DataSource */ } } public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class); TransferService transferService = ctx.getBean(TransferService.class); transferService.transfer(100.00, "A123", "C456"); } 现在 ServiceConfig 是松散耦合与尊重 具体 DefaultRepositoryConfig ,和 内置的IDE工具仍然是有用的:它将是容易的 开发 人员得到一个类型层次结构的 RepositoryConfig 实现。 通过这种方式, 导航 @ configuration 类及其 依赖关系变得不平常 的不同过程 导航基于接口的代码。 结合Java和XML配置 春天的 @ configuration 类支持不 目标是100%完成替代弹簧XML。 一些设施 如弹簧XML名称空间是一个理想的方式来配置 集装箱。 在情况下,XML是方便的和必要的,你有一个 选择:要么实例化容器在一个“以xml为中心的”方式使用, 例如, ClassPathXmlApplicationContext ,或在一个 “以java为中心的“时尚使用 所 和 @ImportResource 注释导入XML作为 需 要。 以xml为中心的使用 @ configuration 类 它最好是引导Spring容器从XML 和包括 @ configuration 类在一个特别 时尚。 例如,在一个大型的现有代码库,使用弹簧 XML,这将会更容易创建 @ configuration 根据基础类,包括他们从现有的XML 文件。 下面你会发现使用的选项 @ configuration 类在这种 “以xml为中心”的情况。 宣布 @ configuration 类作为普通 春天 < bean / > 元素 记住, @ configuration 类 最终只是bean定义在容器。 在这个例子中, 我们创建一个 @ configuration 类命名 AppConfig 并将其包括在 系统测试配置xml 作为一个 < bean / > 定义。 因为 <上下文:注释配置/ > 切换 在,容器将识别 @ configuration 注释和处理 @ bean 方法中声明的 AppConfig 正确。 @Configuration public class AppConfig { private @Autowired DataSource dataSource; public @Bean AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource); } public @Bean TransferService transferService() { return new TransferService(accountRepository()); } } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 93/514 system-test-config.xml jdbc.properties jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa jdbc.password= public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml"); TransferService transferService = ctx.getBean(TransferService.class); // ... } 注意 在 系统测试配置xml 以上, AppConfig < bean / > 不声明一个 id 元素。 而这将是可行的 所以,它是不必 要的假设没有其他bean会参考 它,就不太可能会显式地去 容器的名字。 同样的 数据源 豆——它是只 autowired的类型,所以一个显式的bean id 没有严格要求。 使用 <上下文:组件扫描/ > 到 接 @ configuration 类 因为 @ configuration 是元注释与 component , @ configuration 注释类 组件扫描自动候选人。 使用相同的 场景如上所 述,我们可以重新定义 系统测试配置xml 利用 组件扫描。 注意,在这种情况下,我们不需要 显式地声明 <上下文:注释配置/ > ,因 为 <上下文:组件扫描/ > 使所有 相同 功能。 system-test-config.xml @ configuration 类中心使用XML @ImportResource 在应用 @ configuration 类 是主要的机制来配置容器,它仍然会吗 可能有必要使用至少一些XML。 在这些场景中, 简单地使用 @ImportResource 和定义只有尽可能多的 XML是必要的。 这样做达到一个“篇”的方法来 配置容器和保持XML到最低限 度。 @Configuration @ImportResource("classpath:/com/acme/properties-config.xml") public class AppConfig { private @Value("${jdbc.url}") String url; private @Value("${jdbc.username}") String username; private @Value("${jdbc.password}") String password; public @Bean DataSource dataSource() { return new DriverManagerDataSource(url, username, password); } } properties-config.xml jdbc.properties 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 94/514 jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa jdbc.password= public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); TransferService transferService = ctx.getBean(TransferService.class); // ... } 5.13一个注册一个 LoadTimeWeaver 这个 LoadTimeWeaver 使用弹簧动态吗 变换类被加载到Java虚拟机(JVM)。 使装入时编织添加 @EnableLoadTimeWeaving 你的 @ configuration 类: @Configuration @EnableLoadTimeWeaving public class AppConfig { } 另外对于XML配置使用 背景:加载时间织布 元素: 一旦配置 ApplicationContext 。 任何bean内 ApplicationContext 可以实现 LoadTimeWeaverAware ,从而获得一个 装载 时编织器实例的引用。 这是特别有用 结合 Spring的JPA支持 在 可能需要装入时编织为JPA类转换。 咨询 这个 LocalContainerEntityManagerFactoryBean Javadoc 更多的细节。 更多关于AspectJ装入时编织,看到 SectionA 9 8 4,一个​​ 装入时编织与AspectJ在春季Frameworka​​ 。 5.14一个额外的功能 ApplicationContext 作为讨论的是章介绍, org.springframework.beans.factory 包提供了基本 功能管理和操纵豆类,包括在一个 程序化的方法。 这个 org.springframework.context 包 添加 ApplicationContext 接口,它 扩展了 BeanFactory 接口,在 除了扩展其他接口来 提供额外的功能 在一个更 应用面向框架风格 。 许多 人们使用 ApplicationContext 在一个 完全声明式的时尚,甚至以编程方 式创建它,但是 而是依赖支持类等 ContextLoader 自动实例化一个 ApplicationContext 正常的一部分 启动过程的J2EE web 应用程序。 提高 BeanFactory 功能 更多的面向框架风格上下文包还提供了 以下功能: 访问消息i18n风格 ,通过 MessageSource 接口。 对资源的访问 ,比如url和文件, 通过 ResourceLoader 接口。 事件发表 到bean实现 ApplicationListener 接口,通过 使用 ApplicationEventPublisher 接口。 加载多个(分层)上下文 , 允许每个集中在一个特定的层,如web 层的一个应用程序,通过 HierarchicalBeanFactory 接口。 5.14.1A国际化使用 MessageSource 这个 ApplicationContext 接口 扩展接口称为 MessageSource , 因此提供国际化(i18n)功能。 春天 还提供了接口 HierarchicalMessageSource ,它可以解决 信息分层次。 这些接口提供了基础 在这春天消息解决效果。 在这些定义的方法 界 面包括: 字符串(字符串的代码,对象getMessage[]参数,字符串 默认情况下,地区loc) :基本方法用于检索 消息从 MessageSource 。 当没有 消息是发现指定场所的,默认的消息 使用。 任何参数传递成为替代值,使用 MessageFormat 提供的功能 标准库。 字符串(字符串的代码,对象getMessage[]参数,语言环境 loc) :基本上与前面的方法,但是 有一点区别:不可以指定默认消息; 如果 消息不能被发现,一个 NoSuchMessageException 抛出。 字符串getMessage(MessageSourceResolvable可分解的, 地区地区) :所有属性用于前面的 方法也包裹在一个类命名 MessageSourceResolvable ,您可以 使用这种方法。 当一个 ApplicationContext 正在加载, 它会自动搜索 MessageSource bean中定义的上下文。 bean必须有名称 MessageSource 。 如果这样一个 豆是发现,所有调用前面的方法都是委托给 消息源。 如果没有消息来源是发现, ApplicationContext 试图找到一个 父包含bean使用相同的名称。 如果是,它使用的bean 随着 MessageSource 。 如果 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 95/514 ApplicationContext 无法找到任何源 的消息,一个空的 DelegatingMessageSource 是 实例化为了能够接受调用定义的方法 以上。 弹簧提供了两个 MessageSource 实现, ResourceBundleMessageSource 和 StaticMessageSource 。 同时实现 HierarchicalMessageSource 为了做 嵌套的消息。 这个 StaticMessageSource 很少 但提供了编程方式使用添加消息源。 这 个 ResourceBundleMessageSource 显示在 下面的例子: format exceptions windows 在示例假设您有三个资源包定义 在您的类路径称为 格式 , 异常 和 Windows 。 任何请求 解决一个消息将被处理的标准方式 解决。JDK 通过resourcebundle的消息。 为目的的例子,假设 内容的两个以上的资源包文件… # in format.properties message=Alligators rock! # in exceptions.properties argument.required=The '{0}' argument is required. 一个程序执行 MessageSource 功能是下一个示例所示。 记住,所有 ApplicationContext 实现也 MessageSource 实现,因此 可以转换为 这个 MessageSource 接口。 public static void main(String[] args) { MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); String message = resources.getMessage("message", null, "Default", null); System.out.println(message); } 从上面的输出结果程序将…… Alligators rock! 所以总结一下, MessageSource 定义 在一个文件叫做 它指明 ,它存在的根本原因 你的类路径。 这个 MessageSource bean 定义 这个概念的“可伸缩反应堆” basenames 财产。 这三个文件传递进来 列表的 basenames 属性文件存在 你的类路径的 根和被称为 格式属性 , 异常属性 ,和 窗口属性 分别。 下一个例子显示了参数传递到信息查找;这些 参数将被转换成字符串和插入的占位符 查找信息。 public class Example { private MessageSource messages; public void setMessages(MessageSource messages) { this.messages = messages; } public void execute() { String message = this.messages.getMessage("argument.required", new Object [] {"userDao"}, "Required", null); System.out.println(message); } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 96/514 } 得到的输出结果的调用 execute() 方法将…… The userDao argument is required. 关于国际化(i18n),春天的不同 MessageResource 实现遵循相同的 语言环境的分辨率和回退的规则为标准JDK ResourceBundle 。 总之,和继续 例子 MessageSource 前面定义的,如果你想要的 解决信息对英国(扎)地区,你将创建 文件称 为 格式en gb属性 , 异常en gb属性 ,和 windows en gb属性 分别。 一般来说,语言环境决议是由周围的环境 的应用程序。 在这个例子中,语言环境对(英国) 消息将被解决是手动指定。 # in exceptions_en_GB.properties argument.required=Ebagum lad, the '{0}' argument is required, I say, required. public static void main(final String[] args) { MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); String message = resources.getMessage("argument.required", new Object [] {"userDao"}, "Required", Locale.UK); System.out.println(message); } 结果输出上述程序将运行 被…… Ebagum lad, the 'userDao' argument is required, I say, required. 你也可以使用 MessageSourceAware 接口来获取一个引用 MessageSource 这被定义。 任何bean, 定义在一个 ApplicationContext 实现 这个 MessageSourceAware 接口是注射 应用程序上下文的 MessageSource 当 bean创建和配 置。 注意 作为一种替代 ResourceBundleMessageSource ,弹簧提供了一个 ReloadableResourceBundleMessageSource 类。 这 变体支持相同的包文件格式,但更灵活的比 基于标准 JDK ResourceBundleMessageSource 实现。 特别是,它允许阅读文件 从任何Spring资源位置(不只是从类 路径)和 支持热重载的包属性文件(而有效 缓存它们之间)。 检查 ReloadableResourceBundleMessageSource javadoc的 细节。 5.14.2A标准和定制的事件 事件处理的 ApplicationContext 提供通过 ApplicationEvent 类和 ApplicationListener 接口。 如果一个bean 实现 ApplicationListener 接口是部署到上下文,每次一个 ApplicationEvent 被发布到 ApplicationContext ,那豆是通知。 从本质 上讲,这是标准的 观察者 设计 模式。 Spring提供了以下标准事件: 5.7为多。 一个内置的事件 事件 解释 ContextRefreshedEvent 发表当 ApplicationContext 初始化 或刷新,例如,使用 refresh() 方法 ConfigurableApplicationContext 接口。 这里的“初始化”意味着所有bean加载, 后处 理器bean是检测并激活,单例对象是 预先实例化, ApplicationContext 对象是准备好了 对 于使用。 只要上下文没有被关闭,一个刷新可以 多次被触发,只要这样的选择 ApplicationContext 其实 支持这种“热”刷新。 例如, XmlWebApplicationContext 支 持热 刷新, GenericApplicationContext 不。 ContextStartedEvent 发表当 ApplicationContext 是开始, 使用 start() 方法 ConfigurableApplicationContext 接口。 “开始”这意味着所有 生命周期 bean接收一个显式的 开始信号。 通常这个信号 是用来启动bean之后 一个显式的停止,但它也可以用来启动组件 没有被配置为自动运行, 例如,组件 这还没有开始初始化。 ContextStoppedEvent 发表当 ApplicationContext 是停止了, 使用 停止() 方法 ConfigurableApplicationContext 接口。 “停止”这意味着所有 生命周期 bean接收一 个显式的 停止信号。 一个停止上下文可能重启通过 start() 调用。 发表当 ApplicationContext 是封闭的,使用 这个 close() 方法 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 97/514 ContextClosedEvent ConfigurableApplicationContext 接口。 “封闭”这意味着所有单例bean 摧毁了。 一 个封闭的环境达到生命终止,不得 刷新或重启。 RequestHandledEvent 一个网络自身事件告诉所有的豆子,一个HTTP请求 已经维修。 这个事件发表 在 在请求完 成。 这个事件是 只适用于使用Spring的web应用程序 DispatcherServlet 。 您还可以创建和发布您自己的自定义事件。 这个例子 演示了一个简单的类,它扩展了春天的 ApplicationEvent 基类: public class BlackListEvent extends ApplicationEvent { private final String address; private final String test; public BlackListEvent(Object source, String address, String test) { super(source); this.address = address; this.test = test; } // accessor and other methods... } 发布一个自定义 ApplicationEvent ,叫 publishEvent() 方法在一个 ApplicationEventPublisher 。 通常这 通过创建一个类,它 实现了吗 ApplicationEventPublisherAware 和 它注册作为一个Spring bean。 下面的例子演示了这样一个 类: public class EmailService implements ApplicationEventPublisherAware { private List blackList; private ApplicationEventPublisher publisher; public void setBlackList(List blackList) { this.blackList = blackList; } public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } public void sendEmail(String address, String text) { if (blackList.contains(address)) { BlackListEvent event = new BlackListEvent(this, address, text); publisher.publishEvent(event); return; } // send email... } } 在配置时,Spring容器将检测到 EmailService 实现 ApplicationEventPublisherAware 并将 自动调用 setApplicationEventPublisher() 。 在现实中, 参数传递将Spring容器本身;你只是 与应用程序交互上下文通过它 ApplicationEventPublisher 接口。 接受定制 ApplicationEvent ,创建 一个类,它实现了 ApplicationListener 并注册它作为一个Spring bean。 下面的例子演示了 这样 一个类: public class BlackListNotifier implements ApplicationListener { private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; } public void onApplicationEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress... } } 注意, ApplicationListener 是 通用参数化与该类型的自定义事件, BlackListEvent 。 这意味着 onApplicationEvent() 方法可 以保持类型安全的, 避免需要向下类型转换。 你可以注册成为许多事件 听众如你所愿,但请注意,默认情况下事件监听器接收 事 件同步。 这意味着 publishEvent() 方法将阻塞直到所有听众 已完成处理事件。 这样做的好处同步和 单线程的方法是,当一个 监听器接收到一个事件,它 是在事务上下文的出版商如果事务 上下文是可用的。 如果一个策略来事件发布变得 有必要,请参考 JavaDoc春天的 ApplicationEventMulticaster 接口。 下面的示例展示了使用bean定义 注册和配置上面的每个类: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 98/514 known.spammer@example.org known.hacker@example.org john.doe@example.org 把它们结合在一起,当 sendEmail() 方法 EmailService 豆被调用时,如果有 任何电子邮件,应该列入黑名单,一个自定义事件类型 BlackListEvent 发表。 这个 blackListNotifier bean注册作为一个 ApplicationListener 从而接收 BlackListEvent ,这时它就可 以通知 适当的政党。 注意 春天的事件机制是专为简单的沟通 Spring bean之间在同一应用程序上下文。 然而,对于 更复杂的企业集 成的需要, 分别维护 春天 集成 项目建设提供了全面的支持 轻量级的, 面向模式 、事件驱动的体系结构,建 立 著名的春天的编程模型。 5.14.3A方便访问底层的资源 为获得最佳的使用和理解应用程序上下文,用户 一般应该熟悉Spring的吗 资源 抽象,如前文所述 章 ChapterA 6, 资源 。 一个应用程序上下文是一个 ResourceLoader ,它可用于负载 资源 年代。 资源 本质上是一个更加丰富的功能吗 版本的JDK类 java净url ,事实上, 的实现 资源 包装一个 实例的 java净url 在适当的地方。 一个 资源 可以获得低级资源 从几乎任何位置在一 个透明的方式,包括从 类路径,文件系统位置,任何地方可描写的标准 URL,以及其他一些变化。 如果资源位置的字符串是一个 简 单的路径没有任何特殊的前缀,这些资源来自 是具体的和适当的到实际的应用程序上下文类型。 您可以配置一个bean部署到应用程序上下文 实现特殊的回调接口, ResourceLoaderAware ,自动 在初始化的时候召回与应用 程序上下文本身 传入的 ResourceLoader 。 你可以 还公开属性的类型 资源 , 被用来访问静态资源;他们将被注入到它像任何 其他属性。 你可以指定那些 资源 属性为简单的字符串的路径, 和依赖特殊的JavaBean 属性编辑器 这是自动 登记的上下文,这 些文本字符串转换到实际 资源 当bean对象 部署。 位置路径或路径提供给一个 ApplicationContext 构造函数实际上是 资源字符串,在简单的形式是恰当的治疗 特定的上下文实 现。 ClassPathXmlApplicationContext 对待一个简单的 位置路径作为一个类路径的位置。 您还可以使用位置路径 (资源字符 串)和特殊前缀力载荷的定义 从类路径或一个URL,而不管实际的上下文类型。 5.14.4A方便 ApplicationContext 为web应用程序的实例化 您可以创建 ApplicationContext 实例声明通过使用,例如,一个 ContextLoader 。 当然,你也可以创建 ApplicationContext 实 例 以编程方式通过使用其中一个 ApplicationContext 实现。 这个 ContextLoader 机制有两种 口味: ContextLoaderListener 和 ContextLoaderServlet 。 他们有相同的 功能,但区别在于 听者版本是不可靠的 Servlet 2.3容器。 在Servlet 2.4规范,Servlet上下文 听众必须执行后立即为web Servlet上下文 创建应用 程序,并可用于服务第一个请求(和 当Servlet上下文即将被关闭)。 作为这样一个Servlet 上下文侦听器是一个理想的地方初始化 弹簧 ApplicationContext 。 所有的事情都是公平的, 你应该喜欢 ContextLoaderListener ; 更多信息兼容性,看看的Javadoc ContextLoaderServlet 。 你可以注册一个 ApplicationContext 使用 ContextLoaderListener 如下: contextConfigLocation /WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml org.springframework.web.context.ContextLoaderListener 侦听器检查 contextConfigLocation 参数。 如果参数不存在,侦听器使用 / - inf /中 作为默认的设置。 当 参数 并 存在,侦听 器分离了 通过使用预定义的字符串分隔符(逗号、分号和空格) 和使用值作为位置将应用程序上下文 搜查了。 ant是基于路径 模式也支持。 的例子是 / - inf / *上下文xml 所有文件名称以结束 与“上下文。 xml”,居住在“不想”目录,和 / - inf / * * / *上下文xml ,因为所有这些文件在任何 子目录的“不想”。 您可以使用 ContextLoaderServlet 而不是 ContextLoaderListener 。 Servlet使用 contextConfigLocation 参数只是作为侦 听器 确实。 5.14.5A部署一个弹簧ApplicationContext作为J2EE RAR文件 在Spring 2.5和以后,可以部署一个春天 ApplicationContext作为RAR文件,封装上下文和它的所有 要求bean类和库jar在J2EE RAR部署单元。 这 相当于一个独立的ApplicationContext引导,就 托管在J2EE环境中,能够访问J2EE服务器 设施。 RAR部署是 一种更自然的替代的场景 部署一个无头WAR文件,实际上,一个WAR文件没有任何HTTP 入口点,仅用于引导一个春天 ApplicationContext在J2EE环境中。 RAR部署是理想的应用程序上下文,不需要HTTP 入口点而是只有消息端点和预定 乔布斯。 豆子在这样的环境下可以使用应用 服务器等资源 JTA事务管理器和JDBC数据源的jndi绑定和JMS ConnectionFactory的实例,也可以注册平台的JMX 服务器-所有 通过弹簧的标准事务管理和JNDI和 JMX支持设施。 应用程序组件还可以与其进行交互 应用程序服务器的JCA WorkManager 通过弹簧的 TaskExecutor 抽象。 检查出的JavaDoc SpringContextResourceAdapter 类的配置细节 参与RAR部署。 对于一个简单的部署一个弹簧ApplicationContext作为 J2EE RAR文件: 包所有应用程序类成一个RAR文件, 这是一个标准的 JAR文件和一个不同的文件扩展名。 添加所有 所需的库jar到根的RAR存档。 添加一个 “meta - inf / ra。 xml部署描述 符”(见 SpringContextResourceAdapter 年代JavaDoc)和 相应的Spring XML bean定义文件(s)(一般 “meta - inf /中”),并 将由此产生的RAR文件到 您的应用程序服务器的部署目录中。 注意 这样的RAR部署单元通常是独立的;他们不 组件公开到外面的世界,即使是对其他模块的 相同的应用程序。 交互与一个基于rar ApplicationContext 通过JMS目的地通常发生,它与其他股票 模块。 一个基于rar ApplicationContext也可以,例如,时间表 有些工作,应对新的文件在文件系统(或相似的)。 如果它 需要允许 同步访问从外面看,它可以为例 出口RMI端点,它当然可以被其他应用程序使用 模块在同一台机器上。 5.15一个BeanFactory 这个 BeanFactory 提供底层基础 对于Spring的IoC功能,但它只是直接使用在集成 与其他第三方框架和现在主要在自然历史 对 于大多数用户的春天。 这个 BeanFactory 和 相关的接口,如 BeanFactoryAware , InitializingBean , DisposableBean ,仍然 出现在春天的 向后兼容性的目的与众多的第三方 框架,结合弹簧。 经常第三方组件, 可以不使用更现代的等价物如 @PostConstruct 或 @PreDestroy 为了保持兼容的JDK 1.4或 避免依赖于jsr - 250。 本节提供了额外的背景的差异 之间的 BeanFactory 和 ApplicationContext 和如何访问 IoC容器直接通过一个经典的单例查 找。 5.15.1A BeanFactory 或 ApplicationContext 吗? 使用一个 ApplicationContext 除非你 有一个充分的理由不这样做。 因为 ApplicationContext 包括所有的功能 BeanFactory ,它通常是被推荐的 在 BeanFactory ,除了一些 情况,比如在一个 applet 在记忆 消费可能是关键和一些额外的千字节可能使一个 差异。 然而,对于大多数典型的企业应用程序和 系统中, ApplicationContext 就是 你会想要使用。 Spring 2.0,后来使 重 使用 BeanPostProcessor 扩展点 (影响代理等等)。 如果你 只使用一个普通的 BeanFactory ,大量的支持 如交易和AOP不会生效,至少不是没有 一些额外的步骤在你的部分。 这种情况可 能是令人困惑,因为 没有什么实际上是错误的配置。 下表列出了所提供的功能 BeanFactory 和 ApplicationContext 接口和 实现。 5.8为多。 一个特征矩阵 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 100/514 特性 BeanFactory ApplicationContext Bean实例化/布线 是的 是的 自动 BeanPostProcessor 登记 没有 是的 自动 BeanFactoryPostProcessor 登记 没有 是的 方便 MessageSource 访问( i18n) 没有 是的 ApplicationEvent 出版 没有 是的 明确注册一个bean后处理器与一个 BeanFactory 实现,你必须 写这样的代码: ConfigurableBeanFactory factory = new XmlBeanFactory(...); // now register any needed BeanPostProcessor instances MyBeanPostProcessor postProcessor = new MyBeanPostProcessor(); factory.addBeanPostProcessor(postProcessor); // now start using the factory 明确注册一个 BeanFactoryPostProcessor 当使用一个 BeanFactory 实现,你必须 写这样的代码: XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml")); // bring in some property values from a Properties file PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); cfg.setLocation(new FileSystemResource("jdbc.properties")); // now actually do the replacement cfg.postProcessBeanFactory(factory); 在这两种情况下,明确登记步骤是不方便, 是一个原因为什么不同 ApplicationContext 实现 优先高于平原 BeanFactory 实现在 绝大多数弹簧支持应用程序, 尤其是当使用 BeanFactoryPostProcessors 和 BeanPostProcessors 。 这些机制实现 重要的功 能,如财产占位符替换和 AOP。 5.15.2A胶水代码和邪恶的单例 最好是写在一个依赖项注入最应用程序代码 (DI)风格,代码是服务的一个Spring IoC容器,有 自己的依赖关系由容器提供创建时, 是完全没有意识到容器。 然而,对于小胶水 层的代码,有时需要领带其他代码在一起,你 有时候需要一个单例(或准单例)风格访问 一个春天 IoC容器。 例如,第三方代码可能试图建构新的 对象直接( class . forname() 风格),没有了 能力的获得这些对象一个 Spring IoC容器。 如果 构造的对象由第三方代码是一个小的存根或代理, 然后使用一个单例风格访问Spring IoC容器吗 得到 一个真正的对象来代表,然后控制反转仍 得到的大多数代码(对象走出 容器)。 因此大多数代码仍然没有意识到容器或它如何 被 访问,仍脱离其他代码,所有随后的吗 福利。 ejb stub也可以使用这种方法来代表/代理一个 普通的Java实现对象,是从一个 Spring IoC容器。 虽然Spring IoC容器本身理想情况下不需要一个 单例,它可能是不切实际的在内存使用方面的或 初始化时间 (当使用bean在Spring IoC容器这样的 作为一个Hibernate SessionFactory )对于每个 bean来使用它自己的,非单体Spring IoC容器。 查找应用程序上下文在服务定位器风格 有时唯一的选择来访问共享的spring管理 组件,如在EJB 2.1环境,或当你想要分享 一个 作为父母,WebApplicationContexts ApplicationContext跨 WAR文件。 在这种情况下你应该考虑使用的实用程序类 ContextSingletonBeanFactoryLocator 定位器,描述在这 SpringSource团队博客条目 。 [ 1 ] 看到 背景 [ 2 ] 看到 SectionA 5.4.1之前,一个​injectiona​​​依赖性 6.一个资源 6.1一个介绍 Java的标准 java净url 类和 标准处理程序对于各种URL前缀不幸的是不完全的 充足的足够的所有访问底层的资源。 例如, 没有 标准化的 url 实现 这可能是用于访问资源,需要获得 类路径或相对于一个 ServletContext 。 虽然它是可能的 新处理程序注册 为专业 url 前缀(类似于现有的处理程序等的前缀 http: ),这通常是很复杂的, url 接口仍然缺乏一些可取的 功能,如一个方法来检 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 101/514 查存在的 资源被指出。 6.2一个的 资源 接口 春天的 资源 接口指的是 成为一个更有能力的接口访问底层的抽象 资源。 public interface Resource extends InputStreamSource { boolean exists(); boolean isOpen(); URL getURL() throws IOException; File getFile() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription(); } public interface InputStreamSource { InputStream getInputStream() throws IOException; } 一些最重要的方法 资源 界面: getInputStream() :定位并打开了 资源,返回一个 InputStream 阅读 从资源。 预计每个调用返回 新鲜 InputStream 。 都有责任 调用者关闭流。 存在() :返回一个 布尔 指示是否这个资源实际 存在于物理形式。 isOpen() :返回一个 布尔 指示是否这个资源代表 一个手柄,一个开放的流。 如果 真正的 , InputStream 不能读多次,和 必 须读一次只有然后关闭以避免资源泄漏。 将 是 假 对于所有通常的资源实现, 除了 InputStreamResource 。 getDescription() :返回一个描述 对于这个资源,用于错误输出当处理 资源。 这通常是完全合格的文件名称或实际的 资源 的URL。 其他的方法让你获得一个实际 url 或 文件 对象 代表资源(如果底层的实现是兼容的, 和支持该功能)。 这个 资源 抽象是使用 广泛的在春天本身,作为一个参数类型在许多方法 当一个资源需要签名。 其他方法在某些Spring api (如 构造函数不同 ApplicationContext 实现),拿一个 字符串 在朴实或简单的形式用于 创建一个 资源 恰当 上下文实现,或通过特 殊的前缀 字符串 路径,允许调用者指定一个 特定 资源 实现必须 创建和使用。 而 资源 接口用于 很多春天和春季之前,它实际上是非常有用的使用作为一个 一般的实用程序类本身在您自己的代码,因为对资 源的访问, 即使你的代码不知道或关心的任何其他部分的弹簧。 虽然这夫妇代码到春天,真的只有夫妻到这 小套实用工具类,它 是作为一个更有能力 替代 url ,可以考虑 相当于其他库用于这一目的。 重要的是要注意, 资源 抽象不能替代 功能:它将它在可能的情况下。 例如,一个 UrlResource 将URL,使用包装 url 做它的工作。 6.3内置 资源 实现 有许多 资源 实现,来直接提供开箱即用的 春天: 6.3.1A UrlResource 这个 UrlResource 包装 java净url 的,并且可以用于访问任何 对象,通常可以通过一个URL,比如文件、一个HTTP 目标,FTP target,等。所有url都标准化 字符串 表示,这样适当的 标准化的前缀是用来表示一个URL类型从另一个。 这包括 文件: 访问文 件系统路径, http: 为通过HTTP协议访问的资源, ftp: 通过FTP访问资源,等等。 一个 UrlResource 是由Java代码吗 显式地使用 UrlResource 构造函数,但 经常会被隐式地创建当你叫一个API中采用的方法吗 一个 字符串 的参数,它是用来代表一个 路径。 对于后一种情况,一个javabean 属性编辑器 将最终决定 哪种类型的 资源 创 建。 如果 路径字符串包含几个知名(,)前缀如 类路径: ,它将创建一个合适的专业 资源 为该前缀。 然而,如果它 不能识别前缀,它 会认为这只是一个标准 URL字符串,并将创建一个 UrlResource 。 6.3.2A ClassPathResource 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 102/514 这个类代表一个资源应得到 类路径。 它使用线程上下文类加载器要么,给定 类装入器,或一个给定的类加载资源。 这 资源 实现 支持分辨率 java输入输出文件 如果类 道路资源驻留在文件系统,但不是为类路径 资源驻留在一个罐子里,没有被 扩展(由 servlet引擎,或任何环境)文件系统。 到 地址这各种 资源 实现永远支持分辨率作为 java净url 。 一个 ClassPathResource 是由Java代码吗 显式地使用 ClassPathResource 构造函数,但往往会被隐式地创建当你调用一个API 方法将一个 字符串 论点 代表一个路径。 对于后一种情况,一个javabean 属性编辑器 将承认特别 前缀 类路径: 在字符串的路 径,并创建一个 ClassPathResource 在这种情况下。 6.3.3A FileSystemResource 这是一个 资源 实现 对于 java输入输出文件 处理。 显然,它支持 分辨率作为 文件 ,作为一个 url 。 6.3.4A ServletContextResource 这是一个 资源 实现 对于 ServletContext 资源, 解释相对路径在相关的web应用程序的根 目录。 这总是支持流访问和URL访问,但只允许 java输入输出文件 当web应用程序访问 档案是展开的,资源是物理文件系统上。 是否 扩大,像这样的文件系统,或者 直接访问从罐子里或其他地方像一个DB(它的 可以想象的)实际上是依赖于Servlet容器。 6.3.5A InputStreamResource 一个 资源 实现一个 鉴于 InputStream 。 这只能 使用如果没有具体的 资源 实现是适用的。 特别是,喜欢 ByteArrayResource 或任何基于文件的 资源 实现, 可能的。 与其他 资源 实现,这是一个描述符的 已经 打开资源——因此返回 真正的 从 isOpen() 。 不 使用它如果你需要保持资源描述 符某处,或如果你 需要多次读取流。 6.3.6A ByteArrayResource 这是一个 资源 实现 对于一个给定的字节数组。 它创建了一个 ByteArrayInputStream 对于给定的字节 数组。 它是有用的对于加载内容从任何给定的字节数组,没有 不得不求助于一个一次性 InputStreamResource 。 6.4一个的 ResourceLoader 这个 ResourceLoader 接口指的是 去实现对象可以返回(即负载) 资源 实例。 public interface ResourceLoader { Resource getResource(String location); } 所有应用程序上下文实现 ResourceLoader 接口,因此所有 应用程序上下文可以用来获得 资源 实例。 当你打电话 getResource() 在一个特定的 应用程序上下文,和位置路径指定的没有 特定的前缀,您将会得到一个 资源 类型,是恰 当的 特定的应用程序上下文。 例如,假设以下代码片段 代码被执行的反对 ClassPathXmlApplicationContext 实例: Resource template = ctx.getResource("some/resource/path/myTemplate.txt"); 返回的是什么会 ClassPathResource ,如果同样的方法被执行死刑 反对 FileSystemXmlApplicationContext 实例, 你会得到一 个 FileSystemResource 。 对于一个 WebApplicationContext ,你会得到一个 ServletContextResource ,等等。 这样,你可以加载资源在一个时尚的适合 特定的应用程序上下文。 另一方面,你也可能迫使 ClassPathResource 被使用,不管 应用程序上下文类型,通过指定特殊 类路径: 前缀: Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); 同样,一个人可以强制 UrlResource 是 使用指定的任何标准 java净url 前缀: Resource template = ctx.getResource("file:/some/resource/path/myTemplate.txt"); Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt"); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 103/514 下面的表总结了战略转换 字符串 年代 资源 年代: 6.1为多。 一个资源字符串 前缀 例子 解释 类路径: 类路径:com/myapp/config.xml 从类路径加载。 文件: 文件:/数据/ config . xml 加载 url ,从 文件系统。 [ 1 ] http: http://myserver/logo.png 加载 url 。 (无) /数据/ config . xml 取决于底层 ApplicationContext 。 [ 1 ] 但看到也 SectionA 6 7 3,一个​​ FileSystemResource caveatsa​​ 。 6.5一个的 ResourceLoaderAware 接口 这个 ResourceLoaderAware 接口是 一个特殊的标记接口,确定对象,期望提供 与 ResourceLoader 参考。 public interface ResourceLoaderAware { void setResourceLoader(ResourceLoader resourceLoader); } 当一个类实现 ResourceLoaderAware 并部署到一个 应用程序上下文(它作为spring托管bean),这是公认的 ResourceLoaderAware 由应用程序 上下文。 应用程序上下文将调用 setResourceLoader(ResourceLoader) ,提供 本身作为 参数(记住,所有应用程序上下文在春天 实现 ResourceLoader 接口)。 当然,因为一个 ApplicationContext 是 ResourceLoader ,该bean也可以 实现 ApplicationContextAware 界面和使用提供的 应用程序上下文直接加载 资源,但一般来说,最好是使用专业的 ResourceLoader 接口如果是所有 这是需要的。 代码就被耦合 到资源加载 接口,它可以被认为是一个实用的界面,而不是整体 春天 ApplicationContext 接口。 在Spring 2.5中,您可以依赖的自动装配 ResourceLoader 作为一种替代 实施 ResourceLoaderAware 接口。 “传统” 构造函 数 和 byType 自动装配模式(详见 SectionA 5 4 5,一个​​collaboratorsa​​自动装配 ) 现在能够提供一个依赖的类型 ResourceLoader 无论是一个 构造函数参数或setter方法参数分别。 更多的灵活性 (包括自动装配领域的能力和多个参数方法), 可以考虑 使用新的基于注解的自动装配功能。 在这种情况下, ResourceLoader 将autowired的到一个领域, 构造函数参数或 方法参数,是期待 ResourceLoader 类型只要字段, 构造函数或方法有问题 @ autowired 注释。 有关更多信息, 看到 SectionA 5 9 2,一个​​ @ autowired 一个​​ 。 6.6一个 资源 作为依赖项 如果bean本身是要确定和供应资源 路径通过某种形式的动态过程,它可能是有意义的 bean使用 ResourceLoader 接口 加载资 源。 考虑一个例子装载一个模板的一些 排序,具体资源需要取决于角色的 用户。 如果资源是静态的,它是有意义的,消除了使用 的 ResourceLoader 界面完全, 和只有bean公开 资源 属性需要,预计他们将会注入到它。 是什么让它琐碎,然后注入这些属性,是所有 应用程序上下文注册和使用一个特殊的javabean 属性编辑器 可转换 字符串 路径 资源 对象。 所以如果 myBean 有一个模板属性的类型 资源 ,它可以配置一个 简单的字符串资源的,如下所示: 注意,资源路径没有前缀,所以因为 应用程序上下文本身是要用作 ResourceLoader ,资源本身将 加载通过 ClassPathResource , FileSystemResource ,或 ServletContextResource (适当地) 根据具体类型的上下文。 如果有一个需要强迫一个特定的 资源 类型被使用,那么一个前缀可能 被使用。 以下两个例子展示如何强制 ClassPathResource 和一个 UrlResource (后者被用来访问 文件系统文件)。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 104/514 6.7一个应用程序上下文和 资源 路径 6.7.1A构建应用程序上下文 一个应用程序上下文构造函数(用于一个特定的应用程序 上下文类型)通常以一个字符串或字符串数组作为 位置路径(s)的资源(s) 如XML文件构成 定义的上下文。 当这样一个位置路径没有前缀,具体 资源 类型由,路径和 用来加载bean定义,取决于和是适当的 特定的应用程序上下文。 例如, 如果您创建一个 ClassPathXmlApplicationContext 如下: ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml"); 该bean定义将从类路径装入,作为一个 ClassPathResource 将 使用。 但如果你创建一个 FileSystemXmlApplicationContext 作为 如下: ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml"); 该bean定义将从文件系统加载的位置,在 这种情况下相对于当前工作目录。 注意,使用特殊的类路径前缀或标准 URL前缀的位置路径将会覆盖默认的类型的 资源 创建加载定义。 所以这 FileSystemXmlApplicationContext … ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml"); … 将会加载它的bean定义从类路径中。 然而,这仍然是一个 FileSystemXmlApplicationContext 。 如果它是 随后用作 ResourceLoader , 任何无前缀的路径仍将被作为文件系统路径。 构建 ClassPathXmlApplicationContext 实例——快捷键 这个 ClassPathXmlApplicationContext 公开了一个数量的构造函数实例化启用方便。 基本的想法是,一个供应仅仅包含字符 串数组 刚刚的文件名的XML文件本身(没有领导 路径信息),和一个 也 供应一个 类 ; ClassPathXmlApplicationContext 将获得 从提供的类路径信息。 一个例子将希望明确这一点。 考虑一个目录 布局看起来像这样: com/ foo/ services.xml daos.xml MessengerService.class 一个 ClassPathXmlApplicationContext 实例 由bean中定义的 “services . xml” 和 “daos xml的 可以被实例化像 所以… ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"}, MessengerService.class); 请做参考的javadoc ClassPathXmlApplicationContext 类 各种构造函数的细节。 6.7.2A通配符在应用程序上下文构造函数资源路径 在应用程序上下文的资源路径构造函数值可能 是一个简单的路径(如上所示),它有一个一对一的映射 目标资源,或交替可能包含 特殊的“classpath *:“ 前缀和/或内部ant是基于正则表达式匹配使用 春天的 PathMatcher 实用程序)。 后者的两 实际上是 通配符 一个使用这种机制是通过组件样式时做 应用程序装配。 所有组件可以“发布”上下文定义 一个众所周知的位置路径片段,当最 终的应用程序 上下文创建使用相同的路径前缀的通过 classpath *: ,所有的组件将被碎片 自动弹出。 注意,这个它是特定于使用资源的路径 应用程序上下文构造函数(或当使用 PathMatcher 实用程序类层次结构直接), 和解决在 施工时间。 它没有关系 资源 类型本身。 这是不可能的 使用 classpath *: 建立实际的前缀 资源 作为一个资源点才 一次一个 资源。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 105/514 ant是基于模式 当路径位置包含一个ant是基于模式,例如: /WEB-INF/*-context.xml com/mycompany/**/applicationContext.xml file:C:/some/path/*-context.xml classpath:com/mycompany/**/applicationContext.xml … 解析器是一个更为复杂的但定义过程 试图解决通配符。 它产生了一个资源的路径了 最后,得到非通配符段URL从它。 如果 这个URL 不是一个“jar:“URL或特定容器变体(如。 “ 邮政编码: “在WebLogic。” wsjar “在 WebSphere等等),那么一个 java输入输出文件 是 得到它,用来解决通配符的遍历 文件系统。 对于一个jar URL,解析器可以获取一个 java.net.JarURLConnection 从它或手动 解析jar URL,然后遍历jar文件的内容 解决通配符。 影响可移植性 如果指定的路径已经是一个文件的URL(要么 显式或隐式地因为基地 ResourceLoader 是 文件系统,那么它是保证工作 完全便 携时尚。 如果类路径中指定的路径是一个位置,那么 解析器必须获得最后非通配符路径段URL通过 classloader getresource() 调用。 因 为这 只是一个节点的路径(而不是文件结束时),它实际上是吗 未定义(在 类加载器 javadoc) 到底什么样的一个URL返回在这种 情况下。 在实践中,它 始终是一个 java输入输出文件 代表 目录路径资源可以解析为一个文件系统 位置,或者一个jar URL的某 种资源的类路径 解析为一罐的位置。 不过,有一个可移植性关注 这个操作。 如果一个jar URL获得过去非通配符段, 这个解析器必须能够得到一个 java.net.JarURLConnection 从它,或 手动解析jar URL,能 够行走的内容 jar和解决通配符。 这将工作在大多数环境中, 但会失败在别人,强烈建议 通配符解决资源来自jar被彻底 测试您 的特定环境在你依赖它。 这个 classpath *: 前缀 当构建一个基于xml的应用程序上下文,一个位置 字符串可以使用特殊的 classpath *: 前缀: ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); 这种特殊的前缀指定类路径中所有资源 匹配给定的名称必须获得(在内部,这本质上 发生通过 ClassLoader.getResources(…) 调用),然后合并以形成最终的应用程序上下文 定义。 Classpath *:可移植性 通配符类路径依赖 getResources() 方法 潜在的类加载器。 因为大多数应用服务器现在供应 他们自己的类 加载器实现的,这种行为可能会有所不同 尤其是在处理jar文件。 一个简单的测试来检查 classpath * 作品是 使用类加载器来加载一个文件 在一个jar的类路径: .getClassLoader getClass()().getResources(“< someFileInsideTheJar >”) 。 试试这个测试的文件具有相同的名字,但被放置 在两个不同的位置。 如果 一个不合适的结果 回来,检查应用程序服务器文档设置 这可能会影响行为的类加载器。 “ classpath *: “前缀也可以结合 与 PathMatcher 模式在其余的位置路径,对于 例“ classpath *:meta - inf / *豆xml ”。 在这个 情况下,分辨率的策略很简单:一个 ClassLoader.getResources()调用是使用在过去的非通配符路径 段获得所有匹配的资 源的类装入器 层次结构,然后从每个资源相同的PathMatcher分辨率 上面描述的策略用于通配符子路径。 其他笔记有关通配符 请注意,“ classpath *: “当 结合ant是基于模式只会工作可靠,至少 一个根目录的模式开始之前,除非实际的目标 文件驻留在文 件系统。 这意味着模式像 “ classpath *:* . xml “不会检索文件 根的jar文件,而仅仅从根的扩大 目录。 这源于一个限制在 JDK的 ClassLoader.getResources() 方法只有 返回文件系统的位置对于一个传入空字符串(指示 潜在的根搜索)。 ant是基于模式” 类路径: “ 资源是不能保证找到匹配的资源如果根 包来搜索可在多个类路径位置。 这 是因为资源如 com/mycompany/package1/service-context.xml 可能只在一个位置,但是当一个路径如 classpath:com/mycompany/**/service-context.xml 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 106/514 jsr - 303 Bean验证 Spring框架支持jsr - 303 Bean验证调整 它春 天的 验证器 接口。 一个应用程序可以选择支持jsr - 303 Bean验证 一旦在全球范围内, 中描述的 SectionA 7.8,一 个​​弹簧3 Validationa​​ ,使用它 专门为所有验证 需求。 应用程序也可以注册 额外的春天 验证器 实例 每 DataBinder 实例中描述的那样 SectionA 7 8 3,一个​​配置一个DataBindera​​ 。 这可能是 有用的 插入验证逻辑没有注释的使用。 用于尝试解决它,解析器将工作了(第一次)的URL 返回 "com/mycompany getResource(”) ;。 如果 这个基础包节点存在于多 个类加载器的位置, 实际资源可能不是下面结束。 因此,最好是用 “ classpath *: “与同一ant是基于模式 这种情况下,它将搜 索所有的类路径位置,包含 根包。 6.7.3A FileSystemResource 警告 一个 FileSystemResource 这不是附加 一个 FileSystemApplicationContext (即一个 FileSystemApplicationContext 不是实 际的 ResourceLoader )将治疗绝对vs。 正如你期望的相对路径。 相对路径是相对的 当前工作目录,而绝对路径指的是相对于 根 的文件系统。 为向后兼容(历史)原因,然而,这 变化当 FileSystemApplicationContext 是 这个 ResourceLoader 。 这个 FileSystemApplicationContext 只是强迫所有 附加 FileSystemResource 实例来治疗 所有的位置路径为相对,他们是否从一个 领先的削减 或不是。 在实践中,这意味着以下是等价的: ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/context.xml"); ApplicationContext ctx = new FileSystemXmlApplicationContext("/conf/context.xml"); 像以下:(尽管它会让他们感觉 是不同的,作为一个案例是相对的和其他绝对。) FileSystemXmlApplicationContext ctx = ...; ctx.getResource("some/resource/path/myTemplate.txt"); FileSystemXmlApplicationContext ctx = ...; ctx.getResource("/some/resource/path/myTemplate.txt"); 在实践中,如果真正的绝对路径,它需要的文件系统是 更好的放弃使用绝对路径与 FileSystemResource / FileSystemXmlApplicationContext ,只是力量 使用一个 UrlResource ,通过使用 文件: URL前缀。 // actual context type doesn't matter, the Resource will always be UrlResource ctx.getResource("file:/some/resource/path/myTemplate.txt"); // force this FileSystemXmlApplicationContext to load its definition via a UrlResource ApplicationContext ctx = new FileSystemXmlApplicationContext("file:/conf/context.xml"); 7。 一个验证、数据绑定、类型转换 7.1一个介绍 有优点和缺点考虑验证业务逻辑, 和弹簧提供了一个设计验证(和数据绑定), 不 排除或其中之一。 具体验证不应该绑定到 web层,应该很容易定位,它应 该有可能塞 在任何验证器可用。 考虑到上述,春天已经来了 一个 验证器 界面,都是基本的 非常有用的在每层细节的一个应用程序。 数据绑定是有用的因为允许用户输入是动态绑定 领域模型的一个应用程序 (或者任何你使用的对象 处理用户输入)。 Spring提供了所谓的 DataBinder 就这么干吧。 这个 验证器 和 DataBinder 弥补 验证 包,它主 要是用于但不 限于MVC框架。 这个 BeanWrapper 是一项基本 在Spring框架和概念用于很多地方。 然 而, 你可能不会有需要使用 BeanWrapper 直接。 因为这是 参考文档不过, 我们认为一些解释可能在 订单。 我们将解释 BeanWrapper 在 这一章,因 为如果你要使用它,你会最 可能这样做当试图将数据绑定到对象。 春天的DataBinder和低层BeanWrapper都使用 PropertyEditors解析和格式化属性值。 这个 属性编辑器 概念的一部分 JavaBeans规范,也是本章中解释。 弹簧3 介绍了一种“核心。 转换”方案,提供了一个通用类型 转换工具,以及一个更高层次 的“格式”包 格式化UI字段值。 这些新的包可能被用作简单的 PropertyEditors替代品,也将讨论在这 一章。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 107/514 7.2验证使用Spring的 验证器 接口 弹簧特性一个 验证器 接口 你可以使用验证对象。 这个 验证器 接口工作使用一个 错误 因此,在验证对象, 验证器可以报告验证 失败 错误 对象。 让我们考虑一个小数据对象: public class Person { private String name; private int age; // the usual getters and setters... } 我们将提供验证行为 人 类通过实现以下两个 方法 org.springframework.validation.Validator 接口: 支持(类) ——这 验证器 验证的实例 提供 类 吗? 验证(对象, org.springframework.validation.Errors) -验证 给定对象和案例验证错误,注册那些 给定的 错误 对象 实现 验证器 相当 简单,尤其是当你知道的 ValidationUtils 辅助类,春天 框架还提供了。 public class PersonValidator implements Validator { /** * This Validator validates *just* Person instances */ public boolean supports(Class clazz) { return Person.class.equals(clazz); } public void validate(Object obj, Errors e) { ValidationUtils.rejectIfEmpty(e, "name", "name.empty"); Person p = (Person) obj; if (p.getAge() < 0) { e.rejectValue("age", "negativevalue"); } else if (p.getAge() > 110) { e.rejectValue("age", "too.darn.old"); } } } 正如您可以看到的, 静态 rejectIfEmpty(. .) 方法 ValidationUtils 类是用来拒绝 ' name ' 财产如果是 空 或 空字符串。 看看 Javadoc的吗 ValidationUtils 类来看看功能它 除了前面所示的例子了。 虽然你可以实现一个单 验证器 类来验证每个 嵌套对象在一个富裕的对象,它可能是更好的封装 验证逻辑对于每个嵌套类的对 象在它自己的 验证器 实现。 一个简单的例子 的 “富有” 对象将是一个 客户 这是由两种 字符串 属性(第一和第二名)和一个 复杂 地址 对象。 地址 对象可能是独立使用的 客户 对象,所以一个截然不同的 AddressValidator 已经实现了。 如果你想要 你 CustomerValidator 重用逻辑包含 在 AddressValidator 类不通过 去复制粘贴,你可以依赖注入或实例化 AddressValidator 在你 CustomerValidator ,使用它像这样: public class CustomerValidator implements Validator { private final Validator addressValidator; public CustomerValidator(Validator addressValidator) { if (addressValidator == null) { throw new IllegalArgumentException( "The supplied [Validator] is required and must not be null."); } if (!addressValidator.supports(Address.class)) { throw new IllegalArgumentException( "The supplied [Validator] must support the validation of [Address] instances."); } this.addressValidator = addressValidator; } /** * This Validator validates Customer instances, and any subclasses of Customer too */ public boolean supports(Class clazz) { return Customer.class.isAssignableFrom(clazz); } public void validate(Object target, Errors errors) { ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required"); Customer customer = (Customer) target; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 108/514 try { errors.pushNestedPath("address"); ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors); } finally { errors.popNestedPath(); } } } 验证错误报告 错误 对象传递到验证器。 在 Spring Web MVC的情况下你可以使用 <春天:bind / > 标签检查错误消息,当然你 也可以检查 错误对象自己。 更多的信息关于这些方法它提供了可以 被发现从Javadoc。 7.3一个解决代码错误消息 我们讨论关于数据绑定和验证。 输出消息 相应的验证错误的最后一件事就是我们需要讨论。 在我们的示例所示,我们拒绝了 名称 和 年龄 场。 如果我们要输出误差 消息通过使用 MessageSource ,我们将 使用错误代码我们给当拒绝字段(' name ' 和“年龄”在这种情况下)。 当你调用(无论是直接或间接, 使用例如 ValidationUtils 类) rejectValue 或另一个 拒绝 方法 错误 接口,底层 实现将不仅注册代码,你已经通过了,但也 许多额外的错误代码。 这是什么错误代码注册 取决于 MessageCodesResolver 这 是使用。 默认情况下, DefaultMessageCodesResolver 使用,对吗 例子不仅注册消息的代码你给, 但也 信息,包括字段名你传递给拒绝的方法。 所以 如果你拒绝一个字段使用 rejectValue(“时代”, “太老”) ,除了 太老 代 码,春天也将寄存器 太老 和 太老的年龄智力 (第一个将包括 字段名,第二个将包括字段类型);这是 作为一个方便的辅助开发人员 针对错误消息和 诸如此类的。 更多信息 MessageCodesResolver 和默认 策略可以发现网上的Javadocs为 MessageCodesResolver 和 DefaultMessageCodesResolver 分别。 7.4一个Bean操纵和 BeanWrapper 这个 org.springframework.beans 包坚持 JavaBeans标准提供的太阳。 是一个简单的JavaBean类 一个默认的无参数的构造 函数,它遵循命名约定, (通过一个例子)属性命名 bingoMadness 会有一个setter方法 setBingoMadness(. .) 和一个getter方法 getBingoMadness() 。 更多 JavaBeans和规范的信息,请参考太阳的 网站( java.sun.com/products/javabeans )。 一个非常重要的类在bean包是 BeanWrapper 接口和相应的 实现( BeanWrapperImpl )。 作为引用 Javadoc, BeanWrapper 提供 功能设置和获取属性值(单独或批量), 得到属性描述符,并查询属性,以确定它们是否 可读或写。 同时, BeanWrapper 提供 了支持嵌套的属性,使设置的属性 在子属性到一个无限的深度。 然后, BeanWrapper 支持能够添加 标准JavaBeans PropertyChangeListeners 和 VetoableChangeListeners ,而不 需要支持的代码在目标类。 最后但并非最不重要, BeanWrapper 提供支持 设置索引属性。 这个 BeanWrapper 通常不使用 应用程序代码直接,而是由 DataBinder 和 BeanFactory 。 的方式 BeanWrapper 作品部分 显示它的名字: 它封装了一个bean 执行 上的动作,bean,如设置和获取属性。 7.4.1A设置和获取基本和嵌套的属性 设置和获取属性使用 setPropertyValue(s) 和 getPropertyValue(s) 方法,都有一个 两个重载的变体。 他们都是更详细地描述 Javadoc春天伴随着。 重要的是要知道,那里 是几个约定指示对象的属性。 一个 几个例子: 7.1为多。 一个属性的范例 表达式 解释 名称 表明房地产 名称 对应方法 getName() 或 isName() 和 setName(. .) 帐户名称 表明嵌套的属性 名称 的 房地产 帐户 相应的如 方法 .setName getAccount()() 或 getAccount(). getname() 帐户[2] 表示 第三 元素的 索引属性 帐户 。 索引属性 类型可以是 数组 , 列表 或其他 自然有序 收集 帐户(公司名 称) 显示值的映射条目索引键 公司名称 地图的属性 帐户 下面你会发现一些例子的使用 BeanWrapper 获取和设置 属性。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 109/514 (下一节不是至关重要的,你如果 你不打算工作 BeanWrapper 直接。 如果你只是 使用 DataBinder 和 BeanFactory 和他们的 开箱即用的 实现,你应该跳至有关部分 PropertyEditors )。 考虑以下两类: public class Company { private String name; private Employee managingDirector; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Employee getManagingDirector() { return this.managingDirector; } public void setManagingDirector(Employee managingDirector) { this.managingDirector = managingDirector; } } public class Employee { private String name; private float salary; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } } 下面的代码片段显示了一些示例,演示如何检索 和操作的一些属性实例化 公司 和 员工 : BeanWrapper company = BeanWrapperImpl(new Company()); // setting the company name.. company.setPropertyValue("name", "Some Company Inc."); // ... can also be done like this: PropertyValue value = new PropertyValue("name", "Some Company Inc."); company.setPropertyValue(value); // ok, let's create the director and tie it to the company: BeanWrapper jim = BeanWrapperImpl(new Employee()); jim.setPropertyValue("name", "Jim Stravinsky"); company.setPropertyValue("managingDirector", jim.getWrappedInstance()); // retrieving the salary of the managingDirector through the company Float salary = (Float) company.getPropertyValue("managingDirector.salary"); 7.4.2A内置 属性编辑器 实现 弹簧使用的概念 PropertyEditors 到 效应之间的转换一个 对象 和一个 字符串 。 如果你仔细想想,它有时可能 是方便能够以不 同的方式表示属性比 对象本身。 例如,一个 日期 可以 代表在一个人类可读的方式(如 字符串 ” 2007-14-09 ”),而 我们仍然 能够将人类可读的形式回到最初 日期(或甚至更好:转换任何日期中输入一个人类可读的形式, 回到 日期 对象)。 这种行为可以 通过 注册自定义的编辑器 的类型 java bean属性编辑器 。 注册 定制编辑器在一个 BeanWrapper 或 在一个特定的IoC容器 交替就像前面提到过的 章,给它的知识如何转换属性 所需的类型。 阅读更多关于 PropertyEditors 在的Javadoc java bean 包 提供的太阳。 几个例子,在春天使用财产编辑: 设置属性在bean 是通过使用 PropertyEditors 。 当提及 以 作为一个房地产的价值 一些豆你宣布在XML文件中,弹簧将 (如果setter 相应的属性 类 参数)使用 ClassEditor 试图解决这一参数 一个 类 对象。 解析HTTP请求参数 在春天的 MVC框架是通过使用各种各样的 PropertyEditors 你可以手动绑定在所有 子类的 CommandController 。 春天有很多内置的 PropertyEditors 让生活简单。 每一个是下面列出的,他们都是 位于 org.springframework.beans.propertyeditors 包。 大部分,但不是全部(如上下文),默认注册通过 BeanWrapperImpl 。 在属 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 110/514 性编辑器是什么 可配置在一些时尚,你可以注册自己的课程仍然 变体来覆盖默认的一个: 为多7 2一个内置 PropertyEditors 类 解释 ByteArrayPropertyEditor 编辑字节数组。 只会被转换成字符串 对应的字节表示。 默认注册 通过 BeanWrapperImpl 。 ClassEditor 解析字符串代表实际的类和类 倒过来。 当一个类是没有找到,一个 IllegalArgumentException 抛出。 默认注册由 BeanWrapperImpl 。 CustomBooleanEditor 定制属性编辑器 布尔 属性。 默认注册 通过 BeanWrapperImpl ,但是,可以 被注册自定 义的实例,它作为自定义 编辑器。 CustomCollectionEditor 属性编辑器集合,将任何源 收集 对于一个给定的目标 收集 类型。 CustomDateEditor 可定制的属性编辑java跑龙套。 目前为止,支持 一个自定义DateFormat。 没有注册的默 认情况下。 必须用户 注册需要以适当的格式。 CustomNumberEditor 可定制的属性编辑任意数量子类像 整数 , 长 , 浮 , 双 。 默认注册由 BeanWrapperImpl , 但可以被注册自定义的实例,它作为一个 自定义编辑器。 FileEditor 能够解决字符串 java输入输出文件 对象。 注册的 违约 BeanWrapperImpl 。 InputStreamEditor 单向属性编辑器,可以将一个文本字符串 和生产(通过一个中间 ResourceEditor 和 资源 ) 一个 InputStream ,所以 InputStream 属性可能是 直接设置为字符串。 注意,默认使用 不会 关闭 InputStream 为你! 默认注册由 BeanWrapperImpl 。 LocaleEditor 能够解决字符串 地区 对象,反之亦然(字符串 格式是(语言)_(国家)_(变种),这是相同的 事 toString()方法的现场提供)。 注册的 违约 BeanWrapperImpl 。 PatternEditor 能够解决字符串到JDK 1.5 模式 对象,反之亦然。 PropertiesEditor 能够转换字符串(格式化使用格式 为定义在java . lang的Javadoc。 属性类) 属性 对象。 默认注册 通过 BeanWrapperImpl 。 StringTrimmerEditor 属性编辑器,阀内件的字符串。 还允许 一个空字符串转换成一个 空 值。 不是注册在默认 情况下,用户必须注册为 需要。 URLEditor 能够解决一个URL的字符串表示的 实际 url 对象。 默认注册 通过 BeanWrapperImpl 。 弹簧使用 java.beans.PropertyEditorManager 设置 搜索路径属性编辑,他们可能需要。 搜索 路径还包括 太阳bean编辑 ,其 中包括 属性编辑器 实现类型 如 字体 , 颜色 ,和 大部分的原始类型。 还要注意标准JavaBeans 基础设施将自动发现 属性编辑 器 类(没有你 需要注册他们明确)如果他们在同一个包 他们处理的类,这个类的名称相同, “编辑器” 附加的;例如,一个可能的 下面的类和包的结构,这就足够了 FooEditor 类被认可和使用的 属性编辑器 对于 foo 类型属性。 com chank pop Foo FooEditor // the PropertyEditor for the Foo class 请注意,您还可以使用标准的 BeanInfo JavaBeans机制在这里 (描述 在这里没有惊人的细节 )。 下面的示例使用找到的 BeanInfo 机制来明确 注册一个或多个 属性编辑器 实例与属性相关的类。 com chank pop Foo FooBeanInfo // the BeanInfo for the Foo class 这是Java源代码引用 FooBeanInfo 类。 这将关联 CustomNumberEditor 与 年龄 财产的 foo 类。 public class FooBeanInfo extends SimpleBeanInfo { public PropertyDescriptor[] getPropertyDescriptors() { try { final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 111/514 PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Foo.class) { public PropertyEditor createPropertyEditor(Object bean) { return numberPE; }; }; return new PropertyDescriptor[] { ageDescriptor }; } catch (IntrospectionException ex) { throw new Error(ex.toString()); } } } 注册附加自定义 PropertyEditors 当bean属性设置为一个字符串值,一个Spring IoC 集装箱最终使用标准JavaBeans PropertyEditors 将这些字符串的 复杂类型 的属性。 弹簧预注册一个数量的定制 PropertyEditors (例如,将一个 类名表示为一个字符串变成一个真正的 类 对象)。 此 外,Java的标准 JavaBeans 属性编辑器 查找 机制允许一个 属性编辑器 对于一个类 简单地命名为适当地放置在相同的包 类提 供了支持,自动发现。 如果有一个需要注册其他自定义 PropertyEditors 有几种机制 可用。 最手工方式,这是通常不方便 或推荐的,是直接使用 registerCustomEditor() 方法 ConfigurableBeanFactory 界面, 假设你有一个 BeanFactory 参考。 另一个,稍微方便,机制是 使用 特殊bean工厂后处理器称为 CustomEditorConfigurer 。 尽管bean工厂 后处理器可以使用 BeanFactory 的实现, CustomEditorConfigurer 有一个嵌套的属性 设置,所以强烈建议使用它的 ApplicationContext ,它可能是 在类似的方式部署 到任何其他bean,并自动 发现和应用。 请注意,所有bean工厂和应用程序上下文 自动使用大量的内置属性编辑器,通过他们的 使用一种叫 BeanWrapper 办理产权转 换。 标准属性编辑, BeanWrapper 寄存器中列出 前一节 。 此外, ApplicationContexts 还覆盖或 添加一个额外的数量的编辑 处理资源查找的 的方式适当的特定应用程序上下文类型。 标准JavaBeans 属性编辑器 实例被用来转换属性值表示为字符串 实际的复杂类型的属性。 CustomEditorConfigurer ,一个 bean工厂 后处理器,可以用来方便地添加额外的支持 属性编辑器 实例一个 ApplicationContext 。 考虑一个用户类 ExoticType ,和 另一个类 DependsOnExoticType 需要 ExoticType 设置为一个属性: package example; public class ExoticType { private String name; public ExoticType(String name) { this.name = name; } } public class DependsOnExoticType { private ExoticType type; public void setType(ExoticType type) { this.type = type; } } 当事情是适当的设置,我们希望能够分配 类型属性作为一个字符串,它一个 属性编辑器 将在幕后 转换成一个实际的 ExoticType 实例: 这个 属性编辑器 实现 可能类似于: // converts string representation to ExoticType object package example; public class ExoticTypeEditor extends PropertyEditorSupport { public void setAsText(String text) { setValue(new ExoticType(text.toUpperCase())); } } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 112/514 最后,我们使用 CustomEditorConfigurer 到 注册这个新 属性编辑器 与 这个 ApplicationContext ,然后 可以根据需要使用它: 使用 PropertyEditorRegistrars 另一个机制的财产登记的编辑 Spring容器是创建和使用 PropertyEditorRegistrar 。 这 接口是特别有用,当你需要使用相同的 设置 属性编辑器的几种不同情况:写一个 相应的注册和重用,在每种情况下。 PropertyEditorRegistrars 工作结合 一个接口称 为 PropertyEditorRegistry ,一个接口 这是实现的春天 BeanWrapper (和 DataBinder )。 PropertyEditorRegistrars 尤其 当 结合使用方便的 CustomEditorConfigurer (介绍 这里 ),它公开了一个属性,名为 setPropertyEditorRegistrars(. .) : PropertyEditorRegistrars 添加到 CustomEditorConfigurer 在这个时尚可以 容易共享与 DataBinder 和 Spring MVC 控制 器 。 此外, 它避免了需要同步在定制编辑器:一个 PropertyEditorRegistrar 预计 创建新的 属性编辑器 每个bean创建实例尝 试。 使用 PropertyEditorRegistrar 也许是最好的为例进行了阐述。 首先,你需要 创建自己的 PropertyEditorRegistrar 实现: package com.foo.editors.spring; public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar { public void registerCustomEditors(PropertyEditorRegistry registry) { // it is expected that new PropertyEditor instances are created registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor()); // you could register as many custom property editors as are required here... } } 也看见了 org.springframework.beans.support.ResourceEditorRegistrar 例如 PropertyEditorRegistrar 实现。 请注意,在 其实现的 registerCustomEditors(. .) 方法它创建 每个属性编辑器的新实例。 接下来我们配置一个 CustomEditorConfigurer 和注入一个实例 我们的 CustomPropertyEditorRegistrar 进 它: 最后,在一点离开的焦点 一章,对于那些使用 Spring的MVC web 框架 ,使用 PropertyEditorRegistrars 在 结合数据绑定 控制 器 (如 SimpleFormController )可以非常方便的。 找到下面的示例使用一个 PropertyEditorRegistrar 在 实现的一 initBinder(. .) 方法: public final class RegisterUserController extends SimpleFormController { private final PropertyEditorRegistrar customPropertyEditorRegistrar; public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) { this.customPropertyEditorRegistrar = propertyEditorRegistrar; } protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception { this.customPropertyEditorRegistrar.registerCustomEditors(binder); } // other methods to do with registering a User } 这种风格的 属性编辑器 注册可以导致简洁代码(实施 initBinder(. .) 只是一个线长!),然后呢 允许普通 属性编辑器 登记代码封装 在一个类,然后共享 在尽可能多的 控制器 作为 需要。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 113/514 7.5一个弹簧3类型转换 弹簧3引入了一种 核心转换 包, 提供了一个通用类型转换系统。 该系统定义了一个SPI来 执行类型转换逻辑,以及一个API来执 行类型 在运行时转换。 在Spring容器,这个系统可以使用 作为一个替代PropertyEditors将外部化bean属性 值字符串所需的属 性类型。 公共API也可以使用 在您的应用程序的任何地方,类型转换是必要的。 7.5.1A转换器SPI SPI的实现类型转换的逻辑是简单的和强烈 类型: package org.springframework.core.convert.converter; public interface Converter { T convert(S source); } 创建你自己的转换器,只是上面实现接口。 参数化的类型你年代来回转换,和T作为类型 你是皈依。 为每次调用转换(S),源参数 保证不空。 你的转换器可以抛出任何异常 转换失败。 一个IllegalArgumentException应该被扔到报告 一个无效的源值。 照 顾,以确保你的转换器 实现线程安全的。 数转换器的实现提供了 核心转换支持 包作为一个方便。 这些包括转换器从字符串到数字和其他常见的类型。 考虑 StringToInteger 作为一个示例转换器 实现: package org.springframework.core.convert.support; final class StringToInteger implements Converter { public Integer convert(String source) { return Integer.valueOf(source); } } 7.5.2A ConverterFactory 当你需要集中转换逻辑为整个 类层次结构,例如,当从字符串转换 java . lang。 枚举对象,实现 ConverterFactory : package org.springframework.core.convert.converter; public interface ConverterFactory { Converter getConverter(Class targetType); } 参数化年代是要转换的类型从和R 基础类型定义 范围 类你可以 皈依。 然后实现getConverter(类< T >),T是一个 子类的R。 考虑 StringToEnum ConverterFactory 作为一个例子: package org.springframework.core.convert.support; final class StringToEnumConverterFactory implements ConverterFactory { public Converter getConverter(Class targetType) { return new StringToEnumConverter(targetType); } private final class StringToEnumConverter implements Converter { private Class enumType; public StringToEnumConverter(Class enumType) { this.enumType = enumType; } public T convert(String source) { return (T) Enum.valueOf(this.enumType, source.trim()); } } } 7.5.3A GenericConverter 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 114/514 当您需要一个复杂的转换实现,考虑 这个GenericConverter接口。 与一个更灵活但不强烈 类型的签名,一个GenericConverter 支持转换之间的多 源和目标类型。 此外,一个GenericConverter使可用 源和目标字段上下文可以使用在实现你的 转换逻辑。 这种上下文允许一个类型转换来推动 一个字段注释,或泛型信息上声明一个字段 签名。 package org.springframework.core.convert.converter; public interface GenericConverter { public Set getConvertibleTypes(); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); } 实现一个GenericConverter,有getConvertibleTypes()返回 支持的源- >目标类型配对。 然后实现 转换(对 象,TypeDescriptor,TypeDescriptor)来实现你的 转换逻辑。 源TypeDescriptor提供访问 源字段值被转换着。 目标 TypeDescriptor提供访问目标字段转换后 值将被设置。 A good example of a GenericConverter is a converter that converts between a Java Array and a Collection. Such an ArrayToCollectionConverter introspects the field that declares the target Collection type to resolve the Collection's element type. This allows each element in the source array to be converted to the Collection element type before the Collection is set on the target field. Note Because GenericConverter is a more complex SPI interface, only use it when you need it. Favor Converter or ConverterFactory for basic type conversion needs. ConditionalGenericConverter Sometimes you only want a Converter to execute if a specific condition holds true. For example, you might only want to execute a Converter if a specific annotation is present on the target field. Or you might only want to execute a Converter if a specific method, such as static valueOf method, is defined on the target class. ConditionalGenericConverter is an subinterface of GenericConverter that allows you to define such custom matching criteria: public interface ConditionalGenericConverter extends GenericConverter { boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); } A good example of a ConditionalGenericConverter is an EntityConverter that converts between an persistent entity identifier and an entity reference. Such a EntityConverter might only match if the target entity type declares a static finder method e.g. findAccount(Long). You would perform such a finder method check in the implementation of matches(TypeDescriptor, TypeDescriptor). 7.5.4 ConversionService API The ConversionService defines a unified API for executing type conversion logic at runtime. Converters are often executed behind this facade interface: package org.springframework.core.convert; public interface ConversionService { boolean canConvert(Class sourceType, Class targetType); T convert(Object source, Class targetType); boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); } Most ConversionService implementations also implement ConverterRegistry, which provides an SPI for registering converters. Internally, a ConversionService implementation delegates to its registered converters to carry out type 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 115/514 conversion logic. A robust ConversionService implementation is provided in the core.convert.support package. GenericConversionService is the general-purpose implementation suitable for use in most environments. ConversionServiceFactory provides a convenient factory for creating common ConversionService configurations. 7.5.5 Configuring a ConversionService A ConversionService is a stateless object designed to be instantiated at application startup, then shared between multiple threads. In a Spring application, you typically configure a ConversionService instance per Spring container (or ApplicationContext). That ConversionService will be picked up by Spring and then used whenever a type conversion needs to be performed by the framework. You may also inject this ConversionService into any of your beans and invoke it directly. 注意 如果没有ConversionService登记与春天,原来的 基于属性编辑器使用系统。 注册一个默认的ConversionService与春天,添加 以下的bean定义id conversionService : 一个默认的ConversionService之间可以转换字符串、数字、 枚举、集合、地图和其他常见类型。 补充或 覆盖默认的转换器 使用您自己的自定义转换器(s),设置 这个 转换器 财产。 属性值可以实现 这两个转换器,ConverterFactory,或 GenericConverter 接口。 它也是常见的使用ConversionService在Spring MVC 应用程序。 看到 SectionA 7 6 5,一个​​配置格式在春天MVCa​​ 有关使用 < mvc:注解驱动/ > 。 在某些情况下,您可能希望在应用格式 转换。 看到 SectionA 7 6 3,一个​​FormatterRegistry SPIa​​ 对于 细节使用 FormattingConversionServiceFactoryBean 。 7.5.6A使用ConversionService编程方式 工作ConversionService实例编程,简单 注入一个参考就像任何其他bean: @Service public class MyService { @Autowired public MyService(ConversionService conversionService) { this.conversionService = conversionService; } public void doIt() { this.conversionService.convert(...) } } 7.6一个弹簧3字段格式 正如前面讨论的, 核心转换 是一个通用的类型 转换系统。 它提供了一个统一的ConversionService API以及 一个强类型的转换 器SPI实现转换逻辑从一个 类型到另一个。 Spring容器使用这个系统来绑定bean属性 值。 此外,两个弹簧表达式语言(?)和 使 用这个系统来绑定DataBinder字段值。 例如,当? 需要强迫一个 短 一个 长 完成一个 表达式。 setValue(对象豆、对象值) 尝 试,核心。 执行强制转换系统。 现在考虑一个典型的类型转换需求客户 环境,如web和桌面应用程序。 在这样的环境中, 你通常把 从字符串 支持 客户端回传的 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 116/514 过程,以及背 到字符串 到 支持视图渲染过程。 此外,你经常需要 本地化的字符串值。 更普遍 核心转换 转换器SPI不解决这些 格式化 直接要求。 直接地址,弹簧3引入了一个 方便,提供了一种简单的格式化器SPI和健壮的替代 PropertyEditors为客户端环 境。 一般来说,使用转换器SPI当你需要实现 通用类型转换逻辑;例如,对于之间的转换 一个java跑龙套。 日期和和java朗长。 使用 Formatter SPI当你 工作在客户端环境,如一个web应用程序,和需要 解析和打印本地化的字段值。 这个ConversionService提 供了一个 统一的类型转换API对于spi。 7.6.1A格式化器SPI 格式化程序SPI来实现字段格式的逻辑是简单的和 强类型: package org.springframework.format; public interface Formatter extends Printer, Parser { } 在格式化程序扩展从打印机和解析器积木吗 接口: public interface Printer { String print(T fieldValue, Locale locale); } import java.text.ParseException; public interface Parser { T parse(String clientValue, Locale locale) throws ParseException; } 创建您自己的格式器,只需实现格式化程序 上面的接口。 不要被参数化对象的类型,你想 格式,例如, java util日期 。 实现 这个 print() 操作打印一个实例的T 在客户端显示的语言环境。 实现 parse() 操作的实例来解析从T 格式化的表示返回客户端语言环 境。 你 格式化程序应该抛出ParseException或IllegalArgumentException如果一个 解析失败。 照顾,以确保你的格式化程序 实现 是线程安全的。 几个格式化程序实现中提供 格式 子包作为一种便利。 这个 号码 包提供一个NumberFormatter, CurrencyFormatter,PercentFormatter格式化java朗号码 对象使用一个java文本numberformat。 这个 datetime 包提供了 一个DateFormatter格式化 java跑龙套。 日期对象与一个java文本dateformat。 这个 datetime joda 包提供了全面的 datetime格式支持基于 Joda时间图书馆 。 考虑 DateFormatter 作为一个例子 格式化程序 实现: package org.springframework.format.datetime; public final class DateFormatter implements Formatter { private String pattern; public DateFormatter(String pattern) { this.pattern = pattern; } public String print(Date date, Locale locale) { if (date == null) { return ""; } return getDateFormat(locale).format(date); } public Date parse(String formatted, Locale locale) throws ParseException { if (formatted.length() == 0) { return null; } return getDateFormat(locale).parse(formatted); } protected DateFormat getDateFormat(Locale locale) { DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale); dateFormat.setLenient(false); return dateFormat; } } 春季团队欢迎社区驱动的格式化程序的贡献; 看到 http://jira.springframework.org 做出贡献。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 117/514 7.6.2A注解驱动的格式 正如您将看到的,字段格式可以配置的字段类型 或注释。 绑定一个标注格式器,实现 AnnotationFormatterFactory: package org.springframework.format; public interface AnnotationFormatterFactory { Set> getFieldTypes(); Printer getPrinter(A annotation, Class fieldType); Parser getParser(A annotation, Class fieldType); } 参数化一个是场annotationType你想关联 格式化逻辑,例如 org.springframework.format.annotation.DateTimeFormat 。 有 getFieldTypes() 返回类型的字段 注释可用于。 有 getPrinter() 返回一个打印机来打印一个带注释的字段的值。 有 getParser() 返回一个解析器来解析 clientValue为一个带注释的字段。 下面的示例AnnotationFormatterFactory实现绑定 @NumberFormat formatter的注释。 这个注释允许 要么一个号码风格 或模式指定: public final class NumberFormatAnnotationFormatterFactory implements AnnotationFormatterFactory { public Set> getFieldTypes() { return new HashSet>(asList(new Class[] { Short.class, Integer.class, Long.class, Float.class, Double.class, BigDecimal.class, BigInteger.class })); } public Printer getPrinter(NumberFormat annotation, Class fieldType) { return configureFormatterFrom(annotation, fieldType); } public Parser getParser(NumberFormat annotation, Class fieldType) { return configureFormatterFrom(annotation, fieldType); } private Formatter configureFormatterFrom(NumberFormat annotation, Class fieldType) { if (!annotation.pattern().isEmpty()) { return new NumberFormatter(annotation.pattern()); } else { Style style = annotation.style(); if (style == Style.PERCENT) { return new PercentFormatter(); } else if (style == Style.CURRENCY) { return new CurrencyFormatter(); } else { return new NumberFormatter(); } } } } 触发格式化,只是注释字段@NumberFormat: public class MyModel { @NumberFormat(style=Style.CURRENCY) private BigDecimal decimal; } 格式注释API 一个便携式格式注释API中存在 org.springframework.format.annotation 包。 使用java . lang @NumberFormat格式化。 数字领域。 使用 @DateTimeFormat格式化java跑龙套。 日期、java跑龙套的日历, java跑龙套。 长,或Joda时间字段。 下面的例子使用@DateTimeFormat格式化一个java util日期 作为一个ISO日期(yyyy mm dd): public class MyModel { @DateTimeFormat(iso=ISO.DATE) private Date date; } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 118/514 7.6.3A FormatterRegistry SPI 这个是一个注册FormatterRegistry SPI格式化程序和 转换器。 FormattingConversionService 是 一个实现 FormatterRegistry适合大多数环境。 这个实现可以被配置以编程方式或声明 作为一个Spring bean使用 FormattingConversionServiceFactoryBean 。 因为这个实现还实现了 conversionService ,它可以直接 配置为使用Spring的 DataBinder和弹簧表达式 语言(?)。 回顾FormatterRegistry SPI如下: package org.springframework.format; public interface FormatterRegistry extends ConverterRegistry { void addFormatterForFieldType(Class fieldType, Printer printer, Parser parser); void addFormatterForFieldType(Class fieldType, Formatter formatter); void addFormatterForFieldType(Formatter formatter); void addFormatterForAnnotation(AnnotationFormatterFactory factory); } 如上所示,格式化程序可以由fieldType注册或 注释。 这个FormatterRegistry SPI允许您配置格式规则 集中,而不是复制这样的配置在你 控制器。 例如,您可能希望执行,所有日期字 段 格式化一个特定的方式,或字段与特定的注释是吗 在一个特定的方式格式化。 FormatterRegistry共享,您定义 这些规则一旦 和他们应用每当需要格式化。 7.6.4A FormatterRegistrar SPI 这个是一个注册FormatterRegistrar SPI格式化程序和 通过FormatterRegistry转换器: package org.springframework.format; public interface FormatterRegistrar { void registerFormatters(FormatterRegistry registry); } 一个FormatterRegistrar是有用,当注册多个相关 转换器和格式化程序用于给定格式类别,例如日期 格式化。 它也可以是有用 的,声明式登记 不够的。 例如当一个格式化程序需要索引在 特定的字段类型不同于自己的< T >或当注册 打印机/解析器对。 下一节提供了更多的信息 转换器和格式化程序登记。 7.6.5A配置格式在Spring MVC 在一个Spring MVC应用程序中,您可以配置一个自定义的 ConversionService实例明确的属性 注解驱动的 元素的名称空间的 MVC。 这 ConversionService将随时使用一个类型转换是 需要在控制器模型绑定。 如果没有显式配置, Spring MVC将自动 默认格式器和转换器登记 对于常见的类型,例如数字和日期。 依赖默认格式规则,没有自定义配置 需要在你的Spring MVC配置XML: 只有一行字的配置,默认格式化程序用于数字 和日期类型将被安装,包括支持 @NumberFormat和@DateTimeFormat注释。 Joda完全支持 时间格式库也安装如果Joda时间上是否存在 类路径。 注入一个ConversionService实例使用自定义格式器和 转换器注册,设置转换服务属性,然后 指定自定义转换器,格式器,或 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 119/514 FormatterRegistrars作为属性 的FormattingConversionServiceFactoryBean: 注意 看到 SectionA 7 6 4,一个​​FormatterRegistrar SPIa​​ 和 这个 FormattingConversionServiceFactoryBean 更多信息FormatterRegistrars时使用。 7.7一个配置一个全球日期&时间格式 默认情况下,日期和时间字段,不标注 @DateTimeFormat 从字符串转换 使用的 dateformat短 风格。 如果你喜欢, 你可以改变 这个定义你自己的全球格式。 您将需要确保弹簧不注册违约 格式化程序,相反你应该登记所有手动格式化器。 使用 org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar 或 org.springframework.format.datetime.DateFormatterRegistrar 取决于您使用类Joda时间图书馆。 例如,下面的Java配置将注册一个全球 ” yyyyMMdd 的格式。 这个示例并不依赖 Joda时间图书馆: @Configuration public class AppConfig { @Bean public FormattingConversionService conversionService() { // Use the DefaultFormattingConversionService but do not register defaults DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); // Ensure @NumberFormat is still supported conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory()); // Register date conversion with a specific global format DateFormatterRegistrar registrar = new DateFormatterRegistrar(); registrar.setFormatter(new DateFormatter("yyyyMMdd")); registrar.registerFormatters(conversionService); return conversionService; } } 如果你更喜欢基于XML的配置可以使用 FormattingConversionServiceFactoryBean 。 这是相同的 例子,这次使用Joda时间: 注意 Joda时间提供了单独的不同类型来表示 日期 , 时间 和 日期-时间 值。 这个 DateFormatter , timeFormatter 和 dateTimeFormatter 属性的 JodaTimeFormatterRegistrar 应该 被用于配置不同的格 式为每个类型。 这个 DateTimeFormatterFactoryBean 提供了一个 方便地创建格式化器。 如果您正在使用Spring MVC记得显式配置 转换服务使用。 为基于Java @ configuration 这意味着延长 WebMvcConfigurationSupport 类并覆盖 这个 mvcConversionService() 法。 对于XML你应该 使用 ”转换服务的 属性的 mvc:注解驱动的 元素。 看到 SectionA 7 6 5,一个​​配置格式在春天MVCa​​ 详情。 7.8一个弹簧3验证 弹簧3介绍了几种增强其验证支持。 首先,jsr - 303 API Bean验证是现在完全支持。 第二, 以编程方式使用时,弹簧的 DataBinder现在可以验证对象 以及绑定到他们。 第三,Spring MVC现在支持声明 验证输入controller。 7.8.1A概述jsr - 303的Bean验证API jsr - 303规范的验证约束声明和元数据 对于Java平台。 使用该API,您标注了域模型 属性与声明性验证约束和运行时 强制执行 它们。 有很多内置的约束可以采取 利用。 你也可以定义自己的自定义的约束。 为了说明这一点,考虑一个简单的PersonForm模型两个 属性: public class PersonForm { private String name; private int age; } jsr - 303允许您定义声明性验证约束 反对这样的属性: public class PersonForm { @NotNull @Size(max=64) private String name; @Min(0) private int age; } 当这个类的一个实例验证了一个jsr - 303验证器, 这些限制将被强制执行。 对于一般信息关于jsr - 303,请参阅 Bean验证 规范 。 关于具体的功能 默认的参考实现,看到 Hibernate Validator 文档。 学 习如何设置一个jsr - 303实现作为一个 Spring bean,继续阅读。 7.8.2A配置Bean验证实现 Spring提供了完全支持jsr - 303 API Bean验证。 这包括方便的支持引导一个jsr - 303 作为一个Spring bean实现。 这允许一 个 javax.validation.ValidatorFactory 或 javax.validation.Validator 注射无论 验证是必要的在您的应用程序。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 121/514 使用 LocalValidatorFactoryBean 到 配置一个默认的jsr - 303验证器作为一个Spring bean: 上面的基本配置将触发jsr - 303来初始化 使用其默认引导机制。 jsr - 303的供应商,如 Hibernate Validator,预计将出现在类路 径,并将 被自动检测到。 注入一个验证器 LocalValidatorFactoryBean 实现两 javax.validation.ValidatorFactory 和 javax.validation.Validator 以及弹簧的 org.springframework.validation.Validator 。 你可能注入 引用这两种接口到bean,需要 调用验证逻辑。 注入一个参考 javax.validation.Validator 如果 你更喜欢使用jsr - 303 API直接: import javax.validation.Validator; @Service public class MyService { @Autowired private Validator validator; 注入一个参考 org.springframework.validation.Validator 如果您的bean 需要弹簧验证API: import org.springframework.validation.Validator; @Service public class MyService { @Autowired private Validator validator; } 配置自定义约束 每一个jsr - 303验证约束包括两个部分。 首先, 一个@Constraint注释来声明约束及其 可配置特性。 第二,一个实现的 javax.validation.ConstraintValidator 接口, 实现约束的行为。 把一个声明 一个实现,每个@Constraint注释引用 相应的 ValidationConstraint实现类。 在运行时,一个 ConstraintValidatorFactory 实例化所引用的 实现当约束注释中遇到你 域模 型。 默认情况下, LocalValidatorFactoryBean 配置 SpringConstraintValidatorFactory 使用 Spring创建ConstraintValidator实 例。 这允许你 自定义ConstraintValidators受益于依赖注入像 任何其他Spring bean。 下面是一个例子,一个自定义@Constraint宣言, 后跟一个相关的 ConstraintValidator 实现,使用弹簧用于依赖注入: @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy=MyConstraintValidator.class) public @interface MyConstraint { } import javax.validation.ConstraintValidator; public class MyConstraintValidator implements ConstraintValidator { @Autowired; private Foo aDependency; ... } 正如您可以看到的,一个ConstraintValidator实现可能有其 依赖性的@ autowired像任何其他Spring bean。 额外的配置选项 默认 LocalValidatorFactoryBean 配置应该证明足够的大多数情况下。 有一个 数量的其他配置选项不同的jsr - 303结构, 从信 息插值到遍历解析。 看到JavaDocs 的 LocalValidatorFactoryBean 更多 这些选项的信息。 7.8.3A DataBinder配置一个 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 122/514 因为春天3,DataBinder实例都可以配置 验证器。 一旦配置完成,验证器可以被调用 粘合剂validate() 。 任何验证错误自动 添 加到活页夹BindingResult。 当使用DataBinder编程方式,这可以利用 调用验证逻辑绑定后向目标对象: Foo target = new Foo(); DataBinder binder = new DataBinder(target); binder.setValidator(new FooValidator()); // bind to the target object binder.bind(propertyValues); // validate the target object binder.validate(); // get BindingResult that includes any validation errors BindingResult results = binder.getBindingResult(); 一个DataBinder也可以配置多个 验证器 实例 通过 dataBinder.addValidators 和 dataBinder.replaceValidators 。 这是有 用的在jsr - 303结合全球配置Bean验证 用弹簧 验证器 配置 在本地一个DataBinder实例。 看到 一个​章节​配置一个验证器使用 的弹簧MVCa​​ 。 7.8.4A Spring MVC 3验证 从弹簧3,Spring MVC有能力 自动验证输入controller。 在之前的版本中,这是 开发人员手动调用验证逻辑。 触发controller输入验证 触发验证一个controller输入,只需标注 @Valid输入参数为: @Controller public class MyController { @RequestMapping("/foo", method=RequestMethod.POST) public void processFoo(@Valid Foo foo) { /* ... */ } Spring MVC将验证@Valid对象绑定后这么长时间为 一个适当的验证器被配置。 注意 @Valid的注释部分的标准jsr - 303豆 验证API,并不是一个spring特定构造。 配置一个验证器使用Spring MVC Validator实例时调用一个方法@Valid论点 遇到可能以两种方式配置。 首先,你可以叫 binder.setValidator(Validator)内的 @InitBinder controller 回调。 这允许您配置一个验证器实例/ controller类: @Controller public class MyController { @InitBinder protected void initBinder(WebDataBinder binder) { binder.setValidator(new FooValidator()); } @RequestMapping("/foo", method=RequestMethod.POST) public void processFoo(@Valid Foo foo) { ... } } 第二,你可以叫setValidator(Validator)在全球 WebBindingInitializer。 这允许您配置一个验证器 在所有@Controllers实例。 这可以实现很容易使用 Spring MVC的命名空间: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 123/514 结合全球和当地的一个验证器,配置 全球验证程序如上所示,然后添加一个本地验证器: @Controller public class MyController { @InitBinder protected void initBinder(WebDataBinder binder) { binder.addValidators(new FooValidator()); } } 配置一个jsr - 303验证器使用Spring MVC 与jsr - 303,一个单一的 javax.validation.Validator 实例通常验证 所有 模型对象 这声明验证约束。 配置一个jsr - 303支持 验 证器与Spring MVC,只需添加一个jsr - 303提供者,如 Hibernate Validator,到您的类路径中。 Spring MVC将检测它, 自动启 用jsr - 303支持在所有控制器。 Spring MVC的配置才能使jsr - 303的支持 如下所示: 在这种最小配置,任何时候@Valid controller 输入时,它将被验证了jsr - 303提供者。 jsr - 303,反过来,将执行任何约束声明反 对了 输入。 任何ConstraintViolations将自动被公开为 BindingResult可渲染的错误标准Spring MVC形式 标签。 8。 一个春天的表达式语言(?) 8.1一个介绍 春天表达式语言(简称?)是一个强大的 表达式语言,支持查询和操作一个对象 图在运行时。 语言的语法类似于统一EL但提供 额 外的功能,最明显的是方法调用和基本的字符串 模板的功能。 虽然有几个其他的Java表达式语言可用, OGNL,MVEL,JBoss EL等等,春天表达式语言 被建立提供春季社区提供单井支持吗 表 达式语言,可以使用所有产品在春天 投资组合。 它是由语言特性的要求 项目在春天的投资组合,包括工具要求代码 完成基于 eclipse的党内支持SpringSource工具套件。 这 说,?是基于技术无关的API允许其他 表达式语言实现集成应该需要 出现。 而作为基础?表达式求值的在 弹簧组合,它不直接绑定到春天和可用 独立。 为了是自包含的,许多例子在这 章使用?就好像它是 一个独立的表达式语言。 这 需要创建一些引导基础设施类等 解析器。 大多数Spring用户不需要处理这个基础设施 只有作者 表达,而是将字符串评价。 一个例子 这种典型的使用是集成到创建的XML或? 基于注释的bean定义的部分所示 表达式支持定 义bean 定义。 本章涵盖的特征表达式语言,它的 API,它的语言的语法。 在几个地方一个发明家,发明家的 社会阶级是作为目标对象表达式求值 的。 这些类声明和使用的数据填充它们列在 一章的结束。 8.2一个特性概述 表达式语言支持以下功能 字面表达式 布尔和关系运算符 正则表达式 类表达式 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 124/514 访问属性、数组、列表、地图 方法调用 关系运算符 分配 调用构造函数 Bean引用 阵列结构 内联列表 三元操作符 变量 用户定义函数 收集投影 集合选择 模板化的表达式 8.3一个表达式求值的表达式使用Spring接口 本节介绍了简单的使用接口和它? 表达式语言。 完整的语言参考中可以找到 部分 语言 参考 。 下面的代码介绍了API来评估文字? “你好,世界”的字符串表达式。 ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("'Hello World'"); String message = (String) exp.getValue(); 的价值 消息变量只是“你好,世界”。 类和接口的?你是最有可能使用 位于包 org.springframework.expression 和它的子包和 ?支持 。 接口 ExpressionParser 是 负责解析表达式的字符串。 在这个例子中, 表达式的字符串是一个字符串,表示为周围的单 引用。 接口 表达式 是 负责评估前面定义的表达式字符串。 有 是两个例外,可以扔, ParseException 和 EvaluationException 当调用 ” parser.parseExpression ”和 ” exp getvalue 分别的。 ?支持广泛的特性,比如调用方法, 访问属性、调用构造函数。 作为一个例子的方法调用,我们称之为“concat的方法 字符串文字。 ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("'Hello World'.concat('!')"); String message = (String) exp.getValue(); 信息的价值是现在“Hello World !”。 作为一个例子,调用JavaBean属性的字符串属性 “字节”可以称为如下所示。 ExpressionParser parser = new SpelExpressionParser(); // invokes 'getBytes()' Expression exp = parser.parseExpression("'Hello World'.bytes"); byte[] bytes = (byte[]) exp.getValue(); 嵌套属性?还支持使用标准的“点点”符号, 即prop1.prop2。 prop3和设置属性值 公共字段也可能被访问。 ExpressionParser parser = new SpelExpressionParser(); // invokes 'getBytes().length' Expression exp = parser.parseExpression("'Hello World'.bytes.length"); int length = (Integer) exp.getValue(); 字符串的构造函数可以调用而不是使用一个字符串 文字。 ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 125/514 String message = exp.getValue(String.class); 注意这里使用的通用方法 公共< T > T getValue(类< T > desiredResultType) 。 使用这种方法 删除需要铸造表达式的值到 预期的结果 类型。 一个 EvaluationException 将会抛出如果 价值不能抛到类型 T 或转换使用 注册的类型转换器。 更常见的用法是提供一个表达式?字符串 是针对特定对象实例评估(称为根对象)。 这里有两个选项,选择哪一个取决于对象 对表 达式进行评估将改变每个 调用表达式求值。 在接下来的例子中 我们检索 名称 产权的一个实例 发明家类。 // Create and set a calendar GregorianCalendar c = new GregorianCalendar(); c.set(1856, 7, 9); // The constructor arguments are name, birthday, and nationality. Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian"); ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("name"); EvaluationContext context = new StandardEvaluationContext(tesla); String name = (String) exp.getValue(context); 在过去的 线,这个值的字符串变量的名字将被设置为“尼古拉 特斯拉”。 类StandardEvaluationContext就是你可以指定 对 象的“name”属性将依据。 这是机制 使用如果根对象是不可能改变的,它可以简单地设置一次 在评估上下文。 如果根对象可 能会改变 多次,它可以提供在每次调用 getValue , 作为下一个示例显示: / Create and set a calendar GregorianCalendar c = new GregorianCalendar(); c.set(1856, 7, 9); // The constructor arguments are name, birthday, and nationality. Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian"); ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("name"); String name = (String) exp.getValue(tesla); 在这种情况下,发明家 特斯拉 一直 直接供给 getValue 和表达 评估基础设施创建和管理一个默认评价上下文 在内部,它不需要 一个提供。 StandardEvaluationContext相对昂贵的构造和 在重复使用它建立缓存状态,使后续 表达评估执行更迅速。 因为这个原因,它 是 更好的缓存和重用他们在可能的情况下,而不是构建一个新的 一个用于每个表达式求值。 在某些情况下,它可以理想的使用配置评估上下文和 但仍然提供一个不同的根对象在每次调用 getValue 。 getValue 允许两个 上指定相同的调用。 在这些情况下根对象通过调用被覆盖 任何(可能为空)指定的评估上下文。 注意 在独立使用?有必要创建解析器, 解析表达式和也许提供评估上下文和根 上下文对象。 然而,更常见的用法 是提供只有?表达式字符串的一部分吗 配置文件,例如Spring bean或Spring Web Flow 定义。 在这种情况 下,解析器、评估上下文,根对象 和任何预定义的变量都是建立隐式,要求 用户指定任何其他比表达式。 最后介绍的例子,使用布尔运算符 显示使用发明家对象在前面的例子。 Expression exp = parser.parseExpression("name == 'Nikola Tesla'"); boolean result = exp.getValue(context, Boolean.class); // evaluates to true 8.3.1A EvaluationContext接口的 接口 EvaluationContext 是 使用在评估一个表达式来解析属性、方法 字段,并帮助执行类型转换。 开箱即用 的实现, StandardEvaluationContext ,使用 反射来操纵对象,缓存 java朗反映 ' s 方法 , 领域 ,和 构造函数 为提高性能的实例。 这个 StandardEvaluationContext 就是你 可以指定根对象评估反对通过方法 setRootObject() 或通过根对象 构造函数。 您 还可以指定变量和函数 将用于表达式使用方法吗 setVariable() 和 registerFunction() 。 使用变量和 函数是描述在语言参考 部分 变量 和 功能 。 这个 StandardEvaluationContext 也是,你可以吗 注册自定义 ConstructorResolver 年代, MethodResolver 年代, PropertyAccessor 年代延长?如何评估 表达式。 请参考这些类的JavaDoc更多 细节。 类型转换 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 126/514 默认情况下?使用转换服务可以在春天 核心 ( org.springframework.core.convert.ConversionService )。 这个转换服务有许 多转换器建在为常见 转换但也完全可扩展的如此定义之间的转换 类型可以被添加。 另外它有关键的能力,它是 仿制药知道。 这意味着在使用中泛型类型 表情,?会尝试转换维护类型 遇到的任何对象的正确性。 在实践中这意味着什么? 假设任务,使用 setValue() ,是被用来设置一个 列表 财产。 属性的类型实际上是 列表<布尔> 。 会认 识到,? 元素列表需要转换 布尔 在被放置在它。 一个简单的 示例: class Simple { public List booleanList = new ArrayList(); } Simple simple = new Simple(); simple.booleanList.add(true); StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple); // false is passed in here as a string. SpEL and the conversion service will // correctly recognize that it needs to be a Boolean and convert it parser.parseExpression("booleanList[0]").setValue(simpleContext, "false"); // b will be false Boolean b = simple.booleanList.get(0); 8.4一个表达式支持定义bean定义 ?表情可以使用XML或基于注解的 配置元数据定义BeanDefinitions。 在这两种情况下, 语法定义表达式的形式 # { <表达式 字 符串> } 。 8.4.1A基于XML的配置 一个属性或constructor - arg值可以设置使用表达式 如下所示 变量“systemProperties’是预定义的,所以你可以使用它 在你的表达式如下所示。 注意,您不需要前缀 预定义的变量 用“#”符号在这个上下文。 你也可以参考其他bean属性的名字, 的例子。 8.4.2A基于注解的配置 这个 这个元素包含一个@ value 注释可以放在字段, 方法和构造函数参数的方法/指定一个默认的 值。 这里有一个例子来设置默认字段值 变量。 public static class FieldValueTestBean @Value("#{ systemProperties['user.region'] }") private String defaultLocale; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 127/514 public void setDefaultLocale(String defaultLocale) { this.defaultLocale = defaultLocale; } public String getDefaultLocale() { return this.defaultLocale; } } 但在一个相当于属性setter方法显示 下面。 public static class PropertyValueTestBean private String defaultLocale; @Value("#{ systemProperties['user.region'] }") public void setDefaultLocale(String defaultLocale) { this.defaultLocale = defaultLocale; } public String getDefaultLocale() { return this.defaultLocale; } } Autowired的方法和构造函数也可以使用 这个元素包含一个@ value 注释。 public class SimpleMovieLister { private MovieFinder movieFinder; private String defaultLocale; @Autowired public void configure(MovieFinder movieFinder, @Value("#{ systemProperties['user.region'] }"} String defaultLocale) { this.movieFinder = movieFinder; this.defaultLocale = defaultLocale; } // ... } public class MovieRecommender { private String defaultLocale; private CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao, @Value("#{systemProperties['user.country']}"} String defaultLocale) { this.customerPreferenceDao = customerPreferenceDao; this.defaultLocale = defaultLocale; } // ... } 8.5语言参考 8.5.1A字面表达式 这个类型的文字表达式支持都是字符串,日期, 数值(int,真实,和十六进制),布尔和null。 字符串是 由单引号分隔。 将单引号本身 在一个字符串使用 两个单引号字符。 下面的清单显示了简单的使用 文字。 通常他们不会被用于隔离,但 作为一个更复杂的表 达式,例如使用文字在一个 边的一个逻辑比较运算符。 ExpressionParser parser = new SpelExpressionParser(); // evals to "Hello World" String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue(); // evals to 2147483647 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 128/514 int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); boolean trueValue = (Boolean) parser.parseExpression("true").getValue(); Object nullValue = parser.parseExpression("null").getValue(); 数据支持使用负号,指数 符号,小数点。 默认情况下真正的数字解析使用 Double.parseDouble()。 8.5.2A属性、数组、列表、地图、索引器 导航与产权引用很容易,只需使用一个时期 显示一个嵌套的属性值。 类的实例的发明家,加感 和特斯拉,来填充数据列在部分 类 用于 例子 。 导航“下”,得到特斯拉的出生年份和 加感的出生的城市下面的表达式是使用。 // evals to 1856 int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context); 不区分大小写是允许第一个字母的财产 的名字。 数组和列表的内容得到了使用方 括号表示法。 ExpressionParser parser = new SpelExpressionParser(); // Inventions Array StandardEvaluationContext teslaContext = new StandardEvaluationContext(tesla); // evaluates to "Induction motor" String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class); // Members List StandardEvaluationContext societyContext = new StandardEvaluationContext(ieee); // evaluates to "Nikola Tesla" String name = parser.parseExpression("Members[0].Name").getValue(societyContext, String.class); // List and Array navigation // evaluates to "Wireless communication" String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext, String.class); 地图的内容得到了指定文字的关键 括号内的值。 在这种情况下,因为钥匙的人员 地图都是字符串,我们可以指定字符串字面 值。 // Officer's Dictionary Inventor pupin = parser.parseExpression("Officers['president']").getValue(societyContext, Inventor.class); // evaluates to "Idvor" String city = parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(societyContext, String.class); // setting values parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, "Croatia"); 8.5.3A内联列表 列表可以直接表达的表达式中使用{ }符号。 // evaluates to a Java list containing the four numbers List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context); List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context); { }本身意味着一个空列表。 由于性能原因,如果 列表本身是完全由固定文字然后创建一个常数列表 代表表达式,而不是建立一个 新的列表中每一个评价。 8.5.4A阵列结构 数组可以使用熟悉的Java语法,可选地 提供一个初始化的数组填充在施工时间。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 129/514 int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context); // Array with initializer int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context); // Multi dimensional array int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context); 目前还不允许提供一个初始化器在构造 一个多维数组。 8.5.5A方法 方法调用使用典型的Java编程语法。 你可能 还在文字调用方法。 也支持可变参数。 // string literal, evaluates to "bc" String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class); // evaluates to true boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class); 8.5.6A运营商 关系运算符 关系运算符;平等,不平等,小于,小于 或等于、大于、大于或等于支持使用 标准算子符号。 // evaluates to true boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class); // evaluates to false boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class); // evaluates to true boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class); 除了标准的关系运算符?支持 “运算符”和基于正则表达式的匹配操作符。 // evaluates to false boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class); // evaluates to true boolean trueValue = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); //evaluates to false boolean falseValue = parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); 每个符号操作符也可以被指定为一个纯粹的字母等效。 这就避免了 问题,有特殊含义的符号使用的文档类型中 表达式是嵌入式 (如。 一个XML文档)。 显示文本等价物 在这里:lt(“<”),gt(' > '),勒(“< =”)、通用电气(“> =”), eq(' = = ')、ne(“! =”),div(“/”)、国防部(“%”),而不是(' !”)。 这些都是不区分大小写的。 逻辑运算符 逻辑运算符,支持和,或者,不。 他们使用了下面。 // -- AND -- // evaluates to false boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class); // evaluates to true String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"; boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); // -- OR -- // evaluates to true boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class); // evaluates to true String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')"; boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); // -- NOT -- 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 130/514 // evaluates to false boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class); // -- AND and NOT -- String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"; boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); 数学运算符 加法操作符可以用在数字、字符串和日期。 减法可以用在数字和日期。 乘法和 部门只能使用在数字。 其他数学运算符 支持 模量(%)和指数功率(^)。 标准算子 优先执行。 下面演示了这些操作符。 // Addition int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 String testString = parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string' // Subtraction int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 // Multiplication int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 // Division int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 // Modulus int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 // Operator precedence int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21 8.5.7A赋值 设置的属性是通过使用赋值运算符。 这通常是一个调用中完成的 setValue 但也可以做在一个调用 getValue 。 Inventor inventor = new Inventor(); StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor); parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2"); // alternatively String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inventorContext, String.class); 8.5.8A类型 特殊的“T”操作符可以用来指定的一个实例 java . lang。 类(“类型”)。 静态方法调用使用这个 运营商也。 这个 StandardEvaluationContext 使用 TypeLocator 找到类型和 StandardTypeLocator (可替换) 建造一个理解java。 朗包。 这 意味着T() 引用类型在java。 朗不需要完全限定的, 但所有其他类型引用必须。 Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); Class stringClass = parser.parseExpression("T(String)").getValue(Class.class); boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") .getValue(Boolean.class); 8.5.9A构造函数 构造函数可以调用使用新的操作符。 完全 限定类名应该用于所有但原始类型和 字符串(整数、浮点等,可以用)。 Inventor einstein = p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')") 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 131/514 .getValue(Inventor.class); //create new inventor instance within add method of List p.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))") .getValue(societyContext); 8.5.10A变量 变量可以引用表达式使用语法 # variableName。 变量是设置使用方法setVariable在 StandardEvaluationContext。 Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); StandardEvaluationContext context = new StandardEvaluationContext(tesla); context.setVariable("newName", "Mike Tesla"); parser.parseExpression("Name = #newName").getValue(context); System.out.println(tesla.getName()) // "Mike Tesla" #这和# root变量 变量#这是总是定义和引用当前 评价对象(对不合格的参考解决)。 变量#根总是定义,指根 上下文对象。 虽然#这可能不同的表 达式作为组件 评价,#根总是指根。 // create an array of integers List primes = new ArrayList(); primes.addAll(Arrays.asList(2,3,5,7,11,13,17)); // create parser and set variable 'primes' as the array of integers ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("primes",primes); // all prime numbers > 10 from the list (using selection ?{...}) // evaluates to [11, 13, 17] List primesGreaterThanTen = (List) parser.parseExpression("#primes.?[#this>10]").getValue(context); 8.5.11A功能 您可以扩展通过注册用户定义函数?可以 被称为在表达式的字符串。 这个功能是注册 这个 StandardEvaluationContext 使用 法。 public void registerFunction(String name, Method m) 引用的Java方法提供实现的 函数。 例如,一个实用程序方法来扭转一个字符串显示 下面。 public abstract class StringUtils { public static String reverseString(String input) { StringBuilder backwards = new StringBuilder(); for (int i = 0; i < input.length(); i++) backwards.append(input.charAt(input.length() - 1 - i)); } return backwards.toString(); } } 该方法然后注册评估上下文和可以 被使用在一个表达式的字符串。 ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.registerFunction("reverseString", StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class })); String helloWorldReversed = parser.parseExpression("#reverseString('hello')").getValue(context, String.class); 8.5.12A Bean引用 如果评估上下文已经配置了一个bean解析器是有可能的 查找bean从一个表达式使用符号(@)。 ExpressionParser parser = new SpelExpressionParser(); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 132/514 StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new MyBeanResolver()); // This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation Object bean = parser.parseExpression("@foo").getValue(context); 8.5.13A三元操作符(是以if - then - else) 您可以使用三元操作符用于执行是以if - then - else 条件逻辑表达式内。 一个最小的例子是: String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class); 在这种情况下,逻辑错误导致返回字符串 价值”falseExp’。 一个更实际的例子如下所示。 parser.parseExpression("Name").setValue(societyContext, "IEEE"); societyContext.setVariable("queryName", "Nikola Tesla"); expression = "isMember(#queryName)? #queryName + ' is a member of the ' " + "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'"; String queryResultString = parser.parseExpression(expression).getValue(societyContext, String.class); // queryResultString = "Nikola Tesla is a member of the IEEE Society" 也见下一节在猫王运营商一个更 短三目操作符的语法。 8.5.14A猫王算子 猫王运营商是一个缩短的三元操作符的语法 和用于 groovy 语言。 与三元操作符语法通常你需要重复 变量两次,例如: String name = "Elvis Presley"; String displayName = name != null ? name : "Unknown"; 相反,你可以使用猫王算子,命名为相似 到埃尔维斯的发型。 ExpressionParser parser = new SpelExpressionParser(); String name = parser.parseExpression("null?:'Unknown'").getValue(String.class); System.out.println(name); // 'Unknown' 这是一个更复杂的例子。 ExpressionParser parser = new SpelExpressionParser(); Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); StandardEvaluationContext context = new StandardEvaluationContext(tesla); String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class); System.out.println(name); // Mike Tesla tesla.setName(null); name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class); System.out.println(name); // Elvis Presley 8.5.15A安全导航操作符 安全的导航操作符是用来避免 NullPointerException 和来自 groovy 语言。 通常当你有对象的引用你可能 需要确认它不是空 之前访问方法或 对象的属性。 为了避免这种情况,安全导航操作符 只会返回null而不是抛出异常。 ExpressionParser parser = new SpelExpressionParser(); Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan")); StandardEvaluationContext context = new StandardEvaluationContext(tesla); String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class); System.out.println(city); // Smiljan tesla.setPlaceOfBirth(null); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 133/514 city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class); System.out.println(city); // null - does not throw NullPointerException!!! 注意 猫王算子可以用于应用默认值 表达式,例如在一个 这个元素包含一个@ value 表达式: @Value("#{systemProperties['pop3.port'] ?: 25}") 这将注入系统属性 pop3港口 如果它 如果不定义或25。 8.5.16A集选择 选择是一个强大的表达式语言功能,允许你 改变一些源集合到另一个通过选择从 条目。 选择使用语法 ?[selectionExpression] 。 这将过滤 收集和返回一个新的集合包含的一个子集 原始元素。 例如,选择将使我们 能够很容易地得到一个 塞尔维亚发明家的列表: List list = (List) parser.parseExpression("Members.?[Nationality == 'Serbian']").getValue(societyContext); 选择是可能的在两个列表和地图。 在前一种情况下 选择标准是依据每个列表元素 同时对地图的选择标准是依据每个 映射项 (对象的Java类型 . entry )。 地图 条目有他们的键和值可以作为属性的使用 选择。 这个表达式将返回一个新的地图构成的元素 原来的地图,输入值小于27。 Map newMap = parser.parseExpression("map.?[value<27]").getValue(); 除了返回所有选中的元素,它是可能的 检索只是第一个或最后一个值。 获得第一个条目 匹配选择语法 ^[…] 同时, 获得最后的 匹配选择语法 $[…] 。 8.5.17A收集投影 投影允许一个集合来驱动的评估 子表达式,其结果是一个新的集合。 的语法 投影是 ![projectionExpression] 。 最容易 理解的 例子,假设我们有一个列表的发明者,但希望 城市中出生。 实际上我们需要评估 “placeOfBirth。 城市的每一个进入的发明家 列表。 使用 投影: // returns [ 'Smiljan', 'Idvor' ] List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]"); 地图也可以用来驱动投影和在这种情况下 投影计算表达式对每个条目在地图 (表示为一个Java . entry )。 的结果 投影在地图 上是一个列表组成的评价 对每个地图投影表达条目。 8.5.18A表达式模板 表达式模板允许一个混合的字面文本与一个或 更多的评估模块。 每个评估块分隔与前缀 和后缀字符,您可以定义,一个共同的 选择是使用 # { } 为分隔符。 例如, String randomPhrase = parser.parseExpression("random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue(String.class); // evaluates to "random number is 0.7038186818312008" 字符串是评估通过连接文字文本的随机 数字与计算表达式的结果在# { } 分隔符,在这种情况下的结果,随机()方法调用。 这个 第 二个参数的方法 parseExpression() 是 类型 ParserContext 。 这个 ParserContext 接口用于 影响表达式解析为支持 表达式 模板功能。 的定义 TemplateParserContext 如下所示。 public class TemplateParserContext implements ParserContext { public String getExpressionPrefix() { return "#{"; } public String getExpressionSuffix() { return "}"; } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 134/514 public boolean isTemplate() { return true; } } 8.6的例子中使用的类 发明家java package org.spring.samples.spel.inventor; import java.util.Date; import java.util.GregorianCalendar; public class Inventor { private String name; private String nationality; private String[] inventions; private Date birthdate; private PlaceOfBirth placeOfBirth; public Inventor(String name, String nationality) { GregorianCalendar c= new GregorianCalendar(); this.name = name; this.nationality = nationality; this.birthdate = c.getTime(); } public Inventor(String name, Date birthdate, String nationality) { this.name = name; this.nationality = nationality; this.birthdate = birthdate; } public Inventor() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNationality() { return nationality; } public void setNationality(String nationality) { this.nationality = nationality; } public Date getBirthdate() { return birthdate; } public void setBirthdate(Date birthdate) { this.birthdate = birthdate; } public PlaceOfBirth getPlaceOfBirth() { return placeOfBirth; } public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) { this.placeOfBirth = placeOfBirth; } public void setInventions(String[] inventions) { this.inventions = inventions; } public String[] getInventions() { return inventions; } } PlaceOfBirth.java package org.spring.samples.spel.inventor; public class PlaceOfBirth { private String city; private String country; public PlaceOfBirth(String city) { this.city=city; } public PlaceOfBirth(String city, String country) { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 135/514 this(city); this.country = country; } public String getCity() { return city; } public void setCity(String s) { this.city = s; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } } 社会java package org.spring.samples.spel.inventor; import java.util.*; public class Society { private String name; public static String Advisors = "advisors"; public static String President = "president"; private List members = new ArrayList(); private Map officers = new HashMap(); public List getMembers() { return members; } public Map getOfficers() { return officers; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isMember(String name) { boolean found = false; for (Inventor inventor : members) { if (inventor.getName().equals(name)) { found = true; break; } } return found; } } 9。 一个面向方面的编程与弹簧 9.1一个介绍 面向方面编程 (AOP)补充 面向对象编程(OOP)通过提供另一种思维方式 关于程序结构。 关键单元的模块化在OOP是类, 而在 AOP单元模块化是 方面 。 方面使模块化的问题如事务 管理,跨越多个类型和对象。 (这种忧虑是 通常称为 横切 担忧在AOP 文学)。 的关键部件之一是春天了 aop 框架 。 虽然Spring IoC容器不依赖 AOP,这意味着您不需要使用AOP如果你不想,AOP 补充了 Spring IoC提供一个非常能干的中间件 解决方案。 AOP是用于Spring框架来…… 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 136/514 Spring 2.0 AOP Spring 2.0引入了一个更简单和更强大的写作方 式 或者使用一个定制方面 基于 方法 或 @ aspectj注释 风格 。 这两种风格提供完全类型 建议和使用 AspectJ切入点语言,同时仍然使用 Spring AOP进行 编织。 Spring 2.0模式,讨论了基于@ aspectj AOP支 持 在这一章。 Spring 2.0 AOP仍然完全向后兼 容 Spring 1.2 AOP,低层AOP支持所提供的弹簧 1.2 api了 以下 章 。 … 提供声明性企业服务,特别是作为一个 替代EJB声明式服务。 最重要 的 服务是 声明式事务 管理 。 … 允许用户实现自定义方面,以补充他们的 使用OOP和AOP。 如果你有兴趣只有在通用的声明式服务 或其他预包装的声明式中间件服务 如池,你 不需要直接处理Spring AOP,可以跳过大部分的吗 一章。 9.1.1A AOP概念 让我们首先定义一些中央AOP概念和 术语。 这些条款不是spring特定… 不幸的是,AOP 术语并不是非常直观的;然而,它甚至会 更令人困惑的如果弹 簧使用自己的术语。 方面 :一个模块化的担忧 跨越多个类。 事务管理是一个很好的 的例子在企业Java应用程序的横切关注点。 在Spring AOP,方面使用常规类( 基于方法 )或定期 类注释与 @Aspect 注释( @ aspectj 风格 )。 连接点 :一个点在执行 的一个程序,如执行一个方法或处理 一个例外。 在Spring AOP,连接点 总是 代表一个方法执行。 建议 :由一个方面采取行动在一个 特定的连接点。 不同类型的建议包括“约”, “之前”和“之后”的建议。 (建议类型下 面讨论)。 许多的AOP框架,包括弹簧、模型一个建议 拦截器 ,维护一个链的 拦截器 周围 连接点。 切入点 :一个谓词匹配连接 点。 的建议是关联到一个切入点表达式和运行在 任何连接点的切入点匹配(例如,执行 的一个方 法和一个特定的名称)。 连接点的概念作为 匹配切入点表达式是AOP的核心,弹簧使用 AspectJ切入点表达式语言默认情况 下。 介绍 :声明额外 方法或字段代表一个类型。 Spring AOP允许你 引入新的接口(以及相应的实现)任何 建议对象。 例如,您可 以使用一个介绍做一个 bean实现一个 IsModified 接口,简化缓存。 (介绍被称为 类型间声明在AspectJ社区。) 目标对象 :对象的咨询顾问 一个或多个方面。 也被称为 建议 对象。 因为Spring AOP实现 使用运行时代理,这个对象将永 远是一个 代理 对象。 AOP代理 :一个对象创建的AOP 框架为实现方面合同(建议方法 死刑等等)。 在Spring框架,AOP代理将 一个JDK动态代理 或CGLIB代理。 编织 :链接方面与其他 应用程序类型或对象来创建一个建议的对象。 这可以 在编译时完成(使用AspectJ编译器为例), 加载 时间,或在运行时。 Spring AOP,像其他纯Java AOP 框架,在运行时执行编织。 类型的建议: 建议之前 :建议执行 在一个连接点,但没有能力阻止 执行流继续连接点(除非它抛出一个 例外)。 回国后的建议 :建议是 完成后执行连接点通常:例如,如果一个 方法返回没有抛出异常。 在投掷的建议 :建议是 如果一个方法执行退出通过抛出异常。 (最后)后建议 :建议是 执行无论手段连接点退出(正常 或异常返回)。 Around通知 :建议包围 连接点比如方法调用。 这是最强大的 种建议。 在建议可以执行自定义行为之前和 在方法调用。 它还负责选择 是否继续进行连接点或快捷方式的建议 方法执行通过返回自己的返回值或抛出 例外。 约的建议是最一般类型的建议。 因为春天 AOP,像AspectJ,提供全方位的建议类型,我们建议 那你用最强大的建议类型,可以实 现 所需的行为。 例如,如果您只需要更新一个缓存与 一个方法的返回值,因此你最好实现之后 返回的建议比周围的建议,尽管 around通知可以 完成同样的事情。 使用最具体的类型提供建议 一个简单的编程模型和更少的潜在错误。 例如, 你不需要调用 proceed() 方法 在 连接点 用于在建议, 因而不能失败来调用它。 在Spring 2.0中,所有的建议都是静态类型的参数,这样 和你一起工作的建议参数适当的类型(类型 返回值的方法执行例如)而不 是 对象 数组。 连接点的概念,与切入点,是关键 AOP有别于旧技术只提供 拦截。 切入点使建议是独立的目标 面向对象的层次结构。 例如,一 个在建议提供 声明式事务管理可以应用于一组方法 跨越多个对象(如所有业务操作 服务层)。 9.1.2A Spring AOP功能和目标 Spring AOP是用纯Java实现。 这里不需要 特殊的编译过程。 Spring AOP不需要控制 类加载器层次结构,因此是适合用于 Servlet 容器或应用程序服务器。 目前只支持Spring AOP方法执行连接点 (建议执行方法的Spring bean)。 场拦截 还没有实现,尽管支持场拦截可以吗 添加不打 破Spring AOP的核心api。 如果你需要建议 字段访问和更新的连接点,考虑之类的语言 AspectJ。 Spring AOP的AOP方法不同于大多数其他AOP 框架。 我们的目标不是要提供最完整的AOP 实现(虽然Spring AOP相当有能 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 137/514 力);而是去 提供一个紧密集成Spring AOP实现之间和奥委会 帮助解决常见的问题在企业应用程序。 因此,例如,Spring框架的AOP功能 通常结合使用Spring IoC容器。 方面 配置使用正常的bean定义语法(虽然这允许 强大 的“自动代理”功能):这是一个关键的区别 其他的AOP实现。 有些事情你不能轻易或 有效地与Spring AOP,如建议非常细粒度 的对象 (如域对象通常):AspectJ是最好的选择在这样的 情况下。 然而,我们的经验是,Spring AOP提供了一个优秀的 解决大多 数问题,企业Java应用程序 服从AOP。 Spring AOP永远努力竞争,AspectJ提供 AOP解决方案。综合 我们相信这两个基于代理的框架 像Spring AOP和成熟的框架如 AspectJ是有价值的, ,他们是互补的,而不是在竞争。 Spring 2.0 无缝地集成了Spring AOP和AspectJ和IoC,使所有 使用AOP提 供的在一个一致的基于spring 应用程序体系结构。 这种集成不影响弹簧 AOP API或AOP联盟API:Spring AOP仍然是向后兼 容的。 看到 下面的章节 对于一个 Spring AOP的讨论api。 注意 的一个中心原则的Spring框架的 具有非侵袭性 ,这是你的想法 不应该被迫介绍特定于框架的类和 接口业 务/域模型。 然而,在一些地方 Spring框架确实给你选择介绍弹簧 特定于框架的依赖关系到你的代码库:基 本原理在 给你这样的选项,因为在某些情况下它可能是 单纯的容易阅读或一些特定功能的代码块 在这样一 种方式。 Spring框架(几乎)总是为你提供 选择虽然:你可以自由做出明智的决策, 哪个选项最适合您的特定 用例或场景。 这样的一个选择,这一章是相关的 而AOP框架(AOP风格)选择。 你有 选择AspectJ和/或Spring AOP,你也有 选择的 要么@ aspectj注释风格方法或Spring XML 方式方法。 事实上,这一章选择 介绍了@ aspectj风格 方法首先不应该被当作一个 迹象表明,春季团队支持@ aspectj的注释风格 方法在Spring XML方式。 看到 SectionA 9.4,一个​​选择AOP声明使用​​风格 对于一个 更完整的讨论识大体、每个风格。 9.1.3A AOP代理 Spring AOP默认使用标准J2SE 动态 代理 对AOP代理。 这使得任何接口(或设置 接口)进行代理。 Spring AOP还可以使用CGLIB代理。 这是需要代理 类,而不是接口。 默认情况下使用CGLIB是如果一个企业 对象没有实现一 个接口。 因为它是良好的实践 程序接口而不是类、业务类通常 将实现一个或多个业务接口。 它是可能的 力使用CGLIB ,在 那 些(希望罕见的)情况下,你需要建议的方法 不上声明一个接口,或者你需要通过一个代理对象 到一个方法作为一个具体的类型。 它是重要的去把握事实是Spring AOP 基于代理的 。 看到 SectionA 9 6 1,一个​​理解AOP proxiesa​​ 对于一个彻底的检查 正是 这实际上意味着实现细节。 9.2一个@ aspectj支持 @ aspectj指的是一个风格的声明方面作为普通Java 类注释Java 5注释。 @ aspectj风格的 引入的 AspectJ 项目 AspectJ 5的 一部分发布。 Spring 2.0解释 相同的注释为AspectJ 5、图书馆使用AspectJ提供的 切入点解析和匹配。 AOP运行时仍然是纯 粹的Spring AOP 虽然,没有依赖AspectJ编译器或 韦弗。 使用AspectJ编译器和韦弗允许使用 全AspectJ语言,并讨论了 SectionA 9.8,一个​​使用AspectJ和弹簧applicationsa​​ 。 9.2.1A启用@ aspectj支持 使用@ aspectj切面在Spring配置你需要的 使弹簧支持配置Spring AOP基于@ aspectj 方面,和 自动代理 豆子基于是否或 不 建议他们通过那些方面。 通过自动代理我们意味着如果 弹簧确定bean是建议由一个或多个方面,它将 自动生成一个代理的 bean来截距法 调用和确保的建议是根据需要执行。 @ aspectj的支持可以启用XML或Java风格 配置。 在这两种情况下你还需要确保 AspectJ的 aspectjweaver.jar 图书馆在你的 应用程序的类路径(版本1 6 8或更高)。 这个图书馆是可用的 “自由” 目录的一个AspectJ分布或通过Maven中央存储库。 使@ aspectj支持Java配置 使@ aspectj支持使用Java @ configuration 添加 @EnableAspectJAutoProxy 注释: @Configuration @EnableAspectJAutoProxy public class AppConfig { } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 138/514 使@ aspectj支持使用XML配置 使@ aspectj支持基于XML的配置与使用 aop:aspectj火狐的一个插件 元素: 这个假设您正在使用模式中描述的支持 AppendixA E, XML的基于配置 。 看到 SectionA e 2 7,一个​​了 aop schemaa​​ 如何导 入标签 aop命名空间。 如果您正在使用DTD,仍有可能使@ aspectj 支持通过添加以下定义您的应用程序 背景: 9.2.2A声明一个方面 与@ aspectj支持启用,任何bean中定义 应用程序上下文的类,是一个@ aspectj方面(有 @Aspect 注释)将自动 检测到春天和用 于配置Spring AOP。 以下 示例显示所需的最小的定义不是很有用 方面: 一个正规的bean定义的应用程序上下文,指向 一个bean类的 @Aspect 注释: 和 NotVeryUsefulAspect 类 定义,标注 org aspectj朗注释方面 注释; package org.xyz; import org.aspectj.lang.annotation.Aspect; @Aspect public class NotVeryUsefulAspect { } 方面(类标注 @Aspect )可能有方法和字段只是 像任何其他类。 他们还可能包含切入点,建议,和 简介(类型间)声明。 Autodetecting方面通过组件扫描 你可以注册方面类在你的春天一般的咖啡豆 XML配置,或者自动侦测他们通过类路径扫描- 就像任何其他 spring管理bean。 然而,请注意, @Aspect 注释是 不 足够的自动识别在类路径中:为此, 您需要添加一个单 独的 component 注释 (或另外一个定义构造型注解,合格, 按规则Spring的组件扫描)。 建议方面与其他方面? 在Spring AOP,它是 不 可能有 方面自己是目标的其他方面的建议。 这个 @Aspect 注释在类标记为一个 方面,因此排除了它从汽车代理。 9.2.3A声明一个切入点 回想一下,确定感兴趣的切入点连接点,因此 使我们能够控制当建议执行。 Spring AOP只有 支持方法执行连接点为Spring bean ,所以 你能想到的一个切入点匹配方法的执行 Spring bean。 一个切入点声明有两个部分:一个签名 由一个名称和任何参 数,和一个切入点表达式 决定 到底 这方法执行我们吗 感兴趣的。 在AOP @ aspectj注释风格,一个切入点 签名是由常规方法 定义和切入点 表达式是表示使用 @Pointcut 注释(该方法服务 作为切入点签名 必须 有一个 无效 返回类型)。 一个例子会使这个区别一个切入点 签名和一个切入点表达清楚。 下面的例子定义了 一个切入点命名 ”anyOldTransfer” 这 将匹配 执行任何方法命名 “转让” : @Pointcut("execution(* transfer(..))")// the pointcut expression private void anyOldTransfer() {}// the pointcut signature 切入点表达式的值的形式 @Pointcut 注释是一个常规AspectJ 5切入点表达式。 对于一个完整的讨论AspectJ的切入点 语言, 看到 AspectJ 编程指南 (和基于Java 5的扩展, AspectJ 5开发人员笔记本 )或一个AspectJ的书籍等 一个​​ Eclipse AspectJ 一个​​ 等人通过Colyer或 一个​​ AspectJ在 行动 一个​​ Ramnivas Laddad通过。 支持切入点指示器 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 139/514 其他切入点类型 完整的AspectJ切入点语言支持额外的 切入点 指示器,在春天不支持。 这些都是: 电话,得到 集,preinitialization,staticinitialization, 初始化,处理程序,,,,adviceexecution withincode cflow cflowbelow,如 果,@this ,和 @withincode 。 使用这些切 入点指示器在切入点表达式 Spring AOP解释将 导致一个 IllegalArgumentException 被 扔。 这个组由Spring AOP切入点指示器可能 扩展在 将来的版本中支持更多的AspectJ切入点 指示 器。 Spring AOP支持以下AspectJ切入点指示器 (PCD)用于切入点表达式: 执行 ——匹配方法 执行连接点,这是你的主要切入点指示器 将使用在 使用Spring AOP吗 在 ——限制匹配连接点 在某些类型(简单地执行一个方法声明 在一个 匹配类型在使用Spring AOP) 这 ——限制匹配连接点 (执行方法在使用Spring AOP)的bean 参考 (Spring AOP代理)是给定的一个实例 类型 目标 ——限制匹配连接点 (执行方法在使用Spring AOP)目标 对象(应 用程序对象代理)的一个实例 给定类型 args ——限制匹配连接点 (执行方法在使用Spring AOP) 给出的实例 的参数类型 @ target时 ——限制匹配连接点(执行方法当 使用Spring AOP)类的 对象有一个执行 注释指定类型的 @args —— 限制匹配连接点(执行方法当 使用Spring AOP)在运行时 类型的实际参数 通过给定的类型有标注(年代) @within ——限制匹配连接点类型内给定 注释(执行方法中声明的类 型的 鉴于注释在使用Spring AOP) @annotation ——限制匹配的加入 点的主题(方法执行连接点 在Spring AOP)已给定的注释 因为Spring AOP限制只匹配方法执行 加入点,讨论的切入点指示器上面了 比你会发现窄的定义在AspectJ编程 指南。 此 外,AspectJ本身基于类型的语义和在一个 执行连接点均 这 ”和 ” 目标 “引用同一个对象——对象 执行该方法。 Spring AOP是一个基于代理的系统和 区分代理对象本身(绑定到 ” 这 ”)和目标对象背后的代理 (绑定到“ 目标 ”)。 注意 由于Spring的基于代理的AOP框架性质, 受保护的方法被定义 不 拦截,既没有JDK代理(这并不适用) 也不是 为CGLIB代理(这在技术上是可能的,但不 值得推荐的对AOP的目的)。 因此,任何给定的切入点 将匹配与 公 共方法只有 ! 如果你的拦截需求包括保护/私有方法 甚至是构造函数,考虑使用台弹力 原生AspectJ编织 相反 Spring的基 于代理的AOP框架。 这就构成了一个不同的 AOP使用模式具有不同特点,所以一定要做 你自己熟悉织造前 作出决定。 Spring AOP还支持额外的PCD命名 ” bean ”。 这金刚石可以限制匹配 到一个特定的连接点名为Spring bean,或者一组命 名的 Spring bean(当使用通配符)。 “ bean “金刚石 具有以下形式: bean(idOrNameOfBean) “ idOrNameOfBean “令牌可以被命名 任何Spring bean:有限的通配符支持使用 ” * 字符被提供,所以如果你建立 一些命名 约定你的Spring bean,您可以很容易的 写一个“ bean “金刚石表达式找出它们。 作为 情况与其他切入点指示器, ” bean “金刚石可以& &”| | ed,”艾德,! (否定)太。 注意 请注意,“ bean 的金刚石是 只有 支持Spring AOP和 不 在本机AspectJ编织。 这是一个 spring特定扩展 标准PCDs AspectJ 定义了。 “ bean “金刚石运行在 实例 水平(建立在春天 bean名称概念),而不是只在类型水平 (这是编织基于AOP 是有限的)。 基于实例的切入点指示器是一个特殊的能力 Spring的基于代理的AOP框架及其紧密集成 与 Spring bean工厂,它是自然的和 简单的识别特定bean的名字。 结合切入点表达式 切入点表达式可以组合使用“& &”、“| |” 和“!”。 也可以参考切入点表达式的名字。 下面的例子显示了三个切入点表达 式: anyPublicOperation (如果一个方法相匹配 执行连接点代表执行任何公共方法); inTrading (如果一个方法执行匹配 在交易 模块), tradingOperation ( 如果一个方法执行匹配代表任何公共方法 交易模块)。 @Pointcut("execution(public * *(..))") private void anyPublicOperation() {} 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 140/514 @Pointcut("within(com.xyz.someapp.trading..*)") private void inTrading() {} @Pointcut("anyPublicOperation() && inTrading()") private void tradingOperation() {} 这是一个最佳实践,构建更复杂的切入点表达式 出的小名叫组件如上所示。 指 切入点的名字,正常的Java可见性规则应用(你可 以看到 私人的切入点在同一类型、保护中的切入点 层次结构、公共切入点任何地方等等)。 能见度不 影响切入点 匹配 。 分享共同切入点定义 在使用企业应用程序,你经常想 指模块的应用程序和特定的操作集合 从几方面。 我们建议定义一个 “SystemArchitecture”方面,抓住共同切入点表达式 为了这个目的。 一个典型的这样的方面看起来如下: package com.xyz.someapp; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class SystemArchitecture { /** * A join point is in the web layer if the method is defined * in a type in the com.xyz.someapp.web package or any sub-package * under that. */ @Pointcut("within(com.xyz.someapp.web..*)") public void inWebLayer() {} /** * A join point is in the service layer if the method is defined * in a type in the com.xyz.someapp.service package or any sub-package * under that. */ @Pointcut("within(com.xyz.someapp.service..*)") public void inServiceLayer() {} /** * A join point is in the data access layer if the method is defined * in a type in the com.xyz.someapp.dao package or any sub-package * under that. */ @Pointcut("within(com.xyz.someapp.dao..*)") public void inDataAccessLayer() {} /** * A business service is the execution of any method defined on a service * interface. This definition assumes that interfaces are placed in the * "service" package, and that implementation types are in sub-packages. * * If you group service interfaces by functional area (for example, * in packages com.xyz.someapp.abc.service and com.xyz.def.service) then * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))" * could be used instead. * * Alternatively, you can write the expression using the 'bean' * PCD, like so "bean(*Service)". (This assumes that you have * named your Spring service beans in a consistent fashion.) */ @Pointcut("execution(* com.xyz.someapp.service.*.*(..))") public void businessService() {} /** * A data access operation is the execution of any method defined on a * dao interface. This definition assumes that interfaces are placed in the * "dao" package, and that implementation types are in sub-packages. */ @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))") public void dataAccessOperation() {} } 切入点这样定义的一个方面,可以引用 任何地方,你需要一个切入点表达式。 例如,为了使 服务层的事务,你可以写: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 141/514 这个 < aop:配置> 和 < aop:顾问> 元素进行 SectionA 9.3,一个​​supporta​​基于AOP 。 讨论了事务元素 ChapterA 12, 事务管 理 。 例子 Spring AOP用户可能使用 执行 切入点指示器最常。 这个 一个执行表达式的格式是: execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 所有部件除了返回类型模式(ret型模式在 上面的代码片段),名称模式和参数模式是可选的。 返回的类型模式决定了的返回类型 方法必须为了一个连接点匹配。 大多数 你经常使用 * 作为返回类型 模式匹配的任何返回类型。 一个完全限定类型名称 将匹 配只有在方法返回给定的类型。 这个名字 模式匹配方法名。 您可以使用 * 通配符是全部或部分名称模式。 参数模式是 稍微 复杂的: () 匹配方法, 不带参数,而 (. .) 匹配任何 数目的参数(零或更多)。 该模式 (*) 以一个参数匹配方法的任何 类型, (*,字符 串) 匹配方法采取两种 参数,第一可以是任何类型,第二必须是一个字符串。 咨询 语言语义 部分的AspectJ编程指南 为更多的信 息。 一些常见的例子给出了切入点表达式 下面。 执行任何公共方法: execution(public * *(..)) 任何方法的执行与一个名称开头 “设置”: execution(* set*(..)) 执行定义的任何方法 AccountService 接口: execution(* com.xyz.service.AccountService.*(..)) 执行任何方法中定义的服务 包: execution(* com.xyz.service.*.*(..)) 执行任何方法中定义的服务包 或一个包里: execution(* com.xyz.service..*.*(..)) 任何连接点(方法执行只有在Spring AOP)内 服务包: within(com.xyz.service.*) 任何连接点(方法执行只有在Spring AOP)内 服务包或包里: within(com.xyz.service..*) 任何连接点(方法执行只有在Spring AOP) 代理实现了 AccountService 接口: this(com.xyz.service.AccountService) “这更多的是常用的在一个绑定形式:- 看到下面的部分建议对于如何制作代理 对象可以在身体的建议。 任何连接点(方法执行只有在Spring AOP) 目标对象实现 AccountService 接口: target(com.xyz.service.AccountService) “目标”更多地使用一个绑定形式:- 见以下部分的建议如何使目标 对象可以在身体的建议。 任何连接点(方法执行只有在Spring AOP) 接受一个参数,并在运行时传递的参数 是 可序列化的 : args(java.io.Serializable) “args”更多地使用一个绑定形式:-看到 以下部分的建议对于如何制作方法参数 身体中可用的建议。 注意,本例中给出的切入点是不同的 执行(* *(java io序列化)) :args 版本匹配如果在运行时传递的参数是可序列化的, 执行版 本匹配如果方法签名声明 单一类型的参数 可序列化的 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 142/514 任何连接点(方法执行只有在Spring AOP) 目标对象有一个 transactional 注释: @target(org.springframework.transaction.annotation.Transactional) “@ target时”也可以用于绑定形式:——看 以下部分的建议如何使注释 对象可以在身体的建议。 任何连接点(方法执行只有在Spring AOP) 声明的类型的目标对象有一个 transactional 注释: @within(org.springframework.transaction.annotation.Transactional) “@within”也可以用于绑定形式:——看 以下部分的建议如何使注释 对象可以在身体的建议。 任何连接点(方法执行只有在Spring AOP) 执行方法 transactional 注释: @annotation(org.springframework.transaction.annotation.Transactional) “@annotation”也可以用于绑定形式:- 见以下部分的建议如何使注释 对象可以在身体的建议。 任何连接点(方法执行只有在Spring AOP) 接受一个参数,并在运行时的类型 参数传递有 @Classified 注释: @args(com.xyz.security.Classified) “@args”也可以用于绑定形式:——看 以下部分的建议如何使注释 对象(s)中可用的建议的身体。 任何连接点(方法执行只有在Spring AOP) Spring bean名为“ tradeService ”: bean(tradeService) 任何连接点(方法执行只有在Spring AOP) Spring bean有名称,匹配通配符表达式 ” *服务 ”: bean(*Service) 编写好的切入点 在编译期间,AspectJ切入点过程,为了优化匹配性能。 检查代码 和确定每个连接点匹配(静态或动态)给定的切入点是一个昂贵的 过程。 (动态 比赛意味着匹配不能完全决定从静态分析和测试将放置在代码 决定是否有一个实际的比赛当代码运行)。 第一次 遇到一个切入点声明, AspectJ将重写它到一个最佳状态匹配过程。 这是什么意思? 主要的切入点 是重写的析取范式(析取范式) 和组件的切入点是排序,这样那些吗 组件是首先检查评估更便宜。 这意味着您不必担心理解 不同的切入点的性能指示器和可能 供应以任何顺序排列在一个切入点声明。 然而,AspectJ只能使用它是什么告诉,为获得最佳性能的匹配你应该 想想他们正试图实现和缩小搜索空间尽可能匹配的 定义。 现有的指示器自然分为三组:燃起理智之火,范围和背景: 指示器燃起理智之火是那些选择特定类型的连接点。 例如:执行、获取、设置、电话,处理程序 范围指示器是那些选择一组加入的兴趣点(可能很多种类)。 例如:在,withincode 上下文指示器是那些匹配(和可选的绑定)基于上下文。 例如:这,目标,@annotation 一个写好的切入点应该尝试至少包括第一两种类型(燃起理智之火和范围),同时 上下文指示器可能包括如果希望匹配基于连接点 上下文,或绑定,上下文 使用的建议。 仅仅提供要么燃起理智之火还是上下文指示器指示者将工作但 可能影响织造性能(时间和 内存使用)由于所有的额外的处理和分析。 范围 指示器是非常快和他们的使用来匹配意味着AspectJ很快可以解散群 连接点,不 应该被进一步加工——这就是为什么一个好的切入点应该包括 如果有可能的话。 9.2.4A声明的建议 的建议是关联到一个切入点表达式,并运行之前, 之后,或在方法执行匹配的切入点。 切入点 表达式可以是一个简单的参考为命 名的切入点,或一个 切入点表达式中声明的地方。 建议之前 之前在一个方面建议申报使用 @ before一样 注释: import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BeforeExample { @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doAccessCheck() { // ... 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 143/514 } } 如果使用一个合适的切入点表达式我们可以重写 以上的例子: import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BeforeExample { @Before("execution(* com.xyz.myapp.dao.*.*(..))") public void doAccessCheck() { // ... } } 回国后的建议 回国后建议运行当匹配方法执行 返回正常。 这是宣布使用 @AfterReturning 注释: import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterReturning; @Aspect public class AfterReturningExample { @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doAccessCheck() { // ... } } 注意:当然可能有多个建议 声明和其他成员,都在相同的方面。 我们只是显示一个建议在这些例子来。宣言 对这个问题的关注 讨论的时间。 有时你需要访问的建议身体实际值 这是返回。 您可以使用的形式 @AfterReturning 这将返回 值: import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterReturning; @Aspect public class AfterReturningExample { @AfterReturning( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", returning="retVal") public void doAccessCheck(Object retVal) { // ... } } 名称中使用的 返回 属性必须 对应于一个参数的名称的建议方法。 当一个 方法执行返回,返回值将被传递到 建议方法相应的参 数值。 一个 返回 条款也限制只匹配 这些方法执行,返回一个指定类型的价值 ( 对象 在这种情况下,它将匹配任何 返回值)。 请注意,这是 不 可能 返回一个完全不同的参考在使用后返回 建议。 在投掷的建议 在运行时抛出建议匹配方法执行退出 通过抛出异常。 这是宣布使用 @AfterThrowing 注释: import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterThrowing; @Aspect public class AfterThrowingExample { @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doRecoveryActions() { // ... } } 你经常想要建议只运行一个给定的时候例外 类型是扔,你也经常需要访问扔 异常的建议的身体。 使用 扔 属性既限制匹配(如果 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 144/514 需要,使用 Throwable 随着异常类型 否则),并将其绑定到一个忠告,抛出异常 参数。 import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.AfterThrowing; @Aspect public class AfterThrowingExample { @AfterThrowing( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", throwing="ex") public void doRecoveryActions(DataAccessException ex) { // ... } } 名称中使用的 扔 属性必须 对应于一个参数的名称的建议方法。 当一个 方法执行退出通过抛出异常,异常 传递给建议方法相应 的参数值。 一个 扔 条款也限制只匹配 这些方法执行,抛出一个异常指定类型的 ( DataAccessException 在这种情况下)。 (最后)后建议 (最后)建议后运行一个匹配方法执行。然而 退出。 这是宣布使用 @After 注释。 建议后必须准备处理包括正常的和 异常返回 条件。 它通常用于释放 资源等。 import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.After; @Aspect public class AfterFinallyExample { @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doReleaseLock() { // ... } } Around通知 最后一种建议是around通知。 Around通知运行 “周围”的一个匹配的方法执行。 有机会去做的工作 前后方法执行,以确定 何时、如何 即使,这个方法实际上可以执行在所有。 Around通知 是常用的如果你需要共享状态之前和之后的一个方法 在一个 线程安全的方式执行(启动和停止一个定时器, 例)。 总是使用最强大的形式的建议,满足你 需求(即不使用around通知如果以前 简单的建议 会做)。 在宣布使用建议 @Around 注释。 第一个参数 建议的方法必须的类型 ProceedingJoinPoint 。 主体内 的建议,称 proceed() 在 ProceedingJoinPoint 导致 底层方法来执行。 这个 进行 方法 也可以称为传入一个吗 Object[] —— 值在数组将作为参数 的方法 执行当它收益。 进行的行为的时候叫 Object[]有点不同 的行为对周围进行建议编制的AspectJ 编译器。 建议写左右使用传统的AspectJ 语言, 传递的参数的数量进行必须匹配 数目的参数传递到周围的建议(不是数量的 参数被底层连接点),值传递给 按照给定的参数位置 取代原来的值 连接点的实体价值势必(别担心如果 这没有意义现在!)。 采用的方法是春天 简单的和一个更好的匹配它的基于代 理的,只执行 语义。 你只需要知道这种差异如果你 编译@ aspectj切面写给春天和使用进行 参数与AspectJ编译器和韦弗。 有 一个办法 写等方面,是100%兼容跨Spring AOP和 AspectJ,这是下一节中讨论的建议 参数。 import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.ProceedingJoinPoint; @Aspect public class AroundExample { @Around("com.xyz.myapp.SystemArchitecture.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal; } } 返回的值在建议将返回值 被调用者的方法。 一个简单的缓存方面例如 可以返回一个值从一个缓存,如果它有一个、调用 proceed() 如果它不。 注意,进行可能调用一次,很多次,或 不是所有在身体周围的建议,所有这些都是 完全合法。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 145/514 建议参数 Spring 2.0提供了充分的类型化的建议——这意味着你声明 你需要的参数在建议签名(正如我们看到的 上面的例子返回和投掷) 而不是工作 Object[] 数组的所有时间。 我们将看到如何 使参数和其他上下文值提供给建议的身体 在一个时刻。 首先让我们 看一看如何编写通用的建议 可以找出方法目前的建议 建议。 访问当前 连接点 任何建议方法可以宣布作为它的第一个参数,一个 类型的参数 org aspectj朗的连接点 (请 注意周围的建议是 需要 申报 第一个 参数的类型 ProceedingJoinPoint ,这是一个 子类的 连接点 。 这个 连接点 接口提供了许多 有用的方法如 getArgs() (返回 方 法参数), getThis() (返回 代理对象), getTarget() (返回 目标对象), getSignature() (返回一个 描述的方法是建议)和 toString() (打印一个有用的描述 该方法被建议)。 请查阅Javadocs完全 细节。 传递参数给建议 我们已经看到了如何绑定返回值或异常 值(使用后回国后和投掷的建议)。 让 参数值提供给建议的身体,你可以使用 绑定形式的 args 。 如果一个参数的名字是使用 代替一个类型名称的参数表达式,那么这个值的 相应的参数将作为参数传递价值 调用的建 议。 一个例子应该使这个更清晰。 假设 你想建议的执行,拿一个dao操作 帐户对象作为第一个参数,你需要访问 账户在建议的 身体。 您可以编写以下: @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + "args(account,..)") public void validateAccount(Account account) { // ... } 这个 args(账户,. .) 部分的切入点 表达式有两个目的:首先,这限制了匹配 只有那些方法执行的方法需要至少一个 参数和参数传 递给该参数是一个实例 的 帐户 ;其次,它使实际的 帐户 对象提供给建议通过 这个 帐户 参数。 另一种方式写这是声明一个切入点, “提供” 帐户 对象价值当它 匹配一个连接点,然后只是参考命名的切入点从 这个建议。 这 将如下所示: @Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + "args(account,..)") private void accountDataAccessOperation(Account account) {} @Before("accountDataAccessOperation(account)") public void validateAccount(Account account) { // ... } 感兴趣的读者是再一次提到了AspectJ 编程指南为更多的细节。 代理对象( 这 ),目标对象 ( 目标 )、注释( @within, @ target时,@annotation,@args )都可以绑定在一个类似的 时尚。 下面 的例子显示了如何匹配 执行的方法的一种 @Auditable 注释,并提取 审计代码。 第一个定义 @Auditable 注释: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Auditable { AuditCode value(); } 然后建议匹配的执行 @Auditable 方法: @Before("com.xyz.lib.Pointcuts.anyPublicMethod() && " + "@annotation(auditable)") public void audit(Auditable auditable) { AuditCode code = auditable.value(); // ... } 建议参数和泛型 Spring AOP可以处理泛型类声明和使用 方法参数。 假设您有一个泛型类型如下: public interface Sample { void sampleGenericMethod(T param); void sampleGenericCollectionMethod(Collection>T> param); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 146/514 } 你可以限制拦截的方法对特定类型 只需输入参数类型参数的建议 参数类型你想要拦截的方法: @Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") public void beforeSampleMethod(MyType param) { // Advice implementation } 这个作品是很明显的我们已经讨论过的 以上。 然而,需要指出的是,这个工作不了 泛型集合。 所以你不能定义切入点像 这个: @Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") public void beforeSampleMethod(Collection param) { // Advice implementation } 做这项工作我们会检查每一个元素的 集合,它是不合理的,因为我们还不能决定 治疗 空 值在一般。 实现 类似于这个你必须输入 参数 收集< ? > 和手动 检查类型的元素。 确定参数名称 参数绑定在通知调用依赖于匹配 切入点表达式中使用的名称,名称声明的参数 (建议和切入点)方法签名。 参数名称 不 可以通 过Java反射,所以 Spring AOP使用以下策略来确定参数 名称: 1. 如果参数名称由用户指定 明确,然后指定参数名称用:两个 建议和切点注释有一个可选的 “argNames”属性可以用来指定 参数 带注释的方法的名字——这些参数名称 是 在运行时可用。 对于 示例: @Before( value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", argNames="bean,auditable") public void audit(Object bean, Auditable auditable) { AuditCode code = auditable.value(); // ... use code and bean } 如果第一个参数的 连接点 , ProceedingJoinPoint ,或 JoinPoint.StaticPart 式,你 会省去参数的名字从价值的 “argNames”属性。 例如,如果您修改前面的 建议接收连接点的对象,“argNames” 属性不需要包括: @Before( value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", argNames="bean,auditable") public void audit(JoinPoint jp, Object bean, Auditable auditable) { AuditCode code = auditable.value(); // ... use code, bean, and jp } 特殊优待的第一个参数 连接点 , ProceedingJoinPoint ,和 JoinPoint.StaticPart 类型是 特别是方便建议,不收取任何其他 连接点上下文。 在这种情况下,你可以简单地省略了 “argNames”属性。 例如,下面的建议不需要 宣布“argNames”属 性: @Before( "com.xyz.lib.Pointcuts.anyPublicMethod()") public void audit(JoinPoint jp) { // ... use jp } 2. 使用 ”argNames” 属性是一个 小笨手笨脚,所以如果 ”argNames” 属性 没有被指定,那么Spring AOP将着眼于调试 吗 信息类,并试图确定参数 局部变量的名称表。 这些信息将 现在只要类被编译和调试 信息( ' - g:增值” 在最低限度)。 这个 后果与这个国旗的编译是:(1)你的代码 会稍微容易理解(逆向),(2) 类文件大小将略微大(一般 无关紧要的),(3)优化以删 除未使用的地方 变量不会被应用到你的编译器。 换句话说, 你应该遇到任何艰难建筑这个标志 在。 如果一个@ AspectJ方面已编制的AspectJ 编译器(ajc)即使没有调试信息还有 不需要添加argNames属性的 编译器将保留 所需的信息。 3. 如果代码被编译时没有必要的调试 信息,然后Spring AOP将试图推断出配对 绑定变量的参数(例如,如果只有一个 变量是绑 定在切入点表达式,和建议 方法只接受一个参数,结对是显而易见的!)。 如果 绑定的变量是模糊给出可用的 信息,然后一个 AmbiguousBindingException 将 扔。 4. 如果上述所有策略都失败那么一个 IllegalArgumentException 将 扔。 进行参数 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 147/514 我们说,我们会早些时候描述如何编写一个 进行调用 与参数 工作 在Spring AOP和AspectJ一致。 解决方案是简单 确保建议签 名绑定每个方法 参数的顺序。 例如: @Around("execution(List find*(..)) &&" + "com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " + "args(accountHolderNamePattern)") public Object preProcessQueryPattern(ProceedingJoinPoint pjp, String accountHolderNamePattern) throws Throwable { String newPattern = preProcess(accountHolderNamePattern); return pjp.proceed(new Object[] {newPattern}); } 在很多情况下你会做这个绑定无论如何(如 上面的例子)。 建议订购 会发生什么当多个建议所有想要运行在 同样的连接点? Spring AOP遵循相同的优先规则 AspectJ来确定订单的建议执行。 最 高的 优先建议首先运行的“在”(所以给两块 之前建议的,最高优先级运行第一)。 “在 出路”从一个连接点,最高优先级的跑最 后的建议 (所以给两块的建议后,最高 优先将运行第二)。 当两块中定义的建议 不同 方面都需要运行在相同的 连接点,除非你指定否则执行顺序是 未定义的。 你可以控制执行的顺序指 定 优先。 这样做是在正常的弹簧,要么 实施 org.springframework.core.Ordered 界面方面类或注释的 秩序 注释。 给定两个 方面, 低价值方面返回从 下令getvalue() (或注释值) 优先级越高。 当两块中定义的建议 这个 相同 方面都需要运行在相同的连接点, 排序是未定义的(因为没有方法来检索 声明顺序通过反射对 javac编译后的类)。 考虑 崩溃这样的建议方法到一个建议方法/连接点 在各个方面类或重构建议到单独的 方面可以订购类— 这在方面水平。 9.2.5A介绍 介绍(称为类型间声明在AspectJ)启用 一个方面声明,建议对象执行一个给定的接口, 并提供一个实现该接口的代表 对象。 摘要提出一种使用 @DeclareParents 注释。 这 注释用于声明,匹配类型有一个新的父 (因此得名)。 例如,给定一个接口 UsageTracked ,和一个实现 该接口 DefaultUsageTracked ,以下 方面声明,所有服务接口的实现者也 实现 UsageTracked 接 口。 (在 为了通过JMX公开统计数据为例。) @Aspect public class UsageTracking { @DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class) public static UsageTracked mixin; @Before("com.xyz.myapp.SystemArchitecture.businessService() &&" + "this(usageTracked)") public void recordUsage(UsageTracked usageTracked) { usageTracked.incrementUseCount(); } } 接口的实现是由这个类型的 带注释的字段。 这个 价值 属性的 @DeclareParents 注释是一个AspectJ 类型模式:-任何bean的 一个匹配的类型将会实现 UsageTracked接口。 注意,在上面的之前的建议 例,服务bean可以直接用作实现的 UsageTracked 接口。 如果访问 你会写编程bean以下: UsageTracked usageTracked = (UsageTracked) context.getBean("myService"); 9.2.6A方面的实例化模型 (这是一个高级主题,所以如果你只是开始 AOP你可以跳过它,直到后来。) 默认情况下会有一个单一的实例,在每一个方面 应用程序上下文。 AspectJ调用这个singleton实例化 模型。 可以定义方面,以 替代生命周期:- Spring支持AspectJ的 关于perthis 和 pertarget 实例化模型( percflow, percflowbelow, 和 pertypewithin 不是 目前支持)。 一个“关于perthis”方面是宣布通过指定一个 关于perthis 条款 @Aspect 注释。 让我们看一个 例子,然后我们将解释它是如 何工作的。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 148/514 @Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())") public class MyAspect { private int someState; @Before(com.xyz.myapp.SystemArchitecture.businessService()) public void recordServiceUsage() { // ... } } 的效果 '关于perthis” 条款是一个 方面的实例将被创建为每个独特的服务对象执行 一个业务服务(每个独特的对象绑定 到‘这’在连接点 切入点表达式相匹配)。 方面的实例被创建 第一次一个方法被调用的服务对象。 方面 超出范围当服务对象 超出范围。 之前 方面的实例被创建,没有建议在它执行。 作为 一旦创建了实例方面,建议内部声明 它将执行在匹配的连接点,但 只有当服务对象 是一个这方面有关。 看到AspectJ编程 更多的信息来指导每条款。 这个 “pertarget” 实例化模型工作在 关于perthis完全一样,但创建一个方面的实例 每一个独特的目标对象在匹配连接点。 9.2.7A例子 现在,您已经看到了如何工作的所有组成部分,让我们 将它们组合在一起来做一些有用的! 执行业务服务可以有时失败由于 并发性问题(例如,僵局失败者)。 如果操作是 重试,它很可能下一次成功。 对于业务 服务那里 是合适的,在这样的条件下重试(幂等 操作,不需要返回到用户冲突 分辨率),我们想透明地重试操作来避免 客户看到 PessimisticLockingFailureException 。 这是一个 要求清楚地穿过多个服务在服务 层,因此是理想的实现通过一个方面。 因为我们想重试操作,我们将需要使用约 建议,这样我们可以调用多次进行。 下面是基本的 方面实现看起来: @Aspect public class ConcurrentOperationExecutor implements Ordered { private static final int DEFAULT_MAX_RETRIES = 2; private int maxRetries = DEFAULT_MAX_RETRIES; private int order = 1; public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; } public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; } @Around("com.xyz.myapp.SystemArchitecture.businessService()") public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { int numAttempts = 0; PessimisticLockingFailureException lockFailureException; do { numAttempts++; try { return pjp.proceed(); } catch(PessimisticLockingFailureException ex) { lockFailureException = ex; } } while(numAttempts <= this.maxRetries); throw lockFailureException; } } 注意方面实现了 下令 所以我们可以设置界面 的优先级高于事务方面的建议(我们想要一个 新鲜的事务我们每一次重试)。 这个 maxRetries 和 秩序 属性都需要配置的 春天。 主要的行动发生在 doConcurrentOperation 约的建议。 注意,对于 目前我们 应用重试逻辑所有 businessService()年代 。 我们试着继续,如果我们失败了 与一个 PessimisticLockingFailureException 我 们 只是试一次,除非我们已经用尽了所有的重试 尝试。 相应的Spring配置是: 完善的方面,这样它只重试幂等 操作,我们可以定义一个 幂等 注释: @Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { // marker annotation } 和使用注释来注释实现的服务 操作。 更改方面只有重试幂等操作 只是涉及炼油切入点表达式,因此只有 @Idempotent 操作 匹配: @Around("com.xyz.myapp.SystemArchitecture.businessService() && " + "@annotation(com.xyz.myapp.service.Idempotent)") public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { ... } 9.3基于AOP支持 如果你不能使用Java 5,或者只是喜欢一个基于xml的 格式,然后Spring 2.0还提供了支持定义方面使用 新的“aop”名称空间标 签。 完全相同的切入点表达式和建议 支持各种当使用@ aspectj风格,因此在这 一节中,我们将关注新 语法 并参考了 读者在前 一节的讨论( SectionA 9.2,一个​​supporta​​@ aspectj )了解写作的切入点 表情和绑定参数的建议。 使用aop命名空间标记描述了在这一节中,您需要 导入spring aop模式中描述 AppendixA E, XML的基于配置 。 看到 SectionA e 2 7,一个​​了 aop schemaa​​ 如何导入标记 aop命名空间。 在Spring配置,所有方面和顾问的元素 必须被放置在一个吗 < aop:配置> 元素 (你可以有一个以上的 < aop:配置> 元素 在一 个应用程序上下文配置)。 一个 < aop:配置> 元素可以包含切入点, 顾问,元素(注意这些方面必须声明在那 顺序)。 警告 这个 < aop:配置> 风格的配置 大量使用Spring的 汽车代理 机制。 这可能会导致 问题(如建议没有被编织) 如果你已经在使用 显式汽车代理通过使用 BeanNameAutoProxyCreator 或者诸如此类的。 这个 推荐使 用模式是使用要么只是 < aop:配置> 风格,或只是 AutoProxyCreator 风格。 9.3.1A声明一个方面 使用模式的支持,一个方面是一个简单的普通Java 对象定义为一个bean在Spring应用程序上下文。 国家 和行为是捕获对象的 字段和方法, 切入点和建议信息捕获在XML。 一个方面是宣布使用< aop:方面>元素,和 支持bean的引用使用 Ref 属性: ... ... bean支持方面(“ aBean “在这个 例)当然可以被配置和依赖注入的一样 其他Spring bean。 9.3.2A声明一个切入点 一个名叫切入点可以被声明在一个< aop:配置> 元素,使切入点定义共享跨几个 方面和顾问。 一个切入点代表执行任何业务服务 服务层可以定义如下: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 150/514 注意,切入点表达式本身是使用相同的AspectJ 切入点表达式语言中描述 SectionA 9.2,一个​​supporta​​@ aspectj 。 如果您使用 的是基于模式 声明风格与Java 5,您可以参考切入点定义命名 在类型(@Aspects)在切入点表达式,但是这种特性 不可以在JDK 1.4及以下(它依赖Java 5具体 AspectJ反射api)。 在JDK 1.5因此,另一种定义 上面的切入点是: 假设你有一个 SystemArchitecture 方面 中描述的 一个​章节​分享共同的切入点definitionsa​​ 。 声明一个切入点在一个方面是非常相似的声明 一个顶级的切入点: ... 同样的方式在一个@ aspectj方面,切入点声明的使用 基于模式的定义风格可能收集连接点上下文。 对于 例如,下面的“本”的 切入点收集对象作为加入 点上下文并将其传递到建议: ... 必须声明的建议得到收集的连接点 上下文通过包括参数匹配的名字: public void monitor(Object service) { ... } 当结合切入点子表达式,“& &”是尴尬的 在一个XML文档,所以关键词”和“,”或“和”不是“可以 用来代替“& &”、“| |”和“! 分别的。 例如, 前面的切入点可能更好的写为: ... 注意,在这种方式的切入点定义引用他们 XML id和不能被用作命名为切入点,形成复合 切入点。 指定切入点支持基于模式的定 义 风格就更有限了,@ aspectj 风格。 9.3.3A声明的建议 相同的五个建议类型的支持对于@ aspectj 风格,和他们有相同的语义。 建议之前 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 151/514 建议在匹配之前运行方法执行。 这是 宣布在一个 < aop:方面> 使用 < aop:在>元素。 ... 这里 dataAccessOperation 是的id吗 切入点定义在顶部( < aop:配置> ) 水平。 定义切入点内联相反,取代 切入点ref 属性与 一个 切入点 属性: ... 当我们在讨论中提到的@ aspectj风格,使用命名 切入点可以大大提高你的可读性 代码。 方法属性识别方法 ( doAccessCheck ),提供身体的 建议。 这个方法必须定义bean的引用 方面的元素,其中包含的建议。 在一 个数据访问操作 是执行(一个方法执行连接点匹配的切入点 表达式),“doAccessCheck”方法的方面上的bean 调用。 回国后的建议 回国后建议运行当匹配方法执行 正常完成。 它是在一个声明 < aop:方面> 在像以前那样 建议。 例如: ... 正如在@ aspectj风格,可以拿到的 返回值在建议的身体。 使用返回的属性 指定名称的参数,返回值应该是 通过: ... doAccessCheck的方法必须声明一个参数命名的 retval 。 这个参数约束的类型 匹配相同的方式为@AfterReturning描述。 对于 例,方法签名可以声明为: public void doAccessCheck(Object retVal) {... 在投掷的建议 时,就会执行后扔建议匹配的方法执行 出口通过抛出异常。 它是在一个声明 < aop:方面> 使用后扔 元素: ... 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 152/514 正如在@ aspectj风格,可以拿到的 抛出异常在建议的身体。 使用投掷属性 指定名称的参数的异常应 通过: ... doRecoveryActions的方法必须声明一个参数命名的 dataAccessEx 。 这个参数约束的类型 匹配相同的方式为 @AfterThrowing描述。 例如, 方法签名可以声明为: public void doRecoveryActions(DataAccessException dataAccessEx) {... (最后)后建议 (最后)建议后运行一个匹配方法执行。然而 退出。 这是宣布使用 在 元素: ... Around通知 最后一种建议是around通知。 Around通知运行 “周围”的一个匹配的方法执行。 有机会去做的工作 前后方法执行,以确定 何时、如何 即使,这个方法实际上可以执行在所有。 Around通知 是常用的如果你需要共享状态之前和之后的一个方法 在一个 线程安全的方式执行(启动和停止一个定时器, 例)。 总是使用最强大的形式的建议,满足你 要求;不要使用around通知如果以前 简单的建议 做的。 在宣布使用建议 aop:约 元素。 的第一个参数 建议的方法必须是类型 ProceedingJoinPoint 。 主体内 的建议,称 proceed() 在 ProceedingJoinPoint 导致 底层方法来执行。 这个 进行 方法 也可以调用传入一个吗 Object[] —— 数组中的值将作为参 数的方法 执行当它收益。 看到 一个​章节​advicea​​周围 对笔记进行调用 与一个 Object[] 。 ... 实施 doBasicProfiling 的建议是完全一样的(减去@ aspectj的例子 注释当然): public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal; } 建议参数 基于模式的支持完全类型声明风格建议 在同样的方式描述的支持,通过匹配@ aspectj 切入点参数的名字对方法参数的建议。 看到 一个​章节parametersa​​​建议 详情。 如果你 想要显式地指定参数名称(不建议方法 依靠检测策略之前所描述的),那么这是 通过使用 自变量名称 属性的建议 元素,它是在相同的方式对待“argNames” 属性在一个注释中描述的建议 一个​​章节确定参 数namesa​​ 。 例如: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 153/514 这个 自变量名称 属性接受 用逗号分隔的参数名称的列表。 在下面发现一个稍复杂一点的例子基于xsd 的方法,说明了一些周围的建议结合使用 一个数量的强类型的参数。 package x.y.service; public interface FooService { Foo getFoo(String fooName, int age); } public class DefaultFooService implements FooService { public Foo getFoo(String name, int age) { return new Foo(name, age); } } 接下来是方面。 注意到的事实 配置文件(. .) 方法接受一个数量的 强类型的参数,第一个恰好是连接 点用来进行方法调用:的出 现 参数是一个迹象表明 配置文件(. .) 是被用作吗 周围 建议: package x.y; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.util.StopWatch; public class SimpleProfiler { public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable { StopWatch clock = new StopWatch( "Profiling for '" + name + "' and '" + age + "'"); try { clock.start(call.toShortString()); return call.proceed(); } finally { clock.stop(); System.out.println(clock.prettyPrint()); } } } 最后,这是XML配置需要 影响执行上面的建议为特定的加入 点: 如果我们有以下驱动脚本,我们会得到输出 这样的事情在标准输出: import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; import x.y.service.FooService; public final class Boot { public static void main(final String[] args) throws Exception { BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml"); FooService foo = (FooService) ctx.getBean("fooService"); foo.getFoo("Pengo", 12); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 154/514 } } StopWatch 'Profiling for 'Pengo' and '12'': running time (millis) = 0 ----------------------------------------- ms % Task name ----------------------------------------- 00000 ? execution(getFoo) 建议订购 当多个建议需要执行相同的连接点 (执行方法)排序规则中描述 一个​章节orderinga​​​建议 。 之间的优先 方面是由要么添加 秩序 注释到bean的支持 方面或通过bean实现 下令 接口。 9.3.4A介绍 介绍(称为类型间声明在AspectJ)启用 一个方面声明,建议对象执行一个给定的接口, 并提供一个实现该接口的代表 对象。 摘要提出一种使用 aop:声明的父母 元素在一个 aop:方面 这个元素用来声明 匹配类型有一个新的父(因此得名)。 例如,假定有 一个 接口 UsageTracked ,和一个 实现该接口的 DefaultUsageTracked ,以下方面 声明,所有服务接口的实现者也实现了 UsageTracked 接口。 (为了 通过JMX公开统计数据为例。) 类支持 usageTracking bean将 控制方法: public void recordUsage(UsageTracked usageTracked) { usageTracked.incrementUseCount(); } 接口的实现是由 实现接口 属性。 的价值 类型匹配 属性是一个AspectJ类型模式 :-任何bean的一个匹配的类型将会实现 UsageTracked 接口。 注意,在 在以上示例的建议,服务bean可以直接使用 作为实现的 UsageTracked 接口。 如果访问bean 以编程方式你会写 以下: UsageTracked usageTracked = (UsageTracked) context.getBean("myService"); 9.3.5A方面的实例化模型 唯一支持实例化模型方案定义方面 是单例模式。 其他实例化模型可以支持 将来的版本中。 9.3.6A顾问 “顾问”的概念提出了从AOP支持 在Spring 1.2中定义并没有直接等效在AspectJ。 一个顾问就像一个小的独立的方面,都有一 个单独的块 的建议。 这个建议本身就是一个由豆,和必须 实现一个接口描述的建议 SectionA 10 3 2,一个​​建议类型​​Springa 。 顾问可以利用 AspectJ切入点表达式虽然。 Spring 2.0支持顾问的概念 < aop:顾问> 元素。 你会最常见的 看到它结合使用事务的建议,也有它 自己的名称空间支持 Spring 2.0中。 这是它的样子: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 155/514 以及 切入点ref 属性用于 上面的例子,你也可以使用 切入点 属性 定义一个切入点表达式内联。 定义优先级的一个顾问,所以建议可以 参与排序,使用 秩序 属性 定义 下令 价值的顾问。 9.3.7A例子 让我们来看看并发锁失败重试的例子 SectionA 9 2 7,一个​​Examplea​​ 看起来当重写使用 模式支持。 执行业务服务可以有时失败由于 并发性问题(例如,僵局失败者)。 如果操作是 重试,它很有可能下次会成功的。 对于 业务服务 其适合于重试在这样的条件下 (幂等操作,不需要返回到用户 冲突解决),我们想透明地重试操作 避免客户看到 PessimisticLockingFailureException 。 这是一个 要求清楚地穿过多个服务在服务 层,因此是理想的实现通过一个方面。 因为我们想重试操作,我们将需要使用约 建议,这样我们可以调用多次进行。 下面是基本的 方面实现看起来(它只是一个普通的 Java类使用 模式支持): public class ConcurrentOperationExecutor implements Ordered { private static final int DEFAULT_MAX_RETRIES = 2; private int maxRetries = DEFAULT_MAX_RETRIES; private int order = 1; public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; } public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; } public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { int numAttempts = 0; PessimisticLockingFailureException lockFailureException; do { numAttempts++; try { return pjp.proceed(); } catch(PessimisticLockingFailureException ex) { lockFailureException = ex; } } while(numAttempts <= this.maxRetries); throw lockFailureException; } } 注意方面实现了 下令 所以我们可以设置界面 的优先级高于事务方面的建议(我们想要一个 新鲜的事务我们每一次重试)。 这个 maxRetries 和 秩序 属性都需要配置的 春天。 主要的行动发生在 doConcurrentOperation 在建议的方法。 我们试图 接下 来,如果我们不能用 PessimisticLockingFailureException 我们只是尝试 我们已经筋疲力尽了,除非我们所有的重试尝试。 这个类是相同的,使用@ aspectj的例子, 但随着注释删除。 相应的Spring配置是: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 156/514 注意,这里我们假设所有的业务 服务是等幂的。 如果不是这种情况我们可以改进 方面,只能重试真正幂等操作,通过 引入一个 幂 等 注释: @Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { // marker annotation } 和使用注释来注释实现的服务 操作。 更改方面只有幂等操作重试 只是涉及炼油切入点表达式,因此只有 @Idempotent 操作 匹配: 9.4一个选择AOP声明样式使用 一旦你已经决定,一个方面是最好的方法 实现一个给定的要求,如何决定使用Spring之间 AOP和AspectJ之间,方面语言(代码)风 格,@ AspectJ 注释风格,或Spring XML风格吗? 这些决定的影响 由多种因素,包括应用程序的需求,发展 工具和团队熟悉AOP。 9.4.1A Spring AOP和AspectJ完整吗? 使用最简单的事情可以工作。 Spring AOP是简单的比 使用全AspectJ是没有要求介绍AspectJ 编译器/韦弗进入您的开发和构 建过程。 如果你只 需要建议执行操作在Spring bean,那么春天 AOP是正确的选择。 如果你需要建议对象进行管理 Spring容 器(如域对象一般),然后你会 需要使用AspectJ。 你还需要使用AspectJ如果你希望 建议加入点除了简单的方法执行(例如, 字段 获取或设置的连接点,等等)。 当使用AspectJ,您可以选择AspectJ语言 语法(也称为“代码风格”)或@ aspectj注释 风格。 显然,如果你不使用Java 5 +然后 别无选择 为你做… 使用代码风格。 如果方面起到了很大的作用在你的 设计,你可以使用 AspectJ开发工具 (AJDT) Eclipse插件, 然后AspectJ语言语法 优先选择:它是清洁和简单的,因为语言是 故意设计为写作方面。 如果你不使用Eclipse, 或只有几个方面 不扮演重要角色在你的 应用程序,那么您可能需要考虑使用@ aspectj风格和 坚持常规Java编译在IDE,并添加一个 方面编织阶 段构建脚本。 9.4.2A @ aspectj或XML为Spring AOP吗? 如果你选择使用Spring AOP,然后你有一个选择 @ aspectj或XML风格。 显然如果你不运行在Java 5 +,然后 XML风格是合适 的选择,因为Java 5项目有 各种权衡考虑。 XML风格将对现有Spring用户最熟悉。 它 可以使用任何的JDK级别(指从内部命名为切入点 切入点表达式并仍然需要Java 5 +虽然)和后盾 真正的pojo。 当使用AOP作为一种工具来配置企业服务 然后XML可以是一个不错的选择(一个好的测试是您是 否考虑 切入点表达式的一部分,您的配置你可能想 改变独立)。 与XML风格可以说是清晰 您的配置中存在哪些方面的系统。 XML风格有两个缺点。 首先它不完全 封装的实现要求它地址的 单一的地方。 干燥原理说,应该有一个单独的, 明确的,权威的 表示知识的任何一块 在一个系统。 当使用XML样式,知识的 如何 一个要求是实现跨越 声明支持bean的类和XML的 配置文 件。 当使用@ aspectj风格有一个单一的 模块-方面——信息封装。 其次,XML样式稍微限制它可以表达 比@ aspectj风格:只 有“单体”方面的实例化模型 是支持,是不可能结合的切入点声明命名 在XML。 例如,在@ aspectj风格你可以写点东西 如: @Pointcut(execution(* get*())) public void propertyAccess() {} @Pointcut(execution(org.xyz.Account+ *(..)) public void operationReturningAnAccount() {} @Pointcut(propertyAccess() && operationReturningAnAccount()) public void accountPropertyAccess() {} 在XML样式我可以声明前两个切入点: XML方法的缺点是,你不能定义 ” accountPropertyAccess “切入点通过结合这些 定义。 @ aspectj风格的支持额外的实例化模型, 富裕切入点组合。 它的优势是保持方面 作为一个模块化的单位。 它也有优势的@ aspectj切面可以 理解(因此消耗)都通过Spring AOP和AspectJ -所以如果 你以后决定你需要实现的功能,AspectJ 额外的需求 然后很容易迁移到一个 基于aspectj的方法。 在平衡弹簧团队喜欢@ aspectj 只要你有风格方面,远不止简单的“配置” 企业 服务。 9.5混合类型方面 它是完全有可能混合风格方面的@ aspectj使用 自动代理支持,方案定义 < aop:方面> 方面, < aop:顾问> 宣布顾问甚至 代理 和拦截器定义使用Spring 1.2风格在同一 配置。 所有这些都是使用相同的底层实现 支持机制,将共存没有任何困难。 9.6代理机制 Spring AOP使用JDK的动态代理或CGLIB要么创建 代表一个给定的目标对象。 (JDK动态代理是首选 每当你有一个选择)。 如果目标对象进行代理实现至少一个接口 然后将使用JDK的动态代理。 所有的接口实现 由目标类型将代理。 如果目标对象不 实现任何接口然后CGLIB代理将被创建。 如果你想强迫使用CGLIB代理(例如, 代理的每个方法定义的目标对象,而不只是这些 通过其接口实现),你可以这样做。 然而,有 一些 问题需要考虑: 最后 方法不能被建议,因为他们 不能被覆盖。 Spring 3.2的,不需要添加CGLIB到你 项目类路径,CGLIB类下重新打包 org。 springframework和包括直接在spring核心 JAR。 这 意味着基于cglib代理支持'只是作品的相同的方式 JDK动态代理总是有。 你的代理对象的构造函数会调用两次。 这是一个自然的结果,即代理模型CGLIB 为每个代理生成子类对象。 对于每个代理 实例,两个对象的创建:实际的代理对象和一个 子类的实例实现的建议。 这种行为是 没有表现出在使用JDK代理。 通常,调 用构造函数 代理的类型两次,不是问题,因为通常只 作业发生,没有真正的逻辑实现 构造函数。 强制使用CGLIB代理设置 的价值 代理目标类 属性的 < aop:配置> 元素为真: 迫使CGLIB代理当使用@ aspectj火狐的一个插件的支持, 设置 “代理目标类的 属性的 < aop:aspectj火狐的一个插件> 元素 真正的 : 注意 多个 < aop:config / > 部分 倒塌成一个单一的统一汽车代理创造者在运行时,这 应用 最强 代理设置,任何 的 这个 < aop:config / > 部分(通常来自 不同的XML bean定义文件)指定。 这也适用于 < tx:注解驱动/ > 和 < aop:aspectj火狐的一个插件/ > 元素。 需要明确的是:使用“ 代理目标类= " true " ” 在 < tx:注解驱动/ > , < aop:aspectj火狐的一个插件/ > 或 < aop:config / > 元素将强制使用 CGLIB代理 对于所有三个人 。 9.6.1A理解AOP代理 Spring AOP是 基于代理的 。 这是至关 重要的是你掌握了语义的最后一个语句 其实就是在你写你自己的方面或使用任何 春天 提供的基于aop方面Spring框架。 考虑第一场景,你有一个普通的, 联合国代理,没有什么特殊之处,直接对象引用,因为 下面的代码片段所示。 public class SimplePojo implements Pojo { public void foo() { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 158/514 // this next method invocation is a direct call on the 'this' reference this.bar(); } public void bar() { // some logic... } } 如果你对某个对象调用方法的引用,该方法 调用 直接 在对象引用,可以 下面看到。 public class Main { public static void main(String[] args) { Pojo pojo = new SimplePojo(); // this is a direct method call on the 'pojo' reference pojo.foo(); } } 事情变化时稍加参考,客户端代码已经是 一个代理。 考虑下面的图和代码片段。 public class Main { public static void main(String[] args) { ProxyFactory factory = new ProxyFactory(new SimplePojo()); factory.addInterface(Pojo.class); factory.addAdvice(new RetryAdvice()); Pojo pojo = (Pojo) factory.getProxy(); // this is a method call on the proxy! pojo.foo(); } } 关键的是要理解是,客户端代码内部 这个 主要(. .) 的 主要 类 有一个引用代理吗 。 这意味着 上的方法调用该对象引用将呼吁 代理,和作为 这样的代理将能够代表所有的拦截器 (建议),相关的特定的方法调用。 然而,一旦 电话终于达到目标对象, SimplePojo 参考在这种情况下,任何方法 调用,它可能使在本身,如 这条() 或 这foo() ,将被调用的反对 这 参考, 不 代理。 这有 着重要的意义。 它 意味着自我调用是 不 要结果 相关的建议与方法调用获得机会 执行。 好吧,那么什么是必须要做的吗? 最好的方法( 最好是使用术语)是松散这里重构代码,这样 自动调用不会发生。 当然,这确实需要 一些工作 你的一部分,但它是最好的,至少侵入性的方法。 接下来的 方法绝对是可怕的,我几乎不愿点它 准确,因为它太可怕。 你可以(窒息!)完全的领带 在你的类的逻辑Spring AOP通过这样做: public class SimplePojo implements Pojo { public void foo() { // this works, but... gah! ((Pojo) AopContext.currentProxy()).bar(); } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 159/514 public void bar() { // some logic... } } 这完全夫妇Spring AOP代码, 和 它使类本身知道的事实 它被用于一个AOP上下文,苍蝇在面对AOP。 它还需要一些额外的配 置当代理正在 创建: public class Main { public static void main(String[] args) { ProxyFactory factory = new ProxyFactory(new SimplePojo()); factory.adddInterface(Pojo.class); factory.addAdvice(new RetryAdvice()); factory.setExposeProxy(true); Pojo pojo = (Pojo) factory.getProxy(); // this is a method call on the proxy! pojo.foo(); } } 最后,必须指出AspectJ没有这个 自动调用问题,因为它不是一个基于代理的AOP 框架。 9.7一个编程创建@ aspectj代理 除了声明方面在你的配置或者使用 < aop:配置> 或 < aop:aspectj火狐的一个插件> ,这也是可能的 以编程方式创建代理,建议 目标对象。 为 全面详细的Spring的AOP API,见下一章。 这里我们想 专注于能够自动创建代理使用@ aspectj 方面。 类 org.springframework.aop.aspectj.annotation.AspectJProxyFactory 可以用来创建一个代理为目标对象,建议由一个吗 或 多个@ aspectj切面。 对于这类基本用法很简单,如 下面的插图。 看到满信息。Javadocs // create a factory that can generate a proxy for the given target object AspectJProxyFactory factory = new AspectJProxyFactory(targetObject); // add an aspect, the class must be an @AspectJ aspect // you can call this as many times as you need with different aspects factory.addAspect(SecurityManager.class); // you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect factory.addAspect(usageTracker); // now get the proxy object... MyInterfaceType proxy = factory.getProxy(); 9.8一个使用AspectJ和Spring应用程序 一切我们已经覆盖到目前为止在这一章是纯Spring AOP。 在本节中,我们将看看如何使用AspectJ 编译器/韦弗取代,或者除 了,Spring AOP如果你需要去 超出了Spring AOP提供的设施独自。 春天的船舶小AspectJ方面库,它是可用的 独立的在你的分配 弹簧方面jar ,你将需要添加这个 到您的类路径中为了使用方面在 它。 SectionA 9 8 1,一个​​使用AspectJ依赖注入与域对象 Springa​​ 和 SectionA 9 8 2,一个​​其他弹簧方面​​AspectJa 讨论的内容 库,以及如何使用它。 SectionA 9 8 3,一个​​AspectJ方面使用Spring配置IoCa​​ 讨论了如何依赖注入AspectJ 方面编织使用 AspectJ编译器。 最后, SectionA 9 8 4,一个​​装入时编织与AspectJ在春季Frameworka​​ 介绍了装入时编织的 Spring应用程序 使用AspectJ。 9.8.1A使用AspectJ依赖注入与域对象 春天 Spring容器实例化bean中定义和配置 你的应用程序上下文。 也可以问一个bean工厂 配置一个 预先存在的 对象给定的名称 bean定义包含配置应用。 这个 弹簧方面jar 包含一个 注解驱动方面,使用这种能力允许 依赖注入的 任何对象 。 支持是 用于 创建的对象 以外的控制 任何容器 。 域对象往往属于这 类别,因为他们往往是通过编程方式创建使用 新 操作符,或由一个ORM 工具的结果 数据库查询。 这个 @Configurable 注释标志 一个类作为资格台弹力配置。 在最简单的 情况下可以使用它就像一个标记注释: package com.xyz.myapp.domain; import org.springframework.beans.factory.annotation.Configurable; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 160/514 @Configurable public class Account { // ... } 当用作标记接口,通过这种方式,弹簧将配置 注释类型的新实例( 帐户 在 这种情况下)使用bean定义(通常原型作用域) 完全限定 名称相同类型名称 ( com xyz myapp域帐户 )。 因为默认的 名字对于一个bean是完全限定名称的类型,方便 方法声明的原型 定义就是省略了 id 属性: 如果你想要显式地指定bean的名称。原型 定义来使用,你可以直接在注释: package com.xyz.myapp.domain; import org.springframework.beans.factory.annotation.Configurable; @Configurable("account") public class Account { // ... } 春天将寻找一个bean定义命名 “ 帐户 ”并使用它作为定义配置 新 帐户 实例。 您还可以使用自动装配避免必须指定一个 专用的bean定义在所有。 有弹簧应用自动装配 使用“ 自动装配 ”属性的 @Configurable 注释:指定要么 @Configurable(自动装配=自动装配的类型) 或 @Configurable(自动装配=自动装配的名字 对于 自动装配的类型或名称分别。 作为替代,截至 Spring 2.5最好是指定显式,注解驱动的 依赖注入你的 @Configurable bean使用 @ autowired 或 @ inject 在字段或方法水平(见 SectionA 5.9,一个​​configurationa​​基于注解的容器 为进一步的细 节)。 最后你可以使Spring依赖性检查对象 引用新创建和配置对象通过使用 dependencyCheck 属性(例如: @Configurable(自动装 配=自动装配的名字,dependencyCheck = true) )。 如果这个属性被设置为true,那么春天将验证后 配置,所有属性( 这不是原 语或 集合 )已经被建立。 使用注释本身并没有什么当然。 这是 AnnotationBeanConfigurerAspect 在 弹簧方面jar ,作用于 存在的注释。 在本质方面 说“回来后 从初始化一个新的对象类型的注释 @Configurable ,配置新 使用Spring创建的对象按照属性的 注释”。 在这种 背景下, 初始化 指 新实例化的对象(如。 ,对象实例 ” 新 '操作符),以及 可序列化的 对象,接受着 反序列化(如。 ,通过 readResolve() )。 注意 一个关键的短语在上面的段落是‘ 在 精华 ”。 大多数情况下,精确的语义 ” 回来后,初始化一个新的 对象 “会没事的…… 在这种背景下,“ 在 初始化 “意味着依赖项将 注入 在 对象已经构成- 这意味着依赖项将不 可以使用的 类的构造函数体。 如果你想要的依赖项 注入 之前 身体的构造函数执行, 从而可以在主体中使 用的构造函数,然后 您需要定义这个的 @Configurable 宣言像 所以: @Configurable(preConstruction=true) 你可以找到更多的信息关于该语言的语义 各种类型的切入点在AspectJ 在 这个附录 的 AspectJ 编程指南 。 对于这个工作带注释的类型必须与外人 AspectJ韦弗-您可以使用Ant或Maven构建时任务去做 这(见例如 AspectJ 开发环境导 )或装入时编织(见 SectionA 9 8 4,一个​​装入时编织与AspectJ在春季Frameworka​​ )。 这个 AnnotationBeanConfigurerAspect 本身需要 配置到春天(为了获得一个bean的引用 工厂也被用来配置新对象)。 如果你是 使用基于Java的配置简单的添加 @EnableSpringConfigured 任何 @ configuration 类。 @Configuration @EnableSpringConfigured public class AppConfig { } 如果你更喜欢基于XML的配置,春天 上下文 名称空间 定义了一个方便 背景:spring配置 元素: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 161/514 如果您正在使用DTD,而不是模式,相当于 定义是: 实例的 @Configurable 对象 创建 之前 方面已经配置将 结果在消息被发布到调试日志,没有配置 发生的对象。 一个例子可能 是一个bean在春季 配置域对象创建时初始化 春天。 在这种情况下,你可以使用“取决于”bean属性 手动指定bean取决于配 置 方面。 注意 不激活 @Configurable 处理通过bean configurer方面,除非你真的 意思是在运行时依赖其语义。 特别是, 确保 你不要使用 @Configurable 在bean类,注册为普通弹簧豆 容器:你会得到双倍的初始化,否则一旦 通 过容器一旦通过方面。 单元测试 @Configurable 对象 的一个目标 @Configurable 支持是使 独立的单元测试的域对象没有困难 硬编码查找有关。 如果 @Configurable 类型没有 被编织 通过AspectJ然后注释没有影响在单元测试, 你可以简单地设置模拟或存根属性引用的对象 根据试验和正常运作。 如果 @Configurable 类型 有 被编织的AspectJ然后你仍然可以 容器外部的单元测试是正常的,但你会看到 警告消息每次你构建一 个 @Configurable 对象表示它 尚未由Spring配置。 使用多种应用程序上下文 这个 AnnotationBeanConfigurerAspect 使用 来实现 @Configurable 支持 是一个AspectJ singleton方面。 一个单例的范 围方面是 相同的范围 静态 成员,也就是说 有一个方面实例类加载器,定义了每类型。 这意味着如果你定义内的多种应用程序上 下文 相同的类加载器层次结构,你需要考虑在哪里定义 @EnableSpringConfigured bean和在哪里 地方 弹簧方面jar 在 类路 径。 考虑一个典型的春天web配置共享 父应用程序上下文定义公共业务服务和 一切都需要支持他们,和一个孩子应用程序上下文 包 含特定于每个servlet定义的servlet。 所有的 这些上下文将并存在同一个类加载器层次结构, 所以 AnnotationBeanConfigurerAspect 只能 举行一个引用其中一个。 在这种情况下我们建议定义 这个 @EnableSpringConfigured bean的 共享(父)应用程序上下文:这个定义的服务 你可能会想注入到域对象。 一个后果是 那你 不能配置域对象的引用的豆子 定义在这个孩子(servlet特定)上下文使用 @Configurable机制(可能不是你想要做的 无论如 何!)。 当部署多个web应用程序在相同的容器, 确保每个web应用程序加载类型 弹簧方面jar 使用自己的 类加载器(例如,通过放置 弹 簧方面jar 在 “- inf / lib” )。 如果 弹簧方面jar 只有添加到吗 容器类路径(因此广泛加载共享父 类加载器),所有web应用程序 将共享相同的方面实例 这可能不是你想要的。 9.8.2A其他弹簧方面AspectJ 除了 @Configurable 方面, 弹簧方面jar 包含一个AspectJ方面,可以用来驱动弹簧的 事务管理的类型和的方法 transactional 注释。 这是 主要用于用户谁想要使用Spring框架的 事务支持Spring容器之外。 方面解释 transactional 注释是 AnnotationTransactionAspect 。 当使用这个 方面,你必须标注 实现 类 (和/或方法在这类), 不 这个 接口(如果有的话),类实现。 AspectJ遵循Java的 规则,接口是注解 不 继承 。 一个 transactional 注释 类指定缺省的事务语义的执行 任何 公共 操作类。 一个 transactional 注释 方法在类中重写默认的事务语义 给定类的注释(如果存在)。 方法 公共 , 保护 ,默认的 能见度可能都是 注释。 注释 保护 和默认的能见度方法直接是唯一的办法 事务界定为执行这些方法。 对AspectJ的程序员需要使用Spring配置 和事务管理的支持,但不愿(或不能)用 注释, 弹簧方面jar 还包含 文摘 方面,您可以扩展 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 162/514 提供自己的切入点定义。 看到的来源 AbstractBeanConfigurerAspect 和 AbstractTransactionAspect 方面更多 信息。 作 为一个例子,下面的摘录展示了如何 写一个方面配置的所有实例对象中定义的 域模型使用原型匹配的bean定义 完全限定类名: public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect { public DomainObjectConfiguration() { setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver()); } // the creation of a new bean (any object in the domain model) protected pointcut beanCreation(Object beanInstance) : initialization(new(..)) && SystemArchitecture.inDomainModel() && this(beanInstance); } 9.8.3A AspectJ方面使用Spring IoC配置 当使用AspectJ方面与Spring应用程序,它是自然的 希望和期待都可以配置等方面使用 春天。 AspectJ运行时本身负责方面创 造, 和手段的配置创建AspectJ方面通过弹簧 取决于AspectJ实例化模型( ” / xxx “条款)所使用的方面。 大多数的AspectJ方面 singleton 方面。 配置这些方面非常简单:简单地创建一个 bean定义引用类型作为正常的方面,包括 bean属性 “工厂方法= " aspectOf” 。 这 确保弹簧获得方面实例为它通过询问AspectJ 而不是试图创建一个实例本身。 例 如: 非单体方面很难配置:但是这是 可以通过创建原型bean定义和使用 @Configurable 支持 弹簧方面jar 配置 方面一旦bean实例 创建AspectJ 运行时。 如果你有一些@ aspectj切面,你想织与 AspectJ(例如,使用加载时编织为领域模型类型) 和其他@ aspectj切面,你想使用Spring AOP和 这些方面都是使用Spring配置的,那么您将需要 告诉Spring AOP @ aspectj自动代理支持这确切的子集 @ aspectj切 面的定义在配置应该用于 自动代理。 为此,您可以使用一个或多个 <包括/ > 元素在 < aop:aspectj火狐的一个插件/ > 宣言。 每个 <包括/ > 元素指定一个名称的模式, 和只有bean与名称相匹配的至少一个模式 用于Spring AOP火狐的一个插件配置: 注意 不要被误导的名称吗 < aop:aspectj火狐的一个插件/ > 元素:使用它 将会创建 Spring AOP 代理 。 @ aspectj风格的方面声明的只是 这里所用,但AspectJ运行时 不 涉及。 9.8.4A装入时编织与AspectJ在Spring框架 装入时编织(LTW)指的是过程的编织AspectJ 方面的到一个应用程序类文件,因为他们已被载入 Java虚拟机(JVM)。 本节的重点 是 配置和使用LTW在特定条件下的弹簧 框架:这部分不介绍LTW虽然。 全 的具体细节和配置LTW LTW只有AspectJ (与弹簧不 参与),请参阅 LTW 部分的AspectJ开发环境指南 。 Spring框架的增值,带给AspectJ LTW是 在启用多细粒度控制在编织的过程。 “香草”AspectJ LTW是影响使用Java(5 +)代理, 这是 开启通过指定一个VM参数当启动JVM。 这是 因此一项jvm设置,会很好,但是经常在某些情况下 是有点太粗。 春天使 LTW允许您打开LTW 在一个 每- 类加载器 基础上, 这显然是更细粒度和可以在一个更有意义吗 “单个jvm多个应用程序的环境 (如被发现在一个 典型的应用程序服务器环境)。 进一步的, 在某些 环境 ,这种支持使装入时编织 不作任何修改的应用程序服务器的 启动脚本 这将需要添加 -javaagent:/ / aspectjweaver.jar路径 或(我们稍后将讲述在这 部分) -javaagent:路径/去/ org.springframework.instrument - { version } . jar (原名 弹簧剂罐 )。 开发者仅仅修改 一个或多个文件,形成应用程序上下文加载时启用 编织而不是依靠管理员通常负责 的部 署配置如启动脚本。 现在推销结束了,让我们先步行通过 简单的例子AspectJ LTW使用Spring,紧随其后的是详细的 具体介绍了关于元素下面的例 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 163/514 子。 对于一个 完整的例子,请参阅宠物诊所 样品 应用程序。 第一个例子 让我们假设你是一个应用程序的开发人员已经 负责诊断一些性能问题的原因在 系统。 而不是打破一个分析工具,我们要 做的 是开关在一个简单的分析方面,它将使我们能够非常 很快得到一些性能指标,这样我们就可以申请一个 细粒度的分析工具,特定区 域立即 后来。 注意 本文的示例使用XML样式配置,它是 也可以配置和使用@ aspectj与 Java配置 。 特别是 @EnableLoadTimeWeaving 可以使用注释来代替 <上下文:加载时间韦弗/ > (见 低于 详情)。 这里是分析方面。 没什么新奇,只是一个 应急的基于时间的分析器,使用@ aspectj风格的 方面声明。 package foo; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Pointcut; import org.springframework.util.StopWatch; import org.springframework.core.annotation.Order; @Aspect public class ProfilingAspect { @Around("methodsToBeProfiled()") public Object profile(ProceedingJoinPoint pjp) throws Throwable { StopWatch sw = new StopWatch(getClass().getSimpleName()); try { sw.start(pjp.getSignature().getName()); return pjp.proceed(); } finally { sw.stop(); System.out.println(sw.prettyPrint()); } } @Pointcut("execution(public * foo..*.*(..))") public void methodsToBeProfiled(){} } 我们还将需要创建一个 ” meta - inf / aop xml 的文件,通知AspectJ 韦弗,我们想编织我们的 ProfilingAspect 为我们的类。 这个文件 公约,即存在一个文件(或文件)在Java 类路径称为“ meta - inf / aop xml ”是标准 AspectJ。 现在到spring特定部分的配置。 我们需要 配置一个 LoadTimeWeaver (所有 后来解释说,只是把它在信任现在)。 这个装载时 编织器 是必要的组件负责织造方面吗 配置在一个或者多个 meta - inf / aop xml ” 文件到您的应用程序中的类。 好的方面 是它 不需要大量的配置,如下所示(那里 有一些更多的选项,您可以指定,但这些都是详细的吗 后)。 现在,所有需要的工件到位——方面, “ meta - inf / aop xml 的文件,和春天 配置-,让我们创建一个简单的驱动程序类和一个 主要(. .) 方法来演示LTW在 行动。 package foo; import org.springframework.context.support.ClassPathXmlApplicationContext; public final class Main { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class); EntitlementCalculationService entitlementCalculationService = (EntitlementCalculationService) ctx.getBean("entitlementCalculationService"); // the profiling aspect is 'woven' around this method execution entitlementCalculationService.calculateEntitlement(); } } 还有最后一件事要做。 介绍本节 是说,一个可以打开LTW选择性地在吗 每- 类加载器 与Spring的基础,这是 真正的。 然而,对 于这个示例,我们将使用一个Java代理 (提供弹簧)开关在LTW。 这是命令行 我们将使用运行上面的 主要 类: java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main “ -javaagent “是一个Java 5 +的标志 指定和启用 代理 对仪器程序运行在JVM 。 春天 框架附带这样一个代理, InstrumentationSavingAgent ,这是包装 在 弹簧仪器jar 这 提供的值 -javaagent 参数在上面的例子中。 的输出执行 主要 程序将类似于下面。 (我已经介绍了 thread . sleep(. .) 声明到 calculateEntitlement() 实现, 分析器真正捕捉 其他东西比0毫秒- 这个 01234年 毫秒是 不 介绍了一种开销由AOP:)) Calculating entitlement StopWatch 'ProfilingAspect': running time (millis) = 1234 ------ ----- ---------------------------- ms % Task name ------ ----- ---------------------------- 01234 100% calculateEntitlement 因为这是使用AspectJ LTW影响全面的,我们不是 只是限于建议Spring bean;下面的轻微变化 在 主要 程序将产生相同的 结 果。 package foo; import org.springframework.context.support.ClassPathXmlApplicationContext; public final class Main { public static void main(String[] args) { new ClassPathXmlApplicationContext("beans.xml", Main.class); EntitlementCalculationService entitlementCalculationService = new StubEntitlementCalculationService(); // the profiling aspect will be 'woven' around this method execution entitlementCalculationService.calculateEntitlement(); } } 注意在上面的项目我们只是引导 Spring容器,然后创建一个新的实例 StubEntitlementCalculationService 完全 春天的上下文 之外…… 配置建议仍然会编织 在。 无可否认的是简单的…的例子 然而基本的 在春天的LTW支持都被介绍在上面的 例,这一节的其余部分将解释背后的“为什么” 每一位的详细配置和使用情况。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 165/514 注意 这个 ProfilingAspect 用于这 的例子可能是最基本的,但是它是非常有用的。 这是一个很好的例子 一个开 发方面,开发人员可以使用在 开发(当然),然后很容易排除从构建 的应用程序被部署到UAT或生产。 方面 你使用的方面在LTW必须AspectJ方面。 他们 可以写在要么AspectJ语言本身或者你可以写吗 你在@ aspectj风格方面。 后者 的选择当然是 只有一个选项,如果您使用的是Java 5 +,但这确实意味着你的 然后两方面有效的AspectJ 和 春天 AOP方面。 此 外,编译后的类需要方面 可以在类路径中。 ” meta - inf / aop xml ” AspectJ LTW基础设施配置使用一个或多个 ” meta - inf / aop xml 的文件,这是Java 类路径(或直接,或更通常在jar文件)。 结构和这个文件的内容是详细的在主 AspectJ参考文档,感兴趣的读者 称为 到该资源的 。 (我理解这部分是短暂的, 但“ aop xml “文件- - -有100% AspectJ 没有spring特定信息或语义,适用于它,所以 没有额外的值,我可以贡献或结果),所以 而不是重 复比较满意的部分,AspectJ 开发人员写的,我只是指导你。) 需要的库(jar) 至少你将需要以下库使用 Spring框架的支持AspectJ LTW: 1. spring aop jar (版本 2.5或更高版本,加上所有强制性依赖) 2. aspectjweaver.jar (版本1 6 8或更高) 如果你正在使用 弹簧提供代理 让仪器 ,你也将需要: 1. 弹簧仪器jar Spring配置 在春天的关键组件的LTW支持 LoadTimeWeaver 接口(在 org.springframework.instrument.classloading 包),和众多的实现 它的那艘船 春天分布。 一个 LoadTimeWeaver 负责添加一个或更多 java.lang.instrument.ClassFileTransformers 一个 类 加载器 在运行时,它可以打开门 各种各样的有趣的应用程序,其中一个发生的 LTW方面。 提示 如果你不熟悉这个想法的运行时类文件 转换,你是鼓励阅读Javadoc API 文档 java朗仪器 包在继续之前。 这不是一个巨大的繁琐,因为有 ——而恼人地——珍贵的小文档那里…… 关键 接口和类至少会被放置在你 的面前, 参考当你阅读本部分。 配置一个 LoadTimeWeaver 对于一个特定的 ApplicationContext 也很容易吗 添加一行。 (请注意,你几乎肯定会需要 是使用 一个 ApplicationContext 作为你的 Spring容器——通常是一个 BeanFactory 是不够的,因为 LTW的支持使得使用 BeanFactoryPostProcessors )。 使Spring框架的LTW支持,你需要 配置一个 LoadTimeWeaver ,这 通常是通过使用 @EnableLoadTimeWeaving 注释。 @Configuration @EnableLoadTimeWeaving public class AppConfig { } 或者,如果你喜欢,可以使用基于XML的配置 <上下文:加载时间韦弗/ > 元素。 注意 元素定义在“ 上下文 “名称空间。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 166/514 上面的配置将定义和注册一个号码的 自动为您LTW-specific基础设施bean,这样 作为一个 LoadTimeWeaver 和一个 AspectJWeavingEnabler 。 默认 LoadTimeWeaver 是 DefaultContextLoadTimeWeaver 类,它 装修一个自动检测的尝试 LoadTimeWeaver :确切的类型的 LoadTimeWeaver 那将是 “自动检测”是依赖于你的运行时环境 (概括如下表)。 为多9 1一个 DefaultContextLoadTimeWeaver LoadTimeWeavers 运行时环境 LoadTimeWeaver 实现 运行在 Bea的 Weblogic 10 WebLogicLoadTimeWeaver 运行在 IBM WebSphere Application Server 7 WebSphereLoadTimeWeaver 运行在 甲骨文的 OC4J OC4JLoadTimeWeaver 运行在 GlassFish GlassFishLoadTimeWeaver 运行在 JBoss AS JBossLoadTimeWeaver JVM始于春天 InstrumentationSavingAgent (java -javaagent:路径/去/弹簧仪器jar) InstrumentationLoadTimeWeaver 回退,期望底层的类加载器通常根据约定 (如适用 TomcatInstrumentableClassLoader 和 树脂 ) ReflectiveLoadTimeWeaver 注意,这些只是 LoadTimeWeavers 这是个 当使用 DefaultContextLoadTimeWeaver :它 当然是可以指定哪些 LoadTimeWeaver 实现,你 希望使用。 指定一个特定的 LoadTimeWeaver 与Java配置实现 LoadTimeWeavingConfigurer 接口和覆盖 这个 getLoadTimeWeaver() 方法: @Configuration @EnableLoadTimeWeaving public class AppConfig implements LoadTimeWeavingConfigurer { @Override public LoadTimeWeaver getLoadTimeWeaver() { return new ReflectiveLoadTimeWeaver(); } } 如果您使用的是基于XML的配置,你可以指定完全限定 类名的值的 韦弗类 ”属性 <上下文:加载时间韦弗/ > 元素: 这个 LoadTimeWeaver 这是 定义并登记的配置可以 后来恢复从Spring容器使用众所周知的名字 ” LoadTimeWeaver ”。 请记住, LoadTimeWeaver 存在只是作为一个 春天的机制基础设施LTW添加一个或多个 ClassFileTransformers 。 实际的 ClassFileTransformer 这是否就是LTW ClassPreProcessorAgentAdapter (从 org.aspectj.weaver.loadtime 包)类。 看到 Javadoc的类级 ClassPreProcessorAgentAdapter 类作进一步 细节,因为细节实际上是影响如何编织 超出了本节的范围。 最后还有一个属性配置左来讨论: “ aspectjWeaving ”属性 (或“ aspectj编织 如果您使用的是XML)。 这是一个简单的 属性 控制是否启用LTW与否,事情就是这么简单 为,。 它接受三种可能的值,总结如下, 用默认值如果属性不存在被 ” 自动检测 ” 9.2为多。 一个AspectJ编织属性值 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 167/514 注 释 值 XML 值 解释 启用 在 AspectJ编织,和方面将编织 在加载时适当。 禁用 掉 LTW是关闭的… 没有方面将编织在 加载时。 自动 检测 自动检 测 如果春天LTW基础设施可以找到 至少有一个“ meta - inf / aop xml 的文件,然后 AspectJ编织是打 开的,否则它是关闭的。这是默认的 值。 特定于环境的配置 最后这部分包含任何额外的设置和 配置,您将需要在使用Spring的LTW支持 环境如应用服务器和web容器。 tomcat apache tomcat 的默认的类装入器 不支持类转换这就是为什么Spring提供了一个增强的实现 地址这一需要。 命名 TomcatInstrumentableClassLoader 、装载机工作 在Tomcat 5.0及以上,可以分别登记的 每个 web应用程序 如下: Tomcat 6.0。 x或更高 1. 复制 org.springframework.instrument.tomcat.jar 进 $卡特琳娜家 / lib, $卡特琳娜家 代表的根 Tomcat安装) 2. 指导Tomcat使用自定义类加载器(相反 默认的)通过编辑web应用程序上下文 文件: Apache Tomcat 6.0。 x(类似于5 0 x / 5 5 x)系列支持多种上下文位置: 服务器配置文件—— 卡特琳娜家美元/ conf / server . xml 默认上下文配置- 卡特琳娜家美元/ conf /上下文xml —— 影响到所有部署web应用程序 应用程序配置,每网络可以部署在服务器端,要么 卡特琳娜家美元/ conf /[enginename]/[hostname]/[webapp]上 下文xml 或嵌入 这个web存档在 meta - inf /上下文xml 为了提高效率,嵌入式/ web应用程序配置风格 推荐的,因为它将只影响应用程序使用 自定义类加载器和不需要任何更 改服务器配置。 看到Tomcat . x 文档 为更多的细节关于可用的上下文位置。 Tomcat 5 0 x / 5 5倍 1. 复制 org.springframework.instrument.tomcat.jar 进 $卡特琳娜家 /服务器/ lib, $卡特琳娜家 代表的根 Tomcat的 安装。 2. 指导Tomcat使用自定义类加载器相反 默认的一个通过编辑web应用程序上下文 文件: Tomcat 5.0。 x和5.5。 x系列支持多种上下文位置: 服务器配置文件—— 卡特琳娜家美元/ conf / server . xml 默认上下文配置- 卡特琳娜家美元/ conf /上下文xml —— 影响到所有部署web应用程序 应用程序配置,每网络可以部署在服务器端,要么 卡特琳娜家美元/ conf /[enginename]/[hostname]/[webapp]上 下文xml 或嵌入 这个web存档在 meta - inf /上下文xml 为了提高效率,嵌入式web配置风格推荐 推荐的,因为它将只影响应用程序类装入器使用。 看到Tomcat 5. x 文档 为更 多的细节关于可用的上下文位置。 Tomcat版本5 5 20之前包含一些小问题 XML配置解析,避免使用 装载机 标签内 server . xml 配置,无论一个类 装载机 是指定或它是否为官方或一个自定义的 一。 看到Tomcat的bugzilla的 更多的 细节 。 在Tomcat 5.5。 x,版本5 5 20或以后,你应该设置 useSystemClassLoaderAsParent 到 假 来解决这个问题: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 168/514 这个设置是不需要在Tomcat 6或更高。 或者,考虑使用弹簧提供了通用的 虚拟机代理,必须在Tomcat启动脚本(见上图)。 这将使仪器可供所有已部署的web 的应用程 序,无论他们发生什么类加载器上运行。 应用服务器,WebSphere,OC4J,树脂,GlassFish、JBoss BEA WebLogic的最新版本(版本10以上),IBM WebSphere Application Server(version 7及以上), 甲骨文Java EE容器(OC4J 10.1.3.1版及以上),树脂(3.1以上)和JBoss(5。 x或以上) 提供一个类加载器,能够本地仪表。 春天的家乡LTW利用这样的类加载 器来启用AspectJ编织。 您可以启用LTW只需激活装入时编织 如前面所述。 具体地说,你做 不 需要修改启动脚本添加 - javaagent:路径/去/弹簧仪器jar 。 注意,GlassFish仪表能够只在类加载器它的耳朵环境。 对GlassFish的web应用程序,遵循Tomcat安装指令如前所述。 注意,在JBoss 6。 x,这个应用程序服务器扫描需要被禁用,以防止它加载类 在应用程序的真正开始。 一个快速的解决方法是增 加一个文件夹命名为工件 - inf / jboss扫描xml 包含以下内容: 泛型Java应用程序 当类仪表需要环境,不支持或 不支持现有的吗 LoadTimeWeaver 实现, 一个JDK剂可以是唯一的解决方案。 对于这种情况,春天 提供 InstrumentationLoadTimeWeaver , 这需要一个spring特定(但很一般)VM剂, org.springframework.instrument - { version } . jar (原名 弹簧剂罐 )。 使用它,您必须启动虚拟机与春天剂,通过 提供以下JVM选项: -javaagent:/path/to/org.springframework.instrument-{version}.jar 注意,这需要修改虚拟机启动脚本可能阻止你使用这个应用程序服务器 环境(取决于您的操作策略)。 此外,JDK代理将仪器的 整 个 虚拟机可以证明昂贵的。 由于性能原因,推荐使用这个配置只有如果你的目标环境 (如 Jetty )没有(或不支持)专用LTW。 9.9进一步资源 更多的信息在AspectJ可以找到 AspectJ网站 。 这本书 Eclipse AspectJ 艾德里安·Colyer等。 艾尔。(addison - wesley,2005)提供了一个全面的介绍和 AspectJ语言参考。 这本书 AspectJ在行动 Ramnivas Laddad由 (曼宁,2003)是强烈推荐的,这本书的焦点是在 AspectJ,但很多一般AOP主题探索 (在一些深度)。 10。 一个Spring AOP api 10.1一个介绍 前面的章节描述了Spring 2.0和以后的版本的 支持AOP使用@ aspectj和基于方面的定义。 在 这一章中,我们讨论了低层 Spring AOP api和AOP 支持Spring 1.2的应用程序中使用。 为新的应用程序,我们 建议使用Spring AOP支持2.0及以后版本中 所描述的 前一章,但当工作与现有应用程序,或者当 阅读书籍和文章,你可能遇到Spring 1.2风格的例子。 Spring 3.0是Spring 1.2向后兼容和一切 在本章中介绍是完全支持Spring 3.0中。 10.2一个切入点API在春天 让我们看看如何处理关键切入点春天 概念。 10.2.1A概念 春天的切入点模型支持独立的切入点的重用 建议类型。 可以使用同样的目标不同的建议 切入点。 这个 org.springframework.aop.Pointcut 接口 是中央接口,用于目标建议特定的类 和方法。 完整的界面如下所示: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 169/514 public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); } 分裂 切入点 接口 分为两部分允许重用类和方法匹配部分,和 细粒度组成操作(如执行一个“联盟” 另一种方法匹配器)。 这个 ClassFilter 接口用于 限制切入点来一组给定的目标类。 如果 matches() 方法总是返回true,所有目标 类将匹配: public interface ClassFilter { boolean matches(Class clazz); } 这个 MethodMatcher 接口是 通常更重要。 完整的界面如下所示: public interface MethodMatcher { boolean matches(Method m, Class targetClass); boolean isRuntime(); boolean matches(Method m, Class targetClass, Object[] args); } 这个 匹配(方法、类) 方法用于 测试这个切入点是否会匹配一个给定的方法对一个目标 类。 这种评价可以执行一个AOP代理创 建时, 避免需要一个测试在每个方法调用。 如果2参数 匹配方法返回true对于一个给定的方法 isRuntime() MethodMatcher 方法返回 诚然,3参数匹配方法将在每个方法调用 调用。 这使得一个切入点来看看参数传递给 方法调用之前立即目标建议 执 行。 最MethodMatchers是静态的,这意味着他们的 isRuntime() 方法返回false。 在这种情况下, 3参数匹配方法永远不会被调用。 提示 如果可能的话,尽量使切入点的静态,允许AOP 框架来缓存结果评价的切入点当AOP 创建代理。 10.2.2A操作切入点 Spring支持操作切入点:值得注意的是, 联盟 和 路口 。 联盟意味着方法,要么切入点匹配。 十字路口意味着方法这两个切入点 匹配。 联盟通常是更有用的。 切入点可以组合使用静态方法 org.springframework.aop.support.Pointcuts 类,或使用 ComposablePointcut 类 同一个 包。 然而,使用AspectJ切入点表达式 通常是一个更简单的方法。 10.2.3A AspectJ切入点表达式 因为2.0,最重要类型的切入点用春天 org.springframework.aop.aspectj.AspectJExpressionPointcut 。 这是一个切入点,使用 AspectJ提供的库来解析一个 AspectJ切入点表达式字符串。 看到前面一章讨论支持AspectJ 切入点原语。 10.2.4A便利切入点实现 Spring提供了一些方便的切入点实现。 一些 可以用现成的,其他人是打算再在吗 特定于应用程序的切入点。 静态切入点 静态的切入点是基于方法和目标类, 不能考虑到方法的参数。 静态的切入点是 足够—— 和最好的 ——对于大多数用途。 这是 可能的春天要评估一个静态的切入点只有一次,当一个 第一次被调用的方法:在那之后,没有需要评估 切入点又与每个方法调用。 让我们考虑一些静态的切入点实现包括 与春天。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 170/514 正则表达式的切入点 一个明显的方式来指定静态的切入点是常规 表达式。 几个AOP框架除了弹簧使这个 可能的。 org.springframework.aop.support.JdkRegexpMethodPointcut 正则表达式是一个通用的切入点,使用正常吗 表达式支持 JDK 1.4 +。 使用 JdkRegexpMethodPointcut 类, 你可以提供一个列表的模式字符串。 如果任何这些是一个 匹配,切入点将评估为true。 (所以结果是 有效的结合这些切入点。) 使用如下所示: .*set.* .*absquatulate Spring提供了一个方便的类, RegexpMethodPointcutAdvisor ,这允许我们 还引用一个建议(请记住,一个建议可以是一个 拦截 器,在建议,抛出建议等)。 在幕后, 弹簧将使用一个 JdkRegexpMethodPointcut 。 使用 RegexpMethodPointcutAdvisor 简 化了布线, 一个bean封装两个切入点和通知,如图所示 下图: .*set.* .*absquatulate RegexpMethodPointcutAdvisor 可以用 任何意见类型。 属性驱动的切入点 一个重要的类型的静态的切入点是一个 元数据驱动的 切入点。 这种使用价值 元数据属性:通常,源代码级别的元数据。 动态切入点 动态的切入点是昂贵的比静态的评价 切入点。 他们考虑的方法 参数 以及静态信息。 这 意味着他们必须评估每个方法调用; 结果不能被缓存的,作为参数会有所不同。 主要的例子是 控制流 切入点。 控制流的切入点 弹簧控制流的切入点在概念上类似于 AspectJ cflow 切入点,虽然不 强大的。 (目前还没有方法能够指定一个切入点 执行以下 一个连接点匹配另一个切入点。) 一个控制 流切入点匹配当前调用堆栈。 例如,它可能 火如果连接点是调用一个方法 com mycompany web 包,或由 SomeCaller 类。 控制流的切入点是 指定使用 org.springframework.aop.support.ControlFlowPointcut 类。 注意 控制流的切入点是更昂贵的 在运行时评估甚至比其他动态的切入点。 在Java 1.4,成本大约是5次,其他的动 态 切入点。 10.2.5A切入点超类 Spring提供了有用的切入点超类来帮助你 实现自己的切入点。 因为静态的切入点是最有用的,你可能会子类 StaticMethodMatcherPointcut,如下所示。 这需要实现 只是一个抽象方法(尽管 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 171/514 它可以覆盖其他 方法自定义行为): class TestStaticPointcut extends StaticMethodMatcherPointcut { public boolean matches(Method m, Class targetClass) { // return true if custom criteria match } } 还有超类动态的切入点。 您可以使用自定义切入点的任何意见类型在Spring 1.0 RC2及以上。 10.2.6A定制切入点 因为切入点在Spring AOP是Java类,而不是 语言特性(如AspectJ)可以声明自定义 切入点,无论是静态或动态。 自定义切入点在 春天可以 任意复杂的。 然而,使用AspectJ切入点表达式 语言是建议如果可能的话。 注意 后来版本的春天可能提供支持”语义 切入点”作为提供江淮:例如,“所有的方法,改变 实例变量在目标对 象。” 建议在春天10.3 API 现在让我们看看如何Spring AOP处理建议。 10.3.1A建议生命周期 每个建议是Spring bean。 一个实例可以共享的建议 在所有建议的对象,或独特的每个建议的对象。 这 对应 每个类 或 每个 建议。 每个类的建议是最常用的。 它是适合通用 建议如事务顾问。 这些不依赖于状态的 这个代理对象或添加新的状态;他们只是行 为的方法和 参数。 每个建议适合的介绍,来支持 mixin。 在这种情况下,建议增加国家的代理 对象。 它可以使用一个混合的共享和每个建议 同样的AOP代理。 在春天10.3.2A建议类型 弹簧提供几个建议类型的盒子,是 可扩展支持任意类型的建议。 让我们看看基本 概念和标准建议类型。 拦截around通知 最基本的建议类型在春天 拦截around通知 。 春天是符合AOP联盟界面左右 建议使用方法拦截。 MethodInterceptors实施 在建议应该实现以下接口: public interface MethodInterceptor extends Interceptor { Object invoke(MethodInvocation invocation) throws Throwable; } 这个 MethodInvocation 参数 invoke() 方法暴露了方法 调用;目标连接点;AOP代理;和参数 该方法。 这个 invoke() 方法应该 返回 调用的结果:返回值的连接点。 一个简单的 MethodInterceptor 实现 看起来如下: public class DebugInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("Before: invocation=[" + invocation + "]"); Object rval = invocation.proceed(); System.out.println("Invocation returned"); return rval; } } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 172/514 注意调用MethodInvocation的 proceed() 法。 这个顺序从上到下 拦截器链向连接点。 大多数拦截器将 调用此方法,并返回 它的返回值。 然而,一个 MethodInterceptor,像任何around通知,可以返回一个不同的 值或者抛出一个异常,而不是调用 proceed方法。 然而,你不想这样做没有好理由! 注意 与其他AOP MethodInterceptors提供互操作性 联盟兼容的AOP实现。 其他的建议类型 讨论在本节的其 余部分实现常见的AOP 的概念,但在一个spring特定方式。 虽然有一个优势 在使用最具体的建议类型,坚持 MethodInterceptor 在你可能会建议,如果想要运行在另一个方面 AOP框架。 注意,切入点目前不互操作 框架之间,AOP联盟目前尚未定义 切入点的接口。 建议之前 一个简单的建议类型是 之前 建议 。 这并不需要一个 MethodInvocation 对象,因为它只会 进入方法之前调用。 主要的优势的建议是,之前是没有必要的 调用 proceed() 方法,因此也没有 可能无意中未能进行了拦截 链。 这个 MethodBeforeAdvice 界面显示 下面。 (春天的API设计将允许对字段之前的建议, 虽然通常的对象适用于现场拦截和它 的 不太可能会实现它,春天)。 public interface MethodBeforeAdvice extends BeforeAdvice { void before(Method m, Object[] args, Object target) throws Throwable; } 注意,返回类型 无效 。 建议之前 可以插入自定义行为在连接点之前执行,但不能 改变返回值。 如果之前建议抛出一个异常,这 个 将中止进一步执行拦截器链。 异常 将传播支持拦截器链。 如果不加以控制,或 在签名的调用方法时,它将直接传递给 客户 端,否则将被包装在一个未经检查的异常的 AOP代理。 之前的一个例子建议在春天,它统计所有的方法 调用: public class CountingBeforeAdvice implements MethodBeforeAdvice { private int count; public void before(Method m, Object[] args, Object target) throws Throwable { ++count; } public int getCount() { return count; } } 提示 之前的建议可以被用于任何切入点。 抛出建议 抛出建议 之后才调用 返回的连接点如果连接点抛出一个异常。 弹簧提供类型抛出的建议。 注意,这意味着 org.springframework.aop.ThrowsAdvice 接口并 不包含任何方法:它是一个标记接口,确认了 给定对象实现一个或多个类型的 抛出的建议方法。 这些 应该是在形式的: afterThrowing([Method, args, target], subclassOfThrowable) 只有最后一个参数是必需的。 方法签名可能 要么一个或四个参数,取决于建议吗 方法是感兴趣的方法和参数。 以下 类的实例 抛出的建议。 下面的建议是如果一个调用 RemoteException 抛出(包括 子类): public class RemoteThrowsAdvice implements ThrowsAdvice { public void afterThrowing(RemoteException ex) throws Throwable { // Do something with remote exception } } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 173/514 以下的建议是如果一个调用 ServletException 抛出。 与 以上建议,它声明4参数,以便它可以使用 调用方法,方法参数和目标对 象: public class ServletThrowsAdviceWithArguments implements ThrowsAdvice { public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) { // Do something with all arguments } } 最后的示例说明了这两种方法可以 用在一个类,它可以处理 RemoteException 和 ServletException 。 任何数量的抛出的建议 方法可以合并成一个类。 public static class CombinedThrowsAdvice implements ThrowsAdvice { public void afterThrowing(RemoteException ex) throws Throwable { // Do something with remote exception } public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) { // Do something with all arguments } } 注意: 如果一个抛出建议方法抛出一个 异常本身,它将覆盖原来的异常(即改变 抛出的异常给用户)。 压倒一切的异常将 通常是 一个RuntimeException;这是兼容任何方法 签名。 然而,如果一个抛出建议方法会抛出一个检查 例外,它必须匹配目标的声明的 例外 方法,因此在某种程度上耦合到特定目标的方法 签名。 不抛出未申报检查异常 这是不符合目标方法的 签名! 提示 抛出的建议可以被用于任何切入点。 回国后的建议 回国后在春天的建议必须实现 org.springframework.aop.AfterReturningAdvice 界面,如下所示: public interface AfterReturningAdvice extends Advice { void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable; } 回国后的建议已经获得返回值( 它不能修改),调用方法,方法参数和 目标。 以下所有成功后返回的建议项 方法调用,没有抛出的异常: public class CountingAfterReturningAdvice implements AfterReturningAdvice { private int count; public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable { ++count; } public int getCount() { return count; } } 这个建议不改变执行路径。 如果它抛出一个 例外,这将被扔了拦截器链代替 返回值。 提示 回国后的建议可以被用于任何切入点。 介绍的建议 春天把介绍的建议当成一种特殊的 拦截的建议。 介绍需要一个 IntroductionAdvisor , 和一个 IntroductionInterceptor ,实现 以下界面: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 174/514 public interface IntroductionInterceptor extends MethodInterceptor { boolean implementsInterface(Class intf); } 这个 invoke() 方法继承了AOP 联盟 MethodInterceptor 接口必须实现 简介:也就是说,如果被调用的方法是在一个介绍 接口, 介绍拦截器负责处理 方法调用——它不能调用 proceed() 。 介绍的建议不能用于任何切入点,因为它 仅适用于在类中,而不是方法,水平。 你只能使用 介绍与建议 IntroductionAdvisor , 这 有以下方法: public interface IntroductionAdvisor extends Advisor, IntroductionInfo { ClassFilter getClassFilter(); void validateInterfaces() throws IllegalArgumentException; } public interface IntroductionInfo { Class[] getInterfaces(); } 没有 MethodMatcher ,和 因此没有 切入点 ,相关 介绍的建议。 只有类过滤是合乎逻辑的。 这个 getInterfaces() 方法返回 接口引入的这个顾问。 这个 validateInterfaces() 方法是在公司内部使用, 看是否可以实现介绍了接口的配置 IntroductionInterceptor 。 让我们看一个简单的例子从弹簧测试套件。 让我们 假设我们想要介绍以下接口与一个或多个 对象: public interface Lockable { void lock(); void unlock(); boolean locked(); } 这说明了一个 Mixin 。 我们 希望能够投建议可锁定的对象,无论他们的 类型和调用方法锁定和解锁。 如果我们调用lock()方 法, 我们希望所有的setter方法抛出 LockedException 。 因此我们可以添加一个方面 提供了能够使对象不变的,没有他们有 任 何对它的认识:一个很好的例子,AOP。 首先,我们需要一个 IntroductionInterceptor 这确实沉重的 提升。 在本例中,我们扩展了 org.springframework.aop.support.DelegatingIntroductionInterceptor 方便的类。 我们可以实现 IntroductionInterceptor 直接,但使用 DelegatingIntroductionInterceptor 最适合最 情况下。 这个 DelegatingIntroductionInterceptor 是 用来代表一个介绍到实际实现的 引入界面(s)、隐匿使用拦截来做 所以。 委托可 以设置为任何对象使用一个构造函数 参数,默认的代表(当使用不带参数的构造器) 这是。 因此在下面的例子中,代表了 LockMixin 子类的 DelegatingIntroductionInterceptor 。 给定一个代表 (默认情况下本身),一个 DelegatingIntroductionInterceptor 实例看起来 对于所有接口实现的代表(除了 IntroductionInterceptor),并将支持对任何 介绍 他们的。 它的子类可能如 LockMixin 调用 suppressInterface(类intf) 方法抑制 接口不应该暴露。 然而,无论多少 接口 一个 IntroductionInterceptor 准备 支持, IntroductionAdvisor 使用将 控制接口实际上是暴露。 一个介绍界面 将隐瞒任何 实现相同的接口的 目标。 因此LockMixin子类 DelegatingIntroductionInterceptor 并实现了 可锁定的本身。 超类自动回升,可锁定的 可以支持的介绍, 所以我们不需要指定。 我们可以引入任意数量的接口在这种方式。 注意使用 锁 实例变量。 这有效地增加了额外的状态来了在目标 对象。 public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable { private boolean locked; public void lock() { this.locked = true; } public void unlock() { this.locked = false; } public boolean locked() { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 175/514 return this.locked; } public Object invoke(MethodInvocation invocation) throws Throwable { if (locked() && invocation.getMethod().getName().indexOf("set") == 0) throw new LockedException(); return super.invoke(invocation); } } 通常并不需要覆盖 invoke() 方法: DelegatingIntroductionInterceptor 实现—— 这调用委托方法如果方法介绍,否则吗 向着 连接点——通常是足够的。 在 目前情况下,我们需要添加一个检查:不可以调用setter方法 如果在锁定模式。 需要的是简单的介绍顾问。 所有需要做的 是举行一个截然不同的 LockMixin 实例,并指定 介绍了接口的——在这种情况下,只 是 可封闭的 。 一个更复杂的例子可能需要 引用介绍拦截器(这将是定义为一个 原型):在这种情况下,没有配置相关的 LockMixin ,所以我们简单地创建它使用 新 。 public class LockMixinAdvisor extends DefaultIntroductionAdvisor { public LockMixinAdvisor() { super(new LockMixin(), Lockable.class); } } 我们可以把这个顾问非常简单:它不需要 配置。 (然而,这 是 必要的: 不可能使用一个 IntroductionInterceptor 没有 IntroductionAdvisor )。 像往常一样同 介绍,该顾问必须每个,因为它是有状态的。 我们 需要一个不同的实例 LockMixinAdvisor ,和 因此 LockMixin ,对于每个建议的对象。 这个 顾问由部分建议对象的状态。 我们可以把这个顾问通过编程,使用 Advised.addAdvisor() 法,或(推荐的 在XML配置,就像任何其他顾问。 所有代理创建 选择 下面讨论,包括“自动代理的创造者”,正确 处理介绍和有状态的混合。 10.4一个顾问API在春天 在春天,一个顾问是一个方面,它只包含一个 建议与对象关联一个切入点表达式。 除了特殊情况的介绍,任何顾问可以 用于任何建议。 org.springframework.aop.support.DefaultPointcutAdvisor 是最常用 的顾问类。 例如,它可以使用 一个 MethodInterceptor , BeforeAdvice 或 ThrowsAdvice 。 它是可能的混合顾问和咨询类型在春天在相同的 AOP代理。 例如,您可以使用一个拦截around通知,抛出 建议和在一个代理配 置之前建议:春天将 自动创建必要的拦截器链。 10.5一个使用ProxyFactoryBean创建AOP代理 如果你使用Spring IoC容器(一个ApplicationContext或 为你的业务对象BeanFactory)——和你应该! ——你会 想要使用 Spring的AOP FactoryBeans之一。 (请记住,一个工厂 介绍了一个间接层bean,使它创建的对象 不同的类型)。 注意 Spring 2.0 AOP支持下还使用工厂bean 覆盖。 基本的方法来创建一个AOP代理在春天是使用 org.springframework.aop.framework.ProxyFactoryBean 。 这给完全控制切 入点和通知,将申请, 和它们的顺序。 然而,有简单的选项是可取的 如果你不需要这样的控制。 10.5.1A基础知识 这个 ProxyFactoryBean 像其他弹簧 FactoryBean 实现,介绍一个级别的 间接寻址。 如果你定义一个 ProxyFactoryBean 与 名称 foo ,什么对象引用 foo 看到的并不是 ProxyFactoryBean 实例本身,而是一个对象 创造的 ProxyFactoryBean 的实施 这 个 getObject() 法。 该方法将创建一个 AOP代理包装一个目标对象。 最重要的优点之一,使用 ProxyFactoryBean 或另一个奥委会知道类来创建 AOP代理,是这意味着建议和切入点也可以 国际奥委 会所管理的。 这是一个强大的功能,使某些方法 这是很难实现与其他AOP框架。 例如,一个 建议参考应用程序对象本身(除了目 标, 这应该可以在任何AOP框架),受益于所有的吗 可插入性提供了依赖注入。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 176/514 10.5.2A JavaBean属性 同大多数 FactoryBean 实现提供弹簧, ProxyFactoryBean 类本身就是一个JavaBean。 它的 属性用来: 指定目标你想代理。 指定是否使用CGLIB(见下文和也 SectionA 10 5 3,一个​​JDK -和proxiesa​​cglib的基础 )。 一些关键属性是继承 org.springframework.aop.framework.ProxyConfig (父类的所有AOP代理工厂在春天)。 这些关键 属 性包括: proxyTargetClass : 真正的 如果目标类进行代理,而非目标类的 接口。 如果这个属性值设置为 真正的 ,然后CGLIB代理将 被创建(参见 也 SectionA 10 5 3,一个​​JDK -和proxiesa​​cglib的基础 )。 优化 :控制是否或不是 积极优化应用于代理 创建 通过CGLIB 。 人们不应轻率地使用此设置 除非一个人完全了解有关AOP 代理处理 优化。 这是目前仅用于CGLIB代理;它 没有效应与JDK动态代理。 冻 :如果一个代理配置 冻 ,然后更改配置都没有 不再允许。 这是有用的作为一个轻微的优化和 这些情况下当你不想让来电 者能够操纵 代理(通过 建议 接口) 在代理已经创建。 此属性的默认值 是 假 ,所以变化如添加额外的 的建议是允许的。 exposeProxy :决定是否或不是 目前的代理应该是公开的 ThreadLocal 所以,它可以访问的 目标。 如果一个目标需要获得 代理和 exposeProxy 属性设置为 真正的 ,目标可以使用 AopContext.currentProxy() 法。 其他属性具体到 ProxyFactoryBean 包括: proxyInterfaces :字符串数组,数组的接口 的名字。 如果这不是提供,CGLIB代理的目标类 将被使用(但亦见吗 SectionA 10 5 3,一个​​JDK -和proxiesa​​cglib的基础 )。 interceptorNames :字符串数组的 顾问 ,拦截器或其他建议 名称适用。 的顺序是重要的,在第一次名列第一 服务为基础。 也就是说,在列表中第一个拦截器 将会是第一个可以截取调用。 这个名字是bean名称在当前的工厂,包括 从祖先工厂bean名称。 你不能提到bean 这里引用自这样做会导致 ProxyFactoryBean 忽略singleton 设置的建议。 你可以添加一个拦截器的名字与一个星号 ( * )。 这将导致所有的应用 顾问bean与名称开始的前一部分的星号 应用。 使 用这个特性的一个示例中可以找到 SectionA 10 5 6,一个​​使用“全球化”advisorsa​​ 。 单例:工厂是否应该返回一个单一的 对象,不管多久 getObject() 方法被调用。 几个 FactoryBean 的实现提供了这样一个 方法。 默认值是 真正的 。 如果你想要使用有状态的建议——对 例,对有状态的混合——使用原型建议加上 单件价值 假 。 10.5.3A JDK -和cglib建立代理 这部分作为明确的文档上 ProxyFactoryBean 选择创建一个要么 一个JDK -和基于cglib代理特定目标对象(这是 是代理)。 注意 的行为 ProxyFactoryBean 与 关于创建JDK -或cglib基于代理之间变化 版本1.2。 x和2.0的春天。 这个 ProxyFactoryBean 现在展览相似的语义 关于接口的自动的 TransactionProxyFactoryBean 类。 如果类的目标对象,进行代理(以下简称 简单地称为目标类)不实现任何 接口,然后基于cglib代理将被创建。 这是 简单的情况,因 为代理是接口基于JDK和没有 接口意味着JDK代理不可能。 一个简单的插头在 目标bean,并指定拦截器的列表通过 interceptorNames 财产。 注意,一个基于cglib 代理将创建即使 proxyTargetClass 财产的 ProxyFactoryBean 已经设置为 假 。 (很明显这毫无意义,是最好的 将bean定义,因为它是在最好的冗余,在 糟糕的混乱。) 如果目标类实现一个(或多个)接口,那么 类型的代理创建依赖于配置的 ProxyFactoryBean 。 如果 proxyTargetClass 财产的 ProxyFactoryBean 已经设置为 真正的 ,那么将创建基于cglib代理。 这 是有意义的,是符合最 少意外原则。 即使 proxyInterfaces 财产的 ProxyFactoryBean 已经被设置为一个或多个 完全限定的接口名称,这一事实 proxyTargetClass 属性设置为 真正的 将 导致基于cglib 代理在效应。 如果 proxyInterfaces 财产的 ProxyFactoryBean 已经被设置为一个或多个 完全限定的接口名称,然后代理将被创建为基础的 jdk。 创建的代理将执行所有的接口都是 指定的 proxyInterfaces 房地产; 目标类实现一个整体发生更多的接口比 指定的 proxyInterfaces 房地产,这是 很好但这些额外的接口将不会 实现返回的代理。 如果 proxyInterfaces 财产的 ProxyFactoryBean 已经 不 被设置,但目标类 并实现一个(或 更多) 接口,那么 ProxyFactoryBean 将自动检测这一事实吗 目标类并实现至少一个接口和一个 代理将被创建为基础的jdk。 接口,实际上是 代 理将 所有 的接口 目标类实现;实际上,这是仅仅供应一样 一个列表的每个接口,目标类实现 这个 proxyInterfaces 财产。 然而, 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 177/514 它是 更少的工作,并且更不容易输入错误。 10.5.4A代理接口 让我们看一个简单的例子 ProxyFactoryBean 在行动。 这个例子 包括: 一个 目标bean 那将是代理。 这 是“personTarget“bean定义在下面的例子。 一个顾问和拦截器用来提供建议。 AOP代理bean定义指定目标对象( personTarget bean)和接口代理,以及 建议申请。 myAdvisor debugInterceptor 注意, interceptorNames 财产需要 字符串列表:bean的名称或顾问的拦截器 目前的工厂。 顾问,拦截器,前,后返回和 建议可以 使用抛出对象。 顾问的排序是 显著的。 注意 你也许想知道为什么这个列表并不持有bean 引用。 原因是,如果ProxyFactoryBean的 单例属性设置为 false,它必须能够返回 独立代理实例。 如果任何顾问本身就是一个 原型,一个独立的实例需要返回,所以它 的 必要的能够获得的一个实例的原型 工厂;持有一个参考不是足够的。 “人”bean定义可以使用上面的地方 人的实现,如下所示: Person person = (Person) factory.getBean("person"); 在相同的奥委会其他bean上下文可以表达一个强类型 依赖它,像一个普通的Java对象: 这个 PersonUser 类在这个例子会 揭露一个属性的类型的人。 至于它的关注,AOP 代理可以使用透明地在地方“真实”的人 实现。 然而,它的类将是一个动态代理类。 它 可能丢给吗 建议 接口 (下面讨论)。 它可以隐藏目标和代理之间的区别 使用一个匿名 内在bean ,如下所示。 只有 ProxyFactoryBean 定义是不同的,建议 是只包 括的完整性: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 178/514 myAdvisor debugInterceptor 这个的优点是只有一个类型的对象 人 :如果我们希望防止有用的用户 应用程序上下文获取一个引用联合国建议的对象, 或需要 避免歧义与Spring IoC 自动装配 。 也可以说是一个优势 的定义是独立的。ProxyFactoryBean 然而,有 有时能够获得联合国 建议的目标吗 工厂可能实际上是一个 优势 :对于 例,在特定的测试场景。 10.5.5A代理类 如果你需要代理类,而不是一个或更多 接口? 想象一下,在我们的例子中,没有 人 接口:我们需要建议一个类称为 人 没有实现任何业务接口。 在这种情况下,您可以配置弹簧 使用CGLIB代理,而 比动态代理。 只要设置 proxyTargetClass 属性为true的上面ProxyFactoryBean。 虽然最好 程序接口,而 不是类的能力,建议 实现接口的类不可能是有用的在处理 遗留代码。 (一般来说,春天不是规定性的。 虽然它使它 容易申请好实 践,它避免了迫使一个特定的 方法。) 如果你想,你可以强制使用CGLIB在任何情况下,即使 如果你有接口。 CGLIB代理作品通过生成目标类的一个子类 在运行时。 弹簧配置这个生成子类来委派方法 调用原始目标:子类是用来实现 装饰 模式,编织的建议。 CGLIB代理一般应该是透明的,用户。 然而, 有一些问题需要考虑: 最后 方法不能被建议,因为他们 不能被覆盖。 没有需要添加到classpath CGLIB。 像春天的 3.2,CGLIB被重新包装,包括在spring核心JAR。 在 句话说,cglib基于AOP将 工作“开箱即用”的会做的一样 JDK动态代理。 有小的性能区别,CGLIB代理 动态代理。 在Spring 1.0中,动态代理是稍快。 然而,这可能会改变在未来。 性能不应该 决定性的 考虑在这种情况下。 10.5.6A使用“全球”顾问 通过附加一个星号,拦截器的名字,所有的顾问与 bean名称匹配前面的部分星号,将被添加到 顾问链。 这可以派上用场,如果你需 要添加一组标准 “全球”顾问: global* 10.6一个简洁的代理定义 特别是当定义事务代理,你可能最终得到 许多类似的代理定义。 父母和孩子的使用bean 定义,以及内心的bean定义,可以导致 更加简洁 和更简洁的代理定义。 首先一个家长, 模板 ,bean的定义是 创建的代理: PROPAGATION_REQUIRED 这将永远不会被实例化本身,所以实际上可能 不完整的。 然后每个代理需要创建bean只是个孩子 定义,它将目标的代理作为一 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 179/514 个内部bean 定义,因为目标永远不会被用在它自己的 不管怎样。 当然可能覆盖属性从父 模板,如在这种情况下,事务传播 设置: PROPAGATION_REQUIRED,readOnly PROPAGATION_REQUIRED,readOnly PROPAGATION_REQUIRED,readOnly PROPAGATION_REQUIRED 注意,在上面的例子中,我们有明确标志着父母 bean定义为 文摘 通过使用 文摘 属性,描述 以前 ,所以它可能 实际上不是曾经被 实例化。 应用程序上下文(但不是简单的 bean工厂)将默认情况下预实例化所有单件。 这是 因此重要的(至少对单例bean),如 果你有一个 (父)bean定义你想只使用作为模板, 这个定义指定了一个类,您必须确保设置 文摘 属性 真正的 , 否则,应用程序上下 文实际上会尝试预实例化 它。 10.7一个AOP代理以编程方式创建与ProxyFactory 它很容易使用Spring AOP代理以编程方式创建。 这 使您能够使用Spring AOP没有依赖Spring IoC。 以下清单显示了创建一个代理为目标对象, 与一个拦截器和一个顾问。 接口实现的 目标对象将自动代理: ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); factory.addAdvice(myMethodInterceptor); factory.addAdvisor(myAdvisor); MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy(); 第一步是构建一个对象的类型 org.springframework.aop.framework.ProxyFactory 。 你可以 创建这个与目标对象,在上面的 示例中,或指定 接口是在另一个构造函数,代理 您可以添加建议(拦截器作为一种专业类型的建议) 和/或顾问,和操纵他们的终身ProxyFactory。 如果你添加一个 IntroductionInterceptionAroundAdvisor,可以导致代理 实现其他接口。 也有方便的方法在ProxyFactory(被继承 AdvisedSupport ),这允许您添加其他建议 类型如之前和抛出的建议。 AdvisedSupport是父类的 两ProxyFactory和ProxyFactoryBean。 提示 AOP代理创建集成与IoC框架是最好的 实践在大多数应用程序。 我们建议你具体化 配置从Java代码使用 AOP,如一般。 10.8一个操纵建议对象 然而你创建AOP代理,你可以操纵他们使用 org.springframework.aop.framework.Advised 接口。 任何AOP代理可以转换为 这个接口,接口的任何其他 实现了。 这个接口包括以下方法: Advisor[] getAdvisors(); void addAdvice(Advice advice) throws AopConfigException; void addAdvice(int pos, Advice advice) throws AopConfigException; void addAdvisor(Advisor advisor) throws AopConfigException; void addAdvisor(int pos, Advisor advisor) throws AopConfigException; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 180/514 int indexOf(Advisor advisor); boolean removeAdvisor(Advisor advisor) throws AopConfigException; void removeAdvisor(int index) throws AopConfigException; boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; boolean isFrozen(); 这个 getAdvisors() 方法将返回一个顾问 对于每一个顾问,拦截器或其他建议,已添加到类型 工厂。 如果你添加了一个顾问,顾 问在这个索引返回 将您添加的对象。 如果你添加了一个拦截器或其他 建议类型,弹簧将包裹这在一个顾问与切入点 总是返回 true。 因此如果你添加了一个 MethodInterceptor ,返回这个索引advisor 将是一个 DefaultPointcutAdvisor 返回你的 MethodInterceptor 和一个切入点匹配所有 类和方法。 这个 addAdvisor() 方法可以用来添加任何 顾问。 通常顾问控股切入点和通知将是 通用 DefaultPointcutAdvisor ,可以使用 任何建议或切入点(但不是介绍)。 默认情况下,它可以添加或删除顾问或拦截器 即使已经创建一个代理。 唯一的限制是,它是 无法添加或删除一个介绍顾问,因为 现有的代理 从工厂将不会显示界面变化。 (你可以获得一个新的 代理从工厂来避免这个问题。) 一个简单的例子,铸造一个AOP代理 建议 接口和检查和处理 建议: Advised advised = (Advised) myObject; Advisor[] advisors = advised.getAdvisors(); int oldAdvisorCount = advisors.length; System.out.println(oldAdvisorCount + " advisors"); // Add an advice like an interceptor without a pointcut // Will match all proxied methods // Can use for interceptors, before, after returning or throws advice advised.addAdvice(new DebugInterceptor()); // Add selective advice using a pointcut advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice)); assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length); 注意 这是怀疑它是可取的(没有双关) 修改一个业务对象的建议在生产,虽然没有 怀疑合法使用案例。 然而,它可 以是非常有用的 发展:例如,在测试。 我有时会发现它非常 有用的能够添加测试代码的形式,一个拦截器或 其他建议,进入一个方法调用我想测试。 ( 例,建议可以在一个事务创建的 方法:例如,运行SQL检查数据库是 正确的 更新之前,标志着对回滚事务。) 这取决于你如何创建代理,您通常可以设置一个 冻 旗,在这种情况下 建议 isFrozen() 方法将 返回true,任何试图修改建议通过添 加或删除 将导致一个 AopConfigException 。 能够 冻结的状态建议对象有用的在某些情况下,对于 例,以防止调用代码删除安 全拦截器。 它可能 也可用于Spring 1.1允许积极优化如果运行时 建议修改是不需要知道。 10.9一个使用“汽车代理”设施 到目前为止,我们已经被认为是显式创建的AOP代理使用 ProxyFactoryBean 或类似的工厂bean。 春天还允许我们使用“汽车代理“bean定义,它可以 自动代理选定的bean定义。 这是建立在春天 “豆后置处理程序”基础设 施,允许修改任何 作为容器加载bean定义。 在这个模型中,您将设置一些特殊bean定义XML bean定义文件来配置自动代理的基础设施。 这 允许您声明的目标符合汽车代 理:你 不需要使用 ProxyFactoryBean 。 有两种方法可以做到这一点: 使用一个自动代理的创造者,是指特定的豆子在 当前上下文。 一个特殊情况的汽车代理创建,值得 单独考虑;汽车代理创建源代码级别的驱动 元数据属性。 10.9.1A火狐的一个插件bean定义 这个 org.springframework.aop.framework.autoproxy 包提供了以下标准汽车代理的创造者。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 181/514 BeanNameAutoProxyCreator 这个 BeanNameAutoProxyCreator 类是一个 BeanPostProcessor 自动创建AOP 代理bean与名称匹配的文字值或 通配符。 myInterceptor 与 ProxyFactoryBean ,有一个 interceptorNames 财产而不是一个列表的 拦截器,允许正确行为的原型顾问。 命名 “拦截 器”可以顾问或任何类型的建议。 与汽车代理一般来说,主要的点使用 BeanNameAutoProxyCreator 是应用相同的吗 一直到多个对象的配置,以最小的体积的 配置。 这是一个受欢迎的选择申请声明 交易到多个对象。 Bean定义的名字匹配,如“jdkMyBean”和 “onlyJdk”在上面的例子中,是与普通的旧bean定义 目标类。 AOP代理将被自动 创建 BeanNameAutoProxyCreator 。 同样的建议 应用到所有匹配的豆子。 注意,如果使用顾问(相当 比拦截器在上面的例子 中),可以申请的切入点 不同不同的豆子。 DefaultAdvisorAutoProxyCreator 一个更一般的和非常强大的汽车代理创造者 DefaultAdvisorAutoProxyCreator 。 这将 自动应用合格的顾问在当前背景下,没 有 需要包括特定bean的名字在汽车代理顾问的 bean定义。 它提供了相同的价值一致的配置 和避免重复作为 BeanNameAutoProxyCreator 。 使用此机制包括: 指定 DefaultAdvisorAutoProxyCreator bean 定义。 指定任意数量的顾问在同一或相关 上下文。 注意,这些 必须 是顾问, 不只是拦截器或其他建议。 这是必要的,因为 必须有 一个切入点来评估、检查合格证的 每个建议候选人bean定义。 这个 DefaultAdvisorAutoProxyCreator 将 自动评价包含在每个顾问的切入点,看看 什么(如果有的话)的建议应该适用于每个 业务对象(如 “businessObject1”和“businessObject2”的例子)。 这意味着任何数量的顾问可以应用 自动为每个业务对象。 如果没有切入点的 顾问的任何方法相匹配的业务对象,该对象将不会 是代理。 作为bean定义添加新的业务对象, 他们将会被自动代理如果必要的。 一般自动代理的优势使它 不可能获得一个调用者或依赖项联合国建议的对象。 调用getBean(“businessObject1”)在这个 ApplicationContext将 返回一个AOP代理,而不是目标业务对象。 (“内在豆” 成语前面显示还提供这种好处。) 这个 DefaultAdvisorAutoProxyCreator 非常 有用的,如果你想应用相同的建议始终对许多 业务对象。 一旦基础设施定义就 位, 你可以简单的添加新的业务对象不包括特定的 代理的配置。 你也可以减少在额外的方面非常 很容易——例如,跟踪或性能 监控方面, 最小的变化来配置。 DefaultAdvisorAutoProxyCreator提供支持的过滤 (使用一个命名约定,以便只有特定的顾问 评估,允许使用多个不同的配置, AdvisorAutoProxyCreators在同一个工厂)和排序。 顾问 可以实现 org.springframework.core.Ordered 接口,以确保正确的 订购,如果这是一个问题。 这个 TransactionAttributeSourceAdvisor用在上面的例子中有一个 可配置的秩序价值;默认设置是 无序的。 AbstractAdvisorAutoProxyCreator 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 182/514 这是DefaultAdvisorAutoProxyCreator的超类。 你 可以创建自己的汽车代理创作者通过子类化这类,在吗 这是不大可能发生 的顾问定义提供不足 定制的行为的框架 DefaultAdvisorAutoProxyCreator 。 10.9.2A使用元数据驱动的汽车代理 一个特别重要的类型的汽车代理是由 元数据。 这就形成了一个类似的编程模型来。net ServicedComponents 。 而不是定义 元数据 在XML描述符、事务管理和配置 其他企业服务是关押在源代码级别的属性。 在本例中,您使用 DefaultAdvisorAutoProxyCreator ,结合 顾问,理解元数据属性。 元数据的细节 在切入点中举行的部分候选 人顾问,而不是 汽车代理创建类本身。 这是真正的一个特例 DefaultAdvisorAutoProxyCreator ,但是值得 考虑自己的。 (元数据知道代码是在切入点 中包含的顾问, 而不是AOP框架本身。) 这个 /属性 JPetStore目录的 示例应用程序展示了使用属性驱动的汽车代理。 在这种情况下,不需要使用 TransactionProxyFactoryBean 。 简单地定义 事务属性在业务对象是足够的,因为 使用元数据清楚的切入点。 该bean定义包 括 下面的代码,在 / - inf / declarativeServices.xml 。 注意,这是通用的,可以用JPetStore外: 这个 DefaultAdvisorAutoProxyCreator bean 定义(这个名字并不重要,因此它甚至可以省略) 将接所有合格的切入点在当前应 用程序上下文。 在这种情况下,“transactionAdvisor“bean定义,类型 TransactionAttributeSourceAdvisor ,将适用于 类或 方法携带事务属性。 这个 TransactionAttributeSourceAdvisor取决于TransactionInterceptor, 通过构造函数依赖。 示例解 决这个通过自动装配。 这个 AttributesTransactionAttributeSource 取决于 的实现 org.springframework.metadata.Attributes 接口。 在 这个片段,“属性”bean,用满足这个雅加达 共享属性的API来获取属 性信息。 (应用程序 代码必须被编译使用共享属性编译 任务。) 这个 /注释 JPetStore目录的 示例应用程序包含了一个类似的例子,汽车的代理 由JDK 1.5 +注释。 下面的配置使 Spring的自 动检测 事务性 注释,导致隐性代理bean包含这个 注释: 这个 TransactionInterceptor 这里定义取决于 在一个 PlatformTransactionManager 定义,它是 不包括在这个通用的文件(尽 管它可以),因为它将 特定于应用程序的事务需求(通常是 JTA,因为在这个例子中,或Hibernate,JDO或者JDBC): 提示 如果你只需要声明式事务管理、使用 这些通用的XML定义会导致弹簧自动 代理所有的类或方法与事务属 性。 你不会 需要直接处理AOP,和编程模型是相似的 到那的。 净ServicedComponents。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 183/514 这种机制是可扩展的。 它可以做到自动代理 基于自定义属性。 你需要: 定义您的自定义属性。 指定一个顾问与必要的建议,包括 触发的切入点,存在的自定义属性 在一个类或方法。 你可以使用现有的建议, 只是实现一 个静态的切入点,拿起自定义 属性。 有可能这样的顾问是不同的每个建议类 (例如,混合):他们只是需要被定义为原型, 而非单例,bean定义。 例如, LockMixin 介绍 从春天拦截 测试套件,如上图所示,可以结合使用一个 属性驱动的切入点来目标mixin,如下所示。 我们使用 通用 DefaultPointcutAdvisor 、配置使用 JavaBean属性: 上面的 交换() 调用目标的变化 可切换的bean。 客户持有一个引用bean将 不知道这种改变,但是会立即开始触及新 目标。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 184/514 尽管这个例子并没有添加任何建议,它不是 需要添加的建议使用 TargetSource —— 课程任何 TargetSource 可以一起使用吗 具有任意的建议。 10.10.2A池目标源 使用池目标源提供了一个类似的编程模型 与无状态会话ejb,池相同的实例 的维护,将免费对象方法调用的 池。 一个关键的区别SLSB春池,池 那个春天池可以应用到任何一个POJO。 与春天在 一般,这个服务可以被应用于一种非侵入性的方 法。 Spring提供了开箱即用的支持Jakarta Commons池 1.3,它提供了一个相当有效的池实现。 你会 需要在您的应用程序共享池 Jar的类路径中使用这个 特性。 它也可以子类 org.springframework.aop.target.AbstractPoolingTargetSource 支持任何其 他池API。 示例配置如下所示: ... properties omitted 请注意,目标对象——“businessObjectTarget” 例子—— 必须 是一个原型。 这允许 PoolingTargetSource 实现来创建新 的 目标的实例来种植池是必要的。 看到javadoc 对于 AbstractPoolingTargetSource 和具体的 您希望使用子类的属性信 息:“最大容量” 是最基本的,总是会出现。 在这种情况下,“myInterceptor”是一个拦截器,该拦截器的名称 需要定义在相同的奥委会上下文。 然而,它不是 有必要指定 拦截器使用池。 如果你只想要 池,没有其他建议,不要设置interceptorNames产权 所有。 可以配置弹簧以便能够施放 池对象的 org.springframework.aop.target.PoolingConfig 接口,它公开了信息和当前的配置 池 的大小通过介绍。 您需要定义一个 顾问: 这个顾问是通过调用一个方便的方法获得的 AbstractPoolingTargetSource 类,因此使用 MethodInvokingFactoryBean。 这 个顾问的名字(“poolConfigAdvisor” 这里)必须列表中的拦截器的名字在ProxyFactoryBean 暴露的合并对象。 演员将如下所示: PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); System.out.println("Max pool size is " + conf.getMaxSize()); 注意 池无状态的服务对象通常没有必要。 我们 不相信它应该是默认的选择,因为大多数无状态 对象自然是线程 安全的,和实例池是有问题的 如果资源被缓存。 简单的连接池是可以使用汽车代理。 这是可能的 设置TargetSources使用任何自动代理的创造者。 10.10.3A原型目标源 建立一个“原型”目标源类似于一个池 TargetSource。 在这种情况下,一个新实例将创建的目标 在每个方法调用。 虽然在创 建一个新对象的成本 不高在现代JVM,连接的成本的新对象 (满足其奥委会依赖性)可能更昂贵。 因此你 不应该用这种方法没有 很好的理由。 要做到这一点,您可以修改 poolTargetSource 上面所示的定义如下。 (我也改变了名字,为了清晰。) 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 185/514 只有一个属性:目标bean的名称。 继承是用于TargetSource实现确保 一致的命名。 与池目标源、目标bean 必须是一个原型 的bean定义。 10.10.4A ThreadLocal 目标来源 ThreadLocal 目标来源是有用的如果 你需要一个对象被创建为每个传入请求(每个线程 这是)。 的概念 ThreadLocal 提供一个 jdk宽设施来透明地存储资源与一个线程。 设置一个 ThreadLocalTargetSource 相当 一样多的被解释为其他类型的目标 来源: 注意 有严重的问题(比如ThreadLocals)引用(可能导致 内存泄漏)当不正确的使用多线程和 多类加载器环境。 一 个人应该总是考虑包装一个 在一些其他类和threadlocal从来没有直接使用 ThreadLocal 本身(除了当然的 包装类)。 同样,一个人应该永远记住要正确设置和 扰乱(后者只需要调用 threadlocal集(空) )资源的局部 线 程。 取消在任何情况下应做自不取消它 可能会导致问题行为。 Spring的ThreadLocal支持 这是否对你和 应该总是被认为是支持使用吗 没有其他合适的处理代码(比如ThreadLocals)引用。 10.11一个定义新的 建议 类型 Spring AOP设计是可扩展的。 而拦截 实现策略是目前在内部使用,它是可能的 支持任意的建议类型除了开箱即用 拦截 around通知,之前,抛出建议和回国后 建议。 这个 org.springframework.aop.framework.adapter 包是一个SPI包允许支持新的自定义类型的建议 被添加在不改变核心框 架。 唯一约束在一个 定制 建议 类型是必须的 实现 org.aopalliance.aop.Advice 标记接口。 请参考 org.springframework.aop.framework.adapter 包的 Javadocs为进一步的信息。 10.12进一步资源 请参阅示例应用程序的Spring为进一步的例子 Spring AOP的: 的默认配置的JPetStore演示了使用 TransactionProxyFactoryBean 声明性 事务管理。 这个 /属性 JPetStore目录的 演示了使用声明式事务的属性驱动 管理。 11.测试 11.1一个介绍弹簧测试 测试是不可分割的一部分,企业软件开发。 这 章主要是在国际奥委会的增值原则 单元测试 和春天的好处 框架的支持 集成 测 试 。 (彻底治疗的测试在企业 超出了本文的范围参考手册。) 11.2一个单元测试 依赖注入应该让你的代码更少的依赖 集装箱比传统的Java EE开发。 pojo的 ,您的应用程序应该在JUnit测试或者TestNG测试, 简单的实例化对象使用 新 运营商, 没有弹簧或其他容器 。 您可以使用 模拟对象 (结合其他 有价值的测试技术来测试您的代码) 隔离。 如果你遵循 春天的架构建议,由此产生的清洁分层 你的代码库和组件将促进单元测试更加容易。 例如,您可以测试服务 层对象存根或嘲笑道 或存储库的接口,而不需要访问持久数据而 运行单元测试。 真正的单元测试通常运行得非常快,因为没有 运行时基础设施设置。 强调真正的单元测试的一部分 你的开发方法将提高你的生 产率。 你可能不需要 这部分的测试章节帮助你编写有效的单元测试 对于你的国际奥委会建立应用程序。 对于某些单元测试场 景, 然而,Spring框架提供了以下模拟对象和 测试支持类。 11.2.1A模拟对象 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 186/514 环境 这个 org.springframework.mock.env 包 包含模拟的实现 环境 和 PropertySource 抽象介绍 Spring 3.1(见 SectionA 3.3,一 个​Abstractiona​​​环境 和 SectionA 3.4,一个​​PropertySource Abstractiona​​ )。 MockEnvironment 和 MockPropertySource 是有用的对于发展中 容器外 测试代码,取决于 与环境相关的属性。 JNDI 这个 org.springframework.mock.jndi 包 包含一个实现JNDI SPI,您可以使用它来设置 一个简单的JNDI环境测试套件或独立 的应用程序。 如果,例如,JDBC 数据源 年代得到绑定到 相同的JNDI名称在测试代码在一个Java EE容器,你可以 两个应用程序 重用代码和配置在测试场景 没有修改。 Servlet API 这个 org.springframework.mock.web 包 包含一组全面的Servlet API模拟对象,针对 使用Spring的Web MVC框架,它是有用 的为测试Web 上下文和控制器。 这些模拟对象通常更 方便的使用比动态模拟对象如 EasyMock 或现有的Servlet API mock 对象如 MockObjects 。 Portlet API 这个 org.springframework.mock.web.portlet 包包含一组Portlet API模拟对象,针对使用 与Spring MVC框架的Portlet。 11.2.2A单元测试支持类 通用实用工具 这个 org.springframework.test.util 包 包含 ReflectionTestUtils ,这是一个 基于映像的实用方法的集合。 开发者使用这些 方 法在单元和集成测试场景中,他们需要 设置一个非 公共 字段或调用一个 非 公共 setter方法在测试应用程序 代码包括,例如: ORM框架如JPA和Hibernate,宽恕 私人 或 保护 领域 访问,而不是 公共 setter方法 属性在一个域的实体。 Spring的支持等注释 @ autowired , @ inject ,和 @ resource, 提供依赖 注入 私人 或 保护 字段,setter方法, 配置方法。 Spring MVC 这个 org.springframework.test.web 包 包含 ModelAndViewAssert ,您可以使用它在 结合JUnit,TestNG,或其他任何测试框 架为单位 测试处理Spring MVC ModelAndView 对象。 单元测试Spring MVC控制器 测试你的Spring MVC 控制器 年代,使用 ModelAndViewAssert 结合 MockHttpServletRequest , MockHttpSession 等等的 org.springframework.mock.web 包。 11.3一个集成测试 11.3.1A概述 重要的是能够执行一些集成测试 不需要部署到应用程序服务器或连接 其他企业的基础设施。 这将使您能够测试这样的东西 为: 正确的布线的Spring IoC容器上下文。 数据访问使用JDBC或一个ORM工具。 这将包括 事情的正确性,SQL语句,Hibernate查询,JPA 实体映射等。 Spring框架提供了一流的支持集成 测试在 弹簧测试 模块。 实际的名字JAR文件可能包括发行版本 ,也可能是长期的 org.springframework.test 形式,这取决于你在哪里 把它从(请参阅 部分 依赖管理 对于一个解释)。 这个库包含 这个 org.springframework.test 包,其中包含 有价值的类集成测试与Spring容器。 这 测试并不依赖于一个应用程序服务器或其他 部署 环境。 这样的测试是运行慢比单元测试,但快得多 比同等的仙人掌测试或远程测试,依靠部署 到一个应用程序服务器。 在Spring 2.5和以后,单元测试和集成测试支持 的形式提供了注解驱动的 春天还是和TestContext框架 。 这个 还是和 TestContext框架是不可知论者的实际测试框架在使用中, 从而使仪器的测试在各种环境中包括 JUnit,TestNG,等等。 JUnit 3.8支持已被弃用 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 187/514 Spring 3.0的,遗留JUnit 3.8基类的层次结构(例如, AbstractDependencyInjectionSpringContextTests , AbstractTransactionalDataSourceSpringContextTests , 等)是正式弃用,将被删除在后面的版本。 任何测 试类基于这段代码应该迁移到 春天还是和TestContext 框架 。 在Spring 3.1,JUnit 3.8基类在春季 还是和TestContext框架(即, AbstractJUnit38SpringContextTests 和 AbstractTransactionalJUnit38SpringContextTests ) 和 @ExpectedException 一直 正式弃用,将被删除 在后面的版本。 任何测试 类基于此代码应该迁移到JUnit 4或者TestNG 提供支持 春天 还是和 TestContext框架 。 同样,任何测试的方法 @ExpectedException 应该修改为 使用内置的支持在JUnit和 预期的异常 TestNG。 11.3.2A目标的集成测试 Spring的集成测试支持具有以下主要 目标: 管理 Spring IoC 集装箱缓存 在测试执行。 提供 依赖 注入测试夹具实例 。 提供 事务 管理 适合集成测试。 供应 spring特定基类 ,帮助开发人员在编写集成测试。 接下来的几个小节描述每个目标并提供链接 实现和配置细节。 上下文管理和缓存 春天还是和TestContext框架提供了一致的加载 春天 ApplicationContext 年代和 WebApplicationContext 年代以及缓存 这 些上下文。 支持缓存加载上下文很重要, 因为启动时间可以成为一个问题一个​​不是因为开销 春天的本身,而是因为对象实例化 的春天 集装箱需要时间来实例化。 例如,一个项目与50 100的Hibernate映射文件可能需要10到20秒加载 映射文件,导致成本 运行每个测试之前在 每一个测试夹具导致较慢的整体测试运行,减少 开发人员的生产力。 测试类通常声明数组要么 资源位置 对于XML配置元数据的一个​​ 经常在类路径中一个​​或一组 注释 类 这是用来配置应用程序。 这些 位置或类是一样的或类似的规定 web . xml 或其他部署配置 文件。 默认情况下,加载后,配置的 ApplicationContext 重用对于每个 测试。 因此安装成本发生只有一次每个测试套件, 随后的测试 执行速度更快。 在这种背景下,这个词 测试套件 意味着所有的测试运行在相同的JVM的一个​​ 例如,所有的测试运行从一个 Ant、Maven或构建一Gradle 给定的项目或模块。 在不太可能的情况下,一个测试腐蚀 应用程序上下文,需要重新加载一个​​例 如,通过修改 bean定义或一个应用程序对象的状态一个​​还是和TestContext 框架可以配置为重新加载配置和重建 应用程序上下 文在执行下一个测试。 看到 一个​章节​上下文managementa​​ 和 一个​章节​上下文cachinga​​ 还是和TestContext与 框架。 依赖注入的测试夹具 当加载应用程序还是和TestContext框架上下文,它 可以选择配置您的测试类实例通过依赖 注射。 这提供了一种方便的机制设 置测试 夹具使用预配置的bean从您的应用程序上下文。 一个 强大的好处在于,您可以重用应用程序上下文跨越 不同的测试场 景(如。 ,用于配置spring管理对象 图形、事务代理, 数据源 年代, 等),从而避免了需要复制复杂的测试夹具设置 对个人的测试 用例。 作为一个例子,考虑的情况我们上课, HibernateTitleRepository ,实现了数据 访问逻辑为 标题 域的实体。 我们希望 编写集成 测试,测试以下领域: Spring配置:基本上,就是一切有关 的配置 HibernateTitleRepository bean正确和 礼物吗? Hibernate映射文件配置:就是一切映射 正确,正确的延迟加载设置 地方吗? 的逻辑 HibernateTitleRepository :不同的配置 这个类的实例执行如预期的吗? 看到依赖注入的测试夹具的 还是和TestContext框架 。 事务管理 一个常见的问题在测试中,访问一个真正的数据库是他们的 影响持久性存储的状态。 甚至当你使用一个 开发数据库,更改状态 可能会影响未来的测试。 同时,许多操作一个​​如插入或修改一个​​持久数据 不能被执行(或验证)外的事务。 还是和TestContext框架解决了这个问题。 默认情况下, 框架将创建和回滚事务为每个测试。 你 简单地编写代码,可以假定已 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 188/514 经存在一个事务。 如果你 调用代理对象在你的测试以事务的方式,他们将采取的行动 正确,根据其配置的事务语义。 在 另外,如 果一个测试方法删除选中的内容表, 在事务管理的运行测试,事务 将回滚在默认情况下,数据库将会回到它的状态吗 执行测试之 前。 事务支持提供一个 测试通过 PlatformTransactionManager bean 定义在测试的应用程序上下文。 如果你想要一个事务提交一个​​不寻常,但偶尔 当你想要一个特别有用的测试来填充或修改 数据库一个​​还是和TestContext框架 可以指示导致 事务提交,而不是通过回滚 @TransactionConfiguration 和 @Rollback 注释。 看到事务管理的 还是和TestContext框架 。 支持类的集成测试 春天还是和TestContext框架提供了几个 文摘 支持类,简化编写的 集成测试。 这些基础的测试类提供定义良好的钩子 成测试 框架以及方便的实例变量和 方法,使您能够访问: 这个 ApplicationContext ,执行 明确的bean查找或测试上下文的状态作为一个 整个。 一个 JdbcTemplate 为执行SQL 语句来查询数据库。 这样的查询可以被用来 确认数据库状态都 之前 和 在 执行数据库相 关的应用程序 代码,和弹簧确保这样的查询运行的范围 相同的事务作为应用程序代码。 当结合使用 与一个ORM工具,一定 要避免 假 阳性 。 此外,您可能想要创建自己的定制, 应用程序范围的超类与实例变量和方法具体 你的项目。 看到支持类 还是和TestContext 框架 。 11.3.3A JDBC测试支持 这个 org.springframework.test.jdbc 包 包含 JdbcTestUtils ,这是一个收集的 JDBC相关实用功能旨在简化标准数据库 测试场 景。 注意, AbstractTransactionalJUnit4SpringContextTests 和 AbstractTransactionalTestNGSpringContextTests 提供便 利的方法,委托 JdbcTestUtils 在内部。 这个 spring jdbc 模块提供支持 配置和启动嵌入式数据库可以使用 集成测试与数据库进行交互。 有关详细信息,请参见 SectionA 14.8,一个​supporta​​​嵌入式数据库 和 SectionA 14 8 8,​​测试数据访问逻辑与​​嵌入式数据库 。 11.3.4A注释 弹簧测试注释 Spring框架提供了以下组 spring特定 注释,可以使用 你的单元测试和集成测试结合还是和TestContext 框架。 参考各自的 Javadoc为进一步的信息, 包括默认属性值,属性别名,等等。 @ContextConfiguration 定义了类级元数据,用于确定如何 加载和配置一个 ApplicationContext 集成 测试。 具体地说, @ContextConfiguration 声明 要么 应用程序上下文资源 位置 或 带注释的 类 这将用于加载 上下文。 资源位置通常是XML配置文件 位于类路径;然而,带注释的类通常 @ configuration 类。 然而, 资源位置也可指文件在文件 系统, 带注释的类可以组件类等。 @ContextConfiguration("/test-config.xml") public class XmlApplicationContextTests { // class body... } @ContextConfiguration(classes = TestConfig.class) public class ConfigClassApplicationContextTests { // class body... } 作为一种替代或除了声明资源 位置或注释的类, @ContextConfiguration 可用于 申报 ApplicationContextInitializer 类。 @ContextConfiguration(initializers = CustomContextIntializer.class) public class ContextInitializerTests { // class body... } @ContextConfiguration 可能 有选择地用于声明 ContextLoader 策略。 注意, 然而,你通常不需要显式地配置 装载机因 为默认加载程序支持要么资源 位置 或注释 类 以及 初始化 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 189/514 @ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class) public class CustomLoaderXmlApplicationContextTests { // class body... } 注意 @ContextConfiguration 提供支持 继承 资源 位置或配置类以及上下文初始化 默认的超类声明。 看到 一个​章节​上下文managementa​​ 和 javadoc的 @ContextConfiguration 对于 进一步的细节。 @WebAppConfiguration 一个类级别注释,用于声明 ApplicationContext 加载一 集成测试应该是一个 WebApplicationContext 。 仅仅 存在 @WebAppConfiguration 在一个 确保测试类 WebApplicationContext 将加载 对于测试,使用默认值 “文件:src / main / webapp” 对于根的路径 web应用程序的(即。 , 资源基础 路径 )。 资源的基本路径是在幕后使用 创建一个 MockServletContext 提供 随着 ServletContext 为测试的 WebApplicationContext 。 @ContextConfiguration @WebAppConfiguration public class WebAppTests { // class body... } 以覆盖默认,指定一个不同的基础资源 路径通过 隐式 价值 属性。 两 类路径: 和 文件: 资源 前缀是支持的。 如果没有资源 前缀是提供了路径 被认为是一个文件系统资源。 @ContextConfiguration @WebAppConfiguration("classpath:test-web-resources") public class WebAppTests { // class body... } 注意, @WebAppConfiguration 必须结合使用 @ContextConfiguration ,无论是在 一个测试类或在一个测试类层次结 构。 看到 javadoc的 @WebAppConfiguration 对于 进一步的细节。 @ContextHierarchy 一个类级别注释,用于定义的层次结构 ApplicationContext 年代的集成 测试。 @ContextHierarchy 应该 声明一个列表 的一个或多个 @ContextConfiguration 情况下,每 其中定义了一个水平层次的上下文。 以下 示例演示使用 @ContextHierarchy 在单个 测试类;然而, @ContextHierarchy 也可以使用 在一个测试类层次结构。 @ContextHierarchy({ @ContextConfiguration("/parent-config.xml"), @ContextConfiguration("/child-config.xml") }) public class ContextHierarchyTests { // class body... } @WebAppConfiguration @ContextHierarchy({ @ContextConfiguration(classes = AppConfig.class), @ContextConfiguration(classes = WebConfig.class) }) public class WebIntegrationTests { // class body... } 如果你需要合并或覆盖配置对于一个给定的 层次的上下文层次结构在一个测试类层次结构,你 必须显式地名字,水平通过提 供相同的价值吗 名称 属性 @ContextConfiguration 在每个 相应级别的类层次结构。 看到 一个​章节​上下文hierarchiesa​​ 和 javadoc的 @ContextHierarchy 对于 进一步的例子。 @ActiveProfiles 一个类级别注释,用于指明哪 Bean定义概要文件 应该主动当 加载一个 ApplicationContext 对于 测试类。 @ContextConfiguration @ActiveProfiles("dev") public class DeveloperTests { // class body... } @ContextConfiguration @ActiveProfiles({"dev", "integration"}) public class DeveloperIntegrationTests { // class body... 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 190/514 } 注意 @ActiveProfiles 提供 支持 继承 活跃的bean定义 配置文件默认声明的超类。 看到 一个​章节​上下文配置与环境profilesa​​ 和的Javadoc @ActiveProfiles 为例,进一步的细节。 @DirtiesContext 表明,底层的春天 ApplicationContext 一直 脏 在执行一个测试(即, 修改或破坏一个​​以某种方式为例,通过改变 一个单例 bean的状态),应该关闭,不管 是否通过测试。 当一个应用程序上下文被标记 脏 ,这是远离测试 框架的缓存和关闭。 因此, 底层的 Spring容器将重建对于任何后续测试 需要一个上下文相同的配置元数据。 @DirtiesContext 可以作为 两类级别和方法级注释在相同的测试 类。 在这样的场景中, ApplicationContext 被标记为 脏 在任何这样的带注释的方法 作为在整个类。 如果 ClassMode 设置为 在每个测试方法 ,上下文是 标志着脏后班上每个测 试方法。 下面的例子解释当上下文会 脏为各种配置场景: 在当前的测试类,当上声明一个类 与类模式设置为 下课后 (即。 , 默认类模式)。 @DirtiesContext public class ContextDirtyingTests { // some tests that result in the Spring container being dirtied } 在每个测试方法在当前的测试类,当 声明在类与类模式设置为 在每个测试方法。 @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) public class ContextDirtyingTests { // some tests that result in the Spring container being dirtied } 在当前的测试,当上声明一个方法。 @DirtiesContext @Test public void testProcessWhichDirtiesAppCtx() { // some logic that results in the Spring container being dirtied } 如果 @DirtiesContext 用于 测试其上下文配置作为一个上下文层次通过 @ContextHierarchy , hierarchyMode 标记可 以用来控制如何 上下文缓存清除。 默认情况下一个 详尽的 算法将被使用,清除 上下文缓存不仅包括当前水平但也都 其他 上下文层次结构,共享一个共同的祖先上下文 当前测试;所有 ApplicationContext 年代,驻留在一个 子层次的共同祖先上下 文将被移除 上下文缓存和关闭。 如果 详尽的 算法忽略了特定的用例,简单 当前水平 算法可以指定 相反,如下所示。 @ContextHierarchy({ @ContextConfiguration("/parent-config.xml"), @ContextConfiguration("/child-config.xml") }) public class BaseTests { // class body... } public class ExtendedTests extends BaseTests { @Test @DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL) public void test() { // some logic that results in the child context being dirtied } } 为进一步的细节有关 详尽的 和 当前水平 算法的Javadoc中看到 DirtiesContext.HierarchyMode 。 @TestExecutionListeners 定义元数据配置这类级别 TestExecutionListener 年代应该 注册 TestContextManager 。 通常情况下, @TestExecutionListeners 是 结合使用 @ContextConfiguration 。 @ContextConfiguration @TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) public class CustomTestExecutionListenerTests { // class body... } @TestExecutionListeners 支持 继承 听众默认情况下。 看到 Javadoc为例,进一步的细节。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 191/514 @TransactionConfiguration 定义了类级元数据配置事务 测试。 具体地说,该bean的名称 PlatformTransactionManager 这 应该被用来驱动事务可以 显式地指定如果 有多个bean类型 PlatformTransactionManager 在 测试的 ApplicationContext 如果 bean名称所需的 PlatformTransactionManager 不是 “transactionManager”。 此外,您可以更改 defaultRollback 旗帜 假 。 通常情 况下, @TransactionConfiguration 是结合使用 @ContextConfiguration 。 @ContextConfiguration @TransactionConfiguration(transactionManager = "txMgr", defaultRollback = false) public class CustomConfiguredTransactionalTests { // class body... } 注意 如果默认的惯例是足够的为您的测试 配置,您可以避免使用 @TransactionConfiguration 完全。 换句 话说,如果你只有一个事务 经理一个​​或如果你有多个事务经理但 事务管理器测试被命名 为“transactionManager”或 指定通过 TransactionManagementConfigurer 一个​​ 如果你想交易自 动回滚,然后 不需要注释您的测试类 @TransactionConfiguration 。 @Rollback 指示是否交易的注释测试 方法应该 回滚 在测试之后 法完成了。 如果 真正的 ,事务是 回滚;否则,提交事务。 使用 @Rollback 以覆盖默认 回滚国旗在类级配置。 @Rollback(false) @Test public void testProcessWithoutRollback() { // ... } @BeforeTransaction 指明注释的 公共无效 方法应该被执行 之前 一个事务 是开始测试方法的配置为运行在一个事务 通过 transactional 注 释。 @BeforeTransaction public void beforeTransaction() { // logic to be executed before a transaction is started } @AfterTransaction 指明注释的 公共无效 方法应该被执行 在 一个事务 已经结束的测试方法的配置为运行在一个事务 通过 transactional 注 释。 @AfterTransaction public void afterTransaction() { // logic to be executed after a transaction has ended } @NotTransactional 存在该注释指明注释的 测试方法必须 不 执行在一个事务 上下文。 @NotTransactional @Test public void testProcessWithoutTransaction() { // ... } @NotTransactional不 Spring 3.0的, @NotTransactional 是不宜用在 支持移动 事务性 测试 方法到一个单独的(非事务性)测 试类或一个 @BeforeTransaction 或 @AfterTransaction 法。 作为一个 替代注释整个类 transactional ,考虑注释 个人方法 transactional ,这样做允许 混合的事务性和非事务性方法在相同的 测试类,而不需要使用 @NotTransactional 。 标准注释支持 下面的注释是支持标准语义 所有配置的春天还是和TestContext框架。 注意, 这些注释并不特定于测试,可以在任何地方使用 Spring框架。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 192/514 @ autowired qualifier @ resource (javax.annotation) 如果jsr - 250存在 @ inject (javax.inject) 如果jsr - 330 礼物 @ named (javax.inject) 如果jsr - 330 礼物 persistencecontext (javax.persistence) 如果JPA存在 @PersistenceUnit (javax.persistence) 如果JPA存在 @ required transactional jsr - 250生命周期注释 在春天还是和TestContext框架 @PostConstruct 和 @PreDestroy 可使用标准吗 在任何应用程序组件语 义中配置的 ApplicationContext ;然而,这些 生命周期内注释有有限的使用实际的测试 类。 如果一个方法在一个测试类标注 @PostConstruct ,该方法将 之前执行任何 之前 方法 底层测试框架(如。 、方法与JUnit的注释 @ before一样 ),这将适用于每一个 测试方法在测试类中。 另一方面,如果一个方法 在一个 测试类标注 @PreDestroy ,该方法将 从来没有 被执行。 在一个测试类 因此推荐使用测试生命周期 回调的 底层测试框架代替 @PostConstruct 和 @PreDestroy 。 弹簧JUnit测试注释 下面的注释是 只有 支持 当结合使用的 SpringJUnit4ClassRunner 或 这个 JUnit 支持类。 @IfProfileValue 指明注释的测试都启用了一个特定的 测试环境。 如果配置的 ProfileValueSource 返回一个匹配 价值 所提供的 名称 , 测 试是启用的。 这个注释就可以应用到整个 类或个人方法。 类级别使用覆盖 方法级的用法。 @IfProfileValue(name="java.vendor", value="Sun Microsystems Inc.") @Test public void testProcessWhichRunsOnlyOnSunJvm() { // some logic that should run only on Java VMs from Sun Microsystems } 或者,您可以配置 @IfProfileValue 列表的 值 ( 或 语义) 实现testng像支持 测试组 在JUnit环境。 考虑下面的例子: @IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"}) @Test public void testProcessWhichRunsForUnitOrIntegrationTestGroups() { // some logic that should run only for unit and integration test groups } @ProfileValueSourceConfiguration 类级别注释指定什么类型的 ProfileValueSource 使用当检索 剖面值 配置通过 @IfProfileValue 注释。 如果 @ProfileValueSourceConfiguration 是 不宣布为测试, SystemProfileValueSource 被 默认的。 @ProfileValueSourceConfiguration(CustomProfileValueSource.class) public class CustomProfileValueSourceTests { // class body... } @Timed 指明注释的测试方法必须完成执行 在指定的时间(以毫秒为单位)。 如果文本执行 时间超过指定时间,测试失败。 这个时期包括执行测试方法本身, 任何重复的测试(见 @Repeat ),以及任何 设置 或 拆掉 的 测试夹具。 @Timed(millis=1000) public void testProcessWithOneSecondTimeout() { // some logic that should not take longer than 1 second to execute } 春天的 @Timed 注释已经 不同的语义比JUnit的 @ test来(timeout =…) 支持。 具体来说,由于方式处理测试JUnit 执行 超时(即,通过执行测试方法 单独 线程 ), @ test来(timeout =…) 适用于 每个迭代 对于重复和 先发制人地失败测试如果测 试花费的时间太长。 春天的 @Timed 另一方面,乘以 总 测试执行时间(包括所有 重复)和不预先失败测试而是 等待测试完 成之前失败的。 @Repeat 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 193/514 指明注释的测试方法必须被执行 反复。 多少次,测试方法是 指定执行的注释。 执行的范围包括执行重复的 测试方法本身以及任何 设置 或 拆掉 的测试夹具。 @Repeat(10) @Test public void testProcessRepeatedly() { // ... } 11.3.5A春天还是和TestContext框架 这个 春天 还是和TestContext 框架 (位于 org.springframework.test.context 包)提供 通用,注解驱动的单元测试和集成测试 支持 不可知论者的测试框架使用。 还是和TestContext框架还 地方很大的重视 约定优于 配置 与合理的默认值,可以被覆盖 通 过基于注解的配置。 除了通用的测试基础设施,还是和TestContext 框架提供了明确的支持JUnit和TestNG的形式 文摘 支持类。 对于JUnit,春天也 提供一个自定义的JUnit 跑 这允许 一个写所谓的 POJO测试类 。 POJO测试 类不需要扩展一个特定的类层次结构。 以下部分概述的内部构造 还是和TestContext框架。 如果你唯一感兴趣的就是使用这个框架 和不一定感兴趣与您自己的自定 义扩展它 听众或自定义加载器,请直接去配置 ( 上下文管理 , 依赖注入 , 事务管理 ), 支持类 ,和 注释支持 部分。 关键抽象 框架的核心包括 还是和TestContext 和 TestContextManager 类和 TestExecutionListener , ContextLoader ,和 SmartContextLoader 接口。 一个 TestContextManager 上创建一个每个测试的基础 (如。 ,执行一个测试方法在JUnit)。 这 个 TestContextManager 反过来管理一个 还是和TestContext 保存当前的上下文 测试。 这个 TestContextManager 还更新 状态的 还是和TestContext 作为测试的过程 和代表 TestExecutionListener 年代, 这仪器实际测试执行通过提供依赖 注入,管 理事务,等等。 一个 ContextLoader (或 SmartContextLoader )负责 加载一个 ApplicationContext 对于一个给定的 测试 类。 请教Javadoc和弹簧测试套件进行进一步的 信息和各种实现的例子。 还是和TestContext :封装上下文 在这一个测试被执行,不可知论者的实际测试 框架在使用,并且提供了上下文管理和缓存 支持测试实例,它是负责任的。 这个 还是和TestContext 也代表一个 ContextLoader (或 SmartContextLoader )加载一 个 ApplicationContext 如果 要求。 TestContextManager :主入口 点到 春天还是和TestContext框架 , 管理着一个 还是和TestContext 和 信号事件所有注册 的 TestExecutionListener 年代在 定义良好的测试执行点: 之前 类方法之前 的 一个特定的测试框架 测试实例准备 之前 方法之前 的 特定的测试框架 在任何 在方法 的 特定的测试框架 在任何 在类方法 的 特定的测试框架 TestExecutionListener :定义 一个 侦听器 API,用于对测试执行 事件发表的 TestContextManager 与该侦听器注册。 Spring提供了四 TestExecutionListener 实现 这是默认配置: ServletTestExecutionListener , DependencyInjectionTestExecutionListener , DirtiesContextTestExecutionListener ,和 TransactionalTestExecutionListener 。 分别,他们支持Servlet API模拟为一个 WebApplicationContext 、依赖 注入的 测试实例,处理 @DirtiesContext 注释, 与默认的回滚事务测试执行语义。 ContextLoader :战略 界面介绍了Spring 2.5中加载一个 ApplicationContext 为一个集成 测试管理的春天还是和 TestContext框架。 在Spring 3.1,实现 SmartContextLoader 而不是这 接口以提供支持注释类和 活跃的bean定义概要文件。 SmartContextLoader :扩展 的 ContextLoader 接口 Spring 3.1中引入的。 这个 SmartContextLoader SPI 取代了 ContextLoader SPI, 介绍了Spring 2.5。 具体地说,一个 SmartContextLoader 可以选择 过程资源 位置 、注释 类 或上下文 初始化 。 此外,一个 SmartContextLoader 可以设置活动 bean定义概要文 件的上下文,它加载。 Spring提供了以下的实现: DelegatingSmartContextLoader :一个 两个默认加载器内部的代表 AnnotationConfigContextLoader 或 GenericXmlContextLoader 既不同 在配置测试类的声明或 有默认位置或默认配置 类。 WebDelegatingSmartContextLoader : 两个默认加载器之一,代表们在内部一个 AnnotationConfigWebContextLoader 或 GenericXmlWebContextLoader 根据 无论是在配置宣布测试类或 有默 认位置或默认配置 类。 一个web ContextLoader 将 只用于如果 @WebAppConfiguration 存在 在测试类。 AnnotationConfigContextLoader : 加载一个标准 ApplicationContext 从 注释类 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 194/514 AnnotationConfigWebContextLoader : 加载一个 WebApplicationContext 从 注释类 。 GenericXmlContextLoader :加载一个 标准 ApplicationContext 从 XML 资源位置 。 GenericXmlWebContextLoader :加载一个 WebApplicationContext 从XML 资源位置 。 GenericPropertiesContextLoader : 加载一个标准 ApplicationContext 从Java 属性文件。 接下来的小节解释如何配置 还是和TestContext 框架通过注释和 提供工作的例子,如何编写单元测试和集成测试 这个框架。 上下文管理 每个 还是和TestContext 提供上下文 管理和缓存支持测试实例是负责任的 对。 测试实例不自动接收访问 配置 ApplicationContext 。 然而, 如果一个测试类实现了 ApplicationContextAware 接口, 参考 ApplicationContext 提供 到测 试实例。 注意, AbstractJUnit4SpringContextTests 和 AbstractTestNGSpringContextTests 实现 ApplicationContextAware 因此 提供访问 ApplicationContext 自动。 @ autowired ApplicationContext 作为一种替代方法来实现 ApplicationContextAware 界面,你 可以注入应用程序上下文为您的测试类通过 吗 @ autowired 注释可以在一个字段 或setter方法。 例如: @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class MyTest { @Autowired private ApplicationContext applicationContext; // class body... } 同样,如果您的测试配置加载 WebApplicationContext ,你可以注射 web应用程序上下文到您的测试如下: @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration public class MyWebAppTest { @Autowired private WebApplicationContext wac; // class body... } 依赖注入通过 @ autowired 提供的 DependencyInjectionTestExecutionListener 哪一个 默认配置(见吗 一个​章节​依赖注入的测试fixturesa​​ )。 测试类,使用还是和TestContext框架不需要 扩展任何特定的类或实现特定的接口 配置他们的应用程序上下文。 相反,配置是实 现 只需声明 @ContextConfiguration 注释在 类级别。 如果您的测试类没有显式地声明应用程序 上下文资源 位置 或注释 类 ,配置的 ContextLoader 决定如何加载 从默认的上下文位置或默认的配置类。 在 除了上下文资源 位置 和注释 类 ,一个应用程 序上下文也可以 通过应用程序上下文配置 初始化 。 接下来的小节解释如何配置一个 ApplicationContext 通过XML配置 文件,带注释的类(一般 @ configuration 类),或上下文 初 始化使用Spring的 @ContextConfiguration 注释。 或者,您可以实现您自己的自定义和配置 SmartContextLoader 对于高级 使用 情况下。 上下文配置XML资源 加载一个 ApplicationContext 对于 你的测试使用XML配置文件,标注您的测试类 与 @ContextConfiguration 和 配置 位置 属性与一个数组 包含的资源位置的XML配置元数据。 一个 平原或相对路径一个​​例如 “上下文xml” 一个​​将被视为一个类路径 是相对于资源 包中定义的测试类。 一个路径开始 斜杠是当作一个绝对路径位置,例如 “/ org/example/config.xml” 。 一个 路径代表 资源URL(即。 ,一个路径前缀 类路径: , 文件: , http: 等)将被使用 作为 是 。 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from "/app-config.xml" and // "/test-config.xml" in the root of the classpath @ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"}) public class MyTest { // class body... } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 195/514 @ContextConfiguration 支持一个 别名 位置 属性通过 标准Java 价值 属性。 因此,如果你不 需要声明额外的属性 @ContextConfiguration ,你可以省略的 宣言 位置 属性名称和 声明的资源位置通过使用简写形式 通过以下例子。 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"/app-config.xml", "/test-config.xml"}) public class MyTest { // class body... } 同时如果您省略 位置 和 价值 属性从 @ContextConfiguration 注释, 还是和TestContext框架将试图发现一个默认的XML资 源 位置。 具体地说, GenericXmlContextLoader 检测到一个默认位置基于测试类的名称。 如果 你的类命名 com例子mytest , GenericXmlContextLoader 加载应用程序 上下文从 “类路径:/ com/example/mytest-context.xml” 。 package com.example; @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from // "classpath:/com/example/MyTest-context.xml" @ContextConfiguration public class MyTest { // class body... } 上下文配置使用带注释的类 加载一个 ApplicationContext 对于 你的测试用 注释类 (见 SectionA 5.12,一个​​configurationa​​基于java的容器 ),注释您的测 试类 @ContextConfiguration 和配置 类 一个数组,其中包含属性 带注释的类的引用。 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from AppConfig and TestConfig @ContextConfiguration(classes = {AppConfig.class, TestConfig.class}) public class MyTest { // class body... } 如果您省略了 类 属性从 @ContextConfiguration 注释, 还是和TestContext框架将尝试检测存在违约 配置类。 具体地说, AnnotationConfigContextLoader 将检测所有 静态内部类的测试类,满足要求 配置类实现规定的Javadoc @ configuration 。 在接下来的 的例子, OrderServiceTest 类声明一个 静态内部配置类命名 配置 这将被自动用于加载 ApplicationContext 为 测试类。 注意,配置类的名称是任意的。 在 另外,一个测试类可以包含多个静态内部 如果需要配置类。 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from the // static inner Config class @ContextConfiguration public class OrderServiceTest { @Configuration static class Config { // this bean will be injected into the OrderServiceTest class @Bean public OrderService orderService() { OrderService orderService = new OrderServiceImpl(); // set properties, etc. return orderService; } } @Autowired private OrderService orderService; @Test public void testOrderService() { // test the orderService } } 混合的XML资源和注释的类 它有时是理想的混合XML资源和注释 类(即。 ,通常是 @ configuration 类)来配置一个 ApplicationContext 为你的测试。 对 于 例子,如果你使用XML配置在生产中,您可能会决定 你想使用 @ configuration 类配置特定的spring管理组件为您的 测试,或 反之亦然。 如前所述在 一个​章节​弹簧测试Annotationsa​​ 还是和TestContext 框架不允许您声明 两 通过 @ContextConfiguration ,但这并 不意味着你不能同时使用。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 196/514 如果你想要使用XML 和 @ configuration 类配置 你的测试,你将不得不选择一个作为 条目 点 ,你将需要包括或导入 其他的。 例如,在XML可以包含 @ configuration 类通过组件 扫描或定义为Spring bean,正常在XML;相反,在一个 @ configuration 类 可以使用 @ImportResource 导入XML 配置文件。 注意,这个行为是语义上的 相当于你如何配置您的应用程序在生产:在 生产 配置您将定义或者一组XML资源 位置或一组 @ configuration 类,生产 ApplicationContext 将被加载, 但你仍然可以自由添 加或导入其他类型的 配置。 上下文配置与上下文初始化 配置一个 ApplicationContext 为测试使用 上下文初始化,注释您的测试类 @ContextConfiguration 和配置 初始化 一个数组, 其中包含属性 引用的类,实现的 ApplicationContextInitializer 。 这个 宣布上下文初始化器将被用来初始化 ConfigurableApplicationContext 这是 为测试加载。 注意,具体 ConfigurableApplicationContext 类型 由每个宣布初始化 必须兼容 类型的 ApplicationContext 由 这个 SmartContextLoader 在使用(即, 通常一个 GenericApplicationContext )。 此外,初始化的顺序调用依赖 他们是否实现弹簧的 下令 接口或标注 春天的 @Order 注释。 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from TestConfig // and initialized by TestAppCtxInitializer @ContextConfiguration( classes = TestConfig.class, initializers = TestAppCtxInitializer.class) public class MyTest { // class body... } 也可以省略的声明XML配置 文件或注释的类 @ContextConfiguration 完全和 相反只声明 ApplicationContextInitializer 类 然后负责注册豆在语境中​​ 例如,通过编程加载bean定义XML文件 或配置类。 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be initialized by EntireAppInitializer // which presumably registers beans in the context @ContextConfiguration(initializers = EntireAppInitializer.class) public class MyTest { // class body... } 上下文配置继承 @ContextConfiguration 支持 布尔 inheritLocations 和 inheritInitializers 属性,指示是否 资源位置或注释的类和上下文初始 化 声明父类的应该 继承 。 这个 默认值为双方的旗帜是 真正的 。 这意味着 这一个测试类继承了资源位置或注释的类 以及上 下文初始化父类声明的任何。 具体来说,资源位置或注释的类测试 类都添加到列表的资源位置或注释 类声明的超类。 同样,初 始化对于一个 鉴于测试类将被添加到组定义的初始化 测试超类。 因此,子类可以选择 扩展 资源位置、注释 类,或上下文初始 化。 如果 @ContextConfiguration ' s inheritLocations 或 inheritInitializers 属性设置为 假 、资源位置或注释的类 和上下文初 始化器,分别为测试类 影子 和有效地替代配置 超类所定义的。 在接下来的例子中,使用XML资源的位置, ApplicationContext 对于 ExtendedTest 将加载从 “基本配置xml” 和 “扩展配置 xml” , 这个顺序。 bean定义在 “扩展配置xml” 因此可能 覆盖 (即。 ,替换)中定义的 “基本配置xml” 。 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from "/base-config.xml" // in the root of the classpath @ContextConfiguration("/base-config.xml") public class BaseTest { // class body... } // ApplicationContext will be loaded from "/base-config.xml" and // "/extended-config.xml" in the root of the classpath @ContextConfiguration("/extended-config.xml") public class ExtendedTest extends BaseTest { // class body... } 同样,在接下来的例子中,使用带注释的类, 这个 ApplicationContext 对于 ExtendedTest 将被加载的 BaseConfig 和 ExtendedConfig 类,这个顺序。 bean 定义在 ExtendedConfig 因此可能 覆盖(即。 ,替换)中定义的 BaseConfig 。 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from BaseConfig @ContextConfiguration(classes = BaseConfig.class) public class BaseTest { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 197/514 // class body... } // ApplicationContext will be loaded from BaseConfig and ExtendedConfig @ContextConfiguration(classes = ExtendedConfig.class) public class ExtendedTest extends BaseTest { // class body... } 在接下来的例子中,使用上下文初始化器, ApplicationContext 对于 ExtendedTest 将初始化使用 BaseInitializer 和 ExtendedInitializer 。 但是请注意,初始化的顺序被调用 取决于他们是否实现弹簧的 下令 接口或标注 春天的 @Order 注释。 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be initialized by BaseInitializer @ContextConfiguration(initializers=BaseInitializer.class) public class BaseTest { // class body... } // ApplicationContext will be initialized by BaseInitializer // and ExtendedInitializer @ContextConfiguration(initializers=ExtendedInitializer.class) public class ExtendedTest extends BaseTest { // class body... } 上下文配置与环境概要文件 Spring 3.1引入了一流的支持框架 环境的概念和配置文件(即 bean 定义概要文件 ),和集成测试可以 配置为激活特定bean定义 配置文件不同 测试场景。 这是通过一个测试类注解 这个 @ActiveProfiles 注释和 提供一个概要文件的列表应该激活当加载 ApplicationContext 对于测试。 注意 @ActiveProfiles 可以用 与任何实施新的 SmartContextLoader SPI,但 @ActiveProfiles 不支持 实现老 ContextLoader SPI。 让我们看看一些例子使用XML配置和 @ configuration 类。 package com.bank.service; @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from "classpath:/app-config.xml" @ContextConfiguration("/app-config.xml") 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 198/514 @ActiveProfiles("dev") public class TransferServiceTest { @Autowired private TransferService transferService; @Test public void testTransferService() { // test the transferService } } 当 TransferServiceTest 运行时,其 ApplicationContext 将加载从 这个 应用配置xml 配置文件在根 在类路径中。 如果你检查 应用配置xml 你会注意到 AccountRepository bean有一个 依赖一个 数据源 豆;然而, 数据源 不是定义为一个顶级bean。 相 反, 数据源 是定义了两次:一次在吗 生产 剖面和一次 Dev 概要文件。 通过标注 TransferServiceTest 与 @ActiveProfiles(“dev”) 我们指导 春天还是和TestContext框架来加载 ApplicationContext 积极 配置文件设置为 {“dev " } 。 因此,嵌入式 数据库将被创建,和 AccountRepository bean的引用将 连接到发展 数据源 。 和这是可能我们 希望在集成测试。 下面的代码清单演示如何实现 相同的配置和集成测试但使用 @ configuration 类而不是 XML。 @Configuration @Profile("dev") public class StandaloneDataConfig { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .addScript("classpath:com/bank/config/sql/test-data.sql") .build(); } } @Configuration @Profile("production") public class JndiDataConfig { @Bean public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } } @Configuration public class TransferServiceConfig { @Autowired DataSource dataSource; @Bean public TransferService transferService() { return new DefaultTransferService(accountRepository(), feePolicy()); } @Bean public AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource); } @Bean public FeePolicy feePolicy() { return new ZeroFeePolicy(); } } package com.bank.service; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( classes = { TransferServiceConfig.class, StandaloneDataConfig.class, JndiDataConfig.class}) @ActiveProfiles("dev") public class TransferServiceTest { @Autowired private TransferService transferService; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 199/514 @Test public void testTransferService() { // test the transferService } } 在这个变化,我们已经把XML配置成 三个独立的 @ configuration 类: TransferServiceConfig :获得一个 数据源 通过依赖注入使用 @ autowired StandaloneDataConfig :定义了一个 数据源 嵌入式数据库合适 对于开发人员测试 JndiDataConfig :定义了一个 数据源 从JNDI检索,在 生产环境 与基于xml的配置示例,我们仍然注释 TransferServiceTest 与 @ActiveProfiles(“dev”) ,但是这一次 我们指定的所有三个配 置类通过 @ContextConfiguration 注释。 这个 体的测试类本身仍然是完全不变。 加载WebApplicationContext Spring 3.2引入了支持加载 WebApplicationContext 在集成 测试。 指导还是和TestContext框架加载 WebApplicationContext 而不是一个 标准 ApplicationContext ,简单地 注释相应的测试类 @WebAppConfiguration 。 存在 @WebAppConfiguration 在您的测试类 指示还是和TestContext框架(TCF) WebApplicationContext (WAC)应该 为你 的集成测试加载。 在后台TCF使 确保一个 MockServletContext 是 创建和提供给你的测试的WAC。 默认情况下,基础资源 路 径为你 MockServletContext 将 被设置为 “src / main / webapp” 。 这是解释 作为一个路径相对于根你的JVM(即。 ,正常 情况下的路径 你的项目)。 如果您熟悉的目录结构 web应用程序在一个Maven项目,您就会知道 “src / main / webapp” 是 默认的位置吗 根你的战争。 如果你需要覆盖这个默认,只需提供 另一个路径 @WebAppConfiguration 注释(如 @WebAppConfiguration(" src / webapp /测试”) )。 如果你想引用一个基地资源路径从类路径 而不是文件系统,使用 Spring的 类路径: 前缀。 请注意弹簧的测试支持 WebApplicationContexts 就等同于 支持标准 ApplicationContexts 。 当测试与 一个 WebApplicationContext 你可以自由 声明不是XML配置文件或 @ configuration 类通过 @ContextConfiguration 。 你的 当然也可以使用别的测试等注释 @TestExecutionListeners , @TransactionConfiguration , @ActiveProfiles 等。 下面的例子演示一些不同 加载配置选项 WebApplicationContext 。 ExampleA 11 1一个约定 @RunWith(SpringJUnit4ClassRunner.class) // defaults to "file:src/main/webapp" @WebAppConfiguration // detects "WacTests-context.xml" in same package // or static nested @Configuration class @ContextConfiguration public class WacTests { //... } 上面的示例演示了还是和TestContext框架的 支持 约定优于配置 。 如果你 注释一个测试类 @WebAppConfiguration 不指 定 一个资源基本路径,资源路径将有效的默认 “文件:src / main / webapp” 。 同样,如果你声明 @ContextConfiguration 没有 指定资源 位置 , 注释 类 或上下文 初始化 ,春天将尝试 检测存在你的配置(即,使用约定。 “WacTests-context.xml” 在 同一个包 WacTests 类或静态嵌套 @ configuration 类)。 ExampleA 11.2。 一个默认的资源语义 @RunWith(SpringJUnit4ClassRunner.class) // file system resource @WebAppConfiguration("webapp") // classpath resource @ContextConfiguration("/spring/test-servlet-config.xml") public class WacTests { //... } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 200/514 这个例子演示了如何显式地声明一个资源 基本路径与 @WebAppConfiguration 和 一个XML资源位置与 @ContextConfiguration 。 重要的 注意这里是不同的语义路径与这两个 注释。 默认情况下, @WebAppConfiguration 资 源路径 文件系统的基础,然而, @ContextConfiguration 资源 基于位置的类路径。 ExampleA 11.3。 一个显式的资源语义 @RunWith(SpringJUnit4ClassRunner.class) // classpath resource @WebAppConfiguration("classpath:test-web-resources") // file system resource @ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml") public class WacTests { //... } 在这第三个例子中,我们看到,我们可以覆盖默认的 资源语义注释都通过指定一个春天 资源前缀。 在这个例子的对比评论的 前 面的例子。 处理网络模拟 提供全面的web测试支持,Spring 3.2 引入了一个新的 ServletTestExecutionListener 这是 默认启用。 当测试对 WebApplicationContext 这 TestExecutionListener 设置默认线程局部状态通过Spring Web的 RequestContextHolder 在 每个测试之前 方法并创建一个 MockHttpServletRequest , MockHttpServletResponse ,和 ServletWebRequest 基于基础 配置资源路径通过 @WebAppConfiguration 。 ServletTestExecutionListener 也 确保 MockHttpServletResponse 和 ServletWebRequest 可以被注入 到测试实例,一旦测试完成它清除 线程局部状态。 一旦你有一个 WebApplicationContext 加载为你 测试你可能发现你需要和网络交互​​戏 例如,设置您的测试夹具或执行断言 在 调用您的web组件。 下面的例子 演示,模拟可以在你的测试实例autowired的。 注意, WebApplicationContext 和 MockServletContext 都 缓存整个测试套件;但是,其他模拟管理 每个测试方法的 ServletTestExecutionListener 。 ExampleA 11.4。 一个注入模拟 @WebAppConfiguration @ContextConfiguration public class WacTests { @Autowired WebApplicationContext wac; // cached @Autowired MockServletContext servletContext; // cached @Autowired MockHttpSession session; @Autowired MockHttpServletRequest request; @Autowired MockHttpServletResponse response; @Autowired ServletWebRequest webRequest; //... } 上下文缓存 一旦还是和TestContext框架装载一个 ApplicationContext (或 WebApplicationContext )为测试, 上下文将被缓存和重用 所 有 随后声明相同的测试 独特的上下文配置在相同的测试套件。 了解 如何缓存作品,重要的是要理解是什么意思吗 独特的 和 测试 套件 。 一个 ApplicationContext 可以 独特 确定的组合 配置参数,用于加载它。 因此, 结合独特的配置参数是用来生成一个 关键 在 这上下文缓存。 这个 还是和TestContext框架使用下面的配置参数 构建上下文缓存键: 位置 (从 @ContextConfiguration) 类 (从 @ContextConfiguration) 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 201/514 contextInitializerClasses (从 @ContextConfiguration) ContextLoader (从 @ContextConfiguration) activeProfiles (从 @ActiveProfiles) resourceBasePath (从 @WebAppConfiguration) 例如,如果 TestClassA 指定 { "应用程序配置。 xml”、“测试配置xml " } 为 位置 (或 价值 )属性 的 @ContextConfiguration , 还是和TestContext框架将载荷对应的 ApplicationContext 并将其存储在一个 静态 上下文缓存的 一个关键,是基于下 只在那些地点。 所以如果 TestClassB 还定义了 { "应用程序配置。 xml”、“测试配置xml " } 对于它的位 置(或隐或显通过 但没有定义继承) @WebAppConfiguration 、不同的 ContextLoader ,不同的活动 配置文件,或不同的上下 文的初始化,然后同样的 ApplicationContext 将共享 这两个测试类。 这意味着设置成本加载一个 应用程序上下文发生只有一 次(每个测试套件), 随后的测试执行速度更快。 测试套件和分叉的过程 春天还是和TestContext商店应用程序上下文框架 在一个 静态 缓存。 这意味着上下文 简直是存储在一个 吗 静态 变量。 在 句话说,如果测试执行在不同的流程静态缓存 将被清除每个测试执行之间,这将吗 有效的 禁用缓存机制。 受益于缓存机制,所有的测试必须运行 在相同的过程或测试套件。 这可以通过 执行所有测试作为一个群体 在一个IDE。 同样的,当 执行测试和构建框架(如Ant、Maven或Gradle 它是重要的,以确保构建框架不 叉 在测试的。 例如,如果 forkMode 对于Maven插件的设置 总是 或 pertest ,还是和TestContext框架不会 能够缓存应用程序上下文测试类和之间 构建过程将运行很慢的结果。 在不太可能的情况下,一个测试应用程序导致腐败 上下文和需要重新加载一个​​例如,通过修改一个bean 定义或一个应用程序对 象的状态一个​​你可以标注 您的测试类或测试方法 @DirtiesContext (见讨论 @DirtiesContext 在 一个​章节​弹簧测试 Annotationsa​​ )。 这指示 春天从缓存中移除上下文和重建 应用程序上下文在执行下一个测试。 注意,支持 为 @DirtiesContext 注释是 提供的 DirtiesContextTestExecutionListener 这是 默认启用。 上下文层次 当编写集成测试,依靠一个加载弹簧 ApplicationContext ,它往往是 足以测试单个上下文;然而,有次 当它是有利的,甚至是必要 的测试对比的一个层次 ApplicationContext 年代。例如,如果 您正在开发一个Spring MVC web应用程序通常会 有一根 WebApplicationContext 通过弹簧加载的 ContextLoaderListener 和一个 孩子 WebApplicationContext 加载通过 春天的 DispatcherServlet 。 这个结果在一个 父子层次结构,共享组件和上下文 基础设施配置中声明根上下文和 在孩子的消费环境网 络自身组件。 另一个使用 案例可以发现在春天批处理应用程序,你经常有 父上下文,提供配置为共享的批处理 基础设施和一个 孩子上下文特定的配置 批处理作业。 Spring框架的第3.2.2章,可以写 集成测试,使用上下文层次通过声明上下文 配置通过 @ContextHierarchy 注释,要么在一个单 独的测试类或在一个测试类 层次结构。 如果一个上下文层次结构是宣布在多个类 在一个测试类层次结构也可以合并或覆盖 上 下文配置为一个特定的,名叫水平在上下文 层次结构。 当合并配置对于一个给定的水平 层次结构配置资源类型(即。 、XML配 置 文件或注释类)必须是一致的;否则,它是 完全可以接受有不同的水平在一个上下文层次结构 配置使用不同的资源类型。 以下示例展示常见基础junit 配置场景集成测试,需要使用 上下文层次结构。 ExampleA 11.5。 一个测试类与上下文层次结构 ControllerIntegrationTests 代表一个 典型的集成测试场景一个Spring MVC web 应用程序通过声明一个上下文层次结构包 括两个 的水平,一个用于 根 WebApplicationContext (加载使用 TestAppConfig @ configuration 类)和一个用于 dispatcher servlet WebApplicationContext (加载使用 这个 WebConfig @ configuration 类)。 这个 WebApplicationContext 这是 Autowired的 到测试实例是一个用于 孩子上下文(即。 ,最低的上下文 层次结构)。 @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextHierarchy({ @ContextConfiguration(classes = TestAppConfig.class), @ContextConfiguration(classes = WebConfig.class) }) public class ControllerIntegrationTests { @Autowired private WebApplicationContext wac; // ... } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 202/514 ExampleA 11.6。 一个类层次结构和隐式父上下文 以下测试类定义一个上下文层次结构在一个 测试类的层次结构。 AbstractWebTests 声明了配置一个根 WebApplicationContext 在一个 弹簧驱动的web应用程序。 但是请注意, AbstractWebTests 没有声明 @ContextHierarchy ,因此, 子类的 AbstractWebTests 可以选择 参与一个上下文层次或简单地遵循标准的 语义 @ContextConfiguration 。 SoapWebServiceTests 和 RestWebServiceTests 两个扩展 AbstractWebTests 和定义一个上下文 层次通过 @ContextHierarchy 。 这个 结果是,三个应用程序上下文(一个用于将被装载 每个声明的 @ContextConfiguration ), 应用程 序上下文加载基于配置 AbstractWebTests 将被设置为父吗 上下文的每个上下文加载的混凝土 子类。 @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml") public abstract class AbstractWebTests {} @ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml") public class SoapWebServiceTests extends AbstractWebTests {} @ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml") public class RestWebServiceTests extends AbstractWebTests {} ExampleA 11.7。 一个类层次结构和上下文层次结构合并 配置 下面的类演示使用 命名 层次的水平,以 合并 配置为特定的水平 一个上下文层次结构。 BaseTests 定义了两个 水平的层次结 构, 父 和 孩子 。 ExtendedTests 延伸 BaseTests 和指示春天 还是和TestContext框架来合并上下文配置 孩子 层次水平,只需 确保 声明的名称通过 ContextConfiguration ' s 名称 属性都是 “孩子” 。 结果是三个应用程序 上下文将被加载:一个用于 “/应用程序配置xml” ,一个用于 “/用户配置xml” ,和一个用于 { " /用户配置。 xml”、“/订单配置xml " } 。 作为 与前面 的示例中,应用程序上下文加载 “/应用程序配置xml” 将被设置为父吗 上下文语境的加载 “/用户配置xml” 和 { " /用户配 置。 xml”、“/订单配置xml " } 。 @RunWith(SpringJUnit4ClassRunner.class) @ContextHierarchy({ @ContextConfiguration(name = "parent", locations = "/app-config.xml"), @ContextConfiguration(name = "child", locations = "/user-config.xml") }) public class BaseTests {} @ContextHierarchy( @ContextConfiguration(name = "child", locations = "/order-config.xml") ) public class ExtendedTests extends BaseTests {} ExampleA 11.8。 一个类层次结构和覆盖上下文层次结构 配置 与前面的示例,这个示例演示了 如何 覆盖 配置对于一个给定的 命名上下文层次水平通过设置 ContextConfiguration ' s inheritLocations 旗帜 假 。 因此,应用程序上下文 ExtendedTests 将只加载来自哪里 “/测试用户配置xml” 并将其父母 设 置到上下文加载 “/应用程序配置xml” 。 @RunWith(SpringJUnit4ClassRunner.class) @ContextHierarchy({ @ContextConfiguration(name = "parent", locations = "/app-config.xml"), @ContextConfiguration(name = "child", locations = "/user-config.xml") }) public class BaseTests {} @ContextHierarchy( @ContextConfiguration( name = "child", locations = "/test-user-config.xml", inheritLocations = false )) public class ExtendedTests extends BaseTests {} 在一个上下文弄脏一个上下文层次结构 如果 @DirtiesContext 用于 测试其上下文配置作为一个上下文层次结构, hierarchyMode 标记可以用来控 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 203/514 制如何 上下文缓存清除。 详情咨询洽谈 的 @DirtiesContext 在 一个​章节​弹簧测试Annotationsa​​ 和 Javadoc 对于 @DirtiesContext 。 依赖注入的测试夹具 当你使用 DependencyInjectionTestExecutionListener 一个​​这 配置默认情况下一个​​的依赖性测试实例 注入 从豆在应用程序 上下文 你配置了 @ContextConfiguration 。 你可以使用setter 注入,字段注入,或者两者兼有,根据注释你 选择和你是否放在 setter方法或字段。 对于 一致性与注释支持Spring 2.5中引入的, 3.0,您可以使用Spring的 @ autowired 注释或 @ inject 注 释从 JSR 300。 提示 还是和TestContext框架没有仪器的方式 哪一个测试实例。 因此使用 @ autowired 或 @ inject 对于构造 函数没有任何影响 为测试类。 因为 @ autowired 用于 执行 自动装配的 类型 ,如果你有多个bean的定义 相同的类型,你不能依靠这种方法对于那些特别的豆 子。 在这种情况下,您可以使用 @ autowired 在 结合 qualifier 。 像春天的 3.0你也可以选择使用 @ inject 在 结合 @ named 。 另外, 如果您的测试类可以访问它的 ApplicationContext ,您可以执行一个显式的 查找使用(例如)调用 applicationcontext getbean(“titleRepository”) 。 如果你不希望依赖注入应用到您的测试 实例,只是没有注释字段或setter方法 @ autowired 或 @ inject 。 或者,您可以禁用 依赖注入通过显式配置类完全 与 @TestExecutionListeners 和省略 DependencyInjectionTestExecutionListener.class 从 听 众的名单。 考虑场景的测试 HibernateTitleRepository 类,如中概述 这个 目标 部分。 这个 接下来的两个代码清单演示使用 @ autowired 在田野和setter方法。 提出了应用程序上下文配置毕竟示例代码 清单。 注意 依赖注入的行为在以下代码清单 不是特定于JUnit。 同样的DI技术可用于 结合任何测试框架。 下面的例子使调用静态断言方法 如 assertNotNull() 但是没有追加到 电话 断言 。 在这种情况下,假设 方 法是通过一个正确导入 进口 静态 声明这不是示例中所示。 第一个代码清单显示了一个基于junit的实现 测试类,使用 @ autowired 实地 注射。 @RunWith(SpringJUnit4ClassRunner.class) // specifies the Spring configuration to load for this test fixture @ContextConfiguration("repository-config.xml") public class HibernateTitleRepositoryTests { // this instance will be dependency injected by type @Autowired private HibernateTitleRepository titleRepository; @Test public void findById() { Title title = titleRepository.findById(new Long(10)); assertNotNull(title); } } 或者,您可以配置类来使用 @ autowired 看到的setter注入 下面。 @RunWith(SpringJUnit4ClassRunner.class) // specifies the Spring configuration to load for this test fixture @ContextConfiguration("repository-config.xml") public class HibernateTitleRepositoryTests { // this instance will be dependency injected by type private HibernateTitleRepository titleRepository; @Autowired public void setTitleRepository(HibernateTitleRepository titleRepository) { this.titleRepository = titleRepository; } @Test public void findById() { Title title = titleRepository.findById(new Long(10)); assertNotNull(title); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 204/514 } } 前面的代码清单中使用相同的XML上下文文件 引用的 @ContextConfiguration 注释(即 存储库配置xml ),这 看起来像这样: 注意 如果你从一个弹簧提供扩展测试基类, 碰巧使用 @ autowired 在它的一个 setter方法,你可能会有多个 bean类型的影响 在你的应用程序上下文定义:例如,多个 数据源 豆子。 在这种情况下,你 可以覆盖setter方 法和使用 qualifier 注释来表示 具体目标bean如下,但确保委托 覆盖超类中的方法一样。 // ... @Autowired @Override public void setDataSource(@Qualifier("myDataSource") DataSource dataSource) { super.setDataSource(dataSource); } // ... 指定限定符值表示特定的 数据源 bean注入,缩小 这个组类型匹配到特定的bean。 它的值匹配 反对 <限定 符> 中的声明 相应 < bean > 定义。 bean 的名字是用作后备限定符的值,所以你可能会有效 也指向一个 特定的bean的名称(如上图所示,假设 “一”是bean id)。 测试请求和会话作用域的豆子 请求和会话 范围bean 已经由弹簧几年后的现在, 但它总是有点不平凡的去测试它们。 在Spring 3.2 现在是一个微风来测试你 会和会话范围内的豆子 通过以下步骤。 确保 WebApplicationContext 就会加载 你的测试通过标注您的测试类 @WebAppConfiguration 。 注入模拟请求或会话到您的测试实例和 准备你的测试夹具是适当的。 调用您的web组件,你获取 配置 WebApplicationContext (即。 通过依赖注入)。 针对模拟执行断言。 下面的代码片段显示了XML配置 登录用例。 注意, userService bean有一个 依赖一个会 loginAction bean。 同时, loginAction 被实例化使用 ?表情 那检索用户名 和密码从当前HTTP请求。 在我们的测试,我们会想 配置这些请求参数通过模 拟管理 还是和TestContext框架。 ExampleA 11.9。 一个会bean配置 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 205/514 在 RequestScopedBeanTests 我们将两 这个 userService (即。 ,这个主题下的测试) 和 MockHttpServletRequest 到我们的 测试 实例。 在我们 requestScope() 测试方法我们 建立我们的测试夹具通过设置请求参数提供 MockHttpServletRequest 。 当 loginUser() 在我们的方法被调用 userService 我们保证用户服务 访问会 loginAction 为 电流 MockHttpServletRequest (即。 ,我们 只是设置参数)。 然后我们可以执行断言反对 结果基于已知输入的用户名和密码。 ExampleA 11.10。 一个会豆测试 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @WebAppConfiguration public class RequestScopedBeanTests { @Autowired UserService userService; @Autowired MockHttpServletRequest request; @Test public void requestScope() { request.setParameter("user", "enigma"); request.setParameter("pswd", "$pr!ng"); LoginResults results = userService.loginUser(); // assert results } } 下面的代码片段是一个类似我们在上面看到的 一个会豆;然而,这一次的 userService bean依赖于一个会话范围内 userPreferences bean。 注意, userPreferences 实例化bean使用? 表达式,检索 主题 从 当前的HTTP会话。 在我们的测试, 我们将需要配置一个主题 模拟会话管理的还是和TestContext框架。 ExampleA 11.11。 一个会话范围内的bean配置 在 SessionScopedBeanTests 我们把 userService 和 MockHttpSession 到我们的测试实例。 在 我们 sessionScope() 测试方 法我们建立我们的测试 夹具通过设定预期的“主题”属性提供的 MockHttpSession 。 当 processUserPreferences() 在我们 的方法被调用 userService 我们保证用户服务 访问会话范围内 userPreferences 为 电流 MockHttpSession ,我们可以执行 断 言对结果基于配置的主题。 ExampleA 11.12。 一个会话范围内的bean测试 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @WebAppConfiguration public class SessionScopedBeanTests { @Autowired UserService userService; @Autowired MockHttpSession session; @Test public void sessionScope() throws Exception { session.setAttribute("theme", "blue"); Results results = userService.processUserPreferences(); // assert results } } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 206/514 事务管理 在还是和TestContext框架,事务管理 TransactionalTestExecutionListener 。 注意, TransactionalTestExecutionListener 配 置 默认情况下,即使你不显式地声明 @TestExecutionListeners 在你的测试 类。 使支持事务,然而,您必须提供一个 PlatformTransactionManager bean的 应用程序上下文加载 @ContextConfiguration 语义。 在 另外,你必须声明 transactional 要么在类或方法为你的测试水平。 类级别的事务配置(即。 ,设定一个 显式事务管理器bean的名称和默认的回滚 国旗),请参阅 @TransactionConfiguration 条目 注释 支持 部分。 如果事务是不支持整个测试类,你可以 注释方法明确与 transactional 。 控制是否 事务应该坚持一个特定的测试方法,您可以使 用 @Rollback 注释覆盖 类级别默认回滚设置。 AbstractTransactionalJUnit4SpringContextTests 和 AbstractTransactionalTestNGSpringContextTests 是预先配置的事务 支持类 水平。 有时候你需要执行某些代码之前或之后 事务性测试方法但以外的事务上下文,因为 的例子,来验证初始数据库执行之前的状态 测试或验证预期的事务提交行为试验后 执行(如果测试配置不回滚事务)。 TransactionalTestExecutionListener 支持 @BeforeTransaction 和 @AfterTransaction 注释,正是 这样的场景。 只是注释任何 公共无效 方法在您的测试类,其中的一个 注释, TransactionalTestExecutionListener 确保 你 事务方法之前 或 在 事务方法 在适当的执行 时间。 提示 任何 方法之前 (如方法 标注JUnit的 @ before一样 )和任何 在方法 (如的方法 JUnit的 @After )执行 在 一 个事务。 此外,方法 标注 @BeforeTransaction 或 @AfterTransaction 天生不 执行测试标注 @NotTransactional 。 然而, @NotTransactional 是废弃的 Spring 3.0。 以下示例展示了一个基于junit虚构 集成测试场景突出几个事务相关 注释。 咨询 注释支持 为进一步的信息和配置节的例子。 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @TransactionConfiguration(transactionManager="txMgr", defaultRollback=false) @Transactional public class FictitiousTransactionalTest { @BeforeTransaction public void verifyInitialDatabaseState() { // logic to verify the initial state before a transaction is started } @Before public void setUpTestDataWithinTransaction() { // set up test data within the transaction } @Test // overrides the class-level defaultRollback setting @Rollback(true) public void modifyDatabaseWithinTransaction() { // logic which uses the test data and modifies database state } @After public void tearDownWithinTransaction() { // execute "tear down" logic within the transaction } @AfterTransaction public void verifyFinalDatabaseState() { // logic to verify the final state after transaction has rolled back } } 避免假阳性当测试ORM代码 当你测试应用程序代码,操作的状态 Hibernate会话,确保 冲洗 这个 在测试方法的基础会话执行该代码。 失 败 冲洗底层会话可以生产 假 阳性 :您的测试可以通过,但相同的代码抛出 例外在现场、生产环境。 在接下 来的 hibernate的基础例子测试用例,一个方法演示了一个假 积极的,和其他方法正确结果的公开 冲洗会 话。 注意,这适用于JPA和任何其他ORM 框架,维护一个内存 单位 工作 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 207/514 // ... @Autowired private SessionFactory sessionFactory; @Test // no expected exception! public void falsePositive() { updateEntityInHibernateSession(); // False positive: an exception will be thrown once the session is // finally flushed (i.e., in production code) } @Test(expected = GenericJDBCException.class) public void updateWithSessionFlush() { updateEntityInHibernateSession(); // Manual flush is required to avoid false positive in test sessionFactory.getCurrentSession().flush(); } // ... 还是和TestContext框架支持类 JUnit支持类 这个 org.springframework.test.context.junit4 包提供了支持类基于JUnit 4.5 +测试 情况下。 AbstractJUnit4SpringContextTests : 抽象基类,测试集 春天 还是和TestContext框架 使用显式的 ApplicationContext 测试支持在一个 JUnit 4.5 +环境。 当你扩展 AbstractJUnit4SpringContextTests ,你可以 访问以下 保护 实例 变量: ApplicationContext :使用这个变量 执行明确的bean查找或测试状态的 环境作为一个整体。 AbstractTransactionalJUnit4SpringContextTests : 文摘 事务性 扩展的 AbstractJUnit4SpringContextTests 这也 增加 了一些方便的功能用于JDBC访问。 预计 javax.sql.DataSource bean和 PlatformTransactionManager bean 被定义在 ApplicationContext 。 当 你延长 AbstractTransactionalJUnit4SpringContextTests 你可以访问以下 保护 实例 变量: ApplicationContext :继承 这个 AbstractJUnit4SpringContextTests 超类。 使用这个变量执行显式的bean查找 或 测试上下文的状态作为一个整体。 JdbcTemplate :使用该变量 执行SQL语句来查询数据库。 这样的查询可以 被用来确认数据库状态两个 之前 到 和 在 执行 数据库相关的应用程序代码,和弹簧确保 这样的查询运行在同一事务的范围的 应用程序代码。 当结合使用一个 ORM工具, 一定要避免 假 阳性 。 提示 这些类是一个方便的扩展。 如果你不 想要您的测试类绑定到一个spring特定类 一个​层次​举个例子,如果你 想直接扩展类 你是测试一个​​您可以配置自己的自定义测试类的 使用 @RunWith(SpringJUnit4ClassRunner.class) , @ContextConfiguration , @TestExecutionListeners ,所 以 在。 弹簧JUnit跑 这个 春天还是和TestContext框架 提供 全面整合与JUnit 4.5 +通过一个自定义的跑步者(测试 一个​​JUnit 4.5 4.10)。 通过标注 测试类 @RunWith(SpringJUnit4ClassRunner.class) 、开发人员 可以实现标准基于junit单元测试和集成测试和 同时获得益 处还是和TestContext框架如 支持加载应用程序上下文,依赖注入的测试 情况下,事务测试方法执行,等等。 这个 下面的代码清 单显示的最小要求 配置一个测试类来运行自定义弹簧跑步者。 @TestExecutionListeners 配置 用一个空的列表以禁用默认的 听众,这 否则将需要一个ApplicationContext配置通过 @ContextConfiguration 。 @RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners({}) public class SimpleTest { @Test public void testMethod() { // execute test logic... } } TestNG支持类 这个 org.springframework.test.context.testng 包提供了支持基于测试用例的类TestNG。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 208/514 独立项目 Spring框架包含在3.2之前,Spring MVC测试 框 架已经存在作为一个单独的项目在GitHub上它 增长和演化的实际使用,反馈,和贡献的 许多。 独立 弹簧测试mvc 项目 仍在GitHub,可用于 结 合Spring框架里。 应用升级到3.2 应该取代 弹 簧测试mvc 依赖与 一个依赖 弹簧测试 。 这个 弹簧测试 模块使用一个不同的 包 org.springframework.test.web 但 否则 是几乎相同的只有两个例外。 一个是支持 在 3.2新特性(如异步web请求)。 其他相关 的选项 来创建一个 MockMvc 实例。 在Spring框架 3.2,这只能通过还是和TestContext 框架,它提供 缓存加载的好处 配置。 AbstractTestNGSpringContextTests : 抽象基类,测试集 春天 还是和TestContext框架 使用显式的 ApplicationContext 测试支持在一个 TestNG的环境。 当你扩展 AbstractTestNGSpringContextTests ,你可以 访问以下 保护 实例 变量: ApplicationContext :使用这个变量 执行明确的bean查找或测试状态的 环境作为一个整体。 AbstractTransactionalTestNGSpringContextTests : 文摘 事务性 扩展的 AbstractTestNGSpringContextTests 添加 一 些方便的功能为JDBC访问。 预计 javax.sql.DataSource bean和 PlatformTransactionManager bean 被定义在 ApplicationContext 。 当 你延长 AbstractTransactionalTestNGSpringContextTests , 你可以访问以下 保护 实例 变量: ApplicationContext :继承 这个 AbstractTestNGSpringContextTests 超类。 使用这个变量执行显式的bean查找 或 测试上下文的状态作为一个整体。 JdbcTemplate :使用该变量 执行SQL语句来查询数据库。 这样的查询可以 被用来确认数据库状态两个 之前 到 和 在 执行 数据库相关的应用程序代码,和弹簧确保 这样的查询运行在同一事务的范围的 应用程序代码。 当结合使用一个 ORM工具, 一定要避免 假 阳性 。 提示 这些类是一个方便的扩展。 如果你不 想要您的测试类绑定到一个spring特定类 一个​层次​举个例子,如果你 想直接扩展类 你是测试一个​​您可以配置自己的自定义测试类的 使用 @ContextConfiguration , @TestExecutionListeners 等等, 和手动插装您的测试类 TestContextManager 。 看到的源代码 AbstractTestNGSpringContextTests 对于一个 的例子来测试您的测试类。 11.3.6A Spring MVC的测试框架 这个 Spring MVC的测试框架 提供第一 类JUnit测试支持客户端和服务器 端Spring MVC代码 通过一个流利的API。 通常它加载实际的Spring配置 通过 还是和TestContext框架 和总是使用 DispatcherServlet 处理请求因 此 近似完全集成测试不需要运行Servlet 集装箱。 客户端测试 RestTemplate 的和 允许测试的代码的依赖 RestTemplate 不 需要一个运行的服务器 回应请求。 服务器端测试 Spring Framework 3.2之前,最可能的方法来测试一个春天 MVC控制器是 编写一个单元测试,实例化 控制器,它与模拟或存根注入依赖项,然后调用 其 方法,直接使用 MockHttpServletRequest 和 MockHttpServletResponse 在必要时。 虽然这是很容易做到的,控制器有很多 注释,大部分还有待验证。 请求映 射、数据绑定, 类型转换和验证只是几个例子的什么不是 测试了。 此外,还 有其他类型的注释等方法 @InitBinder , @ModelAttribute ,和 @ExceptionHandler 得到调用, 请求处理的一部分。 Spring MVC测试背后的想法是能够重写这些 控制器测试通过执行实际的请求和生成反应, 因为他们将在运行时,一路上调用控 制器通过 Spring MVC DispatcherServlet 。 控制器可以 还是注射模拟依赖,所以测试可以保持关注 web层。 Spring MVC测试建立在熟悉的“模拟”的实现 Servlet API中可用 弹簧测试 模块。 这允许执行请求和生成反应没有 需要运 行在一个Servlet容器。 大部分 一切都应该工作在运行时是否除了JSP 渲染,不提供一个Servlet容器之外。 此外,如果您熟悉如 何 MockHttpServletResponse 的作品,你就会知道 转发和重定向不实际执行。 相反“转发” 和“重定向”url保存,可以断言 在测试中。 这意味着 如果您使用的是JSP,您可以验证JSP页面请求 被转发。 其他一切呈现包括 @ResponseBody 方法和 视图 类型(除了jsp等) Freemarker,速度,和其他Thymeleaf渲染HTML、JSON、 XML等应按预期工作,并且响应将包含 生成的内容。 下面是一个例子,一个测试请求帐户信息在 JSON格式: import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("test-servlet-context.xml") 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 209/514 public class ExampleTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } @Test public void getAccount() throws Exception { this.mockMvc.perform(get("/accounts/1").accept("application/json;charset=UTF-8")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.name").value("Lee"); } } 测试依赖 WebApplicationContext 支持 还是和TestContext框架 。 它加载弹簧 配置从一个XML配置文件位于相同的包 为测 试类(也支持JavaConfig)和注入创建的 WebApplicationContext 到测试所以 MockMvc 可以用它创建的实例。 这个 MockMvc 然后被用来执行 请求 “/账户/ 1” 和验证结果 响应状态是200,响应内容类型 “application / json” 和响应 内容有一个JSON 财产称为“名称”与“李”的价值。 JSON内容是检查 借助Jayway的 JsonPath项目 。 还有很多其他的选 择结果的验证 执行请求和那些将在稍后讨论。 静态导入 的流利的API在上面的例子中需要几个静态 进口,如 MockMvcRequestBuilders。* , MockMvcResultMatchers。* ,和 MockMvcBuilders。* 。 一个简单的方法来找到这些 类是搜索类型匹配 “MockMvc *” 。 如果使用Eclipse,一定要添加它 们 为“最喜欢的静态成员“在Eclipse首选项下 Java - >编辑- > - >内容帮助 最爱 。 这将允许使用内容帮助之后 输入的第一 个字符的静态方法的名字。 其他ide(如。 IntelliJ)可能不需要任何额外的配置。 只是检查 支持代码完成静态成员。 设置选项 服务器端测试设置的目标是创建一个实例 MockMvc 可用于执行请求。 有两个主要选项。 第一个选项是指通过Spring MVC的配置 这个 还是和TestContext框架 ,加载弹簧 配置和注入 WebApplicationContext 进入 测试 用来创建一个 MockMvc : @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("my-servlet-context.xml") public class MyWebTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } // ... } 第二个选择是简单的注册一个控制器实例 没有加载任何Spring配置。 而不是Spring MVC的基本 配置适合测试注释的控制器 自动创建的。 创建的配置相比 MVC JavaConfig(和MVC的名称空间),可以定制 一个学位,通过构建式方法: public class MyWebTests { private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); } // ... } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 210/514 你应该使用哪个选项? 这个 “webAppContextSetup” 加载实际 Spring MVC配置导致一个更完整的集成 测试。 自 还是和TestContext框架 缓存 加载Spring配置,它有助于保持测试运行快甚至 随着越来越多的测试得到补充。 此外,你可以注入模拟服务 到控制器通过 Spring配置,为了保持 专注于测试web层。 下面是一个示例声明 mock服务5: 然后你可以注入mock服务到测试顺序设置 和验证的期望: @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("test-servlet-context.xml") public class AccountTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Autowired private AccountService accountService; // ... } 这个 “standaloneSetup” 另一方面是 有点接近一个单元测试。 它测试一个控制器在一个时间, 控制器可以注射模拟依赖手 工,和它 不需要加载Spring配置。 这样的测试更 集中在风格和使它更容易看到哪个控制器 测试,是否有具体的Spring MVC配 置需要 工作,等等。 “standaloneSetup”也是一个非常方便的方式 写特别的测试,以验证一些行为或调试 问题。 就像集成与单元测试,没有正确的或 错误的答案。 使用“standaloneSetup”确实意味着需要一些 额外 的“webAppContextSetup”测试,验证了Spring MVC 配置。 或者,你可以决定写所有测试用 “webAppContextSetup”和 总是测试与实际的Spring MVC 配置。 执行请求 执行请求,使用适当的HTTP方法和 额外的构建式方法对应的属性 MockHttpServletRequest 。 例如: mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON)); 除了所有的HTTP方法,您还可以执行文件 上传请求,它在内部创建一个实例 MockMultipartHttpServletRequest : mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8"))); 查询字符串参数可以指定的URI 模板: mockMvc.perform(get("/hotels?foo={foo}", "bar")); 或通过添加Servlet请求参数: mockMvc.perform(get("/hotels").param("foo", "bar")); 如果应用程序代码依赖于Servlet请求参数, 不检查查询字符串,通常是这样,那么它 不管你多么添加参数。 请记住, 参数中提供 的URI模板将解码而 参数提供的 参数(…) 方法 预计将解码。 在大多数情况下它比保留上下文路径和 Servlet路径从请求URI。 如果你必须测试和完整的 请求URI,一定要设置 contextPath 和 servletPath 因此,请求映射 将工作: mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main")) 看着上面的例子中,它将繁琐的设置 servletPath contextPath和与每个执行的请求。 这就是为什么 你可以定义默认请求属性 当构建 MockMvc : public class MyWebTests { private MockMvc mockMvc; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 211/514 @Before public void setup() { mockMvc = standaloneSetup(new AccountController()) .defaultRequest(get("/") .contextPath("/app").servletPath("/main") .accept(MediaType.APPLICATION_JSON).build(); } } 上面的属性将应用于每个请求执行 通过 MockMvc 。 如果相同的属性 还指定在一个给定的请求,它将覆盖默认值。 这就是为 什么,HTTP方法和URI并不重要,当设置 默认请求属性,因为他们必须被指定在每个 请求。 定义预期 期望可以定义通过附加一个或更多 .andExpect(. .) 在调用来执行 要求: mockMvc.perform(get("/accounts/1")).andExpect(status().isOk()); MockMvcResultMatchers。* 定义了大量的 静态成员,其中一些与其他方法的返回类型, 因为坚持执行的结果要求。 秋天的断 言 在两大类。 第一类断言验证的属性 反应,我。 e响应状态,标题和内容。 这些都是 最重要的事情来测试。 第二类断言超越响应,和 允许检查Spring MVC特定构造如这 处理请求的控制器方法,无论是一个例外是 提出和处理,模型的内 容是,什么观点 选择,flash也加入进来,属性等等。 它也 可以验证特定的构造(如Servlet请求和 会话属性。 下面的测试断言绑 定/验证 失败: mockMvc.perform(post("/persons")) .andExpect(status().isOk()) .andExpect(model().attributeHasErrors("person")); 很多时候,当写作测试,它是有用的结果转储 执行的请求。 这个可以做如下,在那里 print() 是一个静态导入的 MockMvcResultHandlers : mockMvc.perform(post("/persons")) .andDo(print()) .andExpect(status().isOk()) .andExpect(model().attributeHasErrors("person")); 只要请求处理导致一个未处理的异常, print() 方法将打印所有可用的 结果数据 system . out 。 在某些情况下,您可能想要直接访问结果 和验证一些,否则不能验证。 这可以 通过附加 .andReturn() 最后在 所有的期望: MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn(); // ... 当所有的测试重复相同的期望,您可以定义 常见的预期一旦当构建 MockMvc : standaloneSetup(new SimpleController()) .alwaysExpect(status().isOk()) .alwaysExpect(content().contentType("application/json;charset=UTF-8")) .build() 注意,期望 总是 应用 和不能被覆盖,不创建一个单独的 MockMvc 实例。 当JSON响应内容包含超媒体链接创建 与 春天 HATEOAS ,生成的链接可以验证: mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people")); 当XML响应内容包含超媒体链接创建 春天 HATEOAS ,生成的链接可以验证: Map ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom"); mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML)) .andExpect(xpath("/person/ns:link[@rel='self']/@href", ns).string("http://localhost:8080/people")); 滤波器注册 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 212/514 当建立一个 MockMvc ,你可以 注册一个或多个 滤波器 实例: mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build(); 过滤器将调用注册通过 MockFilterChain 从 弹簧测试 和过去的过滤器将代表 这个 DispatcherServlet 。 进一步的服务器端测试例子 该框架的测试包括 许多 样品测试 为了演示如何使用Spring MVC 测试。 浏览这些例子进一步的想法。 也 spring mvc展示 已经全部测试覆盖基于Spring MVC测试。 端REST测试 客户端测试代码使用 RestTemplate 。 预期的目标是定义 请求和提供“存根”反应: RestTemplate restTemplate = new RestTemplate(); MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); mockServer.expect(requestTo("/greeting")).andRespond(withSuccess("Hello world", "text/plain")); // use RestTemplate ... mockServer.verify(); 在上面的例子中, MockRestServiceServer ——中央类测试,配置客户端休息 RestTemplate 用一个自定义的 ClientHttpRequestFactory 声称 实际的请求与预期并返回“存根”反应。 在 这种情况下我们期望单个请求“/问候”,想返回 200年响应以“text / plain”内容。 我们可以定义许多 额外的请求和响应是必要的。存根 一旦预期请求和存根反应已经定义, RestTemplate 可以用在客户端代码吗 通常的。 最后的测试 mockServer.verify() 可以用 来验证所有期望的要求进行。 静态导入 就像服务器端测试,流利的API,用于客户端 测试需要几个静态导入。 这些都很容易找到的 搜索 “MockRest *” 。 Eclipse用 户应该添加 “MockRestRequestMatchers。*” 和 “MockRestResponseCreators。*” 为“最喜欢的 静态成员“在 Eclipse首选项下 Java - > - >编辑- >内容帮助最爱 。 这允许 使用内容辅助输入第一个字符后的静态 方法名。 其他ide(例 如。IntelliJ)可能不需要任何额外的 配置。 只是检查支持代码完成静态 成员。 进一步的例子,其他的客户端测试 Spring MVC测试的测试包括 例子 测试 其他客户端测试。 11.3.7A宠物诊所的例子 PetClinic应用程序很好地,可以从 样本库 ,说明了 的几个特点 春天还是和TestContext框架 在JUnit 4.5 +环境。 大多数测试功 能包含在 AbstractClinicTests ,对于这部分清单 如下所示: import static org.junit.Assert.assertEquals; // import ... @ContextConfiguration public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests { @Autowired protected Clinic clinic; @Test public void getVets() { Collection vets = this.clinic.getVets(); assertEquals("JDBC query must show the same number of vets", super.countRowsInTable("VETS"), vets.size()); Vet v1 = EntityUtils.getById(vets, Vet.class, 2); assertEquals("Leary", v1.getLastName()); assertEquals(1, v1.getNrOfSpecialties()); assertEquals("radiology", (v1.getSpecialties().get(0)).getName()); // ... } // ... } 注释: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 213/514 这个测试用例扩展了 AbstractTransactionalJUnit4SpringContextTests 类,它继承了配置用于依赖注入 (通过 DependencyInjectionTestExecutionListener )和 事务行为(通过 TransactionalTestExecutionListener )。 这个 诊所 一个​​实例变量的 应用程序对象正在测试一个​​设置依赖项注入 通过 @ autowired 语义。 这个 testGetVets() 方法说明了 如何使用继承吗 countRowsInTable() 方法容易验证 的行数在一个给定的表,从而验证正 确的行为 被测试的应用程序代码。 这允许更强的测试 和减少依赖准确的测试数据。 例如,您可以 在数据库中添加额外的 行不打断测试。 像许多集成测试,使用一个数据库,大部分的 测试 AbstractClinicTests 取决于 最小数量的数据在数据库中已经在测试用例 运行。 或者,您可以选择填充数据库内 测试夹具设置测试用例的一个​​再次,在相同的 事务作为测试。 PetClinic应用程序很好地支持三种数据访问技术: JDBC,Hibernate和JPA。 通过声明 @ContextConfiguration 没有任何特定 的 资源的位置, AbstractClinicTests 类 将其应用程序上下文加载默认位置, AbstractClinicTests-context.xml ,它声明了一个 常见 数据源 。 子类指定附加 上下文位置,必须声明一个 PlatformTransactionManager 和一个具体的 实施 诊所 。 例如,Hibernate实现的测试等 包含以下的实现。 对于这个示例, HibernateClinicTests 不包含一个单一的线吗 代码:我们只需 要声明 @ContextConfiguration 和测试 继承 AbstractClinicTests 。 因为 @ContextConfiguration 声明没有 任何特定的 资源的位置, 春天还是和TestContext 框架 加载一个应用程序上下文从所有的豆子 定义在 AbstractClinicTests-context.xml (即。 , 继承位置)和 HibernateClinicTests-context.xml , HibernateClinicTests-context.xml 可能覆盖 bean定义在 AbstractClinicTests-context.xml 。 @ContextConfiguration public class HibernateClinicTests extends AbstractClinicTests { } 在一个大型的应用程序中,Spring配置通常是 跨越多个文件。 因此,配置位置 通常一个共同的基类中指定的所有特定于应用程 序的 集成测试。 这样的一个基类也可能添加有用的实例 变量一个​​人口依赖注入,自然一个​​如 SessionFactory 对于一个应用程 序使用 Hibernate。 只要有可能,你应该完全相同的弹簧 配置文件在已部署的集成测试 环境。 一个可能的角度的差别是数据库连接 池和事务基础 设施。 如果你部署一个 成熟的应用程序服务器,您可能会使用它的连接池 (可以通过JNDI)和JTA的实现。 因此在生产你 将使用 一个 JndiObjectFactoryBean 或 < jee:jndi查找> 为 数据源 和 JtaTransactionManager 。 JNDI和JTA不会 可在容器外集成 测试,那么你应该使用一个 像下议院DBCP组合 BasicDataSource 和 DataSourceTransactionManager 或 HibernateTransactionManager 为他们。 你可以 这个差异的行为因素到一个单独的XML文件,有选择 在应用程序服务器和一 个“当地”配置独立于所有 其他配置,这将不是测试和生产之间的不同 环境。 此外,建议使用属性文件 连接设置。 看到 PetClinic应用程序很好地为例。 11.4进一步资源 参考下面的参考资料了解更多信息 测试: JUnit : 一个​​ 一个面向程序员测试框架为Java 一个​​ 。 使用Spring框架的测试套件。 TestNG :一个测试 灵感来自JUnit框架添加了支持Java 5注释, 测试组,数据驱动测试,分布式测试等。 MockObjects.com :网站 致力于模拟对象,一个技术,提高设计的代码 在测试驱动开发。 “模拟 对象” :在维基百科上的文章。 EasyMock :Java 图书馆 一个​​ 模仿对象提供的接口(和 对象通过类扩展)通过生成它们在飞行中使用 Java的代理机制。 一 个​​ 使用Spring框架 在它的测试套件。 JMock :图书馆, 支持测试驱动开发的Java代码与模仿对象。 5 :Java模拟 图书馆基于 测试间谍 模式。 DbUnit : JUnit扩展(也可用与Ant和Maven)目标 数据库驱动的项目,在其他方面,使您的数据库 到一个已知状态测试运行之 间。 这个 磨床 :Java负载测试框架。 PartA IV。 一个数据访问 这部分的参考文档涉及数据 访问和之间的交互数据访问层和 业务或服务层。 春天的综合事务管理支持覆盖 在一些细节,其次是全面覆盖各种各样的数据访问 框架和技术,Spring框架集成 与。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 214/514 ChapterA 12, 事务管理 ChapterA 13, DAO支持 ChapterA 14, 数据访问与JDBC ChapterA 15, 对象关系映射(ORM)数据访问 ChapterA 16, 编组XML使用O / X映射器 12。 一个事务管理 12.1一个介绍Spring框架事务管理 综合事务支持是其中最引人注目的 理由使用Spring框架。 Spring框架提供了一个 一致的抽象为事务管理,提供 以下好处: 一致的编程模型在不同事务api 如Java Transaction API(JTA)、JDBC、Hibernate、Java持久性 API(JPA)和Java数据对象 (JDO)。 支持 声明 事务管理 。 简单的API,用于 编程 事务 管理比复杂事务api如JTA。 优秀的整合与Spring的数据访问 抽象。 以下部分描述了Spring框架的事务 的增值和技术。 (本章还包括讨论 最佳实践、应用服务器集成,解决常见的 问题。) 优势的弹簧 框架的事务支持模型 描述 为什么 你会使用Spring框架的 事务抽象而不是EJB容器管理的事务 (CMT)或选择驱 动本地事务通过专有 如Hibernate API。 理解春天 框架事务抽象 概述了核心类和 描述了如何配置和获取 数据源 实例从各种各样的 来源。 同步 资源交易 描述如何在应用程序代码 确保资源创建、重用,并清理干净 正确。 声明式事务 管理 描述了支持声明式事务 管理。 编程 事务管理 覆盖支持编程(, 是,显式地进行编码)事务管理。 12.2一个优势的Spring框架的事务支持模型 传统上,Java EE开发人员有两种选择 事务管理: 全球 或 当地 交易,它们都有深刻的 的局限性。 全球和本地事务管理了 接下来 的两个部分,然后讨论了如何Spring框架的 事务管理支持地址的限制全球和 本地事务模型。 12.2.1A全局事务 全局事务使您能够处理多个事务 资源,一般关系数据库和消息队列。 这个 应用服务器管理全局事务通过JTA,这是 一个笨重的 API来使用(部分是由于其异常模型)。 此外,一个JTA UserTransaction 通常需要来自JNDI,意味着你 也 需要使用JNDI为了使用 JTA。 显然使用全局事务将限制任何潜在的重用 应用程序代码,因为JTA通常仅能在一个应用程序 服务器环境。 此前,首选的方法是通过使用全局事务 EJB cmt ( 容器管理 事务 ):CMT是形式的 声明式事务管理 ( 有别于 程序性事务 管理 )。 EJB CMT删除事务相关的需要 JNDI查找,当然,使用EJB本身就需要 使用JNDI。 它删除的大部分,但不是全部需要编写Java代码 控制事务。 重大的缺点是CMT挂钩 JTA和一个应用程序服务器环境。 同时,它才可用 一个选择实现业务逻辑ejb,或者至少后面 事务性EJB facade。 底片的EJB一般来说是如此伟大 这不是一个有吸引力的,尤其是在面对 引人注目的方法声明式事务管理。 12.2.2A本地事务 本地事务是特定于资源,如一个事务 关联到一个JDBC连接。 本地事务可能会更容易 使用,但有明显的缺点:他们无法跨越 多个 事务资源。 例如,代码管理 交易使用JDBC连接不能运行在一个全球JTA 事务。 因为应用程序服务器是没有参与 事务管理,它不 能帮助确保正确性跨越 多个资源。 (值得注意的是,大多数应用程序使用 单独的事务资源。) 另一个缺点是当地的 事务是侵入 性的编程模型。 12.2.3A Spring框架的一致的编程模型 春天解决全球和本地的缺点 事务。 它使应用程序开发人员使用 一致 编程模型 在任何 环境 。 你写你的代码一次,并且它可以 受益 从不同的事务管理策略在不同的 环境。 Spring框架提供了声明式和 编程式事务管理。 大多数用户喜欢声明 事务管理,建 议在大多数情况下。 与程序性事务管理,开发人员的工作 Spring框架事务的抽象,它可以运行在任何 底层事务基础设施。 与首选的声明式模型,开发 人员通常写小或 没有代码相关的事务管理,因此不依赖 Spring框架事务API,或任何其他事务 API。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 215/514 你需要一个应用服务器的事务 管理? Spring框架的事务管理支持变化 传统的规则,当 一个企业Java应用程序的需要 一个应用程序服 务器。 特别是,您不需要一个应用程序服务器的简单 声 明性事务通过ejb。 事实上,即使你的 应用程序 服务器具有强大功能JTA,你可能决定 Spring框 架的声明性事务提供更多的力量和一个 更有效 率的编程模型与EJB CMT。 通常你需要一个应用程序服务器的JTA能力只有 如果您的应用程序需要处理多个事务 资源,而不 是一个要求对于很多应用程序。 许多 高端应用 程序使用一个单一的、高度可扩展的数据库(如 Oracle RAC)相反。 独立的事务管理器等 于 Atomikos交易 和 JOTM 是其他 选项。 当然, 你可能需要其他应用服务器功能 如Java消息服 务(JMS)和J2EE连接器体系结构 (JCA)。 Spring框架 给你选择的什么时候 你的应用程序 和规模完全加载应用程序 服务器 。 的日子一去 不复返了唯一的选择 使用EJB CMT或JTA是写 代码和本地事务如 那些在JDBC连接,面临巨额 返工如果你需要 代码运行在全球,容器管理的事 务。 与 Spring框架,只有一些bean定义在你的 配置文件,而不是你的代码,需要改变。 12.3一个了解Spring框架事务抽象 Spring事务的关键抽象的概念 事务策略 。 一个事务策略是 定义的 org.springframework.transaction.PlatformTransactionManager 接口: public interface PlatformTransactionManager { TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; } 这主要是一个服务提供程序接口(SPI),尽管它 可以用 编程 从你 应用程序代码。 因为 PlatformTransactionManager 是一个 接口 ,它可以很容易地嘲笑或粗短的作为 必要的。 它不是绑定到一个JNDI查找策略等。 PlatformTransactionManager 实现 像任何其他对象定义(或bean)在Spring框架奥委会 集装箱。 这个好处足以使得Spring框架交易一个 有价值的抽象甚至当你使 用JTA。 事务代码可以 被测试更容易比直接使用JTA。 又符合春天的哲学 TransactionException 可以抛出 任何 PlatformTransactionManager 接口的方法是 unchecked (即,它扩 展了 这个 java朗runtimeexception 类)。 事务基础设施故障是几乎总是致命的。 在罕见的 情况下,应用程序代码可以恢复一 个事务 失败,应用程序开发人员仍然可以选择捕捉和处理 TransactionException 。 最关键的一点是 开发人员不 迫使 这样 做。 这个 getTransaction(. .) 方法返回一个 TransactionStatus 对象,根据一个 TransactionDefinition 参数。 这个 返回 TransactionStatus 可能代表 一个新的事务,或能代表一个现有的交易如果匹配 交易存在于当前调用堆栈。 在暗示这 后一种情 况是,与Java EE事务上下文,一个 TransactionStatus 关联到一个 线程 的执行。 这个 TransactionDefinition 接口 指定: 隔离 :的程度 这个交易是孤立于其他事务的工作。 对于 的例子,可以看到未提交该事务从其他写 交易吗? 传播 :通常,所有 事务范围内执行的代码将运行在该事务。 然而,您可以选择指定行为的事件 一个事务性方法时执行一个事 务上下文 已经存在。 对于 示例中,代码可以继续运行在现有的事务( 常见的情况),或现有的事务可以暂停和一个新的 交易 创造了。 Spring提供了所有的事务 传播选项从EJB CMT熟悉 。 阅读 事务传播的语义在春天,看到 SectionA 12 5 7,一个​ propagationa​​​事务 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 216/514 超时 多长时间这 事务运行时间自动回滚 由底层事务基础设施。 只读状态 :一个只读 事务可以用在你的代码读取但不修改数据。 只读事务可以是一个有用的优化在某些情况下, 例如当你 使用Hibernate。 这些设置反映标准事务的概念。 如果 必要的,请参阅参考资料,讨论事务隔离级别 和其他核心事务的概念。 理解这些概念是 基 本使用Spring框架或任何事务管理 解决方案。 这个 TransactionStatus 接口 提供了一个简单的方法来控制事务的事务代码 执行和查询交易状态。 应该熟悉的概念, 因为他 们是常见的所有事务api: public interface TransactionStatus extends SavepointManager { boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackOnly(); boolean isRollbackOnly(); void flush(); boolean isCompleted(); } 无论你选择声明或编程 事务管理在春天,定义正确的 PlatformTransactionManager 实现 是绝对必要的。 你通常定义这个实 现通过 依赖注入。 PlatformTransactionManager 实现通常需要知识的环境中 他们的工作:JDBC,JTA,Hibernate,等等。 下面的例子展示 如何定 义一个地方吗 PlatformTransactionManager 实现。 (这个例子使用纯JDBC)。 你定义一个JDBC 数据源 相关 PlatformTransactionManager 将bean定义的引用 数据源 定义。 它会看起来像这样: 如果你使用JTA在一个Java EE容器然后你使用一个容器 数据源 ,通过JNDI, 结合Spring的 JtaTransactionManager 。 这就是 JTA和JNDI查找版本将会看起来像: 这个 JtaTransactionManager 不需要 知道 数据源 ,或任何其他 特定的资源,因为它使用容器的全局事务 管理基础设施。 注意 上述定义的 数据源 bean 使用 < jndi查找/ > 标签从 JEE 名称空间。 基于架构的更多信息 配置,请参阅 AppendixA E, XML的基于配置 ,和更多 信息 < jee / > 标签看到部分 题为 SectionA e 2 3,​​的 JEE schemaa​​ 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 217/514 您还可以使用Hibernate本地事务容易,见 下面的例子。 在这种情况下,您需要定义一个Hibernate LocalSessionFactoryBean , 您的应用程序 代码将使用获得Hibernate 会话 实例。 这个 数据源 bean定义将 类似于当地的JDBC示例先前所显示的,因此不是 如下面的示例所示。 注意 如果 数据源 使用的任何 非jta事务管理器,通过JNDI查找和管理Java EE容器,那么它应该是事务性的,因为春 天 框架,而不是Java EE容器,将管理 事务。 这个 txManager 在本例中是豆的 HibernateTransactionManager 类型。 以同样的方式 随着 DataSourceTransactionManager 需要一个 参考 数据源 , HibernateTransactionManager 需要一个参考 这个 SessionFactory 。 org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml hibernate.dialect=${hibernate.dialect} 如果您在使用Hibernate和Java EE容器管理的JTA 交易,那么你就应该使用相同的 JtaTransactionManager 与前面的JTA JDBC示例。 注意 如果你使用JTA,然后你的事务管理器定义 看起来一样不管你用什么样的数据访问技术,是它 JDBC、JPA支 持Hibernate或其他技术。 这是由于 事实上,JTA事务是全局事务,可以招募 任何事务性资源。 在所有这些情况下,应用程序代码不需要改变。 你 可以改变交易管理仅仅通过改变配置, 即使这变化意味着要从局部到全局事 务或副 亦然。 12.4一个同步资源交易 它现在应该清楚你如何创建不同的事务 经理,以及他们如何与相关资源需要 同步到事务(例如 DataSourceTransactionManager 到一个JDBC 数据源 , HibernateTransactionManager 到一个Hibernate SessionFactory ,等等)。 本节 描述如何在应用程序代码中,直接或间接地使用 持久性API,如JDBC、Hibernate或JDO,确保这些 资源创建、重 用,并清理干净。 这个部分还 讨论了事务同步触发(可选) 通过相关的 PlatformTransactionManager 。 12.4.1A高级同步方法 首选的方法是使用Spring的最高水平模板 建立持久性api或使用本机集成ORM api 事务——意识到工厂bean或代理来管理本 机 资源工厂。 这些感知事务的解决方案在内部处理 资源创建和重用,清理,可选的事务 同步的资源,和异常的映射。 因此用户数 据 访问代码没有解决这些任务,但可以聚焦 纯粹是在非样板持久性逻辑。 通常,您使用 本机ORM API或采取一个 模板 方法用 于JDBC 访问通过使用 JdbcTemplate 。 这些解决方案 在接下来的章节中详细的参考文档。 12.4.2A低级同步方法 类比如 DataSourceUtils (JDBC), EntityManagerFactoryUtils (JPA), SessionFactoryUtils (用于Hibernate), PersistenceManagerFactoryUtils (JDO)等 在一个较低的水平上存在。 当你想要的应用程序代码来处理 直接与资源类型的本 地持久化api,您可以使用 这些类来确保适当的Spring框架管理实例 ,给出了事务(可选地)同步,和异常 在这个过程中发生的正确 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 218/514 在哪里 TransactionProxyFactoryBean 吗? 声明式事务配置Spring 2.0的版本 及以上很大 区别的从以前版本的春天。 这个 主要的区别是, 不再有任何需要配置 TransactionProxyFactoryBean 豆子。 2.0配置风格的pre春天依然是100%有效的 配 置;认为新 < tx:标签/ > 作为 简单地定义 TransactionProxyFactoryBean bean 代 表你。 映射到一个一致的 API。 例如,JDBC的情况,而不是传统的JDBC 方法调用 getConnection() 方法 数据源 ,你相反使用Spring的 org.springframework.jdbc.datasource.DataSourceUtils 类如下: Connection conn = DataSourceUtils.getConnection(dataSource); 如果现有的事务已经有一个连接同步 (链接),该实例返回。 否则,该方法调用 触发器创建一个新的连接,这是(可选) 同步到任何现 有的事务,并把这些信息 随后的重用在同一事务。 如前所述,任何 SQLException 包装在一个春天 框架 CannotGetJdbcConnectionException 之一, Spring框架的层次结构的DataAccessExceptions无限制。 这 方法让你可以获 得更多的信息比很容易从 SQLException ,并确保可移植性 跨数据库,甚至跨越不同的持久化技术。 这种方法还没有春天事务管理工作 (事务同步是可选的),所以你可以使用它不管 不是你是使用Spring事务管理。 当然,一旦你使用了Spring JDBC支持,JPA支持 或Hibernate支持,你通常不喜欢使用 DataSourceUtils 或其他辅助类, 因为你会 变得更快乐工作穿过春天的抽象 比直接与相关的api。 例如,如果您使用弹簧 JdbcTemplate 或 jdbc对象 包来简化你的使用 JDBC,正确连接检索 发生在幕后,你不需要写任何特别 代码。 12.4.3A TransactionAwareDataSourceProxy 非常最低级别的存在 TransactionAwareDataSourceProxy 类。 这是一个 代理为目标 数据源 ,这 包装了目标 数据源 添加 spring管理事务的意识。 在这方面,它是类似的 一个事务性JNDI 数据源 作为 提供了一个Java EE服务器。 它应该几乎从不是必要或可取的使用这个 类,除了现有的代码必须调用和通过了一项标准 JDBC 数据源 接口的实现。 在这种情 况下,它是可能的,这个代码是可用的,但参与 在春季管理事务。 最好是写你的新代码 通过使用上面提到的更高层次的抽象。 12.5一个声明式事务管理 注意 大多数Spring框架用户选择声明式事务 管理。 这个选项有至少影响应用程序代码, 因此是最符合理想的一 个 非侵入性的 轻量级容器。 Spring框架的声明式事务管理了 可能与Spring面向方面编程(AOP),虽然,作为 事务方面代码带有Spring框架分布 和可以用于一 个样板时尚,AOP概念不一般 必须理解有效地利用这段代码。 Spring框架的声明式事务管理是相似的 到EJB CMT,您可以指定事务行为(或缺乏) 方法级到个人。 就可以做 setRollbackOnly() 调用事务内 如果有必要,上下文 两者的不同类型的事务 管理是: 与EJB CMT,这与JTA,Spring框架的 声明式事务管理工作在任何环境。 它可以 使用JTA事务或本地事务使用JDBC、JPA, Hibernate或JDO通过简单的调整配置文件。 你可以使用Spring框架声明式事务 管理任何类,而不只是特殊的类,比如 ejb。 Spring框架提供了声明 回滚 规则 , 一个功能没有EJB等效。 两 编程和声明支持回滚规则 提供了。 Spring框架使您可以自定义事务 行为,通过使用AOP。 例如,您可以插入自定义行为 事务回滚的情况。 你也可以添加任意 的建议, 随着事务的建议。 与EJB CMT,你不能影响 容器的事务管理除外 setRollbackOnly() 。 Spring框架不支持事务的传播 在远程调用上下文,高端应用服务器。 如果 你需要这个功能,我们建议您使用EJB。 然而, 使 用前仔细考虑这样一种特性,因为正常情况下,一个 不希望交易跨越远程调用。 回滚规则的概念很重要:他们让你 指定哪些异常(和throwables)应该 导致 自动回滚。 你指定这个声明,在 配置,而不是Java代码。 所以,尽管你仍然 可以调用 setRollbackOnly() 在 TransactionStatus 对象可以回滚 回当前 事务,通常您可以指定一个规则, MyApplicationException 必须总是结果 在回滚。 其显著的优势,这个选项是业务 对象不依赖于事务基础设施。 例 如,他们 通常不需要进口弹簧事务api或其他弹簧 api。 虽然EJB容器的默认行为的自动回滚 事务在一个 系统异常 (通常是一个运 行时 例外),EJB CMT不回滚事务自动在一个 应用程序异常 (即检查异常 除 了 java rmi remoteexception )。 而 春天的默认行为的声明式事务管理 遵循 EJB公约(回滚是自动只在未经检查的异常),它 经常是有用的定制此行 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 219/514 为。 12.5.1A了解Spring框架的声明性事务 实现 它并不足以告诉你简单的注释你类 与 transactional 注释,添加 @EnableTransactionManagement 你 配置,然后期待你了解 它是如何运作的。 这 部分解释了Spring框架的内部运作的 声明式事务基础设施在发生 事务相关的问题。 最重要的概念掌握关于春天 框架的声明式事务的支持,这种支持是 启用 通过AOP 代理 ,事务性的建议是驱动 通过 元数据 (目 前XML -或基于注释)。 结合AOP与元数据收益率AOP代理事务 使用 TransactionInterceptor 结合 用一个合适 PlatformTransactionManager 实现驱动交易 约方法 调用 。 注意 Spring AOP覆盖着 ChapterA 9, 面向方面的编程与弹簧 。 从概念上讲,调用一个方法在一个事务代理看起来像 这…… 12.5.2A声明式事务实现的例子 考虑下面的界面,和它的服务员 实现。 这个示例使用 foo 和 酒吧 类作为占位符,这样您就可以 专注于事务的使用没有专注于一 个特定的 域模型。 对于本示例的目的,这一事实 DefaultFooService 类抛出 UnsupportedOperationException 实例 在身体 的每个实现的方法是好的,它可以让你看到 创建回滚事务,然后在回应的 UnsupportedOperationException 实例。 // the service interface that we want to make transactional package x.y.service; public interface FooService { Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo); void updateFoo(Foo foo); } // an implementation of the above interface package x.y.service; public class DefaultFooService implements FooService { public Foo getFoo(String fooName) { throw new UnsupportedOperationException(); } public Foo getFoo(String fooName, String barName) { throw new UnsupportedOperationException(); } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 220/514 public void insertFoo(Foo foo) { throw new UnsupportedOperationException(); } public void updateFoo(Foo foo) { throw new UnsupportedOperationException(); } } 假设前两个方法 fooService 接口 , getFoo(字符串) 和 getFoo(String,String), 必须执行事务的上下文中与只读 语义,和其他的 方法 ,insertFoo(Foo) 和 updateFoo(Foo), 必须执行上下文中的一个吗 事务与读写语义。 下面的配置是 详细解释在接下来的 几个段落。 检查前面的配置。 你想让一个服务 对象, fooService 豆、事务。 这个 事务语义被封装在应用 < tx:建议/ > 定义。 这个 < tx: 建议/ > 定义读起来 一个​​ … 所有的方法在开始 ' GET ' 是执行上下文中的只读 事务,和所有其他的方法都是用默认执行 事务语 义 一个​​ 。 这个 事务管理器 属性的 < tx:建议/ > 标签被设置的名称 PlatformTransactionManager bean, 去 驱动 交易,在这 种情况下, txManager bean。 提示 你可以省略的 事务管理器 属性在事务的建议 ( < tx:建议/ > )如果bean的名称 PlatformTransactionManager 那你 想线在有名字 transactionManager 。 如果 这个 PlatformTransactionManager bean 那你想线在有任何其他的名字,那么你必须使用 事务管理器 属性明 确,如 前面的例子。 这个 < aop:config / > 定义确保 定义事务的建议 txAdvice bean 执行在适当的分项目。 首先定义一个 切入点匹配执行任何 操作的定义 fooService 接口 ( fooServiceOperation )。 然后你将 切入点与 txAdvice 使用一个顾问。 这个 结果表明,在执行 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 221/514 fooServiceOperation ,建议定义为 txAdvice 将运行。 表达式中定义 < aop:切入点/ > 元素是一个AspectJ切入点 表达式;看到 ChapterA 9, 面向方面的编程与弹簧 为更多的细节在 切入点 表达式在Spring 2.0。 一个共同的要求就是让整个服务层 事务。 最好的方法是简单地改变切入点 表达式来匹配任何操作在你的服务层。 对于 示例: 注意 在这个例子中它是假定你所有的服务 接口中定义的 x y服务 包,看到 ChapterA 9, 面向方面的编程与弹簧 更多 细节。 现在,我们已经分析了配置,您可能会问 你自己, 一个​​ 好吧…… 但是什么配置 其实做什么? 一个​​ 。 上面的配置将被用来创建一个事务 代理在对象创建的 fooService bean定义。 这个 代理将配置事务的建议,因此,当一个 调用 适当的方法 在代理 ,一个 事务开始,暂停,标记为只读的,等等, 根据事务的配置与之关联的方法。 考虑下面的程序,测试驱动器上 面 配置: public final class Boot { public static void main(final String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class); FooService fooService = (FooService) ctx.getBean("fooService"); fooService.insertFoo (new Foo()); } } 运行的输出将类似于前面的程序 以下。 (Log4J输出和堆栈跟踪的 抛出UnsupportedOperationException由insertFoo(. .)方 法 DefaultFooService类已经被截断为清晰起见)。 [AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'fooService' with 0 common interceptors and 1 specific interceptors [JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService] [TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo [DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo] [DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction [RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException [TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo due to throwable [java.lang.UnsupportedOperationException] [DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] [DataSourceTransactionManager] - Releasing JDBC Connection after transaction [DataSourceUtils] - Returning JDBC Connection to DataSource Exception in thread "main" java.lang.UnsupportedOperationException at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14) at $Proxy0.insertFoo(Unknown Source) at Boot.main(Boot.java:11) 12.5.3A声明式事务回滚 上一节概述了如何指定的基本知识 事务设置类,通常服务层的类, 在应用程序中声明。 本节描述如何 控制回滚的事务在一个简 单的声明 时尚。 推荐的方式来表示对Spring框架的 交易的基础设施,一个事务的工作是卷 回来是抛出 异常 从代码 这是当前事务的上下文中执 行。 春天 框架的事务基础设施代码将抓住任何未处理的 异常 因为它的泡沫在呼叫 堆栈,并确定是否标志着交易 回滚。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 222/514 在默认配置中,Spring框架的事务 基础设施代码 只有 标志着交易 回滚的情况下运行时,未经检查的异常,即当 抛出异常的子类的 一个实例或 RuntimeException 。 ( 误差 年代也会——默认情况下)的结果 在回滚)。 Checked异常,抛出一个事务性的 方法 做 不 结果在回滚在默认 配置。 您可以配置哪些 异常 马克一个事务的类型 回滚,包括已检查的异常。 下面的XML片段 演示了如何配置回滚一个检查, 特定于 应用程序的 异常 类型。 您还可以指定“不回滚规则”,如果你做 不 想要一个事务回滚,当一个 抛出异常。 下面的例子告诉Spring框架的 事务基础设施 提交服务员事务甚至在 面对一个未处理的 InstrumentNotFoundException 。 当Spring框架的事务基础设施捕获 例外,咨询来确定是否配置回滚规则 标记为回滚的事务, 最强 匹配规则赢了。 所以对于以下 配置,任何 例外之外的其他 InstrumentNotFoundException 结果在一个 回滚事务的服务员。 您还可以显示一个需要回滚 编程 。 虽然很简单,这 过程是相当入侵,代码紧密耦合的春天 框架的事务基础设施: public void resolvePosition() { try { // some business logic... } catch (NoProductInStockException ex) { // trigger rollback programmatically TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } 强烈建议您使用声明性方法 回滚如果可能的话。 程序化的回滚是可用的应该 你绝对需要它,但它的用法苍蝇在面对实现 基于 pojo的架构,清洁 12.5.4A配置不同的事务语义不同 bean 考虑的情况你有许多的服务层 对象,你想申请一个 完全不同 他们每个人的事务配置。 这可以通过定义 截然不同的 < aop:顾 问/ > 元素有不同 切入点 和 建议裁判 属性 值。 作为一个点的比较,首先假设你所有的服务 层类定义在一个根 x y服务 包。 让所有bean实例的类的定义在这 包(或子包)和名称 结尾 服务 有默认的事务配置, 你会写以下: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 223/514 下面的例子展示了如何配置两个不同的豆子 与完全不同的事务设置。 12.5.5A < tx:建议/ > 设置 本节总结了各种事务设置 可以指定使用吗 < tx:建议/ > 标签。 默认 < tx:建议/ > 设置如下: 传播设置 是 要求。 隔离级别是 默认的。 事务是读/写。 事务超时默认的默认超时 潜在的交易系统,或没有如果不会超时 支持。 任何 RuntimeException 触发 回滚,和任何检查 异常 不。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 224/514 您可以更改这些默认设置;各种属性的 这个 < tx:方法/ > 是嵌在标签 < tx:建议/ > 和 < tx:属性/ > 标签进行了总结 下图: 为多12 1一个 < tx:方法/ > 设置 属 性 要 求? 默 认 描述 名称 是的 方法名(s)的事务 属性是相联系的。 通配符(*)字符 可以用来副相同的事务属性 设置与一些方法, 例如, 获得* , 处理* , 在*事件 ,等等。 传播 没有 需 要 事务传播行为。 隔离 没有 默 认 事务隔离级别。 超时 没有 1 事务超时的值(以秒为单位)。 只读 没有 假 这是事务只读? 回滚 没有 例外(s) 这引发 回滚,以逗号分隔。 例如, com.foo.MyBusinessException,ServletException。 没有 回滚 没有 例外(s) ,做 不 触发回滚,以逗号分隔。 例如, com.foo.MyBusinessException,ServletException。 12.5.6A使用 transactional 除了基于xml的声明式事务的方法 配置,您可以使用一个基于注解的方法。 宣布 事务语义直接在Java源代码把 声明更接近受灾 的代码。 没有太多的危险 不应有的耦合,因为代码是用来事务性的 几乎总是这样无论如何部署。 易用性所提供的使用 transactional 注释是最好的 通过一个具体实例说明了,这是解释的文本 遵循。 考虑下面的类定义: // the service class that we want to make transactional @Transactional public class DefaultFooService implements FooService { Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo); void updateFoo(Foo foo); } 当上面的POJO被定义为在一个Spring IoC bean 容器,bean实例可以通过添加仅仅事务 一个 行XML配置: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 225/514 方法可视性和 transactional 当使用代理,你应该申请 transactional 注释只 方法 公共 可见性。 如果你 注释保护,私人或包 可见的方法 transactional 注释,没有错误 提 高了,但是带注释的方法不存在配置 事务设置。 考虑使用AspectJ(见下文)如果你 需要标注非公 有制的方法。 提示 你可以省略的 事务管理器 属性 < tx:注解驱动/ > 标签 如果bean的名称 PlatformTransactionManager 那你 想线在有名字 transactionManager 。 如果 这个 PlatformTransactionManager bean 那你想依赖 注入有任何其他的名字,然后你有 使用 事务管理器 属性 明确,如前面的例子。 注意 这个 @EnableTransactionManagement 注释提供了相同的功能如果您使用的是基于Java的 配置。 只需 添加注释 @ configuration 类。 看到Javadoc 详情。 你可以把 transactional 注释一个接口定义之前,一个方法,一个接口 类定 义,或一个 公共 方法在类。 然而,仅仅是存在的 transactional 注释是不够 的 激活事务性行为。 这个 transactional 注释只是 元数据,可以被一些运 行时基础设施 transactional 了解并能使用 元数据配置适当的bean与事务 性行为。 在前面的示例中, < tx:注解驱动/ > 元素 开关在 事务性行为。 提示 春天建议你只标注具体类(和 方法的具体类) transactional 注释,而不是 来注解的接口。 你当然可以 把 transactional 注释一个 接口(或一个接口方法),但这只 能像你 期望它如果您使用的是基于接口的代理。 事实上, Java注释是 没有继承接口 意味着,如果您使用的是基于类 的代理 ( 代理目标类= " true " )或编织的基础 方面( 模式 = " aspectj” ),那么事务 设置不认可的代理和编织 基础 设施,和对象将不会被包裹在一个 事务代理,这将是明显的 坏 。 注意 在代理模式(默认),只有外部方法调用 通过代理来截获。 这意味着 自动调用,实际上,在目标对象的方法调用 另一种方法的目标对象,不会导致一个实际 事务在运行时即使调用方法是标记 transactional 。 考虑使用AspectJ模式(请参阅模式属性表 下图)如果你期望自我调用是用事务 好。 在 这种情况下,是不会有一个代理在第一个 地方;相反, 目标类将编织(即它的字节代码将被修改) 才能 transactional 到运行时 行为的一种方法。 12.2为多。 一个注解驱动的事务设置 XML 属性 注释属性 默认 描述 事务管 理器 N / A(见 TransactionManagementConfigurer Javadoc) transactionManager 事务管理器要使用的名称。 只需要 如果事 务管理器的名称不是 transactionManager ,例子 以上。 模式 模式 代理 默认的模式“代理”过程注释 豆子是代理 使用Spring的AOP框架(以下 代理语义,正 如上面所讨论的,申请方法调用 通过代理来 只)。 另一种模式 “aspectj”而不是编织 类与春天的的影响 AspectJ事务方面,修改 目标类字节 代码,适用于任何类型的方法调 用。 AspectJ编织 需要弹簧方面。 jar的类 路径以及 装入时编织(或编译时编织)启 用。 (见 一个​章节​弹簧configurationa​​ 有 关如何设置 装入时编织了。) 只适用于代理模式。 控制什么类型的 事务 代理创建类标注 这个 transactional 注 释。 如果 代理目标类 属性设置 到 真正的 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 226/514 代理目 标类 proxyTargetClass 假 ,然后基于类的代理 创建的。 如果 代理目 标类 是 假 或者如果属性是省略了,然后 基 于接口的代理创建标准JDK。 (见 SectionA 9.6,一个​​mechanismsa​​代理 对于一个详细 的检查 不同的代理类型。) 秩序 秩序 下令最低优先级 定义事务的顺序的建议 应用于bean注释吗 transactional 。 (更多 相关规定信息订购 AOP的建议, 看到 一个​章节orderinga​​​建议 )。 没有 指定顺序意味着AOP子系统决定 订单的建议。 注意 这个 代理目标类 属性控制什么 类型的事务代理创建类标注 这个 transactional 注释。 如果 代理目标类 设置为 真正的 ,基于类创建代理。 如果 代理目标类 是 假 或 如果省略该属性,标准JDK基于接口的代理 创 建的。 (见 SectionA 9.6,一个​​mechanismsa​​代理 对于一个讨论 不同的代理类型。) 注意 @EnableTransactionManagement 和 < tx:注解驱动/ > 只希望 transactional 在bean在相同的 它们被 定义在应用程序上下文。 这意味着,如果你把 注解驱动的配置在一个 WebApplicationContext 对于一个 DispatcherServlet ,它只检查 transactional 豆子在你 控制器,而不是你的服务。 看到 SectionA 17.2,一个​​ 了 DispatcherServlet 一个​​ 为更多的信息。 最派生位置优先当评估 事务设置方法。 在 下面的例子中, DefaultFooService 类注释的类 水平与设置只读事务,但是 transactional 注释 updateFoo(Foo) 方法在同一个班花 优先考虑的事务设置定义在类 水平。 @Transactional(readOnly = true) public class DefaultFooService implements FooService { public Foo getFoo(String fooName) { // do something } // these settings have precedence for this method @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) public void updateFoo(Foo foo) { // do something } } transactional 设置 这个 transactional 注释是 元数据,指定一个接口、类或方法必须有 事务性语义;例如, 一个​​ 启动一个品牌 新只读事务当调用该 方法时,暂停任何 现有事务 一个​​ 。 默认 transactional 设置为 如下: 传播设置是 PROPAGATION_REQUIRED。 隔离级别是 隔离违约。 事务是读/写。 事务超时默认的默认超时 潜在的交易系统,或任何人如果不会超时 支持。 任何 RuntimeException 触发 回滚,和任何检查 异常 不。 这些默认设置可以改变,各种属性的 这个 transactional 注释是 概括如下表: 为多12 3 transactional 属性 财产 类型 描述 价值 字符串 可选的限定指定事务管理器使用。 传播 枚举: 传播 可选的传播环境。 隔离 枚举: 隔离 可选的隔离级别。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 227/514 readOnly 布尔 读/写和只读事务 超时 int(秒粒度) 事务超时。 将 数组的 类 对象,这 必须来自 可抛出。 可选的数组的异常类 必须 导致回滚。 rollbackForClassname 数组的类名。 类必须是来自 可抛出。 可选的数组的名字的异常类 必须 引起 回 滚。 noRollbackFor 数组的 类 对象,这 必须来自 可抛出。 可选的数组的异常类 不得 导致回滚。 noRollbackForClassname 数组的 字符串 类名, 它必须是来自 可抛 出。 可选的数组的名字的异常类 不得 引起 回 滚。 目前你不能有明确的控制的名称 事务,“名字”意味着事务名称,将 显示在一个事务监视器,如果适用(例如,服务器的 事务监视 器),并在日志输出。 声明性 事务、事务的名字总是完全限定的类 名+”。 “+方法 类的名称以事务方式建议。 例如,如果 handlePayment(. .) 方法 BusinessService 类开始一个事务, 事务的名称是: com.foo.BusinessService.handlePayment 。 多个事务经理与 transactional 大多数Spring应用程序只需要一个事务管理器,但可能存在的情况 在你想要的多个独立的事务管理器在一个单一的应用程序。 的值属性 transactional 注释可以 被用来选择指定的标识 PlatformTransactionManager 被使用。 这可以是bean名称或限定 符值的事务管理器bean。 例如,使用限定符符号,下面的Java代码 public class TransactionalService { @Transactional("order") public void setSomething(String name) { ... } @Transactional("account") public void doSomething() { ... } } 可以结合以下事务管理器bean声明在应用程序上下文。 ... ... 在这种情况下,两个方法 TransactionalService 将运行在另 事务管理器,来区分“秩序”和“账户”限定符。 默认 < tx:注解驱 动的> 目标bean的名字 transactionManager 将 仍然被使用如果没有特别限定PlatformTransactionManager bean是发 现。 自定义快捷注释 如果你发现你总是使用相同的属性 transactional 在许多不同的方法,那么春天的元注释支持允许您定义自定义快捷键 注释为您 的特定的用例。 例如,定义以下注释 @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional("order") public @interface OrderTx { } @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional("account") public @interface AccountTx { } 让我们写前一节的例子一样 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 228/514 public class TransactionalService { @OrderTx public void setSomething(String name) { ... } @AccountTx public void doSomething() { ... } } 这里我们用语法来定义事务管理器限定符,但也可以 包括传播行为,回滚规则、超时等。 12.5.7A事务传播 本节描述一些语义的事务传播 在春天。 请注意,这部分不介绍 事务传播正确,而是细节一些语义 关于事务传播在春天。 在spring管理事务,注意之间的区别 物理 和 逻辑 事务,以及如何设置适用于此。传播 差异。 需要 PROPAGATION_REQUIRED 当传播设置 PROPAGATION_REQUIRED ,一个 逻辑 创建事务范围为每个 方法应用在这些设置。 每一个这样的逻辑 事务范围 可以确定只回滚状态分别, 与外部事务逻辑上独立的范围 内部事务范围。 当然,在案件的标准 PROPAGATION_REQUIRED 行 为,所有这些范围 将被映射到同一个物理事务。 所以一个回滚只有 标志设置在内部事务范围并影响外 事务的机会实际上 commit(正如你期望它 到)。 然而,如果一个内部事务范围设置 回滚只有标记,外层事务还没有决定 回滚本身,所以回滚(默默地引发的内部 事务范围)是意想不 到的。 相应的 UnexpectedRollbackException 扔在那 点。 这是 预期行为 所以, 调用者的事务不能被误导的假设:一个承诺 了,其实那是不。 所以如果一个内部事务( 其中外部调用者不知道)标志着交易默默 只回滚,外部调用者还调用commit。 外部调 用者 需要接收一个 UnexpectedRollbackException 来清楚地指出,执行回滚而。 RequiresNew PROPAGATION_REQUIRES_NEW PROPAGATION_REQUIRES_NEW 形成鲜明对比的是, PROPAGATION_REQUIRED ,使用一个 完全 独立事务对于每个 事务 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 229/514 范围的影响。 在这种情况下,底层物理 交易是不同的,因此可以提交或回滚 独立,与外部事务不受内部 事务的回滚状态。 嵌套 传播嵌套 使用 单 物理事务与多个 保存点,它可以回滚。 这样的部分回滚允许一个 事务范围内触发一个回滚 对其 范围 ,与外部 交易能够继续 尽管一些操作的物理事务已经卷 回来。 这个设置通常映射到JDBC保存点,所以将 只使用JDBC资源交易。 看到 春天的 DataSourceTransactionManager 。 12.5.8A建议事务操作 假设您想要执行 两 事务性 和 一些基本的分析建议。 如何 你这个上下文中的作用 < tx:注解驱动/ > 吗? 当你调用 updateFoo(Foo) 方法,你想看到下列行动: 1. 启动配置分析方面。 2. 事务的建议执行。 3. 方法建议对象执行。 4. 事务提交。 5. 分析方面的报道准确时间整个 事务性方法调用。 注意 这一章是不关心在任何伟大的解释AOP 细节(除非它适用于事务)。 看到 ChapterA 9, 面向方面的编程与弹 簧 以下为详细报道AOP 配置和AOP一般。 这是一个简单的代码上面讨论分析方面。 这个 订购的建议是控制通过 下令 接口。 详细了解 建议订购,看到 一个​章节 orderinga​​​建议 。 package x.y; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.util.StopWatch; import org.springframework.core.Ordered; public class SimpleProfiler implements Ordered { private int order; // allows us to control the ordering of advice public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; } // this method *is* the around advice public Object profile(ProceedingJoinPoint call) throws Throwable { Object returnValue; StopWatch clock = new StopWatch(getClass().getName()); try { clock.start(call.toShortString()); returnValue = call.proceed(); } finally { clock.stop(); System.out.println(clock.prettyPrint()); } return returnValue; } } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 230/514 以上配置的结果是一个 fooService 豆,剖析和事务 方面应用 按照顺序 。 你 配置任意数量的额外的方面以相似的方式。 下面的例子效果相同的设置如上,但使用 纯XML声明性方法。 以上配置的结果将是一个 fooService 豆,剖析和事务 方面应用 这个顺序 。 如果你想要 配置建议执行 在 这个 事务的建议, 之 前 这个 事务的建议,然后你只是交换的价值 bean的配置方面 秩序 属性,以便它 高于建议的秩序价值的事务。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 231/514 你在类似的方式配置额外的方面。 12.5.9A使用 transactional 与 AspectJ 也可以使用Spring框架的 transactional 支持外的 Spring容器通过AspectJ方面。 这样做,你首先 注释您的类(和可选类的方法) transactional 注释,然后你 链接(织)应用程序的 org.springframework.transaction.aspectj.AnnotationTransactionAspect 定义在 弹簧方面jar 文件。 方面必须 还配置一个事务管理器。 你当然可以使用 Spring框架的IoC容器照顾了依赖注入 这个方 面。 最简单的方法来配置事务管理 方面是使用 < tx:注解驱动/ > 元素和指定 模式 属性 AspectJ 中描述的 SectionA 12 5 6, 一个​​使用 transactional 一个​​ 。 因为我们的注意力是 在运行的应用程序的Spring容器外,我们将展示 你怎么做编程。 注意 继续之前,您可能想读 SectionA 12 5 6,一个​​使用 transactional 一个​​ 和 ChapterA 9, 面向方面的编程与弹 簧 分别。 // construct an appropriate transaction manager DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource()); // configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager); 注意 当使用这方面,你必须标注 实现 类(和/或方法在这 类), 不 接口(如果有的话)的类 实现了。 AspectJ的规则, 遵循Java注解的接口 是 没有继承 。 这个 transactional 注释 类指定缺省的事务语义的执行 班上的任何方法。 这个 transactional 注释 方法在类中重写默认的事务语义 给定类的注释(如果存在)。 任何方法可能是注释, 不管能见度。 你的应用程序与编织 AnnotationTransactionAspect 你必须建立 你的应用程序使用AspectJ(请参阅 AspectJ 开发指南 )或使 用加载时编织。 看到 SectionA 9 8 4,一个​​装入时编织与AspectJ在春季Frameworka​​ 讨论与加载时编织 AspectJ。 12.6一个程序性事务管理 Spring框架提供了两种方法的程序性事务 管理: 使用 TransactionTemplate 。 使用 PlatformTransactionManager 直接实现。 春季团队一般建议 TransactionTemplate 对于程序性事务 管理。 第二种方法是类似于使用JTA UserTransaction API,尽管例 外 处理还是少了一些麻烦。 12.6.1A使用 TransactionTemplate 这个 TransactionTemplate 采用相同的 方法作为其他弹簧 模板 如 JdbcTemplate 。 它使用一个回调方法, 免费的应用程序 代码不必进行采集和样板 释放事务资源,导致代码 目的驱动的,在这写的代码仅仅关注 开发者想要做什么。 注意 您将看到以下的例子中,使用 TransactionTemplate 绝对夫妻你 春天的事务基础设施和api。 是否 编程式 事务管理是适合你的发展 需要的是一个决定,你将不得不让自己。 应用程序必须执行的代码在一个事务上下文,和 这将使用 TransactionTemplate 明确地, 看起来像下面的。 你,作为应用程序开 发人员,写一个 TransactionCallback 实现 (通常表示为一个匿名内部类),其中包含代码 那你需要执行事务的上下文中。 然后通 过 您的自定义的一个实例 TransactionCallback 到 执行(. .) 方法暴露在 TransactionTemplate 。 public class SimpleService implements Service { // single TransactionTemplate shared amongst all methods in this instance private final TransactionTemplate transactionTemplate; // use constructor-injection to supply the PlatformTransactionManager public SimpleService(PlatformTransactionManager transactionManager) { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 232/514 Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); this.transactionTemplate = new TransactionTemplate(transactionManager); } public Object someServiceMethod() { return transactionTemplate.execute(new TransactionCallback() { // the code in this method executes in a transactional context public Object doInTransaction(TransactionStatus status) { updateOperation1(); return resultOfUpdateOperation2(); } }); } } 如果没有返回值,使用方便 TransactionCallbackWithoutResult 类和 匿名类如下: transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } }); 代码在回调可以回滚事务通过调用 这个 setRollbackOnly() 方法对提供的 TransactionStatus 对象: transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { try { updateOperation1(); updateOperation2(); } catch (SomeBusinessExeption ex) { status.setRollbackOnly(); } } }); 指定事务设置 你可以指定事务设置,比如传播 模式下,隔离级别、超时等等的 TransactionTemplate 通过编程方式和 在配置。 TransactionTemplate 实例 默认情况下有 默认 事务设置 。 下面的例子显示了 编程定制的事务设置 特定 TransactionTemplate: public class SimpleService implements Service { private final TransactionTemplate transactionTemplate; public SimpleService(PlatformTransactionManager transactionManager) { Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); this.transactionTemplate = new TransactionTemplate(transactionManager); // the transaction settings can be set here explicitly if so desired this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); this.transactionTemplate.setTimeout(30); // 30 seconds // and so forth... } } 下面的例子定义了一个 TransactionTemplate 和一些自定义 事务设置,使用Spring XML配置。 这个 sharedTransactionTemplate 然后可以注入 许多服务是必需的。 " 最后,实例的 TransactionTemplate 类是线程安全的,在那 实例不维护任何会话状态。 TransactionTemplate 实例 做 然而维 护配置状态,所以当一个 类的数量可能共享一个单一的实例 TransactionTemplate ,如果一个类需要使用 TransactionTemplate 与不同的设置(为 例子,一个不同的隔离级别),那么你需要创建两个 截然不同的 TransactionTemplate 实例。 12.6.2A使用 PlatformTransactionManager 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 233/514 你也可以使用 org.springframework.transaction.PlatformTransactionManager 直接管理你的交易。 简单地通过实施 这个 PlatformTransactionManager 你是 使用你的bean通过bean的引用。 然后,使用 TransactionDefinition 和 TransactionStatus 对象可以 启动事务,回滚,并提交。 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); // explicitly setting the transaction name is something that can only be done programmatically def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { // execute your business logic here } catch (MyException ex) { txManager.rollback(status); throw ex; } txManager.commit(status); 12.7一个选择编程和声明式事务 管理 编程式事务管理通常是一个好主意只有 你有一个小数量的事务操作。 例如,如果你 有一个web应用程序,需要事务只针对某个 更新吗 操作,您可能不希望使用Spring事务代理设置 或任何其他技术。 在这种情况下,使用 TransactionTemplate 可能 是一个 好的方法。 能够设置事务名称明确地也 一些只能用编程的方法 事务管理。 另一方面,如果您的应用程序有许多事务 操作、声明式事务管理通常是值得的。 它 保持事务管理的业务逻辑,并不是困难的 配 置。 当使用Spring框架,而不是EJB CMT, 声明式事务管理配置的成本是很大的 减少了。 12.8一个特定于应用服务器的集成 春天的事务抽象通常是应用程序服务器 不可知论者。 此外,春天的 JtaTransactionManager 类,它可以选择 执行JNDI查找JTA UserTransaction 和 transactionManager 对象,自动指定 位置对后者的对象,不同的应用程序服务器。 有 访问JTA transactionManager 允许 为增强的事务语义,特别是支持事务 悬架。 看到 JtaTransactionManager JavaDocs 详情。 春天的 JtaTransactionManager 是 标准的选择上运行的Java EE应用服务器,是众所周知的 所有常见的服务器上工作。 先进的 功能,如事务 悬挂在许多服务器工作——包括GlassFish、JBoss, Geronimo,甲骨文OC4J——没有任何特殊配置要求。 然而,对 于完全支持事务悬挂和进一步的先进 集成、弹簧船舶特殊适配器为IBM WebSphere,BEA WebLogic 服务器和OC4J甲骨文。 这些适配器进行以下 部分。 对于标准的场景,包括WebLogic服务器, WebSphere和OC4J,考虑使用方便 < tx:jta事务管理器/ > 配置 元素。 在配置时,该元 素自动检测 底层服务器并选择最好的事务管理器可用 为平台。 这意味着您不需要配置 特定的适配器类(如在以下部分中讨论) 明确;相反,它们是自动选择,标准 JtaTransactionManager 作为默认的回退。 12.8.1A IBM WebSphere 在WebSphere 6 1 0 9以上,推荐的弹簧JTA 事务管理器来使用 WebSphereUowTransactionManager 。 这种特殊的 适配器 利用IBM的 UOWManager API, 这是可在WebSphere应用程序服务器6 0 2 19,后来呢 和6 1 0 9和以后。 用这个适配器,台弹 力事务 悬挂(暂停/恢复作为发起 PROPAGATION_REQUIRES_NEW )是由官方支持 IBM ! 12.8.2A BEA WebLogic服务器 在WebLogic Server 9.0或以上,您通常会使用 WebLogicJtaTransactionManager 而不是 股票 JtaTransactionManager 类。 这种特殊的 特定于weblogic的子类的正常 JtaTransactionManager 支持全部的能量 春天的事务定义在weblogic管理事务 环 境,超越标准JTA事务语义:功能包括 名字,事务隔离级别,和适当的恢复 交易在所有的情况下。 OC4J 12.8.3A甲骨文 春天一个特殊的适配器类船只OC4J 10 1 3或更高 称为 OC4JJtaTransactionManager 。 这个类是 类似于 WebLogicJtaTransactionManager 类在前一节中讨论的,提供类似的增值上 OC4J:事务名称和事务隔离级别。 完整的JTA功能,包括交易暂停, 与春天的正常工作 JtaTransactionManager 在 OC4J为好。 特殊的 OC4JJtaTransactionManager 适配器只是提供 超出标准的JTA的增值。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 234/514 12.9一个解决常见的问题 12.9.1A使用错误的事务管理器为一个特定的 数据源 使用 正确 PlatformTransactionManager 实现 根据您选择的事务技术和需求。 使用得当,Spring框架仅仅提供了一个简单的 和便携式抽象。 如果您使用的是全局事务,你 必须 使用 org.springframework.transaction.jta.JtaTransactionManager 类 (或一个 应用 特定于服务器的子类 它为所有你的事务。 操作。 否则事务基础设施试图执行 本地事务容器等资源 数据源 实 例。 这样的地方 交易没有意义,和一个好的应用程序服务器对待 他们是错误的。 12.10进一步资源 为更多的信息关于Spring框架的事务 支持: 分布式 交易在春天,有和没有XA 是JavaWorld 演讲中,SpringSource的大卫Syer指导您通过 七个模式在春天分布式事务的 应用程序, 他们三个和四个没有使用XA。 Java 事务设计策略 是一本可以从吗 InfoQ 这提供了一种有节奏的 介绍事务在Java。 它还包括并排 的例子,如何配置和使 用事务和春天 框架和EJB3。 13。 一个DAO支持 13.1一个介绍 数据访问对象(DAO)支持在春天是旨在使它 易于使用的数据访问技术像JDBC、JPA或Hibernate, JDO以一致的方式。 这允许 一个切换的 提到的持久性技术相当容易,它也允许 一个代码而不用担心捕获异常,是特定的 每个技术。 13.2一个一致的异常层次结构 Spring提供了一个方便的翻译从特定于技术的 异常喜欢 SQLException 到自己的异常 类层次结构的 DataAccessException 随着 根异常。 这些异常包装原始异常所以有 从来没有任何风险,你可能失去任何信息,可能 出了差错。 除了JDBC例外,春天也可以包装 hibernate具体的异常,将他们从专有、检查 异常(对于版本3.0之前的Hibernate Hibernate), 一 套集中运行时异常(这同样适用于JDO和JPA 例外)。 这允许一个处理最持久的异常,这 是不可恢复的,只有在适当的层,而不用吗 恼人的样板catch和throw街区和异常声明 一个的dao。 (一个陷阱和处理异常仍可以在任何地方一个需求 来虽然。) 正如上面 提到的,JDBC异常(包括 数据库方言)也转换为相同的层次结构, 这意味着一个可以执行一些操作与JDBC在一致的 编程模型。 上述适用于各种模板类在温泉 支持各种ORM框架。 如果一个人使用拦截器的基础 类然后应用程序必须关心处理 HibernateExceptions 和 JDOExceptions 本身,最好是通过委托给 SessionFactoryUtils ” convertHibernateAccessException(. .) 或 convertJdoAccessException() 分别的方法。 这些方法将例外是兼容的 异常的 org.springframework.dao 异常 层次结构。 作为 JDOExceptions 不加以制止,他们可以吗 也只会抛出,牺牲泛型DAO的抽象 的方面 例外虽然。 异常层次结构,弹簧提供如下图所示。 (请注意,类层次结构的详细图像显示只有一个 子集的整个 DataAccessException 层次结 构)。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 235/514 13.3一个注释用于配置刀或存储库类 最好的方式来保证你的数据访问对象(dao)或 存储库提供例外翻译是使用 @Repository 注释。 这个注释 还允许组件扫描支持 找到和配置您的dao 和仓库,不需要提供XML配置条目 他们。 @Repository public class SomeMovieFinder implements MovieFinder { // ... } 任何刀或存储库实现将需要访问 持久性资源,根据持久性技术使用;为 例子,一个基于JDBC的存储库将需要访问JDBC 数据源 ,一 个jpa基础库需要 访问一个 EntityManager 。 最简单的方法 为了完成这个是有这个资源依赖注射使用 这个 @ autowired、 , @ inject , @ resource 或 persistencecontext 注释。 这是一个 例子为一个JPA存储库: @Repository public class JpaMovieFinder implements MovieFinder { @PersistenceContext private EntityManager entityManager; // ... } 如果您使用的是经典的Hibernate api比你可以注入 SessionFactory: @Repository public class HibernateMovieFinder implements MovieFinder { private SessionFactory sessionFactory; @Autowired public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } // ... } 最后的示例中,我们将展示这是典型的JDBC支持。 你 会有 数据源 注入 初始化方法,您将创建一个 JdbcTemplate 和其他数据 访问支持类 像 SimpleJdbcCall 等使用这种 数据源 。 @Repository public class JdbcMovieFinder implements MovieFinder { private JdbcTemplate jdbcTemplate; @Autowired public void init(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 236/514 // ... } 注意 请见具体覆盖每个持久性技术 有关如何配置应用程序上下文采取 利用这些注释。 14。 一个数据访问与JDBC 14.1一个介绍Spring框架JDBC 所提供的增值Spring框架JDBC抽象 也许最好操作的顺序显示在下表中列出。 该表显示了春天什么行动会照顾和哪些动作 是 你的责任,应用程序开发人员。 14.1为多。 一个Spring JDBC -谁做什么? 行动 春天 你 定义连接参数。 x 打开连接。 x 指定SQL语句。 x 声明参数并提供参数值 x 准备和执行该语句。 x 设置循环来遍历结果(如果 任何)。 x 做这个工作对于每个迭代。 x 处理任何例外。 x 处理事务。 x 关闭连接、语句、结果集。 x Spring框架负责的所有底层细节 可以使JDBC API开发这些乏味的和。 选择一个14.1.1A JDBC数据库访问方法 你可以选择几种方法之间形成的基础 JDBC数据库访问。 除了三种口味的JdbcTemplate, 一个新的SimpleJdbcInsert和 SimplejdbcCall方法优化数据库 元数据和对象的RDBMS需要更多的面向对象的风格 方法相似,JDO查询设计。 一旦你开始使 用一个 这些方法,你仍然可以混合和匹配,包括一个功能 从一个不同的方法。 所有的方法都需要一个JDBC 2.0兼容的 司机,和一 些高级功能需要一个JDBC 3.0驱动程序。 注意 Spring 3.0更新以下所有方法使用Java 5 支持如泛型和可变参数。 JdbcTemplate 是典型的 Spring JDBC方法和最受欢迎的。 这个“最低水平” 方法和所有其他人使用JdbcTemplate在 后台,和所有 更新Java 5泛型和可变参数等的支持。 NamedParameterJdbcTemplate 包装 JdbcTemplate 提供命名参数 而不是传统的JDBC”? “占位符。 这种方法 提 供了更好的文档和易用性当你有多重 参数的SQL语句。 SimpleJdbcInsert和 SimpleJdbcCall 优化数据库元数据限制 数量的必要的配置。 这种方法简化了编码 所以,你只需要 提供的表的名称或过程 并提供参数的映射匹配列名称。 这仅适用于如果数据库提供足够的元数据。 如果 数据库不提供此 元数据,您将必须提供 显式配置的参数。 RDBMS对象包括MappingSqlQuery, SqlUpdate和StoredProcedure 要求您创建 可重用和线程安全对象初始化期间 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 237/514 您的数据 访问层。 这种方法是仿照JDO查询在你 定义你的查询字符串,声明参数和编译查询。 一旦你这样做,执行方法可 以多次调用与 各种参数值传递。 14.1.2A包的层次结构 Spring框架的JDBC抽象框架包括四个 不同的包,即 核心 , 数据源 , 对象 ,和 支持 。 这个 org.springframework.jdbc.core 包 包含 JdbcTemplate 类和它的各种 回调接口,加上各种相关类。 一个分包 命名 org.springframework.jdbc.core.simple 包含 这个 SimpleJdbcInsert 和 SimpleJdbcCall 类。 另一个分包命名 org.springframework.jdbc.core.namedparam 包含 NamedParameterJdbcTemplate 类和相关 支持类。 看到 SectionA 14.2,一个​​使用JDBC核心类来控制基本JDBC加工 误差handlinga​​ , SectionA 14.4,一个​operationsa​​​JDBC批处理 ,和 SectionA 14.5,一个​​简化JDBC操作与SimpleJdbc classesa​​ 这个 org.springframework.jdbc.datasource 包 包含一个工具类,用于容易 数据源 访问,和各种简单的 数据源 实现,可以 用于 测试和运行未修改的JDBC代码之外的Java EE 集装箱。 一个分包命名 org.springfamework.jdbc.datasource.embedded 提 供 支持创建内存数据库实例使用Java数据库 引擎如HSQL和H2。 看到 SectionA 14.3,一个​connectionsa​​​控制数据库 和 SectionA 14.8,一个​supporta​​​嵌入式数据库 这个 org.springframework.jdbc.object 包 包含类,代表RDBMS的查询、更新和存储 程序是线程安全的,可重用的对象。 看到 SectionA 14.6,一个​​建模JDBC操作作为Java objectsa​​ 。 这种方法是通过JDO建模,尽管 课程对象返回的查询 一个​​ 断开 一个​​ 从 数据库。 这种更高层次的JDBC抽象取决于 较低级别的抽象的 org.springframework.jdbc.core 包。 这个 org.springframework.jdbc.support 包提供 SQLException 翻译功能和一些 实用工具类。 期间抛出的异常处理JDBC。 翻译 对异常的定义 org.springframework.dao 包。 这意味着代码使用Spring JDBC抽象层 不需要实现JDBC或rdbms特有的 错误处理。 所有 例外是不翻译,给你选择 捕获异常,您可以恢复,同时允许其他 例外的传递给调用者。 看到 SectionA 14 2 3,一 个​​ SQLExceptionTranslator 一个​​ 。 使用JDBC 14.2核心类来控制基本JDBC加工 错误处理 14.2.1A JdbcTemplate 这个 JdbcTemplate 类是中央类 在JDBC核心包。 它处理创建和释放 资源,帮助您避免一些常见错误,如忘记 关闭连接。 它执 行的基本任务核心JDBC 工作流如语句创建和执行,使应用程序 代码提供的SQL和提取结果。 这个 JdbcTemplate 类执行SQL 查询,更新 语句和存储过程调用,执行迭代 ResultSet 年代和提取返回 参数值。 它还捕获异常并将它们转换为JDBC通用,更多 信息,定义的异常层次结构 org.springframework.dao 包。 当你使用 JdbcTemplate 对于你的 代码,你只需要实现回调接口,给他们一个 明确定义的合同。 这个 PreparedStatementCreator 回调 接口创建一份事先准备好的声明中给定一个 连接 提供的这个类, 提供SQL和任何必要的参 数。 也同样如此 CallableStatementCreator 接口,它 创建可调用语句。 这个 RowCallbackHandler 接口提取 从每一行的值 ResultSet 。 这个 JdbcTemplate 可以用在一刀吗 实现通过直接实例化与 数据源 参考,或者被配置在 一个Spring IoC容器和给DAOs作为 bean引用。 注意 这个 数据源 总是应该 配置为一个bean在Spring IoC容器。 在第一种情况下 bean是直接交给服务;在第二 种情况下它是 给有准备的模板。 所有SQL签发这类记录的 调试 水平属于对应 完全限定类名的模板实例(一般 JdbcTemplate ,但它可能是不同的,如果你是 使用 一个自定义的子类 JdbcTemplate 类)。 JdbcTemplate类的例子使用 本节提供了一些示例 JdbcTemplate 类的使用。 这些例子是 不是一个详尽的清单的所有功能暴露出来的 JdbcTemplate ,看到 服务员的Javadocs 那。 查询(选择) 这是一个简单的查询获取的行数 关系: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 238/514 int rowCount = this.jdbcTemplate.queryForInt("select count(*) from t_actor"); 一个简单的查询使用绑定变量: int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt( "select count(*) from t_actor where first_name = ?", "Joe"); 查询 字符串 : String lastName = this.jdbcTemplate.queryForObject( "select last_name from t_actor where id = ?", new Object[]{1212L}, String.class); 查询和填充 单 域 对象: Actor actor = this.jdbcTemplate.queryForObject( "select first_name, last_name from t_actor where id = ?", new Object[]{1212L}, new RowMapper() { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } }); 查询和填充一个数量的域对象: List actors = this.jdbcTemplate.query( "select first_name, last_name from t_actor", new RowMapper() { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } }); 如果最后两段代码实际上存在于相同的 应用程序,它会有道理删除重复呈现 在这两个 RowMapper 匿名内部 类,并将其提取到 一个类(通常是一个 静态 内部类),然后可以引用 根据需要由DAO方法。 例如,它可能是更好的编写 最后的代码片段如下: public List findAllActors() { return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper()); } private static final class ActorMapper implements RowMapper { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } } 更新(插入/更新/删除)与jdbcTemplate 你使用 更新(. .) 方法 执行插入、更新和删除操作。 参数值 通常提供的var args或者作为一个对象 数组。 this.jdbcTemplate.update( "insert into t_actor (first_name, last_name) values (?, ?)", "Leonor", "Watling"); this.jdbcTemplate.update( "update t_actor set = ? where id = ?", "Banjo", 5276L); this.jdbcTemplate.update( "delete from actor where id = ?", Long.valueOf(actorId)); 其他jdbcTemplate操作 您可以使用 执行(. .) 方法 执行任意SQL,因此该方法通常用于 DDL语句。 这是严重超载与变体以 回调接口、绑定变量数组,等 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 239/514 等。 this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); 下面的示例调用一个简单的存储过程。 更多的 复杂的存储过程支持 覆盖后 。 this.jdbcTemplate.update( "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", Long.valueOf(unionId)); JdbcTemplate 最佳实践 实例的 JdbcTemplate 类 一旦配置线程安全的 。 这是重要的 因为它意味着您可以配置的单独一个实例 JdbcTemplate 然后 安全地注入这 共享 引用到多个dao(或 存储库)。 这个 JdbcTemplate 是有状态的,在 ,它维护一个引用 数据源 ,但这个状态 不 会话状态。 一个常见的做法在使用 JdbcTemplate 类(和相关的 NamedParameterJdbcTemplate 类)是配置 数据源 在Spring的配置文件, 然后依赖注入, 共享 数据源 bean到你的刀 类; JdbcTemplate 会创建在 setter的 数据源 。 这导致 对DAOs看起来部分如下: public class JdbcCorporateEventDao implements CorporateEventDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } // JDBC-backed implementations of the methods on the CorporateEventDao follow... } 相应的配置可能看起来像这样。 显式配置的另一种选择是使用 组件扫描和注释支持依赖注入。 在 这种情况下你注释类的 @Repository (这使它一个候选人 组 件扫描)和注释 数据源 setter方法 @ autowired 。 @Repository public class JdbcCorporateEventDao implements CorporateEventDao { private JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } // JDBC-backed implementations of the methods on the CorporateEventDao follow... } 相应的XML配置文件 看起来像下面的: 如果您使用的是春天的 JdbcDaoSupport 类,和你的不同 jdbc支持DAO类扩展从它,然后你的子类继承了一个 setDataSource(. .) 方法从 JdbcDaoSupport 类。 你 可以选择是否从这个类继承。 这个 JdbcDaoSupport 类提供一个 只是方便使用。 无论上述模板初始化风格 你选择使用(或不),很少需要创建一个新的 实例 JdbcTemplate 类每一次你 要执行的SQL。 一旦配置 完成, JdbcTemplate 实例是线程安全的。 你可能 要多 JdbcTemplate 实例如果你 应用程序访问多个数据库,这就需要多个 数 据源 ,随后多个 不同配置 JdbcTemplates 。 14.2.2A NamedParameterJdbcTemplate 这个 NamedParameterJdbcTemplate 类添加了 支持编程JDBC语句使用命名参数,如 反对编程JDBC语句只使用经典的占位符 ( ’吗?” )参数。 这个 NamedParameterJdbcTemplate 类封装了一个 JdbcTemplate ,代表对包装的 JdbcTemplate 做了大 量的工作。 本节 只有那些区域的描述 NamedParameterJdbcTemplate 类,不同于 这个 JdbcTemplate 本身,即编程JDBC 语 句使用命名参数。 // some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int countOfActorsByFirstName(String firstName) { String sql = "select count(*) from T_ACTOR where first_name = :first_name"; SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName); return namedParameterJdbcTemplate.queryForInt(sql, namedParameters); } 注意使用命名参数符号价值 分配给 SQL 变量和相应的 插入的值 namedParameters 类型的变量( MapSqlParameterSource )。 或者,你可以通过命名参数和他们的 相应值 NamedParameterJdbcTemplate 实例通过使用 地图 欧式风格的 剩余的方法公开 NamedParameterJdbcOperations 和 实现的 NamedParameterJdbcTemplate 类也遵循类似的模式,这里不介绍。 下面的示例展示了使用 地图 的风格。 // some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int countOfActorsByFirstName(String firstName) { String sql = "select count(*) from T_ACTOR where first_name = :first_name"; Map namedParameters = Collections.singletonMap("first_name", firstName); return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters); } 一个不错的特点有关 NamedParameterJdbcTemplate (和现有的 相同的Java包) SqlParameterSource 接口。 你已经看到一 个例子,一个实现这个 接口在前面的代码片段( MapSqlParameterSource 类)。 一个 SqlParameterSource 是一个来源的 命 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 241/514 名参数值 NamedParameterJdbcTemplate 。 这个 MapSqlParameterSource 类是一个非常简单的 实现是一个简单的适配 器周围 java util地图 ,那里的钥匙的 参数名称和值的参数值。 另一个 SqlParameterSource 实现是 BeanPropertySqlParameterSource 类。 这类 包装一个任意的JavaBean(即,一个类的 实例 坚持 JavaBean 约定 ),并使用JavaBean的属性作为包装 命名参数值的来源。 public class Actor { private Long id; private String firstName; private String lastName; public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } public Long getId() { return this.id; } // setters omitted... } // some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int countOfActors(Actor exampleActor) { // notice how the named parameters match the properties of the above 'Actor' class String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName"; SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor); return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters); } 请记住, NamedParameterJdbcTemplate 类 包装 一个经典的 JdbcTemplate 模板;如果你需要访问包裹 JdbcTemplate 实例 来访问功能 仅出现在 JdbcTemplate 类,你可以 使用 getJdbcOperations() 方法来访问 包装 JdbcTemplate 通过 JdbcOperations 接口。 看到也 一个​​章节 JdbcTemplate 最佳practicesa​​ 对于 使用指南的 NamedParameterJdbcTemplate 类在上下文 一个应用程 序。 14.2.3A SQLExceptionTranslator SQLExceptionTranslator 是一个 接口的实现类,它可以转换 SQLExceptions 和春天的 org.springframework.dao.DataAccessException , 这是不可知论者关于数据访问策略。 实现可以 是通用的(例如,使用 SQLState编码JDBC)或专有的 (例如,使用Oracle错误代码),为更精确。 SQLErrorCodeSQLExceptionTranslator 是 实施 SQLExceptionTranslator 这是默认情况下使用的。 这个实现使用特定的供 应商代码。 它是更为精准 SQLState 实现。 错误代码翻译是基于代码保存在JavaBean类型 类称为 SQLErrorCodes 。 这类被 创建 和填充的 SQLErrorCodesFactory 作为 顾名思义是一个工厂来创建 SQLErrorCodes 基于内容的 配置文件命名 sql错误 代码的xml 。 这个文件是 填充供应商编码和基于 DatabaseProductName 来自 该方法 。 对于实际的代码 你正在使用的数 据库使用。 这个 SQLErrorCodeSQLExceptionTranslator 适用于以下序列匹配规则: 注意 这个 SQLErrorCodesFactory 被 默认定义错误代码和自定义异常的翻译。 他们是在一个文件夹命名为抬 头 sql错误代码的xml 从类路径和 匹配的 SQLErrorCodes 实例 基于数据库名称位于从数据库的元数据 数 据库在使用。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 242/514 1. 任何自定义翻译实现子类。 通常 提供的混凝土 SQLErrorCodeSQLExceptionTranslator 使用 所以这个规则不适用。 它 只适用于如果您有其实 提供了一个子类实现。 2. 任何自定义实现的 SQLExceptionTranslator 接口,是 提供作为 customSqlExceptionTranslator 财产的 这个 SQLErrorCodes 类。 3. 的实例的列表 CustomSQLErrorCodesTranslation 类, 提供 customTranslations 财产的 SQLErrorCodes 类,是 寻找一个 匹配。 4. 错误代码匹配是应用。 5. 使用回退的翻译。 SQLExceptionSubclassTranslator 是 默认的回退的翻译。 如果这个翻译不是可用的 然后接下来的回 退的翻译是 SQLStateSQLExceptionTranslator 。 您可以扩展 SQLErrorCodeSQLExceptionTranslator: public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator { protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) { if (sqlex.getErrorCode() == -12345) { return new DeadlockLoserDataAccessException(task, sqlex); } return null; } } 在这个例子中,特定的错误代码 -12345年 是翻译和其他错误被翻译成默认吗 翻译的实现。 使用这个自定义翻译,它是 要通过它 来了 JdbcTemplate 通过 该方法 setExceptionTranslator 和使用这 JdbcTemplate 对于所有的数据访问 处理这个翻译是必要 的。 这里是一个例子,说明 这个自定义翻译可以使用: private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { // create a JdbcTemplate and set data source this.jdbcTemplate = new JdbcTemplate(); this.jdbcTemplate.setDataSource(dataSource); // create a custom translator and set the DataSource for the default translation lookup CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator(); tr.setDataSource(dataSource); this.jdbcTemplate.setExceptionTranslator(tr); } public void updateShippingCharge(long orderId, long pct) { // use the prepared JdbcTemplate for this update this.jdbcTemplate.update( "update orders" + " set shipping_charge = shipping_charge * ? / 100" + " where id = ?" pct, orderId); } 自定义翻译传递一个数据源,以便查找 错误代码在 sql错误代码的xml 。 14.2.4A执行语句 执行一条SQL语句只需要很少的代码。 你需要一个 数据源 和一个 JdbcTemplate ,包括方便 方法 所提供的 JdbcTemplate 。 这个 下面的例子显示了您需要包括对最小但完全 功能类,创建一个新表: import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class ExecuteAStatement { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public void doExecute() { this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); } } 14.2.5A运行查询 一些查询方法返回一个值。 检索一个计数或 特定的值从一行,使用 queryForInt(. .) , queryForLong(. .) 或 queryForObject(. .) 。 后者将 返回JDBC 类型 Java类 作为参数传入。 如果类型转换是无效的,那么一个 InvalidDataAccessApiUsageException 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 243/514 是 扔。 下面是一个示例,它包含两个查询方法(一个 int 和一个查询 字符串 。 import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class RunAQuery { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int getCount() { return this.jdbcTemplate.queryForInt("select count(*) from mytable"); } public String getName() { return (String) this.jdbcTemplate.queryForObject("select name from mytable", String.class); } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } } 除了单个结果的查询方法,几种方法 返回一个列表的条目,查询返回的每一行。 这个 最通用的方法是 queryForList(. .) 哪一个 返回一个 列表 每个条目是一个地方吗 地图 每个条目在地图 代表列值的行。 如果你添加一个方法 以上的例子来检索一个列表 的所有行,它将看起来像 这个: private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public List> getList() { return this.jdbcTemplate.queryForList("select * from mytable"); } 返回的列表将会看起来像这样: [{name=Bob, id=1}, {name=Mary, id=2}] 14.2.6A更新数据库 下面的示例显示了一个列更新为某小学 关键。 在这个例子中,一个SQL语句行占位符 参数。 参数值可以作为传入可变参数或 另外作为一个对象数组。 因此原语应包装 在原始包装器类显式或使用汽车拳击。 import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; public class ExecuteAnUpdate { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public void setName(int id, String name) { this.jdbcTemplate.update( "update mytable set name = ? where id = ?", name, id); } } 14.2.7A检索自动生成的键值 一个 Update() 便利方法 支持主键生成检索 由数据库。 这支持JDBC 3.0标准的一部分;请参阅 规范的13.6章的细节。 这个方 法使用一个 PreparedStatementCreator 作为第一个参数, 这是所需的insert语句中指定。 这个 其他参数是一个 钥匙扣 ,其中 包含了 生成的密钥成功回来更新。 没有一个 标准单创建一个适当的方式 PreparedStatement (这也解释了为什么这个方法 签名是这样)。 下面的例子适用于甲骨文但 不能用于其他平台: final String INSERT_SQL = "insert into my_test (name) values(?)"; final String name = "Rob"; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 244/514 KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update( new PreparedStatementCreator() { public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { PreparedStatement ps = connection.prepareStatement(INSERT_SQL, new String[] {"id"}); ps.setString(1, name); return ps; } }, keyHolder); // keyHolder.getKey() now contains the generated key 14.3控制数据库连接 14.3.1A 数据源 春天获得数据库的连接通过一个 数据源 。 一个 数据源 JDBC的一部分吗 规范和是一个广义连接工厂。 它允许一个 容器或框 架来隐藏连接池、事务 管理问题从应用程序代码。 作为一名开发人员,您需要 不知道如何连接到数据库,这就是 责任的管理员 设置数据源。 你 最可能满足这两种角色为您开发和测试代码,但是你没有 一定要知道如何生产数据源 配置。 当使用Spring的JDBC层,你可以获得一个从JNDI数据源 或者你自己配置和连接池的实现提供了 由第三方。 流行的实现是 Apache Jakarta Commons DBCP和C3P0。 实现在春季分布意味着只有 出于测试目的,不提供池。 本节使用Spring的 DriverManagerDataSource 实现和 后文将介绍几个额外的实现。 注意 只使用 DriverManagerDataSource 类应该仅仅用于测试目的,因为它没有 提供池,将多个请求时表现不佳 的一个 连接是由。 你获得一个连接 DriverManagerDataSource 你通常得到一个 JDBC连接。 指定完全限定类名的JDBC 司机这样 DriverManager 可以加载 驱动程序类。 接下来,提供一个URL,JDBC驱动程序之间的变化。 (参考文档为你的司机正确的价 值。) 然后 提供一个用户名和密码来连接到数据库。 这是一个 如何配置一个的例子 DriverManagerDataSource 在Java代码 中: DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); dataSource.setUrl("jdbc:hsqldb:hsql://localhost:"); dataSource.setUsername("sa"); dataSource.setPassword(""); 下面是相应的XML配置: 下面的例子显示了基本的连通性和 配置和C3P0 DBCP。 了解更多的选项,帮助 控制池功能,请参阅产品文档 各自的连接池实 现。 DBCP配置: C3P0配置: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 245/514 14.3.2A DataSourceUtils 这个 DataSourceUtils 类是一个方便的 和强大的助手类,它提供了 静态 方法来获取连接从JNDI和紧密联系如果 必要的。 它支 持创建连接,例如, DataSourceTransactionManager 。 14.3.3A SmartDataSource 这个 SmartDataSource 接口 应该实现的类,它可以提供一个连接到一个吗 关系数据库。 它扩展了 数据源 接口允许类 用它来 查询的连接是否应该关闭在一个给定的 操作。 这种用法是有效的,当你知道你会重用 连接。 14.3.4A AbstractDataSource AbstractDataSource 是一个 文摘 基类 春天的 数据源 实现 实现了代码,是常见的所有 数据源 实现。 你延长 AbstractDataSource 类如果你 正在编写您自己的 数据源 实现。 14.3.5A SingleConnectionDataSource 这个 SingleConnectionDataSource 类是一个 实施 SmartDataSource 接口,封装了一个 单 连接 这是 不 每次使用后关闭。 显然,这不是 多线程能力。 如果任何客户端代码调用 密切 在 假设一个合用的连接,当使用持久化工具,设置 这个 suppressClose 财产 真正的 。 这 设置返 回一个关闭抑制代理包装物理 连接。 注意,你将不能投这个 一个土生土长的甲骨文 连接 或类似的 了。 这是一个测试类。 例如,它方便 测试代码外部应用程序服务器,联同一个 简单的JNDI环境。 相比之下, DriverManagerDataSource ,它重用相同的 连接所有的时间,避免过度创建物理 连接。 14.3.6A DriverManagerDataSource 这个 DriverManagerDataSource 类是一个 实施标准 数据源 界面,配置一个普通的JDBC驱动程序通过bean属性, 并返回一个 新的 连接 每一 时间。 这个实现是有用的为测试和独立 环境外的Java EE容器,或者作为一个 数据源 在一个Spring IoC bean 容器,或结合一个简单的 JNDI环境。 池假设 连接关闭() 电话只会 关闭连接,所以任何 数据源 知道持久性代码应该 工作。 然而,使用javabean样式的连 接池如 commons-dbcp 是如此容易,即使在一个测试环境, 它几乎总是比使用这样的一个连接池了 DriverManagerDataSource 。 14.3.7A TransactionAwareDataSourceProxy TransactionAwareDataSourceProxy 是一个代理 为一个目标 数据源 ,它包装了, 目标 数据源 添加的意识 spring管理事务。 在这方面,它类似于一个 事务性JNDI 数据源 提供 通过一个Java EE服务器。 注意 这是很少需要使用这个类,除非已经 现有的代码,必须调用和传递标准的JDBC 数据源 接口的实现。 在 这种 情况下,它可能仍然有这个代码可用,同时 同时有这段代码参与春季管理事务。 它 通常比自己编写新的代码 使用更高的吗 为资源管理层次的抽象,例如 JdbcTemplate 或 DataSourceUtils 。 (见 TransactionAwareDataSourceProxy Javadocs更多 细节。) 14.3.8A DataSourceTransactionManager 这个 DataSourceTransactionManager 类是一个 PlatformTransactionManager 实现 对于单一的JDBC数据源。 它将一个 JDBC连接的 指定数据源到当前执行的线程,有可能 允许一个线程每数据源连接。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 246/514 应用程序代码是必须的 通过检索JDBC连接 DataSourceUtils.getConnection(数据源) 而不是 Java EE标准 数据源 getconnection 。 它 抛出未经检查的 org.springframework.dao 异常 而不是检查 SQLExceptions 。 所有 框架类像 JdbcTemplate 使用这个 战略隐式。 如果不使用该事务经理, 查找策略的行为就像常见的——它就可以 在任何情况下使用。 这个 DataSourceTransactionManager 类 支持自定义隔离级别,超时,得到应用 适当的JDBC语句查询超时。 支持后者, 应用程 序代码必须使用 JdbcTemplate 或 调用 DataSourceUtils.applyTransactionTimeout(. .) 为创建的每个方法声明。 这个实现可以用来代替 JtaTransactionManager 在单资源 情况下,因为它不需要容器支持JTA。 切换 两者之间只是一个物质 的配置,如果你坚持 需要连接查找模式。 JTA不支持自定义 隔离级别! 14.3.9A NativeJdbcExtractor 有时你需要访问特定于供应商的JDBC方法 不同于标准的JDBC API。 这可能会有问题,如果你是 运行在一个应用程序服务器或 一个 数据源 把 连接 , 声明 和 ResultSet 对象有自己的包装器对象。 为获得本机对象您可以配置您的 JdbcTemplate 或 OracleLobHandler 与 NativeJdbcExtractor 。 这个 NativeJdbcExtractor 有各种口味 来匹配你的执行环境: SimpleNativeJdbcExtractor C3P0NativeJdbcExtractor CommonsDbcpNativeJdbcExtractor JBossNativeJdbcExtractor WebLogicNativeJdbcExtractor WebSphereNativeJdbcExtractor XAPoolNativeJdbcExtractor 通常 SimpleNativeJdbcExtractor 是 足以展开一个 连接 对象在 大多数环境。 看到更多细节的Javadocs。 14.4一个JDBC批处理操作 大多数JDBC驱动程序提供改进的性能如果你批多个 调用同一个预置语句。 通过分组成批量更新你 限制数量的往返数据库。 14.4.1A JdbcTemplate基本批处理操作的 你完成 JdbcTemplate 批 通过实现两种方法处理的一个特殊的接口, BatchPreparedStatementSetter ,并通过在 作为第二个 参数在你 batchUpdate 方法调用。 使用 getBatchSize 方法 提供电流的大小批量。 使用 setValues 方法设置的值 参数的预 置语句。 这种方法被称为 您指定的次数,在 getBatchSize 调用。 下面的示例更新 演员表基于条目列表。 整个列表用作 批处 理在这个例子中: public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[] batchUpdate(final List actors) { int[] updateCounts = jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setString(1, actors.get(i).getFirstName()); ps.setString(2, actors.get(i).getLastName()); ps.setLong(3, actors.get(i).getId().longValue()); } public int getBatchSize() { return actors.size(); } } ); return updateCounts; } // ... additional methods } 如果你是处理一个流的更新或阅读 文件,那么您可能有一个首选的批量大小,但最后一批 可能没有,数量的条目。 在这种情况下 你可以使用 InterruptibleBatchPreparedStatementSetter 接口,它允许您中断一批一旦输入源 是疲惫。 这个 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 247/514 isBatchExhausted 方法允许 你信号结束的批处理。 14.4.2A批量操作的对象列表 两 JdbcTemplate 和 NamedParameterJdbcTemplate 提供了另一种 提供批量更新的方法。 而不是实现一个特殊的 批处理 接口,您提供的所有参数值调用列表。 该框架循环遍历这些值,并使用一个内部的准备 声明setter。 API的变化取决于你是否使 用命名 参数。 你提供的命名参数的数组 SqlParameterSource ,一个条目的每个成员 批处理。 您可以使用 SqlParameterSource.createBatch 方法创建 这个数组,数组传入要么JavaBeans或数组的地图 包含参数值。 这个例子显示了一个批量更新使用命名参数: public class JdbcActorDao implements ActorDao { private NamedParameterTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int[] batchUpdate(final List actors) { SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray()); int[] updateCounts = namedParameterJdbcTemplate.batchUpdate( "update t_actor set first_name = :firstName, last_name = :lastName where id = :id", batch); return updateCounts; } // ... additional methods } 对于一个SQL语句使用经典”吗? “占位符,你 通过在一个列表包含一个对象数组更新值。 这 对象数组必须有一个条目在SQL 各占位符 语句,它们必须在同一个订单中定义的 SQL语句。 同样的例子使用经典的JDBC”? “占位符: public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[] batchUpdate(final List actors) { List batch = new ArrayList(); for (Actor actor : actors) { Object[] values = new Object[] { actor.getFirstName(), actor.getLastName(), actor.getId()}; batch.add(values); } int[] updateCounts = jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", batch); return updateCounts; } // ... additional methods } 上述所有批量更新方法返回一个int数组 包含影响的数量为每个批处理输入的行。 这个数 是报道的JDBC驱动程序。 如果计数 不可用,JDBC 驱动程序返回一个2值。 14.4.3A与多个批次的批处理操作 最后一个示例批处理批量更新,是如此 大,你想把他们分成几个较小的批次。 你 当然可以做到这一点的方法上面提到的通过 多 个调用 batchUpdate 法,但 现在有一个更方便的方法。 该方法以,除了 SQL语句,一个对象集合包含参数, 更新的数量为每个批 处理和一个 ParameterizedPreparedStatementSetter 设置 值参数的预置语句。 该框架循环 通过所提供的值和打破成批量 更新调用的 指定大小。 这个例子显示了一个批量更新使用批量大小的 100: public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 248/514 } public int[][] batchUpdate(final Collection actors) { int[][] updateCounts = jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", actors, 100, new ParameterizedPreparedStatementSetter() { public void setValues(PreparedStatement ps, Actor argument) throws SQLException { ps.setString(1, argument.getFirstName()); ps.setString(2, argument.getLastName()); ps.setLong(3, argument.getId().longValue()); } } ); return updateCounts; } // ... additional methods } 批处理更新方法对于这个调用返回的数组 int数组包含一个数组条目与数组每一批的 受影响的行数为每个更新。 顶级数组的长 度 表示数量的批次执行和二级数组的 长度表示数量的更新在这批处理。 的数量 更新在每批应提供所有的批量大小 除了最后 一个批次,可能要少一些,这取决于 更新对象提供的总数。 更新计数为每个更新 语句是一个报道的JDBC驱动程序。 如果数量不 是 可用,JDBC驱动程序返回一个2值。 14.5一个简化JDBC操作与SimpleJdbc类 这个 SimpleJdbcInsert 和 SimpleJdbcCall 类提供一个简化的 配置利用数据库元数据,可以 这可以通过JDBC驱动程序。 这意 味着有更少的配置 前面,虽然你可以覆盖或关掉元数据处理如果 你更愿意提供所有的细节在你的代码。 使用SimpleJdbcInsert 14.5.1A插入数据 首先,让我们看看 SimpleJdbcInsert 类与最少的 配置选项。 你应该实例化 SimpleJdbcInsert 在数据访问层的 初始化方法。 对于这个示例,初始化方法是 setDataSource 法。 你不需要子类 这个 SimpleJdbcInsert 类,简单地创建一个新的 实例并设置 表名称使用 withTableName 法。 配置方法 这类遵循“流体”风格,返回的实例 SimpleJdbcInsert ,它允许您链所有 配置方 法。 这个示例只使用一个配置方法; 你将看到的例子,以后多个参数。 public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor"); } public void add(Actor actor) { Map parameters = new HashMap(3); parameters.put("id", actor.getId()); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); insertActor.execute(parameters); } // ... additional methods } 这里使用execute方法接受一个平原 java跑龙套地图 作为其唯一的参数。 这个 在这里,需要注意是这些密钥用于地图必须 匹配 的列名表中定义的数据库。 这是 因为我们读元数据以构建实际的插入 语句。 自动生成的键SimpleJdbcInsert 14.5.2A检索使用 下面的例子使用了相同的插入是前面的,但不是 通过id获取自动生成的键,并将它保存在 新演员对象。 当你创建 SimpleJdbcInsert ,除了指定 表的名字,您指定的名称的列生成的密钥 usingGeneratedKeyColumns 法。 public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 249/514 .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { Map parameters = new HashMap(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods } 主要区别在执行插入由第二个 方法是,你不添加id映射和你打电话 executeReturningKey 法。 这返回一个 java朗数 对象,您可 以创建一个 数值类型的实例是用于我们的域名,你 不能依赖所有数据库返回一个特定的Java类在这里; java朗数 是基类,您可以 依靠吗 在。 如果你有多个自动生成的列,或者生成的值 正非数字,然后你可以使用吗 钥匙扣 这是 返回 executeReturningKeyHolder 法。 对于一个SimpleJdbcInsert 14.5.3A指定列 你可以限制列插入指定的列表 列名与 usingColumns 方法: public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingColumns("first_name", "last_name") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { Map parameters = new HashMap(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods } 执行插入是相同的,如果你有依靠 在元数据来确定哪些列使用。 14.5.4A使用SqlParameterSource提供参数值 使用 地图 提供参数值 工作很好,但这不是最方便的类来使用。 春天 提供了几个实现的 SqlParameterSource 接口,可以使用 相反, 一是 BeanPropertySqlParameterSource , 这是一个非常方便的类,如果你有一个javabean兼容的类 包含你的价值观。 它将使用相应的getter方法 提取参数值。 这里是一个例子: public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods } 另一个选择是 MapSqlParameterSource 就像一个地图,但 提供一个更方便 addValue 方法, 可以进行链接。 public class JdbcActorDao implements ActorDao { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 250/514 private JdbcTemplate jdbcTemplate; private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { SqlParameterSource parameters = new MapSqlParameterSource() .addValue("first_name", actor.getFirstName()) .addValue("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods } 正如您可以看到的,该配置是相同的,只有 执行代码的改变使用这些替代输入 类。 14.5.5A与SimpleJdbcCall调用一个存储过程 这个 SimpleJdbcCall 类利用元数据 在数据库中查找的名字 在 和 出 参数,所以,你不必明确声明它们。 你可以 声明参数如果你 喜欢去做,或者如果你有参数 如 数组 或 Struct 没有一个 自动映射到一个Java类。 第一个例子展示了一个简单的 过程只返回 标量值 VARCHAR 和 日期 格式从MySQL数据库。 示例程序 读取指定的演员进入和返回 first_name , last_name ,和 出生日 期 列在表格 的 出 参数。 CREATE PROCEDURE read_actor ( IN in_id INTEGER, OUT out_first_name VARCHAR(100), OUT out_last_name VARCHAR(100), OUT out_birth_date DATE) BEGIN SELECT first_name, last_name, birth_date INTO out_first_name, out_last_name, out_birth_date FROM t_actor where id = in_id; END; 这个 在id 参数包含 id 的演员你查找。 这个 出 参数返回数据从表中读取。 这个 SimpleJdbcCall 在一个类似的声明吗 方式到 SimpleJdbcInsert 。 你应该 实例化和配置类的初始化方法的 数据访问 层。 StoredProcedure类相比,你没有 创建一个子类,您不必声明的参数 在数据库中查找元数据。 以下 是一个例子,一个 SimpleJdbcCall配置使用上述存储 程序。 唯一的配置选项,除了 数据源 ,是存储的名称 程序。 public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.procReadActor = new SimpleJdbcCall(dataSource) .withProcedureName("read_actor"); } public Actor readActor(Long id) { SqlParameterSource in = new MapSqlParameterSource() .addValue("in_id", id); Map out = procReadActor.execute(in); Actor actor = new Actor(); actor.setId(id); actor.setFirstName((String) out.get("out_first_name")); actor.setLastName((String) out.get("out_last_name")); actor.setBirthDate((Date) out.get("out_birth_date")); return actor; } // ... additional methods } 您编写的代码的执行调用涉及 创建一个 SqlParameterSource 包含在 参数。 这是 重要的名称相匹配的输入值和提供的 参数 名称宣布 在存储过程。 此案没有匹配,因为你使用 元数据来确定数据库对象应该是指在一个 存储过程。 什么是指定源为存储 程序不一定是它是存储在数据库中。 一些 数据库变换名字大写而其他人使用小写 或用例指定。 这个 执行 方法以在参数 并返回一个包含任意地图 出 参数由 这个名称所指定的存储过程。 在这种情况下他们 出第一名,去年 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 251/514 的名字 和 出出生日期 。 的最后一部分 执行 方法创建 一个演员实例使用返回检索的数据。 再次,它是 重要的使用的名字 出 参数作为他们 在存储过程 中声明。 同时, 这个案子的名字 出 参数存储在 地图匹配的结果 出 参数名称 数据库,数据库之间可能有所变化。 到 让你的代 码的可移植性更好你应该做一个不区分大小写的查询或 指导弹簧使用 CaseInsensitiveMap 从 Jakarta Commons项目。 做 后者,你创建你自己的 JdbcTemplate 并设置 setResultsMapCaseInsensitive 财产 真正的 。 然后你通过这个定制的 JdbcTemplate 实例的构造函数 你 SimpleJdbcCall 。 你必须包括 commons集合jar 在您的类路径 这工作。 这里是一个例 子,这个配置: public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadActor = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_actor"); } // ... additional methods } 通过采取这种行动,你避免利益冲突的情况下使用 的名字你的返回 出 参数。 14.5.6A显式地声明参数来使用 SimpleJdbcCall 你已经了解了参数推导了基于元数据, 但是你可以声明然后明确如果你希望。 这可以通过创建 和配置 SimpleJdbcCall 与 declareParameters 方法,该方法接受一个变量 数量的 SqlParameter 对象作为输入。 看到 下一节详细讨论如何定义一个 SqlParameter 。 注意 显式声明是必要的,如果你使用的数据库 不是一个弹簧支持数据库。 目前弹簧支持元数据 存储过程调用的 查找以下数据库:Apache Derby,DB2、MySQL、微软SQL Server、Oracle和Sybase。 我们也 支持元数据 查找存储功能:MySQL,微软 SQL服务器,和甲骨文。 你可以选择要申报一个,一些或所有的参数 明确。 参数元数据仍然是用在哪里的你不 显式声明的参数。 到 旁路的所有处理元 数据查找潜在的参数和 只使用声明的参数,你调用该方法 withoutProcedureColumnMetaDataAccess 作为 《宣言》。 假设 你有两个或两个以上不同的电话 为一个数据库函数签名声明。 在这种情况下你所说的 useInParameterNames 指定的列表 参 数名称包括对于一个给定的签名。 下面的示例显示了一个全面申报过程调用,使用 信息从前面的例子。 public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadActor = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_actor") .withoutProcedureColumnMetaDataAccess() .useInParameterNames("in_id") .declareParameters( new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR), new SqlOutParameter("out_last_name", Types.VARCHAR), new SqlOutParameter("out_birth_date", Types.DATE) ); } // ... additional methods } 执行和结束的结果是两个例子 相同的;这一指定所有细节明确而不是依赖 元数据。 SqlParameters 14.5.7A如何定义 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 252/514 定义一个参数为SimpleJdbc类和也的 RDBMS操作类,覆盖着 SectionA 14.6,一个​​建模JDBC操作作为Java objectsa​​ , 你 使用一 个 SqlParameter 或它的一个子类。 你 通常指定参数名称和SQL类型的构造函数。 SQL类型被指定使用 java sql类型 常量。 我们已经看到 声明: new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR), 第一线的 SqlParameter 声明了一个在参数。 在参数可以用于存储 过程调用和查询使用 SqlQuery 和它的子类中覆盖 下一 节。 第二行用 SqlOutParameter 声明了一个 出 参数中使用存储过程 调用。 还有一个 SqlInOutParameter 对于 inout 参数,参 数,提供一个 在 值的过程,也返回 值。 注意 只有参数声明为 SqlParameter 和 SqlInOutParameter 将被用来提供吗 输入值。 这是不同的 StoredProcedure 类,它为向后 兼容性原因允许输入值提供 参数声明为 SqlOutParameter 。 因为在参数,除了名字和SQL类型,你 可以指定一个量表数值数据或类型名称为自定义数据库 类型。 对于 出 参数,您可以提供一 个 RowMapper 处理映射从返回的行 一个 Ref 光标。 另一个选项是指定一个 SqlReturnType 这提供了一个机会 定义定制的 处理的返回值。 14.5.8A使用SimpleJdbcCall调用一个存储功能 你叫一个存储函数在几乎相同的方式调用 存储过程,除了你提供一个函数的名字,而不是一个 程序的名字。 你使用 withFunctionName 方法配置一部分表明我们想做一个 调用一个函数,对应的字符串函数调用 生成的。 一个专门的执行调用, executeFunction, 是用来执行函数 和它返回函数的返回值是一个对象的指定 类型,这意味着你不需要检索的返回值 结果图。 一个 类似的便利方法命名 executeObject 是 也可用于存储过程,只有一个 出 参数。 下面的例子是基于一个存储函数命名 让 演员的名字 返回一个演员的姓名。 这里是MySQL源对于这个函数: CREATE FUNCTION get_actor_name (in_id INTEGER) RETURNS VARCHAR(200) READS SQL DATA BEGIN DECLARE out_name VARCHAR(200); SELECT concat(first_name, ' ', last_name) INTO out_name FROM t_actor where id = in_id; RETURN out_name; END; 我们再来调用这个函数创建一个 SimpleJdbcCall 在初始化 法。 public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcCall funcGetActorName; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate) .withFunctionName("get_actor_name"); } public String getActorName(Long id) { SqlParameterSource in = new MapSqlParameterSource() .addValue("in_id", id); String name = funcGetActorName.executeFunction(String.class, in); return name; } // ... additional methods } execute方法使用 返回一个 字符串 包含返回值 函数调用。 14.5.9A返回结果集/ REF光标从一个SimpleJdbcCall 调用一个存储过程或函数,返回一个结果集 是有点棘手。 一些数据库返回结果集在JDBC 结果处理而其他人需要明确注册 出 参 数的特定类型。 这两种方法都需要 额外的处理来遍历结果集和处理 返回的行。 与 SimpleJdbcCall 你使用 这个 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 253/514 returningResultSet 方法和声明一个 RowMapper 实现用于一个 具体的参数。 如果结果集返回在 结果处理,没有名字的定义, 所以返回的 结果必须匹配的顺序进行声明 RowMapper 实现。 指定的名称是 还用于存储处理结果列表在结果图 返回执行语 句。 下面的例子使用了一个存储过程中不带 参数和返回所有行从t演员表。 这是 MySQL源对于这个过程: CREATE PROCEDURE read_all_actors() BEGIN SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a; END; 调用这个过程你声明 RowMapper 。 因为要映射到类 遵循JavaBean的规则,你可以使用 ParameterizedBeanPropertyRowMapper 这是 由传递所需的类映射到的 newInstance 法。 public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadAllActors; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_all_actors") .returningResultSet("actors", ParameterizedBeanPropertyRowMapper.newInstance(Actor.class)); } public List getActorsList() { Map m = procReadAllActors.execute(new HashMap(0)); return (List) m.get("actors"); } // ... additional methods } 执行调用传入一个空的地图,因为这个调用 不带任何参数。 演员的名单然后检索得到的 结果地图并返回给调用者。 14.6一个建模JDBC操作作为Java对象 这个 org.springframework.jdbc.object 包 包含类允许您访问数据库在一个更多 面向对象的方式。 作为一个例子,你可以执行 查询和获取 返回的结果作为一个列表包含业务对象与关系 列数据映射到业务对象的属性。 你也可以 执行存储过程和运行更 新、删除和插入 语句。 注意 许多Spring开发者相信各种RDBMS操作 下面描述类(除 StoredProcedure 类)通常可以直接取代 JdbcTemplate 调用。 它常常是简单的写 DAO方法,简单地调用一个方法 JdbcTemplate 直接(相对于 封装 一个查询作为一种成熟的类)。 然而,如果你正变得可测量的值从使用RDBMS 操作类,继续使用这些类。 14.6.1A SqlQuery SqlQuery 是一个可重用的、线程安全的类 封装了一个SQL查询。 子类必须实现 newRowMapper(. .) 方法提供一个 RowMapper 可以创建一个实例 对象遍历每行中获得的 ResultSet 这是中创建 执行查询。 这个 SqlQuery 类是 很少直接使 用,因为 MappingSqlQuery 子类提供了一个更方便的实现映射行 到Java类。 其他实现,扩展 SqlQuery 是 MappingSqlQueryWithParameters 和 UpdatableSqlQuery 。 14.6.2A MappingSqlQuery MappingSqlQuery 是一个可重用的查询在吗 这具体的子类必须实现抽象 mapRow(. .) 方法将每一行的 提供 ResultSet 成一 个对象的 指定的类型。 下面的示例显示了一个自定义查询,地图了 数据 t演员 关系的一个实例 演员 类。 public class ActorMappingQuery extends MappingSqlQuery { public ActorMappingQuery(DataSource ds) { super(ds, "select id, first_name, last_name from t_actor where id = ?"); super.declareParameter(new SqlParameter("id", Types.INTEGER)); compile(); } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 254/514 @Override protected Actor mapRow(ResultSet rs, int rowNumber) throws SQLException { Actor actor = new Actor(); actor.setId(rs.getLong("id")); actor.setFirstName(rs.getString("first_name")); actor.setLastName(rs.getString("last_name")); return actor; } } 这个类扩展 MappingSqlQuery 参数化与 演员 类型。 这个 构造函数,对于这个客户查询需要 数据源 作为唯一的参数。 在这 个 构造函数调用构造器的你超类的 数据源 和SQL,应该 执行该查询检索的行。 这个SQL将用于 创建一个 PreparedStatement 所以它可能 包含占位符的任何参数中传递 执行你 必须声明每个参数使用吗 declareParameter 方法传 递一个 SqlParameter 。 这个 SqlParameter 需要一个名称和JDBC类型作为 定义在 java sql类型 。 在你定义所有 参数,您调 用 编译() 方法所以 语句可以准备和后执行。 这个类是线程安全的 编译后,所以只要这些实例 是由刀是初始化它们可以一直作 为实例 变量和被重用。 private ActorMappingQuery actorMappingQuery; @Autowired public void setDataSource(DataSource dataSource) { this.actorMappingQuery = new ActorMappingQuery(dataSource); } public Customer getCustomer(Long id) { return actorMappingQuery.findObject(id); } 该方法在本例中检索客户id, 是作为唯一的参数传递。 因为我们只需要一个对象 我们简单地调用返回的便利方法 findObject 与id作为参数。 如果我们有相反的查询返回一个列表 的对象,把额外的参数然后我们将使用其中一个 执行方法,它采用一组参数 值传递 可变参数。 public List searchForActors(int age, String namePattern) { List actors = actorSearchMappingQuery.execute(age, namePattern); return actors; } 14.6.3A SqlUpdate 这个 SqlUpdate 类封装了一个SQL 更新。 像一个查询,更新对象是可重用的,像所有 RdbmsOperation 类,一个更新可以有 参 数和定义在SQL。 这个类提供了一些 更新(. .) 方法类似于 执行(. .) 查询对象的方法。 这个 SqlUpdate 类是混凝土。 它可以 细分类,例如,添加一个自定义的更新方法,如在 以下代码片段,它只是叫 执行 。 然而, 你不需要继承 SqlUpdate 类 因为它可以 很容易地通过设置参数化SQL和声明 参数。 import java.sql.Types; import javax.sql.DataSource; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.SqlUpdate; public class UpdateCreditRating extends SqlUpdate { public UpdateCreditRating(DataSource ds) { setDataSource(ds); setSql("update customer set credit_rating = ? where id = ?"); declareParameter(new SqlParameter("creditRating", Types.NUMERIC)); declareParameter(new SqlParameter("id", Types.NUMERIC)); compile(); } /** * @param id for the Customer to be updated * @param rating the new value for credit rating * @return number of rows updated */ public int execute(int id, int rating) { return update(rating, id); } } 14.6.4A StoredProcedure 这个 StoredProcedure 类是一个超类 对对象的抽象,RDBMS存储过程。 这个类是 文摘 和它的各种 执行(. .) 方法 保护 访问, 防止使用其他比通过一个子类,它提供了更严格的 打字。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 255/514 继承的 SQL 属性的名称将会成为 存储过程在RDBMS。 定义一个参数 StoredProcedure 类,你使用一个 SqlParameter 或它的一个子类。 你必须 指定参数名称和SQL类型的构造函数 下面的代码片段。 SQL类型被指定使用 java sql类型 常量。 new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR), 第一线的 SqlParameter 声明了一个在参数。 在参数可以用于存储 过程调用和查询使用 SqlQuery 和它的子类中覆盖 下一 节。 第二行用 SqlOutParameter 声明了一个 出 参数用于存储 过程调用。 还有一个 SqlInOutParameter 对于 我 nOut 参数,参 数,提供一个 在 值的过程,也返回 值。 对于 我 n 参数,除了 名称和SQL类型,您可以指定一个量表数值数据或一个 类型名称为自定义数据库类型。 对于 出 参数你 可 以提供一个 RowMapper 处理映射的行 返回从一个裁判光标。 另一个选项是指定一个 SqlReturnType 这使您能够定义 自定 义处理的返回值。 下面的示例是一个简单的刀使用 StoredProcedure 要调用一个函数, sysdate() 配有任何Oracle数据库。 到 使用存储过程的功 能你必须创建一个类 延伸 StoredProcedure 。 在这个例子中, StoredProcedure 类是一个内部类,但如果 你需要重用 StoredProcedure 你申报的 它作为一个顶级类。 这个例子没有输入参数,但一个 输出参数声明为日期类型使用类 SqlOutParameter 。 这个 execute() 法执行过程和提取返回的日期的 结果 地图 。 结果 地图 有一个入口为每个声明输出 参 数,在这种情况下只有一个,使用参数名称 关键。 import java.sql.Types; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.object.StoredProcedure; public class StoredProcedureDao { private GetSysdateProcedure getSysdate; @Autowired public void init(DataSource dataSource) { this.getSysdate = new GetSysdateProcedure(dataSource); } public Date getSysdate() { return getSysdate.execute(); } private class GetSysdateProcedure extends StoredProcedure { private static final String SQL = "sysdate"; public GetSysdateProcedure(DataSource dataSource) { setDataSource(dataSource); setFunction(true); setSql(SQL); declareParameter(new SqlOutParameter("date", Types.DATE)); compile(); } public Date execute() { // the 'sysdate' sproc has no input parameters, so an empty Map is supplied... Map results = execute(new HashMap()); Date sysdate = (Date) results.get("date"); return sysdate; } } } 下面的示例 StoredProcedure 有两个输出参数(在本例中,甲骨文REF游标)。 import oracle.jdbc.OracleTypes; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.object.StoredProcedure; import javax.sql.DataSource; import java.util.HashMap; 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 256/514 import java.util.Map; public class TitlesAndGenresStoredProcedure extends StoredProcedure { private static final String SPROC_NAME = "AllTitlesAndGenres"; public TitlesAndGenresStoredProcedure(DataSource dataSource) { super(dataSource, SPROC_NAME); declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper())); declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper())); compile(); } public Map execute() { // again, this sproc has no input parameters, so an empty Map is supplied return super.execute(new HashMap()); } } 注意,重载的变体 declareParameter(. .) 法中已使用 这个 TitlesAndGenresStoredProcedure 构造函数 传递 RowMapper 实 现 实例;这是一个非常方便和强大的方法来重用现有的 功能。 代码的两个 RowMapper 实现提供 下面。 这个 TitleMapper 类地图一个 ResultSet 一个 标题 域对象中的每一行提供 ResultSet : import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; import com.foo.domain.Title; public final class TitleMapper implements RowMapper { public Title mapRow(ResultSet rs, int rowNum) throws SQLException { Title title = new Title(); title.setId(rs.getLong("id")); title.setName(rs.getString("name")); return title; } } 这个 GenreMapper 类地图一个 ResultSet 一个 流派 域对象中的每一行提供 ResultSet 。 import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; import com.foo.domain.Genre; public final class GenreMapper implements RowMapper<Genre> { public Genre mapRow(ResultSet rs, int rowNum) throws SQLException { return new Genre(rs.getString("name")); } } 将参数传递到一个存储过程,它具有一个或多个 输入参数在其定义RDBMS中,您可以编写一个强烈 类型 执行(. .) 方法,将代表 超 类的无类型 执行(Map参数) 方法 ( 保护 访问); 示例: import oracle.jdbc.OracleTypes; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.StoredProcedure; import javax.sql.DataSource; import java.sql.Types; import java.util.Date; import java.util.HashMap; import java.util.Map; public class TitlesAfterDateStoredProcedure extends StoredProcedure { private static final String SPROC_NAME = "TitlesAfterDate"; private static final String CUTOFF_DATE_PARAM = "cutoffDate"; public TitlesAfterDateStoredProcedure(DataSource dataSource) { super(dataSource, SPROC_NAME); declareParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE); declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper())); compile(); } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 257/514 public Map<String, Object> execute(Date cutoffDate) { Map<String, Object> inputs = new HashMap<String, Object>(); inputs.put(CUTOFF_DATE_PARAM, cutoffDate); return super.execute(inputs); } } 14.7一个常见的问题与参数和数据值处理 常见问题与参数和数据值存在 不同的方法由Spring框架提供的JDBC。 14.7.1A提供SQL类型信息参数 春天通常决定了SQL类型的参数依据 传入参数的类型。 可以显式地提供 SQL类型被用来当设置参数值。 这是有时 需要正确地 设置NULL值。 你可以提供SQL类型信息在几个方面: 许多更新和查询的方法 JdbcTemplate 采取额外的参数 的形式 int 数组。 该数组是用来 显示SQL类型的对应的参数使用 常数 值 java sql类型 类。 提供 一个条目为每个参数。 您可以使用 SqlParameterValue 类 包装参数值,需要这些额外的信息。 创建 一个新实例为每个值和通过在SQL类型和参 数 在构造函数中值。 你也可以提供一个可选的规模 参数的数值。 方法使用命名参数,使用 SqlParameterSource 类 BeanPropertySqlParameterSource 或 MapSqlParameterSource 。 他们都有方法 注册SQL类型为任何指定参数 值。 14.7.2A处理BLOB和CLOB对象 你可以存储图片,其他二进制对象,大块大块的 文本。 这些大型对象被称为BLOB和CLOB数据的二进制 字符数据。 在春天你可 以处理这些大型对象通过使用 JdbcTemplate的直接和还在使用更高的抽象 RDBMS对象和提供的 SimpleJdbc 类。 所有 这些 方法使用的一种实现 LobHandler 接口的实际管理 LOB数据。 这个 LobHandler 提供了访问 LobCreator 类,通过 getLobCreator 方法,用于创建新的LOB 对象被插入。 这个 LobCreator / LobHandler 提供 以下支持LOB的输入和输出: blob 一个​byte[]​getBlobAsBytes和setBlobAsBytes 一个​InputStream​getBlobAsBinaryStream和 setBlobAsBinaryStream CLOB 一个​​getClobAsString字符串和setClobAsString 一个​InputStream​getClobAsAsciiStream和 setClobAsAsciiStream 读者​​getClobAsCharacterStream和 setClobAsCharacterStream 下一个例子显示了如何创建和插入一个BLOB。 以后你 将会看到如何从数据库读回。 这个例子使用一个 JdbcTemplate 和一个 实施 AbstractLobCreatingPreparedStatementCallbac K 。 它实现了一个方法, setValues 。 这种方法提供了一个 LobCreator 那你使用设置值的LOB 列在您的SQL insert语句。 在这个例子中,我们假设有一个变量, lobHandle R ,已经被设置为一个实例 的 DefaultLobHandler 。 你通常设置这个 价值通 过依赖注入。 final File blobIn = new File("spring2004.jpg"); final InputStream blobIs = new FileInputStream(blobIn); final File clobIn = new File("large.txt"); final InputStream clobIs = new FileInputStream(clobIn); final InputStreamReader clobReader = new InputStreamReader(clobIs); jdbcTemplate.execute( "INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)", new AbstractLobCreatingPreparedStatementCallback(lobHandler) { protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { ps.setLong(1, 1L); lobCreator.setClobAsCharacterStream(ps, 2, clobReader, (int)clobIn.length()); lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, (int)blobIn.length()); } } 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 258/514 ); blobIs.close(); clobReader.close(); 通过在lobHandler,在这个例子中是一个平原 DefaultLobHandler 使用方法 setClobAsCharacterStream ,通过在 内容的CLOB。 使用方法 setBlobAsBinaryStream ,通过在内容 的BLOB。 现在是时间去阅读LOB数据从数据库。 再一次,你 使用一个 JdbcTemplate 与相同的实例变量 l obHandler 和一个引用 DefaultLobHandler 。 List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table", new RowMapper<Map<String, Object>>() { public Map<String, Object> mapRow(ResultSet rs, int i) throws SQLException { Map<String, Object> results = new HashMap<String, Object>(); String clobText = lobHandler.getClobAsString(rs, "a_clob"); results.put("CLOB", clobText); byte[] blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob"); results.put("BLOB", blobBytes); return results; } }); 使用方法 getClobAsString, 检索内容的CLOB。 使用方法 getBlobAsBytes, 检索内容的BLOB。 14.7.3A传入的值列表的条款 SQL标准允许选择行基于一个表达式 这包括一个变量的值列表。 一个典型的例子 select * from t演员,id(1、2、3) 。 这个变 量 列表是不直接支持准备语句的JDBC 标准;你不能声明一个变量数量的占位符。 你需要 一个数量的变化与所需数量的占位符 准备, 或者你需要动态生成的SQL字符串一旦你知道如何 许多占位符是必需的。 命名参数中提供支持 这个 NamedParameterJdbcTemplate 和 JdbcTemplate 以后者的方法。 传入值作为一个 java util列表 的 原始对象。 这个列表 将被用来插入所需的 占位符和传入值在该声明 执行。 注意 小心当传递的价值观。 JDBC标准并 不保证你可以使用超过100个值为 在 表达式列表。 各种数据库超过这 个数字, 但他们通常有一个硬限制多少值是允许的。 甲骨文的极限是1000。 除了原始值的值列表,你可以 创建一个 java util列表 的对象数组。 这 列表将支持多个表达式的定义 在 条款如 select * from t 演员那里(id、last_name)在((1, “约翰逊”),(2,' Harrop ')) 。 当然,这要求你的 数据库支持这种语法。 14.7.4A处理复杂类型为存储过程调用 当您调用存储过程你可以有时使用复杂 特定于数据库的类型。 以适应这些类型,弹簧 提供了一个 SqlReturnType 为处理这些 当 他们返回存储过程调用和 SqlTypeValue 当他们通过传递 参数的存储过程。 下面是一个示例返回一个Oracle的价值 Struct 用户声明类型的对象 项目类型 。 这个 SqlReturnType 接口有一个方法命名 getTypeValue 必须实现的。 这个接口是用来作为部分的 声明的 SqlOutParameter 。 final TestItem - new TestItem(123L, "A test item", new SimpleDateFormat("yyyy-M-d").parse("2010-12-31");); declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE", new SqlReturnType() { public Object getTypeValue(CallableStatement cs, int colIndx, int sqlType, String typeName) throws SQLException { STRUCT struct = (STRUCT)cs.getObject(colIndx); Object[] attr = struct.getAttributes(); TestItem item = new TestItem(); item.setId(((Number) attr[0]).longValue()); item.setDescription((String)attr[1]); item.setExpirationDate((java.util.Date)attr[2]); return item; } })); 你使用 SqlTypeValue 到 通过在一个Java对象的价值像 TestItem 到一个存储过程。 这个 SqlTypeValue 接口有一个方法命名 createTypeValue 你必须实现。 这个 通过活动连接,您可以使用它来创建 数据库对象(如 StructDescriptor 年代,见以下 例,或 ArrayDescriptor 年代。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 259/514 final TestItem - new TestItem(123L, "A test item", new SimpleDateFormat("yyyy-M-d").parse("2010-12-31");); SqlTypeValue value = new AbstractSqlTypeValue() { protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException { StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn); Struct item = new STRUCT(itemDescriptor, conn, new Object[] { testItem.getId(), testItem.getDescription(), new java.sql.Date(testItem.getExpirationDate().getTime()) }); return item; } }; 这 SqlTypeValue 现在可以被添加 到地图包含输入参数调用的执行 存储过程。 另一个使用的 SqlTypeValue 经过 在一个数组的值以Oracle存储过程。 甲骨文有自己的 内部 数组 类,必须用在这 情况下,你 可以使用 SqlTypeValue 创建 甲骨文的一个实例 数组 和填充它 与价值观从Java 数组 。 final Long[] ids = new Long[] {1L, 2L}; SqlTypeValue value = new AbstractSqlTypeValue() { protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException { ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, conn); ARRAY idArray = new ARRAY(arrayDescriptor, conn, ids); return idArray; } }; 14.8一个嵌入式数据库支持 这个 org.springframework.jdbc.datasource.embedded 包提供了支持嵌入式Java数据库引擎。 支持 HSQL , h2 ,和 Derby 本机提供。 你 也可以使用一个可扩展的API来插入新的嵌入式数据库类型和 数据源 实现。 14.8.1A为什么使用嵌入式数据库吗? 嵌入式数据库在开发阶段是有用的 项目由于其轻量级的性质。 福利包括易 配置,快速启动时间、可测试性和能力 在开发过程 中快速发展的SQL。 14.8.2A创建嵌入式数据库实例使用Spring的XML 如果你想揭露嵌入式数据库实例作为bean的一个 春天ApplicationContext,使用嵌入式数据库的标签 spring jdbc命名空间: <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:test-data.sql"/> </jdbc:embedded-database> 前面的配置创建一个嵌入式HSQL数据库 填充SQL从模式。 sql和testdata。 sql资源 类路径。 数据库实例提供了春天 集装箱 作为一个bean类型 javax.sql.DataSource 。 这个bean可以被注入到数据访问对象 需要。 14.8.3A以编程方式创建一个嵌入式数据库实例 这个 EmbeddedDatabaseBuilder 类提供了 一口流利的API来创建一个嵌入式数据库编程。 使用 这当你需要创建一个嵌入式 数据库实例在一个 独立的环境,比如数据访问对象的单元测试: EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); EmbeddedDatabase db = builder.setType(H2).addScript("my-schema.sql").addScript("my-test-data.sql").build(); // do stuff against the db (EmbeddedDatabase extends javax.sql.DataSource) db.shutdown() 14.8.4A扩展嵌入式数据库支持 Spring JDBC嵌入式数据库支持可扩展的两种方法: 1. 实现 EmbeddedDatabaseConfigurer 支持一个新的嵌入式数据库类型,例如Apache 德比。 2. 实现 DataSourceFactory 到 支持一个新的数据源的实现,比如连接 池,管理嵌入式数据库连接。 你是鼓励捐献回来扩展Spring 社区 jira.springframework.org 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 260/514 14.8.5A HSQL使用 Spring支持HSQL 1 8 0以上。 HSQL是默认的嵌入式 如果没有指定数据库类型是明确的。 指定HSQL明确, 设置 类型 属性的 嵌入式数据库 标签 HSQL 。 如果 你是使用builder API调用 setType(EmbeddedDatabaseType) 方法 EmbeddedDatabaseType.HSQL 。 14.8.6A使用H2 Spring支持数据库以及H2。 启用H2,设置 类型 属性的 嵌入式数据库 标签 h2 。 如果 你是使用builder API调用 setType(EmbeddedDatabaseType) 方法 EmbeddedDatabaseType.H2 。 14.8.7A使用Derby 春天也支持Apache Derby 10.5及以上。 让德比, 设置 类型 属性的 嵌入式数据库 标签 Derby 。 如果 使用构建器API,调用 setType(EmbeddedDatabaseType) 方法 EmbeddedDatabaseType.Derby 。 14.8.8A测试数据访问逻辑与嵌入式数据库 嵌入式数据库提供一个轻量级的方法来测试数据访问 代码。 以下是数据访问单元测试模板,使用一个 嵌入式数据库: public class DataAccessUnitTestTemplate { private EmbeddedDatabase db; @Before public void setUp() { // creates an HSQL in-memory database populated from default scripts // classpath:schema.sql and classpath:data.sql db = new EmbeddedDatabaseBuilder().addDefaultScripts().build(); } @Test public void testDataAccess() { JdbcTemplate template = new JdbcTemplate(db); template.query(...); } @After public void tearDown() { db.shutdown(); } } 14.9一个初始化一个数据源 这个 org.springframework.jdbc.datasource.init 包提供了支持初始化一个现有的 数据源 。 嵌入式数据库支持提供了 一个选 项来创建和初始化 数据源 对于一个应用程序,但是有时你 需要初始化一个实例运行在某个服务器上。 14.9.1A初始化数据库实例使用Spring的XML 如果你想要初始化数据库,您可以提供一个 引用一个DataSource bean,使用 初始化数据库 标记在 spring jdbc 命名空间: <jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:com/foo/sql/db-schema.sql"/> <jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/> </jdbc:initialize-database> 上面的示例脚本指定运行两种反对 数据库:第一个脚本是创建一个模式,第二个是一个 测试数据集插入。 脚本的位置也可以模 式 通配符在平时的蚂蚁风格用于资源在弹簧(如。 classpath *:/ com/foo/ * * / sql / *数据的sql )。 如果一个模式是 用脚本 执行在词汇的顺序URL或 文件名。 的默认行为数据库初始化器是 无条件地执行脚本提供。 这不会永远 你想要的,例如,如果对一个现有的数据库,运行 已经有测试 数据在它。 不小心删除的可能性 数据是减少最常见的模式(如上图),创建了 表的第一个,然后再插入数据,第一步将会失败如果 已经存在的表。 但是,为了获得更多的控制创建和删除 现有的数据,XML命名空间提供了一个两个选项。 这个 首先是标志开关初始化和关闭。 这可以设置 根据环境(如拉一个布尔值系统 属性或一个环境bean),例如。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 261/514 <jdbc:initialize-database data-source="dataSource" enabled="#{systemProperties.INITIALIZE_DATABASE}"> <jdbc:script location="..."/> </jdbc:initialize-database> 第二个选项来控制与现有数据所发生的是 更能容忍失败。 为此你可以控制的能力 初始化器忽略某些错误的SQL执行的 脚本,例 如。 <jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS"> <jdbc:script location="..."/> </jdbc:initialize-database> 在这个例子中我们 说我们希望有时候脚本将运行在一个空的 数据库和有一些下降语句的脚本将 因此失败。 所以失败的SQL 滴 语句将 忽视,但其他的失败将导致异常。 这是有用的,如果 你的SQL方言不支持 滴…… 如果存在 (或 类似的)但你要无条件删 除所有之前的测试数据 重新创建它。 在这种情况下,第一个脚本通常是一组下降, 其次是一套 创建 语句。 这个 忽略失败 选项可以设置为 没有 (默认的), 滴 (忽略失败 滴)或 所有 (忽略所有失败)。 如果你需要更多的控制比你得到的XML名称空间,你 可以简单地使用 DataSourceInitializer 直接,它定义一个组件在应用程序 中。 初始化的其他组件的依赖 数据库 一个大的类的应用程序可以使用这个数据库 初始化器没有进一步的并发症:那些不使用 数据库直到Spring上下文已经开始。 如 果你 应用程序是 不 其中的一个然后你可能 需要阅读这一节的其余部分。 数据库初始化取决于数据源实例和 运行脚本提供了在其初始化的回调(观点。 init方法 在XML bean定义或 InitializingBean )。 如果其他bean依赖于相同的数据 源,也使用数据源在一个初始化的回调之后 可能有一个问题,因为数据尚未 初始化。 一个 常见的例子是一个缓存初始化 急切地从数据库加载数据在应用程序 启动。 绕过这个问题你两个选择:改变你的缓存 初始化策略,后面的阶段,或确保数据库 初始化器初始化第一。 第一个选项可能容易如果应用程序在你的 控制,而不是其他。 一些建议如何实现这个 是 使缓存初始化懒洋洋地在第一次使用, 改善应用程序的启动时间 有你的缓存或一个单独的组件初始化 缓存实现 生命周期 或 SmartLifecycle 。 当应用程序上下文开始 了 SmartLifecycle 可以自动启动 它的 autoStartup 国旗是集,和一个 生命周期 可以开始手动调用吗 ConfigurableApplicationContext.start() 在 封闭的上下文。 使用弹簧 ApplicationEvent 或类似 自定义观察者机制引发缓存的初始化。 ContextRefreshedEvent 总是发表的吗 上下 文时准备好使用(毕竟豆子已经 初始化),所以,通常是一个有用的钩(这是怎么了 SmartLifecycle 通过默认)。 第二个选项也可以很容易。 一些建议 实现这个是 依靠弹簧BeanFactory默认行为,这是 豆子是初始化,在注册订单。 你可以很容易地 安排采用惯例一组 <进口/ >元素,订单 应用程序模块, 和确保数据库和数据库初始化 上市第一 单独的数据源和业务组件 用它和控制他们的启动顺序将它们 单独的ApplicationContext实例(如父母有 数据源和儿童有业 务组件)。 这 结构是常见的在Spring web应用程序,但可以更多 一般应用。 使用一个模块运行时像SpringSource dm Server和 单独的数据源和组件都依赖于它。 如指定bundle启动顺序为 datasource - > 初始化器- >业务组件。 15。 一个对象关系映射(ORM)数据访问 15.1一个介绍ORM和春天 Spring框架支持集成 使用Hibernate,Java持久化API(JPA),Java数据对象(JDO)和 iBATIS SQL映射为资源管理、数据访问对象 (DAO) 实现,和交易策略。 例如,对于Hibernate 有一流的支持与几个方便奥委会特性 解决了许多典型的Hibernate集成问题。 您可以配置 所有的支持功能,为O / R映射工具(对象关系)通过 依赖注入。 他们可以参加春季的资源 和事务管理,他们符合春天 的通用 事务和DAO异常层次结构。 推荐的集成 风格是代码DAOs反对纯Hibernate,JPA,JDO api。 这个 使用Spring的旧式风 格的刀模板不再推荐; 然而,这种风格的报道中可以找到 SectionA一个。 1、一个​​ORM usagea​​经典 在附录。 春天增加显著增强ORM层你的选择 当您创建数据访问应用程序。 你可以利用尽可能多的 集成支持如你所愿,你应该比较这种 集成 努力的成本和风险建立一个类似的基础设施 内部。 您可以使用许多ORM支持像一个图书馆, 无论技术,因为所有东西都设 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 262/514 计成一组 可重用的javabean。 ORM在Spring IoC容器促进 配置和部署。 因此大多数的例子在这一节中显示 配置Spring容器 内。 好处,使用Spring框架来创建你的ORM DAOs 包括: 更容易测试。 Spring的IoC方法使得 它容易交换实现和配置的位置 hibernate SessionFactory 情况下, JDBC 数据源 情况 下,事务 经理和映射对象实现(如果需要)。 这反过来使其 更容易测试每一块持续相关代码 隔离。 常见的数据访问异常。 弹簧可以 从你的ORM工具包装异常,将他们从专有 (潜在的检查),一个共同的运行时异常 DataAccessException层次结构。 这个特性允许您处理大多数 持久性的异常,这是不可恢复的,只有在 适当的层,没有恼人 的样板捕获,抛出, 异常声明。 你仍然可以陷阱和处理异常 必要的。 记住,JDBC异常(包括数据库特定的 方言)也转换为相同 的层次结构,这意味着你 可以执行一些操作与JDBC在一个一致的编程吗 模型。 通用资源管理。 春天 应用程序上下文可以处理位置和配置 hibernate SessionFactory 实例,JPA 会 实例,JDBC 数据源 实 例,iBATIS SQL映射 配置对象,和其他相关资源。 这使得这些 值容易管理和改变。 Spring提供了高效、简单 安全操作的持 久性资源。 例如,相关代码 使用Hibernate通常需要使用相同的冬眠 会话 以确保效率和适当的 事务处理。 春天使它易于 创建和绑定 会话 当前线程 透明的, 暴露出当前 会话 通过 hibernate SessionFactory 。 因此春天 解决了许多慢性问题的 典型Hibernate使用,任何地方 或JTA事务环境。 综合事务管理。 你可以 包装你的ORM代码与一个声明式的、面向方面的编程 (AOP)风格方法拦截器通过 transactional 注释或 显式配置事务AOP的建议在一个XML 配置文件。 在这两种情况下,事务语义和异常 处理(回滚等)将为您处理。 如 前所 下面,在 资源和事务 管理 ,你也可以交换各种事务经理, 不影响你的orm相关代码。 例如,您可以互换 本地事务和JTA 之间,相同的全服务(这样的 作为声明性事务)可在这两个场景。 此外,jdbc相关代码完全整合事务性的 你使用的代码做 ORM。 这是有用的数据访问 不适合ORM,比如批处理和BLOB流, 仍需要 共享公共事务与ORM操作。 待办事项: 提供链接到当前的样本 15.2一般ORM集成的考虑 本节重点介绍注意事项,适用于所有ORM 技术。 这个 SectionA 15.3,一个​​Hibernatea​​ 一节中提供了更多的 细节和也显示这些 功能和配置在一个具体的 上下文。 春天的主要目标是清楚的ORM集成应用程序 分层,与任何数据访问和事务的技术,和宽松的 耦合的应用程序对象。 没有更多的 业务服务依赖关系 数据访问或事务策略,没有更多的硬编码的资源 查找,没有更多的,没有更多的单件前些年定制服务 注册中 心。 一个简单的和一致的方法来连接应用程序 对象,使他们成为可重用和自由从集装箱依赖关系 可能的。 所有的个人数据访 问特性是有用的在他们自己的 但良好的集成Spring应用程序上下文的概念,提供 基于xml的配置和交叉引用的普通JavaBean实 例 那不需要弹簧意识。 在一个典型的Spring应用程序,许多 重要的对象是JavaBeans:数据访问模板、数据访问 对象、事务经 理、业务服务,使用数据访问 对象和事务管理器,web视图解析器、web控制器 使用业务服务,等等。 15.2.1A资源和事务管理 典型的业务应用程序是堆满了重复 资源管理代码。 许多项目试图发明他们自己的 解决方案,有时牺牲正确处理失败的 编程方 便。 提倡简单的解决方案为适当的春天 资源处理,即国际奥委会通过模板的例子 JDBC和AOP应用拦截器的ORM技术。 基础设施提供了适当的资源处理和 适当的转换的特定API的一个未经检查的异常 基础设施异常层次结构。 春天 介绍了一个 DAO异常层次结构,适用于任何数据访问 策略。 为直接JDBC, JdbcTemplate 类 在上一节中提到的处理和适当提供连接 转换 的 SQLException 到 DataAccessException 层次结构,包括 翻译的特定于数据库的SQL错误代码到有意义的异常 类。 为ORM 技术,详见下一节如何得到 相同的异常转换效益。 当涉及到事务管理, JdbcTemplate 在春季班钩子 事务支持,同时支持JTA和JDBC事务,通过 各自的弹簧事务管理器。 为支持 ORM 技术弹簧提供了Hibernate,JPA和JDO支持通过 Hibernate,JPA,JDO事务经理以及JTA支持。 对于 事务支持的细节,请参 阅 ChapterA 12, 事务管理 一章。 15.2.2A异常翻译 当你使用Hibernate,JPA或JDO在刀,你必须决定如何 处理持久性技术的原生异常类。 DAO 抛出一个子类的一个 HibernateException , PersistenceException 或 JDOException 根据不同的技术。 这些异常都是运行时异常和不需要 宣布或 捕获。 您可能还需要处理 IllegalArgumentException 和 IllegalStateException 。 这意味着调用者 只能治疗异常通常致命 的,除非他们想靠吗 在持久性技术自身的异常结构。 捕捉 具体原因如一个乐观锁定失败是不可能的 无需调用者实现策略。 取 消这交易 可能接受的应用程序和/或强烈orm 不需要任何特殊的例外处理。 然而,春天使 异常翻译应用透明地通过 @Repository 注释: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 263/514 @Repository public class ProductDaoImpl implements ProductDao { // class body here... } <beans> <!-- Exception translation bean post processor --> <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/> <bean id="myProductDao" class="product.ProductDaoImpl"/> </beans> 后处理程序自动查找所有例外 翻译(实现的 PersistenceExceptionTranslator 接口) 和建议所有bean标记着 @Repository 注 释,以便 发现译者可以拦截和应用适当的 翻译在抛出的异常。 总之:你可以实现DAOs基于朴素的持久性 技术的API和注解,同时仍然受益 spring管理事务,依赖注入和透明 异常转换(如果需 要)春天的自定义异常 层次结构。 15.3一个Hibernate 我们将首先报道 Hibernate 3 在一个春天 环境中,用它来说明这一方法,弹簧需要 对O / R映射器集成。 本部分将涵盖许多问题 细节和显示不同的DAO实现和 事务界定。 大多数这些模式可以直接翻译 所有其他支持ORM工具。 以下部分在这一章 将覆盖 其他ORM技术,显示更简短的例子吗 那里。 注意 Spring 3.0的,弹簧需要Hibernate 3.2或更高版本。 15.3.1A SessionFactory 设置在一个春天 容器 为了避免将应用程序对象硬编码的资源查找, 你可以定义资源,如JDBC 数据源 或Hibernate SessionFactory 在春天像豆子 集 装箱。 需要访问资源的应用程序对象接收 引用这样的预定义的实例通过bean引用 示的刀定义在接下来的部分。 以下摘自一个XML应用程序上下文定义 显示了如何设置一个JDBC 数据源 和一个 hibernate SessionFactory 上 它: <beans> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/> <property name="username" value="sa"/> <property name="password" value=""/> </bean> <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource"/> <property name="mappingResources"> <list> <value>product.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.HSQLDialect </value> </property> </bean> </beans> 开关从本地Jakarta Commons DBCP BasicDataSource 到一个jndi位于 数据源 (通常是管理的 应用程序服务器)只是一个物质 的配置: <beans> <jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/> </beans> 你也可以访问jndi坐落 SessionFactory ,使用Spring的 JndiObjectFactoryBean / < jee:jndi查找> 检索并将其公开。 然而,这 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 264/514 通常是不常见的EJB上下文之外。 15.3.2A实现DAOs基于普通Hibernate 3 API Hibernate 3有一个特性称为上下文会话,其中 Hibernate本身管理一个电流 会话 每笔交易。 这是大约 相当于一个Hibernate 的春天的同步 会话 每笔交易。 相应的 就像下面的例子DAO实现基于平原 Hibernate API: public class ProductDaoImpl implements ProductDao { private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public Collection loadProductsByCategory(String category) { return this.sessionFactory.getCurrentSession() .createQuery("from test.Product product where product.category=?") .setParameter(0, category) .list(); } } 这种风格是类似于Hibernate参考 文档和例子,除了抱着 SessionFactory 在一个实例变量。 我们强烈推荐这样一个基于实例的 设置在老派 静态 HibernateUtil 类 从Hibernate的CaveatEmptor示例应用程序。 (一般来说,不 保持任何资源 静态 变量,除非 绝对 必要的。) 上面的刀是依赖注入模式:它适合 很好地融入一个Spring IoC容器,就像如果编码的反对 春天的 hibernatetemplate 。 当然,这 样的刀 也可以设置在普通的Java(例如,在单元测试)。 简单 实例化并调用 setSessionFactory(. .) 与所需的工厂参考。 作为一 个Spring bean定义,DAO 就像下面一样: <beans> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> </beans> 这把刀的主要优势是,它取决于风格 Hibernate API只;没有进口弹簧类是必需的。 这是 当然吸引人从一个具有非侵袭性的角度 来看,和将没有 怀疑感觉更自然的Hibernate开发者。 然而,DAO抛出平原 HibernateException (这是遏制,那么 不是必须声明或捕捉),这意味着调用者只能 治疗异常通常致命的—— 除非他们想依赖 Hibernate的异常层次结构。 捕捉特定原因如一个 乐观锁定失败就不可能不把调用者 实现策略。 取消这交易 可能是可以接受的 应用程序基于hibernate和/或强烈不需要任何 特殊例外处理。 幸运的是,春天的 LocalSessionFactoryBean 支持Hibernate的 SessionFactory.getCurrentSession() 方法 任何Spring事务策 略,返回当前Spring管理 事务性 会话 即使 HibernateTransactionManager 。 当然, 标准的方法的行为仍然是当前的回归 会 话 有关正在进行的JTA 事务,如果任何。 这种行为适用不管你 使用Spring的 JtaTransactionManager ,EJB 容器管理的事务 (cmt),或JTA。 总之:你可以实现DAOs基于平原Hibernate 3 API,同时仍然能够参与spring管理 事务。 15.3.3A声明式事务划分 我们建议你使用Spring的声明式事务 支持,这使您能够代替显式事务界定 API调用在你的Java代码与AOP事务拦截器。 这 事务 拦截器可以配置在Spring容器使用 要么Java注释或XML。 这个声明式事务功能允许你 保持业务服务的重复事务界定。自由 代码和关注添加业务逻辑,这是真正的价值 您的应用程序。 注意 继续之前,你是 强烈 鼓励阅读 SectionA 12.5,一个​managementa​​​声明式事务 如果你 并没有这样做。 此外,像传播行为和事务语义 隔离级别可以改变在一个配置文件,不影响 业务服务实现。 下面的例子展示了如何配置一个AOP 事务拦截器,使用XML,因为一个简单的服务类: <?xml version="1.0" encoding="UTF-8"?> 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 265/514 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- SessionFactory, DataSource, etc. omitted --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <aop:config> <aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> </aop:config> <tx:advice id="txAdvice" transaction-manager="myTxManager"> <tx:attributes> <tx:method name="increasePrice*" propagation="REQUIRED"/> <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <bean id="myProductService" class="product.SimpleProductService"> <property name="productDao" ref="myProductDao"/> </bean> </beans> 这是服务类,建议: public class ProductServiceImpl implements ProductService { private ProductDao productDao; public void setProductDao(ProductDao productDao) { this.productDao = productDao; } // notice the absence of transaction demarcation code in this method // Spring's declarative transaction infrastructure will be demarcating // transactions on your behalf public void increasePriceOfAllProductsInCategory(final String category) { List productsToChange = this.productDao.loadProductsByCategory(category); // ... } } 我们还显示一个属性支持基于配置,在 下面的例子。 你 @ transactional注释服务层与注释和指导 Spring容器来找到这些注释 和提供事务 语义这些注释的方法。 public class ProductServiceImpl implements ProductService { private ProductDao productDao; public void setProductDao(ProductDao productDao) { this.productDao = productDao; } @Transactional public void increasePriceOfAllProductsInCategory(final String category) { List productsToChange = this.productDao.loadProductsByCategory(category); // ... } @Transactional(readOnly = true) public List<Product> findAllProducts() { return this.productDao.findAllProducts(); } } 正如您可以看到的从以下配置示例中, 配置是大大简化,而XML的例子中, 同时还提供相同的功能由注释 在服务层代码。 你需要 提供的 TransactionManager实现和一个“< tx:注解驱动/ >” 条目。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 266/514 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- SessionFactory, DataSource, etc. omitted --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <tx:annotation-driven/> <bean id="myProductService" class="product.SimpleProductService"> <property name="productDao" ref="myProductDao"/> </bean> </beans> 15.3.4A程序性事务界定 你可以限定交易在一个更高的水平的 应用程序的基础上,这样的底层数据访问服务跨越 任何数量的操作。 也不存在的限制 实 现周围的业务服务;它只需要一个 春天 PlatformTransactionManager 。 再一次, 后者可以来自任何地方,但最好是作为一个 bean引用 通过 setTransactionManager(. .) 方法, 就像 productDAO 应该设定的吗 setProductDao(. .) 法。 以下 片段显示 一个事务管理器和一个业务服务定义 一个Spring应用程序上下文,和一个示例的一个业务方法 实现: <beans> <bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> <bean id="myProductService" class="product.ProductServiceImpl"> <property name="transactionManager" ref="myTxManager"/> <property name="productDao" ref="myProductDao"/> </bean> </beans> public class ProductServiceImpl implements ProductService { private TransactionTemplate transactionTemplate; private ProductDao productDao; public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionTemplate = new TransactionTemplate(transactionManager); } public void setProductDao(ProductDao productDao) { this.productDao = productDao; } public void increasePriceOfAllProductsInCategory(final String category) { this.transactionTemplate.execute(new TransactionCallbackWithoutResult() { public void doInTransactionWithoutResult(TransactionStatus status) { List productsToChange = this.productDao.loadProductsByCategory(category); // do the price increase... } } ); } } 春天的 TransactionInterceptor 允许任何 检查应用程序异常被抛出的回调代码,虽然 TransactionTemplate 仅限于未经检查 的 例外在回调。 TransactionTemplate 在案件触发回滚 一个未经检查的应用程序异常,或如果事务被标记 回滚只有由应用程 序(通过 TransactionStatus )。 TransactionInterceptor 同样的行为 违约但允许可配置回滚策略/方法。 15.3.5A事务管理策略 两 TransactionTemplate 和 TransactionInterceptor 代表实际的 事务处理到一个 PlatformTransactionManager 实例,可以 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 267/514 一个 HibernateTransactionManager (对于一个 hibernate SessionFactory ,使用一个 ThreadLocal 会话 下罩)或一个 JtaTransactionManager (委托给了JTA 子系统的容器),Hibernate应用程序。 你甚至可以使用 一个自定义 PlatformTransactionManager 实现。 开关从本地Hibernate事务管理 对JTA,比如当面对分布式事务的要求 某些部署您的应 用程序,只是一种 配置。 简单地取代Hibernate事务经理 春天的JTA事务实现。 两个事务界定 和数据访问代码将工作没有变化, 因为他们只是利用 一般事务管理api。 对于分布式事务跨多个Hibernate会话 工厂,干脆把 JtaTransactionManager 作为一个交易策略与多个 LocalSessionFactoryBean 定义。 每个刀 然后得到一个特定的 SessionFactory 引用传递到其相应的bean属性。 如果所有的 潜在 JDBC数据源是事务性的容器,一个业务服务 可以限定交易在任何数量的dao和任意数量的吗 会话工厂没有特殊的方面,只 要是使用 JtaTransactionManager 作为战略。 <beans> <jee:jndi-lookup id="dataSource1" jndi-name="java:comp/env/jdbc/myds1"/> <jee:jndi-lookup id="dataSource2" jndi-name="java:comp/env/jdbc/myds2"/> <bean id="mySessionFactory1" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource1"/> <property name="mappingResources"> <list> <value>product.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.show_sql=true </value> </property> </bean> <bean id="mySessionFactory2" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource2"/> <property name="mappingResources"> <list> <value>inventory.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.OracleDialect </value> </property> </bean> <bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="sessionFactory" ref="mySessionFactory1"/> </bean> <bean id="myInventoryDao" class="product.InventoryDaoImpl"> <property name="sessionFactory" ref="mySessionFactory2"/> </bean> <bean id="myProductService" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> <property name="inventoryDao" ref="myInventoryDao"/> </bean> <aop:config> <aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> </aop:config> <tx:advice id="txAdvice" transaction-manager="myTxManager"> <tx:attributes> <tx:method name="increasePrice*" propagation="REQUIRED"/> <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> </beans> 两 HibernateTransactionManager 和 JtaTransactionManager 允许适当的jvm级别 与Hibernate缓存处理,没有特定容器的 事务 经理查找或JCA连接器(如果你不使用EJB来发起 事务)。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 268/514 HibernateTransactionManager 可以导出 Hibernate JDBC 连接 对纯JDBC 访问代码,为一个特定的 数据源 。 此功能允许高 级事务界定与混合 Hibernate和JDBC数据访问完全没有JTA,如果你 只有一个数据库访问。 HibernateTransactionManager 自动暴露 Hibernate事务作为一个JDBC事务如果你有设置 传入 SessionFactory 与 数据源 通过 数据源 财产的 LocalSessionFactoryBean 类。 或者,你 可以指定明确的 数据源 对于 该交易被认为是暴露通过吗 数据源 财产的 HibernateTransactionManager 类。 15.3.6A比较容器管理和本地定义的资源 你可以切换一个容器管理的JNDI SessionFactory 和一个 局部定义的一个,而无需改变一行 应用程序代码。 是否保持资源定义 在容器 或在本地应用程序中主要是一个重要的事务 策略,您使用。 而春天定义的地方 SessionFactory ,一个手动注册 JNDI SessionFactory 不提供任何 福利。 部署 SessionFactory 通过Hibernate的JCA连接器提供的附加价值 参与Java EE服务器的 管理基础设施,但是 不添加实际价值超过。 春天的事务支持不是绑定到一个容器。 与任何其他战略配置比JTA、事务支持也 工作在一个独立的或测试环境。 特别是在典 型的 单数据库升级到企业级的情况下交易,弹簧的单资源的地方 事务支持是 一个轻量级的和强大的替代JTA。 当您使用本地 EJB 无状态会话bean来驱动交易,你主要取决于一个EJB 容器和JTA,即使你只访问一个数据库,只有 使用无状态会话bean来提供 声明性事务通过 容器管理的事务。 同时, 直接使用JTA编程方式需要一个Java EE环境 好。 JTA并不只涉及集装箱依赖从JTA 本身和JNDI 数据源 实例。 对于非弹簧,jta驱动,你要冬眠交易使用 Hibernate JCA连接器,或额外的Hibernate事务代码 TransactionManagerLookup 配置为 适当的jvm级别缓存。 台弹力事务工作与局部定义的 hibernate SessionFactory 当他们做 当地一个JDBC 数据源 如果他们是 访问一个数据库。 因 此你只需要使用Spring的JTA 事务策略当你有分布式事务的需求。 JCA连接器需要特定容器部署步骤, 显然JCA支持放在第一 位。 这个配置需要 更多的工作比一个简单的web应用程序的部署与当地资源 定义和台弹力事务。 还有,你经常需要 企业版你 的容器如果你正在使用,例如, WebLogic表达,它不提供JCA。 一个Spring应用程序与 当地的资源和交易跨越一个单一的数据库 工作 任何Java EE web容器(没有JTA,JCA或EJB)如Tomcat, 树脂、甚至普通码头。 此外,您可以很容易地重用这样的 中间层在 桌面应用程序或测试套件。 考虑到所有的事情,如果你不使用ejb,坚持当地 SessionFactory 设置和弹簧的 HibernateTransactionManager 或 JtaTransactionManager 。 你把所有的 好处,包括适当的事务的jvm级别缓存和 分布式事务,没有不便的容器 部署。 JNDI注 册一个Hibernate SessionFactory 通过JCA连接器 只有增加价值如果结合ejb。 15.3.7A伪应用服务器与Hibernate的警告 在一些JTA环境具有非常严格的 XADataSource 实现——目前 只有一些WebLogic Server和WebSphere版本——当 Hibernate是 配置不考虑JTA PlatformTransactionManager 对象 这种环境下,它是可能的假警报或例外 显示在应用程序服务 器日志。 这些警告或异常 表明连接访问不再有效,或JDBC 访问不再是有效的,可能是因为事务不再 活跃的。 作为一个例子,这 是一个实际的异常从服务器: java.sql.SQLException: The transaction is no longer active - status: 'Committed'. No further JDBC access is allowed within this transaction. 你解决这个警告通过简单地使Hibernate意识到 JTA PlatformTransactionManager 实例, 它将同步(连同弹簧)。 你有两个选 择 这样做: 如果在您的应用程序上下文你已经直接 获得JTA PlatformTransactionManager 对象 (大概从JNDI通过 JndiObjectFactoryBean 或 < jee:jndi查找> ) 和喂养它,例如,春天的 JtaTransactionManager ,那么最简单的方法 是指定 的引用bean定义这个JTA吗 PlatformTransactionManager 实例作为 的价值 JtaTransactionManager 财产 对于 LocalSessionFactoryBean。 弹簧然后让 对象可以冬眠。 更有可能你不已经JTA PlatformTransactionManager 实例, 因为春天的 JtaTransactionManager 可以 发现它本身。 因 此 您需要配置Hibernate来查找JTA PlatformTransactionManager 直接。 这可以通过配置应用服务器,具体的 TransactionManagerLookup 类在Hibernate 配置中所描述的一样,Hibernate手册。 本节的其余部分描述的事件顺序 有和没有发生Hibernate的意识的JTA PlatformTransactionManager 。 当Hibernate配置不与任何意识的JTA PlatformTransactionManager ,以下 事件发生在当一个JTA事务提交: 1. JTA事务提交。 2. 春天的 JtaTransactionManager 是 同步到JTA事务,所以它被称为回通过 afterCompletion 回调的JTA事务 经理。 3. 在其他活动中,这种同步可以 触发一个回调到春天冬眠,通过Hibernate的 afterTransactionCompletion 回调(使用 清除 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 269/514 Hibernate缓存),其次是显式的 close() 调用Hibernate会话,它 使Hibernate来尝试 close() JDBC 连接。 4. 在某些环境中,这个 连接关闭() 叫然后触发 警告或错误,应用程序服务器不再考虑 连接 可用的,因为 事务已经提交。 当Hibernate配置了JTA的意识 PlatformTransactionManager ,以下 事件发生在当一个JTA事务提交: 1. JTA事务准备提交。 2. 春天的 JtaTransactionManager 是 同步到JTA事务,事务被称为 回来通过 beforeCompletion 回调的 JTA事务管理器。 3. 春天是知道Hibernate本身是同步的 JTA事务,比前面的行为不同 场景。 假设Hibernate 会话 需要关闭, 春天将关闭现在。 4. JTA事务提交。 5. Hibernate是同步的JTA事务,所以 事务被称为回通过 afterCompletion 回调的JTA事务 经理,可以正确地明确其缓存。 JDO 15.4 Spring支持标准JDO 2.0和2.1 api作为数据访问 策略,遵循同样的风格为Hibernate支持。 这个 相应的集成类位于 org.springframework.orm.jdo 包。 15.4.1A PersistenceManagerFactory 设置 弹簧提供了一个 LocalPersistenceManagerFactoryBean 类, 允许您定义一个本地JDO PersistenceManagerFactory 在春天 应用程序上下文: <beans> <bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean"> <property name="configLocation" value="classpath:kodo.properties"/> </bean> </beans> 或者,您可以设置一个 PersistenceManagerFactory 通过直接 实例化一个 PersistenceManagerFactory 实现 类。 一个JDO PersistenceManagerFactory 实现类遵循javabean模式,就像一个JDBC 数据源 的实现类,它是 一个自然的适合一个配置,使用 弹簧。 这个设置风格 通常支持spring定义JDBC 数据源 ,传递到 ConnectionFactory 财产。 例如,对于 开源JDO实现 DataNucleus(原JPOX)( http://www.datanucleus.org/ ), 这是XML配置 PersistenceManagerFactory 实现: <beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="myPmf" class="org.datanucleus.jdo.JDOPersistenceManagerFactory" destroy-method="close"> <property name="connectionFactory" ref="dataSource"/> <property name="nontransactionalRead" value="true"/> </bean> </beans> 你也可以设置JDO PersistenceManagerFactory 在JNDI 环境的Java EE应用服务器,通常通过JCA 连接器提供了特定的JDO实 现。 春天的 标准 JndiObjectFactoryBean 或 < jee:jndi查找> 可以用来 检索和揭露这样一个 PersistenceManagerFactory 。 然而, 一个EJB上下文之外,没有真正的利益存在着 PersistenceManagerFactory 在JNDI:只有 选择这样一个设置为一个很好 的理由。 看到 SectionA 15 3 6,一个​​比较容器管理和本地定义的resourcesa​​ 对于一个讨论;参数 也有适用于JDO。 15.4.2A实现DAOs基于JDO API的平原 DAOs可以直接写对平原JDO API,没有 任何Spring依赖项,通过使用一个注射 PersistenceManagerFactory 。 以下 是一个例 子,一个相应的DAO实现: public class ProductDaoImpl implements ProductDao { private PersistenceManagerFactory persistenceManagerFactory; public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { this.persistenceManagerFactory = pmf; } public Collection loadProductsByCategory(String category) { PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager(); 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 270/514 try { Query query = pm.newQuery(Product.class, "category = pCategory"); query.declareParameters("String pCategory"); return query.execute(category); } finally { pm.close(); } } } 因为上面的刀是依赖注入模式,它 正好符合Spring容器,就像如果编码的反对 春天的 JdoTemplate : <beans> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmf"/> </bean> </beans> 主要的问题是,他们总是这样DAOs得到一个新的 PersistenceManager 从工厂。 到 访问spring管理事务 PersistenceManager ,定义一个 TransactionAwarePersistenceManagerFactoryProxy (包括在弹簧)放在你的目标 PersistenceManagerFactory ,然后通过 引用代理加入到dao像下面的例子: <beans> <bean id="myPmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> <property name="targetPersistenceManagerFactory" ref="myPmf"/> </bean> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmfProxy"/> </bean> </beans> 你的数据访问代码将收到一个事务 PersistenceManager (如果有的话) PersistenceManagerFactory.getPersistenceManager() 它调用的方法。 后者方法调用会通过代理, 首先检查当前的事务吗 PersistenceManager 还没找到新 一个从工厂。 任何 close() 号召 PersistenceManager 被忽略的情况下 一个事务 PersistenceManager 。 如果你的数据访问代码总是运行在一个活跃的交易 (或至少在活动事务同步),它是安全的 省略了 persistencemanager关闭() 打 电话 因此整个 最后 块,你可能做的 保持你的DAO实现简洁: public class ProductDaoImpl implements ProductDao { private PersistenceManagerFactory persistenceManagerFactory; public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { this.persistenceManagerFactory = pmf; } public Collection loadProductsByCategory(String category) { PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager(); Query query = pm.newQuery(Product.class, "category = pCategory"); query.declareParameters("String pCategory"); return query.execute(category); } } 这样的dao,依靠活跃的交易,这是推荐的 你执行活动事务通过关闭 TransactionAwarePersistenceManagerFactoryProxy ' s allowCreate 国旗: <beans> <bean id="myPmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> <property name="targetPersistenceManagerFactory" ref="myPmf"/> <property name="allowCreate" value="false"/> </bean> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmfProxy"/> </bean> </beans> 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 271/514 这把刀的主要优势是,它取决于风格JDO API 只有,没有进口弹簧类是必需的。 这是当然 从一个角度吸引具有非侵袭性,可能会 觉得更 自然的JDO开发人员。 然而,DAO抛出平原 JDOException (这是遏制,那么 不是必须声明或捕捉),这意味着调用者只能 治疗异常致命的,除非你想依靠 JDO的 异常结构。 捕捉特定原因如乐观 锁定失败就不可能不把调用者了 实现策略。 取消这交易可能是可以接受的 应用程序 和/或基于jdo强烈不需要任何特殊的 异常处理。 总之,你可以根据平原DAOs JDO API,他们可以 还参与spring管理事务。 这种策略可能 吸引你如果你已经熟悉JDO。 然而,这 样的dao 扔平原 JDOException ,你会 必须显式地转换为春天的吗 DataAccessException (如果需要的话)。 15.4.3A事务管理 注意 你是 强烈 鼓励阅读 SectionA 12.5,一个​managementa​​​声明式事务 如果你还没有这么做,以获得一个 更详 细的报道Spring的声明式事务 支持。 在交易执行服务操作,您可以使用 春天的共同声明式事务设施。 例如: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="myTxManager" class="org.springframework.orm.jdo.JdoTransactionManager"> <property name="persistenceManagerFactory" ref="myPmf"/> </bean> <bean id="myProductService" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> </bean> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="increasePrice*" propagation="REQUIRED"/> <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> </aop:config> </beans> JDO需要一个活跃的事务修改持久化对象。 非事务性冲洗概念不存在在JDO,相比之下 冬眠。 由于这个原因,您需要设置选择 JDO 实现一个特定的环境。 具体地说,您需要设置 它显式地对JTA同步,来检测一个活跃的JTA 事务本身。 这是没有必要为本地 事务 由弹簧的 JdoTransactionManager ,但 有必要参与JTA事务,是否由 春天的 JtaTransactionManager 或者通过EJB CMT 和 平原JTA。 JdoTransactionManager 能够 露出一个JDO事务JDBC访问代码访问相同的 JDBC 数据源 ,只要 注册 JdoDialect 支持检索的 底层JDBC 连接 。 这是 对于基于jdbc的JDO 2.0实现默认情况下。 15.4.4A JdoDialect 作为一个高级功能,既 JdoTemplate 和 JdoTransactionManager 支持自定义 JdoDialect 可以传递到 JdoDialect bean属性。 在这个场景中,dao将 没有收到一个 PersistenceManagerFactory 参考而是一个完整的 JdoTemplate 实例 (例如,传递到 JdoTemplate 财产的 JdoDaoSupport )。 使用 JdoDialect 实现,您可以启用 高级功能由弹簧,通常是在一个特定于供应商的 方式: 运用特定的事务语义如定制 隔离级别或事务超时 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 272/514 检索事务JDBC 连接 因接触到基于jdbc的 DAOs 应用查询超时,它会自动计算 从spring管理事务超时 急切地冲洗一 使用PersistenceManager, 让 可见,基于jdbc事务更改数据访问代码 高级翻译的 JDOExceptions 到 春天 DataAccessExceptions 看到 JdoDialect Javadoc为更多的细节 在其运作和如何使用它们在春天的JDO 支持。 15.5一个JPA 春天JPA,可用在 org.springframework.orm.jpa 包,提供 全面支持 Java 持久性API 以类似的方式的集成 Hibernate或JDO,虽 然意识到底层实现 以提供额外的功能。 15.5.1A三个选项设置在一个春天的JPA环境 春天的JPA支持提供了三种方法建立JPA 会 需要使用的 应用程序获得一个实体管理器。 LocalEntityManagerFactoryBean 注意 只使用这个选项在简单的部署环境如 独立的应用和集成测试。 这个 LocalEntityManagerFactoryBean 创建 一个 会 适合 简单的部署环境中,应用程序只使用JPA 数据访问。 工厂bean使用 JPA PersistenceProvider 自动 机制(根据JPA的Java SE引导),在大多数 情况下,只需要您指定持久性单元名称: <beans> <bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="myPersistenceUnit"/> </bean> </beans> 这种形式的JPA部署是最简单和最 有限的。 你不能指 现有的JDBC 数据源 bean 定义和不支持全局事务的存在。 此外, 编织 (字节码转换)的持久化类是 特定于提供程序,通常需要一个特定的JVM指定代理 在启动。 这个选项是足够的只有独立 应用程序 和测试环境,JPA规范 设计。 获得一个 会 从 JNDI 注意 使用这个选项当部署一个Java EE 5服务器。 检查 你的服务器的文档如何部署一个自定义JPA提供者 到你 的服务器,允许不同的提供商比 服务器的默认。 获得一个 会 从JNDI(例如在一个Java EE 5环境),是一个简单的问题 改变XML配置: <beans> <jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/> </beans> 这一行动承担标准Java EE 5引导:Java EE服务器autodetects持久性单元(实际上, meta - inf / persistence . xml 在应用程序 jar文件) 和 持久化单元ref Java EE中的条目 部署描述符(例如, web . xml )和 定义环境命名上下文位置对于那些坚持 单位。 在这种情况下,整个持久性单元的部署, 包括织造(字节码转换)的持久性 类是Java EE服务器。 JDBC 数据源 通过JNDI定义 位置 meta - inf / persistence . xml 文件; EntityManager事务是集成了服务器的JTA 子系统。 春天仅仅使用获得的 会 ,将其传给 应用程序对象通过依赖注入和管理 交易的持久化单元, 通常通过 JtaTransactionManager 。 如果多个持久性单元用于相同的应用程序, 该bean的jndi检索等名称应与持久性单元 持久性单元名称,应用程序使用引用它们, 例如,在 @PersistenceUnit 和 persistencecontext 注释。 LocalContainerEntityManagerFactoryBean 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 273/514 当加载时编织是必需的吗? 并不是所有的JPA提供者需要JVM剂;Hibernate 是一个 例子,一个没有。 如果你的供应商不需要 一个 剂或你有其他选择,比如应用增强 在构建时 通过一个自定义的编译器或一个ant任务, 装载 时编织器 不应该 是 使用。 注意 使用这个选项在一个基于spring的完整的JPA功能 应用程序环境。 这包括诸如Tomcat web容器 以及独立 的应用和集成测试 复杂的持久性的需求。 这个 LocalContainerEntityManagerFactoryBean 给 完全控制 会 配置和适合的环境中细粒度 定制是必需的。 这个 LocalContainerEntityManagerFactoryBean 创建 一个 PersistenceUnitInfo 实例的基础 在 persistence . xml 文件,提供的 dataSourceLookup 策略,指定的 LoadTimeWeaver 。 可能是这样处理 自定义数据源的JNDI和控制外的编织 过程。 下面的 示例显示了一个典型的bean定义 LocalContainerEntityManagerFactoryBean : <beans> <bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="someDataSource"/> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/> </property> </bean> </beans> 下面的示例显示了一个典型 persistence . xml 文件: <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL"> <mapping-file>META-INF/orm.xml</mapping-file> <exclude-unlisted-classes/> </persistence-unit> </persistence> 注意 这个 排除未上市类 元素总是 表明 没有 扫描带注释的实体 类应该发生,为了支持 <排除未上市类/ > 快捷 方式。 这是在 线与JPA规范,这表明捷径,但 不幸的是在冲突与JPA XSD,这意味着 假 的快捷方式。 因此, < 排除未上市类>假 < /排除未上市类/ > 不支持。 简单 省略了 排除未上市类 元素如果你想 实体类扫描发 生。 使用 LocalContainerEntityManagerFactoryBean 是 最强大的JPA的设置选项,允许灵活的地方 配置在应用程序中。 它支持 链接到现有的 JDBC 数据源 ,同时支持本地 和全球事务,等等。 然而,它也增加了 要求在运行时环境中,比如可用性的 一个编织 能力类装入器如果持久性提供者的要求 字节码转换。 这个选项可能冲突与内置的JPA功能 Java EE 5服务器。 在一个完整的Java EE 5的环境,考虑获得 你 会 从JNDI。 另外,指定一 个自定义 persistenceXmlLocation 在你的 LocalContainerEntityManagerFactoryBean 定义,例如,meta - inf /我的持久 性。 xml,并且仅包含 一个描述符与这个名字在你的应用程序jar文件。 因为 Java EE 5服务器只有寻找违约 meta - inf / persistence . xml 文件,它忽略了这种 自定义持久性单元,从而避免冲突与一个 台弹力JPA设置前期。 (这适用于树脂3.1 例 子)。 这个 LoadTimeWeaver 接口是一个 spring提供了类,允许JPA ClassTransformer 实例 插在一个特定的方式,这取决于环境是一个 web容 器或应用程序服务器。 挂钩 ClassTransformers 通过一个Java 5 代理 通 常是没有效率的。 代理工作反对 整个虚拟机 和检查 每一 类加载的,通常 是 不良在生产服务器环境。 Spring提供了许多 LoadTimeWeaver 实现 不同的环境,从而使 ClassTransformer 实例 仅应用 每个类装入器 并不是每 VM。 指 一个​章节​弹簧configurationa​​ 在AOP章要了解更多有关 LoadTimeWeaver 实现和他们的设置,一般性或定制 不同的平台(比如Tomcat,WebLogic,OC4J,GlassFish、树脂和JBoss)。 在上述描述的部分,您可以配置一个上下文宽 LoadTimeWeaver 使用 @EnableLoadTimeWeaving 注释的 背景:加载时间织布 XML元素。 这样一个全球韦弗捡到所有JPA LocalContainerEntityManagerFactoryBeans 自动。 这是最便捷的方式建立一 个装载时编织器,自动提供平台 (WebLogic,OC4J,GlassFish、Tomcat、树脂、JBoss或VM剂)和自动传播的韦弗所有韦弗知道 豆: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 274/514 <context:load-time-weaver/> <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> ... </bean> 然而,如果需要,可以手动指定一个专门的韦弗通过 LoadTimeWeaver 属性: <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/> </property> </bean> 无论怎样LTW配置,使用这种技术,JPA应用程序依赖 仪表可以运行在目标平台(例:Tomcat)不需要代理。 这是重要的,特别是当 托管应用程序依赖于不同的JPA实现 因为JPA变压器应用只在类装入器级别,从而 彼此隔绝。 处理多个持久性单元 应用程序依赖于多个持久性单元 位置,存储在不同的罐子在类路径中,例如, 弹簧提供了 PersistenceUnitManager 作为一个 中 央存储库,避免持久性单元的发现 过程,它可以是昂贵的。 默认的实现允许 多个位置的指定解析和后来恢复 通过持久性单元名 称。 (默认情况下,类路径是 寻找 meta - inf / persistence . xml 文件。) <bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager"> <property name="persistenceXmlLocations"> <list> <value>org/springframework/orm/jpa/domain/persistence-multi.xml</value> <value>classpath:/my/package/**/custom-persistence.xml</value> <value>classpath*:META-INF/persistence.xml</value> </list> </property> <property name="dataSources"> <map> <entry key="localDataSource" value-ref="local-db"/> <entry key="remoteDataSource" value-ref="remote-db"/> </map> </property> <!-- if no datasource is specified, use this one --> <property name="defaultDataSource" ref="remoteDataSource"/> </bean> <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitManager" ref="pum"/> <property name="persistenceUnitName" value="myCustomUnit"/> </bean> 默认的实现允许定制的 PersistenceUnitInfo 情况下, 前美联储到JPA提供者,通过声明通过它 属性, 影响 所有 主办单位,或 通 过编程,通过 PersistenceUnitPostProcessor ,这 允许持久化单元的选择。 如果没有 PersistenceUnitManager 是指定的, 一 是创建和内部使用 LocalContainerEntityManagerFactoryBean 。 15.5.2A实现DAOs基于普通JPA 注意 虽然 会 实例是线程安全的, EntityManager 实例不。 这个 注入JPA EntityManager 像 一个 EntityManager 获取从一个 应用程序服务器的JNDI环境所定义的那样,JPA 规范。 它代表的所有调用当前 事务 EntityManager ,如果任何;否则,它 回落到一个新创建的 EntityManager 每个操作,实际上 使其使用 线程安全的。 可以编写代码对平原JPA没有任何 Spring依赖项,通过使用一个注射 会 或 EntityManager 。 弹簧可以理解 @PersistenceUnit 和 persistencecontext 注释都在 如果一个字段和方法级别 PersistenceAnnotationBeanPostProcessor 是 启用。 一个普通的JPA DAO实现使用 @PersistenceUnit 注释可能 看起来像这样: public class ProductDaoImpl implements ProductDao { private EntityManagerFactory emf; @PersistenceUnit public void setEntityManagerFactory(EntityManagerFactory emf) { this.emf = emf; } public Collection loadProductsByCategory(String category) { EntityManager em = this.emf.createEntityManager(); try { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 275/514 方法和字段级注入 注释表明依赖注入(如 @PersistenceUnit 和 persistencecontext )可以应用于现场或 方 法在类的内部,因此表达式 方法级 注入 和 字段 级注入 。 字段级注释是简洁和更容易使用,同时 方法级允许进一步加工的注入依赖项。 在这两 种情况下成员可见性(公共的、受保护的、私有 的)做 并不重要。 类级注释呢? 在Java EE 5平台,它们用于依赖 声明,而不是资 源注入。 Query query = em.createQuery("from Product as p where p.category = ?1"); query.setParameter(1, category); return query.getResultList(); } finally { if (em != null) { em.close(); } } } } 上面的刀也不依赖春天和仍然很合身 成一个Spring应用程序上下文。 此外,DAO利用 注释需要注入的默认 会 : <beans> <!-- bean post-processor for JPA annotations --> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> <bean id="myProductDao" class="product.ProductDaoImpl"/> </beans> 作为一种替代方法来定义 PersistenceAnnotationBeanPostProcessor 明确,考虑使用弹簧 背景:注释配置 XML元素在你 应用 程序上下文配置。 这样做将自动注册所有 弹簧标准后处理器对基于注解的配置, 包括 CommonAnnotationBeanPostProcessor 和 等等。 <beans> <!-- post-processors for all standard config annotations --> <context:annotation-config/> <bean id="myProductDao" class="product.ProductDaoImpl"/> </beans> 主要的问题是,它这样一个刀总是创建一个新的 EntityManager 通过工厂。 你 可以避免这一点,请求事务吗 EntityManager (也称为“共享 EntityManager“因为它是一个共享的,线程安全的代理实际 事务性EntityManager)注射而不是 工厂: public class ProductDaoImpl implements ProductDao { @PersistenceContext private EntityManager em; public Collection loadProductsByCategory(String category) { Query query = em.createQuery("from Product as p where p.category = :category"); query.setParameter("category", category); return query.getResultList(); } } 这个 persistencecontext 注释有 可选属性 类型 ,该属性的默认值 PersistenceContextType.TRANSACTION 。 这个默认是 你需要获得一个共享EntityManager代理。 另一种, PersistenceContextType.EXTENDED ,是一个完全 不同的事情:这个结果 在一个所谓的扩展EntityManager, 这是 不是线程安全的 ,因此不能使用 在并发访问的组件(比如spring管理单例 bean。 扩展 EntityManagers只应该用于有状态 组件,例如,驻留在一个会话,与生命周期的 EntityManager没有绑定到当前事务而是被 完全 依赖于应用程序。 注入的 EntityManager 是 spring管理(意识到正在进行的事务)。 它是重 要的 注意,尽管新DAO实现使用方法级别 注入一个 EntityManager 而不 是 一个 会 ,没有变化 需要在应用程序上下文XML由于注释的使用。 这把刀的主要优势是,它只风格取决于 Java持久化API;没有进口弹簧类是必 需的。 此外,正如JPA注释是理解,这种注射 应用自动由Spring容器。 这是 吸引人的从一个 具有非侵袭性的角度来看,和可能会感觉更自然的JPA 开发 人员。 15.5.3A事务管理 注意 你是 强烈 鼓励阅读 SectionA 12.5,一个​managementa​​​ 声明式事务 如果你还没有这么做,以获得一个 更详细的报 道Spring的声明式事务 支持。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 276/514 在交易执行服务操作,您可以使用 春天的共同声明式事务设施。 例如: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="myEmf"/> </bean> <bean id="myProductService" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> </bean> <aop:config> <aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> </aop:config> <tx:advice id="txAdvice" transaction-manager="myTxManager"> <tx:attributes> <tx:method name="increasePrice*" propagation="REQUIRED"/> <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> </beans> 春天JPA允许配置 JpaTransactionManager 揭露一个JPA事务 JDBC访问代码访问相同的JDBC 数据源 ,只要注册 JpaDialect 支持检索的 底层JDBC 连接 。 出 箱,Spring提供了方言的Toplink,Hibernate和OpenJPA JPA 实现。 见下一节详细讨论 JpaDialect 机制。 15.5.4A JpaDialect 作为一个高级功能 JpaTemplate , JpaTransactionManager 和子类的 AbstractEntityManagerFactoryBean 支持自定义 JpaDialect ,被传递到 JpaDialect bean属性。 在这种情况下, DAOs没有得到 会 参考而是 一个完整的 JpaTemplate 实例(例 如, 通过 到 JpaTemplate 财产 的 JpaDaoSupport )。 一个 JpaDialect 实现可以使一些高级功能支持的春天, 通常在一个特定 于供应商的方式: 运用特定的事务语义如定制 隔离级别或事务超时) 检索事务JDBC 连接 因接触到基于jdbc的 DAOs) 高级翻译的 PersistenceExceptions 春天 DataAccessExceptions 这是特别有价值的特殊事务语义 和高级翻译的异常。 默认实现 使用( DefaultJpaDialect )不提供任何 特殊的功能,如果上面的 功能是必需的,你必须 指定适当的方言。 看到 JpaDialect Javadoc更多 详细的操作和怎样使用它们在春天的JPA 支持。 15.6一个iBATIS SQL映射 iBATIS支持Spring框架多像JDBC 支持,它支持相同的模板样式编程,和作为 与JDBC和其他ORM技术支持工作的iBATIS 春天的 异常层次结构,让您享受Spring的IoC 特性。 事务管理可以通过Spring的标准处理 设施。 没有特殊的事务策略是必要的,为iBATIS, 因为没有特别的事务性资源涉及其他比 JDBC 连接 。 因此,春天的标准JDBC DataSourceTransactionManager 或 JtaTransactionManager 非常 足够的。 注意 Spring支持iBATIS 2. x。 iBATIS 1。 x支持类是没有 不再提供。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 277/514 15.6.1A设置 SqlMapClient 使用iBATIS SQL地图涉及创建SqlMap配置文件 包含语句和结果的地图。 春天负责装货 那些使用 SqlMapClientFactoryBean 。 为 的例子,我们将使用以下 帐户 类: public class Account { private String name; private String email; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getEmail() { return this.email; } public void setEmail(String email) { this.email = email; } } 到地图这 帐户 类和iBATIS 2。 x我们 需要创建以下SQL映射 帐户xml : <sqlMap namespace="Account"> <resultMap id="result" class="examples.Account"> <result property="name" column="NAME" columnIndex="1"/> <result property="email" column="EMAIL" columnIndex="2"/> </resultMap> <select id="getAccountByEmail" resultMap="result"> select ACCOUNT.NAME, ACCOUNT.EMAIL from ACCOUNT where ACCOUNT.EMAIL = #value# </select> <insert id="insertAccount"> insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#) </insert> </sqlMap> 配置文件为iBATIS 2看起来像这样: <sqlMapConfig> <sqlMap resource="example/Account.xml"/> </sqlMapConfig> 记住,iBATIS加载资源从类路径,所以要 确定添加 帐户xml 文件类 路径。 我们可以使用 SqlMapClientFactoryBean 在 Spring容器。 请注意,使用iBATIS SQL地图2。 x,JDBC 数据源 通常是指定的吗 SqlMapClientFactoryBean ,使懒惰 加载。 这是配置需要这些bean 定义: <beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation" value="WEB-INF/sqlmap-config.xml"/> <property name="dataSource" ref="dataSource"/> </bean> </beans> 15.6.2A使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport 这个 SqlMapClientDaoSupport 类提供了一个 支持类相似 SqlMapDaoSupport 。 我们把它实现我们的道: 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 278/514 public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao { public Account getAccount(String email) throws DataAccessException { return (Account) getSqlMapClientTemplate().queryForObject("getAccountByEmail", email); } public void insertAccount(Account account) throws DataAccessException { getSqlMapClientTemplate().update("insertAccount", account); } } 在DAO,我们使用预配置的 SqlMapClientTemplate 执行查询, 在设置好 SqlMapAccountDao 在 应用程序上下文和布线与我 们的 SqlMapClient 实例: <beans> <bean id="accountDao" class="example.SqlMapAccountDao"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean> </beans> 一个 SqlMapTemplate 实例还可以 手动创建,传递 SqlMapClient 作为 构造函数参数。 这个 SqlMapClientDaoSupport 基 地 preinitializes类只是一个 SqlMapClientTemplate 实例对我们。 这个 SqlMapClientTemplate 提供了一种通用的 执行 法,把一个自定义的 SqlMapClientCallback 实现作为参数。 这 可以,例 如,用于批处理: public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao { public void insertAccount(Account account) throws DataAccessException { getSqlMapClientTemplate().execute(new SqlMapClientCallback() { public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { executor.startBatch(); executor.update("insertAccount", account); executor.update("insertAddress", account.getAddress()); executor.executeBatch(); } }); } } 一般来说,任何操作组合所提供的本地 SqlMapExecutor API可以用于这样一个回调。 任何抛出 SQLException 自动转换 Spring的通用 DataAccessException 层次结构。 15.6.3A实现DAOs基于普通iBATIS API DAOs可以编写对平原iBATIS API,没有任何 Spring依赖项,直接使用注射 SqlMapClient 。 下面的示例显示了一个 相应的DAO 实现: public class SqlMapAccountDao implements AccountDao { private SqlMapClient sqlMapClient; public void setSqlMapClient(SqlMapClient sqlMapClient) { this.sqlMapClient = sqlMapClient; } public Account getAccount(String email) { try { return (Account) this.sqlMapClient.queryForObject("getAccountByEmail", email); } catch (SQLException ex) { throw new MyDaoException(ex); } } public void insertAccount(Account account) throws DataAccessException { try { this.sqlMapClient.update("insertAccount", account); } catch (SQLException ex) { throw new MyDaoException(ex); } } } 在这个场景中,您需要处理 SQLException 抛出的iBATIS API定制 时尚,通常通过包装它在自己的特定于应用程序的刀 例外。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 279/514 连接在应用程序上下文仍然看起来像它 并在示例的吗 SqlMapClientDaoSupport , 由于这样的事实,即普通刀仍然遵循了ibatis 的基础 依赖注入模式: <beans> <bean id="accountDao" class="example.SqlMapAccountDao"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean> </beans> 16。 一个编组XML使用O / X映射器 16.1一个介绍 在这一章,我们将介绍Spring的对象/ XML映射支持。 对象/ XML映射,或O / X映射 短,是指将一个XML文档,从一个对象。 这个 转换过程也 被称为XML编组或XML序列化。 本章使用这些术语可以互换使用。 该领域内的O / X映射,一个 Marshaller 负责序列化一个吗 对象(图)XML。 以类似的方式,一个 解组程序 反序列化XML的 对象 图。 这个XML可以把形式的DOM文档,一个输入或输出流,或一个SAX处理程序。 一些使用Spring的好处对你的O / X映射需求: 易于配置一个 春天的bean工厂很容易marshallers配置,而不需要构建JAXB上下文, JiBX绑定工厂,等marshallers可以配置为在 您的应用程序的任何其他bean 上下文。 此外,XML的基于配置可用于许多marshallers,使 配置更加简单。 一致的接口 Spring的O / X映射操作通过两个全球接口: Marshaller 和 解组程序 接口。 这些抽象允许你切换O / X映射 框架 相对轻松地,很少或根本没有变化需要在类的 编组。 这种方法还有其他的好处使我们能够完成XML编组与 一个混搭的方法(例 如一些编组进行,其他使用JAXB使用XMLBeans) 非侵入性的方式,利用每个技术的力量。 一致的异常层次结构一个 Spring提供了一个转换从异常从底层O / X映射工具来自己的异常 层次与 XmlMappingException 作为根异常。 可以预期, 这些运行时异常包装原始异常所以不丢失信息。 16.2一个信号员和解组程序 所述的介绍,一个 Marshaller 序列化一个对象到XML,一个 解组程序 反序列化XML流到一个对象。 在本节中,我们将描述 这两 个弹簧接口用于此目的。 16.2.1A Marshaller 春天所有编组操作背后的抽象 org.springframework.oxm.Marshaller 接口,主要的方法 下面列出的是。 public interface Marshaller { /** * Marshals the object graph with the given root into the provided Result. */ void marshal(Object graph, Result result) throws XmlMappingException, IOException; } 这个 Marshaller 接口有一个主要方法,统帅给定的 对象到一个给定的 javax.xml.transform.Result 。 结果是一个标签 界面,基 本上代表了一个XML输出抽象:混凝土实现包装各种XML 表示,这显示在下表中。 结果 实现 封装XML表示 DOMResult org w3c dom节点 SAXResult org xml sax contenthandler StreamResult java输入输出文件 , java io outputstream ,或 java io作家 注意 虽然 元帅() 方法接受一个普通对象作为它的第一个 参数,最 Marshaller 实现不能处理任意 对象。 相反,一 个对象类必须映射到一个映射文件,标有一个注释, 注册信号员,或有一个共同的基类。 参考进一步部分 在这 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 280/514 一章来确定你选择的O / X技术管理这个。 16.2.2A解组程序 类似 Marshaller ,有 org.springframework.oxm.Unmarshaller 接口。 public interface Unmarshaller { /** * Unmarshals the given provided Source into an object graph. */ Object unmarshal(Source source) throws XmlMappingException, IOException; } 这个界面也有一个方法,该方法从给定的读取 javax.xml.transform.Source (一个XML输入抽象),并返回 对象读取。 与结果,源 是一个标记接口,有三个具体的实现。 每个 包装不同的XML表示,显示在下表中。 源 实现 封装XML表示 DOMSource org w3c dom节点 SAXSource org xml sax inputsource ,和 org xml sax xmlreader StreamSource java输入输出文件 , java io inputstream ,或 java io读者 尽管有两个单独的编组接口( Marshaller 和 解组程序 ),所有实现发现在春天ws实现在 一个类。 这意味着您可以连接一个编组 器类和引用它既是marshaller和一个 解组程序在你的 中 。 16.2.3A XmlMappingException 春天将异常从底层O / X映射工具自身的异常层次结构 XmlMappingException 作为根异常。 可以预期,这些运行时 异常包装 原始异常所以不会丢失任何信息。 此外, MarshallingFailureException 和 UnmarshallingFailureException 提供区分编组和 解组操作,即使底层O / X映射工具 并不这样做。 O / X映射显示异常层次结构如下图所示: O / X映射异常层次结构 16.3一个使用信号员和解组程序 春天的OXM可以用于各种各样的情况。 在接下来的例子中,我们将使用它 元帅的设置一个spring管理应用程序作为一个XML文 件。 我们将使用一个简单的JavaBean来 代表设置: public class Settings { private boolean fooEnabled; public boolean isFooEnabled() { return fooEnabled; } public void setFooEnabled(boolean fooEnabled) { this.fooEnabled = fooEnabled; } } 应用程序类使用这个bean来存储它的设置。 除了一个主要方法,类有两个 方法: 储存设定() 保存设置bean到文件命名 设置xml 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 281/514 ,和 loadSettings() 再次加载这些设置。 一个 main() 方法构造一个Spring应用程序上下文,并调用这两个方法。 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; public class Application { private static final String FILE_NAME = "settings.xml"; private Settings settings = new Settings(); private Marshaller marshaller; private Unmarshaller unmarshaller; public void setMarshaller(Marshaller marshaller) { this.marshaller = marshaller; } public void setUnmarshaller(Unmarshaller unmarshaller) { this.unmarshaller = unmarshaller; } public void saveSettings() throws IOException { FileOutputStream os = null; try { os = new FileOutputStream(FILE_NAME); this.marshaller.marshal(settings, new StreamResult(os)); } finally { if (os != null) { os.close(); } } } public void loadSettings() throws IOException { FileInputStream is = null; try { is = new FileInputStream(FILE_NAME); this.settings = (Settings) this.unmarshaller.unmarshal(new StreamSource(is)); } finally { if (is != null) { is.close(); } } } public static void main(String[] args) throws IOException { ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Application application = (Application) appContext.getBean("application"); application.saveSettings(); application.loadSettings(); } } 这个 应用 同时需要 Marshaller 和 解组程序 属性设置。我们可以使用以下 中 : <beans> <bean id="application" class="Application"> <property name="marshaller" ref="castorMarshaller" /> <property name="unmarshaller" ref="castorMarshaller" /> </bean> <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> </beans> 这个应用程序上下文使用Castor,但是我们可以使用任何其他marshaller实例描述 在本章后面。 注意,不需要任何进一步的 Castor配置默认值,所以bean 的定义是相当简单的。 还请注意, 使用CastorMarshaller 实现两 Marshaller 和 解组程序 ,所以 我们可以参考 到 使用CastorMarshaller bean都 Marshaller 和 解组程序 应用程序的属性。 这个示例应用程序将生成以下 设置xml 文件: <?xml version="1.0" encoding="UTF-8"?> <settings foo-enabled="false"/> 16.4基于XML配置 Marshallers可以配置更简明地使用标签从OXM名称空间。 让这些标签可用,适当的模式必须首先在导言引用的XML配置文 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 282/514 件。 注意“oxm相关文本如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oxm="http://www.springframework.org/schema/oxm" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd"> 目前,下列标签可用: jaxb2-marshaller xmlbeans marshaller 蓖麻marshaller jibx marshaller 每个标签将被解释在其各自的信号员的部分。 作为一个例子虽然,这里就是 配置一个JAXB2 marshaller看起来像: <oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/> JAXB 16.5 JAXB绑定编译器转换一个W3C XML Schema为一个或多个Java类,一个 jaxb属性 文件,以及可能出现的一些资源文件。 JAXB 也提供一个 的方法来产生一个带注释的Java类的模式。 Spring支持JAXB 2.0 API作为XML编组策略,遵循 Marshaller 和 解组程序 接口描述 SectionA 16.2,一个​​Marshaller和 Unmarshallera​​ 。 相应的集成 类居住在 org.springframework.oxm.jaxb 包。 16.5.1A Jaxb2Marshaller 这个 Jaxb2Marshaller 类实现两个春天 Marshaller 和 解组程序 接口。 它 需要一个上下文路径操作,您可以设置使用 contextPath 财产。 上下文路径是一个列表的冒号(:)分隔的Java包名称中包含模式 派生类。 它还提供了一个 classesToBeBound 属性,它允许您设置一个数组的 类是由marshaller。 执行模式验证通过指定一个或多个 模式的资源到 bean,就像这样: <beans> <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="classesToBeBound"> <list> <value>org.springframework.oxm.jaxb.Flight</value> <value>org.springframework.oxm.jaxb.Flights</value> </list> </property> <property name="schema" value="classpath:org/springframework/oxm/schema.xsd"/> </bean> ... </beans> XML的基于配置 这个 jaxb2-marshaller 标签配置 org.springframework.oxm.jaxb.Jaxb2Marshaller 。 这里是一个例子: <oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/> 另外,列表的类来绑定可以提供给信号员通过 类一定 子标记: <oxm:jaxb2-marshaller id="marshaller"> <oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Airport"/> <oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Flight"/> ... </oxm:jaxb2-marshaller> 可用的属性是: 属性 描述 需要 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 283/514 id 信号员的id 没有 contextPath JAXB上下文路径 没有 16.6一个蓖麻 Castor的XML映射是一个开源的XML绑定框架。 它允许您将数据包含在 一个java对象模型到/从一个XML文档。 默认情况下, 它不需要任何进一步的配置, 尽管一个映射文件可以用来更好地控制Castor的行为。 在Castor的更多信息,请参阅 蓖麻网站 。 Spring集成类位于 org.springframework.oxm.castor 包。 16.6.1A使用CastorMarshaller 与JAXB, 使用CastorMarshaller 实现两 Marshaller 和 解组程序 接口。 它可以连接如下: <beans> <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" /> ... </beans> 16.6.2A映射 尽管可以依靠Castor的默认编组的行为,它可能是必要的 更多的控制它。 这可以通过使用Castor映射文件。 要了解更多信息, 请参考 到 Castor的XML映射 。 映射可以设置使用 mappingLocation 资源属性,指示 下面用一个类路径的资源。 <beans> <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" > <property name="mappingLocation" value="classpath:mapping.xml" /> </bean> </beans> XML的基于配置 这个 蓖麻marshaller 标签配置 org.springframework.oxm.castor.CastorMarshaller 。 这里是一个例子: <oxm:castor-marshaller id="marshaller" mapping-location="classpath:org/springframework/oxm/castor/mapping.xml"/> marshaller实例可以被配置在两个方面,通过指定的位置,要么 一个映射文件(通过 映射位置 属性),或通过 识别Java pojo(通过 目标类 或 目标包 属性),存在相应的 XML描述符类。 后者的方式通常是结合使用XML代码生成 从XML模式。 可用的属性是: 属性 描述 需要 id 信号员的id 没有 编码 编码用于从XML数据分解 没有 目标类 一个Java类名称为POJO的XML类描述符是可用的(如 通过生成代码生成) 没有 目标包 一个Java包名称,标识一个包,包含pojo和他们的 相应蓖麻 XML描述符类(通过代码生成生成从XML模 式) 没有 映射位 置 位置的XML映射文件Castor 没有 16.7一个XMLBeans XMLBeans是XML绑定工具,有完整的XML模式的支持,并提供完整的XML Infoset 保真度。 它采用了不同的方法,大多数其他的 O / X映射框架,在那 所有的类,生成一个XML模式都是来自 XmlObject ,包含XML绑定信息。 在XMLBeans的更多信息,请参阅 XMLBeans web站点 。 这个春天ws集成类驻留 在 org.springframework.oxm.xmlbeans 包。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 284/514 16.7.1A XmlBeansMarshaller 这个 XmlBeansMarshaller 实现两 Marshaller 和 解组程序 接口。 它可以配置如下: <beans> <bean id="xmlBeansMarshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller" /> ... </beans> 注意 注意, XmlBeansMarshaller 只能元帅类型的对象吗 XmlObject , 并不是每个 java . lang . object 。 XML的基于配置 这个 xmlbeans marshaller 标签配置 org.springframework.oxm.xmlbeans.XmlBeansMarshaller 。 这里是一个例子: <oxm:xmlbeans-marshaller id="marshaller"/> 可用的属性是: 属性 描述 需要 id 信号员的id 没有 选项 bean的名称XmlOptions也被用于这个信号员。 通常一个 XmlOptionsFactoryBean 定义 没有 JiBX 16.8 JiBX框架提供了一个解决方案类似JDO提供了ORM:绑定定义定义了 规则Java对象如何从XML转换为或。 在准备绑定和编译 类,一个JiBX绑定编译器提高类文件,添加代码来处理转换的实例 从类或XML。 在JiBX的更多信息,请参阅 JiBX网站 。 Spring集成类位于 org.springframework.oxm.jibx 包。 16.8.1A JibxMarshaller 这个 JibxMarshaller 类实现两 Marshaller 和 解组程序 接口。 操作,它需要类的名称,在,你可以元帅设置使用 targetClass 财 产。 可选地,您可以设置绑定名称使用 bindingName 财产。 在下一个示例,我们绑定 航班 类: <beans> <bean id="jibxFlightsMarshaller" class="org.springframework.oxm.jibx.JibxMarshaller"> <property name="targetClass">org.springframework.oxm.jibx.Flights</property> </bean> ... 一个 JibxMarshaller 是配置为一个类。 如果你想元帅 多个类,您必须配置多个 JibxMarshaller 年代 不同 targetClass 属性 值。 XML的基于配置 这个 jibx marshaller 标签配置 org.springframework.oxm.jibx.JibxMarshaller 。 这里是一个例子: <oxm:jibx-marshaller id="marshaller" target-class="org.springframework.ws.samples.airline.schema.Flight"/> 可用的属性是: 属性 描述 需要 id 信号员的id 没有 目标类 这个信号员的目标类 是的 bindingName 绑定使用的名称这信号员 没有 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 285/514 16.9一个XStream 是一个简单的图书馆XStream序列化对象到XML,然后再返回。 它不需要任何映射,和 生成干净的XML。 XStream的更多信息,请参阅 XStream网站 。 Spring集成类位于 org.springframework.oxm.xstream 包。 16.9.1A XStreamMarshaller 这个 XStreamMarshaller 不需要任何配置,可以配置吗 在一个应用程序上下文直接。 为了进一步定制XML,您可以设置一个 别 名映射 ,包括字符串别名映射到类: <beans> <bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"> <property name="aliases"> <props> <prop key="Flight">org.springframework.oxm.xstream.Flight</prop> </props> </property> </bean> ... </beans> 警告 默认情况下,XStream允许任意类来将它们分散,从而导致安全 漏洞。 因此,建议设置 supportedClasses 产 权 XStreamMarshaller ,就像这样: <bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"> <property name="supportedClasses" value="org.springframework.oxm.xstream.Flight"/> ... </bean> 这将确保只有注册类资格解组。 此外,您可以注册 自定义转换器 确保只有您可以将它们分散支持类。 注意 注意,XStream是一个XML序列化库,而不是一个数据绑定库。 因此,它有 名称空间支持有限。 因此,它是相 当不适合使用在Web服务。 PartA V。 一个Web 这部分的参考文档涵盖了春天 框架的支持表示层(和特别 基于网络的表示层)。 Spring框架的web框架, Spring Web MVC 覆盖在第一夫妇的 章。 一个数量的剩余的章节的部分 参考文档涉及Spring框架的 集成与其他web技术,例如 Struts 和 JSF ( 名字,但两个)。 这部分包括覆盖Spring的MVC portlet框架 。 ChapterA 17, Web MVC框架 ChapterA 18, 视图技术 ChapterA 19, 与其他web框架集成 ChapterA 20, Portlet MVC框架 17。 Web MVC框架 17.1一个介绍Spring Web MVC框架 Spring的Web模型-视图-控制器(MVC)框架设计 围绕一个 DispatcherServlet 分派请求, 到处理程序,可配置的处理程序映射、 视图解析、语言环境 和主题解析以及支持上传文件。 默认 处理程序是基于 controller 和 @RequestMapping 注释,提供一个 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 286/514 Spring Web Flow Spring Web Flow(SWF)的目标是成为最好的 解决方案的管理 web应用程序的页面流。 SWF集成了现有的框架(如Spring MVC,Struts, JSF,在两个servlet和portlet的环境。 如果你有 一个业务 过程(或进程),将受益于一个对话模型 不是一个纯粹的请求模型,然后SWF可能解决方 案。 SWF允许您捕获逻辑页面流作为独立的模块 在 不同的情况下,是可重用的,因此是理想的建筑 web应用程序模块,指导用户通过导航控制 驱动 的业务流程。 主权财富基金的更多信息,请参考 Spring Web Flow网站 。 一个​​ 对扩展开放…… 一个​​ 一个关键的设计原则在Spring Web MVC和在 春天一般 是 一个​​ 对扩展开放、关闭 修改 一个​​ 原理。 一些方法在Spring Web MVC核心类的标记 最 后 。 作为一名开发人员你不能覆盖这些 方法 来提供自己的行为。 这个还没有做任意, 但是具 体该原理在心里。 这一原则的解释,请参考 专家 Spring Web MVC和网络流量 Ladd和其他由赛斯; 特别是看 到节”一看设计,”在117页的 第一个版本。 另 外,看到 1. 鲍勃 马丁,开放闭合原则(PDF) 你不能添加建议最后的方法当你使用Spring MVC。 例如,您不能添加建议 AbstractController.setSynchronizeOnSession() 法。 指 SectionA 9 6 1,一个​​理解AOP proxiesa​​ 更多 信息在AOP代理和为什么你不能 添加建议最后 方法。 广泛的灵活的处理方法。 通过引入弹簧 3.0, controller 机制还允许 你创建RESTful Web站点和应用程序,通过 @PathVariable 注释和其他 特性。 在Spring Web MVC您可以使用任何对象作为命令或 形式支持对象;您不 需要实现一个特定于框架的 接口或基类。 春天的数据绑定是高度灵活的: 对 的例子,它将作为验证错误类型不匹配,可以 评估应用程序,而不是系统错 误。 因此你不需要 复制你的业务对象的属性作为简单、无类型的字符串 你的表单对象仅仅处理无效提交,或转换 正确的字符串。 相反,它通常比直 接结合到你的 业务对象。 春天的视图解析是非常灵活的。 一个 控制器 通常负责 准备模型 地图 与 数据和选择 视图名称但它也可以直接写响应流和 完成请求。 视图名称解 析是高度可配置的通过 文件扩展名或接受标题内容类型的谈判,通过bean 的名字,一个属性文件,甚至是一个自定义的 ViewResolver 实现。 模型( 米 在MVC)是一个 地图 接口,它允许 对于完整的抽象视图技术。 你可以整合 基于直接与模板渲染技术如JSP、速度 和Freemarker,或直接生成XML、 JSON、原子,和许多其他类型 的内容。 该模型 地图 只是 变成了一个适当 的格式,比如JSP请求属性,一个 Velocity模板模型。 17.1.1A特性的Spring Web MVC Spring的web模块包括许多独特的网络支持 特点: 明确分工 。 一个​​每个角色 控制器,验证器,命令对象,形成对象,模型对 象, DispatcherServlet ,处理程序映射视图 解析器,等等一个​​能否实现 由一个专门的 对象。 强大的和简单的配置两个 框架和应用程序类作为JavaBeans 。 这 配 置功能包括简单引用跨语境, 如从web控制器到业务对象 验证器。 适应性、非侵入性、和 的灵活性。 定义任何控制器方法签名你 需要, 可能使用一个参数注释(如 @RequestParam,@RequestHeader,@PathVariable,和更多)对于一 个给定的 场景。 可重用的业务代码 , 不需要 对于重复 。 使用现有的业务对象作为命 令 或形成对象而不是镜像他们扩展特定的 框架的基类。 定制绑定和验证 。 类型 应用程序级验证不匹配是错误,保持 冒犯价 值,本地化的日期和号码绑定,等等 而不是字符串只有形式对象与手工 解析和 转换为业务对象。 可定制的处理程序映射和视图 决议 。 处理程序映射和视图解析 策略 的范围从简单的基于url的配置,来 复杂的,专门解决战略。 春天是多 web MVC框架,要灵活授权一个特定的 技术。 灵活的模型转移 。 模型转移 使用一个名称/值 地图 支持容易 集成与 任何视图技术。 可定制的语言环境和主题的分辨率,支持 有或没有春天的jsp标记库,支持JSTL, 支持速度而不需要额外的桥梁,所以 在。 一个简单而强大的JSP标记库称为 Spring标记库提供支持功能,如数据 绑定和主题 。 自定义标签允许最大 灵活性方面的标 记代码。 标签上的信息 库描述符,请参见附录资格 AppendixA G, 弹簧tld 一个JSP表单标记库,介绍了Spring 2.0中, 使得编写形式在JSP页面更容易。 对于 标记库描述符的信息,请参阅附录资格 AppendixA H, 弹簧形式tld bean的生命周期的范围仅仅是当前HTTP 请求或HTTP 会话 。 这不是一个特定的功能Spring MVC的本身,而是 这个 WebApplicationContext 容器(s),使用Spring MVC。 这些bean范围描述 在 SectionA 5 5 4,一个​​请求、会话和scopesa​​ 全球会议 插拔性17.1.2A其他MVC实现 实现比非spring MVC为一些项目。 许多团队希望利用他们现有的投资技巧和 工具。 大量的知识和经验存在Struts 框架。 如 果你可以忍受Struts的体系结构的缺陷,它可以是一个 可行的选择web层;同样的适用于网络系统和其他 web MVC框架。 如果你不想使用Spring的web MVC,但打算利用 其他的解决方案,弹簧提供,您可以将web MVC 您所选择的框架与弹簧容易。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 287/514 简单地启动一个春天 根应用程序上下文通过它 ContextLoaderListener ,来访问它 它的 ServletContext 属性(或弹簧的 各自的 助手方法)在Struts或网络系统动作。 没有 “插件”是相关的,所以没有专门的集成是必要的。 从 web层的角度来看,您只需使 用弹簧作为一个图书馆, 根应用程序上下文实例作为入口点。 你注册的bean和Spring的服务可以在你 指尖即使没有春天的Web MVC。 春天不竞争 Struts或网络系统在这种情况下。 它只 是解决了许多地区 那纯web MVC框架不,从bean配置数据 访问和事务处理。 所以你可以丰富你的应用程序 一个春天的中间 层和/或数据访问层,即使你只是想 使用,例如,事务抽象与JDBC或 Hibernate。 17.2一个的 DispatcherServlet Spring的web MVC框架是,像许多其他web MVC框架, 请求驱动,围绕一个中心Servlet,分派请求 对控制器和提供其他功能,促 进了 开发web应用程序。 春天的 DispatcherServlet 然而,不仅仅是 那。 它完全集成Spring IoC容器和作为 这样允许您使用 其他功能,春天。 请求处理工作流的Spring Web MVC DispatcherServlet 见下面的吗 图。 读者会认得的模式精明的 DispatcherServlet 是一 个表达的吗 一个​​ 前端控制器 一个​​ 设计模式(这是一个模式 Spring Web MVC股票与其他许多领先的Web框架)。 请求处理工作流在Spring Web MVC(高 级别) 这个 DispatcherServlet 是一个实际的 Servlet (它继承自 HttpServlet 基类),因此是宣布 这个 web . xml 您的web应用程序。 你需要地图 请求你要 DispatcherServlet 到 处理,通过使用URL映射在相同的 web . xml 文件。 这是标准Java EE Servlet配置; 下面的例子 显示了这样一个 DispatcherServlet 声明和 映射: <web-app> <servlet> <servlet-name>example</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>example</servlet-name> <url-pattern>/example/*</url-pattern> </servlet-mapping> </web-app> 在前面的示例中,所有请求开始 /例子 会处理吗 DispatcherServlet 实例命名 例子 。 在一个Servlet 3.0 +环境,你也有 选项配 置Servlet容器以编程方式。 下面是代码 相当于上述基础 web . xml 示例: public class MyWebApplicationInitializer implements WebApplicationInitializer { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 288/514 @Override public void onStartup(ServletContext container) { ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet()); registration.setLoadOnStartup(1); registration.addMapping("/example/*"); } } WebApplicationInitializer 是一个接口 Spring MVC提供确保你的基于代码的配置是发现和 自动使用初始化任何Servlet 3容 器。 一个抽象的基类 实现这interace命名 AbstractDispatcherServletInitializer 使得它更容易 登记 DispatcherServlet 通过 简单地指定 它的servlet映射。 看到 基于代码的Servlet容器初始化 为更多的细节。 以上仅是第一步在设置 Spring Web MVC。 你 现在需要配置各种bean使用的Spring Web MVC 框架(超越 DispatcherServlet 本身)。 详细的 SectionA 5.14,一个​​附加的功能 ApplicationContext 一个​​ , ApplicationContext 实例在春天可以 是作用域。 在Web MVC框架,每个 DispatcherServlet 有自己的 WebApplicationContext ,它继承了所有 已经定义的bean在根 WebApplicationContext 。 这些遗传 豆子可以覆盖在servlet特定的范围,您可以定义 新范围特定bean本地到给定的Servlet 实例。 上下文层次在Spring Web MVC 在初始化的 DispatcherServlet , Spring MVC查找一个文件命名 [servlet - name]servlet xml 在 - inf 您的web应用程序的目 录并创建 定义的bean,覆盖任何bean定义的定义 具有相同名称的在全球范围内。 考虑以下 DispatcherServlet Servlet配置(在 web . xml 文件): <web-app> <servlet> <servlet-name>golfing</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>golfing</servlet-name> <url-pattern>/golfing/*</url-pattern> </servlet-mapping> </web-app> 与上面的Servlet配置到位,你 需要一个文件叫什么 / - inf / 高尔夫 servlet xml 在你的 应用程序,该文件将包含您所有的Spring Web具体的mvc 组件(bean)。 你可以改变的确切位置 配置文件通过一个Servlet初始化参数(见下文 详情)。 这个 WebApplicationContext 是一个 扩展的平原 ApplicationContext 这有一些额外的功能所需的web应用程序。 它不同 从一个正常的 ApplicationContext 在这 有能力解决主题(见吗 SectionA 17.9,一个​themesa​​​使用 ), 和,它知道这Servlet相关(通 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 289/514 过一个链接 这个 ServletContext )。 这个 WebApplicationContext 绑定在 ServletContext ,通过使用静态方法 在 RequestContextUtils 类你可以总是 查找 WebApplicationContext 如果你 需要访问它。 17.2.1A特殊Bean类型 WebApplicationContext 春天 DispatcherServlet 使用特殊的 bean处理请求并呈现适当的视图。 这些bean Spring MVC的一部分。 你可以选择哪些 特殊bean来使用 通过简单的配置一个或更多的人在 WebApplicationContext 。 然而,你不需要做,最初因为Spring MVC 维 护一个列表的默认bean使用如果你没有配置任何。 将在下一节进行详细介绍。 首先看下面的表格 清单的特殊bean类型 DispatcherServlet 依赖。 17.1为多。 一个特殊的bean类型 WebApplicationContext Bean类型 解释 HandlerMapping 地图传入请求传递给处理程序和一个列表的 前置和后处理器(处理程序拦截器)基于一些 标准的细节有所不同 HandlerMapping 实现。 最受欢迎的实现支持 但其他实现注释 的控制器也存在。 HandlerAdapter 帮助 DispatcherServlet 到 调用处理程序映射到一个请求无论处理程序 实际上是调 用。 例如,调用一个带注释的控制器 需要解决各种注释。 因此主要目的 的 HandlerAdapter 是保护 DispatcherServlet 从这些细节。 HandlerExceptionResolver 地图视图也允许例外更多 复杂的异常处理代码。 ViewResolver 解决逻辑基于字符串的视图名称到实际 视图 类型。 LocaleResolver 解决地区客户使用, 为了能够提供国际化的观点 ThemeResolver 解决主题您的web应用程序可以使用, 的例子,提供个性化的布局 MultipartResolver 解析多部分请求例如支持处理 从HTML表单文件上传。 FlashMapManager 存储和检索“输入”和“输出” FlashMap 这可以用于传递属性 从一个请求到另一个, 通常在一个重定向。 DispatcherServlet 17.2.2A默认配置 正如上一节中提到的每个特殊bean 这个 DispatcherServlet 维护一个列表 使用默认的实现。 这个信息是 保存在文件 DispatcherServlet.properties 在包 org.springframework.web.servlet 。 所有特殊bean有一些合理的默认值 他们自己的。 虽然迟早你会需要定制 一个或更多这些bean的属性提供。 例如它是十分常 见的配置 一个 InternalResourceViewResolver 设置其 前缀 财产 父视图文件的位置。 无论细节,这个重要的概念 理解是,一旦 你配置一个特殊bean如一个 InternalResourceViewResolver 在你的 WebApplicationContext ,你 有效地覆盖列表的默认实现 这将一直使用否则这些特别的bean 类型。 例如如果你配置一个 InternalResourceViewResolver , 默认列表的 ViewResolver 实现被忽略。 在 SectionA 17.15,一个​​配置弹簧MVCa​​ 您将了解 其他选项配置Spring MVC包括 MVC Java配置和MVC XML名称空间两者 提供 一个简单的起点和假设小知识 Spring MVC的作品如何。 不管您如何选择 配置您的应用程序中,概念的解释这个 部分是 基本应该对你有所帮助。 17.2.3A DispatcherServlet处理顺序 在你设置一个 DispatcherServlet ,和一个 请求到来时,特定的 DispatcherServlet , DispatcherServlet 开始处理请求 如下: 1. 这个 WebApplicationContext 是 寻找和绑定在请求作为一个属性的 控制器和其他元素在这个过程中可以使用。 它 默认 绑定在钥匙吗 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE 。 2. 语言环境解析器绑定到请求使元素 在这个过程中解决地区使用在处理 请求(渲染视图、准备数据,等等)。 如果你不 需要语 言环境解决,你不需要它。 3. 主题解析器绑定到请求让元素等 作为观点确定哪些主题使用。 如果你不使用的主题,你 可以忽略它。 4. 如果你指定一个多部分文件解析器,请求 multiparts检查;如果multiparts被发现后,请求 裹在 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 290/514 MultipartHttpServletRequest 对于 进一步处理的其他过程中的元素。 看到 SectionA 17.10,一个​​弹簧的多部分(文件上 传)supporta​​ 为进一步的信息关于多部分 处理。 5. 一个适当的处理程序是寻找。 如果一个处理程序被发现, 执行链相关处理程序(预处理器, postprocessors和控制器)是按顺 序执行的准备 模型或渲染。 6. 如果一个模型被返回,视图呈现。 如果没有模型 回来的时候,(可能是由于一个预处理或后处理程序拦截 请求,也许出于安全 原因),没有视图呈现, 因为请求可能已经完成。 处理异常的解析器中声明的 WebApplicationContext 接异常 这期间会抛出请求的处理。 使用这些异常 解析器允许您定义自 定义行为来解决 例外。 春天 DispatcherServlet 还支持 返回的 最后修改日期 ,如 指定的Servlet API。 确定最后的过程 修改日期为一个特定的请求很 简单: DispatcherServlet 查找一个合适的处理程序 映射和测试处理程序是否发现实现了 lastModified 接口。 如果是这样,这 个值的 长 getLastModified(请求) 方法 lastModified 接口返回给 客户端。 你可以定制个人 DispatcherServlet 实例通过添加Servlet 初始化参数( 初始 元素) Servlet声明在 web . xml 文件。 看到 下表 为支持的参数列表。 为多17 2 DispatcherServlet 初始化 参数 参数 解释 contextClass 类,实现了 WebApplicationContext ,这 实例化这个Servlet所使用的上下文。 默认情况下, XmlWebApplicationContext 是使用。 contextConfigLocation 字符串传递到上下文实例(指定的 contextClass ),表明上下文(年代) 被发现。 字符串包含 潜在的多个字符串 (使用逗号作为分隔符)来支持多个上下文。 在 有多个上下文位置与 bean定义 两次,最新的位置优先。 名称空间 名称空间的 WebApplicationContext 。 默认为 [servlet - name]servlet 。 17.3一个实现控制器 控制器提供访问应用程序的行为 通常通过服务接口定义。 控制器 解释用户的输入并将其转换为一个模型,是代表来 用户通过 视图。 弹簧实现控制器在一个非常抽象 的方法,它使您能够创建一个广泛的控制器。 Spring 2.5引入了一个基于注解的编程模型为MVC 控制器,使用等注释 @RequestMapping , @RequestParam , @ModelAttribute ,等等。 这个注释 支持可用于Servlet MVC和Portlet MVC。 控制器 实现在这个风格没有扩展特定的基类 或 实现特定的接口。 此外,他们通常没有 直接依赖于Servlet或Portlet api,尽管您可以轻松 配置访问Servlet或Portlet设施。 提示 可用的 样品 库 ,许多web应用程序利用注释 这一节中描述的支持包括 MvcShowcase , MvcAjax , MvcBasic , 宠物诊所 , 生产商 ,和其他人。 @Controller public class HelloWorldController { @RequestMapping("/helloWorld") public String helloWorld(Model model) { model.addAttribute("message", "Hello World!"); return "helloWorld"; } } 正如您可以看到的, controller 和 @RequestMapping 注释允许灵活 方法名称和签名。 在这个特殊的例子中,方法接受 一个 模型 并返回一个视图的名字作为一个 字符串 ,但其他各种方法参数和 返回值可以作为后面解释这部分。 controller 和 @RequestMapping 和许多其他的 注释形式Spring MVC的基础实施。 本节 这些注释文件以及它们如何使用最为普遍的一种 Servlet环境。 17.3.1A定义一个控制器 controller 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 291/514 这个 controller 注释 表明一个特定的类服务的角色 控制器 。 春天不需要你延长 任何控制器的基类或参考Servlet API。 然 而,你可以 还是参考servlet特定功能如果你需要。 这个 controller 注释作为 一个原型的带注释的类,说明它的作用。 这个 调度程序扫描如此注释的类映射方法和检测 @RequestMapping 注释(见下 部分)。 你可以定义注释控制器豆子明确,使用 标准的Spring bean定义在调度员的上下文。 然而, 这个 controller 原型还允许 为自动, 符合春天一般支持检测 组件类在类路径中,自动注册的bean定义 为他们。 启用自动如此注释的控制器,您添加 组件扫描到您的配置。 使用 spring上下文 模式见下面的XML 代码片段: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.springframework.samples.petclinic.web"/> <!-- ... --> </beans> 17.3.2A映射请求 @RequestMapping 你使用 @RequestMapping 注释url映射如 /预约 到 整个类或一个特定的处理程序方法。 通常 类级别注释映射一个特定请求 路径(或路径模式) 在一个窗体控制器,与额外的方法级注释 缩小主要映射为一个特定的HTTP方法请求方法 (“得到”,“后”等) 或一个HTTP请求参数条件。 下面的例子从 生产商 样品 显示了一个控制器在Spring MVC应用程序,它使用这个 注释: @Controller @RequestMapping("/appointments") public class AppointmentsController { private final AppointmentBook appointmentBook; @Autowired public AppointmentsController(AppointmentBook appointmentBook) { this.appointmentBook = appointmentBook; } @RequestMapping(method = RequestMethod.GET) public Map<String, Appointment> get() { return appointmentBook.getAppointmentsForToday(); } @RequestMapping(value="/{day}", method = RequestMethod.GET) public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) { return appointmentBook.getAppointmentsForDay(day); } @RequestMapping(value="/new", method = RequestMethod.GET) public AppointmentForm getNewForm() { return new AppointmentForm(); } @RequestMapping(method = RequestMethod.POST) public String add(@Valid AppointmentForm appointment, BindingResult result) { if (result.hasErrors()) { return "appointments/new"; } appointmentBook.addAppointment(appointment); return "redirect:/appointments"; } } 在这个示例中, @RequestMapping 是用在许多地方。 第一次使用是在类型(类) 水平,这表明所有的处理方法在这个控制器 相 对于 /预约 路径。 这个 get() 方法有进一步 @RequestMapping 细化:它只 接受GET请求,这意味着为一个HTTP GET /预约 调用这个方法。 这个 post() 有一个类似的细化, getNewForm() 结合了HTTP的定义 方法和路径为一个,这样得到的请求 约会/ 新 由该方法。 这个 getForDay() 方法显示了另一个 使用 @RequestMapping :URI模板。 (见 接下来的 部分 )。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 292/514 一个 @RequestMapping 在类 水平不是必需的。 没有它,所有路径只是绝对的, 不相对。 下面的例子从 宠物诊所 示例应用程 序展示了一个治愈率更高 控制器使用 @RequestMapping : @Controller public class ClinicController { private final Clinic clinic; @Autowired public ClinicController(Clinic clinic) { this.clinic = clinic; } @RequestMapping("/") public void welcomeHandler() { } @RequestMapping("/vets") public ModelMap vetsHandler() { return new ModelMap(this.clinic.getVets()); } } 使用 @RequestMapping 在 接口方法 一个常见的陷阱在处理注释的控制器类 发生在应用功能,需要创建一个代理 控制器对象(如。 transactional 方法)。 通常你 将引入的接口控制器为了使用JDK吗 动态代理。 做这项工作你必须移动 @RequestMapping 注释,以及 任何其他类型和方法级注释(如。 @ModelAttribute , @InitBinder ) 接口 以及映射机制只能“看” 接口公开的代理。 或者,你可以激活 代理目标类= " true " 在配置 功能应用于控 制器(在我们的事务场景 在 < tx:注解驱动/ > )。 这样做表明 基于cglib代理,子类应该用来代替 基于接口的 JDK代理。 为更多的信息在不同的代理 机制看 SectionA 9.6,一个​​mechanismsa​​代理 。 但是要注意,方法参数注释,例如。 @RequestParam ,必须出现在 方法签名的控制器类。 新的支持类 @RequestMapping 方法在Spring MVC 3.1 Spring 3.1引入了一套新的支持类 @RequestMapping 方法称为 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 分别。 他们建议使用,甚至需要利用 新功能在Spring MVC 3.1和前进。 新的支持 类是默 认启用的MVC名称空间和MVC Java 配置但必须配置明确如果使用既不。 本节描述一些 重要的区别旧的和新的支持类。 Spring 3.1之前,类型和方法级的请求映射 在两个独立的阶段检查——一个控制器被选第一个 的 DefaultAnnotationHandlerMapping 和 实际的方法来调用是缩小了的第二 这个 AnnotationMethodHandlerAdapter 。 新的支持类在Spring 3.1中, RequestMappingHandlerMapping 是唯一的地方 在决定哪些方法应该处理请求。 认为作为一 个集合的控制器方法独特的端点 为每个方法和映射来自类型和方法级 @RequestMapping 信息。 这使一些新的可能性。 一次一个 HandlerInterceptor 或 HandlerExceptionResolver 现在可以预计, 基于对象的处理程序是 一个 HandlerMethod , 它允许他们检查的具体方法,其参数和 相关的注释。 加工为URL不再需要 被划分到不同的控制器。 还有几件事情不再可能的: 选择一个控制器首先用 SimpleUrlHandlerMapping 或 BeanNameUrlHandlerMapping 然后狭窄 该方法基于 @RequestMapping 注释。 依靠方法名称作为补救机制 之间消除歧义两 @RequestMapping 方法 这没有一个明确的路径映射URL路径但否则 匹配 同样,如通过HTTP方法。 在新的支持类 @RequestMapping 方法必须被映射 独特的。 有一个默认的方法(没有一个明确的吗 路径映射),请求处理如果没有其他 控制器方法匹配更具体。 在新的支持 如果一个匹 配的方法类没有找到一个404错误 是提高。 上述特征仍支持与现有的支持 类。 然而利用新的Spring MVC 3.1特性 你需要使用新的支持类。 URI模板模式 URI模板 可以用于方便吗 访问URL的选定部分在一个 @RequestMapping 法。 类URI URI模板是一个字符串,其中包含一个或多个 变量名。 当你为这些变量替代值, 模板变成一个URI。 这个 提出 RFC 为 URI模板定义了一个URI是参数化的。 对于 示例中,URI模板 http://www.example.com/users/ { userId } 包含 变量 userId 。 赋值 弗雷德 对变量的收益率 http://www.example.com/users/fred 。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 293/514 在Spring MVC可以使用 @PathVariable 注释方法 参数的值绑定到一个URI模板变量: @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable String ownerId, Model model) { Owner owner = ownerService.findOwner(ownerId); model.addAttribute("owner", owner); return "displayOwner"; } URI模板” /业主/ { ownerId } “ 指定变量名 ownerId 。 当 控制器处理这个请求,值 ownerId 将发现的价值 适当的部分的 URI。 例如,当一个请求到来时 /业主/弗雷德 的价值, ownerId 是 弗雷德 。 提示 处理@PathVariable注释,Spring MVC需要 找到匹配的URI模板变量的名字。 你可以指定它 在注释: @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { // implementation omitted } 或者如果URI模板变量名称匹配方法 参数名称可以省略这些细节。 只要你的代码是不 编译没有调试信 息,Spring MVC将匹配 方法参数名称到URI模板变量名称: @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable String ownerId, Model model) { // implementation omitted } 一个方法可以有任意数量的 @PathVariable 注释: @RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { Owner owner = ownerService.findOwner(ownerId); Pet pet = owner.getPet(petId); model.addAttribute("pet", pet); return "displayPet"; } 当一个 @PathVariable 注释是 用在 Map < String,String > 参数, 地图是填充所有URI模板变量。 URI模板可以组装的类型和路径水平 @RequestMapping 注释。 作为一个结果, findPet() 方法可以调用一个URL 如 /业主/ 42 /宠物/ 21 。 @Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping("/pets/{petId}") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } } 一个 @PathVariable 参数可以 的 任何简单的类型 比如int、long 日期等。弹簧自动转换为适当的类型或 抛出一个 TypeMismatchException 如果不能 这样做。 你也可以注册支持解析额外的数据 类型。 看到 一个​章节​方法参数和类型 Conversiona​​ 和 一个​章节​定制 WebDataBinder initializationa​​ 。 URI模板用正则表达式模式 有时你需要更精确定义URI模板 变量。 考虑到URL “/ spring web / spring web 3 0 5瓶” 。 你怎么把它吗 分成多个部分? 这个 @RequestMapping 注释 支持使用正则表达式在URI模板变量。 这个 语法是 { varName:regex } 在第一部分定义了吗 变量名和第二——表达式定期 示例: @RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}") public void handle(@PathVariable String version, @PathVariable String extension) { // ... } } 路径模式 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 294/514 除了URI模板, @RequestMapping 注释也 支持ant是基于路径的模式(例如, / myPath / *而已 )。 结合URI模板和 ant是基于 团还支持(例如, /业主/ * /宠物/ { petId } )。 模式包含占位符 模式 @RequestMapping 注释 支持$ {… }占位符与当地的属性和/或系统属性 和环境变量。 这可能是有用的情况下路径一个 控制器是映射到可能需要通过配置定制。 更多信息见Javadoc的占位符 来完成 。 矩阵变量 URI规范 RFC 3986 定义包括名称-值对的可能性路径段内。 没有特定的术语的规范。 一般的“URI路径参数”可以应用虽然 更加独特 “矩阵uri” , 源自一个古老的职位由Tim berners - lee,也经常使用 和广为人知。 在这些被称为Spring MVC 作为 矩阵变量。 矩阵变量可以出现在任何路径段,每个矩阵变量 用“;”分隔(分号)。 例如: “/汽车;颜色=红;年= 2012” 。 多个值可以 是“,”(逗号)分隔 “颜色=红、绿、蓝” 或变量名称可以重复 “颜色=红;颜色=绿色;颜色=蓝” 。 如果一个URL将包含矩阵变量,请求映射 模式必须代表他们与URI模板。 这样可以确保请求可以匹配正确不管 矩阵变量存在与 否,他们是按照什么顺序 提供了。 下面是一个例子,提取矩阵变量“问”: // GET /pets/42;q=11;r=22 @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET) public void findPet(@PathVariable String petId, @MatrixVariable int q) { // petId == 42 // q == 11 } 因为所有的路径段可能包含矩阵变量,在某些情况下 你需要更具体的识别在变量预计将: // GET /owners/42;q=11/pets/21;q=22 @RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET) public void findPet( @MatrixVariable(value="q", pathVar="ownerId") int q1, @MatrixVariable(value="q", pathVar="petId") int q2) { // q1 == 11 // q2 == 22 } 一个矩阵变量可以定义为可选的,默认值指定: // GET /pets/42 @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET) public void findPet(@MatrixVariable(required=true, defaultValue="1") int q) { // q == 1 } 所有的矩阵变量可能得到一个地图: // GET /owners/42;q=11;r=12/pets/21;q=22;s=23 @RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET) public void findPet( @MatrixVariable Map<String, String> matrixVars, @MatrixVariable(pathVar="petId"") Map<String, String> petMatrixVars) { // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23] // petMatrixVars: ["q" : 11, "s" : 23] } 注意,使使用矩阵变量,你必须设置 removeSemicolonContent 财产的 RequestMappingHandlerMapping 到 假 。 默认情况 下,它被设置为 真正的 除了 MVC名称空间和MVC的Java配置这两种自动启用 矩阵变量的使用。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 295/514 消耗品媒体类型 你可以缩小主要映射通过指定的列表 消耗品媒体类型。 请求将被匹配只有在 内容类型 请求头匹配指定的 媒体类型。 例如: @Controller @RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json") public void addPet(@RequestBody Pet pet, Model model) { // implementation omitted } 消耗品媒体类型表达式也可以否定在 !文本/平原 匹配所有请求其他比 那些 内容类型 的 文本/平原 。 提示 这个 消耗 条件支持 的类型和方法级别上。 不像大多数其他的条件,当 使用类型水平,方法级消费品类型覆 盖 而不是扩展类型级别消耗品类型。 可生产的媒体类型 你可以缩小主要映射通过指定的列表 可延长的媒体类型。 请求将被匹配只有在 接受 请求头匹配其中的一个 值。 此外,使用 产生 条件保证了实际内容类型用于生成 响应尊重媒体的类型中 产生 条件。 例如: @Controller @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, produces="application/json") @ResponseBody public Pet getPet(@PathVariable String petId, Model model) { // implementation omitted } 就像 消耗 ,可生产的媒体 类型的表达式可以否定在 !文本/平原 匹配所有请求其他比有一个 接受 头值 文本/平原 。 提示 这个 产生 条件支持 的类型和方法级别上。 不像大多数其他的条件,当 使用类型水平,方法级可生产的类型 覆盖 而不是扩展类型级别可生产的类型。 请求参数和头的值 你可以缩小请求匹配通过请求参数 条件如 “myParam” , ”! myParam” ,或 “myParam = myValue” 。 前两个测试的 要求 参数存在缺失和第三/一个特定的参数 值。 下面是一个示例请求参数值 条件: @Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } } 同样可以做测试请求头的存在/缺席 或以匹配根据特定请求报头值: @Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping(value = "/pets", method = RequestMethod.GET, headers="myHeader=myValue") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } } 提示 虽然您可以匹配 内容类型 和 接受 头值使用媒体类型野生 卡(例如 “content - type = text / *” 将 匹配 “text / plain” 和 “text / html” ),建议使用 消耗 和 产生 条件下分别相反。 他们的目的是专门为 这一 目的。 17.3.3A定义 @RequestMapping 处理程序 方法 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 296/514 一个 @RequestMapping 处理器方法可以有 一个非常灵活的签名。 支持的方法参数和返回 值将在下面部分。 大多数参数可 以 用在任意订单的唯一例外 BindingResult 参数。 这是描述的 下一节。 注意 Spring 3.1引入了一套新的支持类 @RequestMapping 方法称为 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 分别。 他们建议使用,甚至需要利用 新功能在Spring MVC 3.1和前 进。 新的支持类是默认启用从MVC名称空间和 与使用MVC Java配置但必须 明确如果使用既不配置。 支持方法参数类型 以下是支持的方法参数: 请求或响应对象(Servlet API)。 选择任何 特定的请求或响应类型,例如 ServletRequest 或 HttpServletRequest 。 会话对象(Servlet API):类型 HttpSession 。 一个论点的 类型强制存在相应的会话。 作为一个 因此,这样的一个论点是永 远 空 。 注意 会话的访问可能不是线程安全的,特别是在 一个Servlet环境。 考虑设置 RequestMappingHandlerAdapter ' s “synchronizeOnSession”标记为“true”如果多个请求 允 许并发访问会话。 org.springframework.web.context.request.WebRequest 或 org.springframework.web.context.request.NativeWebRequest 。 允许通用请求参数访问以及 请求/会话属性访问,没 有联系到本地 Servlet / Portlet API。 java util地区 对于当前 请求场所,由最具体的语言环境解析器 可用的,实际上,配置的 LocaleResolver 在一个Servlet 环境。 java io inputstream / java io读者 通往 请求的内容。 这个值是原始InputStream /读者 Servlet API公开的。 java io outputstream / java io作家 对于产生 响应的内容。 这个值是原始的OutputStream /作家 Servlet API公开的。 java安全主要 包含当前认证用户。 @PathVariable 注释参数 获取URI模板变量。 看到 一个​章节​URI模板Patternsa​​ 。 @MatrixVariable 注释参数 获取名称-值对位于URI路径片断。 看到 一个​章节Variablesa​​​矩阵 。 @RequestParam 注释参数 获取特定的Servlet请求参数。 参数 值转换为声明的方法参数类型。 看到 一个​章节​绑定请求 参数方法参数 @RequestParam 一个​​ 。 @RequestHeader 注释 参数获取特定的Servlet请求的HTTP标头。 参数值转换为声明的方法参数 类型。 @RequestBody 注释 参数获取HTTP请求体。 参数值 转换为声明的方法参数类型使用吗 HttpMessageConverter 年 代。看到 一个​章节​映射与@RequestBody请求主体 annotationa​​ 。 @RequestPart 注释 参数获取内容的一个“多部分/格式数据” 请求部分。 看到 SectionA 17 10 5,一个​​处理文件上传请 求从编程clientsa​​ 和 SectionA 17.10,一个​​弹簧的多部分(文件上传)supporta​​ 。 HttpEntity < ? > 参数 访问Servlet请求的HTTP标题和内容。 这个 请求流将被转换成实体使用 HttpMessageConverter 年代。看到 一个​​章节使用 HttpEntity < ? > 一个​​ 。 java util地图 / org.springframework.ui.Model / org.springframework.ui.ModelMap 对于 丰富的隐式模型,接触到网 络 视图。 org.springframework.web.servlet.mvc.support.RedirectAttributes 指定确切的属性集使用以防 重定向和也添加闪光属 性(属性存储 暂时在服务器端,使它们可用 请求重定向后)。 RedirectAttributes 被用于代替吗 隐式模型如果方法返回一 个“重定向:”前缀的视图 名称或 RedirectView 。 命令或表单对象绑定请求参数bean 属性(通过setter)或直接向田地,同 可定制的类型转换,取决于 @InitBinder 方法和/或 HandlerAdapter配置。 看到 WebBindingInitializer 属性 RequestMappingHandlerAdapter 。 这样 命令对象以及它 们的验证结果将 公开为模型属性默认情况下,使用命令类 类的名字——例如模型属性”orderAddress”命令 “some.package.OrderAddress”类型的对象。 这个 ModelAttribute 注释可用于 一个方法参数来定制模型属性名称 使 用。 org.springframework.validation.Errors / org.springframework.validation.BindingResult 验证结果为前面的命令对象 (或形式 紧接方法参数)。 org.springframework.web.bind.support.SessionStatus 状态处理标记形式处理完成, 触发器的清理会话属性 指定的 @SessionAttributes 注释处理程序的类型级别。 org.springframework.web.util.UriComponentsBuilder 一个建筑工人准备一个URL相对于当前请求的 主机、端口、方 案、上下文路径和文字部分 servlet映射。 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 297/514 这个 错误 或 BindingResult 参数必须遵循 模型对象被绑定立即方法 签名可能更多,一个模型对象和弹簧将创建 一个单独的 BindingResult 实例 他们中的每一个所以以下样品不工作: ExampleA 17.1。 一个无效的BindingResult和@ModelAttribute订购 @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, Model model, BindingResult result) { … } 注意,有一个 模型 参数之间的 宠物 和 BindingResult 。 得到这个工作 你必须重新排序参数如下: @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, Model model) { … } 支持方法返回类型 以下是支持的返回类型: 一个 ModelAndView 对象, 模型隐式富含命令对象和结果 的 @ModelAttribute 注释的参考数据 访问器方法。 一个 模型 对象, 视图名称隐式确定通过 RequestToViewNameTranslator 和 该模型隐式富含命令对象和 结果 @ModelAttribute 注释 参考数据访问方法。 一个 地图 对象揭露 模型、视图与名字隐式确定通过 RequestToViewNameTranslator 和 该模型隐式富含命令对象和 结 果 @ModelAttribute 注释 参考数据访问方法。 一个 视图 对象, 模型隐式确定通过命令对象和 @ModelAttribute 注释的参考数据 访问器方法。 处理程序方法也可以通 过编程的方式 丰富的模型通过宣布 模型 参数(见上图)。 一个 字符串 值解释 作为逻辑视图名称,与模型隐式确定 通过命令对象和 @ModelAttribute 带注释的参考数据访问方法。 处理程序方法 也可以通过编程的方式丰富模型通过声明一个吗 模型 参数(见 以上)。 无效 如果该方法处理响应 本身(通过编写响应内容直接,声明一个 类型的参数, ServletResponse / HttpServletResponse 这 目的),或者如果该视图名称应该是含蓄的 决定通过一个 RequestToViewNameTranslator (不 声明一个响应参数的处理 程序方法 签名)。 如果该方法标注 @ResponseBody ,返回类型是 写入响应HTTP的身体。 返回值将 转换为声明的方法参数类型使用 HttpMessageConverter 年代。看到 一个​章节​映射响应的身体 @ResponseBody annotationa​​ 。 一个 HttpEntity < ? > 或 ResponseEntity < ? > 对象提供 访问Servlet响应HTTP标题和内容。 这个 实体的身体可以转 化为响应流使用 HttpMessageConverter 年代。看到 一个​​章节使用 HttpEntity < ? > 一个​​ 。 一个 可调用< ? > 可以 时返回应用程序想要产生回报 价值管理的异步线程中的Spring MVC。 一个 DeferredResult < ? > 可以 时返回应用程序想要产生回报 值从一个线程自己的选择。 其他任何返回类型被认为是一个单一的模型 属性会接触到视图,使用属性名称 指定通过 @ModelAttribute 在 方法级(或默 认的属性名称基于返回 类型的类名)。 该模型是含蓄地富含命令 对象和结果的 @ModelAttribute 带注释的参考数据访问 方法。 绑定请求参数方法参数 @RequestParam 使用 @RequestParam 注释来绑定 请求参数方法参数在你的控制器。 下面的代码片段显示了使用方法: @Controller @RequestMapping("/pets") @SessionAttributes("pet") public class EditPetForm { // ... @RequestMapping(method = RequestMethod.GET) public String setupForm(@RequestParam("petId") int petId, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } // ... 参数使用这个注释默认是必需的,但 你可以指定一个参数是可选的设置 @RequestParam ' s 需要 属性 假 (例如, 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 298/514 @RequestParam(value = " id ", 要求= false) )。 类型转换是自动应用如果目标方法 参数类型不 字符串 。 看到 一个​章节​方法参数和类型Conversiona​​ 。 映射与@RequestBody请求主体 注释 这个 @RequestBody 方法参数 注释表明一个方法参数应该绑定到 价值的HTTP请求体。 例如: @RequestMapping(value = "/something", method = RequestMethod.PUT) public void handle(@RequestBody String body, Writer writer) throws IOException { writer.write(body); } 你把请求体方法参数使用 HttpMessageConverter 。 HttpMessageConverter 负责 从HTTP请求消息转换到一个对象和转换 从一个对象到HTTP响应体。 这个 RequestMappingHandlerAdapter 支持 @RequestBody 注释与以下 默认 HttpMessageConverters : ByteArrayHttpMessageConverter 将字节数组。 StringHttpMessageConverter 转换 字符串。 FormHttpMessageConverter 转换 表单数据到/从MultiValueMap <字符串,字符串>。 SourceHttpMessageConverter 转换 /从一个javax.xml.transform.Source。 在这些转换器的更多信息,请参阅 消息转换器 。 还要注意 ,如果使用MVC名称空间或MVC Java配置,更广泛 范围的消息转换器 默认注册。 看到 启用MVC Java配置或 MVC XML名称空间 为更多的信息。 如果你打算读和写XML,您将需要配置 这个 MarshallingHttpMessageConverter 与 特定 Marshaller 和一个 解组程序 实现 从 org.springframework.oxm 包。 这个例子 下面显示了如何做,直接在你的配置但如果 配置您的应用程序是通过MVC名称 空间或 MVC Java配置见 启用 MVC Java配置或MVC XML名称空间 相反。 <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <util:list id="beanList"> <ref bean="stringHttpMessageConverter"/> <ref bean="marshallingHttpMessageConverter"/> </util:list> </property </bean> <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/> <bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"> <property name="marshaller" ref="castorMarshaller" /> <property name="unmarshaller" ref="castorMarshaller" /> </bean> <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> 一个 @RequestBody 方法参数可以 标注 @Valid ,在这种情况下,这将是 验证使用配置 验证器 实例。 当使用MVC名称空间或 MVC Java配置,一个jsr - 303验证器 配置自动假设一个jsr - 303实现吗 可以在类路径中。 就像 @ModelAttribute 参数, 一个 错误 参数可以用来检查错误。 如果这种观点并不宣称,一个 MethodArgumentNotValidException 将提高。 异常处理 DefaultHandlerExceptionResolver ,发送 一个 400年 错误返回到 客户机。 注意 也看到 启用MVC Java配置或MVC XML名称空间 对于 配置消息信息转换器和一个验证器 通过MVC名称 空间或MVC Java配置。 映射响应的身体 @ResponseBody 注释 这个 @ResponseBody 注释是 类似 @RequestBody 。 这 注释可以戴上一个方法和表明,返回类型 应该直接写到HTTP响应 正文(而不是放置吗 在一个模型,或解释为一个视图名称)。 例如: @RequestMapping(value = "/something", method = RequestMethod.PUT) @ResponseBody public String helloWorld() { 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 299/514 return "Hello World"; } 上面的例子将导致文本 你好 世界 被写入HTTP响应流。 与 @RequestBody ,春天 将返回的对象转换成一个响应体通过使用一个 HttpMessageConverter 。 更多 这些转换器的信息, 请参见前一节和 消息转换器 。 使用 HttpEntity < ? > 这个 HttpEntity 类似于 @RequestBody 和 @ResponseBody 。 除了获得 请求和响应的身体, HttpEntity (和响应具体的子 类 ResponseEntity )还允许访问 请求和响应头,就像这样: @RequestMapping("/something") public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException { String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader")); byte[] requestBody = requestEntity.getBody(); // do something with request header and body HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set("MyResponseHeader", "MyValue"); return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED); } 上面的例子中得到的价值 MyRequestHeader 请求头,并读取的身体 作为一个字节数组。 它添加了 MyResponseHeader 到 响应,写 你好世界 响应 流,并设置响应状态代码201(创建)。 与 @RequestBody 和 @ResponseBody ,弹簧使用 HttpMessageConverter 将从 并请求和响应流。 为更多的信息关于这些 转换器,请参见前一节和 消息转换器 。 使用 @ModelAttribute 在一个 方法 这个 @ModelAttribute 注释 可以用在方法或方法参数。 本节解释 它的使用方法而下一节解释了它的使用 方法参数。 一个 @ModelAttribute 在一个方法 表明该方法的目的是将一个或多个模型 属性。 这种方法支持相同的参数类型 @RequestMapping 方法但是不能 直接映射到请求。 相反 @ModelAttribute 方法在一个控制器 之前被调用 @RequestMapping 方法,在相同的控制器。 一对夫妇的例子: // Add one attribute // The return value of the method is added to the model under the name "account" // You can customize the name via @ModelAttribute("myAccount") @ModelAttribute public Account addAccount(@RequestParam String number) { return accountManager.findAccount(number); } // Add multiple attributes @ModelAttribute public void populateModel(@RequestParam String number, Model model) { model.addAttribute(accountManager.findAccount(number)); // add more ... } @ModelAttribute 使用方法 填充模型与常用属性例如 填充下拉与国家或与宠物类型,或检索 命令对象像帐户为了使用它来表 示数据 在一个HTML表单。 后者是进一步讨论在未来 部分。 请注意这两个风格的 @ModelAttribute 方法。 在第一个, 隐式方法添加一个属性通过返回它。 在 其次,该方法接受一个 模型 并添加任何 数量的模型属性来它。 你可以选择两个 风格。根据您的需要 一个控制器可以有任意数量的 @ModelAttribute 方法。 所有这些 方法之前被调用 @RequestMapping 方法相同的 控制 器。 @ModelAttribute 方法也可以 定义在一个 @ControllerAdvice 注释 类和这样的方法适用于所有的控制器。 这个 @ControllerAdvice 注释是 一个组件注释允许实现类时自动被检测 通过类路径扫描。 提示 会发生什么,如果一个模型属性名称不明确 指定的吗? 在这种情况下一个默认的名字是分配给模型 属性基于 其类型。 例如,如果这个方法返回一个 类型的对象 帐户 ,使用默认的名称 “账户”。 你可以改变,通过价值 13-4-16 Spring Framework Reference Documentation static.springsource.org/spring/docs/3.2.2.RELEASE/spring-framework-reference/htmlsingle/ 300/514 的 @ModelAttribute 注释。 如果添加 属性直接 模型 ,使用 适当的过载 addAttribute(. .) 方法—— 即。 ,有或没有一个属性的名字。 这个 @ModelAttribute 注释 可以用在 @RequestMapping 方法 为好。 在这种情况下的返回值 @RequestMapping 方法 解释 作为一个模型属性而不是视图名称。 视图的名称是 来自视图名称惯例相反很像的方法 返回一个​无效​看到 SectionA 17 12 3,一个​​视图- RequestToViewNameTranslator 一个​​ 。 使用 @ModelAttribute 在一个 方法参数 如上一节所介绍的 @ModelAttribute 可以用在方法 或方法参数。 本节解释了其使用方法 参数。 一个 @ModelAttribute 在一个方法 参数表明参数应该被检索模型。 如果 没有出现在模型中,参数应该被实例化第一 然后添 加到模型中。 一旦出现在模型中,参数的 字段应该填充所有请求参数, 匹配的名称。 这称为数据绑定在Spring MVC,非常 有用 的机制,使您不必解析每个表单字段 单独。 @RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) public String processSubmit(@ModelAttribute Pet pet) { } 鉴于上面的例子,可以宠物实例从何而来? 有几种选择: 它也许已经在模型由于使用 @SessionAttributes 一个​​看到 一个​​章节使用 @SessionAttributes 存储模型 属性在HTTP会 话requestsa​​之间 。 它也许已经在模型由于一个 @ModelAttribute 方法在相同的 控制器一个​​如上一节所介绍的。 它可能被检索基于URI模板变量和 类型转换器(更详细地解释下)。 它可能被实例化使用其默认构造函数。 一个 @ModelAttribute 方法是一个 常见的方法来获取一个属性的数据库,这可能 有选择地存储在请求之间通过使用 @SessionAttributes 。 在某些情况下, 可以方便的检索属性通过使用一个URI模板吗 变量和类型转换器。 这里是一个例子: @RequestMapping(value="/accounts/{account}", method = RequestMethod.PUT) public String save(@ModelAttribute("account") Account account) { } 在这个示例模型属性的名称(即。 “账户”) 的名字相匹配的一个URI模板变量。 如果你注册 转换器<字符串,帐户> 可以把 这 个 字符串 帐户值转换为 帐户 实例,然后上面的示例 工作不需要一个 @ModelAttribute 法。 下一步是数据绑定。 这个 WebDataBinder 类匹配请求参数 名字一个​​包括查询字符串参数和表单字段一个​​模型 属性字段的名 字。 匹配的字段类型后填充 转换(从字符串到目标字段类型)已经应用 在必要时。 数据绑定和验证都包含在 Chapt