Run script #Bean classes in kotlin spring application - spring

So I have a spring application on kotlin and to test a particular class I want to write up a script but the problem is all these classes are #Component and it'll be pain to intialize each class using new . Is there any way I can utilise the Beans configured in my main function.
I've tried setting up applciation context which returns null, tried lot of things from internet but no luck.

So, after some searching. I found a way.
in the main function where the Spring application starts.
val context = runApplication<App>(*args)
val crawler = context.getBean(MyClass::class.java)
ref: Accessing Spring managed beans outside spring managed classes in kotlin

Related

#SpringBootTest loads unrequired Bean when making IT

I'm making some Integration Tests for my app and I'm encountering this problem I can't see how to solve.
I'm using Spring Boot 2.4.13 + Spring Data Neo4J 6.1.9
FYI, I deleted the Application default test that comes bundled when you create a project through Spring Initializr, and under /src/test/resources I have a .yml file named application.yml
My IT class looks like this:
#SpringBootTest
public class ClientIT {
#Autowired
private ClientServiceImpl service;
#Autowired
private ClientRepository repository;
#Test
void someTest() {
//Given
//When
//Then
}
}
But when I run this test I get the following Exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
And this is the cause:
Caused by: java.lang.IllegalStateException: The provided database selection provider differs from the ReactiveNeo4jClient's one.
The thing is I don't use SDN's Reactive features at all in my project. I don't even understand why Spring tries to load it. I've created an Issue under the Spring Data Neo4j GitHub repository (https://github.com/spring-projects/spring-data-neo4j/issues/2488) but they could only tell me that ReactiveNeo4jDataAutoConfiguration gets automatically included if there's a Driver or Flux class in the classpath which I don't have.
I've been debugging the Spring internals while booting up the Application after JUnit Jupiter methods to no success.
What I could see is that at some point after JUnit Jupiter tests preparation/initialization, "reactiveNeo4jTemplate" gets injected into DefaultListableBeanFactory's beanDefinitionNames variable.
I've tried many combinations of different annotations intended to be used when making Integration Tests but the one time it worked was after I explicitly excluded ReactiveNeo4jDataAutoConfiguration class through
#EnableAutoConfiguration(exclude=ReactiveNeo4jDataAutoConfiguration.class)
What I've always seen in some blogposts is that by using #SpringBootTest I shouldn't worry about this kind of problem but it looks like I need to add that annotation every time I want to make a new IT test.
My Integration Tests basically consist of bootstrapping the application + web server (tomcat) along with an embedded Neo4J instance and after that, making requests to check everything works as it should. Do I really need to worry about all of this just to make these simple tests?
Thank you
References:
How do I set up a Spring Data Neo4j integration test with JUnit 5 (in Kotlin)?
SprintBootTest - create only necessary beans
Answering my own question after finding what is causing this error:
In the linked Github Issue, one of the developers says having Flux.class in the classpath forces SDN to instantiate Neo4jReactiveDataAutoConfiguration which is what is causing the other reactive beans to instantiate.
Apparently, neo4j-harness brings io.projectreactor (where Flux.class belongs) as an indirect dependency through neo4j-fabric which is the root of our problems.
The Spring Data Neo4j will be fixing this issue in a patch later this week.

Why NoSuchBeanDefinedException while running Spring Integration test

I have a Spring Boot project for which one of the developers has written Integration tests against an in-memory DB. Here is some background before I put my question :
We have following Maven modules :
1) web-persistence : which store all entities and interfaces and corresponding implementations. All these classes are in package com.mycompany.persistence.
2) web-services : which stores all our rest controllers and spring security related classes. These are all in package com.mycompany.services
3) web-services module has maven module dependency on web-persistence module.
4) In web-services module we have defined a service UserContextServiceImpl which is implementation of interface IUserContextService. This service basically encapsulates Spring's SecurityContextHolder functionality by exposing only those parts of a current user related information which is needed through its methods so that everywhere we don't have to use SecurityContextHolder. The implementation class is annotated with #Service("userContextService").
5) Interface IUserContextService is in persistence module. There is another class AppDependecyHeper in persistence module which implements ApplicationContextAware and returns UserContextService's concrete instance by calling appContext.getBean('userContextService') where appContext is holding ApplicationContext when application is initialized.
6) Now we don't want to expose Spring Security classes in persistence module(for reasons which are out of context here) and hence above arrangement of getting current user information through a service by calling getBean() method of applicationContext in persistence layer class AppDependencyHelper. This information is then used to update audit fields createdBy and modifiedBy using an EntityListener.
7) Actual application uses only single ApplicationContext where both persistence layer classes and web controllers are loaded in one single spring context.
8) Everything works fine when application runs. However when our integration test Runs the call appContext.getBean('userContextService') fails and throws NosuchBeanDefinitionException as it is unable to find the bean with name 'userContextService'
9) Now finally the code of our integration Test (only giving relevant details) which is located in com.mycompany.persistence.embeddeddb package:
#Runwith(SpringJUnit4ClassRunner.class)
#ConfigurationContext
class MyEntityTest{
#Configuration
#ComponentScan("com.mycompany.persistence")
public static class Config{
.....
}
.....//code which eventually gives call to
AppDependencyHelper's method which in turn tries to retrieve userContextService Bean.
}
Now the questions :
1) Why it fails to retrieve the bean? I have tried addingcom.mycompany.services like below
#ComponentScan({"com.mycompany.persistence","com.mycompany.services"})
but to no avail.
2) What I am able to understand by reading whatever I got is that #ContextConfiguration needs to be provided with either XML files or Annotated classes or WebApplicationInitializers(in my case as we are having only single ApplicationContext) to create an ApplicationContext from #Configuration classes. However I cannot do that as maven starts complaining about circular dependency which is rightly so as my #Service annotated class UserContextServiceImpl is in web-services module which is dependent on web-persistence module already where the test case class is written.
3) The only solution I can think of is moving all integrations test classes to web-services module so that I can tell #ConfigurationContext all the classes from which it should create an ApplicationContext.Am I right?
Last point : The ApplicationContext class is of type GenericApplciationContext when I run tests. When I run application its obviously AnnotatedEmbeddedWebApplicationContext
please let me know if there is any solution to this problem?
From what I'm guessing you are trying to use a component from your web-service module in your integration tests. If that's the case, than yes, you should move your integration test cases to your web-service module - as you've already suggested in your question.
EDIT: You should really rethink your design, because you have a circular dependency problem here: Your web-service module depends on web-persistence, but the implementation of your IUserContextService in web-persistence is found in web-service.
You should probably move your IUserContextService (and all your integration tests) to your web-service package.

using/importing/injecting spring-managed class into non-spring class

Is there a way to inject/import spring-managed class into a legacy code or non-spring class?
I am working on a spring project, but we have this legacy code that needs access to a spring managed class (specifically a jparepsository implementation class). Is this possible?
Sorry, I am a newbie in the Spring framework. Any help/advice will be greatly appreciated. Thanks :)
You can always load an application context up in your code. It's a simple matter of doing something like this:
ApplicationContext context = new ClassPathXmlApplicationContext("path/to/applicationContext.xml");
You'll have access to all the beans created there via the getBean() function. This may be sufficient for you. However, you won't be accessing the same context as other parts of your application, you'll have a copy. So if you have stateful beans in your application context the "unmanaged" code won't see the same state.
if you're talking about a web application, then you can use the WebApplicationContextUtils to access the same context as the rest of the web applicatiom.
WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
Since you have access to the legacy code, what prevents you from rewriting it to be spring managed, or at leas to be ApplicationContextAware?

Injecting spring beans into legacy web app POJOs [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 7 years ago.
In order to provide POJOs in my legacy web app (Tomcat 8.0.26) with the ability to send ActiveMQ messages I've taken the recommendation to introduce Camel (2.15.2) / Spring (4.2.1) into the app to purely for the purpose of managing pooled MQ connections. I'm hoping there isn't an easier way.
Doing things the Spring way I'm thinking everything would need to be based around an MVC architecture with HTTP servlet aware controllers having access to the servlet context and therefore the Spring context in order to inject beanFactory beans into classes annotated with #Controller and #Service (and in fact there must be a Spring #Controller class that enables Spring to inject the #Service class.
However, as I've stated this is legacy code that will not be using the spring web framework.
After much pain it seems that the only way I can get beanFactory beans injected into my POJOs is to go the AspectJ and Weaving route. Before I go down this road can someone tell me that this is currently the best approach (what I've read describing this solution is from 2011 Can't get Spring to inject my dependencies - Spring Newbie) ? Can you point me to documentation and a working example?
Many thanks.
1) aspectj with #Configurable
In your #Configuration annotated class/es
you can add some more annotations
#Configuration
#EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
#EnableSpringConfigured
#EnableAspectJAutoProxy
to enable aspectj and the #Configurable annotation,
you need to import the aspectj lib to your project and add the spring tomcat instrumentable java agent in your tomcat lib folder (give a look here, it exaplains how to configure tomcat) http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/instrument/classloading/tomcat/TomcatInstrumentableClassLoader.html
this is not going to help you if you are going to create your pojos using "new"
MyPojo p = new MyPojo(); // no black magic for this, you will need to satisfies the dependencies yourself) but that would be helpful for example when you load some entities through a framework like hibernate and you want to inject something into them.. #Configurable it's an option that can be evaluated in those cases, for what you describe I would rather not use it.
2) You can have some static methods that uses some static set spring-beans and use them from your pojos, something like
class Util{
private static SprintBeanWithJmsSupport x;
public static setSpringBeanToHandleJmsMessages(SprintBeanWithJmsSupport x){
Util.x = x;
}
public static sendJmsMessage(JmsMessage m){
x.sendMessage(m)
}
}
and you can go with Util.sendJmsMessage(...)
this is a bit shitty but it does the work, I don't personally like this approach
3) set your spring beans in your pojo when they need to use them (maybe behind some nice interfaces that suit your domain)
if you go with spring mvc you will likely end up having some controllers that will use some services (generally they handle security / db access and are the entry point to start the "use cases"), as everything wthin these layers is handled by spring it will be very simple to pass the spring-bean to handle jms messaging to your pojos, this seems to me quite a nice way to handle the problem
I went mostly based on memory and something may not be completely accurate, plus my english is .. what it is, so hope this can be helpful anyway.

How to initialize Application Context in Spring Framework 4

I have a project based on Spring Framework 4 and its subproject - Spring Data Solr.
All examples I had a chance to see explain how to organize your project - from base entity ( pojo's ) classes till spring specific classes such as repositories and service. When it came to test functionality all examples show a test with private field ( spring bean ) which usually becomes initialized with the help of annotation
#ContextConfiguration(classes = some-spring-data-main-class.class, loader = SpringApplicationContextLoader.class)
Then it is possible to call for it's bean's methods in #Test methods.
But, when it comes to init bean in a project - how to make it with Spring 4, which is completely XMLless ( I mean, I do not have an applicationContext xml file ).
P.S. in Spring 3 we usually wrote smth like:
ApplicationContext context = new ClasspathApplicationContext("applicationContext.xml")
Is it reasonable to expect smth similar to this of Spring 4 introduces absolutely new concepts of application initialization? What should we write now to init app's first bean?
I got it !
In Spring 4 now we have to write:
ApplicationContext context = new AnnotationConfigApplicationContext(<out-main-config-class>.class);
and then call for beans and its methods.

Resources