通过自定义注解,简单模拟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) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new MyInterceptor());
Object proxyInstance = enhancer.create();
// 将代理类实例注册到 Spring 容器中
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
builder.getBeanDefinition().setInstanceSupplier(() -> proxyInstance);
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.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
调用 http://localhost:8080/test/do_sql_select
即可执行userDao方法。