Access configuration using spring expression language
An alternative method to the standard role-based voting mechanism implemented by RoleVoter is the use of Spring Expression Language (SpEL) expressions to define arbitrarily complex rules for voting. The straightforward way to implement this feature is to add the use-expressions attribute to the <http> configuration element:
对于实现了标准role-based投票机制的RoleVoter,它可以选择使用Spring Expression Language表达式来表达任意的复杂投票规则。最直接实现这个特性的方法是在<http>中添加use-expressions元素。
<http auto-config="true" use-expressions="true">
This addition will modify the behavior of the access attribute on the URL intercept rule declarations to expect an SpEL expression. SpEL expressions allow for the use of expression language specifications of access criteria. Instead of simple strings such as ROLE_USER, the configuration file can specify expressions that invoke method calls, reference system properties, compute values, and much more.
An important point to note is that if you enable the SpEL expression-based access specifications by setting the use-expressions attribute, you will disable the automatic configuration of the RoleVoter, which understands declarations of roles, like we saw in our simple configuration:
重要的一点是如果你使用了SpEL表达式,那么自动的RoleVoter配置就会被废弃,比如了解roles的声明等,就下下面这样:(就是如果这么声明了,那么程序就看不懂下面的代码了)
<intercept-url pattern="/*" access="ROLE_USER"/>
This means that your access declarations must change if you want to filter access solely by role. Fortunately, this was anticipated, and an SpEL-bound method hasRole is available to check roles. If we rewrote our sample configuration file to use expressions, it would look like this:
它的意思就是如果你想继续使用就必须修改声明。幸运的是,和期望的一样SpEL-bound方法hasRole是可以使用的。如果我们重写上面的例子,那么我们可以像下面的声明一样:
<http auto-config="true" use-expressions="true"> <intercept-url pattern="/*" access="hasRole('ROLE_USER')"/> </http>
As you might expect, the SpEL handling is supplied by a different Voter implementation, o.s.s.web.access.expression.WebExpressionVoter, which understands how to evaluate the SpEL expressions. The WebExpressionVoter relies on an implementation of the o.s.s.web.access.expression.WebSecurityExpressionHandler interface for this purpose. The WebSecurityExpressionHandler is responsible both for evaluating the expressions, as well as supplying the security-specific methods that are referenced in the expressions. The default implementation of this interface exposes methods defined in the o.s.s.web.access.expression.WebSecurityExpressionRoot class.
正像你猜测的一样,SpEl处理的支持是由另一个Voter实现的——o.s.s.web.access.expression.WebExpressionVoter,它知道如何解析SpEl表达式。 WebExpressionVoter依赖于实现WebSecurityExpressionHandler接口。WebSecurityExpressionHandler负责解析表达式和提供表达式引用的security-specific方法。这个接口的默认的实现是由WebSecurityExpressionRoot提供的。
The flow and relationship between these classes is shown in the following diagram:
Methods and pseudo-properties for SpEL access expressions are declared by the public methods provided by the WebSecurityExpessionRoot class, and its superclasses.
The available SpEL methods and pseudo-properties which ship with Spring Security 3 are shown in the following tables. Note that methods and properties not marked as "web only" are available for use when securing other types of resources that utilize SpEL, such as method calls. The examples provided illustrate the use of the method or property in an <intercept-url> access declaration.
In addition to the methods in the previous table, a series of methods are provided that can act as properties in the SpEL expressions. These do not require parentheses or method arguments.
Remember that voter implementations must return a voting decision (grant, deny, or abstain) based on the context of the request. You may note that hasRole sounds like it returns a Boolean response, and in fact this is true. SpEL-based access declarations must consist only of expressions which return a Boolean result. A true result means that the voter grants access, and a false result means that the voter denies access.
If you try to return an expression that doesn't evaluate to a Boolean, you'll
get an unfriendly exception with a message like this:
org.springframework.expression.spel.SpelException: EL1001E:Type conversion problem, cannot convert from class java.lang.Integer to java.lang.Boolean
转载:http://www.iteye.com/topic/283668
最近taowen同学连续发起了两起关于贫血模型和领域模型的讨论,引起了大家的广泛热烈的讨论,但是讨论(或者说是争论)的结果到底怎样,我想值
得商榷。问题是大家对贫血模型和领域模型都有自己的看法,如果没有对此达到概念上的共识,那么讨论的结果应该可想而知,讨论的收获也是有的,至少知道了分
歧的存在。为了使问题具有确定性,我想从一个简单例子着手,用我对贫血模型和领域模型的概念来分别实现例子。至于我的理解对与否,大家可以做评判,至少有
个可以评判的标准在这。
一个例子
我要举的是一个银行转帐的例子,又是一个被用滥了的例子。但即使这个例子也不是自己想出来的,而是剽窃的<<POJOs in Action>>中的例子,原谅我可怜的想像力
。当钱从一个帐户转到另一个帐户时,转帐的金额不能超过第一个帐户的存款余额,余额总数不能变,钱只是从一个账户流向另一个帐户,因此它们必须在一个事务内完成,每次事务成功完成都要记录此次转帐事务,这是所有的规则。
贫血模型
我们首先用贫血模型来实现。所谓贫血模型就是模型对象之间存在完整的关联(可能存在多余的关联),但是对象除了get和set方外外几乎就没有其它的方
法,整个对象充当的就是一个数据容器,用C语言的话来说就是一个结构体,所有的业务方法都在一个无状态的Service类中实现,Service类仅仅包
含一些行为。这是Java
Web程序采用的最常用开发模型,你可能采用的就是这种方法,虽然可能不知道它有个“贫血模型”的称号,这要多
亏Martin Flower(这个家伙惯会发明术语!)。
包结构
在讨论具体的实现之前,我们先来看来贫血模型的包结构,以便对此有个大概的了解。
贫血模型的实现一般包括如下包:
- dao:负责持久化逻辑
- model:包含数据对象,是service操纵的对象
- service:放置所有的服务类,其中包含了所有的业务逻辑
- facade:提供对UI层访问的入口
代码实现
先看model包的两个类,Account和TransferTransaction对象,分别代表帐户和一次转账事务。由于它们不包含业务逻辑,就是一个普通的Java Bean,下面的代码省略了get和set方法。
这两个类没什么可说的,它们就是一些数据容器。接下来看service包中TransferService接口和它的实现
TransferServiceImpl。TransferService定义了转账服务的接口,TransferServiceImpl则提供了转账服
务的实现。
TransferServiceImpl类使用了AccountDAO和TranferTransactionDAO,它的transfer方法负责整个
转帐操作,它首先判断转帐的金额必须大于0,然后判断fromAccountId和toAccountId是一个存在的Account的
accountId,如果不存在抛AccountNotExsitedException。接着判断转帐的金额是否大于fromAccount的余额,如
果是则抛AccountUnderflowException。接着分别调用fromAccount和toAccount的setBalance来更新它
们的余额。最后保存到数据库并记录交易。TransferServiceImpl负责所有的业务逻辑,验证是否超额提取并更新帐户余额。一切并不复杂,对
于这个例子来说,贫血模型工作得非常好!这是因为这个例子相当简单,业务逻辑也不复杂,一旦业务逻辑变得复杂,TransferServiceImpl就
会膨胀。
优缺点
贫血模型的优点是很明显的:
其缺点为也是很明显的:
领域模型
接下来看看领域驱动模型,与贫血模型相反,领域模型要承担关键业务逻辑,业务逻辑在多个领域对象之间分配,而Service只是完成一些不适合放在模型中的业务逻辑,它是非常薄的一层,它指挥多个模型对象来完成业务功能。
包结构
领域模型的实现一般包含如下包:
- infrastructure: 代表基础设施层,一般负责对象的持久化。
- domain:代表领域层。domain包中包括两个子包,分别是model和service。model中包含模型对 象,Repository(DAO)接口。它负责关键业务逻辑。service包为一系列的领域服务,之所以需要service,按照DDD的观点,是因为领域中的某些概念本质是一些行为,并且不便放入某个模型对象中。比如转帐操作,它是一个行为,并且它涉及三个对 象,fromAccount,toAccount和TransferTransaction,将它放入任一个对象中都不好。
- application: 代表应用层,它的主要提供对UI层的统一访问接口,并作为事务界限。
代码实现
现在来看实现,照例先看model中的对象:
与贫血模型的区别在于Account类中包含业务方法(credit,debit),注意没有set方法,对Account的更新是通过业务方法来更新
的。由于“不允许从帐户取出大于存款余额的资金”是一条重要规则,将它放在一个单独的接口OverdraftPolicy中,也提供了灵活性,当业务规则
变化时,只需要改变这个实现就可以了。
TransferServiceImpl类:
与贫血模型中的TransferServiceImpl相比,最主要的改变在于业务逻辑被移走了,由Account类来实现。对于这样一个简单的例子,领域模型没有太多优势,但是仍然可以看到代码的实现要简单一些。当业务变得复杂之后,领域模型的优势就体现出来了。
优缺点
其优点是:
其缺点是:
我的看法
这部分我将提出一些可能存在争议的问题并提出自己的看法。
软件分层
理解软件分层、明晰每层的职责对于理解领域模型以及代码实现是有好处的。软件一般分为四层,分别为表示层,应用层,领域层和基础设施层。软件领域中另外一个著名的分层是TCP/IP分层,分为应用层,运输层,网际层和网络接口层。我发现它们之间存在对应关系,见下表:
TCP/IP分层 软件分层 表示层 负责向用户显示信息。 应用层 负责处理特定的应用程序细节。如FTP,SMTP等协议。 应用层 定义软件可以完成的工作,指挥领域层的对象来解决问题。它不负责业务逻辑,是很薄的一层。 运输层 两台主机上的应用程序提供端到端的通信。主要包括TCP,UDP协议。 领域层 负责业务逻辑,是业务软件的核心。 网际层 处理分组在网络中的活动,例如分组的选路。主要包括IP协议。 网络接口层 操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆(或其他任何传输媒介)的物理接口细节。 基础设施层 为上层提供通用技术能力,如消息发送,数据持久化等。
对于TCP/IP来说,运输层和网际层是最核心的,这也是TCP/IP名字的由来,就像领域层也是软件最核心的一层。可以看出领域模型的包结构与软 件分层是一致的。在软件分层中,表示层、领域层和基础设施层都容易理解,难理解的是应用层,很容易和领域层中Service混淆。领域Service属于 领域层,它需要承担部分业务概念,并且这个业务概念不易放入模型对象中。应用层服务不承担任何业务逻辑和业务概念,它只是调用领域层中的对象(服务和模 型)来完成自己的功能。应用层为表示层提供接口,当UI接口改变一般也会导致应用层接口改变,也可能当UI接口很相似时应用层接口不用改变,但是领域层 (包括领域服务)不能变动。例如一个应用同时提供Web接口和Web Service接口时,两者的应用层接口一般不同,这是因为Web Service的接口一般要粗一些。可以和TCP/IP的层模型进行类比,开发一个FTP程序和MSN聊天程序,它们的应用层不同,但是可以同样利用 TCP/IP协议,TCP/IP协议不用变。与软件分层不同的是,当同样开发一个FTP程序时,如果只是UI接口不同,一个是命令行程序,一个是图形界 面,应用层不用变(利用的都是FTP服务)。下图给出领域模型中的分层:
Repository接口属于领域层
可能有人会将Repository接口,相当于贫血模型中的DAO接口,归于基础设施层,毕竟在贫血模型中DAO是和它的实现放在一起。这就涉及 Repository 接口到底和谁比较密切?应该和domain层比较密切,因为Repository接口是由domain层来定义的。用TCP/IP来类比,网际层支持标准 以太网、令牌环等网络接口,支持接口是在网际层中定义的,没有在网际层定义的网络接口是不能被网际层访问的。那么为什么在贫血模型中DAO的接口没有放在 model包中,这是因为贫血模型中DAO的接口是由service来定义的,但是为什么DAO接口也没有放在service包中,我无法解释,按照我的 观点DAO接口放在service包中要更好一些,将DAO接口放在dao包或许有名称上对应的考虑。对于领域模型,将Repository接口放入 infrastructure包中会引入包的循环依赖,Repository依赖Domain,Domain依赖Repository。然而对于贫血模 型,将DAO接口放入dao包中则不会引入包循环依赖,只有service对DAO和model的依赖,而没有反方向的依赖,这也导致service包很 不稳定,service又正是放置业务逻辑的地方。JDepend这个工具可以检测包的依赖关系。
贫血模型中Facade有何用?
我以前的做一个项目使用的就是贫血模型,使用了service和facade,当我们讨论service和facade有什么区别时,很少有人清 楚,最终结果facade就是一个空壳,它除了将方法实现委托给相应的service方法,不做任何事,它们的接口中的方法都一样。Facade应该是主 要充当远程访问的门面,这在EJB时代相当普遍,自从Rod Johson叫嚷without EJB之后,大家对EJB的热情降了很多,对许多使用贫血模型的应用程序来说,facade是没有必要的。贫血模型中的service在本质上属于应用层 的东西。当然如果确实需要提供远程访问,那么远程Facade(或许叫做Remote Service更好)也是很有用的,但是它仍然属于应用层,只不过在技术层面上将它的实现委托给对应的Service。下图是贫血模型的分层:
从上面的分层可以看出贫血模型实际上相当于取消掉了领域层,因为领域层并没有包含业务逻辑。
DAO到底有没有必要?
贫血模型中的DAO或领域模型中的Repository到底有没有必要?有人认为DAO或者说Repository是充血模型的大敌,对此我无论如 何也不赞同。DAO或Repository是负责持久化逻辑的,如果取消掉DAO或Repository,将持久化逻辑直接写入到model对象中,势必 造成model对象承担不必要的职责。虽然现在的ORM框架已经做得很好了,持久化逻辑还是需要大量的代码,持久化逻辑的掺入会使model中的业务逻辑 变得模糊。允许去掉DAO的一个必要条件就是Java的的持久化框架必须足够先进,持久化逻辑的引入不会干扰业务逻辑,我认为这在很长一段时间内将无法做 到。在rails中能够将DAO去掉的原因就是rail中实现持久化逻辑的代码很简洁直观,这也与ruby的表达能力强有关系。DAO的另外一个好处隔离 数据库,这可以支持多个数据库,甚至可以支持文件存储。基于DAO的这些优点,我认为,即使将来Java的持久化框架做得足够优秀,使用DAO将持久化逻 辑从业务逻辑中分离开来还是十分必要的,况且它们本身就应该分离。
结束语
在这篇文章里,我使用了一个转帐例子来描述领域模型和贫血模型的不同,实现代码可以从附件中下载,我推荐你看下附件代码,这会对领域模型和贫血模型 有个更清楚的认识。我谈到了软件的分层,以及贫血模型和领域模型的实现又是怎样对应到这些层上去的,最后是对DAO(或Repository)的讨论。以 上只是我个人观点,如有不同意见欢迎指出。
ListView:单击和长按弹出上下文菜单(ContextMenu)
http://pop1030123.iteye.com/blog/1125664
notifyDataSetChanged() 动态更新ListView 通过 Handler AsyncTask两种方式
http://www.pocketdigi.com/20100827/75.html#more-75
adapter.notifyDatasetChanged
http://blog.csdn.net/pipisky2006/article/details/6121036
android 中文API
http://android.toolib.net/reference/packages.html