Spring 在业务场景使用方式(IOC实现策略模式、AOP实现拦截、Event解耦等)

Spring 在业务场景使用方式(IOC实现策略模式、AOP实现拦截、Event解耦等)


引言

Spring 框架作为 Java 开发中的重要利器,不仅提供了依赖注入(DI)和面向切面编程(AOP)等核心特性,还提供了丰富的功能和组件,可以应对各种业务场景。本文将通过示例详细解析 Spring 在业务中的常见应用场景和用法,包括使用 IoC 实现策略模式、AOP 实现日志切面、监听器实现业务异步解耦,以及其他常见用法。


1. 使用 IoC 实现策略模式

很多时候,面对不同的业务场景需要不同的业务逻辑,为了避免使用if-else导致代码臃肿,不具备扩展性,我们一般会使用策略模式。策略模式是一种常见的设计模式,它定义了一系列的算法,并将每个算法封装起来,使它们可以相互替换。在 Spring 中,可以通过 IoC 容器来实现策略模式,使用法更加灵活方便。

通过实现InitializingBeanafterPropertiesSet方法完成bean的初始化, 示例代码:

typescript 复制代码
// 定义策略接口
public interface PaymentStrategy extends InitializingBean {
​
    void pay(BigDecimal amount);
​
    PayType getPayType();
​
    @Override
    default void afterPropertiesSet() throws Exception {
        PayFactory.register(getPayType(), this);
    }
}
​
// 实现具体的支付策略
@Component
public class CreditCardPayment implements PaymentStrategy {
​
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("使用信用卡支付:" + amount);
    }
​
    @Override
    public PayType getPayType() {
        return PayType.CREDIT;
    }
}
​
@Component
public class CashPayment implements PaymentStrategy {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("使用现金支付:" + amount);
    }
​
    @Override
    public PayType getPayType() {
        return PayType.CASH;
    }
}
​
// 创建和获取bean的类
public class PayFactory {
​
    private static final Map<PayType, PaymentStrategy> PAY_MAP = new HashMap<>();
​
    public static void register(PayType payType, PaymentStrategy strategy) {
        PAY_MAP.put(payType, strategy);
    }
​
    public static PaymentStrategy getStrategy(PayType payType) {
        return PAY_MAP.get(payType);
    }
}
​
// 使用策略的业务类
@Test
void strategy() {
    PaymentStrategy paymentStrategy = PayFactory.getStrategy(PayType.CASH);
    paymentStrategy.pay(BigDecimal.ONE);
}

2. 使用 AOP 实现拦截

在开发过程中,经常需要使用AOP将与业务无关的操作进行切面处理,以便于专注于业务代码的开发和维护。通常是通过注解和AOP结合,实现思路就是先定义一个注解,然后通过AOP去发现使用该注解的类或方法,增加额外的逻辑,如参数校验,缓存,日志等。

参数校验

代码示例:

less 复制代码
// 定义一个注解作用于方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamCheck {
}
​
// 编写一个切面,做参数校验处理
@Aspect
@Component
public class ParamCheckAspect {
​
    @Before("@annotation(com.example.ParamCheck)")
    public void beforeMethodExecution(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        ParamCheck annotation = method.getAnnotation(ParamCheck.class);
        if (annotation != null) {
            // 进行参数校验逻辑
            for (Object arg : args) {
                if (arg == null) {
                    throw new IllegalArgumentException("参数不能为空");
                }
                // 其他校验逻辑...
            }
        }
    }
}

缓存

定义一个注解用于标记缓存的方法,value是缓存的唯一标识,可通过expiration设置缓存过期时间。

less 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
    String value() default "";
    long expiration() default 60;
}

定义一个切面获取注解标注的方法,获取注解信息,如果缓存中有值,直接返回。如果缓存中没有或者过期,先执行方法,再将结果进行缓存。如果需要根据方法入参进行缓存对应的数据,可以自行扩展。

java 复制代码
@Aspect
@Component
public class CacheAspect {
​
    private final Map<String, CacheEntry> cache = new HashMap<>();
​
    @Around("@annotation(com.example.Cacheable) && execution(* *(..))")
    public Object cacheMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        Cacheable annotation = getCacheableAnnotation(joinPoint);
        String cacheKey = annotation.value();
        if (!cache.containsKey(cacheKey) || cache.get(cacheKey).isExpired()) {
            Object result = joinPoint.proceed();
            long expirationTime = System.currentTimeMillis() + annotation.expiration() * 1000;
            cache.put(cacheKey, new CacheEntry(result, expirationTime));
        }
        return cache.get(cacheKey).getResult();
    }
​
    // 获取Cacheable注解
    private Cacheable getCacheableAnnotation(ProceedingJoinPoint joinPoint) {
        return joinPoint.getTarget().getClass().getMethod(getMethodName(joinPoint)).getAnnotation(Cacheable.class);
    }
​
    private String getMethodName(ProceedingJoinPoint joinPoint) {
        return joinPoint.getSignature().getName();
    }
​
    // 存放缓存信息的对象,包括缓存结果和缓存过期时间
    private static class CacheEntry {
        private final Object result;
        private final long expirationTime;
​
        public CacheEntry(Object result, long expirationTime) {
            this.result = result;
            this.expirationTime = expirationTime;
        }
​
        public Object getResult() {
            return result;
        }
​
        public boolean isExpired() {
            return System.currentTimeMillis() > expirationTime;
        }
    }
}
​

日志打印

对于日志打印,实现也是类似,定义一个注解用于AOP发现,使用前置通知和返回通知处理日志信息。代码示例:

less 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
}
​
@Aspect
@Component
public class LoggingAspect {
​
    private final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
​
    @Pointcut("@annotation(com.example.Loggable)")
    public void loggableMethods() {}
​
    @Before("loggableMethods()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("Before executing method: {}", joinPoint.getSignature().getName());
    }
​
    @AfterReturning(pointcut = "loggableMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logger.info("After executing method: {}, result: {}", joinPoint.getSignature().getName(), result);
    }
}

3. 使用监听器实现业务异步解耦

监听器(Listener)是 Spring 框架中的重要组件,可以实现事件驱动的编程模式,用于解耦业务逻辑。举个例子:订单从确认到支付成功,需要触发财务记账,仓库扣减等操作。如果订单状态的修改需要同步处理下游的业务,这对订单业务非常不友好,对于这种情况,就需要Event异步解耦。

示例代码:

csharp 复制代码
// 支付成功发布事件
@Component
public class PayEventPublishService {
​
    @Autowired
    private ApplicationEventPublisher publisher;
​
    public void publish() {
        // 发布事件
        System.out.println("支付成功");
        // 支付信息
        PayEvent event = new PayEvent("34", 4,  true);
        publisher.publishEvent(event);
        System.out.println("修改订单状态");
    }
}
​
// 记账监听
@Component
public class BillListenerService {
​
    @EventListener
    public void BillListen(PayEvent payEvent) {
        // 记账逻辑
        System.out.println("开始记账,金额扣减"+ payEvent.getMoney());
    }
}
​
// 仓库监听
@Component
public class WarehouseListenerService {
​
    @EventListener
    public void WarehouseListen(PayEvent payEvent) {
        // 仓库扣减逻辑
        System.out.println("仓库扣减"+ payEvent.getCount());
    }
}

4. 使用 Spring Data JPA 进行数据访问

Spring Data JPA 是 Spring 框架的一部分,它提供了简化数据访问的方式,可以轻松地与数据库进行交互。

示例代码:

typescript 复制代码
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByLastName(String lastName);
}
​
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
​
    public List<User> findByLastName(String lastName) {
        return userRepository.findByLastName(lastName);
    }
}

结语

通过以上示例,我们详细解析了 Spring 在业务中的常见应用场景和用法,包括使用 IoC 实现策略模式、AOP 实现日志切面、监听器实现业务异步解耦,以及使用 Spring Data JPA 进行数据访问等。这些功能和组件能够帮助我们简化开发、提高代码质量和可维护性,是 Java 开发中不可或缺的利器。

希望本文能够帮助读者更好地理解和应用 Spring 框架,在实际项目中发挥其最大的作用。

相关推荐
苍何5 分钟前
终于找到免费开源TTS模型,克隆声音不要钱,本地电脑也能跑
后端
用户5936087414013 分钟前
Spring AI 集成 DeepSeek 原生供应商并实现think模式
后端
追逐时光者14 分钟前
别再满网找零散工具了,腾讯 QQ 浏览器这个“帮小忙”工具箱真能省时间
前端·后端
心静自然凉80018 分钟前
Linux网络核心知识+bonding主备模式配置
后端
爻渡2 小时前
异步编程演进史:从回调到Promise再到Async/Await
后端·程序员
要阿尔卑斯吗4 小时前
企业级 RAG 系统的文件标签管理:三层架构与层级优化实战
后端
要阿尔卑斯吗4 小时前
Agent开发之为什么有了LangChain4j框架,我们却不能直接使用它?——桥接层设计详解
后端
用户7713970207064 小时前
从CMD到PowerShell:一个.NET开发者的命令行进化之路
后端
祎雪双十Gy4 小时前
从 DataX 的配置加载说起:我用 FastJson2 做了一个轻量级动态配置管理库
java·后端
Csvn6 小时前
Nginx 配置与运维管理 — 从安装到 SSL 反向代理
后端