I have one problem with #Autowire annotation in my Kotlin code.
There is one piece of code that works perfectly
#Controller
open class PaymentController {
#Autowired
lateinit var autowiredBean: AutowiredBean
#RequestMapping(value = "/SomePage", method = arrayOf(RequestMethod.GET))
fun somePage(#RequestParam("param") param: Int): ModelAndView {
// some code
}
}
But after adding some security checking, #Autowire annotation stops working
#Controller
open class PaymentController {
#Autowired
lateinit var autowiredBean: AutowiredBean
#RequestMapping(value = "/SomePage", method = arrayOf(RequestMethod.GET))
#PreAuthorize("hasPermission('MODULE', 'FINANCE')")
fun somePage(#RequestParam("param") param: Int): ModelAndView {
// some code
}
}
It's just doesn't initialize. I tried to initialize it by my controller constructor but had got the same result.
Any ideas?
Related
I'm using #Bean and #Autowired
#Component
class ConfigurationServiceInvoker() {
fun getRules(request: RulesRequest): RulesResponse {
return runBlocking { stub.geRules(request) }
}
}
#Component
object InvokerConfiguration {
#Bean
fun getConfigurationServiceInvoker(): ConfigurationServiceInvoker {
return ConfigurationServiceInvoker()
}
}
When I calling getRules() function I got error "lateinit property configurationServiceInvoker has not been initialized", how to fix this issue?
#Autowired
private lateinit var configurationServiceInvoker: ConfigurationServiceInvoker
val response = configurationServiceInvoker.getRules()
configurationServiceInvoker.getRules() is being invoked during construction of the object. Runtime annotation processors like that which handles #Autowired occur after the object is constructed. Consider using #PostConstruct to initialize the response value, like so:
#Autowired
private lateinit var configurationServiceInvoker: ConfigurationServiceInvoker
lateinit var response;
#PostConstruct
fun postConstruct() {
response = configurationServiceInvoker.getRules()
}
I have following class (partial code):
#Component
class TestClass: InitializingBean, DisposableBean {
#Autowired
private lateinit var testBean: SomeObject
override fun afterPropertiesSet(){
log.info("testBean 1: $testBdean")
}
fun testFunction(testName: String): Boolean {
log.info("testBean 2: $testBdean")
}
#Throws(Exception::class)
override fun destroy() {
}
}
I saw testBean 1 was run successfully but testBean 2 gave error: lateinit property testBean has not been initialized. So the testBean bean was initialized in afterPropertiesSet() and not available in other functions? I know if we put testBean in the constructor TestClass(testBean) it will be initialized and available to all functions. But is there another way because TestClass will be called from other packages and not every package can pass the testBean to the constructor.
You could create an object that holds your TestClass and use that holder to refer to your create component
something like:
#SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
#Component
class SomeObject(val name:String = "some object")
#Component
class TestClass(val someObject: SomeObject) {
init {
TestClassHolder.testClass = this
}
fun test() = someObject.name
}
object TestClassHolder {
lateinit var testClass: TestClass
}
class NotBeanClass {
fun call() = TestClassHolder.testClass.test()
}
#RestController
class TestController {
#GetMapping
fun test() = NotBeanClass().call()
}
I am studying Kotlin with Spring Boot, and I am trying to use Serenity just for report.
But I have some problem with Mockito because I cannot mock the last part of my code.
That's my case:
#RestController
#RequestMapping("/person")
class PersonController {
#Autowired
private lateinit var personUseCase : PersonUseCase
#GetMapping("/all")
fun findAllPeople(): DataModelResponse<List<PersonDataModelResponse>> {
return DataModelResponse(
PersonDataModelResponseMapper.transform(personUseCase.findAll()))
}
}
#Component
class PersonUseCase {
fun findAll(): List<PersonEntity> {
val personImpl : Person = PersonImplementation()
return personImpl.findAll()
}
}
class PersonImplementation : Person {
private val personDaoResponse : PersonDaoResponse = PersonDaoResponse()
override fun findAll(): List<PersonEntity> {
val listPeopleDao = personDaoResponse.findAll()
return PersonDaoMapper.transform(listPeopleDao)
}
}
internal class PersonDaoResponse (
val identification: Long = 0,
val personName: String = "") {
fun findAll(): List<PersonDaoResponse> {
return listOf(PersonDaoResponse(1, "José"))
}
}
And I am trying to mock my Dao in an integration test:
#RunWith(SerenityRunner::class)
#WithTag("Integration")
#AutoConfigureMockMvc
#WebMvcTest
class PersonControllerTest {
#InjectMocks
private lateinit var personController : PersonController
#InjectMocks
private lateinit var personImplementation : PersonImplementation
#SpyBean
private lateinit var personUseCase : PersonUseCase
#Spy
private val personDaoResponse = PersonDaoResponse()
#Autowired
private lateinit var webApplicationContext: WebApplicationContext
#Autowired
private lateinit var mockMvc: MockMvc
#Rule #JvmField
var springMethodIntegration = SpringIntegrationMethodRule()
#Before
fun init() {
MockitoAnnotations.initMocks(this)
reset(personDaoResponse)
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build()
this.mockMvc = MockMvcBuilders.standaloneSetup(personController).setMessageConverters(MappingJackson2HttpMessageConverter()).build()
}
#Test
fun `integration success`() {
doReturn(listOf(PersonDaoResponse(999L, "Zé")))
.`when`(this.personDaoResponse).findAll()
val result = this.personController.findAllPeople()
assertNotNull(result)
assertEquals(999L, result.data[0].personId)
assertEquals("Zé", result.data[0].name)
}
}
I know this mock is working if I do something I try call the mock method:
assertEquals(999L, this.personDaoResponse.findAll()[0].identification)
How can I Mock this Dao in an integration test like this?
Unfortunately, I don't have an answer for the Mockito problem because I've been off Mockito for way to long.
If Mockito is not a must, I recommend using MockK (https://github.com/mockk/mockk) from my Spring Boot + Kotlin experience. If you need injectable mocked Beans as well, you can add SpringMockK (https://github.com/Ninja-Squad/springmockk) as well.
As your "PersonDaoResponse" is constructed within the PersonImplementation class, you could use something like this with MockK:
every { anyConstructed<PersonDaoResponse>().findAll() } returns listOf(PersonDaoResponse(999L, "Zé"))
Here is my AppConfig:
#Configuration
#EnableWebMvc
#ComponentScan
class AppConfig{
#Bean("myname")
fun name(): Name = Name("Quang")
}
Data class:
data class Name(val value: String ="")
My Controller Class:
#Controller
#RequestMapping("/")
class Main{
#RequestMapping("/")
#ResponseBody
fun index(#Autowired #Qualifier("myname") name: Name): Name {
//this return ""
return name
}
}
The result is "" instead of "Quang".
But now if I inject it from field like this, it works fine:
#Controller
#RequestMapping("/")
class Main{
#Autowired(required = true)
#Qualifier("myname")
lateinit var name:Name
#RequestMapping("/")
#ResponseBody
fun index(): Name {
//this return "Quang" as expected
return name
}
}
So can you explain why #Autowired doesn't works when I use it in method parameter
According to documentation:
Marks a constructor, field, setter method or config method as to be
autowired by Spring's dependency injection facilities.
In your first example, Spring will not autowire anything.
I'm trying to autowire a service in my rest controller like these:
rest controller:
#ApplicationPath("/greetings")
#Component(immediate = true, service = Application.class)
public class RestControllerApplication extends Application {
#Autowired
private MyService myService;
public Set<Object> getSingletons() {
return Collections.<Object>singleton(this);
}
#POST
#Path("/getUploadType")
#Produces("application/json")
public JsonObject getUploadType() {
...
myService.findUploadTypes();
...
}
}
service:
#Component
public class UploadService {
private static final Logger log = Logger.getLogger(UploadService.class);
#Autowired
private OneDAO oneDAO;
#Autowired
private TwoDAO twoDAO;
...
}
but in my rest controller, uploade service is null. Why?
Spring uses its own set of annotations. Instead of #Path plus #[HTTP method] you should use #RequestMapping.
You can find an example here
There is also an extended example here
I have got access to my bean, with these few line of code:
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
MyService myService = context.getBean(MyService.class);
You are declaring a UploadService as #Component but trying to autowire a MyService instance in your controller...
There are two options: you can declare correct service type in your controller or you can make UploadService inheriting from MyService.