Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Spring's @Configurable sometimes work and sometimes not?

I'm trying to use automatic dependency injection via Spring's @Configurable annotation w/ @Resource on the fields needing injection. This involved some setup, like passing spring-agent.jar to my JVM. For the full details see here.

It works... mostly. When my Tomcat is booting up, I see the AspectJ init messages, my User objects automatically get FileService references, etc.

The problem is that sometimes it just doesn't happen. It appears to be completely random; sometimes I boot up and the dependencies are not injected, sometimes they are. I previously had trouble with @Transactional being on my User because it created a conflict, I believe with proxies. I am using JPA, so my User is marked with @Entity, so my best guess right now is that this is creating a conflict. I've read you can't auto proxy a proxy. To offset the conflict, I followed some notes I found online about excluding CGLIB and javassist which Hibernate (my JPA impl) uses.

Clues:

  • It's all or nothing. All of my @Configurable instances have been injected or none of them.
  • Reloading (reinstantiating) the Entity from the DB doesn't appear to help; it's either working or not.
  • Rebooting Tomcat any number of time also won't even fix it. The only thing that appears to roll the dice again is a redeploy. In other words, if I redeploy it may work.

How can I figure out what is going wrong? Is anyone using @Configurable with JPA? Why isn't my dependencyCheck = true throwing an error when dependencies are not actually injected?

Entity

@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" />

...
like image 381
Robert Campbell Avatar asked May 06 '09 09:05

Robert Campbell


2 Answers

First I have to say that it is probably not a good idea to have resources, services or other beans injected into data model classes as dependencies. But that is a question of design.

To the usage of @Configurable I used it in cases where objects are instantiated from outside the Spring context - like custom tags in web applications, filters or servlets. The first way I tried to use them was by load time weaving like you do. That worked quite well but it had some drawbacks like hot code deployment while debugging did not work anymore.

I did also experience exactly the problem you describe and so I decided to switch from load time weaving to compile time. Therefor I installed the AJDT plugin in Eclipse and used the aspecjt support of Spring. That solved my problems.

like image 89
Thomas Einwaller Avatar answered Sep 28 '22 04:09

Thomas Einwaller


To me it sounds like an occurence of a well known bug in Spring: http://jira.springframework.org/browse/SPR-5401.

Can it be that you are trying to use Configurable in several application contexts? In this case only one of them will be subject to dependency injection. Which one wins depends on which application context is the last one to be loaded.

Solution? None :-( There are no plans to fix this issue. That is at least what the SpringSource guy told at the JAX conference in Germany in April.

like image 30
jens Avatar answered Sep 28 '22 04:09

jens