Some powerful
Scala programming language features like
closures,
pattern matching, and
lazy vals don't work well with
dependency injection frameworks like
javax.inject aka
CDI. This is due to stricter class structure requirements to enable proxying.
For example, this "innocent" code will not work:
@Inject private var fbPhotoImporterFactory: Instance[FacebookPhotoImporter] = _ @Produces @Named("facebookPhotoImporter") private var fbPhotoImporter: ActorRef = _ @PostConstruct def init() { logger.debug("Starting FacebookPhotoImporter actor") fbPhotoImporter = Actor.actorOf(fbPhotoImporterFactory.get()) fbPhotoImporter.start() }It will throw:
Caused by: org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001437 Normal scoped bean class com.satukancinta.web.Persistence is not proxyable because the type is final or it contains a final method public final javax.enterprise.inject.Instance com.satukancinta.web.Persistence.com$satukancinta$web$Persistence$$fbPhotoImporterFactory() - Managed Bean [class com.satukancinta.web.Persistence] with qualifiers [@Any @Default]. at org.jboss.weld.util.Proxies.getUnproxyableClassException(Proxies.java:225) at org.jboss.weld.util.Proxies.getUnproxyableTypeException(Proxies.java:178) at org.jboss.weld.util.Proxies.getUnproxyableTypesExceptionInt(Proxies.java:193) at org.jboss.weld.util.Proxies.getUnproxyableTypesException(Proxies.java:167) at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:110) at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:126) at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:345) at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:330) at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:366) at org.jboss.as.weld.WeldContainer.start(WeldContainer.java:82) at org.jboss.as.weld.services.WeldService.start(WeldService.java:89) ... 5 more As you can see, my use code isn't exactly "edge cases".
It's actually a pretty common use case: create a Akka actor and pass a factory function to it, as a closure.
The code above doesn't look like it's using a closure, but it actually is when written like this: (same functionality, but still breaks CDI)
fbPhotoImporter = Actor.actorOf { fbPhotoImporterFactory.get() } I can see why CDI has a strict requirement, and I can also understand why Scala implements it the way it is (Scala developers definitely already has a lot of problems working around powerful Scala features into a very restrictive JVM bytecode requirements). This is the price we pay for having a somewhat inferior language (Java, please don't get offended) in the first place.
But I as an application developer want to have a quick fix for this issue. Re-coding the class in plain Java is one option, but it turns I don't need to. There is a workaround, by creating a helper method then using it:
@Inject private var fbPhotoImporterFactory: Instance[FacebookPhotoImporter] = _ @Produces @Named("facebookPhotoImporter") private var fbPhotoImporter: ActorRef = _ def createFbPhotoImporter() = fbPhotoImporterFactory.get() @PostConstruct def init() { logger.debug("Starting FacebookPhotoImporter actor") fbPhotoImporter = Actor.actorOf(createFbPhotoImporter) fbPhotoImporter.start() } Now Scala is happy and CDI is also happy. Yes it's a bit more verbose but not too bad. And I guess the code is now somewhat more understandable for Java guys. :)
Tip: To
learn more about
Scala programming, I recommend
Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition.