简体   繁体   English

使用@Configurable进行Spring自动装配

[英]Spring autowiring using @Configurable

I'm playing with the idea of using Spring @Configurable and @Autowire to inject DAOs into domain objects so that they do not need direct knowledge of the persistence layer. 我正在尝试使用Spring @Configurable@Autowire将DAO注入到域对象中,这样他们就不需要直接了解持久层了。

I'm trying to follow http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable , but my code seems to have no effect. 我正在尝试关注http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable ,但我的代码似乎没有效果。

Basically, I have: 基本上,我有:

@Configurable
public class Artist {

    @Autowired
    private ArtistDAO artistDao;

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

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

}

And: 和:

public interface ArtistDAO {

    public void save(Artist artist);

}

and

@Component
public class ArtistDAOImpl implements ArtistDAO {

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

}

In application-context.xml, I have: 在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>

Class path scanning and initialisation is performed by the spring module for Play! 类路径扫描和初始化由弹簧模块执行Play! framework, although other autowired beans work, so I'm pretty sure this is not the root cause. 框架,虽然其他autowired bean工作,所以我很确定这不是根本原因。 I'm using Spring 3.0.5. 我正在使用Spring 3.0.5。

In other code (inside a method in bean that's injected into my controller using Spring, in fact), I'm doing this: 在其他代码中(实际上在使用Spring注入我的控制器的bean中的方法内),我这样做:

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

This gives me a NullPointerException trying to access the artistDao in Artist.save(). 这给了我一个NullPointerException试图访问Artist.save()中的artistDao。

Any idea what I'm doing wrong? 知道我做错了什么吗?

Martin 马丁

You need to enable load-time weaving (or other kinds of weaving) in order to use @Configurable . 您需要启用加载时编织(或其他类型的编织)才能使用@Configurable Make sure you enabled it correctly, as described in 7.8.4 Load-time weaving with AspectJ in the Spring Framework . 确保正确启用它,如7.8.4 Spring Framework中使用AspectJ进行加载时编织中所述

I was having this problem with Tomcat 7 using LTW trying to autowire beans into my domain classes. 我使用LTW尝试将bean自动装入我的域类中时遇到Tomcat 7的这个问题。

There was some updates to the doc for 3.2.x at http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-configurable-container that revealed that one can use @EnableSpringConfigured instead of the xml configuration . http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-configurable-container上对3.2.x的文档进行了一些更新,揭示了可以使用@EnableSpringConfigured而不是xml配置。

So I have the following annotation on my Domain object: 所以我在我的Domain对象上有以下注释:

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

@EnableSpringConfigured is a substitue for @EnableSpringConfigured是一个替代品

<context:spring-configured />

and don't forget to add this to your context xml file: 并且不要忘记将它添加到您的上下文xml文件中:

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

Of course I needed to setup Tomcat for load time weaving first. 当然我需要首先设置Tomcat进行加载时间编织。

Also, I ran into a bug in 3.2.0 (null pointer) so I needed to upgrade to Spring 3.2.1 ( https://jira.springsource.org/browse/SPR-10108 ) 另外,我遇​​到了3.2.0中的错误(空指针)所以我需要升级到Spring 3.2.1( https://jira.springsource.org/browse/SPR-10108

All is well now! 一切都很好!

First, enable Spring debug logging. 首先,启用Spring调试日志记录。 I use Log4j to do it. 我使用Log4j来做到这一点。 I've created a logger like so (with Log4j xml configuration so I can use RollingFileAppender): 我已经创建了一个这样的记录器(使用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>

This will allow you to see what Spring is doing and when. 这将让您了解Spring正在做什么以及何时做什么。

Second, you have ArtistDAO autowired but I don't see where you have a bean named ArtistDAO. 其次,您已经自动装配了ArtistDAO,但我看不到您在哪里有一个名为ArtistDAO的bean。 Your DAO component bean will be named "artistDaoImpl" by default. 默认情况下,您的DAO组件bean将命名为“artistDaoImpl”。 Try changing @Component to @Component("artistDao") and applying @Autowired to the setter instead: 尝试将@Component更改为@Component("artistDao")并将@Autowired应用于setter:

private ArtistDAO artistDao;

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

You should just look how Spring Roo does it since it does exactly what you want to do. 您应该看看Spring Roo是如何做到的,因为它完全符合您的要求。

There are lots of things that can cause the NPE your having but most of the time it has to do with not compiling properly with AspectJ compiler and not having the Spring Aspects jar in your AspectJ lib path (this is different than your classpath). 有很多东西可以导致你的NPE,但大部分时间都与未使用AspectJ编译器正确编译并且在AspectJ lib路径中没有Spring Aspects jar (这与你的类路径不同)有关。

First just try to get it to work with Maven and the AspectJ compiler plugin. 首先尝试使用Maven和AspectJ编译器插件。 Thats why I recommend Spring Roo as it will generate a POM file with the correct setup. 这就是为什么我推荐Spring Roo,因为它会生成一个具有正确设置的POM文件。

I have found @Configurable does not really work with LTW (despite one of the answers saying so). 我发现@Configurable并不适用于LTW(尽管其中一个答案是这样说的)。 You need compile time weaving for @Configurable to work because the advice is happening at object construction time (constructor advice cannot be done with Springs LTW). 您需要编译时编织@Configurable才能工作,因为建议是在对象构造时发生的(构造函数建议无法使用Springs LTW完成)。

I had the same problem and never managed to get the code working with @Configurable and @Autowired. 我有同样的问题,从来没有设法使用@Configurable和@Autowired的代码。 I finally decided to write an aspect myself which would handle the @Configurable and @Autowired annotations. 我最终决定自己编写一个方面来处理@Configurable和@Autowired注释。 Here is the 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;
    }

}

Next in your spring context define the aspect so that the springcontext will be injected into the aspect 接下来在spring上下文中定义方面,以便将springcontext注入到方面中

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

I had a similar issue that I resolved today. 我有一个类似的问题,我今天解决了。 The important thing is that you need to enable load-time weaving and make sure the appropriate aspectj classes are loaded. 重要的是您需要启用加载时编织并确保加载适当的aspectj类。 In your pom.xml you need to add the aspectjweaver artifact : 在您的pom.xml中,您需要添加aspectjweaver工件

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

You can change the version if you need to. 如果需要,您可以更改版本。 Then, I would go the xsd route in you application-context.xml instead of the DTD route: 然后,我将在您的application-context.xml而不是DTD路由中使用xsd路由:

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

也许使用DAO的@Repository注释就可以了。

try : @Configurable(autowire=Autowire.BY_TYPE). 尝试:@Configurable(autowire = Autowire.BY_TYPE)。 Autowired defaults to off :< 自动默认为关闭:<

Also, please verify that your version of AspectJ is current. 另外,请确认您的AspectJ版本是最新版本。 I wasted a few hours trying to make this work, and the cause was an old version of Aspectjweaver.jar. 我浪费了几个小时试图完成这项工作,原因是Aspectjweaver.jar的旧版本。 I updated to 1.7.2 and everything worked like a charm. 我更新到1.7.2,一切都像魅力一样。

There is a bug with @Configurable and LTW. @Configurable和LTW有一个错误。 If you use your class as a parameter in any method the auto wiring stops working. 如果在任何方法中将类用作参数,则自动接线将停止工作。 https://jira.spring.io/plugins/servlet/mobile#issue/SPR-8502 https://jira.spring.io/plugins/servlet/mobile#issue/SPR-8502

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM