【问题标题】:Spring autowiring using @Configurable使用 @Configurable 的 Spring 自动装配
【发布时间】:2011-06-09 20:19:18
【问题描述】:

我正在考虑使用 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 的 spring 模块执行!框架,尽管其他自动装配的 bean 可以工作,所以我很确定这不是根本原因。我正在使用 Spring 3.0.5。

在其他代码中(实际上,在使用 Spring 注入到我的控制器中的 bean 方法中),我正在这样做:

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

这给了我一个 NullPointerException 尝试访问 Artist.save() 中的 artistDao。

知道我做错了什么吗?

马丁

【问题讨论】:

标签: java spring aspectj spring-aop


【解决方案1】:

您需要启用加载时编织(或其他类型的编织)才能使用@Configurable。确保您正确启用它,如 7.8.4 Load-time weaving with AspectJ in the Spring Framework 中所述。

【讨论】:

  • 这是错误的。您不需要 LTW 来支持 @Configurable。 Spring Roo 做他想做的事,它不使用 LTW。他的问题很可能是他没有使用 AspectJ 编译器和 Spring Aspects jar 编译代码。
  • @AdamGent 他确实说过 LTW(或其他类型的编织)。 . .对于 Configurable 注解,您需要加载时编织或编译时编织。
  • @JasperBlues 你是对的,LTW 应该工作。我永远无法让它在我的环境中工作,并认为我在 AspectJ in Action 中读到,使用 LTW 构建建议是不可能的。
  • 在 POJO 中使用 spring bean(Map) 作为依赖项在 employeeId 上说 employeeName 是个好主意吗,因为我一次获取 25 个 pojo 的列表,创建了 25 个对象并且每个 POJO 都有 Map 作为依赖项? employeeNameemployeeId 都是 POJO 的字段,但 employeeName 为 null 且 employeeId 已填充 OR 我应该在服务中设置 employeeName 迭代列表?
【解决方案2】:

我在使用 LTW 尝试将 bean 自动装配到我的域类中时遇到了 Tomcat 7 的问题。

http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-configurable-container 对 3.2.x 的文档进行了一些更新,表明可以使用 @EnableSpringConfigured 而不是 xml 配置。

所以我的域对象上有以下注释:

@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)

现在一切都好!

【讨论】:

    【解决方案3】:

    你应该看看 Spring Roo 是怎么做的,因为它完全符合你的要求。

    有很多事情会导致你拥有 NPE,但大多数时候它与没有使用 AspectJ 编译器 正确编译以及没有 Spring Aspects jar 有关strong> 在您的 AspectJ 库路径中(这与您的类路径不同)。

    首先尝试让它与 Maven 和 AspectJ 编译器插件一起工作。这就是我推荐 Spring Roo 的原因,因为它会生成一个具有正确设置的 POM 文件。

    我发现 @Configurable 并不能真正与 LTW 一起使用(尽管其中一个答案是这样说的)。您需要编译时编织才能使 @Configurable 工作,因为建议是在对象构造时发生的(构造函数建议不能使用 Springs LTW 完成)。

    【讨论】:

    • 加载时编织遇到了什么问题?我已经相当成功地使用了它。我使用 LTW 进行集成测试并使用编译时编织进行部署。 .这样我的测试覆盖率报告就出来了,但我的部署很健壮。
    • 因为@Configurable 允许您使用普通的Java 构造函数来实例化对象。该建议应用于 Objects 构造函数。 我认为您只能通过编译时编织来应用构造函数建议,但您似乎也可以使用 LTW 来实现。所以你是对的。你不能做的(这就是我感到困惑的原因)是用 LTW 制作 AspectJ ITD。
    • 啊,我明白了。 .不知道。
    • 这是一个如何使用 spring-aspects.jar 配置 AspectJ 编译时编织的示例,这解决了我的问题:stackoverflow.com/a/9512319/999422
    【解决方案4】:

    首先,启用 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 的 bean。默认情况下,您的 DAO 组件 bean 将被命名为“artistDaoImpl”。尝试将@Component 更改为@Component("artistDao") 并将@Autowired 应用于setter:

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

    【讨论】:

      【解决方案5】:

      我遇到了同样的问题,但从未设法让代码与@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"/>
      

      【讨论】:

        【解决方案6】:

        也许使用 DAO 的 @Repository 注释可以做到这一点。

        【讨论】:

        • 我同意。他应该改用@Repository
        • 怎么和@Configurable有关系?
        • 您能解释一下为什么会有所作为吗?
        • 区别在spring framework reference中有说明。
        【解决方案7】:

        尝试:@Configurable(autowire=Autowire.BY_TYPE)。自动连线默认为关闭:

        【讨论】:

          【解决方案8】:

          我今天解决了一个类似的问题。重要的是您需要启用加载时编织并确保加载了适当的 aspectj 类。在您的 pom.xml 中,您需要添加 aspectjweaver artifact:

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

          如果需要,您可以更改版本。然后,我会在你的 application-context.xml 中使用 xsd 路由,而不是 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>
          

          【讨论】:

            【解决方案9】:

            另外,请确认您的 AspectJ 版本是最新的。我浪费了几个小时试图完成这项工作,原因是 Aspectjweaver.jar 的旧版本。我更新到 1.7.2,一切都像魅力一样。

            【讨论】:

              【解决方案10】:

              @Configurable 和 LTW 存在错误。如果您在任何方法中使用您的类作为参数,则自动连线将停止工作。 https://jira.spring.io/plugins/servlet/mobile#issue/SPR-8502

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2017-07-06
                • 1970-01-01
                • 1970-01-01
                • 2011-07-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多