spring通过ServiceLocatorFactoryBean获取bean方式解读

midoll 420 2023-05-08

spring通过ServiceLocatorFactoryBean获取bean方式解读

使用说明

1.定义测试接口


public interface TestBeanFactory {
    String getBeanName();
}

2.编写实现类


package com.example.demo.service.impl;

import com.example.demo.service.TestBeanFactory;
import org.springframework.stereotype.Service;


@Service("A")
public class TestBeanFactoryImplA implements TestBeanFactory {
    @Override
    public String getBeanName() {
        return TestBeanFactoryImplA.class.getName();
    }
}

package com.example.demo.service.impl;

import com.example.demo.service.TestBeanFactory;
import org.springframework.stereotype.Service;


@Service("B")
public class TestBeanFactoryImplB implements TestBeanFactory {
    @Override
    public String getBeanName() {
        return TestBeanFactoryImplB.class.getName();
    }
}

3.定义代理接口


package com.example.demo.service;

public interface BeanFactory {
    TestBeanFactory get(String type);
}

4.配置注入到ServiceLocatorFactoryBean


package com.example.demo.config;

import com.example.demo.service.BeanFactory;
import org.springframework.beans.factory.config.ServiceLocatorFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class BeanFactoryConfig {
    @Bean
    public ServiceLocatorFactoryBean dataWarehouseFactory() {
        ServiceLocatorFactoryBean dataWarehouseFactory = new ServiceLocatorFactoryBean();
        dataWarehouseFactory.setServiceLocatorInterface(BeanFactory.class);
        return dataWarehouseFactory;
    }
}

5.测试


  @Test
    public void testGetBean() {
        String A = "A";
        TestBeanFactory testBeanFactory = beanFactory.get(A);
        String beanName = testBeanFactory.getBeanName();
        System.out.println("=============="+beanName);
    }
输出:==============com.example.demo.service.impl.TestBeanFactoryImplA

原理说明

主要思想通过JDK动态代理方式反射查找相关class method
首先查看:ServiceLocatorFactoryBean.afterPropertiesSet方法


    /**
    * BeanFactory调用的初始化bean接口时调用(InitializingBean)
    */
    @Override
    public void afterPropertiesSet() {
        if (this.serviceLocatorInterface == null) {
            throw new IllegalArgumentException("Property 'serviceLocatorInterface' is required");
        }

        //生成代理
        this.proxy = Proxy.newProxyInstance(
                this.serviceLocatorInterface.getClassLoader(),
                new Class<?>[] {this.serviceLocatorInterface},
                new ServiceLocatorInvocationHandler());
    }

接下来它是如何查询实现的类呢?查看内部类ServiceLocatorInvocationHandler


    private class ServiceLocatorInvocationHandler implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //特殊处理Object类的几个方法 hashcode() equal() toString()
            if (ReflectionUtils.isEqualsMethod(method)) {
                // Only consider equal when proxies are identical.
                return (proxy == args[0]);
            }
            else if (ReflectionUtils.isHashCodeMethod(method)) {
                // Use hashCode of service locator proxy.
                return System.identityHashCode(proxy);
            }
            else if (ReflectionUtils.isToStringMethod(method)) {
                return "Service locator: " + serviceLocatorInterface;
            }
            else {
                return invokeServiceLocatorMethod(method, args);
            }
        }

        private Object invokeServiceLocatorMethod(Method method, Object[] args) throws Exception {
            Class<?> serviceLocatorMethodReturnType = getServiceLocatorMethodReturnType(method);
            try {
                String beanName = tryGetBeanName(args);
                if (StringUtils.hasLength(beanName)) {
                    // 根据beanName查找
                    return beanFactory.getBean(beanName, serviceLocatorMethodReturnType);
                }
                else {
                    // 根据bean类型查找
                    return beanFactory.getBean(serviceLocatorMethodReturnType);
                }
            }
            catch (BeansException ex) {
                if (serviceLocatorExceptionConstructor != null) {
                    throw createServiceLocatorException(serviceLocatorExceptionConstructor, ex);
                }
                throw ex;
            }
        }
    }

如上可知可以通过bean名称和类型两种方式查找bean,demo演示了根据bean name方式查找,那通过bean type如何做呢?
修改

public interface BeanFactory {
    TestBeanFactory get(String type);
    TestBeanFactory get();
}

测试调用get(),发现:


org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.demo.service.TestBeanFactory' available: expected single matching bean but found 2: A,B

日志可知根据bean type查询到了两个bean,因此系统出错。

总结

  1. 实际开发过程中如果相同bean类型只有一个,不建议使用ServiceLocatorFactoryBean增加复杂性;
  2. 如果对于不同输入需要调用不同实现类,而返回类型,数据结构等信息相同时,可以考虑使用ServiceLocatorFactoryBean;例如:数据库连接池时,根据不同数据库类型名称获取数据库信息