美文网首页
spring通过ServiceLocatorFactoryBea

spring通过ServiceLocatorFactoryBea

作者: HelloWide | 来源:发表于2019-04-29 11:58 被阅读0次

    使用说明

    1.定义测试接口

    /**
     * @author :aoyeye
     * @date :Created in 2019/4/29 10:52
     * @description:
     * @modified By:
     */
    public interface TestBeanFactory {
        String getBeanName();
    }
    

    2.编写实现类

    package com.example.demo.service.impl;
    
    import com.example.demo.service.TestBeanFactory;
    import org.springframework.stereotype.Service;
    
    /**
     * @author :aoyeye
     * @date :Created in 2019/4/29 10:58
     * @description:
     * @modified By:
     */
    @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;
    
    /**
     * @author :aoyeye
     * @date :Created in 2019/4/29 10:59
     * @description:
     * @modified By:
     */
    @Service("B")
    public class TestBeanFactoryImplB implements TestBeanFactory {
        @Override
        public String getBeanName() {
            return TestBeanFactoryImplB.class.getName();
        }
    }
    

    3.定义代理接口

    package com.example.demo.service;
    /**
     * 代理接口
     * @author :aoyeye
     * @date :Created in 2019/4/29 11:03
     * @description:
     * @modified By:
     */
    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;
    
    /**
     * @author :aoyeye
     * @date :Created in 2019/4/29 10:53
     * @description:
     * @modified By:
     */
    @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;例如:数据库连接池时,根据不同数据库类型名称获取数据库信息

    相关文章

      网友评论

          本文标题:spring通过ServiceLocatorFactoryBea

          本文链接:https://www.haomeiwen.com/subject/mkqynqtx.html