#Autowiredl bean from external api always nul - java

I have a external jar. There's a AnnotationConfigApplicationContext in it.
#Configuration
#ComponentScan{~~~~~}
public A_config() {
#Bean
~~~~~~
}
somewhere
AnnotationConfigApplicationContext ctx = new
AnnotationConfigApplicationContext ();
ctx.register(A_config());
I have another application referring external jar.
This application also have
#Configuration
#ComponentScan{~~~~~}
public B_config() {
#Bean
~~~~~~
}
AnnotationConfigApplicationContext ctx = new
AnnotationConfigApplicationContext ();
ctx.register(A_config());
ctx.register(B_config());
When I use ctx.getBean(some.class), some.class in external jar, it's ok.
But I autowired field bean in external jar, it's always null.

You should import your #Configuration defined in the external jar into your #Configuration of your application like so:
#Import(A_config.class)
#Configuration
#ComponentScan{~~~~~}
public B_config() {
#Bean
~~~~~~
}

Related

Run spring boot without #SpringBootApplication annotation as jar library in another project

I'm trying to use #autowired annotation in a spring boot project that's not started by main method with #SpringBootApplication annotation. Instead, i did created a jar of that spring project and i'm using that jar as an external jar in a legacy project (non-spring project).
As result i can't get ApplicationContext and all beans managed by spring when you run application from main method are null.
Is that possible to use Spring boot project as a .jar without run main method??
public class RetrieveSubscriberType {
public RetrieveSubscriberType() {
ApplicationContext appCtx = ApplicationContextUtils
.getApplicationContext();
this.subscriber = (SubscriberDAOImpl)appCtx.getBean("subscriber");
}
appCtx always null
#Configuration
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}
ApplicationContextUtils where method setApplicationContext is not called
From the provided docs
You might need to bootstrap your #Configuration classes via AnnotationConfigApplicationContext
#Configuration classes are typically bootstrapped using either AnnotationConfigApplicationContext or its web-capable variant, AnnotationConfigWebApplicationContext.
A simple example with the former follows:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);
// use myBean ...

Injecting library class as dependencies in spring project

I have multiple library classes in my project which need to be injected into a service class. This is the error statement for IntegrationFactory class:
Consider defining a bean of type 'com.ignitionone.service.programmanager.integration.IntegrationFactory' in your configuration.
This error is coming on almost every injection where this library class is injected.
I have already added the Library package in #ComponentScan, but, as it is read-only file, I can not annotate the library class. I came to know from some answer here that Spring can not inject classes which it does not manage. This library is not built on spring.
I have tried to create a #Bean method which returns the IntegrationFactory(class in question) in the class where #Inject is used, but this too does not seem to work.
How can this be done, preferably without creating a stub/copy class?
This is EngagementServiceImpl class snippet:
#Inject
public EngagementServiceImpl(EngagementRepository engagementRepository,
#Lazy IntegrationFactory integrationFactory, TokenRepository tokenRepository,
EngagementPartnerRepository engagementPartnerRepository, MetricsService metricsService) {
this.engagementRepository = engagementRepository;
this.integrationFactory = integrationFactory;
this.tokenRepository = tokenRepository;
this.engagementPartnerRepository = engagementPartnerRepository;
this.metricsService = metricsService;
}
This is injection part:
#Autowired
private EngagementService engagementService;
This is ConfigClass:
#Configuration
public class ConfigClass {
#Bean
public IntegrationFactory getIntegrationFactory(){
Map<String, Object> globalConfig = new HashMap<>();
return new IntegrationFactory(globalConfig);
}
#Bean
#Primary
public EntityDataStore getEntityDataStore(){
EntityModel entityModel = Models.ENTITY;
return new EntityDataStore(this.dataSource(), entityModel );
}
#ConfigurationProperties(prefix = "datasource.postgres")
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder
.create()
.build();
}
}
You need to add your bean definitions in a configuration class.
#Configuration
public class ServiceConfig {
#Bean
public IntegrationFactory getIntegrationFactory(){
// return an IntegrationFactory instance
}
}
Then you have to make sure your #Configuration class gets detected by Spring, either by having it within your scanned path or by manually importing it via #Import from somewhere withing you scanned path. An example of #Import, considering you are using Spring Boot.
#Import(ServiceConfig.class)
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Hope this helps!
Your Bean IntegrationFactory can't be found, as it is not annotated with any Spring stereotype and therefore not recognized by the component scan.
As you have multiple options to provide an instance of your class to the application context, read the Spring documentation (which also includes samples) to find out which one fits you the most:
https://docs.spring.io/spring/docs/5.1.0.RELEASE/spring-framework-reference/core.html#beans-java-basic-concepts
One Option would be to create a factory which provides an instance of your class to the application context, like it is stated in the documentation:
#Configuration
public class AppConfig {
#Bean
public IntegrationFactory myIntegrationFactory() {
return new IntegrationFactory();
}
}
Do not forget to add the Configuration to the application context.

Spring boot Test fails saying, Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean

Test Class:-
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { WebsocketSourceConfiguration.class,
WebSocketSourceIntegrationTests.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
"websocket.path=/some_websocket_path", "websocket.allowedOrigins=*",
"spring.cloud.stream.default-binder=kafka" })
public class WebSocketSourceIntegrationTests {
private String port = "8080";
#Test
public void testWebSocketStreamSource() throws IOException, InterruptedException {
StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
ClientWebSocketContainer clientWebSocketContainer = new ClientWebSocketContainer(webSocketClient,
"ws://localhost:" + port + "/some_websocket_path");
clientWebSocketContainer.start();
WebSocketSession session = clientWebSocketContainer.getSession(null);
session.sendMessage(new TextMessage("foo"));
System.out.println("Done****************************************************");
}
}
I have seen same issue here but nothing helped me. May I know what I'm missing ?
I have spring-boot-starter-tomcat as compile time dependency in the dependency Hierarchy.
This message says:
You need to configure at least 1 ServletWebServerFactory bean in the ApplicationContext, so if you already have spring-boot-starter-tomcat you need to either autoconfigure that bean or to do it manually.
So, in the test there are only 2 configuration classes to load the applicationContext, these are = { WebsocketSourceConfiguration.class, WebSocketSourceIntegrationTests.class }, then at least in one of these classes there should be a #Bean method returning an instance of the desired ServletWebServerFactory.
* SOLUTION *
Make sure to load all the beans within your configuration class
WebsocketSourceConfiguration {
#Bean
ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
}
OR also enable the AutoConfiguration to do a classpath scanning and auto-configuration of those beans.
#EnableAutoConfiguration
WebsocketSourceConfiguration
Can be done also at the Integration Test class.
#EnableAutoConfiguration
WebSocketSourceIntegrationTests
For more information check the SpringBootTest annotation documentation
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTest.html
in 2.0.5.RELEASE i faced a similar issue when I had the following.
package radon;
..
#SpringBootApplication
public class Initializer {
public static void main(String[] args) {
SpringApplication.run(Config.class, args);
}
}
package radon.app.config;
#Configuration
#ComponentScan({ "radon.app" })
public class Config {
..
}
Changing the package of Initializer from radon to radon.app fixed the issue.
this is because spring is not able to load the properties file at runtime, i was using spring profiles and wasn't providing the (program or vm) argument at runtime( java -jar application.jar) , adding vm argument of profile resolved the issue for me.
java -jar -Dspring.profiles.active=dev application.jar
or using program argument
java -jar application.jar --spring.profiles.active=prod --spring.config.location=c:\config
For web applications, extends *SpringBootServletInitializer* in main class.
#SpringBootApplication
public class YourAppliationName extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(YourAppliationName.class, args);
}
}

JavaConfig Spring make beans available to all application

I have a jar with a main method. I created a java config with the #Configuration annotation.
#Configuration
#ComponentScan(basePackages = { "com.test.commons" })
public class ProxyConfig {
}
In this com.test.commons I have put a service
package com.test.commons;
#Service
public class RestService {
//do rest calls
public String restGetCall(URL url){
...
}
}
I am NOT asking for this
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ProxyConfig.class);
context.getBean("myBean");
The main
#SpringBootApplication(scanBasePackages={"com.test.commons", "com.test.soapproxy" })
public class MainAppProxy
{
private final static Logger logger = LoggerFactory.getLogger(MainAppProxy.class);
public static void main( String[] args )
{
SpringApplication.run(MainAppProxy.class, args);
// Choose environment from the arguments
String env = args[0];
// publish an endpoint here
Configuration config = null;
config = configs.properties
(new File("config_"+env+".properties"));
Endpoint.publish(endpointAddress, new SomeProxyImpl(config));
The class in which I am trying to inject the bean (is the #Component needed here really?)
#Component
public class SomeProxyImpl implements SomeServiceSoap {
#Autowired RestService restService;
I would like to be able to inject this RestService bean through #Autowired in all my application, not only in SomeProxyImpl(which is not working anyway). How can I do that?
Spring don't autowire field created by a new, unless you ask for it, like this : ApplicationContextHolder.getContext().getAutowireCapableBeanFactory().autowireBean(object);
If your SomeProxyImpl class is in the "com.test.soapproxy" package, and your class is annotated with #Component, then Spring must have created an instance with the bean autowired.
You should then get this bean from your context and use it instead of creating a new one.

Using multiple dispatcher servlets / web contexts with spring boot

I created a spring boot application with a parent context (services) and child context (spring-webmvc controllers):
#Configuration
public class MainApiApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.parent(Services.class)
.child(ApiOne.class, MainApiApplication.class)
.run(args);
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
return new TomcatEmbeddedServletContainerFactory();
}
}
Now I want to add another client context (and DispatcherServlet) for my ApiTwo.class configuration. I think I have to do two things:
Move the servletContainer (thus the MainApiApplication.class configuration) out of the child context and
add a path mapping /one/ -> ApiOne.class and /two/ ApiTwo.class
What is the spring boot way to do it?
As #josh-ghiloni already said, you need to register a ServletRegistrationBean for every isolated web context you want to create.
You need to create an application context from a xml or java config class. You can use #Import and #ComponentScan annotation to add shared services to the parent context. Here is an example:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
//#ComponentScan({"..."})
//#Import({})
public class Starter {
public static void main(String[] args) throws Exception {
SpringApplication.run(Starter.class, args);
}
#Bean
public ServletRegistrationBean apiV1() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
XmlWebApplicationContext applicationContext = new XmlWebApplicationContext();
applicationContext.setConfigLocation("classpath:/META-INF/spring/webmvc-context.xml");
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/api/1/*");
servletRegistrationBean.setName("api-v1");
return servletRegistrationBean;
}
#Bean
public ServletRegistrationBean apiV2() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(ResourceConfig.class);
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/api/2/*");
servletRegistrationBean.setName("api-v2");
return servletRegistrationBean;
}
}
Create a ServletRegistrationBean that declares the servlet and its mappings. You will probably also want to exclude DispatcherServletAutoConfiguration from the autoconfigurations called, because it will register a DispatcherServlet at / and override yours
EDIT Despite my comment below saying you might not need this, unless you need your APIs running on separate ports (and it doesn't sound like you do), Dave Syer, one of the authors of Spring Boot, answered a very similar question here: Configure multiple servletcontainers/servlets with spring boot

Categories