spring温习-FactoryBean应用

2021-03-23 11:21:54 浏览数 (1)

不管是平时,还是面试,现在对于spring的讨论少了很多,不再像刚进入人们视野时,那么抢眼;spring现在更像空气一样,只要是java构建的项目,十之八九都是建立在spring之上,因此不可轻视,温故而知新

继《BeanFactory与FactoryBean》之后,再看FactoryBean的应用

在上面的文章中指定FactoryBean可以更加灵活的创建Bean,那就从Spring管理Bean方式说起

XML配置Bean

这种方式,是最早的,借助XML,spring创建bean,来分离调用方与被调用方

代码语言:javascript复制
<bean id="helloWorld" class="me.spring.beans.HelloWorld">
    <property name="name" value="Spring"></property>
</bean>

但是xml不够简洁,且没有编译时检查等功能

@Component @Service

注解开始流行,spring陆续引入了很多注解,关于Bean的有很多

@Component @Service @Repositiory

Java注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的

这些都是把Bean托管给容器

@Bean

从spring3.0开始,可以使用@Configuration与@Bean组合来创建Bean

此注解相对于@Service更灵活了些,如果一个第三方jar中的类需要托管给spring,是没法在类注明@Service注解的,

当然你可以使用XML来实现,但已经约定大于配置,xml不再推荐

除了突破第三方jar限制外,还有

  • 关注点分离,使用@Bean,可以把Bean的创建全部放到了一个地方,接口及其实现不再与具体框架性代码有任何耦合

FactoryBean

虽然@Bean已经相对@Service很灵活了,但还是需要使用@Bean声明,有一些场景无法应对

比如包装RPC,之前写过《RPC的套路》,其实这儿有点AOP的味道

远程service数量是不定的,不能会每一个service去创建并声明上@Bean

一、创建一个FactoryBean

FactroyBean创建的Bean是getObjectType()返回的类型,怎么动态指定此remoteServer,需要使用BeanDefinitionBuilder

代码语言:javascript复制
@Slf4j
public class RemoteProxyFactoryBean<T> implements FactoryBean<T> , MethodInterceptor {

    @Setter
    @Getter
    private Class<T> remoteService;

    @Setter
    @Getter
    private String url;


    @Override
    public T getObject() throws Exception {
        T service = ProxyFactory.getProxy(remoteService, this);
        return service;
    }

    @Override
    public Class<T> getObjectType() {
        return remoteService;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        log.info("RemoteProxyFactoryBean invoke");
        Method method = invocation.getMethod();
        log.info("method:{} invoked",method.getName());

        if (method.getDeclaringClass() == Object.class && method.getName().equals("toString")) { return remoteService.getName()   "@"   System.identityHashCode(remoteService); }

        //远程调用service,可以使用HTTP,也可以是TCP
        StringBuilder sb = new StringBuilder(url);
        sb.append(method.getName());
        sb.append("?var=");
        sb.append(Arrays.toString(invocation.getArguments()));

        log.info("get url:{}",sb.toString());
        return null;
    }
}

二、BeanDefinitionBuilder构建Bean注册spring

使用BeanDefinitionBuilder来构建RemoteProxyFactoryBean的属性,并注册到spring; 注意到上面的RemoteProxyFactoryBean并没有使用@Component托管给spring

代码语言:javascript复制
private void registeRemoteService() throws IOException {
    SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(this.applicationContext);

    //读取此包下的所有类,进行包装
    Resource[] resources = applicationContext.getResources(resolvePackageToScan("com.jack.remote.service"));

    List<String> classNames = Arrays.stream(resources).map(resource -> {
        MetadataReader metadataReader = null;
        try {
            metadataReader = metadataReaderFactory.getMetadataReader(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return metadataReader.getClassMetadata().getClassName();
    }).collect(Collectors.toList());

    //托管给容器
    classNames.forEach(clazz -> {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(RemoteProxyFactoryBean.class);
        beanDefinitionBuilder.addPropertyValue("remoteService",clazz).addPropertyValue("url","http://remotehost/");

        defaultListableBeanFactory.registerBeanDefinition(clazz "-proxy",beanDefinitionBuilder.getBeanDefinition());
    });
}

三、装配

对于远程service包下的类,就可以直接使用@Autowired来装配

总结

至此FactoryBean已经完结,对于它的作用已经了解:它是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式

0 人点赞