第 7 章 EJB Interceptors
7.1. 自定义 Interceptors 复制链接链接已复制到粘贴板!
JBoss EAP 允许您开发和管理自定义 EJB 拦截器。
您可以创建以下拦截器类型:
客户端拦截器
当 JBoss EAP 充当客户端时,运行客户端拦截器。
服务器拦截器
当 JBoss EAP 充当服务器时,运行服务器拦截器。这些拦截器为服务器全局配置。
容器拦截器
当 JBoss EAP 充当服务器时,容器拦截器会运行。这些拦截器在 EJB 容器中配置。
自定义拦截器类应添加到模块中,并存储在 $JBOSS_HOME/modules
目录中。
7.1.1. Interceptor Chain 复制链接链接已复制到粘贴板!
自定义拦截器在拦截器链中的特定点执行。
为 EJB 配置的容器拦截器在 Wildfly 提供的拦截器之前执行,如安全拦截器或事务管理拦截器。因此,容器拦截器可以在调用 Wildfly 拦截器或全局拦截器前处理或配置上下文数据。
服务器和客户端拦截器会在 Wildfly 特定拦截器后执行。
7.1.2. 自定义客户端 Interceptors 复制链接链接已复制到粘贴板!
自定义客户端拦截器实施 org.jboss.ejb.client.EJBClientInterceptor
接口。
此外,还应包含 org.jboss.ejb.client.EJBClientInvocationContext
界面。
以下代码演示了客户端拦截器示例。
客户端拦截器代码示例
7.1.3. 自定义服务器 Interceptors 复制链接链接已复制到粘贴板!
服务器拦截器使用 @javax.annotation.AroundInvoke
注释或 javax.interceptor.AroundTimeout
注释来标记 bean 上调用期间调用的方法。
以下代码演示了一个服务器拦截器示例。
服务器拦截器代码示例
7.1.4. 自定义容器拦截器 复制链接链接已复制到粘贴板!
容器拦截器使用 @javax.annotation.AroundInvoke
注释或 javax.interceptor.AroundTimeout
注释来标记 bean 调用期间调用的方法。
根据 Jakarta Enterprise Beans 3.2 规范的定义,标准 Jakarta EE 拦截器应该在容器完成了安全性上下文传播、交易管理和其他容器提供的调用处理后运行。
以下代码演示了一个拦截器类,用于标记调用的 iAmAround
方法。
容器拦截器代码示例
容器拦截器和 Jakarta EE Interceptor API 之间的差异
虽然容器拦截器被建模为类似于 Jakarta EE 拦截器,但 API 的语义存在一些差别。例如,容器拦截器调用 javax.interceptor.InvocationContext.getTarget()
方法是非法的,因为这些拦截器会在 EJB 组件设置或实例化之前被调用。
7.1.5. 配置容器拦截器 复制链接链接已复制到粘贴板!
容器拦截器使用标准的 Jakarta EE 拦截器库。
因此,它们将 ejb-jar.xml
文件中允许的相同的 XSD 元素用于 ejb-jar 部署描述符的 3.2 版本。
由于它们基于标准的 Jakarta EE 拦截器库,因此只能使用部署描述符来配置容器拦截器。按照设计,应用不需要任何特定于 JBoss EAP 的注释或其他库依赖关系。
配置容器拦截器:
-
在 EJB 部署的
META
文件。-INF/' 目录中创建一个 jboss-
ejb3.xml 在描述符文件中配置容器拦截器元素。
-
使用
urn:container-interceptors:1.0
命名空间来指定容器拦截器元素的配置。 -
使用
<container-interceptors>
元素来指定容器拦截器。 使用
<interceptor-binding>
元素将容器拦截器绑定到 EJB。可以使用以下任一方式绑定拦截器:- 使用通配符(*)将拦截器绑定到部署中的所有 EJB。
- 使用特定 EJB 名称,在单个 Bean 级别上绑定拦截器。
在 EJB 的具体方法级别上绑定拦截器。
注意这些元素使用 EJB 3.2 XSD 配置方式与 Jakarta EE 拦截器相同。
-
使用
以下示例描述符文件演示了配置选项。
容器 Interceptor jboss-ejb3.xml 文件示例
allow-ejb-name-regex
属性允许您在拦截器绑定中使用正则表达式,并将拦截器映射到与指定正则表达式匹配的所有 Bean。使用以下命令,将 ejb3
子系统 的 allow-ejb-name-regex
属性 启用为 true
:
/subsystem=ejb3:write-attribute(name=allow-ejb-name-regex,value=true)
/subsystem=ejb3:write-attribute(name=allow-ejb-name-regex,value=true)
urn:container-interceptors:1.0
命名空间的架构位于 http://www.jboss.org/schema/jbossas/jboss-ejb-container-interceptors_1_0.xsd。
7.1.6. 服务器和客户端拦截器配置 复制链接链接已复制到粘贴板!
服务器和客户端拦截器将全局添加到所用配置文件中的 JBoss EAP 配置中。
服务器拦截器添加到 ejb3
子系统配置中的 <server-interceptors> 元素
中。客户端拦截器添加到 ejb3
子系统配置中的 <client-interceptors> 元素
中。
以下示例演示了如何添加服务器拦截器。
/subsystem=ejb3:list-add(name=server-interceptors,value={module=org.abccorp:tracing-interceptors:1.0,class=org.abccorp.TracingInterceptor})
/subsystem=ejb3:list-add(name=server-interceptors,value={module=org.abccorp:tracing-interceptors:1.0,class=org.abccorp.TracingInterceptor})
以下示例演示了如何添加客户端拦截器。
/subsystem=ejb3:list-add(name=client-interceptors,value={module=org.abccorp:clientInterceptor:1.0,class=org.abccorp.clientInterceptor})
/subsystem=ejb3:list-add(name=client-interceptors,value={module=org.abccorp:clientInterceptor:1.0,class=org.abccorp.clientInterceptor})
每当添加服务器拦截器或客户端拦截器或更改拦截器配置时,必须重新加载服务器。
7.1.7. 更改安全性上下文身份 复制链接链接已复制到粘贴板!
您可以向经过身份验证的用户授予权限,以不同用户身份切换身份和对现有连接执行请求,而不是打开多个客户端连接。
默认情况下,当您对部署到应用服务器的 EJB 进行远程调用时,与服务器的连接会进行身份验证,并且使用连接的任何后续请求都使用原始身份验证的身份执行。对于客户端到服务器调用和服务器对服务器调用也是如此。如果您需要使用来自同一客户端的不同身份,通常您必须打开与服务器的多个连接,以便每个连接都作为不同的身份进行身份验证。相反,您可以允许经过身份验证的用户更改身份。
更改经过身份验证的用户的身份:
在拦截器代码中实施身份更改。
客户端拦截器
拦截器必须通过上下文数据映射传递请求的身份,该映射可通过对
EJBClientInvocationContext.getContext.getContext()的调用来获取
。以下示例代码演示了可切换身份的客户端拦截器。客户端拦截器代码示例
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 容器和服务器拦截器
这些拦截器接收包含身份的
InvocationContext
,并请求切换到新身份。以下代码演示了容器拦截器的隔离示例:容器拦截器代码示例
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
-
应用可以通过编程方式或使用服务加载器机制将客户端拦截器插入到
EJBClientContext
拦截器链中。有关配置客户端拦截器的步骤,请参阅在应用程序中使用客户端拦截器。 创建 Jakarta 身份验证登录模块。
Jakarta Authentication LoginModule 组件负责验证允许用户是否按请求的身份执行请求。以下 abridged 代码示例显示了执行登录和验证的方法:
LoginModule 代码示例
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
7.1.8. 在应用程序中使用客户端拦截器 复制链接链接已复制到粘贴板!
应用可以使用服务加载器,或使用 ClientInterceptors 注释,以编程方式将客户端拦截器插入到 EJBClientContext
侦听器链中。
EJBClientInterceptor
可通过调用 org.jboss.ejb.client.EJBClientInvocationContext#addReturnedContext(String key)
从服务器端调用上下文请求特定数据。如果上下文数据映射中提供的键下存在请求的数据,则会将其发送到客户端。
7.1.8.1. 动态插入客户端拦截器程序 复制链接链接已复制到粘贴板!
创建带有拦截器注册的 EJBClientContext
后,插入拦截器。
以下代码演示了如何使用拦截器注册创建 EJBClientContext
:
EJBClientContext ctxWithInterceptors = EJBClientContext.getCurrent().withAddedInterceptors(clientInterceptor);
EJBClientContext ctxWithInterceptors = EJBClientContext.getCurrent().withAddedInterceptors(clientInterceptor);
创建 EJBClientContext
后,有两个选项可用于插入拦截器:
您可以使用
Callable
操作,使用应用的EJBClientContext
运行以下代码:在Callable
操作中执行的 EJB 调用将应用客户端侧拦截器:ctxWithInterceptors.runCallable(() -> { // perform the calls which should use the interceptor })
ctxWithInterceptors.runCallable(() -> { // perform the calls which should use the interceptor })
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 或者,您可以将新创建的
EJBClientContext
标记为新默认值:EJBClientContext.getContextManager().setThreadDefault(ctxWithInterceptors);
EJBClientContext.getContextManager().setThreadDefault(ctxWithInterceptors);
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
7.1.8.2. 使用服务加载器机制插入客户端拦截器 复制链接链接已复制到粘贴板!
创建 META-INF/services/org.jboss.ejb.client.EJBClientInterceptor
文件,并将它放到客户端应用的类路径中。
文件的规则由 Java ServiceLoader 机制规定。
- 此文件应当包含单独的行,用于 EJB 客户端拦截器实施的每个完全限定类名称。
- EJB 客户端拦截器类必须在类路径中可用。
使用服务加载器机制添加的 EJB 客户端拦截器将按照在类路径中找到的顺序添加,并添加到客户端拦截器链的末尾。
7.1.8.3. 使用 ClientInterceptor 注解插入客户端拦截器 复制链接链接已复制到粘贴板!
通过 @org.jboss.ejb.client.annotation.ClientInterceptors
注释,您可以将 EJB 拦截器放在远程调用的客户端。