web-dev-qa-db-fra.com

Câblage automatique à l'aide de @Configurable

Je joue avec l'idée d'utiliser Spring @Configurable et @Autowire pour injecter des DAO dans des objets de domaine afin qu'ils n'aient pas besoin de connaître directement la couche de persistance.

J'essaie de suivre http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable , mais mon code semble avoir aucun effet.

En gros, j'ai:

@Configurable
public class Artist {

    @Autowired
    private ArtistDAO artistDao;

    public void setArtistDao(ArtistDAO artistDao) {
        this.artistDao = artistDao;
    }

    public void save() {
        artistDao.save(this);
    }

}

Et:

public interface ArtistDAO {

    public void save(Artist artist);

}

et

@Component
public class ArtistDAOImpl implements ArtistDAO {

    @Override
    public void save(Artist artist) {
        System.out.println("saving");
    }

}

Dans application-context.xml, j'ai:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springsource.org/dtd/spring-beans-2.0.dtd">
<beans>

    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
    <bean class="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect" factory-method="aspectOf"/>

</beans>

Le balayage et l’initialisation du chemin de classe sont effectués par le module à ressort pour Play! cadre, bien que d’autres beans autowired fonctionnent, alors je suis à peu près sûr que ce n’est pas la cause première. J'utilise Spring 3.0.5.

Dans un autre code (dans une méthode de bean injectée dans mon contrôleur à l'aide de Spring, en fait), je fais ceci:

Artist artist = new Artist();
artist.save();

Cela me donne une exception NullPointerException qui tente d’accéder à artistDao dans Artist.save ().

Une idée de ce que je fais mal?

Martin

30
optilude

Vous devez activer le tissage au moment du chargement (ou d'autres types de tissage) pour pouvoir utiliser @Configurable. Assurez-vous de l'activer correctement, comme décrit dans 7.8.4 Tissage au moment du chargement avec AspectJ dans Spring Framework .

7
axtavt

J'avais ce problème avec Tomcat 7 qui utilisait LTW pour essayer de transférer automatiquement des beans dans mes classes de domaine. 

La documentation de 3.2.x a été mise à jour à l'adresse http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-configurable-container qui a révélé que l’on peut utiliser @EnableSpringConfigured à la place de la configuration xml.

J'ai donc l'annotation suivante sur mon objet Domain:

@Configurable(preConstruction=true,dependencyCheck=true,autowire=Autowire.BY_TYPE)
@EnableSpringConfigured

@EnableSpringConfigured est un substitut pour

<context:spring-configured />

et n'oubliez pas d'ajouter ceci à votre fichier XML de contexte:

<context:load-time-weaver weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" aspectj-weaving="on"/>

Bien sûr, je devais d'abord configurer Tomcat pour le tissage au temps de chargement. 

En outre, j'ai rencontré un bogue dans la version 3.2.0 (pointeur null), de sorte que je devais passer à Spring 3.2.1 ( https://jira.springsource.org/browse/SPR-10108 )

Tout va bien maintenant!

6
Chris B

Tout d’abord, activez la journalisation du débogage Spring. J'utilise Log4j pour le faire. J'ai créé un enregistreur similaire à so (avec la configuration Log4j xml pour pouvoir utiliser RollingFileAppender):

<log4j:configuration>
  <appender name="roll" class="org.Apache.log4j.rolling.RollingFileAppender">
     blah blah configuration blah blah
  </appender>
  <logger name="org.springframework">
    <level value="debug" />
    <appender-ref ref="roll" />
  </logger>
</log4j:configuration>

Cela vous permettra de voir ce que le printemps fait et quand.

Deuxièmement, vous avez ArtistDAO autowired, mais je ne vois pas où vous avez un haricot nommé ArtistDAO. Votre composant DAO sera appelé "artistDaoImpl" par défaut. Essayez de remplacer @Component par @Component("artistDao") et d'appliquer @Autowired à l'utilisateur:

private ArtistDAO artistDao;

@Autowired
public void setArtistDao(ArtistDAO artistDao) 
{
  this.artistDao = artistDao;
}
4
Paul

Vous devriez juste regarder comment Spring Roo le fait puisqu'il fait exactement ce que vous voulez faire.

Votre NPE peut avoir de nombreuses conséquences, mais la plupart du temps, il s'agit de ne pas compiler correctement avec compilateur AspectJ et de ne pas avoir le jar Aspects Spring dans votre AspectJ lib path (différent de votre classpath).

Commencez par essayer de le faire fonctionner avec Maven et le plugin de compilateur AspectJ. C'est pourquoi je recommande Spring Roo, car il génère un fichier POM avec la configuration correcte.

J'ai trouvé que @Configurable ne fonctionnait pas vraiment avec LTW (malgré l'une des réponses qui le disait). Pour que @Configurable puisse fonctionner, vous avez besoin de compiler le temps de tissage car les conseils sont donnés au moment de la construction de l'objet (les conseils du constructeur ne peuvent pas être réalisés avec Springs LTW).

4
Adam Gent

J'ai eu le même problème et je n'ai jamais réussi à faire fonctionner le code avec @Configurable et @Autowired. J'ai finalement décidé d'écrire moi-même un aspect qui traiterait les annotations @Configurable et @Autowired. Voici le code:

import Java.lang.annotation.Annotation;
import Java.lang.reflect.Field;

import org.Apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@SuppressWarnings( "rawtypes" )
@Aspect
public class AutoInjectDependecyAspect implements ApplicationContextAware {
    private static final Logger LOGGER = Logger.getLogger( AutoInjectDependecyAspect.class );

    private ApplicationContext  applicationContext = null;

    @Pointcut( "execution(  (@org.springframework.beans.factory.annotation.Configurable *).new())" )
    public void constructor() {
    }

    @Before( "constructor()" )
    public void injectAutoWiredFields( JoinPoint aPoint ) {
        Class theClass = aPoint.getTarget().getClass();
        try{
            Field[] theFields = theClass.getDeclaredFields();
            for ( Field thefield : theFields ) {
                for ( Annotation theAnnotation : thefield.getAnnotations() ) {
                    if ( theAnnotation instanceof Autowired ) {
                        // found a field annotated with 'AutoWired'
                        if ( !thefield.isAccessible() ) {
                            thefield.setAccessible( true );
                        }

                        Object theBean = applicationContext.getBean( thefield.getType() );
                        if ( theBean != null ) {
                            thefield.set( aPoint.getTarget(), theBean );
                        }
                    }
                }
            }
        } catch ( Exception e ) {
            LOGGER.error( "An error occured while trying to inject bean on mapper '" + aPoint.getTarget().getClass() + "'", e );
        }

    }

    @Override
    public void setApplicationContext( ApplicationContext aApplicationContext ) throws BeansException {
        applicationContext = aApplicationContext;
    }

}

Ensuite, dans votre contexte de printemps, définissez l’aspect de sorte que le contexte de printemps soit injecté dans l’aspect.

<bean class="[package_name].AutoInjectDependecyAspect" factory-method="aspectOf"/>
3
Guy Chauliac

essayez: @Configurable (autowire = Autowire.BY_TYPE). La valeur par défaut pour le câblage automatique est désactivée: <

1
MikePatel

J'ai eu un problème similaire que j'ai résolu aujourd'hui. L'important est d'activer le tissage au moment du chargement et de vous assurer que les classes aspectj appropriées sont chargées. Dans votre pom.xml, vous devez ajouter l'artefact aspectjweaver :

...
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.12</version>
</dependency>
....

Vous pouvez changer la version si vous en avez besoin. Ensuite, je voudrais aller la route xsd dans votre application-context.xml au lieu de la route DTD:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!--Scans the classpath for annotated components @Component, @Repository, @Service, and @Controller -->
    <context:component-scan base-package="your.base.package"/>
    <!--Activates @Required, @Autowired, @PostConstruct, @PreDestroy and @Resource -->
    <context:annotation-config/>
    <!--This switches on the load-time weaving for @Configurable annotated classes -->
    <context:load-time-weaver/>

</beans>
1
Jorge

Peut-être que l’utilisation de @Repository annotation pour DAO le fera.

1
duffymo

Il y a un bogue avec @Configurable et LTW. Si vous utilisez votre classe comme paramètre dans une méthode, le câblage automatique cesse de fonctionner . https://jira.spring.io/plugins/servlet/mobile#issue/SPR-8502

0
CodeMonster

Vérifiez également que votre version de AspectJ est à jour. J'ai perdu quelques heures à essayer de résoudre ce problème, en raison d'une ancienne version de Aspectjweaver.jar. J'ai mis à jour à 1.7.2 et tout a fonctionné comme un charme.

0
Alex