The war application uses JEE/CDI/JAX RS stack, managed by/deployed to Wildfly.
I want to simplify some parts of the app, for instance to let Spring handle configuration from properties file. So I need to combine JEE with Spring together.
Here is Spring Configuration:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:test.properties")
public class Config {
#Value( "${hostName}" )
private String hostName;
public String getHostName() {
return hostName;
}
}
Config is injected via CDI annotion:
import javax.inject.Inject;
public class Consumer {
#Inject
Config config;
public String test(){
return "test +" + config.getHostName();
}
}
Config is injected successfully, but its getHostName method returns always null. In Maven dependencies I've included spring-core, spring-context, spring-beans to the dependencies.
Now the test.properties file does not exist in the classpath.
How to trigger the file loading by Spring and throw an exception that a file does not exist. To make sure it works.
Related
I have spring jar for my entity beans created as export--java--JARfile, where i have beans defined as follows:
package com.my.beans;
#Component("expBean")
public class ExpBean {
}
I also have config in this jar
package com.my;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class pBeanConfig {
#Bean
public ExpBean getExpBean() {
return new ExpBean();
}
}
I have client spring application where i am adding above jar having only beans as by external dependency and trying to get my beans when spring app starts using following code in main code in client spring app.
package com.my;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication()
#ComponentScan("com.my")
public class Application {
public static void main (String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
WMyBean bean = (WMyBean) ctx.getBean("expBean");
bean.doSomething();
}
}
But when checked the list of bean definition printed, i do not see my bean from external jar and also i get following error.
"Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'expBean' available"
I tried many options but not sure whats missing.
Option 1
#ComponentScan("com.my")
#SpringBootApplication
public class Application {
Option 2
#ComponentScan({"com.my"})
#SpringBootApplication
public class Application {
Need valuable inputs on step that i am missing out.
Question 1: Is it mandatory for me to do config change in client project to access my dependent jar entities?
Question 2: Can it be dynamic, load all entities without configuration as #bean in client project config?
Question 3: Do i need to build client jar every-time i change my dependent jar file with more entities?
Thanks
First I am assuming the build tool is Gradle, but you can import the jar as a local file like this for the sake of experiment:
dependencies {
implementation files('libs/something_local.jar')}
Then, assuming you use Eclipse you can right click the project folder and do a 'Gradle refresh'
In your config class you can either autowire in the bean for the class as a whole , or choose to create it whenever an instance of the class is created by declaring it within the class constructor:
import org.springframework.beans.factory.annotation.Autowired;
//I also have config in this jar
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class pBeanConfig {
//Autowire in the bean for the whole class.
#Autowired
ExpBean expBean;
public pBeanConfig() {
//Or, you may want to have a bean instance whenever the pBeanConfig class is instantiated.
ExBean exBean;
}
}
I'm making a number of assumptions for this answer like it's a Gradle project, but you can set up a similar dependency for Maven by modifying the POM file to include that file.
Build tools aside, in Eclipse you can simply add the jar file by right clicking the project and build path => configure build path => add external jar file.
UPDATE:
This pic describes how I set up the bean within a separate jar file via eclipse. The steps should show that the bean can be reached with this project even though it's in a jar and not in the project itself.
This question already has answers here:
How can I #Autowire a spring bean that was created from an external jar?
(5 answers)
Closed 3 years ago.
I have a Spring Boot project and I can't get components from an external jar to be autowired. When I try to, I got a org.springframework.beans.factory.NoSuchBeanDefinitionException saying that can't find a bean with that name available.
I tried some solutions found in similar questions, like these ones:
How to autowire #service from external Jar in Spring
Spring Boot #autowired does not work, classes in different package
How can I #Autowire a spring bean that was created from an external jar?
..but still can't managed it to work.
Here is an example of what I'm trying to accomplish:
Here is boot class in the Spring Boot project spring-project-example
package com.springdi.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import com.dependency.example.DependencyBasePackageClass;
import com.dependency.example.somepackage.SomeBean;
#SpringBootApplication
#ComponentScan(basePackages = {"com.springdi.example"}, basePackageClasses = DependencyBasePackageClass.class)
public class SpringProjectExampleApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringProjectExampleApplication.class, args);
String beanName = SomeBean.class.getName();
System.out.printf("%s can be autowired: %s\n", beanName, String.valueOf(context.containsBean(beanName)).toUpperCase());
}
}
It's just a simple Spring Boot project checking if it is possible to autowire a component present in the dependency jar.
Here is the component in the jar (dependency-example-1.0.0.jar)
package com.dependency.example.somepackage;
import org.springframework.stereotype.Component;
#Component
public class SomeBean {
public void someMethod() {
System.out.println("Some process...");
}
}
And here is the base package class of this same jar
package com.dependency.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Just a class to serve as the root for component
* scanning in "com.dependency.example" and its sub-packages
*/
#Configuration
#ComponentScan
public class DependencyBasePackageClass {
}
I've already tried #Import(DependencyBasePackageClass.class) in SpringProjectExampleApplication and #ComponentScan with basePackages and basePackageClasses, but no success.
I also tried using #SpringBootApplication(scanBasePackageClasses = {SpringProjectExampleApplication.class, DependencyBasePackageClass.class})
and the not type safe #SpringBootApplication(scanBasePackages = {"com.springdi.example", "com.dependency.example"}).
#Configuration #ComponentScan({"com.dependency.example"}) also fails, context.containsBean("com.dependency.example.somepackage.SomeBean") still returns false.
This jar is included in classpath and in the pom.xml as a dependency
<dependencies>
<!-- other dependencies -->
<dependency>
<groupId>com.rbaggio</groupId>
<artifactId>dependency-example</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/dependency-example-1.0.0.jar</systemPath>
</dependency>
</dependencies>
Could it be the location of the jar, the way it is included or some extra configuration needed?
I'd appreciate any help! Thanks in advance.
Okey some basic things, you have mixed up your packages a bit.
#SpringBootApplication will scan all classes in packages below the class this is annotated on. This annotation is an alias for #EnableAutoConfiguration, #Configuration and #ComponentScan means that #ComponentScan(basePackages = {"com.springdi.example"}, basePackageClasses = DependencyBasePackageClass.class) is not needed.
com.springdi.example // class with #SpringBootApplication annotation
|
|
|
com.springdi.example.* // Will find all #Service, #Component, #Configuration
// in subpackages below the #SpringBootApplication
// annotation
You can read more about the annotation here SpringBootApplication
Since your other annotated classes are NOT in the same package structure as the #SpringBootApplication you need to define all the places you want to scan for annotations.
#SpringBootApplication(scanBasePackages = {"com.springdi.example", "com.dependency.example"})
will probably include all the packages that you want to scan through.
I am new to Spring / Spring Boot, so please pardon if what I am asking is trivial.
I have created Spring Boot application which exposes the REST endpoint:
package com.atomic.contentguard;
...
#Controller
#RequestMapping("/rest")
public class AcgController {
#RequestMapping(value="/acg-status",method=RequestMethod.GET)
#ResponseBody
public String getStatus(){
return "Hi there!";
}
}
It all works fine when you run it as standalone Spring Boot application, the endpoint is testable by going to http://localhost:8080/rest/acg-status.
What I want to achieve is to "bring it" into another application, which would be including my application as a dependency in the pom.xml, expecting this REST endpoint to show up in it.
What I've done so far is included it in another project pom.xml as:
</dependencies>
...
<dependency>
<groupId>com.atomic</groupId>
<artifactId>contentguard</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
And then included it in that other application #ComponentScan section of config file:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.atomic.contentguard"})
public class EnvInfoWebConfig extends WebMvcConfigurerAdapter {
}
It does not however show up when you run target application:
No mapping found for HTTP request with URI [/other-application-context/rest/acg-status] in DispatcherServlet with name 'envinfo-dispatcher'
What am I missing / doing wrong?
You can do this simply by using the spring boot Application Launcher class in your main project as below (You don't need WebMvcConfigurerAdapter class):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan(basePackages = { "com.atomic.contentguard"})
public class AcgLauncher extends SpringBootServletInitializer {
//This method is required to launch the ACG application
public static void main(String[] args) {
// Launch Trainserv Application
SpringApplication.run(AcgLauncher.class, args);
}
}
Spring Boot uses this class during the server startup and scans the specified packages for all spring components (controllers, services, components).
I am studying for the Spring Core certification and I have the followind doubt with an exercice related to the beans configuration using the Java configuration way.
So I have the following RewardsConfig class that configure my beans (this class is into the application folder src/main/java):
package config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import rewards.RewardNetwork;
import rewards.internal.RewardNetworkImpl;
import rewards.internal.account.AccountRepository;
import rewards.internal.account.JdbcAccountRepository;
import rewards.internal.restaurant.JdbcRestaurantRepository;
import rewards.internal.restaurant.RestaurantRepository;
import rewards.internal.reward.JdbcRewardRepository;
import rewards.internal.reward.RewardRepository;
#Configuration
public class RewardsConfig {
#Autowired
DataSource dataSource;
#Bean
public RewardNetwork rewardNetwork(){
return new RewardNetworkImpl(accountRepository(), restaurantRepository(), rewardRepository());
}
#Bean
public AccountRepository accountRepository(){
JdbcAccountRepository repository = new JdbcAccountRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RestaurantRepository restaurantRepository(){
JdbcRestaurantRepository repository = new JdbcRestaurantRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RewardRepository rewardRepository(){
JdbcRewardRepository repository = new JdbcRewardRepository();
repository.setDataSource(dataSource);
return repository;
}
}
As you can see I declare 4 methods that are used to create 4 beans and that specify the dependency that occurs among these beans.
So I have a RewardNetwork bean that is implemented by RewardNetworkImpl class that depends from the following 3 beans: AccountRepository, RestaurantRepository and RewardRepository.
Is it the correct interpretation of the Java configuration is Spring?
Can I say for example that RewardNetwork is the declared bean and that RewardNetworkImpl its the current implementation of this bean?
All the 3beans (AccountRepository, RestaurantRepository and RewardRepository) depends by another bean dataSource that, as you can see in the previous code snippet, is declared as #Autowired:
#Autowired
DataSource dataSource;
This bean is not declared in this configuration class because it changes according to the environment (test, developt, production).
So, in my case it is declared into the unit test folder src/test/java:
package rewards;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
#Configuration
public class TestInfrastructureConfig {
/**
* Creates an in-memory "rewards" database populated
* with test data for fast testing
*/
#Bean
public DataSource dataSource(){
return
(new EmbeddedDatabaseBuilder())
.addScript("classpath:rewards/testdb/schema.sql")
.addScript("classpath:rewards/testdb/test-data.sql")
.build();
}
}
So the dataSource bean define a datasource that is valid only for the test environment (used when I perform a unit test).
Now my doubt is: I have 2 different configuration classes and the dataSource bean is not definied into the RewardsConfig configuration class that contains the 3 beans that use it. Why I can't not use the #Import annotation to use it into RewardsConfig?
Something like it:
#Import(TestInfrastructureConfig.class)
How it work exactly?
Tnx
You don't have to import beans to make them available for autowiring. #Import is used to add extra configuration classes.
You really don't want to hard-import a test configuration class, because then your production code is referring to test-only code (and, in this case, always activating it). Instead, think of your configuration class more like an abstract class: declare autowired beans, but don't worry about how they get there. The downstream (runtime) configuration will supply them, and you don't need to know how. Maybe you're supplying an in-memory H2 for testing and using Spring Cloud Connectors for actual runs, doesn't matter.
I have a hard time loading properties from my properties file. I'm trying to load a property into my configuration class which is listed below.
package dk.fitfit.budget.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:application.properties")
public class ApplicationConfig {
private static final Logger logger = LoggerFactory.getLogger(ApplicationConfig.class);
#Autowired
private Environment env;
#Value("${snot:test}")
private String snot;
public ApplicationConfig() {
logger.info("Application config loaded!"); // Displays as expected
logger.info("snot: {}", snot); // snot: null
logger.info("env: {}", env); // env: null
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If I change the name of the file (application.properties) or "#PropertySource("classpath:application.properties")" I get an error about the file not being present. So clearly it's being loaded (to some extend anyway).
I was at least expecting to see the default string "test" injected into the variable snot. But not even that is done. I'm not able to autowire Environment either... not sure if there is a relation.
The content of my application.properties is as below.
snot=snog
My application.properties file is placed in src/main/resources/.
Anyone got a clue about what I'm doing wrong?
The purpose of a constructor is to initialize a class instance. You have to construct and initialize the class instance before Spring can do anything to it. As such, you cannot expect the env and snot fields to have anything other than the default null value within the constructor.
Spring (and all dependency-injection tools) can't inject fields before the instance is created with new, which is one reason why constructor injection should be preferred to field injection. Generally, you can use #Value on constructor parameters, but there are certain issues with #Configuration classes that make constructor injection impractical.
However, there's a solution meant for exactly this situation: #PostConstruct. This method will be called after the DI framework has done all its magic to the bean:
#PostConstruct
public void log() {
logger.info("Application config loaded!"); // Displays as expected
logger.info("snot: {}", snot); // snot: null
logger.info("env: {}", env); // env: null
}