前言
实现类似于mybatis那样,通过动态代理方式,达到接口方法映射到自定义的服务逻辑实现,以及bean的注册和使用
代码地址
https://gitee.com/BlueDriver/code-demo/tree/master/demo/dynamic-proxy-mapper
相关类定义
Mapper类定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Mapper {
}
Mapper类的代理实现定义
public class MapperProxy implements InvocationHandler {
/**
* 日志对象
*/
private static final Logger LOGGER = LoggerFactory.getLogger(MapperProxy.class);
/**
* applicationContext,用于获取业务处理时获取bean
*/
private ApplicationContext applicationContext;
public MapperProxy(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* 代理方法处理逻辑
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
LOGGER.info("方法[{}]代理实现", method.getName());
if (Object.class.equals(method.getDeclaringClass())) {
// 是Object类的方法,调用原有实现
return method.invoke(this, args);
}
// 自定义的实现逻辑,任由发挥
return "代理处理结果:" + method.getName();
}
}
Mapper类对应的bean
public class MapperFactoryBean<T> implements FactoryBean<T> {
/**
* 接口类
*/
private Class<T> mapperInterClass;
/**
* applicationContext
*/
private ApplicationContext applicationContext;
public MapperFactoryBean(Class<T> mapperInterClass, ApplicationContext applicationContext) {
this.mapperInterClass = mapperInterClass;
this.applicationContext = applicationContext;
}
@Override
@SuppressWarnings("unchecked")
public T getObject() {
return (T) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{mapperInterClass},
new MapperProxy(applicationContext));
}
@Override
public Class<?> getObjectType() {
return mapperInterClass;
}
/**
* Is the object managed by this factory a singleton? That is,
* will {@link #getObject()} always return the same object
* (a reference that can be cached)?
*/
@Override
public boolean isSingleton() {
return true;
}
}
工具类,用于从指定的包下查找含Mapper注解的接口类
public class ClassFindUtils {
/**
* 日志对象
*/
private static final Logger LOGGER = LoggerFactory.getLogger(ClassFindUtils.class);
/**
* (类文件)资源文件通配符
*/
private static final String RESOURCE_PATTERN = "/**/*.class";
/**
* 根据 包路径+注解 查询类
*
* @param annotationClass 注解类
* @param <A> 注解类泛型
* @param basePackages 包路径(例:com.example.demo)
* @return 类集合
* 参考实现:ClassPathScanningCandidateComponentProvider#scanCandidateComponents
*/
public static <A extends Annotation> Set<Class<?>> findClass(Class<A> annotationClass,
String... basePackages) {
Set<Class<?>> classSet = new HashSet<>();
for (String basePackage : basePackages) {
classSet.addAll(findClass(annotationClass, basePackage));
}
return classSet;
}
/**
* 根据 包路径+注解 查询类
*
* @param annotationClass 注解类
* @param <A> 注解类泛型
* @param basePackage 包路径(例:com.example.demo)
* @return 类集合
* ClassPathScanningCandidateComponentProvider#scanCandidateComponents
*/
public static <A extends Annotation> Set<Class<?>> findClass(Class<A> annotationClass,
String basePackage) {
Set<Class<?>> classSet = new HashSet<>();
try {
// spring工具类,可以获取指定路径下的全部类
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
// MetadataReader 的工厂类
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
// 用于读取类信息
MetadataReader reader = readerFactory.getMetadataReader(resource);
// 扫描到的class
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
// 判断是否有指定主解
A annotation = clazz.getAnnotation(annotationClass);
if (annotation != null) {
classSet.add(clazz);
}
}
} catch (IOException | ClassNotFoundException e) {
LOGGER.error("扫描指定包[{}]下含注解[{}]的类异常", basePackage, annotationClass.getName());
LOGGER.error("异常信息", e);
}
return classSet;
}
}
将Mapper类注册为bean的处理
@Component
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ApplicationContextAware {
/**
* 日志对象
*/
private static final Logger LOGGER = LoggerFactory.getLogger(MapperScannerConfigurer.class);
/**
* mapper类包路径
*/
private String mapperPackage;
/**
* applicationContext
*/
private ApplicationContext applicationContext;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 包路径
String[] basePackages = StringUtils.tokenizeToStringArray(mapperPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// 在包路径下查找含指定注解的类
Set<Class<?>> classSet = ClassFindUtils.findClass(Mapper.class, basePackages);
for (Class<?> clazz : classSet) {
// 将找到的含Mapper注解的类进行bean定义和注册
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MapperFactoryBean.class);
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
// 设置构造参数(对应 MapperFactoryBean 的构造器)
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(clazz);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(applicationContext);
// bean名称采取类名首字母小写的形式
String beanName = Introspector.decapitalize(ClassUtils.getShortName(clazz));
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, beanName);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
LOGGER.info("为[{}]注册名称为[{}]的bean", clazz.getName(), beanName);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// do nothing
}
/**
* 从环境配置中读取mapper包路径
*/
@Override
public void setEnvironment(Environment environment) {
mapperPackage = environment.getProperty("code.demo.mapper.package", "code.demo");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
测试
定义一个含Mapper注解的接口
@Mapper
public interface UserDao {
String getNameById(int id);
String getAddressById(int id);
}
单测类
@SpringBootTest(classes = ServiceStarter.class)
public class MapperTest {
@Autowired
private UserDao userDao;
@Test
public void testLogin() {
String name = userDao.getNameById(1);
System.err.println(name);
String address = userDao.getAddressById(2);
System.err.println(address);
}
}
结果:
2022-07-10 18:29:27.844 INFO 19840 --- [ main] c.d.d.proxy.mapper.binding.MapperProxy : 方法[getNameById]代理实现
代理处理结果:getNameById
2022-07-10 18:29:27.846 INFO 19840 --- [ main] c.d.d.proxy.mapper.binding.MapperProxy : 方法[getAddressById]代理实现
代理处理结果:getAddressById
代码地址
https://gitee.com/BlueDriver/code-demo/tree/master/demo/dynamic-proxy-mapper