Groovy Spring DSL: constructing beans with properties of other beans - spring

I have a bunch of Spring beans some of which need to be initialized from other beans, and some of which need to be initialized from properties of those other beans. E.g.:
Foo {
}
Bar {
String getBaz()
}
Qux {
Qux(Foo foo, String baz)
}
I thought I could write something like
beans = {
foo(Foo) {}
bar(Bar) {}
qux(Qux, ref('foo'), ref('bar').baz) {}
}
but obviously this doesn't work because ref('bar') isn't Bar, it's a RuntimeBeanReference.
In plain Spring (3+) what I want is apparently possible with spring expressions but I can't figure out the necessary syntax with the Grails Spring DSL. Can it be done?

I think you meant the classes to look like this:
class Foo {
}
class Bar {
String baz
}
class Qux {
Foo foo
String baz
Qux(Foo f, String b) {
foo = f
baz = b
}
}
and the 2nd ref('foo') should have been ref('bar'). Then this will work:
beans = {
foo(Foo)
bar(Bar) {
baz = 'wazzup'
}
qux(Qux, ref('foo'), '#{bar.baz}')
}

Related

Spring bean names and aliases

Lets say that in an external config a bean is wired with name "externalBean".
Ex.
#Bean("externalBean")
public Foo foo() {
Foo foo = new Foo();
return foo;
}
If I do the following in my own config:
#Bean("myBean")
public Foo foo(#Qualifier("externalBean") Foo foo) {
return foo;
}
I can now autowire the bean in my code by using #Qualifier("myBean").
Is it correct to say that myBean is now an alias for the externalBean?
Is there now only one instance of the Foo bean?
Any remarks?
Suppose it is effectively an alias as there will only be one instance of Foo, although there will still be two beans. These will both just return the same reference to the Foo object.
Try for example
#SpringBootApplication
public class BeanApp{
public static void main(String[] args) {
SpringApplication.run(BeanApp.class, args);
}
static class Foo {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Bean("externalBean")
public Foo foo(){
return new Foo();
}
#Bean("myBean")
public Foo foo1(#Qualifier("externalBean") Foo foo){
return foo;
}
#Bean
public String ready(#Qualifier("externalBean") Foo foo, #Qualifier("myBean") Foo foo1){
foo.setName("This is a bean");
System.out.println(foo.getName());
System.out.println(foo1.getName());
return "Ready";
}
}
This will print "This is a bean" twice, even though getName is called from the two separate beans.
Is it correct to say that myBean is now an alias for the externalBean?
No, alias for myBean is myBean and for externalBean is externalBean
Is there now only one instance of the Foo bean?
No, you will have two instances - externalBean and myBean
Remarks:
If you require only one bean of Foo either you can directly create it in one of foo methods or you can have separate method which does it for you without #Bean annotation.
If you want to have two beans with different names, you do not have stick to aliases, you can rename the methods to intended bean names (e.g myBean, externalBean etc.). While #Autowire you can specify the required bean respective to method(bean) names.

How to write a Spring Boot YAML configuration with nested JSON?

I have been using Spring Boot applications with YAML for the external configuration, and this has been working very well so far. A toy example:
#Component
#ConfigurationProperties
class MyConfig {
String aaa;
Foo foo;
static class Foo {
String bar;
}
}
And then a yaml file with the following properties:
aaa: hello
foo.bar: world
My problem is that I really need to add a JsonObject into my configuration. I first tried adding it as a field in the MyConfig class, and then writing the following YAML file which I believe is syntactically valid:
aaa: hello
from:
{
"first_initial": "D",
"last_initial": "E"
}
foo.bar: world
Spring threw the following error with that: Cannot access indexed value in property referenced...
I finally resorted to making the value a plain string instead and using the > folding tag to put it in the YAML, but this means I have to manually parse the string into a JsonObject in my code.
Anyone have an idea for how to do this?
This should work:
#Component
#ConfigurationProperties
class MyConfig {
String aaa;
Foo foo;
String from;
static class Foo {
String bar;
}
// ... getters setters
public JsonObject getFromAsJson() {
// create object from "this.from"
}
}
aaa: hello
foo:
bar: world
from: |
{
"first_initial": "D",
"last_initial": "E"
}
and this:
aaa: hello
foo:
bar: world
from: "{\"first_initial\": \"D\", \"last_initial\": \"E\"}"
The first version would preserve line breaks.

spring/scala: Possible to autowire bean dependencies?

I'm trying do the following without xml configuration in spring (in scala)
<beans ... >
## (1)
## Auto create a bean by classname
## Auto-wires properties of x.y.Bar
<bean id="baz" class="x.y.Baz"/>
## (2)
## Create a x.y.Foo and auto-wire the property
<bean id="foo" class="x.y.Foo">
<property name="b" ref="baz"/>
</bean>
</beans>
where we have:
class Baz {}
class Foo {
#Autowired //?
#BeanProperty
val baz:Baz = null
}
I have the following test setup:
#Configuration
class Config {
//#Autowired // Seem not to help
#Bean //( autowire=Array( classOf[Autowire.BY_TYPE ]) )
def foo: Foo = null
}
class BeanWithAutowiredPropertiesTest {
#Test
#throws[Exception] def beanWithAutowiredPropertiesTest(): Unit = {
var ctx = new AnnotationConfigApplicationContext(classOf[Config]);
val foo = ctx.getBean(classOf[Foo])
assertTrue(foo != null)
assertTrue(ctx.getBean(classOf[Foo]).baz != null)
}
}
I understand a couple of simple alternatives:
#ComponentScan -- this approach has several issues:
imprecision - there can be many classes matching an auto-wired type in a package
it doesn't (in itself) permit selecting specific values for properties
scanning is painfully slow for large projects
Can't add #Component to 3rd party classes
(If I could register candidate auto-wire types, by name, that would help a lot!)
implementing the #Bean declaration as a method:
.
#Bean
def foo:Foo = {
val f = new Foo()
f.baz = ?? grrr! where from? Not available in this Config
f
}
However:
this sorta circumvents the point of auto-wiring. If I explicitly chose a parameter, baz to set, then I need to actually get a reference to it to do that in the first place. Frequently this can be difficult, especially if the actual baz to be used might be specified in another #Configuration.
because I'm creating the object, don't I need to auto-wiring all of its dependencies? What if Baz has 100 properties and I only way to specify 1 explicitly and have the rest auto-wired?
AFAIK, the xml based configuration doesn't have any of these problems - but I'm at a loss because the spring manual says you can do all the same things via annotations.
NB. I also see:
#Bean( autowire=Array( classOf[Autowire.BY_TYPE ]) )
might be possible. I can't find example online, and scala complains (annotation parameter is not a constant).
[edited]
class ApplicationController #Inject() (messagesApi: MessagesApi){
... code here ...
}
messagesApi is an injected member
see more in https://github.com/mohiva/play-silhouette-seed/blob/master/app/controllers/ApplicationController.scala
[before edit]
I'v found this to be usefull
Answered by Joshua.Suereth.
This is the short version ("ugly but works"):
var service: Service = _;
#Autowired def setService(service: Service) = this.service = service
For automatic bean creation you need to annotate the classes as Component, Service or Persistence, in addition to enabling the ComponentScan at config level.
So you can try:
#Component
class Baz {}
#Component
class Foo {
#Autowired //?
#BeanProperty
val baz:Baz = null
}
For multiple classes implementing the same interface, you can specify names to both the component and the dependency references, i.e.
#Component("baz1")
class Baz {}
#Component("foo1")
class Foo {
#Resource("baz1") //?
#BeanProperty
val baz:Baz = null
}
Notice the #Autowire has been replaced by #Resource.

Grails services in abstract class

I have an abstract class that many classes extend. Everything is in src/groovy.
In my abstract class I would like to have a service injected that the child classes would inherit so I don't have to inject them in every single one.
abstract class Animal {
def noiseService
abstract Sound getSound()
}
class Dog extends Animal {
Sound getSound() {
noiseService.bark()
}
}
In my resources.groovy:
animal(com.thepound.Animal) { bean ->
noiseService = ref("noiseService")
}
This produced an error saying it couldn't instantiate the class because it is abstract, so I added this to the definition:
bean.abstract = true
Now I no longer get an error, however the services are always null in my child classes. How can I get this to work?
Here is what I ended up doing.
I followed Burt Beckwith's post here http://burtbeckwith.com/blog/?p=1017 to create an ApplicationContextHolder class.
Then
abstract class Animal {
def noiseService = ApplicationContextHolder.getBean("noiseService")
abstract Sound getSound()
}
Now this works
class Dog extends Animal {
Sound getSound() {
noiseService.bark()
}
}
I didn't have to put anything in resources.groovy for the Dog or Animal classes
If you want to instantiate Dog, just do this:
noiseService(com.whatever.DogNoiseService) { bean ->
}
animal(com.thepound.Dog) { bean ->
noiseService = ref("noiseService")
}

Grails: How to put a minSize constraints on map

I have this object
#Validateable
class Foo {
Map<String, String> items
static constraints = {
items minSize: 1
}
}
but this test fail:
#Test
void shouldNotValidateIfItemsIsEmpty() {
Foo foo = new Foo(items: [:])
assert !foo.validate()
}
What do I do wrong? It's supposed to work according to grails 'minSize' documentation: "Sets the minimum size of a collection or number property."
The documentation might be misleading. The minSize constraint will only apply to:
String
Arrays
Classes which implements the java.util.Collection interface
java.util.Map however does not extend the java.util.Collection interface
See the supports method of MinSizeConstraint:
public boolean supports(Class type) {
return type != null && (
String.class.isAssignableFrom(type) ||
Collection.class.isAssignableFrom(type) ||
type.isArray());
}
You can develop your own custom constraint for this or a custom validator as suggested by Thermech
In addition, in order for Grails to mock the validate method properly your test class should be something like:
#TestMixin(ControllerUnitTestMixin) class FooTest {
#Test
void shouldNotValidateIfItemsIsEmpty() {
Foo foo = mockCommandObject Foo
foo.items = [:]
assert !foo.validate()
} }
The only way I found, is with a custom validator:
static constraints = {
items validator: { Map map, obj, errors ->
if (map.size() < 1) errors.rejectValue('items', 'minSize.notmet')
}
}

Resources