美文网首页
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