Java 使用@Configurable的Spring自动布线

Java 使用@Configurable的Spring自动布线,java,spring,aspectj,spring-aop,Java,Spring,Aspectj,Spring Aop,我正在考虑使用Spring@Configurable和@Autowire将DAO注入域对象,这样它们就不需要直接了解持久层 我试着跟着,但我的代码似乎没有效果 基本上,我有: @Configurable public class Artist { @Autowired private ArtistDAO artistDao; public void setArtistDao(ArtistDAO artistDao) { this.artistDao =

我正在考虑使用Spring
@Configurable
@Autowire
将DAO注入域对象,这样它们就不需要直接了解持久层

我试着跟着,但我的代码似乎没有效果

基本上,我有:

@Configurable
public class Artist {

    @Autowired
    private ArtistDAO artistDao;

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

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

}
以及:

在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>
这给了我一个试图访问Artist.save()中artistDao的NullPointerException

知道我做错了什么吗


Martin

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

为了使用
@可配置的
,您需要启用加载时编织(或其他类型的编织)。确保已正确启用它,如中所述。

首先,启用Spring调试日志记录。我用Log4j来做。我创建了一个类似这样的记录器(使用log4jxml配置,这样我就可以使用RollingFileAppender):

你应该看看它是怎么做的,因为它正是你想做的

有很多事情可能会导致NPE出现,但大多数情况下,它与未使用AspectJ编译器正确编译有关,并且在AspectJ库路径中没有Spring Aspects jar(这与类路径不同)

首先,试着让它与Maven和AspectJ编译器插件一起工作。这就是为什么我推荐SpringRoo,因为它将生成具有正确设置的POM文件


我发现@Configurable实际上不适用于LTW(尽管有一个答案这么说)。您需要编译时编织@Configurable才能工作,因为建议发生在对象构造时(构造函数建议无法使用Springs LTW完成)。

请尝试:@Configurable(autowire=autowire.BY_TYPE)。自动连线默认为关闭:我今天解决了一个类似的问题。重要的是,您需要启用加载时编织,并确保加载了适当的aspectj类。在pom.xml中,您需要添加:

。。。
org.aspectj
aspectjweaver
1.6.12
....
如果需要,可以更改版本。然后,我将使用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>

Tomcat7使用LTW试图将bean自动连接到我的域类时,我遇到了这个问题

当时对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(空指针)中遇到了一个bug,所以我需要升级到Spring 3.2.1()


现在一切都好了

另外,请验证您的AspectJ版本是否为最新版本。我浪费了几个小时试图让它工作,原因是Aspectjweaver.jar的旧版本。我更新到了1.7.2,一切都很顺利。

我也遇到了同样的问题,从来没有让代码与@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"/>

存在@Configurable和LTW的错误。如果在任何方法中将类用作参数,则自动关联将停止工作。

我同意。他应该使用@Repository,而不是它与
@Configurable
有什么关系?你能解释一下为什么会有不同吗?在.related;)中解释了不同之处你能在你的
@Configurable
标记的类艺术家中使用
@Value
注释吗?这是错误的。您不需要LTW来支持@Configurable。SpringRoo做他想做的事情,它不使用LTW。他的问题很可能是他没有使用AspectJ编译器和Spring方面jar编译代码。@AdamGent他说的是LTW(或其他类型的编织)。您需要为可配置的注释进行加载时编织或编译时编织。@Jasperblus您认为LTW应该工作是正确的。我无法让它在我的环境中工作,我认为我在AspectJ中读到,用LTW构建建议是不可能的。在POJO中使用spring bean(Map)作为依赖项,在
employeeId
上获取say
employeeName
,这是一个好主意吗,因为我一次获取25个POJO的列表,创建了25个对象,每个POJO都有映射作为依赖项?
employeeName
employeeId
都是POJO的字段,但是
employeeName
为空,并且
employeeId
已填充,或者我应该在服务中设置
employeeName
以迭代列表。加载时编织有什么问题?我用得很成功。我使用LTW进行集成测试,并使用编译时编织进行部署。通过这种方式,我的测试覆盖率报告很好,但是我的部署很健壮。因为
@Configurable
允许您使用普通的Java构造函数来实例化对象。建议应用于对象构造函数。我认为您只能将构造函数建议应用于编译时编织,但似乎您也可以使用LTW。所以你是对的。你不能做的(这就是为什么我感到困惑)是用LTW制作AspectJ ITD。我不知道。下面是一个如何使用spring-aspects.jar配置AspectJ编译时编织的示例,这解决了我的问题:
<?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>
@Configurable(preConstruction=true,dependencyCheck=true,autowire=Autowire.BY_TYPE)
@EnableSpringConfigured
<context:spring-configured />
<context:load-time-weaver weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" aspectj-weaving="on"/>
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;
    }

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