上篇讲解了在spring中aspectj,对普通@Transactional的事务控制支持,这篇主要了解一下spring的@Configurable对新建对象的spring属性支持,并顺带发布一个简单的rich domain的例子:)
上篇地址:http://www.iflym.com/index.php/code/the-theory-about-spring-and-aspectj-pre.html
主要的代码是由org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect 来负责提供,其中主要涉及到了切入点的划分,以及如何对切入点进行处理。
//在初始化bean之前 @SuppressAjWarnings("adviceDidNotMatch") before(Object bean) : beanConstruction(bean) && preConstructionCondition() && inConfigurableBean() { configureBean(bean); } //在初始化bean之外 @SuppressAjWarnings("adviceDidNotMatch") after(Object bean) returning : beanConstruction(bean) && postConstructionCondition() && inConfigurableBean() { configureBean(bean); }
这里主要定义了在两个切入点之前和之后应该做的事情,即进行configureBean(bean)操作,首先看这个inConfigurableBean切入点的表达式
public pointcut inConfigurableBean() : @this(Configurable);
这里,即指所有在类上面声明了@Configurable注解的类均满足切入点表达式,通过spring的api我们知道,主要声明了@Configurable注解,并且在spring xml文件中进行了属性配置,加上相应的annotation-driven,即可达到在new对象之后即可获得相应属性的目的。如下的xml配置:
<bean class="com.greejoy.gtip.component.support.domain.BaseDomain" scope="prototype"> <property name="baseDAO" ref="baseDAO"/> </bean>
一般的配置,即如所上所示,那么为什么需要这个配置,这得从方法configureBean(bean)说起。进入到configureBean(bean)的实现方法:
//为这个类注入beanFactory public void setBeanFactory(BeanFactory beanFactory) throws BeansException { beanConfigurerSupport.setBeanFactory(beanFactory); beanConfigurerSupport.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver()); } //配置bean public void configureBean(Object bean) { beanConfigurerSupport.configureBean(bean); }
这里的配置方法,即调用beanConfigureSupport去配置bean信息,继续看实现方法
BeanWiringInfo bwi = this.beanWiringInfoResolver.resolveWiringInfo(beanInstance); if (bwi.indicatesAutowiring() || (bwi.isDefaultBeanName() && !this.beanFactory.containsBean(bwi.getBeanName()))) { // Perform autowiring (also applying standard factory / post-processor callbacks). this.beanFactory.autowireBeanProperties(beanInstance, bwi.getAutowireMode(), bwi.getDependencyCheck()); Object result = this.beanFactory.initializeBean(beanInstance, bwi.getBeanName()); checkExposedObject(result, beanInstance); } else { // Perform explicit wiring based on the specified bean definition. Object result = this.beanFactory.configureBean(beanInstance, bwi.getBeanName()); checkExposedObject(result, beanInstance); }
上面的方法,即是通过读取该类在spring xml的配置信息,再进行属性注入。所以说,我们在spring xml中配置的信息,不是为了通常情况下的取得spring bean,而也只是简单的配置下属性的依赖信息。spring读取了依赖信息之后,将该bean所依赖的信息设置到类上即可。而该过程在new一个对象之后即完成了,所以我们在使用这个对象时即可直接得到所注入的东西。
通过以上两个例子,我们可以再打开一下思路。实际上spring的配置文件,不仅仅是需要一些bean信息而配置,它也可以配置类与类之间的依赖关系,尽管这个依赖关系并不是为了简单的取得spring容器里的spring对象而使用。而以前在使用spring时,总是想办法从spring容器中取对象,而并不关心它的配置的依赖关系有什么用;现在来说,spring容器对象很重要,而spring的配置信息同样也很重要,它表明了相应类与类之间,对象与对象之间的属性依赖,我们可以通过这个依赖信息,达到我们自己想要的目的。
有了以上两个东西,那么我们就可以简单的实现一个rich domain了,并且将相应的事务控制代码直接控制在domain层!首先是需要一个sessionFactory(为什么不是dao,因为dao也去年啦),同时为了配置domain的getDomain方法,这个sessionFactory必须是静态的,然后需要进行事务控制,只需要在相应需要事务控制的方法上加一个@Transactional即可。
protected static SessionFactory sessionFactory; /** * 注入静态sessionFactory,由DomainStaticSessionFactoryInjectBean负责进行注入 * * @param sessionFactory hibernate会话Factory */ public static void setSessionFactory(SessionFactory sessionFactory) { BaseDomain.sessionFactory = sessionFactory; } /** 取对象,静态方法 */ @Transactional(readOnly = true) @SuppressWarnings("unchecked") public static <T extends Entityable> T get(Class<T> clazz, long id) { if(id <= 0) return null; return (T) sessionFactory.getCurrentSession().get(clazz, id); } /** 更新,实例方法 */ @Transactional public void update() { sessionFactory.getCurrentSession().update(this); }
上面涉及到使用的方法,对于实例方法,spring的annotationAspect已经提供了,所以我们只需要一个静态事务支持的aspect即可:
/** 匹配所有transaction注释的公共静态方法 */ @Pointcut( "execution(@org.springframework.transaction.annotation.Transactional static * *.*(..))") private void executionOfStaticTransactionalMethod() { }
这样即可匹配相应的静态方法,其它处理逻辑与spring的处理逻辑相一致,就不再列出。同时,为了防止有些方法调用了sessionFactory,即没有声明@Transactional注解的情况(这种情况下,方法会报错,因为没有运行在事务控制中,相应的session会没有被赋值),故做了一个声明:对于使用了sessionFactory却没有声明的方法,将会在编织时发一个错误,这个由aspectj提供:
@DeclareError("transactionNeeded()") public static final String transactionNeededMessage = "在访问数据库时必须追加Transactional注释"; @Pointcut( "call(* org.hibernate.SessionFactory.*(..)) " + " && (" + " withincode(!@org.springframework.transaction.annotation.Transactional * com.greejoy.gtip.component.support.domain.BaseDomain+.*(..))" + " || " + "withincode(!@org.springframework.transaction.annotation.Transactional static * com.greejoy.gtip.component.support.domain.BaseDomain+.*(..))" + ")") public void transactionNeeded() { }
这样,即可以保证相应的代码运行在正确的方法之内,如果没有写的话,在启动程序时,就会在终端输出相应的错误信息。
转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/the-theory-about-spring-and-aspectj-post.html
What a great resource!
Valuable info. Lucky me I found your site by accident, I bookmarked it.
http://chairo.cn