我正在尝试通过Spring的@Configurable注释w / @Resource在需要注入的字段上使用自动依赖注入。这涉及一些设置,比如将spring-agent.jar传递给我的JVM。有关详细信息see here。
它主要起作用。当我的Tomcat启动时,我看到AspectJ init消息,我的User对象自动获取FileService引用等。
问题是有时它不会发生。它似乎是完全随机的;有时我启动并且不会注入依赖项,有时它们是。我以前遇到过@Transactional在我的用户上的问题,因为它造成了冲突,我相信代理。我正在使用JPA,因此我的用户标有@Entity,所以我现在最好的猜测是这会产生冲突。我读过你不能自动代理代理。为了抵消冲突,我在网上发现了一些关于排除Hibernate(我的JPA impl)使用的 CGLIB 和 javassist 的注意事项。
线索:
我怎样才能弄清楚出了什么问题?是否有人使用@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" />
...
答案 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));
}
}