mybatisplus生成的mapper和bean,后置处理器实现根据bean获取mapper

该文章介绍了一个名为MyBatisCommonMapper的类,它实现了BeanPostProcessor接口,用于在Spring中处理MyBatisPlus的Mapper和Bean。这个类能根据给定的Bean类获取对应的BaseMapper实例,通过存储和映射Mapper接口和工厂名称来实现这一功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatisplus生成的mapper和bean,后置处理器实现根据bean获取mapper

package cc.ct.business.service.processor;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author congrihe
 * @date 2023年02月21日 15:01
 */
@Component
@Slf4j
public class MyBatisCommonMapper implements BeanPostProcessor {
    private final static Map<String, BaseMapper<?>> MAPPER_INTERFACE = new ConcurrentHashMap<>();

    private final static Map<String,String> FACTORY_NANE = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    public static <T> BaseMapper<T> getMapper(Class<T> t){
        return (BaseMapper<T>) MAPPER_INTERFACE.get(t.getName());
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof  BaseMapper){
            String mapperInterface = FACTORY_NANE.get(beanName);
            MAPPER_INTERFACE.put(mapperInterface,(BaseMapper<?>) bean);
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MapperFactoryBean){
            Class<?> mapperInterface = ((MapperFactoryBean<?>) bean).getMapperInterface();
            Type[] actualTypeArguments = ((ParameterizedType) (mapperInterface.getGenericInterfaces())[0])
                    .getActualTypeArguments();
            String typeName = actualTypeArguments[0].getTypeName();
            FACTORY_NANE.put(beanName,typeName);
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }


}

调用方式

 BaseMapper<?> mapper = MyBatisCommonMapper.getMapper(Bean.class);
### 使用 AOP 拦截 MyBatis Mapper 方法 在 Spring Boot MyBatis 的集成环境中,AOP 可用于拦截 MyBatisMapper 接口调用。然而,在实际操作中存在一些挑战,例如由于 MyBatis 自身使用 JDK 动态代理生成 Mapper 实现类,而这些实现类不会继承接口上的注解,因此可能导致 AOP 无法正常工作[^2]。 #### 解决方案概述 为了成功拦截 MyBatisMapper 方法并应用 AOP 切面逻辑,可以采用以下几种方式: 1. **调整代理机制** 默认情况下,MyBatis 使用的是 JDK 动态代理来生成 Mapper 接口的实现类。如果希望使用 CGLIB 进行增强,则需要手动配置 Spring AOP 来强制使用基于接口的方式而不是依赖于 MyBatis 自动生成的代理对象[^4]。 2. **引入自定义注解** 在目标 Mapper 方法上添加特定的自定义注解,并通过切点表达式匹配这些方法。这种方式能够更精确地控制哪些方法会被拦截。 3. **处理事务一致性问题** 如果涉及缓存更新或其他副作用的操作,需要注意事务管理的影响。因为即使方法执行完毕,但如果后续发生回滚,则可能破坏数据一致性。可以通过监听 `TransactionSynchronizationManager` 或者延迟缓存刷新等方式解决此问题[^3]。 以下是具体实现步骤及相关代码示例: --- #### 配置与实践 ##### 1. 创建自定义注解 定义一个新的注解供开发者标记需要被监控的目标方法。 ```java import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MonitorMapper { String value() default ""; } ``` ##### 2. 修改 Mapper 接口 将上述注解放置于待拦截的具体 SQL 查询/修改函数之上。 ```java public interface UserMapper { @MonitorMapper(value = "findUserById") Optional<User> findById(Long id); @MonitorMapper(value = "updateUserStatus") int updateStatus(@Param("id") Long userId, @Param("status") String status); } ``` ##### 3. 编写 Aspect 类 利用 Spring 提供的支持构建一个通用型切片组件,专门负责捕获所有带有指定标签的方法调用事件。 ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @Aspect public class MapperInterceptor { private static final Logger logger = LoggerFactory.getLogger(MapperInterceptor.class); @Around("@annotation(com.example.demo.annotations.MonitorMapper)") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try { return joinPoint.proceed(); // 执行原始方法 } finally { long elapsedTime = System.currentTimeMillis() - start; logger.info("{} executed in {} ms", joinPoint.getSignature(), elapsedTime); } } } ``` 注意这里我们采用了环绕通知(`@Around`)形式以便既能获取到前置条件又能记录后置状态变化情况;同时借助 SLF4J 日志库打印耗时统计信息作为演示用途之一. ##### 4. 确保正确加载 Bean 定义 确认项目上下文中已经注册好了刚才创建好的 aspect bean ,通常只需简单声明即可自动扫描发现它: ```properties spring.main.allow-bean-definition-overriding=true ``` 另外还需要开启全局支持功能开关: ```java @EnableAspectJAutoProxy(proxyTargetClass = true) @SpringBootApplication public class DemoApplication { ... } ``` 设置参数 proxyTargetClass=true 强制切换至Cglib模式从而绕过原有局限性约束. --- ### 总结 综上所述,虽然直接针对 mybatis mapper 层实施 aop 存有一定难度,但是经过合理设计之后完全可以达成预期效果——既保留框架原有的灵活性优势又增强了可观测性扩展能力[^1].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值