论道WP(三):应用程序栏
作者通过具体翔实的例子介绍了Winodws Phone 7中应用程序栏的使用方式。
该内容已经被标记书签!
标记书签错误,请重试!

作者 Manjunath R Naganna 译者 张龙 发布于 2009年7月14日
使用了O/R Mapping工具的典型J2EE应用都会面临这样一个问题:如何通过最精简的SQL查询获取所需的数据。很多时候这可不是轻而易举的事情。默认情况下,O/R Mapping工具会按需加载数据,除非你改变了其默认设置。延迟加载行为保证了依赖的数据只有在真正请求时才会被加载进来,这样就可以避免创建无谓的对象。有时我们的业务并不会使用到依赖的那些组件,这时延迟加载就派上用场了,同时也无需加载那些用不上的组件了。
典型情况下,我们的业务很清楚需要哪些数据。但由于使用了延迟加载,在执行大量Select查询时数据库的性能会降低,因为业务所需的数据并不是一下子获得的。这样,对于那些需要支持大量请求的应用来说可能会产生瓶颈(可伸缩性问题)。
来看个例子吧,假设某个业务流程想要得到一个Person及其Address信息。我们将Address组件配置成延迟加载,这样要想得到所需的数据就需要更多的SQL查询,也就是说首先查询Person,然后再查询Address。这增加了数据库与应用之间的通信成本。解决办法就是在一个单独的查询中将 Person和Address都得到,因为我们知道这两个组件都是业务流程所需的。
如果在DAO/Repository及底层Service开发特定于业务的Fetching-API,对于那些拥有不同数据集的相同领域对象来说,我们就得编写不同的API进行抓取并组装了。这么做会使Repository及底层Service过于膨胀,最终变成维护的梦魇。
延迟抓取的另一个问题就是在获取到请求的数据前要一直打开数据库连接,否则应用就会抛出一个延迟加载异常。
说明:如果在查询中使用预先抓取来获取二级缓存中的数据时,我们将无法解决上面提出的问题。对于Hibernate来说,如果我们使用预先抓取来获取二级缓存中的数据,那么它将从数据库而不是缓存中去获取数据,哪怕是二级缓存中已经存在该数据。这就说明Hibernate也没有解决这个问题,从而表明我们不应该在查询中通过预先抓取来获得二级缓存中的对象。
对于那些可以让我们调节查询以获取缓存对象的O/R Mapping工具来说,如果缓存中有对象就会从缓存中获取,否则采取预先抓取的方式。这就解决了上面提到的事务/DB连接问题,因为在查询的执行过程中会同时获取缓存中的数据而不是按需读取(也就是延迟加载)。
通过下面的示例代码来了解一下延迟加载所面对的问题及解决办法。考虑如下场景:某领域中有3个实体,分别是Employee、Department及Dependent。
这三个实体之间的关系如下:

我们要执行三个操作:
以上三个操作需要获取并呈现不同的数据。使用延迟加载有如下弊端:
但另一方面,使用预先抓取也存在如下弊端:
在Repository/DAO或底层服务中使用特定于操作的API可以解决上述问题,但却会导致如下问题:
为了解决上面这些问题,Repository/DAO层需要根据不同的业务情况执行不同的查询来获取实体。就像Aspect类所定义的那样,我们可以根据特定的操作使用不同的抓取机制来覆盖Repository/DAO类所定义的默认抓取模式。所有的抓取模式类都实现了相同的接口。


Repository类使用了上述的抓取模式来执行查询,如下代码所示:
public Employee findEmployeeById(int employeeId) {
List employee = hibernateTemplate.find(fetchingStrategy.queryEmployeeById(),
new Integer(employeeId));
if(employee.size() == 0)
return null;
return (Employee)employee.get(0);
}
Repository类中的employee的抓取策略需要根据实际情况进行调整。我们决定将Repository层的抓取策略调整到 Repository和Service层外,放在一个Aspect类中,这样当需要增加新的业务逻辑时只需修改Aspect类并增加一个针对于 Repository的抓取策略实现即可。这里我们使用了面向方面的编程(Aspect Oriented Programming)以根据业务的不同使用不同的抓取策略。
什么是面向方面的编程?
面向方面的编程(AOP)可以通过模块化的形式实现实际应用中的横切关注点,如日志、追踪、动态分析、错误处理、服务水平协议、策略增强、池化、缓存、并发控制、安全、事务管理以及业务规则等等。对这些关注点的传统实现方式需要我们将这些实现融合到模块的核心关注点中。凭借AOP,我们可以在一个叫做方面(aspect)的独立模块中实现这些关注点。模块化的结果就是设计简化、易于理解、质量提升、开发时间降低以及对系统需求变更的快速响应。
接下来读者朋友们可以参考 Ramnivas Laddad所著的《AspectJ in Action》一书以详细了解AspectJ的概念以及编程方式,还可以了解一下AspectJ的开发工具。
Aspect在抓取策略实现上扮演着重要角色。抓取策略是个业务层的横切关注点,它会随着业务的变化而变化。Aspect对于特定的业务逻辑下使用何种抓取策略起到了至关重要的作用。这里我们将对抓取策略的管理放在了底层服务和Respository层之外。任何新的业务都可能需要不同的抓取策略,这样我们就无需修改底层服务或是Respository层的API就能应用新的抓取策略了。
FetchingStrategyAspect.aj
/**
Identify the getEmployeeWithDepartmentDetails flow where you need to change the fetching
strategy at repository level
*/
pointcut empWithDepartmentDetail(): call(* EmployeeRepository.findEmployeeById(int))
&& cflow(execution(* EmployeeDetailsService.getEmployeeWithDepartmentDetails(int)));
/**
When you are at the specified poincut before continuing further update the fetchingStrategy in
EmployeeRepositoryImpl to EmployeeWithDepartmentFetchingStrategy
*/
before(EmployeeRepositoryImpl r): empWithDepartmentDetail() && target(r) {
r.fetchingStrategy = new EmployeeWithDepartmentFetchingStrategy();
}
/**
Identify the getEmployeeWithDependentDetails flow where you need to change the fetching
staratergy at repository level
*/
pointcut empWithDependentDetail(): call(* EmployeeRepository.findEmployeeById(int))
&& cflow(execution(* EmployeeDetailsService.getEmployeeWithDependentDetails(int)));
/**
When you are at the specified poincut before continuing further update the fetchingStrategy in
EmployeeRepositoryImpl to EmployeeWithDependentFetchingStrategy
*/
before(EmployeeRepositoryImpl r): empWithDependentDetail() && target(r) {
r.fetchingStrategy = new EmployeeWithDependentFetchingStrategy();
}
这样,Repository到底要执行何种查询就不是由Service和Repository层所决定了,而是由外面的Aspect决定,纵使增加了新的业务也无需修改底层服务和Repository层。决定执行何种查询的逻辑就成为一个横切关注点了,它被放在Aspect中。Aspect会根据业务规则的不同在Service层调用Repository层的API之前将抓取策略注入到Repository中。这样我们就可以使用相同的Service和 Repository层API来满足各种不同的业务规则了。
来看个具体示例吧,该示例会同时抓取一个employee的Department和Dependent的详细信息。我们需要对业务层进行一些变更,增加一个方法:getEmployeeWithDepartmentAndDependentsDetails(int employeeId)。实现新的抓取策略类EmployeeWithDepartmentAndDependentFetchingStaratergy,后者又实现了EmployeeFetchingStrategy并重写了queryEmployeeById方法,该方法会返回优化后的查询,可以在一个SQL语句中获取所需数据。
由Aspect将上述的抓取策略注入到相关的业务中,如下所示:
pointcut empWithDependentAndDepartmentDetail(): call(* EmployeeRepository.findEmployeeById(int))
&& cflow(execution(* EmployeeDetailsService.getEmployeeWithDepartmentAndDependentsDetails(int)));
before(EmployeeRepositoryImpl r): empWithDependentAndDepartmentDetail() && target(r) {
r.fetchingStrategy = new EmployeeWithDepartmentAndDependentFetchingStaratergy();
}
如你所见,我们并没有修改底层业务与Repository层而是使用Aspect和一个新的FetchingStrategy实现就完成了上述新增的业务。
现在我们来谈谈关于二级缓存的查询优化问题。在上面的示例代码中,我们对department实体进行一些修改并配置在二级缓存中。如果对 department实体采取预先抓取,那么对于同样的department实例,纵使它位于二级缓存中,每次也都需要查询数据库。如果不在查询中获取 department实体,那么业务层就需要参与到事务当中,因为我们并没有将department实体缓存起来而是通过延迟加载的方式得到它。
这样,事务声明就从底层移到了业务层,虽然我们知道该业务需要哪些数据,但O/R Mapping工具却没有提供相应的机制来解决上面遇到的问题,即预先抓取缓存中的数据。
对于那些没有缓存的数据来说这种方式没什么问题,但对于缓存数据来说,这就依赖于O/R Mapping工具了,因为只有它才能解决缓存数据问题。
该示例附带的源代码详细解释了抓取策略。该zip文件含有一个工程示例,阐述了上面谈到的所有场景。你可以使用任何IDE或是使用aspectj编译器从命令行执行代码。在执行前请确保jdbc.properties文件与你机器上的信息一致并创建示例应用所需的表。
你可以使用Eclipse IDE以及AJDT插件运行代码,请按照下面的步骤进行:
本文介绍了如何通过不同的抓取策略从后端系统中获取数据,这是以模块化的方式根据业务需求实现的,同时又不会导致底层服务或Repository层过度膨胀。
关于作者:Manjunath R Naganna目前供职于阿尔卡特朗讯公司,担任高级软件工程师一职,尤其专注基于Java/J2EE的企业应用设计与实现。他感兴趣的领域包括 Spring Framework、领域驱动设计、事件驱动架构以及面向方面的编程。对于Hemraj Rao Surlu对本文所做的编辑与排版工作,Manjunath表达了自己的谢意,同时他也很感谢自己的领导Ramana与Saurabh Sharma在百忙之中抽出时间对本文进行审阅并提出很多重要的反馈信息。
查看英文原文:Fetching strategy implementation in a J2EE application using AOP。
感谢宋玮对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。
这不就是策略模式么,为什么要用AOP,感觉帮助不大,因为究竟怎么取数据是和业务相关的,业务变了,代码就要修改,即使用AOP也要修改AOP相关的aspectj文件,重新编译代码啊。
业务分散到了aspectj文件中了,维护挺麻烦的
感觉是对业务逻辑的一个判断,是否能够使用立即抓取或者懒加载从而对性能的提升,正如楼上的兄弟说的策略模式,其实细究的话这种变化情况更似桥梁模式,因为他主要在于两端变化(业务-抓取),用AOP是将业务的变化那边给予的处理,如果不用AOP也无非增加一个接口,一个实现类,一个管理类外加一个XML反射;当然用AOP可以加少一些
看那个aspect的文件里头:
EmployeeDetailsService.getEmployeeWithDepartmentAndDependentsDetails(int)
service层还是增加了方法,意义不大。
“我们并没有修改底层业务与Repository层而是使用Aspect和一个新的FetchingStrategy实现就完成了上述新增的业务。”
作者可能觉得,这样做不用去修改代码,修改aspectj的文件,不算修改代码。:)。
不过文章的主题是抓取策略,增加程序的可扩展性只是策略模式的一个特性而已。
感觉设计的关键是在FetchingStaratergy的实现上,用AOP去管理FetchingStaratergy似乎不如直接用IOC来得方便。
在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了Java SE1.6中对于锁的性能优化,以及锁的存储结构及升级过程。
本次分享将首先介绍现代富文本编辑器的组成和实现,然后结合UEditor的开发过程,与参会者分享UEditor在设计和实现的过程中,所涉及到的核心功能的细节实现。
本次演讲视频录制于百度技术沙龙。
我们所开发的应用程序大多都需要提供一个图形用户界面(GUI)。关于GUI应用的架构设计,已经有了Form & Control、MVC,、MVP、 Passive View等多种模式。模式可以帮助我们建立优雅的架构,但前提是弄清楚模式的应用场景。弄清楚GUI应用面临的设计上的问题,有助于我们正确的挑选设计方案。
MongoDB是一种非常易用的NoSQL方案,Brian C. Dilley在这篇文章里介绍了MongoDB的优劣势,并介绍了MJORM项目。MJORM用于MongoDB,是一个没有注解的Java ORM库。
随着网络基础设施的逐步成熟,从RPC进化到Web Service,并在业界开始普遍推行SOA,再到后来的RESTful平台以及云计算中的PaaS与SaaS概念的推广,分布式架构在企业应用中开始呈现出不同的风貌,然而殊途同归,这些分布式架构的目标仍然是希望回到建造巴别塔的时代,系统之间的交流不再为不同语言与平台的隔阂而产生障碍。
精益软件开发方法因其对市场和交付的重视和在各种场景下体现出的适应能力正在获得广泛的关注。特别是在精益创业(Lean Startup)渐渐兴起和技术日新月异的今天,其"极端"的思想也变得越来越必要和可行。 InfoQ就此主题对他做了深入的采访。
6 条回复
关注此讨论 回复