为什么Spring的@Configurable有时可以工作,有时候不工作?

时间:2009-05-06 09:10:31

标签: java spring jpa dependency-injection annotations

我正在尝试通过Spring的@Configurable注释w / @Resource在需要注入的字段上使用自动依赖注入。这涉及一些设置,比如将spring-agent.jar传递给我的JVM。有关详细信息see here

它主要起作用。当我的Tomcat启动时,我看到AspectJ init消息,我的User对象自动获取FileService引用等。

问题是有时它不会发生。它似乎是完全随机的;有时我启动并且不会注入依赖项,有时它们是。我以前遇到过@Transactional在我的用户上的问题,因为它造成了冲突,我相信代理。我正在使用JPA,因此我的用户标有@Entity,所以我现在最好的猜测是这会产生冲突。我读过你不能自动代理代理。为了抵消冲突,我在网上发现了一些关于排除Hibernate(我的JPA impl)使用的 CGLIB javassist 的注意事项。

线索:

  • 全有或全无。我的所有@Configurable实例都已注入,或者都没有注入。
  • 从数据库重新加载(重新实例化)实体似乎没有帮助;无论是否工作。
  • 重启Tomcat任何时间也都不会修复它。唯一似乎再次掷骰子的是重新部署。换句话说,如果我重新部署它可能会有效。

我怎样才能弄清楚出了什么问题?是否有人使用@Configurable和JPA?为什么我的 dependencyCheck = true 在没有实际注入依赖项时抛出错误?

实体

@Entity
@Configurable(dependencyCheck = true)
@NamedQueries( { @NamedQuery(name = "User.findAll", query = "SELECT user FROM User user"),
    @NamedQuery(name = "User.findByEmail", query = "SELECT user FROM User user WHERE user.email = :email") })
public abstract class User extends BaseModel {

private static final long serialVersionUID = 7881431079061750040L;

@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;

@Column(unique = true, nullable = false)
private String email;

@Basic(optional = false)
private String password;

@Resource
private transient UserEmailer userEmailer;

@Resource
private transient FileService fileService;

...

aop.xml文件

<!DOCTYPE aspectj PUBLIC
    "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver options="-verbose">
        <include within="com.myapp.domain..*" />
        <exclude within="*..*CGLIB*" />
        <exclude within="*..*javassist*" />
    </weaver>
    <aspects>
        <aspect name="org.springframework.beans.factory.aspectj.AbstractInterfaceDrivenDependencyInjectionAspect" />
    </aspects>
</aspectj>

的applicationContext.xml

...

<context:spring-configured />

<context:load-time-weaver />

<context:component-scan base-package="com.myapp" />

...

6 个答案:

答案 0 :(得分:3)

首先,我必须说,将资源,服务或其他bean作为依赖项注入数据模型类可能不是一个好主意。但这是一个设计问题。

对于@Configurable的用法,我在从Spring上下文实例化对象的情况下使用它 - 比如Web应用程序,过滤器或servlet中的自定义标记。我尝试使用它们的第一种方法是像你一样编织加载时间。这工作得很好,但它有一些缺点,如热代码部署,而调试不再工作。

我也确实遇到了你描述的问题,因此我决定从加载时间编织切换到编译时。因此我在Eclipse中安装了AJDT plugin并使用了Spring的aspecjt支持。这解决了我的问题。

答案 1 :(得分:3)

对我而言,这听起来像是Spring中一个众所周知的错误:http://jira.springframework.org/browse/SPR-5401

您是否尝试在多个应用程序上下文中使用Configurable?在这种情况下,只有其中一个将受到依赖注入。哪一个获胜取决于哪个应用程序上下文是最后一个要加载的。

解决方案?没有:-(没有计划解决这个问题。这至少是SpringSource家伙在4月份在德国举行的JAX大会上所说的。

答案 2 :(得分:2)

无法发现任何明显的东西,所以只是一个建议 - 您是否尝试过使用编译时编织?希望这会在运行时产生一致的结果,尽管在开发过程中可能会有点麻烦。

答案 3 :(得分:1)

当注入不起作用时,无论出于何种原因,代码都无法进行任何依赖性检查,因此现在出现错误。

否则我在这里看不到任何表示随机失败的内容。你能提取一个简化的例子来检查吗?

答案 4 :(得分:1)

听起来您的部署过程是可疑的。您可以进行有效的部署,然后将其复制到目录中。然后再进行另一次部署,直到找到一个不起作用的部署。 (按任意顺序)然后最后,使用一个比较无比的工具来比较两个部署目录结构和文件,看看是否有任何差异。

祝你好运,没有什么比看似随机的问题可以扼杀一些生产力。

答案 5 :(得分:0)

我找到了原因;因为自定义bean无需注册即可注册。如果在加载Spring bean org.springframework.context.config.internalBeanConfigurerAspect之前使用过您的bean,那么@Configurable自动装配将不起作用。 我的解决方案如下:

@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
@EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED)
@EnableSpringConfigured
@ComponentScan(basePackages = "zhibo")
public class AppConfig extends WebMvcConfigurationSupport {

    @Bean
    public DemoProcessor demoProcessor() {
        return new DemoProcessor();
    }
}


public class DemoProcessor implements BeanPostProcessor,BeanDefinitionRegistryPostProcessor {

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
    Stream.of(beanDefinitionNames).forEach(System.err::println);
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    String[] beanDefinitionNames = registry.getBeanDefinitionNames();
    Map<String, BeanDefinition> zhiboBeans = new LinkedHashMap<>();
    //1. remove your beans. let spring's beans go ahead
    Stream.of(beanDefinitionNames).forEach(beanName->{
        BeanDefinition bd = registry.getBeanDefinition(beanName);
        if(bd.getBeanClassName()!=null && bd.getBeanClassName().startsWith("zhibo.")) {
            registry.removeBeanDefinition(beanName);
            zhiboBeans.put(beanName, bd);
        }
    });
    //2. register your beans again
    zhiboBeans.forEach((k,v)->registry.registerBeanDefinition(k, v));
    }
}