只有接口,没有实现类。spring是如何注入空接口并执行代码的?

发布时间 2023-06-07 15:19:32作者: 猥琐熊花子酱

里面涉及到了两个姿势点:
1.jdk动态代理,java运行只有接口,没有实现类,生成一个可执行的对象
2.spring FactoryBean ,通过spring提供的bean工厂,可是轻松的根据参数实例化需要的bean
以上两者结合,就可以实现只有接口也能注入并使用

只有接口生成一个可执行的类

一个被代理的空接口

public interface MyInterface {

    @Select("select 'testSql'")
     void myTest(String s);

}

代理类

public class MyMapperInvocation<T> implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String[] value1 = method.getAnnotation(Select.class).value();
        System.out.println("selectById调用成功,拿到的sql:"+ Arrays.toString(value1)+"----拿到的参数:"+args[0]);
        return null;
    }


}

测试一下

public class Test {

    public static void main(String[] args) throws Exception {
        MyInterface myInterface =   (MyInterface)Proxy.newProxyInstance(MyInterface.class.getClassLoader(),new Class[]{MyInterface.class},new MyMapperInvocation<>());
        myInterface.myTest("myTest");
    }

}

可以看到只有一个空接口,也可以执行代码

把空接口注入spring,像mybatis的mapper一样执行

通过后置处理器,把代理类注入到spring

@Configuration
public class AnnotationScannerConfigurer implements BeanDefinitionRegistryPostProcessor {

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyFactoryBean.class);//定义工厂类
		builder.addPropertyValue("proxyInstanceClass", MyInterface.class);//设置工厂类的参数,工厂类根据参数生成代理对象
		BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(builder.getBeanDefinition(), "myInterface");//设置代理对象的beanName
		BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, beanDefinitionRegistry);//添加到spring容器定义中
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
		System.out.println("----postProcessBeanFactory----");
	}
}

测试代码

@Slf4j
@RestController
public class MyController {

    @Resource
    private MyInterface myInterface;

    @GetMapping("/test9")
    public String test9() {
       myInterface.myTest("test9");
       return "ok";
    }
}

发现依然可以执行