快捷搜索:  汽车  科技

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)接下来就对这三种使用方式一一进行详细的分析。前面的文章提到了有三种比较常见的方式:测试类:这段代码是非常常见的Bean依赖另一Bean场景,只不过这里被依赖的Bean的Scope是prototype类型的,那么测试代码的输出结果会是什么样的呢?我们来运行一下就知道了,下图就是它的运行结果:从结果可以发现,service2的prototype类型失效了,想这种简单的注入的化并不能让prototype类型的Bean每次都使用最新的实例(原因也很简单:只有在Bean实例化的时候才会注入依赖,后续肯定只能用注入的这个Bean)。那么我们该如何做到在大作用域的Bean中正确的使用小作用域的Bean呢?

前面一篇文章介绍了Spring bean的Scope功能(Spring Bean Scope的使用方法、实现原理以及自定义Scope),分析了它的用法和实现原理。这篇文章就来详细介绍一下大作用域下应该如何合理的使用小作用域的Bean。

首先看看下面的代码。

类定义:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(1)

XML配置文件:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(2)

测试类:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(3)

这段代码是非常常见的Bean依赖另一Bean场景,只不过这里被依赖的Bean的Scope是prototype类型的,那么测试代码的输出结果会是什么样的呢?我们来运行一下就知道了,下图就是它的运行结果:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(4)

从结果可以发现,service2的prototype类型失效了,想这种简单的注入的化并不能让prototype类型的Bean每次都使用最新的实例(原因也很简单:只有在Bean实例化的时候才会注入依赖,后续肯定只能用注入的这个Bean)。那么我们该如何做到在大作用域的Bean中正确的使用小作用域的Bean呢?

前面的文章提到了有三种比较常见的方式:

  • 每次使用的时候,直接从ApplicationContext中获取,这种方式最直观。
  • 使用lookup-method注入的方式,这种方式用的一般较少。
  • 使用<aop:scoped-proxy/>方式,这种方式用的较多,也是最合理的方式。

接下来就对这三种使用方式一一进行详细的分析。

从Context中直接获取

这种方式是最直观的:要用的时候直接去拿最新的Bean就是了。把最初的Demo进行一点小小的修改:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(5)

然后我们看看那输出的结果:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(6)

从结果上我们可以看出,每次调用service2输出的UUID都是不一样的,这说明每次拿到的service2对应的Bean实例也是不一样的,达到了我们前面提出的那个效果。这种方式非常简单,原理这里就不再啰嗦了。

使用lookup-method注入的方式实现

首先介绍一下什么是lookup-method注入。lookup方法注入指的是:Spring容器重写bean上的方法并返回容器中另一个bean的功能。被重写的方法必须是如下格式的:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

返回值也必须是一个Spring Bean。

我们看看lookup-method注入方式的用法,还是以最初的demo为基础进行一点点小修改:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(7)

XML配置

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(8)

输出结果:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(9)

从结果上可以看出,这种方式也实现了我们想要的结果,那么lookup-method实现的原理是什么呢?

其实lookup-method原理很简单,使用的是Cglib的代理技术:在初始化Bean的时候会判断一下这个Bean是否有方法需要被注入,如果有的化,就会利用Cglib技术进行方法注入。最后调用这个方法时就会进入对应的MethodInterceptor,我们再看看这个MethodInterceptor里的核心代码:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(10)

从上面Cglib回调方法中可以发现,本质上还是调用BeanFactory的getBean方法,所以每次调用也会得到最新的Bean,也就达到了我们要求的效果。

使用scoped-proxy方式

scoped-proxy是aop的一种配置方式,所以这种方式本质上使用的是aop代理。我们先看看demo,和第一个demo相比,只需要修改一下XML配置文件即可:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(11)

输出结果:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(12)

这种方式显而易见是最简单也是最合理的,配置简单,使用简单。使用的时候和singleton类型的Bean是一模一样的,无需关心被依赖的Bean的Scope。

那么scoped-proxy的实现原理是怎么样的呢?以上面的代码为例:

  1. 首先scoped-proxy会把原来的service2 BeanDefinition替换掉,拷贝一个全新的BeanDefinition,新的Definition定义的类是:ScopedProxyFactoryBean。
  2. 然后ScopedProxyFactoryBean在实例化之后,会创建一个TestService2的代理类实例。
  3. 代理类使用的是AOP技术,并且指定了一个特殊的targetSource:SimpleBeanTargetSource。
  4. 最后当想要使用service2的时候,就会拿到ScopedProxyFactoryBean创建的代理实例。

上面这个过程最关键的就是SimpleBeanTargetSource这个类了,通过前面关于AOP的分析文章我们知道,AOP代理的时候,会通过targetSource拿到实际需要代理的类,而SimpleBeanTargetSource就是用来提供这个实际被代理类实例的,其代码如下图:

详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean(详细分析一下Spring中如何正确的在大作用域中依赖小作用域的Bean)(13)

也就是说每次调用service2的方法的时候,都会先调用这个getTarget到BeanFactory中拿一个最新的Bean实例,自然就达到了我们想要的效果。


相关阅读:

Spring Bean Scope的使用方法、实现原理以及自定义Scope

一张图讲清Spring AOP的实现原理

你知道Spring AOP的相关配置是怎么绑定到Bean的吗?快来看看吧!

猜您喜欢: