Spring5:使用Spring中的MetadataReader来读取class文件并为接口执行jdk代理
Posted 你是小KS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring5:使用Spring中的MetadataReader来读取class文件并为接口执行jdk代理相关的知识,希望对你有一定的参考价值。
1. 声明
当前内容主要为复习和学习Spring中如何实现MapperScanner的功能(即为接口实现动态代理),模拟mybatis和spring之间的交互操作(个人理解)
内容为:
- 扫描指定文件下的所有的接口的class文件
- 使用MetadataReader的实现类来读取class文件内容
- 为接口创建执行代理
当前内容思路来源:Spring源码解析
2. 基本demo
1. 创建接口
public interface BookMapper {
List<String> findAllBookNames();
}
public interface UserMapper {
List<String> findAllUserNames();
}
使用非常简单的接口模拟
2.其他部分
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
/**
*
* @author hy
* @createTime 2021-07-11 14:49:55
* @description 通过扫描文件方式实现为接口添加动态代理
*
*/
public class Test {
// 模拟bean工厂
static Map<Class<?>, Object> beanFactory = new HashMap<Class<?>, Object>();
public static void main(String[] args) {
String basePackage = "com.hy.java.proxy";
String userClassPath = "D:\\\\eclipse-workspace\\\\Java8BasicReStudy\\\\target\\\\classes";
// 这个basePackage可以转换为下面形式
String replaceAll = basePackage.replaceAll("\\\\.", "\\\\\\\\");
// 只扫描接口
String path = userClassPath + "\\\\" + replaceAll + "\\\\";
// 1.使用文件扫描器方式扫描所在的接口文件
List<File> findFiles = searchFileUsingRegex(path, "*.class");
List<Class<?>> findInterfaces = new ArrayList<Class<?>>();
for (File clazzFile : findFiles) {
MetadataReader metadataReader;
try {
metadataReader = new SimpleMetadataReaderFactory().getMetadataReader(new FileSystemResource(clazzFile));
ClassMetadata classMetadata = metadataReader.getClassMetadata();
boolean interface1 = classMetadata.isInterface();
System.out.println("当前读取的class文件是否为接口:" + interface1);
String className = classMetadata.getClassName();
System.out.println("当前类名称:" + className);
// 只选择接口文件
if (interface1) {
Class<?> forName;
try {
forName = Class.forName(className);
findInterfaces.add(forName);
System.out.println("找到接口====>" + className);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (IOException e) {
;// ignore
}
}
// 2. 通过获取当前的接口类,并使用代理方式生成和执行
for (Class<?> interfaceClazz : findInterfaces) {
Object proxy = generatorProxy(interfaceClazz);
beanFactory.put(interfaceClazz, proxy);
}
// 模拟工厂bean的获取
UserMapper userMapper = (UserMapper) beanFactory.get(UserMapper.class);
List<String> findAllUserNames = userMapper.findAllUserNames();
System.out.println(findAllUserNames);
BookMapper bookMapper = (BookMapper) beanFactory.get(BookMapper.class);
List<String> findAllBookNames = bookMapper.findAllBookNames();
System.out.println(findAllBookNames);
}
static List<File> searchFileUsingRegex(String searchDir, String regex) {
Path sourcePath = Paths.get(searchDir);
// 开启递归扫描文件
String findFileRegex = regex;
try {
List<File> result = new ArrayList<File>();
Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
try (DirectoryStream<Path> newDirectoryStream = Files.newDirectoryStream(dir, findFileRegex)) {
newDirectoryStream.forEach(x -> {
// 加入文件中
result.add(x.toFile());
});
}
return FileVisitResult.CONTINUE;
}
});
return result;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return new ArrayList<File>(0);
}
}
// 生成代理类的方法
static Object generatorProxy(Class<?> interfaceClazz) {
return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class<?>[] { interfaceClazz },
new BasicInvocationHandler(interfaceClazz));
}
// 模拟的执行器
static class Executor {
Object executor(String sql) {
System.out.println("执行sql......" + sql);
return new ArrayList<String>(0);
}
}
// 接口执行代理
static class BasicInvocationHandler implements InvocationHandler {
private final Class<?> interfaceClazz;
public BasicInvocationHandler(Class<?> interfaceClazz) {
this.interfaceClazz = interfaceClazz;
}
private Map<String, String> sqlMap = new HashMap<String, String>();
{
sqlMap.put("com.hy.java.proxy.BookMapper.findAllBookNames", "select bookName from book");
sqlMap.put("com.hy.java.proxy.UserMapper.findAllUserNames", "select userName from user");
}
private Executor executor = new Executor();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
String interfaceName = interfaceClazz.getName();
String id = interfaceName + "." + name;
String sql = sqlMap.get(id);
System.out.println(id);
return executor.executor(sql);
}
}
}
这里简化mybatis的sql获取部分
3. 执行结果
执行成功
以上是关于Spring5:使用Spring中的MetadataReader来读取class文件并为接口执行jdk代理的主要内容,如果未能解决你的问题,请参考以下文章
Spring5基于注解的Bean管理简直是Spring中的Spring