spring和aspectj的结合inject原理(下篇)

     上篇讲解了在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

相关文章:

作者: flym

I am flym,the master of the site:)

《spring和aspectj的结合inject原理(下篇)》有3个想法

发表评论

邮箱地址不会被公开。 必填项已用*标注