简单模拟Mybatis通过注解查询SQL

通过自定义注解,简单模拟Mybatis通过注解查询SQL。

首先,创建自定义注解@MyDao和@MySelect。

MyDao.java

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyDao {
    String[] value() default {};
}

MySelect.java

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MySelect {
    String[] value() default {};
}

创建UserDao,使用上面的自定义注解。

UserDao.java

@MyDao
public interface UserDao {

    @MySelect("select * from sys_user where id = #{id}")
    String getNameById(String id);

}

MyInterceptor.java
处理@MySelect注解的方法实现。

@Slf4j
@Component
public class MyInterceptor implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MySelect mySelect = method.getAnnotation(MySelect.class);
        if (mySelect != null) {
            String sql = mySelect.value()[0];
            // 解析sql中的参数
            Map<String, Object> paramMap = new HashMap<>();
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                paramMap.put(parameters[i].getName(), args[i]);
            }
            // 执行sql
            String workSql = format(sql, paramMap);
            log.info("work sql: {}", workSql);
        }
        return null;
    }


    /**
     * 替换sql中的参数
     */
    private String format(String str, Map<String, Object> paramMap) {
        if (MapUtil.isEmpty(paramMap)) {
            return str;
        }
        AtomicReference<String> text = new AtomicReference<>(str);
        paramMap.forEach((paramName, paramValue) -> {
            String value = String.valueOf(paramValue);
            if (paramValue instanceof String) {
                value = "'" + value + "'";
            }
            text.set(StringUtils.replace(text.get(), "#{" + paramName + "}", value));
        });
        return text.get();
    }
}

InterfaceDynamicRegister.java
处理@MyDao的注解的Mapper。

@Configuration
public class InterfaceDynamicRegister implements ImportBeanDefinitionRegistrar {
    private static final String BASE_PACKAGE = "com.jjh.business.mapper"; // 指定扫描的包路径
    private static final String RESOURCE_PATTERN = "/**/*.class"; // 指定扫描的文件类型

    @SneakyThrows
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        // 扫描所有包含MyDao注解的类
        List<Class<?>> classes = new ArrayList<>();
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + RESOURCE_PATTERN;
        Resource[] resources = resolver.getResources(packageSearchPath);
        SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(MyDao.class));
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                String className = metadataReader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(className);
                classes.add(clazz);
            }
        }
        // 创建接口的实现类并注册到spring
        for (Class<?> clazz : classes) {
            // 将代理类实例注册到 Spring 容器中
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            // 调用BeanDefinition的setInstanceSupplier方法,传入一个Lambda表达式,该表达式返回代理对象的实例
            builder.getBeanDefinition().setInstanceSupplier(() -> {
                // CGlib代理
                // Enhancer enhancer = new Enhancer();
                // enhancer.setSuperclass(clazz);
                // enhancer.setCallback(new MyInterceptor());
                // Object proxyInstance = enhancer.create();
                // return proxyInstance;

                // JDK代理
                return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{clazz},
                        new MyInterceptor());
            });
            registry.registerBeanDefinition(clazz.getSimpleName(), builder.getBeanDefinition());
        }

    }
}

TestController.java
调用userDao,查询数据。

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private UserDao userDao;
    
    @GetMapping("/do_sql_select")
    public Object doSqlSelect() {
        log.info("do_sql_select is do. {}", System.currentTimeMillis());
        userDao.getNameById("0001");
        return "Ok";
    }
}

pom 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

调用 http://localhost:8080/test/do_sql_select 即可执行userDao方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值