I'm writing a Grails plugin that defines a Spring bean in the plugin descriptor
def doWithSpring = {
myBean(MyBean)
}
I need to get a reference to this bean from another class in the plugin.
class Something {
def doIt() {
// I need to get a reference to myBean here. Is this the best way?
MyBean myBean = ApplicationHolder.application.mainContext.getBean('myBean')
}
}
Something is a class defined in src/groovy within the same plugin as the bean, but Something is not itself a spring bean. In Grails 1.3.7 is there a better way of achieving this than that shown above? I'm looking for a better way because I know the *Holder classes are deprecated in Grails 2.0
In Grails 2.x exists the Holders utility class to get the grailsApplication and the applicationContext.
In 1.3.7 I think the option is create your own holder, as described here.
Related
It seems that the #PostConstruct method is not called when a bean is added to the context using a Kotlin BeanDefinitionDsl.
This happened to me in my own project but to create a simple way to reproduce it, here's what I did.
I forked the Spring example of using the Kotlin DSL https://github.com/sdeleuze/spring-kotlin-functional
I added a #PostConstruct to the UserHandler class. (More details below.)
I pushed the result here: https://github.com/benjishults/spring-kotlin-functional
So all you need to do is fork my repo and do a gradle run.
My questions are:
Shouldn't I expect that #PostConstruct to be called since I'm bringing the class in as a bean?
Am I missing a step?
Is this a Spring bug?
If you don't want to pull my repo, here are more details about what I did. I added this to the UserHandler class:
#PostConstruct
fun afterPropertiesSet() {
System.out.println("AFTER PROPERTIES SET CALLED")
}
along with the import and the Gradle dependency.
The UserHandler bean is pulled into the context using a call to the bean method within a beans DSL like so:
fun beans() = beans {
bean<UserHandler>()
// ...
}
and this is brought into the context with:
beans().initialize(context)
GenericApplicationContext instantiated in the Application class does not support out of the box #PostContruct. To make it works, you should use AnnotationConfigApplicationContext instead and remove the exclude for spring-aop in the Gradle build.
I want to be able to include Services in my Groovy Classes in /src/groovy
I found a solution with :
myBean(MyBean) { bean ->
bean.autowire = 'byName'
}
But I dont want to make this entry in the resources.groovy for all Class, so is there a Solution to Autowire all classes in a specific folder?
I'm using grails 2.4.3
This seems to be similar to this question: Grails 2.x service injection in Groovy/src
What we use and is proposed there is to get the service via the application context:
import grails.util.Holders
...
def myService = Holders.grailsApplication.mainContext.getBean 'myService'
It's not completely auto-wired, but seems to be the best way to get services into src/groovy.
Edit: also works for Grails 3
You can make a class com.example.MyClass in src/groovy a Spring bean by adding the following to BuildConfig.groovy
grails.spring.bean.packages = ['com.example']
and annotating the class with #Component, e.g.
#Component
class MyClass {
#Value('${conf.apiVersion}')
String apiVersion
#Autowired
SomeService someService
}
As shown above, you can dependency-inject the class with the usual Spring annotations such as #Value and #Autowired. I find this a much more convenient way to register a Spring bean than modifying resources.groovy.
If I have a Spring configuration class (i.e. a class annotated with #Configuration) can I use constructor injection ?
As it stands if I add one I get a no default constructor message, and if I add a default constructor it uses that rather than the overloaded one, which doesn't really help.
There is a bug report about this limitation. It will be fixed with Spring 4.3.
Please note that another bug report (not fixed yet today fixed in 4.3-RC1) report a problem when using this very new feature and injecting generics in constructor of a #Configuration class.
In Spring 4.3, you can use org.springframework.beans.factory.ObjectProvider in #Configuration annotated class constructors to inject beans. for example:
#Configuration
public class SimpleBean {
private final InnerBean prop1;
public Simple Bean(ObjectProvider<InnerBean> innerBeanProvider) {
prop1 = innerBeanProvider.getObject();
}
}
I have several services implementing a common interface and I want to be able to choose one of them to inject into other services when my application starts up.
I have tried referencing the service implementation from resources.groovy as shown below but then Spring makes a new instance of the selected service and doesn't autowire its dependencies.
How can I get this solution to work? Or is there another way?
class MyService {
Repository repository
interface Repository {
void save(...)
}
}
class MySqlRepositoryService implements MyService.Repository { ... }
class FileRepositoryService implements MyService.Repository { ... }
resources.groovy:
beans = {
...
repository(FileRepositoryService) { }
}
It's of course possible to retrieve the reference to service from hand-built factory, but in my opinion, the approach you've taken is the best one. I use it myself, because it gathers all the information on configuration phase of the application in one place, so it's easier to track down which implementation is used.
The pitfall with autowiring that you've encountered can be explained very easily. All the classes put in grails-app/services are automatically configured by Grails as Spring singleton beans with autowiring by name. So the bean definition you've placed in grails-app/conf/resources.groovy creates another bean, but without the defaults imposed by Grails conventions.
The most straightforward solution is to put the implementation in src/groovy to avoid duplication of beans and use the following syntax to turn on the autowiring:
beans = {
repository(FileRepositoryService) { bean ->
bean.autowire = 'byName'
}
}
I would like to know if it's possible to use Spring to resolve the dependencies of an object created manually in my program. Take a look at the following class:
public class TestClass {
private MyDependency md;
public TestClass() {
}
...
public void methodThaUsesMyDependency() {
...
md.someMethod();
...
}
}
This TestClass is not a spring bean, but needs MyDependency, that is a spring bean. Is there some way I can inject this dependency through Spring, even if I instantiate TestClass with a new operator inside my code?
Thanks
Edit: The method I'm describing in my original answer below is the general way to accomplish DI external of the container. For your specific need - testing - I agree with DJ's answer. It's much more appropriate to use Spring's test support, for example:
#Test
#ContextConfiguration(locations = { "classpath*:**/applicationContext.xml" })
public class MyTest extends AbstractTestNGSpringContextTests {
#Resource
private MyDependency md;
#Test
public void myTest() {
...
While the above example is a TestNG test, there is also Junit support explained in 8.3.7.2. Context management and caching.
General approach: Annotate your class with #Configurable and utilize AspectJ load-time or compile-time weaving. See 6.8.1 in the Spring documentation on AOP for more details.
You can then annotate your instance variables with #Resource or #Autowired. Though they accomplish the same goal of dependency injection, I recommend using #Resource since it's a Java standard rather than Spring-specific.
Lastly, remember to consider using the transient keyword (or #Transient for JPA) if you plan on serializing or persisting the objects in the future. Chances are you don't want to serialize references to your DI'd repository, service, or component beans.
See the autowire() method on the AutowireCapableBeanFactory class. If you use an ClasspathXmlApplicationContext, you can get the factory with getAutowireCapableBeanFactory()
To get the ApplicationContext, you would need to use a static singleton or other central repository, such as JNDI or a Servlet container. See DefaultLocatorFactory on how to get an instance of the ApplicationContext.
If what you need is for testing purposes, Spring has good support for the scenario that you described above.
Check out Spring Reference manual section on Testing