spring aop+queue+多线程轮询消费实现方法拦截后的异步处理

本文介绍了如何利用Spring AOP进行方法拦截,并结合队列和多线程实现异步处理。通过创建切点、定义队列消费者和服务类,以及处理未捕获异常,来实现日志记录和计费功能。
摘要由CSDN通过智能技术生成

一、spring aop的优缺点

优点:低侵入式设计,耦合性低,维护性高。
缺点:采用反射生成代理对象,性能上有瓶颈;织入增强处理都是同步的单一线程,总是在方法返回之前进行。

二、业务场景分析

对一般的业务场景,我们采用aop是可以的。但是如果我们的织入处理很复杂,而且与方法的返回无关。比如复杂日志的记录,这时候我们考虑异步方式去完成代理方法的增强处理。大型的系统,可以采用aop+MQ,自己维护一个消息队列系统,在对方法进行拦截后,只需发送消息到MQ系统,告诉另一个业务系统需要做什么。现在的业务场景是,下游客户调用我们API,我们API调用上游API,需要将调用情况记录数据库进行计费。这里,通过一个单例队列来存放“消息”,并开启一个线程轮询从队列取“消息”,再进行“消费”处理。

三、代码实践

这里采用AspectJ的方式,完成aop动态代理:
存放消息的队列:
/**
 * 存放消息的队列
 * 这里采用无界队列LinkedBlockingQueue,由于存的对象可能使上游计费对象,
 * 也可能是下游计费对象,这里用泛型
 * @author robert
 *
 * @param <T>
 */
@Component
public class ChargingQueue<T> {
	private BlockingQueue<T> chargeQueue = new LinkedBlockingQueue<T>();

	public void add(T t) {
		chargeQueue.add(t);
	}

	public T poll() throws InterruptedException {
		return chargeQueue.poll(1, TimeUnit.SECONDS);
	}

}


切点类:
/**
 * 
 * @ClassName: ChargeAspect
 * @Description: 切点类
 * @author robert
 * @date 2017-7-6
 *
 */
@Aspect
@Component
public class ChargeAspect {
	@Autowired
	private ChargingQueue chargingQueue;
	// 本地异常日志记录对象
	private static final Logger logger = Logger.getLogger(ChargeAspect.class);

	// 上游计费切点
	@Pointcut("@annotation(com.roman.api.aop.async.charge.UpStreamChargingLog)")
	public void upStreamCharging() {
	}

	// 下游计费切点
	@Pointcut("@annotation(com.roman.api.aop.async.charge.DownStreamChargingLog)")
	public void downStreamCharging(
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring AOP(面向切面编程)是Spring框架中的一个组件,它允许您以一种声明性的方式来处理横切关注点(如事务管理,日志记录等)。 通过使用AOP,可以将这些关注点从应用程序的主体中分离出来,从而实现代码的复用和灵活性。 在使用Spring框架中实现多数据源的切换时,可以使用自定义注解的形式来实现。首先,首先在应用程序的主体中定义两个数据源。 然后,可以定义一个自定义注解,用于标识哪些方法应该使用哪个数据源。例如,使用“@Primary”注解标记主要数据源,使用“@Secondary”注解标记辅助数据源。 然后,在Spring配置中定义一个AOP切面,该切面使用上述自定义注解来切换数据源。下面是这种方法的一个示例: ```java @Aspect @Component public class DataSourceAspect { @Around("@annotation(Primary)") public Object primaryDataSource(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 切换到主要数据源 DynamicDataSourceContextHolder.setDataSource(DynamicDataSourceContextHolder.DATA_SOURCE_PRIMARY); try { return proceedingJoinPoint.proceed(); } finally { // 切换回默认数据源 DynamicDataSourceContextHolder.clearDataSource(); } } @Around("@annotation(Secondary)") public Object secondaryDataSource(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 切换到辅助数据源 DynamicDataSourceContextHolder.setDataSource(DynamicDataSourceContextHolder.DATA_SOURCE_SECONDARY); try { return proceedingJoinPoint.proceed(); } finally { // 切换回默认数据源 DynamicDataSourceContextHolder.clearDataSource(); } } } ``` 在上面的代码中,我们可以看到“@Around”注解被用于定义一个环绕通知,该通知基于使用“@Primary”或“@Secondary”注解的方法进行拦截。 在方法执行之前,我们使用“DynamicDataSourceContextHolder”来将数据源设置为主要或辅助数据源。 在方法执行完成之后,我们将数据源切换回默认数据源。 最后,我们可以将“@Primary”和“@Secondary”注解带到相应的方法上,以切换不同的数据源,例如: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override @Primary public User getUserById(long id) { return userDao.getUserById(id); } @Override @Secondary public List<User> getAllUsers() { return userDao.getAllUsers(); } } ``` 在上面的代码中,我们可以看到“@Primary”注解被用于getUserById()方法,表示这个方法应该从主要数据源中读取数据。相反,getAllUsers()方法被标记为“@Secondary”注解,表示这个方法应该从辅助数据源中读取数据。 通过这种方式,我们可以很容易地切换应用程序中的不同数据源,并且代码的重复率很低。这种方法适用于需要在应用程序的不同部分使用不同数据源的多租户应用程序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值