Will declaring #configuration on a class make it a spring bean? - spring

I am having a spring boot project and I have a class declared with #configuration annotation.
will declaring a class with #configuration make it a spring bean ?. so here is my code below
#Configuration
public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new PaginationArgumentResolver());
argumentResolvers.add(new FlightFilterArgumentResolver());
argumentResolvers.add(new CampaignFilterArgumentResolver());
argumentResolvers.add(new ContactListFilterArgumentResolver());
argumentResolvers.add(new UserFilterArgumentResolver());
argumentResolvers.add(new PrimecastAccountFilterArgumentResolver());
argumentResolvers.add(new MessageHistoryFilterArgumentResolver());
}
}
Will the above code results in the creation of a spring bean DateTimeFormatConfiguration in the application context when it is started?

Yes. #Configuration annotated class will register as a spring bean. Check the following snippet from documentation. The primary purpose of #configuration to act as a bean source.
When #Configuration classes are provided as input, the #Configuration
class itself is registered as a bean definition, and all declared
#Bean methods within the class are also registered as bean
definitions.
[1] https://docs.spring.io/spring/docs/4.3.25.RELEASE/spring-framework-reference/htmlsingle/#beans-java-basic-concepts

Related

Spring #Bean - creation of Spring Bean depends on service class name

I use Spring 4.2.8 and I do have the service class below. If this class has the name ScheduleEmailCreateAndSendServiceImpl than everything works fine (method generalEmailMessage is invoked at start time in order to create the Spring Bean)
If I rename this class to EmailCreateAndSendServiceImpl than method generalEmailMessage will not be invoked at start time - does anyone know why?
#Service("emailCreateAndSendService")
public class ScheduleEmailCreateAndSendServiceImpl extends AbstractService
implements EmailService {
protected EmailMessage generalMessage;
#Override
public void createAndSendMessage(String receiver, boolean emailActive, Object[] values) throws BusinessException {
// create and send email
}
#Bean
public EmailMessage generalEmailMessage() {
this.generalMessage = new GeneralEmailInformationMessage();
return generalMessage;
}
}
[EDIT]
with this code it is the same
#Configuration
public #Data class ScheduleGeneralEmailConfiguration {
protected EmailMessage generalMessage;
public ScheduleGeneralEmailConfiguration() {
System.out.println("asdf");
}
#Bean
public EmailMessage generalEmailMessage() {
this.generalMessage = new GeneralEmailInformationMessage();
return generalMessage;
}
}
#Bean annotated methods should be in #Configuration annotated class.
You can also put the #Bean annotated methods in the main class of the Spring Boot application annotated with #SpringBootApplication which encapsulates #Configuration, #EnableAutoConfiguration, and #ComponentScan annotations.
Make sure your #Configuration annotated class is placed in the same package or sub package of the Spring Boot Application class

Why spring boot does not load beans configuration in order?

When i try to run a spring boot project, it tolde me that it can not autowire some beans whitch are instanciated in a configuration classes.
I think that spring can not load those configuration classes in order.
The stack trace : no bean found the be autowired Ignite<Long,MyEntity> myEntityCache in MyDao
Here is the source :
The main class
#SpringBootApplication
// The beans in the IgniteConfig have to be loaded before dao, service, and Controller
#ComponentScan(basePackageClasses={IgniteConfig.class,AppConfig.class})
public class DemoIgnite {
public static void main(String[] args) {
SpringApplication.run(DemoIgnite .class, args);
}
}
Config Class 1
#Configuration
public class IgniteConfig {
#Bean
public SpringContext springContext() {
return new SpringContext();
}
#Bean
public Ignite igniteInstance(#Autowired SpringContext springContext) {
IgniteConfiguration cfg = new IgniteConfiguration();
cfg.setIgniteInstanceName("instance");
List<CacheConfiguration> ccDas = new ArrayList<>();
CacheConfiguration cch = new CacheConfiguration<>("myEntitycache");
cch.setCacheMode(CacheMode.REPLICATED);
cch.setIndexedTypes(Long.class, myEntity.class);
ccDas.add(cch);
cfg.setCacheConfiguration( ccDas.toArray(new CacheConfiguration[0]));
SpringCacheManager springCacheManager = new SpringCacheManager();
springCacheManager.setConfiguration(cfg);
return Ignition.start(cfg);
}
#Bean
public IgniteCache<Long, MyEntity> myEntityCache(#Autowired Ignite igniteInstance) {
return igniteInstance.cache("myEntitycache");
}
Config class 2
#Configuration
#ComponentScan({
"com.demo.repository",
"com.demo.service",
"com.demo.controller"
})
public class AppConfig {
}
Dao class
#Repository
public class MyDao{
#Autowired
private Ignite<Long,MyEntity> myEntityCache;
...
Service class:
#Service
public class MyService{
#Autowird
private MyDao dao;
...
Controller class:
#RestController
#RequestMapping
public class MyController{
#Autowired
private MyService service;
....
This means that you don't have a bean of Ignite<Long,MyEntity> type in your context. Moreover springContext bean seems redundant, it's not used by igniteInstance bean. As pointed out by moilejter it probably should be:
IgniteConfig
#Bean
public Ignite ignite() {
...
}
#Bean
public IgniteCache<Long, MyEntity> myEntityCache() {
return ignite().cache("myEntitycache");
}
MyDao
#Repository
public class MyDao {
#Autowired
private IgniteCache<Long, MyEntity> myEntityCache;
...
}
In principle Spring performs the bean setup in few phases as explained in chapter 1.3.2. Instantiating Beans docs:
Bean definition discovery - resources like #Configuration classes or XML files are scanned and bean signatures are collected.
Eager beans instantiation e.g. singletons - from the definitions collected in point 1 while resolving dependencies between definitions. That's why there is no explicit bean instantiation order as the process is driven from dependencies.
Lazy beans instantiation e.g. #Lazy annotated - when the context is already up, this beans will be constructed only when accessed from code.

Spring boot defining configuration beans per user

I am using Spring boot. I have some question regarding the spring boot beans.
But I have doubt
I use bean which are default scope that is singleton. So they will have only one instance per application.
#Configuration
public class ...{
#Bean
public void method() {}
}
And
Now i use bean which scope is prototype. So they will have each instance per request.
#Configuration
public class ...{
#Bean
#Scope("prototype")
public void method() {}
}
But
I want single instance per user..? all request use single instance per user.
#Configuration
class Abc {
#Bean
#Scope("session")
public YourBean getYourBean() {
return new YourBean();
}
}
You will need to define one singleton bean with a property using prototype bean:(xml example)
With #bean definition:
#Component
#Scope("singleton")
public class SingletonBean {
// ..
#Autowired
private PrototypeBean prototypeBean;
//..
}
#Component
#Scope("prototype")
public class PrototypeBean {
//.......
}
Example: https://www.baeldung.com/spring-inject-prototype-bean-into-singleton

Spring AOP with bean scanned using mybatis.spring.*.MapperScan

I am working on one component to achieve audit using Spring AOP. I could use it for most of the service's methods. But found that with Mybatis mappers AOP point-cuts don't work.
Basically, Spring AOP only works with Spring-managed beans. But these mapper beans have been scanned using mybatis.spring.*.MapperScan and can be autowired in other Spring components.
Why can these beans not be scanned for Spring AOP? Any idea?
I can use AspectJ but was keen to find out how mybatis.spring.*.MapperScan works.
for example -
I have these configurations one for Mybatis mapper scan and other config for application specific configurations.
#Configuration
#MapperScan("com.test.mapper")
public class ProviderConfiguration {
#Bean
public SqlSessionFactory sqlSessionFactory(final DataSource src) throws Exception {
...
}
}
#Configuration
#EnableAspectJAutoProxy
public class MainConfiguration {
}
My Dao logic where i call mapper method -
#Component
public class TestDao {
//injecting mybatis mapper here
#Inject
private SaveTableData saveTableData;
public TableData save(TableData tableData) {
saveTableData.updateTableData(tableData);
}
}
I have registered my pointcuts as below
#Component
#Aspect
public class TestAdvices {
#Pointcut("execution(* com.test.mapper.SaveTableData.updateTableData(*))")
public void commonSaveTableData(TableData tableData) {
}
#Pointcut("execution(* com.test.service.CreateTableData.createTableData(*))")
public void commonCreateTableData(TableData tableData) {
}
//advices
#After("commonSaveTableData(tableData)")
public void addHistoryWhenSaveTableData(TableData tableData) throws Throwable {
//do stuff
}
//advices
#After("commonCreateTableData(tableData)")
public void addHistoryWhenCreateTableData(TableData tableData) throws Throwable {
//do stuff
}
}
Issue is commonCreateTableData which is on service method works as expected. But commonSaveTableData which is on Mybatis mapper method does't get invoke.
Question is if i can autowire these Mappers in any Spring bean why can't Spring AOP intercept method call using these pointcuts?
I think your pointcut expression is not correct, try this
#Component
#Aspect
public class TestAdvices {
#Pointcut("execution(* com.test.mapper.SaveTableData.updateTableData(*)) && args(tableData)", argNames="tableData")
public void commonSaveTableData(TableData tableData) {
}
//advices
#After("commonSaveTableData(tableData)", argNames="tableData")
public void addHistoryWhenSaveTableData(TableData tableData) throws Throwable {
//do stuff
}
//...
}
The reason you can't cut into the mapper like this is that when mapper is scanned by mybatis, it's bean definition has bean changed in a way that its interface is still the mapper interface but its class has been changed to MapperFactoryBean

#Profile Spring Annotation in Camel

I have a Spring Boot + Apache Camel project that works brilliantly. I just added a new bean though where I wanted to have its implementation be profile-specific. I created Spring tests to verify it, and it works as expected, but when I run the server I get the following stack trace:
Caused by: org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: MyFancyBean
at org.apache.camel.component.bean.RegistryBean.getBean(RegistryBean.java:94)
at org.apache.camel.model.language.MethodCallExpression.createExpression(MethodCallExpression.java:196)
at org.apache.camel.model.language.MethodCallExpression.createPredicate(MethodCallExpression.java:210)
at org.apache.camel.model.language.ExpressionDefinition.createPredicate(ExpressionDefinition.java:148)
at org.apache.camel.model.ValidateDefinition.createProcessor(ValidateDefinition.java:63)
at org.apache.camel.model.ValidateDefinition.createProcessor(ValidateDefinition.java:35)
at org.apache.camel.model.ProcessorDefinition.makeProcessorImpl(ProcessorDefinition.java:545)
at org.apache.camel.model.ProcessorDefinition.makeProcessor(ProcessorDefinition.java:506)
at org.apache.camel.model.ProcessorDefinition.addRoutes(ProcessorDefinition.java:222)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1068)
I have an interface and two implementations:
public interface MyFancyBean { ... }
public class FooFancyBean implements MyFancyBean { ... }
public class NonFooFancyBean implements MyFancyBean { ... }
Depending on profile, the correct bean is read instantiated:
#Configuration
public class AppConfig {
#Bean
#Profile("foo")
MyFancyBean fooBean() {
return new FooFancyBean();
}
#Bean
#Profile("!foo")
MyFancyBean nonFooBean() {
return new NonFooFancyBean();
}
}
I've verified this works a couple of ways. First, a couple tests:
#ActiveProfiles("anything-but-foo")
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan(basePackages = {"com.example", "com.jtv.spring.boot"})
#EnableAutoConfiguration
#Component
public class NonFooBean_SpringTest {
#Autowired
private MyFancyBean bean;
#Test
// ... here "bean" is instantiated as "NonFooFancyBean"
So the test works.
Further, when I start my app, depending on profile the correct bean in my #Configuration class above is called.
But Camel is still angry and says "NoSuchBeanException" on startup.
FWIW, here's how I'm referencing the bean:
#Component
public class MyCamelRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
// [...]
from("direct:processStuff").
validate().method("MyFancyBean").
process("MyProcessor");
}
}
How do I get Camel to honor this config?
Whoooo... Y'all get to be my rubber duck today. I just autowired it. (This doesn't work for my processor, which is why it didn't occur to me initially.)
#Component
public class MyCamelRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
// [...]
#Autowired MyFancyBean myFancyBean;
from("direct:processStuff").
validate().method(myFancyBean).
process("MyProcessor");
}
}

Resources