Статьи

JSR-299 CDI перехватчики для фасоли

Еще один ранний выпуск модуля Spring-CDI, на этот раз это реализация шаблона перехватчика. Он реализует JSR-299-совместимую модель привязки перехватчиков для управляемых компонентов Spring. Мы будем использовать это в наших бизнес-приложениях для сквозных задач.

Обратите внимание: цель моего блога — поделиться идеями и обсудить их. Если вы используете это в своих приложениях, вы действуете на свой страх и риск.

Реализация шаблона перехватчика JSR-299

Характеристики

Модуль перехватчика обеспечивает следующие функции:

  • Используйте аннотации JSR-299 @Interceptor, @InterceptorBinding, @AroundInvoke и @Nonbinding и их определенную семантику в управляемых компонентах Spring
  • Поддержка перехватчиков на уровне класса (применяется ко всем объявленным методам), а также на определенных методах (перехватчики уровня метода)
  • Поддержка цепочек из нескольких перехватчиков для одного и того же целевого метода
  • Поддержка целевых бобов
  • Интеграция с Spring AOP (только прокси CGLIB)

Перехватчики для обратных вызовов событий жизненного цикла поддерживаются начиная с Spring 2.5 и поэтому не являются предметом этого модуля Sping-CDI (аннотации @PostConstruct и @PreDestroy).

Этот выпуск модуля перехватчика Spring-CDI не поддерживает аннотацию JSR318 @Interceptors. Спецификация CDI гласит: «Если перехватчик не объявляет аннотацию @Interceptor, он должен быть связан с bean-компонентами, использующими @Interceptors или ejbjar.xml.» Поскольку JSR299 определяет @InterceptorBinding в качестве механизма привязки, я бы не рекомендовал больше использовать аннотацию @Interceptors.

Перехватчики тайм-аута и перехватчики по умолчанию пока не поддерживаются.

Ссылка на скачивание

Модуль перехватчика Spring-CDI — это обычное расширение IoC-контейнера Spring, поставляемое в виде архива JAR. Вы можете скачать модуль JAR и поместить его в путь к классу вашего приложения Spring.

Скомпилированный модуль перехватчика Spring-CDI JAR: версия 0.5.1

Источники: Версия 0.5.1
API-Doc: Версия 0.5.1

Все размещено в git-репозитории на Github.com .

зависимости

Вот мои зависимости во время выполнения:

		<dependency>
			<groupid>org.springframework</groupid>
			<artifactid>spring-context</artifactid>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupid>org.springframework</groupid>
			<artifactid>org.springframework.aop</artifactid>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupid>javax.enterprise</groupid>
			<artifactid>cdi-api</artifactid>
			<version>1.0</version>
		</dependency>

		<dependency>
			<groupid>org.jboss.spec.javax.interceptor</groupid>
			<artifactid>jboss-interceptors-api_1.1_spec</artifactid>
			<version>1.0.0.Final</version>
		</dependency>

		<dependency>
			<groupid>cglib</groupid>
			<artifactid>cglib</artifactid>
			<version>2.2.2</version>
		</dependency>

Конфигурация

Если JAR-модуль перехватчика Spring-CDI и его зависимости находятся на вашем пути к классам, все, что вам нужно сделать, это:

(1) зарегистрировать InterceptorAwareBeanFactoryPostProcessor в контексте вашего приложения
(2) определить фильтр включения, чтобы включить javax.interceptor.Interceptor в качестве компонента аннотация в вашем контексте: тег компонентного сканирования

<beans>
...
<context:component-scan base-package="com.schlimm.springcdi.interceptor.simple" scoped-proxy="targetClass">
<context:include-filter type="annotation" expression="javax.interceptor.Interceptor" />
</context:component-scan>

<bean class="com.schlimm.springcdi.interceptor.InterceptorAwareBeanFactoryPostProcessor"/>
...
</beans>

Если вы хотите заказать перехватчики, вам нужно настроить свойство interceptorOrder для InterceptorAwareBeanFactoryPostProcessor:

<bean class="com.schlimm.springcdi.interceptor.InterceptorAwareBeanFactoryPostProcessor">
<property name="interceptorOrder">
<list>
<value>com.schlimm.springcdi.interceptor.ct._91_C1.interceptors.TransactionInterceptor</value>
<value>com.schlimm.springcdi.interceptor.ct._92_C1.interceptors.OldSecurityInterceptor</value>
<value>com.schlimm.springcdi.interceptor.ct._92_C1.interceptors.VIPSecurityInterceptor</value>
<value>com.schlimm.springcdi.interceptor.ct._92_C1.interceptors.SecurityInterceptor</value>
</list>
</property>
</bean>

Пример использования

В следующих фрагментах кода показано, как можно использовать шаблоны перехватчиков, которые вы настроили в приложении Spring, как описано выше. Для более сложных сценариев смотрите мои примеры модульных тестов.

Предположим, у вас есть бизнес-интерфейс с именем: Simple_MyServiceInterface

public interface Simple_MyServiceInterface {

	String sayHello();
	String sayHello(String what);
	String sayGoodBye();
	
}

Это ваша реализация сервиса.

@Component
@ReturnValueAdopted
public class Simple_MyServiceInterface_Impl implements Simple_MyServiceInterface {

	@Override
	public String sayHello() {
		return "Hello";
	}

	@Override
	public String sayHello(String what) {
		return what;
	}

	@Override
	public String sayGoodBye() {
		return "Good bye";
	}

}

Обратите внимание на аннотацию @ReturnValueAdopted на уровне типа. Это объявление привязки перехватчика:

@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReturnValueAdopted { }

Этот перехватчик также объявляет привязку перехватчика @ReturnValueAdopted, что означает, что перехватчик применяется к Simple_MyServiceInterface_Impl.

@Interceptor @ReturnValueAdopted
public class Simple_MyInterceptor {

	@AroundInvoke
	public String extendReturnValueWithSomeNonsense(InvocationContext ctx) throws Exception {
		String result = null;
		ctx.getContextData().put("Some", "Nonsense");
		try {
			result = (String)ctx.proceed();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return result + "_hello_world";
	}
	
}

Аннотация @Interceptor объявляет класс как перехватчик. Он будет перехватывать все вызовы методов в бине Simple_MyServiceInterface_Impl. Более конкретно, он расширит возвращаемые значения с помощью «_hello_world» Также обратите внимание на данные контекста, записанные в InvocationContext. Это позволяет цепям перехватчиков обмениваться контекстными данными.

Теперь вы можете автоматически связать bean-компонент Simple_MyServiceInterface_Impl в произвольные bean-компоненты:

@ContextConfiguration("/test-context-interceptor-simple.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class SimpleInterceptorTestCase {

	@Autowired // will be intercepted
	private Simple_MyServiceInterface someInterceptedBean;
	
	@Test
	public void testHelloWorldExtension() {
		Assert.assertTrue(someInterceptedBean.sayHello().equals("Hello_hello_world"));
	}
	
}

Для внедренного компонента будет применен определенный Simple_MyInterceptor. Когда вызывается метод sayHello (), перехватчик расширяет возвращаемое значение сообщением «_hello_world».

Как это работает

Основой является InterceptorAwareBeanFactoryPostProcessor . Он использует две стратегии для реализации логики. InterceptorResolutionLogic сканирует контекст приложения на наличие зарегистрированных перехватчиков. InterceptorOrderingStrategy реализует порядок цепочек перехватчиков для вызова целевого метода. Результат сбора данных в рамках этих двух стратегий сохраняется в InterceptorMetaDataBean. Постпроцессор фабрики бинов также регистрирует InterceptorAwareBeanPostProcessorwith создает прокси для каждого перехваченного компонента. Этот прокси-сервер имеет перехваченный компонент в качестве цели и InterceptedBeanProxyAdvice в качестве рекомендации перехватчика. Этот совет создает (и кэширует) цепочки пользовательских перехватчиков JSR299 для каждого уникального вызова метода. Он извлекает необходимые метаданные для цепочки перехватчиков из InterceptorMetaDataBean.

Попробуйте все, если у вас есть время!

От http://niklasschlimm.blogspot.com/2011/08/jsr-299-cdi-interceptors-for-spring.html