Using CDI from Spring
Spring and Java EE are converging in many areas, mainly due to CDI. In fact, by confining yourself to a suitable subset of annotations and other configuration mechanisms, you can design your persistence and service layers to run either on Java EE 6 or on Spring 3, simply by selecting the appropriate set of libraries.
The web layer is a different story: if your web framework is tightly coupled to either Java EE 6 or Spring, it will be hard or even impossible to simply change the container, but even in the web layer, there are solutions like Apache Wicket which work well both with Spring and Java EE, all you need is some glue code to access the dependency injection container.
I'm currently migrating a web application from Tomcat/Spring to Glassfish, and since this application is based on Spring MVC, I cannot completely replace Spring in this step. The goal of the migration is to drop all Spring dependencies from all components except the web frontend and to somehow make the service beans (stateless session beans and CDI managed beans) visible to the Spring web application context.
Looking for a solution, I found Rick Hightower's post on CDI and Spring living in harmony and the related CDISource project. The basic idea is to use a Spring
It was very easy to integrate the CDISource Spring Bridge into my project, I just cloned their Git repository, ran the Maven build, added
to my own POM and included
This was enough to get going, but I soon found out that some standard but not-so-basic use cases are not yet supported, e.g.
The main change is in
The next point to note are qualifiers: I added a
I'm sure this is not the end of the story, both Spring and CDI are complex enough to allow use cases where this bridge is likely to break, but it now works perfectly for me in my real-life application.
The web layer is a different story: if your web framework is tightly coupled to either Java EE 6 or Spring, it will be hard or even impossible to simply change the container, but even in the web layer, there are solutions like Apache Wicket which work well both with Spring and Java EE, all you need is some glue code to access the dependency injection container.
I'm currently migrating a web application from Tomcat/Spring to Glassfish, and since this application is based on Spring MVC, I cannot completely replace Spring in this step. The goal of the migration is to drop all Spring dependencies from all components except the web frontend and to somehow make the service beans (stateless session beans and CDI managed beans) visible to the Spring web application context.
Looking for a solution, I found Rick Hightower's post on CDI and Spring living in harmony and the related CDISource project. The basic idea is to use a Spring
BeanFactoryPostProcessor
to populate Spring's application context with a factory bean for each CDI bean obtained from the CDI BeanManager, thus making all CDI beans accessible as Spring beans.It was very easy to integrate the CDISource Spring Bridge into my project, I just cloned their Git repository, ran the Maven build, added
- <dependency>
- <groupId>org.cdisource.springbridge</groupId>
- <artifactId>springbridge</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
to my own POM and included
in my Spring configuration.
This was enough to get going, but I soon found out that some standard but not-so-basic use cases are not yet supported, e.g.
@Stateless
EJBs@Inject
ed into CDI beans.- Multiple beans of the same type with
@Named
or other qualifiers. - Producer methods
CdiBeanFactoryPostProcessor
to make it work for my application. Here is the solution:- package org.cdisource.springintegration;
- import java.lang.reflect.Type;
- import java.util.Set;
- import javax.enterprise.inject.spi.Bean;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
- import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
- import org.springframework.beans.factory.support.BeanDefinitionBuilder;
- import org.springframework.beans.factory.support.DefaultListableBeanFactory;
- public class CdiBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
- private static Logger logger = LoggerFactory.getLogger(CdiBeanFactoryPostProcessor.class);
- private boolean useLongName;
- private BeanManagerLocationUtil beanManagerLocationUtil = new BeanManagerLocationUtil();
- @Override
- public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
- throws BeansException {
- DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
- Set
> beans = beanManagerLocationUtil.beanManager().getBeans(Object.class); - for (Bean bean : beans) {
- if (bean instanceof SpringIntegrationExtention.SpringBean) {
- continue;
- }
- if (bean.getName() != null && bean.getName().equals("Spring Injection")) {
- continue;
- }
- logger.debug("bean types = {}", bean.getTypes());
- Class beanClass = getBeanClass(bean);
- BeanDefinitionBuilder definition = BeanDefinitionBuilder
- .rootBeanDefinition(CdiFactoryBean.class)
- .addPropertyValue("beanClass", beanClass)
- .addPropertyValue("beanManager", beanManagerLocationUtil.beanManager())
- .addPropertyValue("qualifiers", bean.getQualifiers()).setLazyInit(true);
- String name = generateName(bean);
- factory.registerBeanDefinition(name, definition.getBeanDefinition());
- logger.debug("bean name = {}, bean class = {}", bean.getName(), beanClass.getName());
- }
- }
- private Class getBeanClass(Bean bean) {
- Class klass = Object.class;
- for (Type type : bean.getTypes()) {
- if (type instanceof Class) {
- Class currentClass = (Class) type;
- if (klass.isAssignableFrom(currentClass)) {
- klass = currentClass;
- }
- }
- }
- return klass;
- }
- private String generateName(Bean bean) {
- String name = bean.getName() != null ? bean.getName() : generateNameBasedOnClassName(bean);
- return name;
- }
- private String generateNameBasedOnClassName(Bean bean) {
- Class beanClass = getBeanClass(bean);
- return !useLongName ? beanClass.getSimpleName() + "FactoryBean" : beanClass.getName()
- .replace(".", "_") + "FactoryBean";
- }
- public void setUseLongName(boolean useLongName) {
- this.useLongName = useLongName;
- }
- }
The main change is in
getBeanClass()
: It is important to note that the CDI API Bean.getBeanClass()
does not return the class of the given bean: the result is the class in which the bean is defined. In particular, for producer methods, the result is the class containing the method and not the method return type.Bean.getTypes()
returns all classes and interfaces of potential injection points that the bean will match. This set of types does not always include the bean's implementation class, e.g. for a stateless session bean with a local business interface, the bean types will only be java.lang.Object
and the business interfaces (and any superinterfaces of the business interface).getBeanClass()
currently selects the most specialized CDI bean type and uses this type to create the corresponding Spring factory bean.The next point to note are qualifiers: I added a
qualifiers
property to the CdiFactoryBean
and changed its getObject()
method to use the qualifiers for looking up the bean in the CDI bean container:- package org.cdisource.springintegration;
- import java.lang.annotation.Annotation;
- import java.util.Set;
- import javax.enterprise.inject.spi.BeanManager;
- import org.cdisource.beancontainer.BeanContainer;
- import org.cdisource.beancontainer.BeanContainerImpl;
- import org.springframework.beans.factory.FactoryBean;
- import org.springframework.beans.factory.InitializingBean;
- public class CdiFactoryBean implements FactoryBean
- private Class beanClass;
- private boolean singleton = true;
- private BeanContainer beanContainer;
- private BeanManager beanManager;
- private Set
qualifiers; - @Override
- public void afterPropertiesSet() throws Exception {
- if (beanManager == null)
- throw new IllegalStateException("BeanManager must be set");
- beanContainer = new BeanContainerImpl(beanManager);
- }
- public void setBeanClass(Class beanClass) {
- this.beanClass = beanClass;
- }
- @Override
- public Object getObject() throws Exception {
- return beanContainer.getBeanByType(beanClass, qualifiers.toArray(new Annotation[] {}));
- }
- @Override
- public Class getObjectType() {
- return beanClass;
- }
- @Override
- public boolean isSingleton() {
- return singleton;
- }
- public void setSingleton(boolean singleton) {
- this.singleton = singleton;
- }
- public void setBeanManager(BeanManager beanManager) {
- this.beanManager = beanManager;
- }
- public void setQualifiers(Set
qualifiers) { - this.qualifiers = qualifiers;
- }
- }
I'm sure this is not the end of the story, both Spring and CDI are complex enough to allow use cases where this bridge is likely to break, but it now works perfectly for me in my real-life application.
1 comment:
asics gel runmiles avis Ils pourraient être utilisés asics gel nimbus homme decathlon pour traiter les maladies vénériennes lorsqu'ils sont administrés à l'état frais, tandis adidas zx flux rose noir pas cher qu'une teinture fabriquée à partir d'eux serait utilisée comme sédatif, anesthésique adidas zx flux adv x et vulnéraire. En ce qui concerne les chaussures de travail traditionnelles. Les nike air flight huarache noir baskets des femmes Nike Dunk fleur de prune il asics femme running arrive dans la couleur rose pâle, la Nike Dunk Metropolis crampon nike pas cher adulte Femme, l'attrayant tout noir avec des informations grises, les revenus Nike Dunk noir massif.
Post a Comment