views:

1505

answers:

5

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"&gt;
<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" />

...
+1  A: 

When the injection doesn't work, for whatever reason, there is no way for code to do any dependency check, so there is now error thrown.

Otherwise I can't see anything here that would indicate the random failure. Can you extract a simplified example to check?

Jörn Zaefferer
Hi Jörn, thanks for the comment. I will try a simplified example when I get home. I need to update this question, because I've since switched from using the spring-agent.jar to a smaller-scoped TomcatInstrumentableClassLoader, see section 6.8.4.6.2. Tomcat of the Spring reference document. Oddly, this change appears to lower the frequency of the problem occuring, but doesn't fix it entirely.
rcampbell
Is there a reason why you are using @Configurable over @Component?
Rich Kroll
According to the documentation they'd both accomplish the same thing here. I use @Component for DI injection on Spring-managed beans, while I use @Config for DI injection on non-Spring-managed beans. So even though they'd both work, I use the different ones to hint as to the Spring status of a bean.
rcampbell
+1  A: 

Can't spot anything obvious, so just a suggestion - have you tried using compile-time weaving? Hopefully that would lead to consistent results at runtime, although it can be a bit more hassle during development.

James
+1  A: 

It sounds like your deploy process is suspect. Can you do a deployment that works, then copy that off to a directory. Then do another deployment until you get one that doesn't work. (In either order) Then finally, use a tool like beyond compare to compare the two deployment directory structures and files, and see if there are any differences.

Good luck, nothing like a seemingly random problem to kill some productivity.

Nathan Feger
That's a really good idea I never considered. I'll try it out tonight. BTW, it's funny that I started using @Configurable to INCREASE my productivity; before I was doing manual DI at certain points (like Factory, Repo, etc) and I was chasing down missed areas where the domain object is instantiated but never injected. Right now I'd say @Cofig is +1 for elegance, -1 for productivity compared to the orig solution
rcampbell
Yeah, I'm on a project now, where I'm using a set of new technologies. They rarely improve productivity until you make it over the hump. I haven't even tried to face spring's annotated properties. Too many new things, and the xml works everytime.
Nathan Feger
+1  A: 

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.

Thomas Einwaller
Thanks Thomas. I have some explanation of why I designed it this way here: http://stackoverflow.com/questions/694374/how-can-i-resolve-the-conflict-between-loose-coupling-dependency-injection-and-aBasically it comes down to domain-driven design influencing me. My domain models used to be just data; they mostly represented table rows. After reading this very good book, I decided to move more towards OOP/data + behavior, root entities being responsible for their child entities, etc. A good example of where I use this is to inject the Spring-wired Factory for a child entity into his parent.
rcampbell
which "very good book" do you mean? sounds interesting ...
Thomas Einwaller
The book is Domain-Driven Design by Eric Evans. I learned of it by a recommendation by Martin Fowler in Patterns of Enterprise Application Architecture which is another really great book.
rcampbell
+2  A: 

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.

jens