【问题标题】:Why does Spring's @Configurable sometimes work and sometimes not?为什么 Spring 的 @Configurable 有时有效,有时无效?
【发布时间】:2010-10-24 03:30:18
【问题描述】:

我正在尝试通过 Spring 的 @Configurable 注释和 @Resource 在需要注入的字段上使用自动依赖注入。这涉及一些设置,例如将 spring-agent.jar 传递给我的 JVM。欲知详情see here。

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

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

线索:

  • 要么全有,要么全无。我的所有@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>

applicationContext.xml

...

<context:spring-configured />

<context:load-time-weaver />

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

...

【问题讨论】:

  • 我也很长一段时间以来一直在使用@Configurable 和@Transactional 遇到这些类型的问题。我认为这与初始化弹簧上下文之前加载类的类加载器有关。请参阅此线程:forum.springsource.org/showthread.php?t=68406。这个错误的偶发性非常烦人。
  • 鉴于 Spring 的所有更改,现在是否有更好的解决方案来解决这个问题?

标签: java spring jpa dependency-injection annotations


【解决方案1】:

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

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

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

【讨论】:

  • 谢谢托马斯。我对我为什么在这里设计它有一些解释:stackoverflow.com/questions/694374/… 基本上它归结为影响我的领域驱动设计。我的领域模型曾经只是数据;它们主要代表表格行。读完这本非常好的书后,我决定更多地转向 OOP/数据 + 行为、根实体负责其子实体等。我使用它的一个很好的例子是为子实体注入 Spring-wired Factory进入他的父母。
  • 这本书是 Eric Evans 的领域驱动设计。我是通过 Martin Fowler 在 Patterns of Enterprise Application Architecture 中的推荐了解到的,这是另一本非常棒的书。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 我还建议使用编译时编织进行生产部署。还可以防止与忘记设置 aspect-j weaver JDK 参数相关的问题。 . . .编译时编织非常适合集成测试。
    【解决方案4】:

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

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

    【讨论】:

    • 嗨,乔恩,感谢您的评论。回家后我会尝试一个简化的例子。我需要更新这个问题,因为我已经从使用 spring-agent.jar 切换到更小范围的 TomcatInstrumentableClassLoader,请参阅第 6.8.4.6.2 节。 Tomcat 的 Spring 参考文档。奇怪的是,此更改似乎降低了问题发生的频率,但并不能完全解决问题。
    • 您使用@Configurable 而非@Component 是否有原因?
    • 根据文档,他们都会在这里完成同样的事情。我在 Spring 管理的 bean 上使用 @Component 进行 DI 注入,而在非 Spring 管理的 bean 上使用 @Config 进行 DI 注入。因此,即使它们都可以工作,我还是使用不同的来暗示 bean 的 Spring 状态。
    【解决方案5】:

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

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

    【讨论】:

    • 这是一个我从未考虑过的好主意。我今晚试试。顺便说一句,有趣的是我开始使用 @Configurable 来提高我的工作效率;在我在某些点(如工厂、回购等)进行手动 DI 之前,我正在寻找域对象被实例化但从未注入的遗漏区域。现在我会说@Cofig 与 orig 解决方案相比,优雅 +1,生产力 -1
    • 是的,我现在正在进行一个项目,我正在使用一组新技术。在您克服困难之前,它们很少会提高生产力。我什至没有尝试面对 spring 的注释属性。新东西太多,xml每次都有效。
    【解决方案6】:

    我找到了原因;因为自定义 bean 没有顺序注册。如果在加载 Spring bean org.springframework.context.config.internalBeanConfigurerAspect 之前使用了您的 bean,则 @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));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-18
      • 1970-01-01
      • 2019-05-30
      • 2018-06-15
      • 2011-02-13
      • 1970-01-01
      • 2012-07-07
      • 1970-01-01
      相关资源
      最近更新 更多