rabbit:connection-factory:理解为什么Spring命名空间元素与其包装的类不同

huangapple go评论63阅读模式
英文:

rabbit:connection-factory : understanding why a spring namespace element differs from the class it does wrap

问题

我对两种我预期应该足够接近以相同方式运作的用例之间的差异感到困惑。我有一个Spring xml内容,在其中定义了一个消息桥接,使用Spring Integration和spring-amqp来消耗和发布从/到rabbitMq代理。

代理必须通过SSL访问,曾经以非TLS方式访问。我们在端口5671(SSL)上向rabbit发布没有问题。但我们在消耗时遇到了问题...连接工厂未使用SSL套接字而是使用标准套接字...导致服务器拒绝接受传入的帧,因为出现了"{unsupported_record_type,65}"。

尝试了各种更改后,我们最终让它工作了,通过使用相应的“命名空间”元素替换了基本基于bean的声明...即:

“Bean”方法,不起作用:

<bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
    <constructor-arg value="clientSSLConnectionFactory" />
    <property name="addresses" value="some.fully.qualified.name:5671" />
    <property name="username" value="user" />
    <property name="password" value="password" />
    <property name="cacheMode" value="CONNECTION" />
    <property name="connectionCacheSize" value="50" />
    <property name="virtualHost" value="/" />
</bean>

变成

“Namespace”方法:起作用:

<rabbit:connection-factory id="rabbitConnectionFactory" 
		connection-factory="clientSSLConnectionFactory"
		addresses="some.fully.qualified.name:5671"
		username="user"
		password="password"
		cache-mode="CONNECTION" 
		connection-cache-size="50" 
		virtual-host="/" />

我以为两者是相同的(命名空间基本上是一个包装器),但在ConnectionFactory和CachingConnectionFactory内部设置一些断点后,我意识到它们的构造方式不同。特别是我们看到,使用命名空间方法时,连接在第一种方法中使用基本套接字,而在第二种方法中使用SSLSocket。

我们将连接流追踪到这个非常基本的流程(如下所示):它显示了哪些(主要!)方法/类参与SSL套接字和稍后连接的设置。

 步骤  命名空间   BEAN
  使用SslProtocol DefaultListablebeanFactory #60      <br>..=> RabbitConnectionFactoryBean #52<br>….=> ConnectionFactory #64 DefaultListablebeanFactory #60<br>..=> RabbitConnectionFactoryBean #52<br>….=> ConnectionFactory #64
  使用SslProtocol DefaultListablebeanFactory #60       <br>..=> RabbitConnectionFactoryBean #108<br>….=> ConnectionFactory #113
  newConnection() SimpleMessageListenerContainer #126  <br>..=> CachingConnectionFactory #78<br>….=> ConnectionFactory #113<br>…...=> socketFactory #89<br>…....=> sslContextFactory #68  SimpleMessageListenerContainer #97<br>..=> CachingConnectionFactory #94 <br>....=> ConnectionFactory #101 <br>…....=> socketFactory  null  <br>…....=> sslContextFactory null

你可以看到,在两种情况下都调用了useSslProtocol方法,但“bean”方法只使用了一次,通过一个不是后续连接所使用的连接工厂...因此不是SSl套接字。
我添加了Eclipse调试器提供的对象的#ID(在这里很有用,可以显示对象是否是同一个实例)

显然,我可能会满意于命名空间解决方案...但不幸的是它不允许设置“connectionListeners”(我们使用的内容)....我也想深入了解Spring和NamespaceHandlers的工作原理。

我可能能够进一步分析,但我也必须承认,我对Spring不够熟悉...虽然我了解大部分机制,但我在使用这些知识来弄清楚实例化的“所有神奇”过程中遇到了一些困难。
例如,我知道<object>命名空间使用了<Object>NamespaceHandler(https://docs.spring.io/spring-amqp/api/org/springframework/amqp/rabbit/config/RabbitNamespaceHandler.html),但我无法理解这是如何使命名空间与基本bean不同的。

那么...任何提示都可以帮助我们使用Bean方法(或包括监听器)和/或理解这种差异都是受欢迎的。

英文:

I am puzzled about a difference between 2 uses cases I expected to be close enough to work the same way.
I have a Spring xml content within which I am defining a messaging bridge, using Spring Integration and spring-amqp to consume and publish from/to a rabbitMq broker.

The broker has to be accessed through SSL, where it used to be formerly accessed the non-TLS way.
We have no problem publishing to a rabbit on port 5671 (SSL). But we encountered one consuming... with the ConnectionFactory not using a SSL socket but a standard one... resulting in the server refusing the incoming frame because of an "{unsupported_record_type,65}".

After trying various changes, we ended up having it work, replacing a basic bean-based declaration by the corresponding "namespaced" element... ie :

"Bean" method, not Working:

&lt;bean id=&quot;rabbitConnectionFactory&quot; class=&quot;org.springframework.amqp.rabbit.connection.CachingConnectionFactory&quot;&gt;
    &lt;constructor-arg value=&quot;clientSSLConnectionFactory&quot; /&gt;
    &lt;property name=&quot;addresses&quot; value=&quot;some.fully.qualified.name:5671&quot; /&gt;
    &lt;property name=&quot;username&quot; value=&quot;user&quot; /&gt;
    &lt;property name=&quot;password&quot; value=&quot;password&quot; /&gt;
    &lt;property name=&quot;cacheMode&quot; value=&quot;CONNECTION&quot; /&gt;
    &lt;property name=&quot;connectionCacheSize&quot; value=&quot;50&quot; /&gt;
    &lt;property name=&quot;virtualHost&quot; value=&quot;/&quot; /&gt;
&lt;/bean&gt;

becomes

"Namespace" method : Working:

&lt;rabbit:connection-factory id=&quot;rabbitConnectionFactory&quot; 
		connection-factory=&quot;clientSSLConnectionFactory&quot;
		addresses=&quot;some.fully.qualified.name:5671&quot;
		username=&quot;user&quot;
		password=&quot;password&quot;
		cache-mode=&quot;CONNECTION&quot; 
		connection-cache-size=50&quot; 
		virtual-host=&quot;/&quot; /&gt;

I though both were identical (and that the namespace was basicall a wrapper), but after setting up some breakpoints within ConnectionFactory and CachingConnectionFactory (mainly within constructors and new connection methods), I realized that there were not constructed the same way. Especially, we saw that, with the namespace method, the connexion was using a basic socket for the first method while a SSLSocket was used for the second one.

We tracked the connexion flow down to this very basic flow (below) : it shows which (main!) methods / classes participate in the set up of the SSL Socket, and later the connection.

 STEP  NAMESPACE   BEAN
  useSslProtocol DefaultListablebeanFactory #60      <br>..=> RabbitConnectionFactoryBean #52<br>….=> ConnectionFactory #64 DefaultListablebeanFactory #60<br>..=> RabbitConnectionFactoryBean #52<br>….=> ConnectionFactory #64
  useSslProtocol DefaultListablebeanFactory #60       <br>..=> RabbitConnectionFactoryBean #108<br>….=> ConnectionFactory #113
 newConnection() SimpleMessageListenerContainer #126  <br>..=> CachingConnectionFactory #78<br>….=> ConnectionFactory #113<br>…...=> socketFactory #89<br>…....=> sslContextFactory #68  SimpleMessageListenerContainer #97<br>..=> CachingConnectionFactory #94 <br>....=> ConnectionFactory #101 <br>…....=> socketFactory  null  <br>…....=> sslContextFactory null

You can see that the useSslProtocol method is called in both cases, but the "bean" method does use it only once, through a ConnectionFactory that is not the one used for the later connection... hence the non-SSl socket.
I added the #ID of the objects provided by Eclipse debugger (usefull here to show whether objects are the same instance or not)

Obviously, I could be satisfied with the namespace solution... but unfortunately it does not allow to setup "connectionListeners" (that we use).... and also I would like to understand a little deeper how do Spring and NamespaceHandlers do work.

I might be able to go further in my analysis, but I must also confess I am not easy enough with Spring... although I undersand most mecanisms, I experience some difficulties using this knownledge to figure out what is really going on on the "all magical" process of instanciation.
EG, I know the <object> namespace does use a <Object>NamespaceHandler ( https://docs.spring.io/spring-amqp/api/org/springframework/amqp/rabbit/config/RabbitNamespaceHandler.html), but I wasn't able to undersand how does this make the namespace differ from the basic bean.

So... any hint to help us achieve using the Bean method (or include the listners) and/or understand the difference is welcome here.

答案1

得分: 2

NamespaceHandler 只提供了解析特定标签所需的信息。它将 connection-factory 关联到 ConnectionFactoryParser,后者知道如何处理该特定元素。它会将 bean 或 BeanDefinition 注册到应用程序上下文中。

但是,在您的 bean 和命名空间之间存在一个相当大的区别。在您的 bean 定义中,您使用了一个带有 valueconstructor-arg。这将导致调用接受一个 String(即主机名,稍后您会覆盖)的构造函数。您的本意是使用 ref 来引用上下文中的另一个 bean。

所以基本上,您的 bean 定义对 clientSSLConnectionFactory bean 没有任何作用,而命名空间中的定义则有作用。

英文:

The NamespaceHandler only provides the needed information on with what class to parse certain tags. It will link connection-factory to a ConnectionFactoryParser which knows what to do with that specific element. It will register beans or BeanDefinition into the application context.

However there is one, quite big, difference between your bean and the namespace. In your bean definition you are using a constructor-arg with a value. This will lead to invoking the constructor that takes a String (which is the hostname, which you later override). What you intended was to use a ref to reference another bean in the context.

So basically your bean definition doesn't do anything with the clientSSLConnectionFactory bean, where as the one with the namespace does.

huangapple
  • 本文由 发表于 2023年6月19日 18:50:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76505906.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定