Spring ServiceLocatorFactoryBean 小结

前言

偶然看到一篇微信公众号文章的分享 https://mp.weixin.qq.com/s/11VKseROGVcJHPCJ8xQ3XA,感觉挺有意思,在这里记录下。
为什么感觉有意思呢?是因为它通过spring 提供的ServiceLocatorFactoryBean 来实现服务定位模式,将服务的提供者和具体实现进行了解耦,你可以使用简单工厂模式来实现,但是不够友好,原因在于你用工厂模式的时候需要在工厂类里面维护创建具体实现类的创建,非真正意义的上的解耦,而本文要介绍的就能做到这一点,如下是示例。


示例:依据不同的文件类型来模拟解析文件

一、代码示例

1、我需要定义一个解析工厂,没错他就是接口,没有具体实现

// 解析工厂
public interface ParserFactory {
    Parser getParser(ContentType contentType);
}
// 文件类型枚举
public enum ContentType {
    JSON,
    CSV
}

2、我需要一个解析接口,用于不同类型的文件解析

public interface Parser {
    List parse(Reader r);
}

3、模拟实现两个解析类,CSV/JSON

@Component("CSV")
public class CSVParser implements Parser {
    @Override
    public List parse(Reader r) {
        System.out.println("parser csv file");
        return null;
    }
}

@Component("JSON")
public class JSONParser implements Parser {
    @Override
    public List parse(Reader r) {
        System.out.println("parser json file");
        return null;
    }
}

4、以上都具备了,那我要咋么用呢?这时需要有个客户端类帮助我们获取解析器类型

@Service
public class Client {
    @Autowired(required = false)
    private ParserFactory parserFactory;

    public List getAll(ContentType contentType) {

        // 关键点,直接根据类型获取
        return parserFactory
                .getParser(contentType)
                .parse(null); // 传入不同的文件读取Reader
    }
}

有么有注意到,这里的parserFactory 我只定义了接口,并没有实现,它是怎么注入的?

5、通过ServiceLocatorFactoryBean注入ParserFactory

@Configuration
public class ParserConfig {

    @Bean("parserFactory")
    public FactoryBean serviceLocatorFactoryBean() {
        ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
        // 设置服务定位接口
        factoryBean.setServiceLocatorInterface(ParserFactory.class);
        return factoryBean;
    }
}

6、现在这些接口以及client都有了,我要怎么用呢?很简单,如下

@RestController
@RequestMapping("/test")
public class MyController{

    @Autowired
    private Client client;

    @Operation(summary = "测试接口调用")
    @GetMapping("/{type}")
    public String test(@PathVariable String type) throws Exception {
        client.getAll(ContentType.valueOf(type.toUpperCase()));
        return "ok";
    }
}

到这里,你可以发起请求用于测试:curl -X GET “http://localhost:8080/test/csv”

二、原理探究

1.ServiceLocatorFactoryBean 是什么?ParserFactory它只是一个接口什么也没干,为什么就能找到Parser的具体实现类呢?

这时因为有个内部类实现 ServiceLocatorInvocationHandler 它利用了我们提供的ContentType,也即为bean的name来获取具体的实现类的,

  private Object invokeServiceLocatorMethod(Method method, Object[] args) throws Exception {
            Class<?> serviceLocatorMethodReturnType = this.getServiceLocatorMethodReturnType(method);

            try {
            	// 获取我们传入的bean name
                String beanName = this.tryGetBeanName(args);
                Assert.state(ServiceLocatorFactoryBean.this.beanFactory != null, "No BeanFactory available");
                // 如果 bean name不为空则通过beanFactory去加载指定名称的bean实例
                return StringUtils.hasLength(beanName) ? ServiceLocatorFactoryBean.this.beanFactory.getBean(beanName, serviceLocatorMethodReturnType) : ServiceLocatorFactoryBean.this.beanFactory.getBean(serviceLocatorMethodReturnType);
            } catch (BeansException var5) {
                if (ServiceLocatorFactoryBean.this.serviceLocatorExceptionConstructor != null) {
                    throw ServiceLocatorFactoryBean.this.createServiceLocatorException(ServiceLocatorFactoryBean.this.serviceLocatorExceptionConstructor, var5);
                } else {
                    throw var5;
                }
            }
        }

比如我们这里为JSONParser 起的名字叫 JSON,就是通过它去加载 JSONParser 然后执行具体的代码。

@Component("JSON")
public class JSONParser implements Parser {
    @Override
    public List parse(Reader r) {
        System.out.println("parser json file");
        return null;
    }
}

如下图所示:
在这里插入图片描述

2. ServiceLocatorFactoryBean的作用

其实ServiceLocatorFactoryBean 用于查找 Bean 的。我们提供的工厂类交给他通过bean name来获取具体实现的单例bean,从而实现业务和定义的解耦,非常省事。

总结

服务定位模式Service Locator Pattern来解决,它帮助我们消除紧耦合实现及其依赖性,并提出将服务与其具体类解耦。

参考:https://www.cnblogs.com/qiushuiyu-108/p/17140900.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值