How can I exclude beans from certain contexts in Spring? - spring

I have a project that mixes Spring Batch with Spring Web. It's primarily a Spring Batch project that runs jobs, but we've got a few REST endpoints on there, and the Spring Batch Admin Manager module as well. Our base project has its own (primary) application context, but the Admin Manager project we've included in the POM effectively creates its own context as well.
One of my classes is a #RestController which means it automatically gets instantiated by both contexts. This controller has a couple of #Autowired dependencies, which is fine when the first context runs because it finds them all. But when the second context runs, it fails to find those dependencies and so the app fails to launch properly.
But because this second context is created automatically, behind the scenes, from that aforementioned Sping Batch Admin Manager project, I don't really have control over it. Is there some way I can manually specify in my #RestController that it should be excluded from all but my primary context?

You can use one of the #Conditional annotations
E.g. #ConditionalOnClass or #ConditionalOnMissingClass or #ConditionalOnBean or #ConditionalOnMissingBean
So your controller just checks whether there is classes/beans which is required to run the endpoint
Read more here

Related

Facing issue while integrating CustomSpringUtilityJar in SpringBootApplication

I am having two spring applications and the details as follows:
1). Application One: This is a utility jar and its purpose is for CRUD operations on a specific table. We are using spring data jpa and hibernate for achieving the same. Since it is an utility this will not run alone
and it will be incuded as dependency to other applications. In this case we are using Application2 for that.
2) Application Two: This is a web service application made of Spring boot using yml configuration and also using Application One for CRUD operations on that table.
Issue: While running application Two(i.e. Spring Boot Application) , I am getting the error message related to the the components, repositories and entities of Application one,
as the bean is not registered in the configuration. Following are the approaches I used to solve thes:
Approach 1: In application 2, along with #SpringBootApplication annotation:
added Component Scan, Enity Scan and Enable JPA repositories and in these three annotations we have to include the package of both Application1 and Application2.
This scenario is working fine.
Note: I am not using Approach 1 as I have to make these three changes in every service applications which is going to use this utility jar. So decided to proceed with Approach2
Approach 2: Created an custom annotation like #UtilJar annotation and configuration class in Application1 and in that configuration class, added the three
annotations along with the configuration annotation.
In this case, while adding the custom annotation(created from Application1) in Application2, all the components and repositories registered in Application1 is registered and error is
showing related to the repositories registered in Application 2 . The only way I can solve on this is to give EnableJPARepository in Application2 which again similar to
Approach1.
So the root cause what I understood is because of #EnableJPARepository annotation given in the Application1 prompted for this error in Application2. Can anyone suggest me how to proceed on this.

Using SpringBoot as an application loader

I have a spring-boot app that acts as a small framework for other apps. It provides a couple of JMS queues and a DAO layer to retrieve and store data from a common set of data stores. The problem is that the original developer of this framework app is scanning all the package "com.mycompany" (rather than com.mycompany.framework) so that it can load the beans of the specific app that may be declared under com.mycompany.myapp1 or com.mycompany.myapp2 an which JARs are bundled together with the JARs of the framework.
We only load a single app in the JVM (app1 or app2), but these apps may share other libraries and sometimes we end up with beans in the context that we don't need. (these may be needed in app1 but not in app2)
So, what would be your advice ?
My problem is similar to what was described here:
https://github.com/spring-projects/spring-boot/issues/3300
I am debating if each app should be aware of the framework and load it. Or if the framework should instantiate a class loader and create a new Spring context loading the app specific code as suggested in the link above.
Perhaps you should consider leveraging some of Spring Boot's Auto Configuration capabilities such as #ConditionalOnProperty or #ConditionalOnClass in your framework. That way, you can only actually enable certain beans if and when the application using your framework takes some specific action (e.g. has a given jar on the classpath, or sets a configuration value). For reference check out: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-developing-auto-configuration

#AutoConfigureBefore does not trigger

The intention of this project is to create hooks into Spring Boot's lifecycle, right before Liquibase executes its database schema changesets. The hooks will eventually be used to start / stop a Docker (or Docker-like) container with a Postgres instance. The project must be able to deal with:
tests run from the IDE (IntellIJ, STS)
tests run with Maven Surefire
spring-boot:run
executable JAR
integration test on CI server
Given the above limitations, the best approach appeared to be to have an Auto-Configuration and instructing it to run before Liquibase.
The Auto-Configuration class has been annotated:
#ConditionalOnProperty(prefix = "docker_42",
name = "enabled", matchIfMissing = false)
#AutoConfigureBefore({LiquibaseAutoConfiguration.class })
public class Docker42AutoConfiguration {
spring.factories has a single entry:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
nl._42.autoconfig.Docker42AutoConfiguration
The entire (stripped down) project can be found here: https://github.com/robert-bor/auto-configuration-question
The results can be verified by either:
setting up the project in your IDE & running a test, or
executing spring-boot:run
In Spring Boot's log you will see Liquibase executing before the custom AutoConfiguration.
Note that various other routes have been tried (ApplicationListener, RunListener), but none played nice with all the required in-roads.
Pointers as to why #AutoConfigureBefore does not work in my project would be very much appreciated.
You're trying to apply a configuration semantic to some runtime constraints. Auto-configuration is about providing bean definitions in the context, something that will ultimately create bean instances that you can wire in your components.
Assume that you need bean Foo in order to auto-configure something. You need to make sure that FooAutoConfiguration runs before yours so that the context has a chance to contain a bean definition for Foo. That is explained in quite details in our last Devoxx university.
The documentation you referenced in that comment does not imply runtime constraints in any way:
Hint for that an auto-configuration should be applied before other specified auto-configuration classes.
applying the auto-configuration does not mean that the beans created by that configuration will effectively start before the beans created by another configuration. In other words, what you've done is making sure that your auto-configuration configures the context before the liquibase auto-configuration has a chance to do so. It does not imply, in any way, that the beans that will be created (we're not there yet) will be created in that order.
If that's what you want to do and you have no bean dependency, you can just forget about all that and create a bean lifecycle dependency instead. It can be tricky because liquibase may or may not be there but we essentially do that for Hazelcast (see the auto-configuration). Basically we need to make sure that Hazelcast is started before the JPA container is started in case it wants to use Hazelcast as second level cache.

Access Spring beans from a Junit Suite

I want to accomplish this - Run a background process (a Solr instance actually) that all the tests in my JUnit Suite will use.
To do this - Created a JUnit class annotated with #RunWith(Suites.class). And added a ClassRule on the Suite to start the server and stop it. Individual tests in the suite were annotated with #SpringApplicationConfiguration and #RunWith(SpringJunit4ClassRunner.class). And I also require access to some of the Beans in the Suite itself (like a spring managed settings bean). What's the best way to do this. What I tried.
Annotated individual tests with #SpringApplicationConfiguration
Had the Suite create an ApplicationContext via
SpringApplication.run and access any bean that it wants (a spring
managed Settings bean for example and use it one of ClassRules of
this Suite).
What I observed is that the ApplicationContext context gets created everytime, One for the Suite because I called SpringApplication.run and one for every test. I obviously want to avoid this and caching of the ApplicationContext between test runs also does not seem to work in this case.
So what are the best practices to handle this case.
Any suggestions/recommendations will be highly appreciated.
This is a long forgotten question, but it shown up on my google search, so I am assuming it still somehow relevant :)
I was going down on the same path but I hit so many road blocks that I decided to change my approach:
Create a JUnit runner as described here.
Change #1 to inherit SpringJUnit4ClassRunner here is an example.
Finally, annotate your Test classes with #RunWith(MyCustomRunner.class)
There you go. You can add whatever logic you need inside your runner.

Share spring container between test application and embedded tomcat

We are using cucumber-jvm to write an integration test layer in our application. One of the challenges we are finding is managing the database between the tests and the web application.
A typical scenario is that we want to persist some entities in a Given step of a scenario, then perform some actions on the user interface that may, in turn, persoist more entities. At the end, we want to clean the database. Because the cucumber-jvm tests are in one jvm and the web application is running in another jvm we cannot share a transaction (at least in a way of which I am aware) so the database must be cleaned manually.
My initial thought was to use an Embedded Tomcat server running off of an embedded in-memory database (HSQLDB) in the same JVM as the cucumber-jvm test. This way we might be able to share a single spring container, and by extension a single transaction, from which all objects could be retrieved.
During my initial tests it looks like Spring gets loaded and configured twice: once when the test starts and the cucumber.xml is read, and a second time when the embedded tomcat starts and the web application reads its applicationContext.xml. These appear to be in two completely separate containers because if I try to resolve an object in one container that is specified in the other container then it doesn't resolve. If I duplicate my configuration then I get errors about duplicate beans with the same id.
Is there a way that I can tell Spring to use the same container for both my test application and the embedded tomcat?
I'm using Spring 3.2.2.GA and Embedded Tomcat 7.0.39 (latest versions of both libraries).
Am I crazy? Do I need to provide more technical details? Apologies if I use some incorrect terminology.
Thanks
p.s. If my problem seems familiar to you and you can suggest an alternative solution to the one I am trying, please let me know!
Jeff,
It is normal that spring is loaded twice. There are two places where two spring contexts are created:
In the servlet container listener org.springframework.web.context.ContextLoaderListener that is configured in web.xml. This one reads its configuration from the file set by the context-param contextConfigLocation.
In the implementation of ObjectFactory provided by cucumber-spring plugin cucumber.runtime.java.spring.SpringFactory. This one reads its configuration from cucumber.xml.
The two spring contexts are totally different and their instances are kept in two different places. As a servlet context attribute for the former and kept by the JavaBackend for the latter.
When starting the embedded tomcat, it is possible to get access to the servlet context and thus set ourself the spring context used bt tomcat with the one from cucumber. But, spring has a special class called WebApplicationContext for context used in a servlet container. The cucumber SpringFactory on other hand creates its context through ClassPathXmlApplicationContext. So unless there is a way to specify the type of application context from the xml config, we will have to provide an ObjectFactory that shoots a WebApplicationContext.
What we can do is to have two web.xml. One for the normal and one for the test. For the test, we use our version of the ContexLoader listener.

Resources