找回密码
 FreeOZ用户注册
查看: 4645|回复: 11
打印 上一主题 下一主题

一个典型的Spring AOP例子

[复制链接]
跳转到指定楼层
1#
发表于 28-1-2009 09:24:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
在做数据库应用开发的时候,我们常常要处理打开连接和关闭连接的问题,传统的来说都是显式的来实现的,就是说在进行一个查询或者操作(CRUD)之前需要open connection,做完之后Finally close connection。

现在,借助于AspectJ,我们可以让framework做掉这件事。

首先,确保aspectjrt.jar在classpath里面,然后加入<aop:aspectj-autoproxy/>进Spring的application context xml。
然后,写一个Generic DAO(基于hibernate)

public abstract class GenericDAO {
   protected Session session;
   
   public void openSession()
   {
    session=HibernateUtil.getSessionFactory().openSession();
   }
   
   public void closeSession()
   {
   if (session!=null) session.close();
   }
}

所有的其他的DAO都extend这个DAO

最后,写一个Advice,声明一个pointcut,指向DAO包里面所有public以query开头的method(可以类推到其他的public method,以insert或者update或者任何容易分辨的标识开头)。

@Aspect
public class GenericDAOAdvice {

@Pointcut("execution(public * org.yourproject.dao.*.query*(..))")
public void dataAccessOperation() {}

@Before("dataAccessOperation()")
public void openSession(org.aspectj.lang.JoinPoint jp) {
  
  GenericDAO dao=(GenericDAO)jp.getTarget();
  
  dao.openSession();
  
}

@After("dataAccessOperation()")
public void closeSession(org.aspectj.lang.JoinPoint jp) {
  
  GenericDAO dao=(GenericDAO)jp.getTarget();
  
  dao.closeSession();
  
}
}

这样,在这些DAO public method执行之前,连接自动打开,执行之后,连接自动关闭。

如果Hibernate和Spring已经集成了(HibernateDaoSupport),那么Spring已经处理了open和close session,也许也是利用了一样的机制。在其他经常需要处理一些例如TRANSACTION,IO,POOLING的场合,类似上面这个例子的实现还是会很好用的。Spring就是这样一个优秀的轻量级框架,它既给了你很多有用的基础工具,又保持了很好的开放性和易集成性。

评分

参与人数 2威望 +60 收起 理由
ubuntuhk + 30 谢谢分享!
coredump + 30 谢谢分享!

查看全部评分

回复  

使用道具 举报

2#
发表于 28-1-2009 22:22:49 | 只看该作者

谢谢楼主分享!
回复  

使用道具 举报

3#
发表于 29-1-2009 10:50:33 | 只看该作者
可以试试 Spring的宣告式事务管理:
...
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Repository
public abstract class JpaCoreServiceImpl<T> implements CoreService<T> {
    @PersistenceContext
    protected EntityManager em;      
   
    @Transactional(readOnly = false)
    public void remove(T obj) {
        if (obj == null)
            return;
        em.remove(obj);
    }
   
    @Transactional(readOnly = true)
    public T getSingleResult(Query query) {
        if (query == null)
            return null;
        try {
            return (T) query.getSingleResult();
        } catch (NoResultException ex) {
            return null;
        }
    }
}

结合JPA,Hibernate(也可以是其它JPA实现框架),使用Spring的宣告式事务管理,可以实现一行@Transactional定义所有事务方面的细节。结合@Repository,可以看到EJB3.0中的@PersistenceContext在非EJB容器中,也由Spring支持了。
回复  

使用道具 举报

4#
发表于 29-1-2009 11:06:17 | 只看该作者
提示: 作者被禁止或删除, 无法发言
About that transaction issue, the only thing you need to do is adding some configurations in xml to use AspectJ to support JPA. Then it's done.
Do not need to create own Advice class or add that ugly annotation.
回复  

使用道具 举报

5#
 楼主| 发表于 29-1-2009 16:13:59 | 只看该作者
Advice在需要处理programmable transaction的时候还是有用的,比如EJB里的BMT。annotation是XML config的另一种形式,但是他们常常都是必要的,对每个method需要注明不同的transaction attribute比如required, require new, support等等,这个步骤是节省不掉的。
回复  

使用道具 举报

6#
发表于 29-1-2009 18:39:37 | 只看该作者
Java中AOP的实现是完全runtime的吗? 不知道对性能的影响有多大?
回复  

使用道具 举报

7#
发表于 29-1-2009 20:03:44 | 只看该作者
提示: 作者被禁止或删除, 无法发言
“对每个method需要注明不同的transaction attribute比如required, require new, support等等” ,其实我是从来没遇到过这样的情况,如果真的有这种需要,当用不同前缀的名字,然后据此在xml里配置advice。如果在BO这一层放那么多关于transaction的annotation其实非常难看繁琐,不见得便于管理,我的annotation只用在entity里和controller里。当然只是个人看法,也可能各人情况不同。

反射对性能的影响 现在大家都不怎么考虑那个了,只要写的爽就行,可能jdk也提高了点。现在动态语言不是还有点小流行么
回复  

使用道具 举报

8#
发表于 29-1-2009 20:49:15 | 只看该作者
AspectJ对性能的影响还是非常显著的,根据俺实际使用时的感受来看,这个影响体现在编译和运行两个方面。特别是你的机器性能不强,又在用Eclipse即时编译的时候,那个速度简直就没法忍受了。

而如果用Spring AOP的AspectJ的轻量级Annotation支持的话,就几乎感受不到性能上的问题。至少我用不到AspectJ的那么细的切面定义,所以也就用不到AspectJ的特殊编译包,Spring AOP足够日常应用了。

不过俺一般用Spring AOP做审计或是日志功能,以及少量业务逻辑。对于数据库逻辑层本身来讲,一般还是用Hibernate + Spring的宣告式事务管理。首先是用Spring管理数据源:

<bean id="dataSource"
                class="org.springframework.jndi.JndiObjectFactoryBean"
                p:jndiName="${datasource.jndi.name}">
</bean>

然后是使用Spring的附合EJB3标准的轻量级EntityManager,并指定Hibernate为JPA Vendor:
        <bean id="entityManagerFactory"                class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
                p:dataSource-ref="dataSource">
                <property name="jpaVendorAdapter">
                        <bean
                                class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
                                p:generateDdl="true" p:showSql="false" />
                </property>
                <property name="jpaProperties">
                        <props>
                                <prop key="hibernate.dialect">
                                        ${hibernate.dialect}
                                </prop>
                        </props>
                </property>
        </bean>

然后是使用Spring超级好用的事务管理及Annotation支持:

        <bean id="transactionManager"
                class="org.springframework.orm.jpa.JpaTransactionManager"
                p:entityManagerFactory-ref="entityManagerFactory" />

        <tx:annotation-driven transaction-manager="transactionManager" />

这样就可以在项目中使用EJB3全部的JPA标记,并可以使用Spring自身提供的宣告式事务管理了。

这里值得一说的是Spring的宣告式事务管理,一个@Transactional可以定义整个事务的方方面面:锁定机制,rollback机制,异常处理策略,共享机制。

现在可能很多人会觉得Annotation这东西不好用,没有XML那么明了好看,在代码中觉得乱。这里想说的是,有的工业设计是这样的:初学者觉得很好用很容易上手,不用深入的学习,长期的使用,就可以很快熟练使用。类似的设计比如Windows操作系统,但有的时候,这种设计的结果是,你使用1000个小时,和使用1个小时,用法没什么太大区别,你刚学的时候怎么用,用1000个小时以后,还是那么用,你的经验提升了,但在工具方面,你的经验没有给你带来任何的工作效率上的提升。这种设计比较典型的就是Windows操作系统,而反例则是Linux或是MacOS操作系统。

拿Windows来举例,对于初学者和新手来说,打开一个程序,操作是差不多的:点击一个程序图标,这个图标可能在开始菜单里,也可能在混乱的桌面上,或者快速启动栏中,总之对于新手老手,操作差不多。而在MacOS中,对于新手来说,打开程序,操作功能,mouse是不离手的。而对于老手,早就习惯了使用QuickSilver,打开程序变成:“点击苹果键-》输入要打开程序的前两个字母-》回车”,根本不用费眼睛去找,手也不用跑mouse去,脑子也没处理找图标的过程,基本就是条件反射。

再举一个例子:在Windows下找一个窗口,无论新手还是老手,都要在下面一个变态的细栏中挑。在MacOS下,对于新手,是在Dock中找,和Windows类似。对于老手,设好Expose,找程序要作的就是F6,搞定。

这是GUI的工业设计,从脚本编程能力来讲,Windows的BAT和MACOS或Linux的BASH,SED,AWK更没有可比性了(装了cygwin的除外)

我想无论是Spring,Spring MVC,新的EJB3的JPA,还是JBOSS的Seam,现在都在慢慢地从XML转向Annotation,这不是人家一拍脑袋想出来的,而是专家经验,他们的设计,是为有经验人士设计,而非新手。所以在实际项目中不用上几百个小时,恐怕很难体会到其中的妙处。

评分

参与人数 3威望 +70 收起 理由
Having_Fun + 20 好例子对比
hoopoos + 20 我很赞同!
ubuntuhk + 30 谢谢分享!

查看全部评分

回复  

使用道具 举报

9#
发表于 29-1-2009 21:07:37 | 只看该作者
没考虑那么多

Spring 2.5 后就转向 annotation 了

不过还是会遇到 annotation 不灵,必须用 xml 的情况

xml 是地狱,annotation 即将是地狱
回复  

使用道具 举报

10#
 楼主| 发表于 30-1-2009 08:40:47 | 只看该作者
annotation 和 xml都是不错的解决方式,不过,写在xml里面有个很大的好处是配置更容易管理,因为xml本身就是可读写可操作的。有的时候我们既希望最大限度的发挥framework的作用,又不希望过于紧密的依赖于某个framework,所以我总是倾向于松散的集成各个layer。至于反射的性能方面是会下降一些,不过productivity提高了,AOP不滥用就行了。
回复  

使用道具 举报

11#
发表于 30-1-2009 13:18:33 | 只看该作者
提示: 作者被禁止或删除, 无法发言
My opion is ,  when you repeating the same logic again and again,  probably you need to refactor your code.

In our case, if you have to give every BO method a similar annotation just for transaction, probably you should use simple common  xml configuration insteadly. And that seperates the database dependency out of your BO.

And the reason to use annotation in entity beans is you may need to do some special settings for some fields. And it makes sense because the entity bean is database realated in real life. Same as in controller class,  every annotation you write should be unique and maintance some unique information

The right choice is always basing on the facts.

[ 本帖最后由 black_zerg 于 8-2-2009 20:45 编辑 ]
回复  

使用道具 举报

12#
发表于 30-1-2009 19:45:23 | 只看该作者
原帖由 hoopoos 于 30-1-2009 09:40 发表
annotation 和 xml都是不错的解决方式,不过,写在xml里面有个很大的好处是配置更容易管理,因为xml本身就是可读写可操作的。有的时候我们既希望最大限度的发挥framework的作用,又不希望过于紧密的依赖于某个framew ...


使用Spring AOP,性能是不会下降的。因为Spring的Context是使用ApplicationContext一次性加载,而AspectJ是运行时解析。这两种方式在运行时性能的差距是具大的。特别是对于Web应用来说,使用WebApplicationContext一次性在启动时把配置加载进内存,之后的执行效率是非常快的。这也是Spring和EJB容器的一个很大区别。在EJB3中,你使用@Stateful或@Stateless这些SessionBean时,EJB容器是可以在运行时处理的。而Spring中的@Autowired及Spring MVC中的@Controller是一次性加载的。
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+10, 30-8-2025 05:53 , Processed in 0.026772 second(s), 32 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表