本文实现扫描对应包下接口,使用JDK动态代理进行接口代理
一丶JDK动态代理的接口
/**
* 该注解标志需要代理的接口,类似mybatis的@Mapper注解,也可以扫描的时候扫描所有接口,不进行筛选
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DemoRegistry {
}
/**
* 进行动态代理的接口,@DemoRegistry代表筛选的条件
*/
@DemoRegistry
public interface DemoBean {
String hello();
}
/**
* 接口的动态代理实现
*/
public class DemoInvocationHandler implements InvocationHandler {
public static Object build(Class<?> type) {
// 创建代理对象,参数分别为类加载器,需要的接口Class对应(因为代理的type就是接口,所以直接new一个class数组就行),具体的代理实现
return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new DemoInvocationHandler());
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 自己实现对应的代理逻辑
// 这里实现为如果方法定义的class为Object,就直接调用这个对象的方法
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 方法返回值为String,就返回一个hell word,代理的具体逻辑可以自己实现
Class<?> returnType = method.getReturnType();
if (returnType == String.class) {
return "hell word";
}
return null;
}
}
/**
* 在Spring中想给接口注入对应的代理实现可以使用FactoryBean,在注册BeanDefinition的时候设置接口对应的beanClass为该FactoryBean即可
*/
public class DemoFactoryBean<T> implements FactoryBean<T> {
/**
* 代理的接口类型
*/
public final Class<T> type;
public DemoFactoryBean(Class<T> type) {
this.type = type;
}
@Override
public T getObject() throws Exception {
// 获取代理对象,使用JDK动态代理
return (T) DemoInvocationHandler.build(type);
}
@Override
public Class<T> getObjectType() {
// 获取对象的类型
return type;
}
}
二丶接口扫描及注册BeanDefinition
/**
* 启动接口扫描的注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
// @Import注解是Spring提供的导入对应的类的注解,通过导入具体实现了ImportBeanDefinitionRegistrar接口的类可以自己注册BeanDefinition
@Import({DemoRegistrarTest.class})
public @interface EnableDemo {
/**
* 扫描的包路径
*/
String[] value();
}
/**
* 注册BeanDefinition
*/
public class DemoRegistrarTest implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取类上的EnableDemo注解的属性
Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableDemo.class.getName());
// 类扫描器
MyClassScan scan = new MyClassScan(registry);
// 从注解配置中获取扫描的包路径进行扫描
int scanCount = scan.scan((String[]) map.get("value"));
System.out.println(scanCount);
}
}
/**
* 自定义扫描器
*/
public class MyClassScan extends ClassPathBeanDefinitionScanner {
public MyClassScan(BeanDefinitionRegistry registry) {
super(registry, false);
// 注册筛选器
registerFilters();
}
public void registerFilters() {
// 筛选只有包含DemoRegistry注解的接口
addIncludeFilter(new AnnotationTypeFilter(DemoRegistry.class));
}
/**
* 这个必须要重写,spring提供的ClassPathBeanDefinitionScanner默认是不要接口的,不重写这个方法,获取不到接口
*/
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
// 是不是接口且是独立的bean(不依赖其他bean)
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 进行扫描,获取BeanDefinitionHolder
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
// 循环替换beanDefinition中的beanClass为FactoryBean,不进行替换的话,beanClass是接口,Spring无法通过接口得到Bean对象
for (BeanDefinitionHolder definitionHolder : beanDefinitionHolders) {
// 获取beanDefinition
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definitionHolder.getBeanDefinition();
// 获取扫描出的接口的beanClassName
String beanClassName = beanDefinition.getBeanClassName();
// 设置BeanClass为DemoFactoryBean
beanDefinition.setBeanClass(DemoFactoryBean.class);
// 添加构造方法的参数,DemoFactoryBean的构造方法需要Class对象,这里设置的是string的参数,spring会使用Class.forName加载
// 成DemoFactoryBean中构造方法需要的类参数
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
}
return beanDefinitionHolders;
}
}
三丶测试
/**
* 测试代码
*/
@Service
public class DemoInterfaceTest {
/**
* 注入对象
*/
@Autowired
private DemoBean demoBean;
@PostConstruct
public void init() {
// 执行方法,这里就会执行代理中的逻辑
System.out.println(demoBean.hello());
System.out.println(demoBean.toString());
// 输出
// hell word
// com.example.cachedemo.factory.DemoInvocationHandler@30bf26df()
}
}