How to automatically add all Converters to a FormatterRegistry in Spring? - spring

I have a Spring Boot 2.1.6 project that uses Spring's Converters a lot (24 of them). All are annotated as #Component. Now I've added a #EnableWebMvc and have to add them to the FormatterRegistry via registry.addConverter in a WebMvcConfigurer.addFormatters
Can I have Spring find all of them automatically (they are all in the same separate package) and add them or do I have to manually add all 24 of them and change my WebMvcConfigurer every time I add a converter?

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
List<Formatter> formatters;
#Override
public void addFormatters(FormatterRegistry registry) {
formatters.forEach(registry::addFormatter);
}
}

Since you have implemented the Converter interface and also annotated them with #Component, you can get them all by injecting them as a collection. #Autowired List<Converter> converters;

Related

spring-boot-starter-jdbc DAO repository object not injected in working legacy webservice

I am new in the spring/boot word and have a working JAX-WS based web-service declared in a springboot project. It is started and configured via web.xml and sun-jaxws.xml. So, no beans included there only endpoints declarations and servlet definitions and mappings.
I just now want to save the items i get in the webservice into the mysql database using spring-boot-starter-jdbc which is not working:
I can't achieve this as the repository is not injected in the webservice implementation.
Followed all steps in other question, but not achieving this!
Normally declaration of the datasource parameters in application.properties and annotating #webservice and #Repository would suffice to get the injection of the repository working in the webservice class. What am i missing ?
Here details of the steps I followed:
the webservice implementation is a package X and i created a #SpringBootApplication in order to use a mysql datasource declared in application.properties.
So i annotated the webservice as a #Component and the data access repository with #Repository and #Component
parent version: spring-boot-starter-parent : 1.5.10.RELEASE
webservice service implementation:
BServiceManager.java
package X;
....
#Component
#WebService(name = "***",***)
#BindingType("http://schemas.xmlsoap.org/wsdl/soap/http")
#XmlSeeAlso({
packagesxxx.class,
....
})
public class BServiceManager
implements xxxxx
{
....
#Autowired
private ItemRepository irepo;
....
#WebMethod(**)
#WebResult(***)
public ResponseDataInfo sendInfo( ){
....
trepo.saveitem(item)
....
}
}
ItemRepository.java
package Y.Z;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
#Component
#Repository
public class ItemRepository {
#Autowired
private JdbcTemplate jdbcTemplate ;
....
public boolean saveitem(Item item) {
....
}
}
Item.java
package Y.Z;
public class Item {
....
}
GetItemsApplication
package Y;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
//#ComponentScan(basePackages={"Y","Y.Z","X"})
#SpringBootApplication
public class GetItemsApplication {
....
public static void main(String[] args) {
SpringApplication.run(GetItemsApplication.class, args);
log.info("--Spring Boot inits done--");
}
}
application.properties
spring.data.jpa.repositories.enabled=false
spring.data.jdbc.repositories.enabled=true
# MySQL properties
spring.datasource.url=****
spring.datasource.username=****
spring.datasource.password=****
....
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
logging.level.org.springframework.jdbc.core.JdbcTemplate=debug
NB: even having datasource bean is not helping :
File: DataSourceConfig.java
package Y.Z;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
//#EnableJdbcRepositories for Spring
#Configuration
public class MDataSourceConfig {
#Bean
public DataSource dataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
return dataSourceBuilder.build();
}
#Bean
public JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
Your Webservice doesn't seem to get created by Spring.
Therefore Spring has no control over its dependencies, so you have to get the dependency programmatically.
There are many ways to do this.
Easy but not very elegant and uses global variables which might cause problems, especially with tests: https://stackoverflow.com/a/18486178/66686
More elegant but requires weaving Spring autowiring using #Configurable

#Bean works without #Configuration. How can it still work without #Configuration?

The bottom code is my Spring Batch program code. when you see the bottom, you can see the code's problem. there is no #Configuration. originally, it was impossible to inject to dependency classes, but it was injected.
The first image is my project explorer.
I will inject dataSource to dataSource in BatchJob but it can't work because I didn't add #Configuration at BatchConfiguration. class but it still work even no #Configuration. so I wonder How can#Bean DataSource inject without #Configuration? you can check second image what this project works.
so plz I wanna solve my wondering and you can see that full source in my github address and my English skill is not good
package com.bootbatch.job;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
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.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import javax.sql.DataSource;
#ComponentScan("com.bootbatch")
#PropertySource("classpath:/database.properties")
#EnableBatchProcessing
public class BatchConfiguration {
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(env.getRequiredProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
return dataSource;
}
#Bean
public DataSourceInitializer databasePopulator() {
System.out.println("===>databasePopulator에 접속!!");
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScript(new ClassPathResource("org/springframework/batch/core/schema-oracle10g.sql"));
// populator.addScript(new ClassPathResource("truncate_secondjob.sql"));
populator.setContinueOnError(true);
populator.setIgnoreFailedDrops(true);
DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDatabasePopulator(populator);
initializer.setDataSource(dataSource());
return initializer;
}
}
The "problem" is your own code in your main method (which you hapilly forgot to include in your question!).
#SpringBootApplication
public class SpringBootBatch06Application {
public static void main(String[] args) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException, InterruptedException {
SpringApplication.run(SpringBootBatch06Application.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext(BatchConfiguration.class, BatchJob.class);
// Other code removed
}
}
You are creating a new AnnotationConfigApplicationContext for those 2 classes. Which will make those 2 classes components automatically (regardless of a #Component or #Configuration annotation). So you are basically working around Spring Boot and its auto-configuration (probably because it didn't work).
It is also allowed for #Components to have #Bean methods, they will operate in so called "lite #Bean Mode" (see this section of the Spring Reference Guide).
So because they are now first of all components (or beans) and have #Bean methods they will produce new beans (although not as you think they do, read the aformentioned documentation).

Spring bean getting initialised twice in java configuration

I've created a spring application using spring-security with java based configuration. I've also included a jar file (created by me) in my project.
The problem I am facing is:- i have to write #ComponentScan(basePackages = {"com.mypackage"}) in both the classes (SpringConfig.java and SecurityConfig.java) which leads to initialization of beans twice.
Removing either of #componentscan leads to error:- Error creating bean with name 'securityConfig'.
Below are my java classes.
SpringConfig.java
package com.mypackage.config;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"com.mypackage"})
public class SpringConfig extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SpringConfig.class);
#PostConstruct
public void init(){
logger.debug("Spring Config initialized");
}
}
SecurityConfig.java
package com.mypackage.config;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
#ComponentScan(basePackages = {"com.mypackage"})
public class SecurityConfig extends WebSecurityConfigurerAdapter{
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
//This Configuration class is in my jar file.
// with package starting with same name com.mypackage
#Autowired
com.mypackage.frameworks.config.Configuration config;
#PostConstruct
public void init(){
logger.debug("Security config initiaziled");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
try {
auth.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");
} catch (Exception e) {
e.printStackTrace();
}
}
}
MyController.java
package com.mypackage.controller;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
#Controller
public class MyController {
private static final Logger logger = LoggerFactory.getLogger(MyController.class);
#PostConstruct
public void init(){
logger.debug("-------Controller created-------");
}
}
You have configured bean definitions into multiple #Configuration classes. My suggestion is - Aggregating #Configuration classes with #Import into single place.
Now you can able to apply #ComponentScan(basePackages = {"com.mypackage"}) in one place and context also loads bean only one time.
The #Import annotation provides just this kind of support, and it is the direct equivalent of the element found in Spring beans XML files.
Please refer this link - https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html
Beans will be configured and created twice because both application context scans the same package "com.mypackage". One solution is to separate SpringConfig beans package from SecurityConfig beans package. be as more specific as you can in #ComponentScan package value

Capture org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage() invocation

I'm using AspectJ and AOP in a Spring-boot project in order create an external library to log some activities.
Although I have configured this pointcut:
#Pointcut("call(void org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(Message,Channel))")
private void getEventOnMessage(){}
the aspect
#Before(value="getEventOnMessage()")
public void getEventOnMessage(JoinPoint joinPoint){
System.out.println("VOILA'");
}
is not triggered.
Details:
package com.tim.sdp.timLogging.Aspects.handler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages="org.springframework.amqp.rabbit.listener.adapter")
public class AppConfig {
#Bean()
public AspectForOnMessage myAspect() {
return new AspectForOnMessage();
}
}
Aspect class implementation:
package com.tim.sdp.timLogging.Aspects.handler;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class AspectForOnMessage {
#Pointcut("call(void org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(Message,Channel))")
private void getEventOnMessage(){}
#Before(value="getEventOnMessage()")
public void getEventOnMessage(JoinPoint joinPoint){
System.out.println("VOILA'");
}
}
Might you help me, please? It's the only event I can not capture.
In this forum you can find another person with the same problem:
Spring forum link
Thank you in advance.
Oh, a classical one!
As documented here, call() is not supported in proxy-based Spring AOP which you have configured in your application via #EnableAspectJAutoProxy. You need to switch from "AOP lite" to the full power of AspectJ as described there or stick with pointcuts really supported in Spring AOP.

Spring set bean name with #Named

I use javax standard annotation #Named for defining beans in spring4. To set the bean name I could tried #Named("MyBean") but it did not change the bean name.
I used spring Component annotation #Component("MyBean") and it worked fine.
Is it possible to set the bean name by using #Named
The bean is defined asL
#Named("myBean") //This not
#Component("myBean") //This works
#Scope("session")
public class User implements HttpSessionBindingListener, Serializable {
The application.context is
<context:component-scan base-package="foo.bar" />
I agree to what #fabian has said. You can use #Named annotation to set the bean name. If bean name doesn't matches, it falls back to auto-wiring by type.
I tried couple of examples. They worked for me.
AppConfig.java
package com.named;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
public class AppConfig {
}
NamedService.java
package com.named;
import javax.inject.Named;
#Named("namedTestDependency")
public class NamedService {
public void namedMethod(){
System.out.println("Named method");
}
}
NamedServiceTest.java
package com.named;
import static org.junit.Assert.assertNotNull;
import com.named.AppConfig;
import com.named.NamedService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=AppConfig.class)
public class NamedServiceTest {
//Matched by name of dependency
#Autowired
private NamedService namedTestDependency;
//Falls back to auto-wiring by type
#Autowired
private NamedService noDeclaration;
#Test
public void testAutowiring(){
assertNotNull(namedTestDependency);
assertNotNull(noDeclaration);
}
}

Resources