12

我正在尝试通过 Spring 的 @Configurable 注释和 @Resource 在需要注入的字段上使用自动依赖注入。这涉及一些设置,例如将 spring-agent.jar 传递给我的 JVM。有关完整的详细信息,请参见此处

它工作......主要是。当我的 Tomcat 启动时,我会看到 AspectJ 初始化消息,我的用户对象会自动获取 FileService 引用等。

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

线索:

  • 要么全有,要么全无。我的所有 @Configurable 实例都已注入或没有注入。
  • 从数据库重新加载(重新实例化)实体似乎没有帮助;它要么工作要么不工作。
  • 多次重新启动 Tomcat 也无法修复它。似乎再次掷骰子的唯一事情是重新部署。换句话说,如果我重新部署它可能会起作用。

我怎样才能弄清楚出了什么问题?有人在 JPA 中使用 @Configurable 吗?为什么实际上没有注入依赖项时,我的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>

应用程序上下文.xml

...

<context:spring-configured />

<context:load-time-weaver />

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

...
4

6 回答 6

3

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

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

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

于 2009-05-26T10:19:31.300 回答
3

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

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

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

于 2009-05-20T07:11:56.593 回答
2

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

于 2009-05-18T18:12:21.070 回答
1

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

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

于 2009-05-14T12:22:57.523 回答
1

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

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

于 2009-05-19T13:47:52.667 回答
0

我找到了原因;因为自定义 bean 没有顺序注册。如果在加载 Spring bean 之前使用了您的 bean org.springframework.context.config.internalBeanConfigurerAspect,那么 @Configurable autowire 将不起作用。我的解决方案如下:

@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));
    }
}
于 2020-08-20T04:26:47.810 回答