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.
Related
Taking into account the following example where I'm trying to use the Sample configuration bean within SampleStarter to start the service with the bean properly filled. The .scala file has SampleStarter.scala as name, with Sample being defined within that exact same file.
#SpringBootApplication
#Service
object SampleStarter {
#(Autowired #setter)
var sample: Sample = _ // always null, #Autowired not working?
def getInstance() = this
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
#Configuration
#ConfigurationProperties("sample")
#EnableConfigurationProperties
class Sample {
#BeanProperty
var myProperty: String = _
def run(): Unit = { // bean config seems OK, when debugging with #PostConstruct the 'myProperty' value is filled properly
print(myProperty)
}
}
Whenever I hit sample.run() after SpringApplication.run(classOf[Sample], args: _*), sample property is always null. I reckon this has something to do with object in Scala since all their members are static AFAIK. I took this SO question How to use Spring Autowired (or manually wired) in Scala object? as inspiration but still can't make my code to work.
Is there something wrong the way I'm instantiating the classes or is it something related to Scala?
EDIT
Following #Rob's answer, this is what I did trying to replicate his solution.
#SpringBootApplication
#Service
object SampleStarter {
#(Autowired #setter)
var sample: Sample = _
def getInstance() = this
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
#Configuration // declares this class a source of beans
#ConfigurationProperties("sample") // picks up from various config locations
#EnableConfigurationProperties // not certain this is necessary
class AppConfiguration {
#BeanProperty
var myProperty: String = _
#Bean
// Error -> Parameter 0 of constructor in myproject.impl.Sample required a bean of type 'java.lang.String' that could not be found.
def sample: Sample = new Sample(myProperty)
}
class Sample(#BeanProperty var myProperty: String) {
def run(): Unit = {
print(myProperty)
}
}
#Configuration declares a source file that is capable of providing #Beans. This is not what you want. You want/need to have Sample as a POJO class (POSO?) and then have another class "AppConfiguration" (say) annotated with #Configuration that creates an #Bean of type Sample
My scala is very rusty so this may not compile at all but the structure should be roughly correct.
#Configuration // declares this class a source of beans
#ConfigurationProperties("sample") // picks up from various config locations
#EnableConfigurationProperties // not certain this is necessary
class AppConfiguration {
#Bean
def getSample(#BeanProperty myProperty: String): Sample = new Sample(myProperty)
}
class Sample {
var myProperty: String
def Sample(property : String) = {
this.myProperty = myProperty
}
def run(): Unit = {
print(myProperty)
}
}
Now you have a Sample class as an #Bean and it can be #Autowired in where ever necessary.
#SpringBootApplication
#Service
object SampleStarter {
// note its typically better to inject variables into a constructor
// rather than directly into fields as the behaviour is more predictable
#Autowired
var sample: Sample
def getInstance() = this // not required ??
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
FWIW, you can start the SpringBoot application independently and have the logic for #Service entirely separate. An #Service is another type of #Bean and is made available to the rest of the SpringBootApplication.
My scenario:
I'm building an app that uses Kotlin and SpringBoot 2.0.3. I'm trying to write all my unit tests in JUnit5. All 3 of these are new to me, so I'm struggling a bit.
I'm using a #ConfigurationProperties class (instead of #Value) to inject values from my application.yml into my Spring context.
#Configuration
#ConfigurationProperties(prefix = "amazon.aws.s3")
class AmazonS3Config {
val s3Enabled: Boolean = false
val region: String = ""
val accessKeyId: String = ""
val secretAccessKey: String = ""
val bucketName: String = ""
}
I then have a Kotlin class that is utilizing these properties, following Kotlin/Spring best practice to define the injected class as a constructor parameter.
class VqsS3FileReader(val amazonS3Config: AmazonS3Config) : VqsFileReader {
companion object: mu.KLogging()
override fun getInputStream(filePath: String): InputStream {
val region: String = amazonS3Config.region
val accessKeyId: String = amazonS3Config.accessKeyId
val secretAccessKey: String = amazonS3Config.secretAccessKey
val bucketName: String = amazonS3Config.bucketName
logger.debug { "The configured s3Enabled is: $s3Enabled" }
logger.debug { "The configured region is: $region" }
logger.debug { "The configured accessKeyId is: $accessKeyId" }
logger.debug { "The configured secretAccessKey is: $secretAccessKey" }
logger.debug { "The configured bucketName is: $bucketName" }
val file: File? = File(filePath)
//This method is not yet implemented, just read a file from local disk for now
return file?.inputStream() ?: throw FileNotFoundException("File at $filePath is null")
}
}
I have not completed this implementation, as I'm trying to get the unit test working first. So for the moment, this method doesn't actually reach out to S3, just streams a local file.
My unit test is where I'm getting stuck. I don't know how to inject the properties from my application.yml into the test context. Since the ConfigProperty class is passed as a construction parameter, I have to pass it when I establish my service in my unit test. I've tried various solutions that don't work. I found this piece of info, which was helpful:
If Spring Boot is being used, then #ConfigurationProperties instead of #Value annotations can be used, but currently this only works with lateinit or nullable var properties (the former is recommended) since immutable classes initialized by constructors are not yet supported.
So this means I cannot use class VqsS3FileReaderTest(amazonS3Config: AmazonS3Config): TestBase() { ... } and then pass the config to my service.
This is what I have currently:
#ActiveProfiles("test")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ExtendWith(SpringExtension::class)
#ContextConfiguration(classes = [AmazonS3Config::class, VqsS3FileReader::class])
class VqsS3FileReaderTest(): TestBase() {
#Autowired
private lateinit var amazonS3Config: AmazonS3Config
#Autowired
private lateinit var fileReader: VqsS3FileReader
val filePath: String = "/fileio/sampleLocalFile.txt"
#Test
fun `can get input stream from a valid file path` () {
fileReader = VqsS3FileReader(amazonS3Config)
val sampleLocalFile: File? = getFile(filePath) //getFile is defined in the TestBase class, it just gets a file in my "resources" dir
if (sampleLocalFile != null) {
val inStream: InputStream = fileReader.getInputStream(sampleLocalFile.absolutePath)
val content: String = inStream.readBytes().toString(Charset.defaultCharset())
assert.that(content, startsWith("Lorem Ipsum"))
} else {
fail { "The file at $filePath was not found." }
}
}
}
With this, my test runs, and my context seems to setup properly, but the properties from my application.yml are not being injected. For my debug output, I see the following:
08:46:43.111 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured s3Enabled is: false
08:46:43.111 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured region is:
08:46:43.112 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured accessKeyId is:
08:46:43.112 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured secretAccessKey is:
08:46:43.112 [main] DEBUG com.ilmn.vqs.fileio.VqsS3FileReader - The configured bucketName is:
All empty strings, which is the default values. Not the values I have in my application.yml:
amazon.aws.s3:
s3Enabled: true
region: us-west-2
accessKeyId: unknown-at-this-time
secretAccessKey: unknown-at-this-time
bucketName: test-bucket
I see mistake in the following line:
#ContextConfiguration(classes = [AmazonS3Config::class, VqsS3FileReader::class])
Please put configuration classes here (instead of just beans).
Short - hot to fix test
Create class (if missing) like VqsS3Configration in the main module (e.g. in the module, where you have production code)
Create class like VqsS3TestConfigration in the same package with your tests. Content on this file:
#org.springframework.context.annotation.Configuration // mark, that this is configuration class
#org.springframework.context.annotation.Import(VqsS3Configration::class) // it references production configuration from test configuration
#org.springframework.context.annotation.ComponentScan // ask Spring to autoload all files from the package with VqsS3TestConfigration and all child packages
class VqsS3TestConfigration {
/*put test-related beans here in future*/
}
Then go to test and change declaration:
#ContextConfiguration(classes = [VqsS3TestConfigration ::class]) // we ask Spring to load configuration here
I created sample application here: https://github.com/imanushin/spring-boot2-junit5-and-kotlin-integration
Please execude line .\gradlew.bat test or gradlew.bat bootRun in the src folder. Test will check, that we able to read properties. bootRun will print auto-loaded properties
Boring theory
First of all - Spring has Configuration classes - they are needed to load and initialize other classes. Instead of Service or Comonent classes, main purpose of Configuration classes - just create services, components, etc.
If we will simplify algorithm of the Spring application load, then it will be like this:
Find Configuration classes
Read annotation of them, understand list of classes (e.g. reference tree), which should be loaded (and in addition - how they should be loaded)
Load classes with different ways:
3.1. For classes, which are annotated with #ConfigurationProperties - put configuration items here
3.2. For classes, which are annotated with #RestController - register them as rest controllers
3.N. etc...
How does Spring understand, what configuration should be loaded?
Formally is it done by Spring Boot, however I will name it as Spring
Understand several initial configurations - they can be put into the class SpringApplicationBuilder, into the test annotations (see above), into the XML context, etc. For our case we use test annotation and #ContextConfiguration attribute
Recursive get all imported configuration (e.g. Spring reads #Import annotation, then it get children, then it check their imports, etc.)
Use Spring Factories to get configuration automatically from jar
Therefore, in our case, Spring will do actions like this:
Get configuration from test annotation
Get all other configurations by recursive way
Load all classes into the contet
Start test
Okay, it took me all day, but I finally got my application properties to load into my unit test context. I made 2 changes:
First, I added the #Service annotation to my VqsS3FileReader service - which I had originally forgotten. Also, while I had updated my Test to not inject the AmazonS3Config via the constructor, I had neglected to update my service to do the same. So I changed
this:
class VqsS3FileReader(val amazonS3Config: AmazonS3Config) : VqsFileReader {
companion object: mu.KLogging()
...
to this:
#Service
class VqsS3FileReader : VqsFileReader {
companion object: mu.KLogging()
#Resource
private lateinit var amazonS3Config: AmazonS3Config
...
Finally, I modified my Spring annotations on my test.
from this:
#ActiveProfiles("test")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ExtendWith(SpringExtension::class)
#ContextConfiguration(classes = [AmazonS3Config::class, VqsS3FileReader::class])
class VqsS3FileReaderTest(): TestBase() {
...
to this:
#ActiveProfiles("test")
#SpringBootTest
#ComponentScan("com.ilmn.*")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ExtendWith(SpringExtension::class)
#EnableAutoConfiguration
#SpringJUnitConfig(SpringBootContextLoader::class)
class VqsS3FileReaderTest(): TestBase() {
...
It seems like I have an unordinary amount of annotations on my test now... so I will be looking carefully at what each of them really do, and see if I can reduce it. But at least my properties are being injected into my test context now.
In Spring framework and Java world, there is an interesting object collector pattern that I use.
For example consider below -
public interface Calculator {
SomeOutput calculate(SomeInput input);
}
#Component
public class CalImpl1 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
#Component
public class CalImpl2 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
Now this can easily injected in another class using Spring DI
#Component
public class Main {
//This line collects all to implementors of this and set it here.
#Autowired
public List<Calculator> calculators;
//other methods
}
Now problem is I am not sure how same thing can be achieved in scala. I have done some search and found cake pattern (http://loicdescotte.github.io/posts/scala-di/) used in scala but that didn't seem to achieve same thing as object collectors like above. I also want to follow open close principle which I think gets violated in cake pattern but using object collectors I can easily achieve it.
is there a way achieve same object collectors like implementation in scala?
There are templates in lighbend activator that illustration using spring DI on Play, Akka and Scala applications. Please see this: https://www.lightbend.com/activator/templates#filter:spring
I haven't used Spring as DI, I usually use Guice (explicitly used because it's default on play framework 2) and Implicits parameters both as a compilation DI.
Sample:
class B
class X(x: Int)(implicit c: B)
//DI - mostly define in main method/application
implicit val c: B = new B
val x = new X(2)
Explicitly using java.util.List worked for me. This is not the prettiest solution but it shows that it basically works. Haven't tried that but implementing a corresponding PropertyEditor you could stick with the Scala types.
trait Calculator {
def calculate(input: SomeInput) : SomeOutput
}
#Component
class CalImpl1 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class CalImpl2 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class Main #Autowired()(calculators: java.util.List[Calculator]) {
// or inject field if constructor injection is not desired
// #Autowired
// var calculators: java.util.List[Calculator] = _
}
object Main {
def main(args: Array[String]) = {
val ctx = new AnnotationConfigApplicationContext("your package name here")
val main = ctx.getBean(classOf[Main])
// calculators should now be wired in the returned instance
}
}
Sample app located here : https://github.com/rushidesai1/Grails2_4_2_BeanIssue
Question:
In resources.groovy if we declare a bean like this
beans = {
testObject(TestObject){bean ->
bean.scope = "prototype"
map = new HashMap() // or [:]
//And also if we declare any object like this
testA = new TestA()
}
}
and Now if we DI testObject bean or do 'Holders.grailsApplication.mainContext.getBean("testObject")', then the bean we get will have singleton 'map' and singelton 'testA' object.
Here testObject is declared as 'prototype' and even then both 'map' and 'testA' are singleton
I want to know if this is a bug or it is working as designed. It is completely counter intuitive that it would work like this since we are specifically doing new and so we expect a new bean being injected everytime.
Use the Unit test case to see more detailed version of my question.
Thanks in advance for clarification !!!
I want to know if this is a bug or it is working as designed.
Yes, I think it is working as designed.
Your testObject bean is a singleton. That singleton bean only has 1 copy of the map and testA properties. The behavior you are describing is exactly what I would expect.
EDIT:
I have reviewed the application in the linked project and this is what is going on...
In resources.groovy you have something like this:
testObject(TestObject) { bean ->
bean.scope = "prototype"
mapIssue1 = ["key1FromResource.groovy": "value1FromResource.groovy"]
}
That testObject bean is a prototype scoped bean so each time you retrieve one, you will get a new instance. However, you have the initialization Map hardcoded in the bean definition so the bean definition that is created has that Map associated with it so every bean created from that bean def will have the same Map. If you want a different Map instance, you could create it in afterPropertiesSet or similar.
The unit test at https://github.com/rushidesai1/Grails2_4_2_BeanIssue/blob/e9b7c9e70da5863f0716b998462eca60924ee717/test/unit/test/SpringBeansSpec.groovy is not very well written. Seeing what is going on relies on interrogating stdout after all of those printlns. The behavior could be more simply verified with something like this:
resources:groovy
import test.TestObject
beans = {
testObject(TestObject) { bean ->
bean.scope = "prototype"
mapIssue1 = ["key1FromResource.groovy":"value1FromResource.groovy"]
}
}
SpringBeansSpec.groovy
package test
import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import spock.lang.Specification
#TestMixin(GrailsUnitTestMixin)
class SpringBeansSpec extends Specification {
static loadExternalBeans = true
void 'test bean properties'() {
setup:
def testObject1 = grailsApplication.mainContext.testObject
def testObject2 = grailsApplication.mainContext.testObject
expect: 'test TestObject beans are not the same instance'
!testObject1.is(testObject2)
and: 'the TestObject beans share values defined in the bean definition'
testObject1.mapIssue1.is(testObject2.mapIssue1)
}
}
On one hand it might be confusing that even if you are using new it should be creating a new Object each time you get testA bean and on the other hand it is working as expected. How?
Alright! So the answer lies in Spring java Configuration. The resources.groovy is using DSL which internally is a Configuration file.
Not sure if you know or remember about springs #Configuration annotation. Using this we are making POJO a configuration file.
Now the rules of Spring are:
Any bean created is singleton by default until unless specified.
Even if you are using new in java configuration file. Spring is made wise enough that it is a spring config file and hence new doesn't mean a new Object always.
Hence, for equivalent configuration file if I skip testObject and map for now is below:
#Configuration
public class JavaConfiguration {
#Bean
public Teacher teacher(){
TestA testA = new TestA();
return teacher;
}
}
Here, we have used new TestA(). But spring will always return same object until you specify explicitly to use scope Prototype.
Hence, above Configuration file would be like below after enabling prototype scope:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
#Configuration
public class JavaConfiguration {
#Bean
#Scope(value="prototype")
public Teacher teacher(){
TestA testA = new TestA();
return teacher;
}
}
and corresponding DSL would be:
testA(TestA){bean->
bean.scope='prototype'
}
Hope it helps!!
In my application that uses Spring container I created my own annotation, and I wanted at runtime get Class objects of classes that are annotated with my annotation. For this I wanted to utilize Spring container.
In my .xml configuration file I put
<context:component-scan base-package="some.package" >
<context:include-filter type="annotation" expression="some.package.Question" />
</context:component-scan>
so the classes that are annotated with my Question annotation are detected by Spring. Problem is that those classes don't have no parameter constructor, so now I have 2 options:
Define no-parameter constructor in those classes
Define the beans in .xml and use constructor-arg
but is it possible to annotate constructor arguments with some annotation, so Spring will know that it needs to pass null value during creation of a bean?
Also those beans will have prototype scope, and from the point of view of an application the contents of an constructor arguments are not known during the creation of a bean.
EDIT:
I had to use #Value("#{null}") for annotation constructor arguments
I think your first suggestion of using a no-arg constructor sounds cleaner - the reason is that the object created is, from your perspective, being considered properly initialized even though the instance variables have null values - this can be indicated by having a default constructor
If it cannot be changed, your approach of using #Value("#{null}") also works, I was able to test out in a test case:
#MyAnnotation
public class Component1 {
private String message;
#Autowired
public Component1(#Value("#{null}") String message){
this.message = message;
}
public String sayHello(){
return this.message;
}
}
This may not be what you're looking for, but if you want to re-use Spring's classpath scanner and wrap it in your own implementation, you can use the following;
Class annotation = [your class here ];
String offsetPath = [your path here ];
// Scan a classpath for a given annotation class
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
// MZ: Supply the include filter, to filter on an annotation class
scanner.addIncludeFilter(new AnnotationTypeFilter(annotation));
for (BeanDefinition bd : scanner.findCandidateComponents(offsetPath))
{
String name = bd.getBeanClassName();
try
{
Class classWithAnnotation = Class.forName(name);
}
catch (Exception e)
{
//Logger.fatal("Unable to build sessionfactory, loading of class failed: " + e.getMessage(), e);
return null;
}