Annotated a class with #Configuration and #Controller. Need help in refactoring - java

Below is my class in which i had to use both #Configuration and #Controller as there should be only one instance of Thymeleaf in the entire application else i get exceptions for that. My other classes are annotated with #RequestScope so i cannot use a singleton scoped bean. So i had a mixup of Configuration and Controller to get the result, but i feel it is a bad practice. I would appreciate any help to refactor the code and remove the bad practice.
UPDATE
I am using spring-boot 1.5.14. I am using the following approach to process a template and keep the processed template as string.
#Controller
#Configuration
#EnableWebMvc
#ApplicationScope
public class MyThymeleafConfig {
#GetMapping("/view-template")
#ResponseBody
public void viewTemplates() {
Context context = new Context();
context.setVariable("mydata", "this is it");
String html = templateEngine().process("templates/view-to-process.html", context);
System.out.println(html);
}
/*
configuration for thymeleaf and template processing
*/
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
}
#Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("classpath:");
templateResolver.setSuffix(".html");
templateResolver.setCacheable(false);
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
}
To serve static resources the following config:
#Configuration
#EnableWebMvc
public class StaticResourceConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/**")
.addResourceLocations("/static/", "classpath:static/");
}
}
UPDATE
I have also mentioned the reasons why i couldn't accept the below mentioned answers as my other classes have request scopes.
UPDATE
I have other classes with #RequestScopelike below:
#RequestScope
#Controller
public class SecondController {
#GetMapping("/viewPage")
public String viewPage(Model model) {
model.addAttribute("mydata", "sjfbsdf");
model.addAttribute("somedata", "sjdfksfjhshgdfbskdfj");
return "templates/view-to-process.html";
}
}

Assuming you're using Spring Boot, since you have it in tags, you do not need any configuration to use Thymeleaf.
By just having this dependency, you can:
#GetMapping("/view-template")
public String viewTemplates(Model model) {
model.addAttribute("mydata", "this is it")
return "view-to-process";
}
And it should work.
By the way, yes, having #Configuration and #Controller in the same class is something you should never need.

If you see the source codes of the annotations (Spring 5) you have:
Controller
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* #return the suggested component name, if any (or empty String otherwise)
*/
#AliasFor(annotation = Component.class)
String value() default "";
}
Configuration
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated
* with this Configuration class. If left unspecified (the common case),
* a bean name will be automatically generated.
* <p>The custom name applies only if the Configuration class is picked up via
* component scanning or supplied directly to a {#link AnnotationConfigApplicationContext}.
* If the Configuration class is registered as a traditional XML bean definition,
* the name/id of the bean element will take precedence.
* #return the suggested component name, if any (or empty String otherwise)
* #see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
#AliasFor(annotation = Component.class)
String value() default "";
}
you notice that they are the same (they both include the more generic #Component annotation). So it doesn't make sense to use them both by seeing this fact. Another thing, more important, is that spring is trying to give a sort of tags meaning of these annotations that should describe the use.
The Configuration is used to wire in necessary parts to the application to function properly, at startup phase.
The Controller is used to define a class which is serving as an interface to the outside world, i.e: how can other actors use your application.
As you can see, it makes very little sense to use those 2 together.

Take look at Spring Boot documentation typical layout
Also this article SOLID Programming Principles
And look at Spring Boot guide Spring Boot Thymeleaf (you don't need your #Bean configurations)
In two words you should separate
1. MyThymeleafConfig configuration
2. TemplateController with viewTemplates() and another endpoints

To refactor them, it's easy and straight forward to separate #Bean methods to a separate #Configuration class:
#Configuration
// #Controller is redundant as we have #Configuration
// #EnableWebMvc is also redundant since you already annotate it in other class
// #ApplicationScope is also redundant since you do not need to create bean of MyThymeleafConfig anymore
public class MyThymeleafConfig {
/*
configuration for thymeleaf and template processing
*/
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
}
#Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("classpath:");
templateResolver.setSuffix(".html");
templateResolver.setCacheable(false);
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
}
#Configuration class return the same bean instance, regardless how many times you call the bean methods.
Now in your controller:
#Controller
public class MyThymeleafConfig {
#Autowired
private SpringTemplateEngine templateEngine;
#GetMapping("/view-template")
#ResponseBody
public void viewTemplates() {
Context context = new Context();
context.setVariable("mydata", "this is it");
String html = templateEngine.process("templates/view-to-process.html", context);
System.out.println(html);
}
}
But honestly, I don't know why you have to manually interact with TemplateEngine / SpringTemplateEngine, since Spring-thymeleaf will automatically process the template with given variable for you. (Like #sedooe example)

Don't put request mappings inside configuration classes, it violates the principal of separation of concerns. You can go for a approach like below.
All the application wide beans are setup in Application class which is present in the root of the classpath. Application class in the best place to have your thymeleaf and static resource configurations too, since the Application class have application-scope.
#SpringBootApplication
#EnableWebMvc
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
resolver.setCache(false);
return resolver;
}
#Bean
public TemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setEnableSpringELCompiler(true);
templateEngine.addDialect(new LayoutDialect());
templateEngine.addDialect(new Java8TimeDialect());
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new
SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/templates/");
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
}
If you put the static resources inside a folder named static or public in the classpath, springboot identify that as the location for static resources. Then you don't need to override addResourceHandlers method. If you really want to do it, you can do it inside the Application class extending WebMvcConfigurerAdapter. You don't need separate class to configure just static resource paths.
Don't put request mappings inside configuration classes, put them in separate controller classes like:
#Controller
public class MyController {
#GetMapping("/view-template")
#ResponseBody
public void viewTemplates() {
Context context = new Context();
context.setVariable("mydata", "this is it");
String html = templateEngine().process("templates/view-to-process.html", context);
System.out.println(html);
}
}
Of cause, springboot allows you to do it the way you like, but you'd better stick to a general approach.

These two annotations are for different things, thus it's better to not use them on the same class. Cause it's against Separation of Concerns principal.

Related

Respect #Lazy annotation on non-#Primary #Bean

I'm having problems getting Spring to respect the #Lazy annotation on #Bean methods when it is configured to use a different #Bean method that returns an implementation of the same interface that is flagged as #Primary.
Specifically, I have a #Configuration-annotated class with several #Bean methods that all return the same interface. Many of these #Bean methods are #Lazy, as they contact external services for which the application may not currently be using. The #Primary bean is not #Lazy, as it looks at runtime configuration to determine which implementation to return.
Here is a contrived example of that configuration class, revolving around a fictitious ThingService interface:
#Configuration
#ComponentScan(basePackages = { "com.things" })
public class ThingConfiguration {
#Bean
public ThingOptions thingOptions() {
ThingOptions options = new ThingOptions();
options.sharing = true;
return options;
}
#Primary
#Bean
public ThingService primaryThing(ThingOptions options, ApplicationContext context) {
System.out.println("PrimaryThing -- Initialized");
if (options.sharing) {
return context.getBean("OurThing", ThingService.class);
} else {
return context.getBean("YourThing", ThingService.class);
}
}
#Lazy
#Bean(name = "YourThing")
public ThingService yourThing() {
System.out.println("YourThingService -- Initialized");
return new YourThingService();
}
#Lazy
#Bean(name = "OurThing")
public ThingService ourThing() {
System.out.println("OurThingService -- Initialized");
return new OurThingService();
}
}
I then have a #Component that depends on this interface which that the #Primary annotation will ensure that the correct implementation will be injected into the object. Here is an example of that downstream #Component:
#Component
public class ThingComponent {
private final ThingService thingService;
#Inject
public ThingComponent(ThingService thingService) {
this.thingService = thingService;
}
}
I then built a small test to ensure that #Lazy and #Primary are all being respected.
public class ThingTest {
#Test
public void TestLazyAndPrimary() {
// Arrange
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ThingConfiguration.class);
context.refresh();
// Act
ThingComponent component = context.getBean(ThingComponent.class);
// Assert
Assert.assertNotNull(component);
}
}
However, when I run this test, I found that #Lazy was being ignored. The following text is emitted to the console:
PrimaryThing -- Initialized
OurThingService -- Initialized
YourThingService -- Initialized
The "YourThing" #Bean should not have been initialized, as it was #Lazy and not loaded at runtime via the ApplicationContext.getBean() method. Yet when the ThingComponent is resolved, it causes the #Bean methods with that return an implementation of ThingService to be hydrated before the #Primary mean is chosen.
How do I get the #Primary annotated implementation of an interface to be respected without causing all of the non-#Primary implementations annotated with #Lazy to be hydrated?
I have been unable to stop the #Primary annotation from forcing eager hydration of all #Bean methods that return that interface, even though this information seems available without forcing hydration from the annotations in exclusivity. I got around this by using a naming convention on #Bean methods instead.
Specifically, I changed my #Primary annotated #Bean method to include a name like so:
#Configuration
#ComponentScan(basePackages = { "com.things" })
public class ThingConfiguration {
// #Primary -- I don't want someone to accidentally use this without a #Qualifier!
#Bean(name = "PrimaryThingService")
public ThingService primaryThing(ThingOptions options, ApplicationContext context) {
System.out.println("PrimaryThing -- Initialized");
if (options.sharing) {
return context.getBean("OurThing", ThingService.class);
} else {
return context.getBean("YourThing", ThingService.class);
}
}
// ... the rest of the methods removed for clarity ...
}
Then I placed a #Qualifier on the ThingService being injected into the #Component like so:
#Component
public class ThingComponent {
private final ThingService thingService;
#Inject
public ThingComponent(#Qualifier("PrimaryThingService") ThingService thingService) {
this.thingService = thingService;
}
}
Now when I rerun the test, I get the following output:
PrimaryThing -- Initialized
OurThingService -- Initialized
So this removes the #Primary annotation in place of using a named #Bean following a convention of "Primary{Interface}", stepping around the Spring's overeager hydration of non-#Primary annotated #Bean methods.

swagger for spring: No mapping found

I am trying to make a documentation of my API with swagger.
My REST API is written with the help of spring and typical Controller looks like:
#Controller
#RequestMapping("/mymapping")
public class ParameterController {
#Autowired
Mydao myDao;
/**
* GET list
*
* #return The resulting string
*/
//produces replaced accept-header
#RequestMapping(value="/Parameter", method=RequestMethod.GET, produces={MediaType.APPLICATION_JSON_UTF8_VALUE, "application/ld+json"})
public #ResponseBody List<String> getAllParameters() {
List<String> params= myDao.getAllParameters();
return params;
}
}
To configurate the Swagger I followed the instruction here:
added a package in Maven
created a configuration Java class SwaggerConfig (I use only Java-configuration for Spring)
added resource Handlers to the existing class extending WebMvcConfigurerAdapter
added packages "io.swagger" and "springfox.documentation" to the #ComponentScan annotation of the same class extending WebMvcConfigurerAdapter
But each time when I call http://localhost:8080/spring-security-rest/api/v2/api-docs I receive
16:43:43,370 WARN qtp1072754634-13 servlet.PageNotFound:1136 - No mapping found for HTTP request with URI [/spring-security-rest/api/v2/api-docs] in DispatcherServlet with name 'org.springframework.web.servlet.DispatcherServlet-437a7f13'
The system searches the path /spring-security-rest/api/v2/api-docs among the controllers' mappings. And does not find it.
What did I forget?
Thank you in advance.
I solved mine by placing the SwaggerConfig Java Class in the same package where I had a the class with #SpringBootApplication.
Heres a suggestion:
Typically this is due to improper use of component scan. You can read more about component scan here (read the 2 questions fully :) )
Click here for the 2 questions
my SwaggerConfig class look like this:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket beerMapApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(regex("/api.*"))
.build()
.pathMapping("/")
.apiInfo(apiInfo());
}
#Bean
public UiConfiguration uiConfig() {
return UiConfiguration.DEFAULT;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("title")
.description("description")
.version("0.1")
.build();
}
}
and this is how my WebMvcConfigurerAdapter.addResourceHandlers looks:
#Configuration
#EnableWebMvc
#ComponentScan({ "com.something.server.controller" })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
registry
.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}

Fastest way to deliver jsp views without xml-conf using spring project stub from start.spring.io

I am using spring again after a longer period and happy to see that xml configurations are no longer required in every case.
I want to build a RESTful App, but I still have to deliver the frontend app. I figured the simplest way without using any additional template engines like thymeleaf would be serving a static jsp.
I'm using the project template from start.spring.io with just spring-mvc as dependency, thus i'm using spring boot as well.
I wrote a controller in order to deliver the jsp, but it seems that the mapping for the views has to be configured first.
#Controller
public class StaticPagesController {
#RequestMapping(value = "/")
public String index(){
return "index";
}
}
So i created a configuration class:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "de.tuberlin.sense.emp")
public class WebConfiguration {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".html");
return viewResolver;
}
}
index.html is located in main/webapp/WEB-INF/views/
When i send a request to /, I get a WARN in the logs wich states No mapping found for HTTP request with URI [/WEB-INF/views/index.html] in DispatcherServlet with name 'dispatcherServlet'
What am I missing? Can I do this without any xml configuration?
Here is my main application class code:
UPDATE:
#SpringBootApplication
public class ExperimentManagementPlatformApplication {
public static void main(String[] args) {
SpringApplication.run(ExperimentManagementPlatformApplication.class, args);
}
}
Try adding a view controller:
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
Reference
It seems to me that your controller is not being detected, I would suggest to check your file structure, is the StaticPagesController controller in the de.tuberlin.sense.emp package? (as stated in:)
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "de.tuberlin.sense.emp")
public class WebConfiguration
I was able to serve the static page without needing any controller at all by just adding the html file to the static directory. Furthermore i found out that the entire webapp directory is not included when the app is deployed as jar which explains why the files in there could not be found.
The Spring documentation
Your config class should extend WebMvcConfigurerAdapter class
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "de.tuberlin.sense.emp")
public class WebConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".html");
return viewResolver;
}
}

Spring configuration without any XMLs at all

I want to create a spring app that doesn't use any XMLs at all (no web.xml no context.xml or anything). So far it seems to work quite fine, except that my view resolver has some problems and I cannot figure it out on my own.
here's my WebApplicationInitializer
public class AppConfig implements WebApplicationInitializer {
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("fi.cogniti.service.config");
return context;
}
#Override
public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(
context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
// Enabling spring security
// servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
// .addMappingForUrlPatterns(null, false, "/*");
}
}
and my spring configuration
#Configuration
#EnableWebMvc
#ComponentScan("fi.cogniti.service")
public class SpringConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/pages/");
resolver.setSuffix(".jsp");
return resolver;
}
}
and finally my controller
#Controller
#RequestMapping("/")
public class HomeController {
#RequestMapping
public String entry() {
return "index";
}
}
index.jsp is located in src/main/webapp/pages/index.jsp.
So, if in my controller I use the annotation #ResponseBody, then the controller gives me the response "index", hence I know that my configuration works at least to some extent, however, if I remove the annotation in hopes that it would return the content of index.jsp, I only get a 404 error.
Any suggestions?
Change:
dispatcher.addMapping("/*");
To something that doesn't match everything (you will get this error otherwise). For example:
dispatcher.addMapping("*.html");
In this way, http://localhost:8080/.html should show the jsp (change the #RequestMapping("/") in the controller to something more 'human')
You also should change this line including WEB-INF:
resolver.setPrefix("/WEB-INF/pages/");
I'm not sure if WebApplicationInitializer is getting executed (check this, where they are suggesting to use ServletContextInitializer but it's still creating issues).
If this is the case, you couldn't use the .addMapping("*.html"). If this is the case, you can add the following lines to SpringConfig in order to achieve the same result:
#Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(
dispatcherServlet(), "*.html");
registration
.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
return registration;
}
Currently all your jsp files are accessible by everyone, it is recommended to put them in WEB-INF instead at the top level.
Then modify your configuration for the view resolver to the following.
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".jsp");
return resolver;
}
You don't need to set the viewClass property as that is determined for you.
Next add the following to have the DispatcherServlet pass on requests that it cannot handle itself. This is needed due to the fact that you mapped the servlet to /.
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
With these 2 changes you have secured your jsps from being accessible by everyone and made default resources being handled again.
I would strongly suggest using Spring Boot as that would really simplify your life.
In your controller the method should look like:
#RequestMapping(method = RequestMethod.GET)
public ModelAndView entry() {
return new ModelAndView("index");
}

Spring JavaConfig properties in bean is not getting set?

I am looking at using Spring JavaConfig with some property files but properties in bean is not getting set?in bean is not getting set?
Here is my WebConfig:
#Configuration
#EnableWebMvc
#PropertySource(value = "classpath:application.properties")
#Import(DatabaseConfig.class)
#ImportResource("/WEB-INF/applicationContext.xml")
public class WebMVCConfig extends WebMvcConfigurerAdapter {
private static final String MESSAGE_SOURCE = "/WEB-INF/classes/messages";
private static final Logger logger = LoggerFactory.getLogger(WebMVCConfig.class);
#Value("${rt.setPassword}")
private String RTPassword;
#Value("${rt.setUrl}")
private String RTURL;
#Value("${rt.setUser}")
private String RTUser;
#Bean
public ViewResolver resolver() {
UrlBasedViewResolver url = new UrlBasedViewResolver();
url.setPrefix("/WEB-INF/view/");
url.setViewClass(JstlView.class);
url.setSuffix(".jsp");
return url;
}
#Bean(name = "messageSource")
public MessageSource configureMessageSource() {
logger.debug("setting up message source");
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename(MESSAGE_SOURCE);
messageSource.setCacheSeconds(5);
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver lr = new SessionLocaleResolver();
lr.setDefaultLocale(Locale.ENGLISH);
return lr;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
logger.debug("setting up resource handlers");
registry.addResourceHandler("/resources/").addResourceLocations("/resources/**");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
logger.debug("configureDefaultServletHandling");
configurer.enable();
}
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
}
#Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver b = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.put("org.springframework.web.servlet.PageNotFound", "p404");
mappings.put("org.springframework.dao.DataAccessException", "dataAccessFailure");
mappings.put("org.springframework.transaction.TransactionException", "dataAccessFailure");
b.setExceptionMappings(mappings);
return b;
}
#Bean
public RequestTrackerConfig requestTrackerConfig()
{
RequestTrackerConfig tr = new RequestTrackerConfig();
tr.setPassword(RTPassword);
tr.setUrl(RTURL);
tr.setUser(RTUser);
return tr;
}
}
The value in tr.url is "rt.setUrl" not the value in application.properties?
I'm not 100%, but I think your #PropertySource isn't quite right. Instead of
#PropertySource(value = "classpath:application.properties")
It should just be:
#PropertySource("classpath:application.properties")
based on this:
Spring PropertySource Documentation
Also, based on the link above and since you have mentioned you were converting to a java config approach instead of xml, I think the below might be the solution to your issue:
Resolving ${...} placeholders in and #Value annotations In
order to resolve ${...} placeholders in definitions or #Value
annotations using properties from a PropertySource, one must register
a PropertySourcesPlaceholderConfigurer. This happens automatically
when using in XML, but must be
explicitly registered using a static #Bean method when using
#Configuration classes. See the "Working with externalized values"
section of #Configuration Javadoc and "a note on
BeanFactoryPostProcessor-returning #Bean methods" of #Bean Javadoc for
details and examples.
The example from the link above is how I normally do it:
#Configuration
#PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
#Autowired
Environment env;
#Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
So add at the top:
#Autowired
Environment env;
Then in your method use:
tr.setPassword(env.getProperty("rt.setPassword"));
and so on for the remaining property values. I am just not as familiar with the way you are doing it. I know the above approach will work though.
Aside from #ssn771 answer that involves injecting the Environment and retrieving the properties through it which is indeed the suggested way of doing it, this is what I've done as a workaround without having to change the way #Value is being used in the #Configuration POJO.
Out of the many suggested things the most important one is that you need to configure PropertySourcesPlaceholderConfigurer in Spring 3.1+ (or PropertyPlaceholderConfigurer in Spring 3.0). It must be static if you want it to be applied to the configuration class (to use #Value annotations).
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
From javadoc for PropertySourcesPlaceholderConfigurer :
This class is designed as a general replacement for PropertyPlaceholderConfigurer in Spring 3.1 applications. It is used by default to support the property-placeholder element in working against the spring-context-3.1 XSD, whereas spring-context versions <= 3.0 default to PropertyPlaceholderConfigurer to ensure backward compatibility. See the spring-context XSD documentation for complete details.

Categories

Resources