Spring 在业务场景使用方式(IOC实现策略模式、AOP实现拦截、Event解耦等)
引言
Spring 框架作为 Java 开发中的重要利器,不仅提供了依赖注入(DI)和面向切面编程(AOP)等核心特性,还提供了丰富的功能和组件,可以应对各种业务场景。本文将通过示例详细解析 Spring 在业务中的常见应用场景和用法,包括使用 IoC 实现策略模式、AOP 实现日志切面、监听器实现业务异步解耦,以及其他常见用法。
1. 使用 IoC 实现策略模式
很多时候,面对不同的业务场景需要不同的业务逻辑,为了避免使用if-else
导致代码臃肿,不具备扩展性,我们一般会使用策略模式。策略模式是一种常见的设计模式,它定义了一系列的算法,并将每个算法封装起来,使它们可以相互替换。在 Spring 中,可以通过 IoC 容器来实现策略模式,使用法更加灵活方便。
通过实现InitializingBean
的afterPropertiesSet
方法完成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 框架,在实际项目中发挥其最大的作用。