如何動態在spring mvc中增加bean


閱讀對象##

搭框架人員,或者其他感興趣的開發人員

背景##

一般來說在業務代碼中,加上 @Component, @Service@Repository, @Controller等注解就可以實現將bean注冊到Spring中了。
但是在寫框架,可能有些類會動態生成,怎么動態注冊到Spring中呢?

BeanDefinitionRegistryPostProcessor 接口##

BeanDefinitionRegistryPostProcessor接口是一個可以修改spring工廠中已定義的bean的接口,該接口有個postProcessBeanDefinitionRegistry方法。

/**
 * Modify the application context's internal bean definition registry after its
 * standard initialization. All regular bean definitions will have been loaded,
 * but no beans will have been instantiated yet. This allows for adding further
 * bean definitions before the next post-processing phase kicks in.
 * [@param](https://my.oschina.net/u/2303379) registry the bean definition registry used by the application context
 * [@throws](https://my.oschina.net/throws) org.springframework.beans.BeansException in case of errors
 */
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

實現##

代碼###

public class App implements BeanDefinitionRegistryPostProcessor{
 @Override
 public void postProcessBeanDefinitionRegistry(
		BeanDefinitionRegistry registry) throws BeansException {
	registry.registerBeanDefinition("demoEntityDao", getDefinition(AcAlert.class));
 }
 private BeanDefinition getDefinition(Class<?> cls) {
	DaoInterfaceGenerator g = new DaoInterfaceGenerator(cls);
	Class<?> daoClass = g.generateClass();
	GenericBeanDefinition bd = new GenericBeanDefinition();
	
	bd.setBeanClass(MapperFactoryBean.class);
	bd.getConstructorArgumentValues().addGenericArgumentValue(daoClass);
	bd.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference("sqlSessionFactory"));
	
	return bd;
 }
}

步驟說明###

  1. 實現BeanDefinitionRegistryPostProcessor接口
  2. 處理postProcessBeanDefinitionRegistry方法
  3. 生成BeanDefinition,上面展示了動態添加一個mybatis Dao的bean注冊過程

我遇到的坑##

當動態生成生成mybatis的dao接口時,報這個錯誤:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demoEntityDao': Post-processing of FactoryBean's singleton object failed; nested exception is java.lang.IllegalArgumentException: interface com.dhcc.ac.domain.AcAlertDao is not visible from class loader
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:116)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1590)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:254)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1054)
at com.dhcc.framework.App.main(App.java:48)
Caused by: java.lang.IllegalArgumentException: interface com.dhcc.ac.domain.AcAlertDao is not visible from class loader
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:581)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
at java.lang.reflect.WeakCache.get(WeakCache.java:127)
at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
at org.springframework.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.java:121)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:109)
at org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization(AbstractAdvisingBeanPostProcessor.java:90)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1723)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:113)
... 5 more

錯誤原因在於動態生成的class加載的classloader,與Proxy.newProxyInstance使用的ClassLoader,不是同一個classloader,所以在Proxy.newProxyInstance找不到這個新生成的類。

解決辦法###

動態加載類,一般都是用defineClass動態加載到ClassLoader里去。
但defineClass不是public,所以在這里卡了一段時間。
后來,突然之間找到了解決辦法,使用反射調用defineClass就好了呀。

小結##

技術沒什么高深的,我后面將要寫的一個功能,用到了本篇的技術,這里先介紹一下。


我的微信zouhaibin294148,不知不覺寫了10多年代碼了
二維碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM