利用类 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;
}
}
本文介绍了如何使用Java的ClassPathScanningCandidateComponentProvider扫描自定义注解的类,并利用动态代理技术实现代理类,同时展示了如何将代理类注入Spring容器,以及处理@SqlSelect和@Proc注解的方法。

3498

被折叠的 条评论
为什么被折叠?



