1. 程式人生 > >使用spring工廠方法(FactoryBean)模式動態建立bean

使用spring工廠方法(FactoryBean)模式動態建立bean

有時候自己需要動態建立一些bean,並託管給spring進行注入使用,比如Mybatis的Mapper。在什麼時機去建立Mapper介面的例項(動態代理),並能讓spring自動注入給依賴依賴方,spring的工廠方法FactoryBean就可以解決(不是BeanFactory)。

下面以 Retrofit 為例簡單說明,自己實現了介面定義(RetrofitService)的HTTP-RPC呼叫,然後託管給spring注入給依賴方,但介面RetrofitService的實力是由retrofit動態建立(動態代理)

先宣告一個 FactoryBean的實現

public class ServiceFactoryBean
implements FactoryBean {
private Retrofit retrofit; private Class serviceClass; public void setServiceClass(Class serviceClass){ this.serviceClass = serviceClass; } public void setRetrofit(Retrofit retrofit) { this.retrofit = retrofit; } @Override public
Object getObject() throws Exception { return retrofit.create(this.serviceClass); } @Override public Class getObjectType() { return serviceClass; } @Override public boolean isSingleton() { return true; } }

在spring進行bean定義處理的時候就把類B的bean在spring中註冊一下(實現介面BeanDefinitionRegistryPostProcessor)

@Configuration
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Autowired
    BeanNameGenerator beanNameGenerator;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        logger.info("Invoke Metho postProcessBeanDefinitionRegistry");

        Reflections reflections = new Reflections("com.myapp.api.service");
        Retrofit retrofit = retrofit();
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(RetrofitService.class);
        for (Class<?> serviceClass : annotated) {
            for(Annotation annotation : serviceClass.getAnnotations()){
                if (annotation instanceof RetrofitService){//自定義註解RetrofitService,都需要通過retrofit建立bean
                    RootBeanDefinition beanDefinition = new RootBeanDefinition();
                    beanDefinition.setBeanClass(ServiceFactoryBean.class);
                    beanDefinition.setLazyInit(true);
                    beanDefinition.getPropertyValues().addPropertyValue("retrofit", retrofit);
                    beanDefinition.getPropertyValues().addPropertyValue("serviceClass", serviceClass);
                    String beanName = this.beanNameGenerator.generateBeanName(beanDefinition, registry);
                    registry.registerBeanDefinition(beanName, beanDefinition);
                }
            }
        }
    }

    public Retrofit retrofit() {

        final String basic = Base64.encodeToString(token.getBytes());

        final OkHttpClient okHttpClient = new OkHttpClient();
        okHttpClient.setReadTimeout(60, TimeUnit.SECONDS);
        okHttpClient.setConnectTimeout(60, TimeUnit.SECONDS);

        okHttpClient.interceptors().add(new Interceptor() {
            @Override
            public Response intercept(Interceptor.Chain chain) throws IOException {
                Request original = chain.request();

                Request.Builder requestBuilder = original.newBuilder()
                        .header("Authorization", basic)
                        .method(original.method(), original.body());

                Request request = requestBuilder.build();
                return chain.proceed(request);
            }
        });

        return new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(JacksonConverterFactory.create())
                .client(okHttpClient)
                .build();
    }

}

如自己定義的類B介面

@RetrofitService
public interface UserService {
    @POST("/user/register")
    Call<Object> register(@Body User user);
}

為什麼要用BeanDefinitionRegistryPostProcessor,可以看看org.springframework.context.support.AbstractApplicationContext#refresh

BeanFactory 它的職責包括:例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。

FactoryBean 作用是產生其他bean例項getObjectType()

到此完成,需要使用者的地方直接使用按照駝峰命名引用或使用註解Autowired