SpringAOP应用(AspectJ)

任务需求

系统A的数据库中定义了一张document表,用来存放文档的基本信息,当用户对表数据执行增删改操作时,需要发送消息给系统B,通知其执行相应处理,消息内容包括文档变动类型和文档数据信息。

需求实现

我们能想到的最简单的处理方式如下:

修改数据和发送消息本是两个独立的功能,doAction方法却将其耦合在了一起,方法的处理暂时不存在任何问题,然而随着时间的推移,需求的变动,如果想要禁用消息就需要对业务层的代码进行修改,将sendMessage方法注释掉。

能否通过一种更为优雅的方式来避免硬编码情况的发生?

在上述处理方式中引入了服务容器的概念,消息服务相当于功能切面,而Spring框架相当于功能切面的容器。
doAction方法通过标签的方式来声明对消息服务的依赖,如果容器中含有消息服务,则在修改数据的同时触发消息,如果没有,则只完成修改数据的处理操作。
这样处理的好处是服务之间的耦合度变的更加松散,可通过服务插拔的处理方式来实现业务需求的变动,如果想要禁用消息,只需要将消息切面从Spring容器中移除即可,而不用修改任何代码。

实现细节

首先定义出消息服务切面
切面由切入点和通知构成,AspectJ中通过@Aspect标签来声明
@Aspect
public class SendMessageAdvice {
	private static final Logger LOG = LoggerFactory.getLogger(SendMessageAdvice.class);
	@Pointcut("@annotation(org.chen.aopdemo.SendMessage)")
	public void send() {}
	@Around("send()")
	public void doSendMessage(final ProceedingJoinPoint pjp) throws Throwable {
		pjp.proceed();//执行被拦截的方法
		//获取所拦截的方法名
		MethodSignature msig=(MethodSignature) pjp.getSignature();
		String name = msig.getName();
        Class<?>[] parameters = msig.getParameterTypes();
        Object target = pjp.getTarget();
        Method method = target.getClass().getMethod(name, parameters);//获取拦截的method
        //方法是否使用了泛型参数
        if(method.isBridge()){
        	Class<?> erasedParam=null;
            Class<?> targetParam=null;
        	Annotation[] annotations=target.getClass().getAnnotations();
        	first:for(Annotation annotation:annotations){
        		if(annotation instanceof BridgeMethodMappings){
        			BridgeMethodMappings mappings=(BridgeMethodMappings) annotation;
        			for(BridgeMethodMapping mapping:mappings.value()){
        				if(mapping.methodName().equals(name)){
        					erasedParam=mapping.erasedParamTypes()[0];
        					targetParam=mapping.targetParamTypes()[0];
        					break first;
        				}
        			}
        		}
        	}
        	//将泛型参数替换成指定参数
        	for(int i=0;i<parameters.length;i++){
        		if(parameters[i].equals(erasedParam)){
        			parameters[i]=targetParam;
        		}
        	}
        	//重新得到方法声明
        	method = target.getClass().getMethod(name, parameters);//获取拦截的method
        }
        SendMessage sendMsg=method.getAnnotation(SendMessage.class);//获取方法所声明的SendMessage标签
        SignalType singleType=sendMsg.single();//获取标签声明的消息类型
		Annotation[][] annotations=method.getParameterAnnotations();
		Integer msgDataIndex=null;
		//获取含有MessageData标签的方法参数索引
		first:for(int i=0;i<annotations.length;i++){
			Annotation[] paramAnnotation=annotations[i];
			for(int j=0;j<paramAnnotation.length;j++){
				if(paramAnnotation[j] instanceof MessageData){
					MessageData msgData=(MessageData) paramAnnotation[j];
					if(msgData.order()==0){
						msgDataIndex=new Integer(i);
						break first;
					}
				}
			}
		}
		if(msgDataIndex!=null){
			Serializable obj=(Serializable) pjp.getArgs()[msgDataIndex];//获取方法参数值
			doSendMessage(singleType,obj);//执行发送消息操作,传递消息类型和消息数据
			LOG.info("发送消息成功,消息类型:"+singleType.getOp()+",消息数据:"+obj);
		}
	}
}
@Pointcut标签声明了消息服务的切入点,如果方法声明了SendMessage标签则拦截该方法执行发送消息的处理。
@Around标签声明了通知,方法中描述了发送消息的具体操作。
然后将消息服务切面注入到Spring容器中
在Spring配置文件中添加如下配置
<aop:aspectj-autoproxy />
<bean id="sendMessage" class="cn.com.gei.krp.ecm.core.index.jms.SendMessageAdvice">
	<property name="jmsurl" value="tcp://user-df29b9c8dc:61616?jms.useAsyncSend=true" />
</bean>
最后在DocumentDao的相关方法中对消息服务声明依赖(通过SendMessage标签)
@BridgeMethodMappings({
	@BridgeMethodMapping(methodName="delete",erasedParamTypes={Serializable.class},targetParamTypes={String.class}),
	@BridgeMethodMapping(methodName="update",erasedParamTypes={Object.class},targetParamTypes={Document.class}),
	@BridgeMethodMapping(methodName="insert",erasedParamTypes={Object.class},targetParamTypes={Document.class}),
})
public class DocumentDao extends 
	SimpleHibernateDao<Document, String> implements IDocumentDao{
	@Override
	@SendMessage(type=MessageType.deleteDocument)
	public void delete(@MessageData String id) {
		super.delete(id);
	}
	@Override
	@SendMessage(type=MessageType.updateDocument)//声明消息服务依赖
	public void update(@MessageData Document entity) {
		super.update(entity);
	}
	@Override
	@SendMessage(type=MessageType.addDocument)
	public void insert(@MessageData Document entity) {
		super.insert(entity);
	}
}
这样,在执行document的增删改操作时,如果Spring声明了消息服务,则执行发送消息的处理,否则只执行原操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值