In my spring boot application I have multiple #Service implementations of an interface. Which of these implementations is used at runtime for any given request is configured in a databse.
Something like this:
Value
Service Bean
Hello
ServiceA
World
ServiceB
Foo
ServiceA
Bar
ServiceC
The correct bean is then retrieved using the application context and the defined Service Bean Name from the database. However it could be possible that a Service Bean is mentioned in the database that does not exist in the application context. I'd rather detect this during startup than at runtime.
This question basically boils down to how you add your own validation to the spring boot startup process or what's the best practice? I tried throwing an Exception when creating the bean, that deals with the mapping of values to Service Beans, and handling it with my own FailureAnalyzer. But the FailureAnalyzer never gets called because due to the missing bean an UnsatisfiedDependencyException is also thrown and causes the application to stop.
I found a solution that I'm happy with.
I did not register my FailureAnalyzer in my resource folder in the META-INF/spring.factories file.
As described in the question throwing my Exception during bean creation caused an UnsatisfiedDependencyException. So instead I used an ApplicationListener and perform my check, when the application is ready but has not yet started.
#Component
public class ApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
if(!checkConsistency())
throw new MyRuntimeException();
}
}
Now my FailureAnalyzer gets called and handles MyRuntimeException and stops the application with a proper message.
#Component
public class MyRuntimeExceptionFailureAnalyzer extends AbstractFailureAnalyzer<MyRuntimeException> {
#Override
protected FailureAnalysis analyze(Throwable rootFailure, MyRuntimeException cause) {
return new FailureAnalysis(buildErrorMessage(cause), buildActionMessage(cause), cause);
}
}
Related
Can i make my repository be accessed in a class who isn't a RestController or something like that ?
I have an WatchService who listen a folder, to reads the files and after persist to a database. My watchservice works just like reading files, but I want persist using my JPARepository to persists, can i do that?
Springboot Application v2.1.6.RELEASE
#Repository
public interface MyRepository extends JpaRepository<MyClass, Long> { }
public class MyWatchService implements Runnable{
#Autowired
private MyRepository myRepository;
// SOME CODES COMES HERE
#Override
public void run() {
// SOME CODES COMES HERE
myRepository.save(MyClass); // In this point give a nullPointerException
}
}
I get that Exception:
java.lang.NullPointerException
at com.WatchService.run(WatchService.java:515)
at java.base/java.lang.Thread.run(Thread.java:835)
You get the NullPointerException because the dependency did not get injected. You used the annotation correctly, but the dependencies do not get injected, by some magic.
In order for this to work (i.e. for the beans to get injected), you need to let the DI- or IoC Container instantiate the bean for your (in JEE this would be CDI, in Spring it is the Spring IoC Container). This can be done by injection (d'uh! Injection-inception) or programmatically.
A Spring-centric solution is explored in this question.
I'm receiving a null pointer exception when operating on my service because my service is not being Autowired into the class. I've implemented this class's repository and service exactly the same as others in this application and I haven't had this problem before. The class does in fact warn about issues with the Autowire but I'm not sure how to fix them:
Autowired members must be defined in valid spring bean
Again, this is set up the same as other classes and I do not have this issue. Within the service class, it complains that the repository cannot be autowired into the constructor because there are multiple beans of the same type. My other service class shows this warning as well but does not have problems being Autowired into classes and operated upon. Definitions below, please ask for any other context that would be helpful.
//TransactionCategoryRepository.java
#Repository("transactionCategoryRepository")
public interface TransactionCategoryRepository extends
CrudRepository<TransactionCategory, Integer> {
}
--
//TransactionCategoryService.java
#Service("transactionCategoryService")
public class TransactionCategoryService {
private TransactionCategoryRepository transactionCategoryRepository;
#Autowired
public TransactionCategoryService(TransactionCategoryRepository repository) {
this.transactionCategoryRepository = repository;
}
public void saveTransactionCategory(TransactionCategory transactionCategory) {
transactionCategoryRepository.save(transactionCategory);
}
}
--
//Utilities.java
public class PlaidUtilities {
private Logger logger =
LoggerFactory.getLogger(PlaidUtilities.class.getSimpleName());
private PlaidClient mPlaidClient;
#Autowired
TransactionCategoryService mTransactionCategoryService;
...
The multiple bean warning is thrown on respository in TransactionCategoryService.java and the Autowired definition warning is thrown in Utilities.java. The breaking null pointer exception error occurs later in Utilities.java when operating on mTransactionCategoryService.
Unless you need them, take the names out of the #Service and #Repository annotations. I've found it just makes things awkward.
The other thing that might be wrong is that you're not scanning those packages. You can change that in your main class by altering the boot application attribute to #SpringBootApplication(scanBasePackages={"your.package.here"})
Have a look here at this question where they detail it
I am using Spring Boot 1.5.7 and Apache Camel 2.19.3, using Spring Boot AutoConfiguration provided by spring-boot-starter-camel
It is pretty basic Spring Boot and Camel initialized as in their tutorial, so we have a RouteBuilder component that does exactly that.
#Component
public class CIFRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
// build routes
}
}
We have a Configuration that defines some beans we need in our application
#Configuration
public class MyConfiguration {
#Bean
public void Map<String, Object> map() {
return new HashMap<>()
}
}
Finally, we have a custom InflightRepository implementation that should be scanned by auto-configuration and added to the CamelContext which basically works, but for some reason, the component doesn't get initialized properly. Means, its dependencies are not initialized but the bean is instantiated and injected in my Application.
#Component
public class MyCustomInflightRepository extends DefaultInflightRepository {
#Autowired
private Map<String, Object> map;
#Override
public void add(Exchange exchange) {
super.addExchange(exchange);
// ....
}
}
The problem now is that map remains (null), I also tried adding a #PostConstruct initializer method but it doesn't get called.
As far as I was able to reconstruct, it seems to be connected to premature in CamelAutoConfiguration where the CamelContext bean gets instantiated (done in private method afterPropertiesSet.
InflightRepository inflightRepository = getSingleBeanOfType(applicationContext, InflightRepository.class);
if (inflightRepository != null) {
LOG.info("Using custom InflightRepository: {}", inflightRepository);
camelContext.setInflightRepository(inflightRepository);
}
If MyCustomInflightRepository doesn't implement InflightRepository, the bean is initialized correctly, but indeed not recognized by Camel. When disabling auto-configuration, the bean's dependencies are injected.
So, either I'm doing the impossible by Spring standards or there's something fishy with the Camel component for Spring.
I'm a bit quick on resolving this (I wanted to post this two days ago already^^), but a colleague figured out what could be the problem.
When using CamelAutoConfiguration the InflightRepository bean (or practicially everything for which Camel tries to get a matching bean here), the context is accessed before property resolvers are fully initialized which leads to the bean being initialized (and cached in context) before any auto-wired properties can be resolved.
I'm not a Spring expert but this behavior is a bit problematic in my opinion because uninitialized beans are pulled into the CamelContext when you rely on Spring DI for your custom components.
To be sure, I'll raise this with the maintainers...
By the way, my simple solution was to manually setting the in-flight repository in context configuration (as suggested)
#Bean
public CamelContextConfiguration camelConfig() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext context) {
context.setInflightRepository(new MyCustomInflightRepository(/* Dependencies go here */ ));
}
#Override
public void afterApplicationStart(CamelContext camelContext) {
}
};
}
Also it seems to be an issue when use camel-http-starter in your project which isn't recommended, they claim it is deprecated.
So, either don't do DI (regardless if via property or constructor injection) for your camel-managed beans or skip that starter.
The problem is that a Map<String,Object> is too vague for Spring to be able to understand what you want; I think the default behavior is that it'll give you all beans keyed by name.
Instead, be more specific, or possibly provide the necessary parameters as constructor arguments and configure them explicitly in an #Bean method (it's a good idea to always use constructor injection anyway).
Given a Spring configuration that exclusively contains eager (non-lazy) singleton beans, i.e. the defaults, is it possible to have Spring throw an exception in the case where any of those beans is not injected anywhere? I'm essentially looking for a way to detect dead code in the form of Spring beans.
My question is somewhat similar to these.
http://forum.spring.io/forum/spring-projects/container/116494-any-tools-or-method-to-identify-unused-spring-beans
Spring Instantiation and 'unused beans'
How to detect unused properties in Spring
However,
I'm not interested in manually inspecting a graph or parsing log data.
I don't have the added complexity of multiple context files, overriding beans, bean post-processing, or xml. It's a simple, straightforward, annotation-driven configuration.
I'm using Spring Boot 1.2.6 which is several years newer than those questions (maybe new functionality exists).
Spring will certainly throw an exception if a necessary bean is missing. Can it also throw an exception in the opposite scenario where a bean is found but unnecessary?
Spring will certainly throw an exception if a necessary bean is
missing. Can it also throw an exception in the opposite scenario where
a bean is found but unnecessary?
TL/DR:
Spring does not support this (and probably never will).
Long version:
Detecting if a bean is used can be really hard.
First, lets define when does spring throw the "missing bean" exception.
During the initialisation of the spring context, spring creates the beans in the order in which it will allow for all dependencies to be satisfied (if possible). If a bean is missing a dependency, spring will throw an exception (as you said).
So, the exception is thrown during the spring context initialisation process.
Now, you could say that we could monitor this process and look for a bean that was not used as a dependency in any other bean.
The problem is that not all bean dependencies are defined during the spring context initialisation process.
Let's look at the following example:
First, we have a simple interface, DataService
public interface DataService {
String getData();
}
Now we have 2 spring beans that implement this interface:
#Service("firstDataService")
public class FirstDataService implements DataService {
#Override
public String getData() {
return "FIRST DATA SERVICE";
}
}
#Service("secondDataService")
public class SecondDataService implements DataService {
#Override
public String getData() {
return "SECOND DATA SERVICE";
}
}
Now, imagine that there is no bean that depends on these two beans directly. When I say directly, I mean there is no bean that depends on these beans via constructor-based, setter-based or field-based dependency injection.
Because of that, spring will not inject these beans inside any other bean during the context initialisation process.
Now, consider the following bean:
#Service
public class DataCollector {
#Autowired
ApplicationContext applicationContext;
String getDataFromService(String beanName) {
DataService ds = (DataService) applicationContext.getBean(beanName);
return ds.getData();
}
}
If I call the getDataFromService method of the DataCollector bean with "firstDataService" value for the beanName parameter, the method will return "FIRST DATA SERVICE" as a result.
If I call the method with "secondDataService", I will return "SECOND DATA SERVICE" as a result.
Now, when spring looks at the definition of DataController during context initialisation, there is no way to determine on which beans DataCollector depends on.
It all depends on the application logic, and the value that we use for the beanName parameter when we call the getDataFromService method.
Because of that, spring is not capable of determining if there is bean that is never used (because the bean usage can be dynamic, like in the case above).
I have a custom ServletContextListener that I am using to initialize and start up a Cron4J scheduler.
public class MainListener implements ServletContextListener {
#Value("${cron.pattern}")
private String dealHandlerPattern;
#Autowired
private DealMoqHandler dealMoqHandler;
}
I am autowiring some objects in the Listener as shown, and would like for Spring to manage the listener's instantiation. I am using programmatic web.xml configuration through WebApplicationInitializer, but so far the Listener isn't being autowired (NullPointerExceptions whenever I try to access the supposedly autowired objects).
I've already tried to add my customer listener after adding the ContextLoaderListener, shown below:
public class CouponsWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(SpringAppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
container.addListener(new MainListener()); //TODO Not working
}
I checked these past questions Spring - Injecting a dependency into a ServletContextListener and dependency inject servlet listener and tried to implement the following code inside the contextInitialized method of my listener:
WebApplicationContextUtils
.getRequiredWebApplicationContext(sce.getServletContext())
.getAutowireCapableBeanFactory()
.autowireBean(this);
However, I just get the following exception:
Exception sending context initialized event to listener instance of class com.enovax.coupons.spring.CouponsMainListener: java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
at org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext(WebApplicationContextUtils.java:90) [spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
How can I make sure that Spring has already finished instantiation before adding my listener?
My mistake. It turns out the code in the original post is correct, and the listeners are being added in the correct order.
The issue was that I had annotated my custom listener with a #WebListener annotation (Servlet 3.0). This was causing the web app to disregard my addListener() code in WebApplicationInitializer, and instantiate the custom listener AHEAD of Spring's ContextLoaderListener.
The following code block illustrates the ERRONEOUS code:
#WebListener /* should not have been added */
public class CouponsMainListener implements ServletContextListener {
#Autowired
private Prop someProp;
}
You cannot use new with Spring beans - Java doesn't care about Spring and Spring has no way to modify the behavior of the new operator. If you create your objects yourself, you need to wire them yourself.
You also need to be careful what you do during initialization. When using Spring, use this (simplified) model: First, Spring creates all the beans (calls new for all of them). Then it starts to wire them.
So you must be extra careful when you start to use the autowired fields. You can't always use them right away, you need to make sure that Spring is finished initializing everything.
In some cases, you can't even use autowired fields in #PostProcess methods because Spring couldn't create the bean because of cyclic dependencies.
So my guess is that "whenever I try to access" is too early.
For the same reason, you can't use WebApplicationContextUtils in the WebApplicationInitializer: It hasn't finished setting up Spring, yet.