实现类似 MyBatis 的注解 Mapper

本文介绍了如何使用Java的ClassPathScanningCandidateComponentProvider扫描自定义注解的类,并利用动态代理技术实现代理类,同时展示了如何将代理类注入Spring容器,以及处理@SqlSelect和@Proc注解的方法。
摘要由CSDN通过智能技术生成

利用类 ClassPathScanningCandidateComponentProvider 查找自定义注解的类。

public class DaoAnnotationComponentProvider extends ClassPathScanningCandidateComponentProvider {

    public DaoAnnotationComponentProvider() {
        super(false);
        addIncludeFilter(new AnnotationTypeFilter(Dao.class, false));
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface();
    }
}

利用 Java 的动态代理实现接口的代理类。

采用 Java 的动态代理实现,实现接口 InvocationHandler 即可。主要的思路是:从 @Select 接口获取 SQL,通过 Reflection 获取 Method 的返回类型,从 @Param 中获取参数名称,此参数名称和 SQL 中名称参数一一对应。


public class DaoProxy implements InvocationHandler{
	/**
	 * 工厂方法
	 */
	public static <T> T newInstance(Class<T> innerInterface) {
		ClassLoader classLoader = innerInterface.getClassLoader();
		Class<?>[] interfaces = new Class[] { innerInterface };
		return (T) Proxy.newProxyInstance(classLoader, interfaces, new DaoProxy());
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (method.isDefault()) {
			final Class<?> declaringClass = method.getDeclaringClass();
			Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
					.getDeclaredConstructor(Class.class, int.class);
			constructor.setAccessible(true);
			return constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
					.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
		}

		SqlSelect annoSelect = method.getAnnotation(SqlSelect.class);
		Proc annoProc= method.getAnnotation(Proc.class);

		if (annoProc !=null && annoSelect!=null) {
			throw new IllegalArgumentException("以下注解接口不能同时标注两个以上:@Select, @Proc:" + method.getName());
		}

		log.debug("==> invoke method:" + method.getName());
		if (annoSelect != null) {		// 如果method上存在@Select注解
			return new SelectExecutor().invokeProxyImpl(proxy, method, args);
		} else if (annoProc !=null){
			return new ProcAndFuncExecutor().invokeProxyImpl(proxy, method, args);
		} else {
//			return method.invoke(this, args);
			throw new IllegalArgumentException("不是支持的注解类型");
		}
	}

}

利用类 BeanFactoryPostProcessor 接口将代理类注入到 spring 容器中。

/**
 * 向Spring容器中注入带@Dao接口的proxy实现,即为接口创建proxy实例。
 */
public class DaoBeanProcessor implements BeanFactoryPostProcessor, ResourceLoaderAware {
	private ResourceLoader resourceLoader;
	/** 被扫描的包 */
	private String packages;

	/**
	 * 注入spring的配置资源
	 */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		Set<BeanDefinition> beans = scanCallerInterfaceDef();
		DefaultListableBeanFactory bf = (DefaultListableBeanFactory)beanFactory;
		registerDaoProxyBean(beans, bf);
	}

    /**
     * 扫描给定的package,找出其中带有@Caller接口的定义集合
     */
    private Set<BeanDefinition> scanCallerInterfaceDef() {
    	ClassPathScanningCandidateComponentProvider componentProvider = new DaoAnnotationComponentProvider();
    	componentProvider.setResourceLoader(resourceLoader);

    	Set<BeanDefinition> beans = new LinkedHashSet<BeanDefinition>();
    	String[] pkgArr = packages.split(",");
    	for (String pkg : pkgArr){
    		beans.addAll(componentProvider.findCandidateComponents(pkg.trim()));
    	}
    	return beans;
    }

    /**
     * 向Spring容器注入proxyBean
     * @param beans 所有的带@Dao接口的定义
     */
    private void registerDaoProxyBean(Set<BeanDefinition> beans, DefaultListableBeanFactory beanFactory){
    	for (BeanDefinition bd : beans){
			Class<?> clazz = null;
			try {
				clazz = Class.forName(bd.getBeanClassName());
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				continue;
			}
			String name = clazz.getSimpleName();
			Object obj = DaoProxy.newInstance(clazz);
			beanFactory.registerSingleton(name.substring(0,1).toUpperCase() + name.substring(1), obj);
    	}
    }

	public String getPackages() {
		return packages;
	}

	public void setPackages(String packages) {
		this.packages = packages;
	}

}
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gjhuai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值