I am trying to make an application that uses Spring annotations to import the configurations. For this question i narrowed it down to two files. The Startup class:
package core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Slf4j
#Configuration
#Import(ConfigSettings.class)
public class Startup {
public static void main (String args[]) {
log.info("main class");
}
}
and the ConfigSettings
package core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Slf4j
#Configuration
#ComponentScan({"connections", "filter"})
#PropertySource({"classpath:config/${env.config:dev}.application.properties"})
public class ConfigSettings {
public ConfigSettings() {
log.info("Constructor ConfigSettings");
}
}
I expected the outcome to be:
[INFO]Constructor ConfigSettings
[INFO]main class
But it only shows mainclass. It looks like the constructor of the config settings is not called at all. I expect it to call it because of the import annotation.
Can anyone explain what is going wrong? Thank you in advance!
Your best bet is to make the config class return config object that contains your values. Generally I don't tend to add an all-encompassing config object, but have a config file for each component (database, controllers, etc...).
You can then return the configured object as a bean and let spring inject it. If I were to make a config file for a RestTemplate (as a simple example):
#Service
public class RestClientConfig {
#Value("${your.config.value}")
private String yourValue;
private final RestTemplate restTemplate = new RestTemplate();
#Bean
public RestTemplate restTemplate() {
// Configure it, using your imported values
// ...
return restTemplate;
}
}
However, the main method is outside of the spring container and you won't be able to bootstrap it that way, but with the above method you can call the configured component directly where you need to use it.
Related
Could you tell me why does a method annotated with #PostContruct runs in my case? As far as I know, a method with #PostContruct is processed by Bean Post Processors. If you want to activate the default CommonAnnotationBeanPostProcessor you need to add <context:annotation-config/> in the XML configuration but I want to use only annotation config. In my case, #ComponentScan in configuration is pointed to the service. It means that only classes from this package candidates to be instantiated.
Configuration class:
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "service")
public class AppConfig {
}
Simple class:
package service;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
#Component
public class Simple {
#PostConstruct
private void sout(){
System.out.println("SOUT");
}
}
And the launcher:
import config.AppConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import service.Simple;
public class Launcher {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Simple simple = ctx.getBean("simple", Simple.class);
}
}
The output of the app is "SOUT". Could you explain be who invokes the #PostContruct method and how?
AnnotationConfigApplicationContext is an alternative to XML based configurations. When you use it to create objects, it first creates the object and the #Autowired properties, and then calls the #PostConstruct method. It is a handy alternative to writing a setup() or init() method which you would have to call yourself.
RESOLVED - see answer.
I've looked through many similar questions and don't see a similar case right off. Certainly this isn't a unique situation and I'm just missing it?
Update A Spring example I found shows a priority property that may help here, but I have only found the XML example. Question expanded below.
Problem Summary
Two view resolvers appear to be conflicting in my SpringWebMVC application.
Problem Details
I'm work on a web app using Spring 4.0.3-RELEASE and have recently added Jackson to support returning Json from calls to a specific controller. This was working until I added an #Override to my SpringWebConfig for configureViewResolvers. Now my calls to my controller which was serving Json just return the template name which should call the Jackson mapper bean.
The big question
How can I make these two coexist? I have found that I can call:
registry.order(int)
and set it to 9 just to make sure it was last, but it still intercepted the jsonTemplate response from the controller. I don't see a way to set an order for the MappingJackson2JsonView bean. #Bean(order=0), for example, is invalid.
Things Tried
Redacting the ViewResolverRegistry, as expected, produces an error when trying to get mapped jsp views.
javax.servlet.ServletException: Could not resolve view with name 'someView' in servlet with name 'spring-mvc-dispatcher'
As noted in the question statement above, I've tried setting the order on the registry for the ViewResolverRegistry, but this did not help.
I also have tried adding the following to the MappingJackson2JsonView instance, view:
Properties props = new Properties();
props.put("order", 1);
view.setAttributes(props);
But as before, this doesn't prevent the ViewResolverRegistry from intercepting "jsonTemplate" before the Jackson mapper can process it.
I also have changed the load order of the configs in the AppInitializer, the code below has been updated to reflect the new load order, but this also did not help.
Reading through the Spring documentation a bit more, it appears that adding a ContentNegotiationConfigurer is going to be what I need to resolve this and I'm presently looking at how to get this to work in a way that preserves auto mapping the Model returned to the jsonTemplate view. Exapmles I've seen so far use a jsp as a view with specific properties called out, which defeats the purpose of using a Json Mapper.
Configuration
I have multiple config classes defined in my package com.mytest.config.
AppInitializer.java handles adding the *config classes to the context.
package com.mytest.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class AppInitializer implements WebApplicationInitializer {
private Logger logger = LoggerFactory.getLogger(AppInitializer.class);
#Override
public void onStartup(ServletContext container) throws ServletException {
try {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(JSONConfiguration.class);
ctx.register(SpringWebConfig.class);
ctx.setServletContext(container);
container.addListener(new ContextLoaderListener(ctx));
container.addListener(new RequestContextListener());
logger.info("Created AnnotationConfigWebApplicationContext");
ServletRegistration.Dynamic dispatcher = container.addServlet("spring-mvc-dispatcher", new DispatcherServlet(ctx));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
logger.info("DispatcherServlet added to AnnotationConfigWebApplicationContext");
} catch (Exception e) {
logger.error(e.getLocalizedMessage(), e);
}
}
}
SpringWebConfig.java is where I register the majority of my beans.
package com.mytest.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages={"com.mytest.controller","com.mytest.bean","com.mytest.model"})
#PropertySource(value={"classpath:application.properties"})
public class SpringWebConfig extends WebMvcConfigurerAdapter {
#Autowired
private Environment env;
private Logger logger = LoggerFactory.getLogger(SpringWebConfig.class);
// bunches of beans such as JdbcTemplate, DataSource... omitted for simplicity
#Override // apparent problem location -- needed for jsp resolving
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/html/",".jsp");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
logger.info("DefaultServletHandlerConfigurer enabled");
}
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new com.honda.hrao.rid.config.RequestInterceptor());
logger.info("RequestInterceptor added to InterceptorRegistry");
}
}
JSONConfiguration.java is a controller I set up just for JSON.
package com.mytest.config;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.BeanNameViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
#Configuration
#ComponentScan(basePackages = {"com.mytest.controller"})
#EnableWebMvc
public class JSONConfiguration {
private Logger logger = LoggerFactory.getLogger(JSONConfiguration.class);
#Bean // needed for JSON conversion of bean responses
public View jsonTemplate() {
logger.info("Registered MappingJackson2JsonView");
MappingJackson2JsonView view = new MappingJackson2JsonView();
Properties props = new Properties();
props.put("order", 1);
view.setAttributes(props);
view.setPrettyPrint(true);
return view;
}
#Bean
public ViewResolver viewResolver() {
logger.info("Starting ViewResolver bean");
return new BeanNameViewResolver();
}
}
Implementation
In my Controller, the following method should return JSON.
#Autowired
AppConstants appConstants;
#RequestMapping(method = RequestMethod.GET, value = "getAppConstants")
public String getAppConstants(Model model) {
model.addAttribute("AppConstants",appConstants);
if(appConstants==null) {
Logger.error("appConstants not autowired!!!");
return null;
}
return "jsonTemplate";
}
As mentioned above in Things Tried, this works fine if I remove the ViewResolverRegistry bean from the SpringWebConfig and if I leave the bean in place, the above controller method returns
404, /WEB-INF/views/html/jsonTemplate.jsp
The requested resource is not available.
-- which I understand. That's what the view resolver should do. How do I make my JSON calls bypass this?
It turns out there were only a couple of things missing. The first was to add the following annotation to the bean declaration for the mapper:
#Primary
So now, the bean setup looks like this.
#Bean // needed for JSON conversion of bean responses
#Primary
public View jsonTemplate() {
logger.info("Registered MappingJackson2JsonView");
MappingJackson2JsonView view = new MappingJackson2JsonView();
Properties props = new Properties();
props.put("order", 1);
view.setAttributes(props);
view.setPrettyPrint(true);
return view;
}
The second was to use a ContentNegotiationConfigurer. In my SpringWebConfig, I added the following:
public void configurationContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.ignoreUnknownPathExtensions(false)
.defaultContentType(MediaType.TEXT_HTML);
}
and changed my configureViewResolvers function as follows:
#Override // needed for jsp resolving
public void configureViewResolvers(final ViewResolverRegistry registry) {
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setPrettyPrint(true);
registry.enableContentNegotiation(view);
registry.jsp("/WEB-INF/views/html/",".jsp");
}
One clue was found in this example. The rest came from the Spring documentation.
I have a utils class that use #Autowired to inject a repository using spring-boot-starter-data-jpa. But when I used this repository to access the database, it said the repository is null. I used the same method in my controller and it works well. And here is my Utils.class
package com.example.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import com.example.dao.RuleRepository;
import com.example.model.Project;
import com.example.model.Rule;
public class Judge {
#Autowired
RuleRepository ruleRepository;
public boolean ageJudge(Project project) {
try {
if (ruleRepository == null)
{
System.out.println("yes");
}else {
System.out.println("false");
}
return false;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
}
Here is my Application.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#ComponentScan(basePackages = {"com.example"})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
This is the RuleRepository.java
package com.example.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.model.Project;
import com.example.model.Rule;
public interface RuleRepository extends JpaRepository<Rule, Integer>{
Rule findById(Integer id);
Rule findByRuleName(String ruleName);
}
It is the directory.
The RuleRepository works well in controller. So, what is the problem?
Your util class Judge is a plain POJO not a Spring bean and you can only inject Spring beans inside another Spring beans not Plain POJOs.
If you wish to use your ruleRepository bean inside Judge then make it a Spring component using #Component annotation:
#Component
public class Judge {
#Autowired
RuleRepository ruleRepository;
..............................
}
User #Service annotation of Judge class is acting as business logic implementation class.
Your Judge should be annotated #Component
#Component
public class Judge{
// ...
}
so that Spring will instantiate a Judge bean and it will be available for injection. You can then use that judge bean in any managed bean (e.g: a controller)
// SomeController
#Autowired
Judge judge;
But if you instantiate judge object your self, like this:
Judge judge2 = new Judge();
your repository will be null, be cause Spring have nothing to do with judge2 object, it is not managed by Spring.
You need to make your Judge class at least a #Component of your project, which will make your class managed by Spring, therefore your RuleRepository will be instantiated.
If it doesn't work on first try, you will have to add your com.example.controller package in the list of packages to scan, in the #ComponentScan annotation
First as everyone mention your class Judge does not have #Component annotations.
The other thing is, maybe my Spring is getting little bit rusty.
But as far as I remember, I think your repository also require to have #Component or #Repository annotation
I've seen a lot of questions about this error before, but no resolution that works for me.
I'm new to Spring, but trying to use the Spring Data for Neo4J library for a project. I decided to start with a quick spike to make sure I know how everything is working, and so I set up a simple App class with a main method like so:
package org.example.neo4jSpike;
import org.example.neo4jSpike.domain.Actor;
import org.example.neo4jSpike.repositories.ActorRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
/**
* Hello world!
*
*/
#Component
public class App
{
#Autowired
private ActorRepository actors;
#SuppressWarnings("resource")
public static void main( String[] args )
{
ApplicationContext context = new AnnotationConfigApplicationContext(SpikeConfiguration.class);
App a = context.getBean(App.class);
a.init();
}
private void init(){
Actor michaelDouglas = actors.save(new Actor("Michael Douglas"));
System.out.println( "Hello World!" );
System.out.println(michaelDouglas.getId());
System.out.println("Total people: " + actors.count());
}
}
I have the configuration class setup as well:
package org.example.neo4jSpike;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableNeo4jRepositories(basePackages = "org.example.neo4jSpike.repositories")
#EnableTransactionManagement
public class SpikeConfiguration extends Neo4jConfiguration{
#Bean
public SessionFactory getSessionFactory() {
// with domain entity base package(s)
return new SessionFactory("org.example.neo4jSpike.domain");
}
// needed for session in view in web-applications
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
}
I'll add the code for my repositories and domain classes if needed, but they're all set up in a similar manner, and are all pretty simple.
When I try and run the main, however, I get
No qualifying bean of type [org.example.neo4jSpike.App] is defined
I don't see how it's not defined, it's right there, defined as an #Component. What am I misunderstanding?
Doesn't matter if you put the #Component annotation if Spring is not scanning your class package. You can add a #ComponentScan annotation in you configuration class and configure it to scan the package where your App class is located. Alternatively you can remove the #Component annotation and declare a Bean of type App in the configuration class.
Hope this can help.
I'm trying to set up my profiles using YAML configuration files. Rather than having different #Configuration classes annotated with #Profile for different ones (or even a same #Configuration class with different annotated #Beans), i would like to have one #Configuration class using Placeholder with YAML configuration file.
Taking a look at Spring boot documentation, YamlPropertiesFactoryBean javadoc and this Stackoverflow topic, i've came up with the following code:
A simple YAML file: application-default-config.yml
foo: myFoo
A #Configuration file:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
#Configuration
public class ApplicationConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer ymlProperties() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer =
new PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("config/application-${spring.profiles.active:default}-config.yml"));
propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
return propertySourcesPlaceholderConfigurer;
}
#Value("${foo}")
private String fooValue;
#Bean
public String fooValue() {
return fooValue;
}
}
A test file:
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationConfig.class)
public class ApplicationConfigTest {
#Autowired
Environment environment;
#Autowired
String fooValue;
#Test
public void testFooViaEnvironment() {
Assert.assertEquals("myFoo", environment.getProperty("foo"));
}
#Test
public void testFooViaWiredValue() {
Assert.assertEquals("myFoo", fooValue);
}
}
And, finally, a main file:
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.ClassPathResource;
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ApplicationConfig.class);
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("config/application-${spring.profiles.active:default}-config.yml"));
ConfigurableEnvironment environment = ctx.getEnvironment();
environment.getPropertySources()
.addFirst(new PropertiesPropertySource("custom", yaml.getObject()));
ctx.refresh();
String fooViaWiredValue = (String) ctx.getBean("fooValue");
System.out.println(String.format("fooViaEnvironment=%s", environment.getProperty("foo")));
System.out.println(String.format("fooViaWiredValue=%s", fooViaWiredValue));
}
}
I've noticed that in my test class, i can access the 'foo' property via #Value annotation but not via Environment.
As well explained in some Stackoverflow topics like this and that, annotating a PropertySourcesPlaceholderConfigurer as a #Bean is not enough to tight it with the Environment. It is necessary to do it programmatically as i did in Application class. In contrast, annotated #Values are wired accordingly.
My questions are, in fact, about best practices working with Spring.
I would prefer using Environment rather than #Value annotation.
Is there any way to load my YAML configuration files into Spring application context inside a test environment without needing to do it programmatically as i did in Application class ?
Is there any reason i should prefer using #Value (or any other profile configuration) over Environment + YAML configuration files ? As i said, i would like to have a unique #Bean using placeholders rather than multiples #Beans annotated with #Profiles.
I found this SPR where Chris Beams states why PropertySourcesPlaceholderConfigurer should not be registered automatically by Spring and why one should prefer user Environment over #value.
In addition, on Spring reference documentation, the Spring Testing Annotation section shows how to use ApplicationContextInitializer, which could be used to setup YamlPropertiesFactoryBean properly.