Пружинная автопроводка с использованием @Configurable - PullRequest
32 голосов
/ 16 января 2011

Я играю с идеей использования Spring @Configurable и @Autowire для внедрения DAO в доменные объекты, чтобы они не нуждались в непосредственном знании уровня персистентности.

Я пытаюсьследуйте http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable,, но мой код, кажется, не имеет никакого эффекта.

В основном у меня есть:

@Configurable
public class Artist {

    @Autowired
    private ArtistDAO artistDao;

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

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

}

И:

public interface ArtistDAO {

    public void save(Artist artist);

}

и

@Component
public class ArtistDAOImpl implements ArtistDAO {

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

}

В application-context.xml у меня есть:

<?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>

Сканирование и инициализация пути класса выполняется пружинным модулем для Play!фреймворк, хотя работают и другие bean-компоненты, поэтому я уверен, что это не основная причина.Я использую Spring 3.0.5.

В другом коде (фактически внутри метода в bean-компоненте, который внедряется в мой контроллер с помощью Spring), я делаю это:

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

Это дает мне исключение NullPointerException при попытке доступа к artistDao в Artist.save ().

Есть идеи, что я делаю неправильно?

Martin

Ответы [ 10 ]

9 голосов
/ 16 января 2011

Вам необходимо включить ткачество во время загрузки (или другие виды ткачества), чтобы использовать @Configurable. Убедитесь, что вы включили его правильно, как описано в 7.8.4 Плетение времени загрузки с AspectJ в Spring Framework .

6 голосов
/ 14 февраля 2013

У меня была эта проблема с Tomcat 7, когда LTW пытался автоматически связывать bean-компоненты с классами моего домена.

Были некоторые обновления документа для 3.2.x на http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-configurable-container, которые показали, что можно использовать @EnableSpringConfigured вместо конфигурации xml.

Итак, у меня есть следующая аннотация на моем объекте Domain:

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

@ EnableSpringConfigured является заменой для

<context:spring-configured />

и не забудьте добавить это в свой xml-файл контекста:

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

Конечно, мне нужно было сначала настроить Tomcat для определения времени загрузки.

Кроме того, я столкнулся с ошибкой в ​​3.2.0 (нулевой указатель), поэтому мне нужно было обновить до Spring 3.2.1 (https://jira.springsource.org/browse/SPR-10108)

)

Теперь все хорошо!

4 голосов
/ 06 сентября 2011

Вы должны просто посмотреть, как Spring Roo делает это, поскольку он делает именно то, что вы хотите.

Есть много вещей, которые могут вызвать NPE, но большинство из нихвремя это связано с неправильной компиляцией с компилятором AspectJ и отсутствием Spring Aspect jar в вашем пути к библиотеке AspectJ (это отличается от вашего classpath).* Сначала попробуйте заставить его работать с Maven и плагином компилятора AspectJ.Вот почему я рекомендую Spring Roo, поскольку он сгенерирует POM-файл с правильной настройкой.

Я обнаружил, что @Configurable на самом деле не работает с LTW (несмотря на один из ответов, говорящих так).Для работы @Configurable требуется время компиляции, потому что рекомендация происходит во время создания объекта (рекомендация для конструктора не может быть выполнена с помощью Springs LTW).

4 голосов
/ 26 апреля 2011

Сначала включите ведение журнала отладки Spring.Я использую Log4j, чтобы сделать это.Я создал такой регистратор (с конфигурацией Log4j xml, чтобы я мог использовать 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>

Это позволит вам увидеть, что и когда делает Spring.

Во-вторых,у вас есть ArtistDAO с автоматическим подключением, но я не вижу, где у вас есть бин с именем ArtistDAO.Ваш компонент компонента DAO по умолчанию будет называться "artistDaoImpl".Попробуйте изменить @Component на @Component("artistDao") и вместо этого применить @Autowired к сеттеру:

private ArtistDAO artistDao;

@Autowired
public void setArtistDao(ArtistDAO artistDao) 
{
  this.artistDao = artistDao;
}
3 голосов
/ 20 июня 2014

У меня была такая же проблема, и мне так и не удалось заставить код работать с @Configurable и @Autowired. Наконец я решил написать аспект, который бы обрабатывал аннотации @Configurable и @Autowired. Вот код:

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;
    }

}

Далее в контексте Spring определите аспект, чтобы Springcontext был введен в аспект

<bean class="[package_name].AutoInjectDependecyAspect" factory-method="aspectOf"/>
1 голос
/ 07 августа 2012

У меня была похожая проблема, которую я решил сегодня.Важно то, что вам нужно включить время загрузки и убедиться, что загружены соответствующие классы aspectj.В ваш pom.xml вам нужно добавить aspectjweaver артефакт :

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

. При необходимости вы можете изменить версию.Затем я бы пошел по маршруту xsd в вашем application-context.xml вместо маршрута 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 голос
/ 12 января 2012

try: @Configurable (autowire = Autowire.BY_TYPE). По умолчанию автоматическое подключение отключено: <</p>

1 голос
/ 16 января 2011

Возможно, для этого подойдет аннотация @ Repository для DAO.

0 голосов
/ 26 апреля 2016

Ошибка @Configurable и LTW.Если вы используете свой класс в качестве параметра в любом методе, то автоматическое подключение перестает работать.https://jira.spring.io/plugins/servlet/mobile#issue/SPR-8502

0 голосов
/ 19 июня 2013

Также, пожалуйста, убедитесь, что ваша версия AspectJ является текущей. Я потратил несколько часов, пытаясь заставить это работать, и причиной стала старая версия Aspectjweaver.jar. Я обновился до 1.7.2 и все заработало как шарм.

...