How to work with spring annotations in large applications? - spring

If you are starting a Spring project, you may know where you have placed the #controller, #requestmapping annotations and other annotations and the corresponding controller class.
Lets say few months down the line a new developer comes into your team, how would he figure out which classes to work with? Because unlike the xml based approach where all the configurations is centrally located in an config.xml file, we don't have nothing of that sort with annotation as per my knowledge (I am new to spring), we write the respective annotations in a class itself.

In Spring, two ways to define your configuration :
Either in a XML file or
Java class
So just like xml, in Java also you need to create a configuration class which will have all the required configurations details. You can define all of your beans, filters etc here.
For example , You create a configuration class MvcConfig with the below annotations
#Configuration
#EnableWebMvc
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ComponentScan(basePackages = {"com.abc.example"})
Now your base package is com.abc.example. In all the applications, the best practice is to keep all of you controller\service\DAO classes in specific packages like
Controller : com.abc.example.controller,
Service : com.abc.example.service,
DAO : com.abc.example.dao
So anybody who comes in will know where are all the respective classes located and from where to start.
Example configuration class :
#Configuration
#EnableWebMvc
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ComponentScan(basePackages = {"com.abc.example"})
public class MvcConfig extends WebMvcConfigurerAdapter

Related

Sping-boot configuration-properties and service layer injection

I'm new to spring dependency-injection and am reaching out to learn about best practices. I would like to know if its a good design philosophy to inject classes annotated with #ConfigurationProperties into service layer classes (annotated with #Service). Im trying to map properties in my application.yml to a config-class as follows -
#ConstructorBinding
#ConfigurationProperties(prefix = "application")
class ApplicationConfig(
val kafka: someDeeplyNestedType = SomeDeeplyNestedObj()
) {
// helper functions
}
I'm then injecting above config class in service layer as follows -
#Service
#EnableConfigurationProperties(ApplicationConfig::class)
class RestService(val config: ApplicationConfig) {
init {
// Reference config object
// Reference application.yml properties via config object.
}
}
I'm curious to know if I can improve upon my current implementation - not sure if its agreeable to pass configuration classes to service-layer classes. I'm also curious to know if theres any better approach to wiring ApplicationConfig without needing to use EnableConfigurationProperties annotation.
It is agreeable, documented, and probably "unrivaled" (only bounded by: "limitations" (no SpEL -> helper functions!?;)).
To work with #ConfigurationProperties beans, you can inject them in the same way as any other bean, as shown in the following example:
#Service
public class MyService {
private final SomeProperties properties;
...
The only problems can arise from the "deeply", not "owning" the (config) structure ...and possibliy from "helper functions".
But
The prefix = "application" "sounds" suspicious!
Note:
[Most - almost All] (official) spring* boot properties, are already "typesafe", and have their object/class representation in spring-boot-autoconfigure packages.
Please study that "typesafe chapter", but also gazing at PropertySource Abstraction.
There is no hard rule for this as in Spring Boot we can add #EnableConfigurationProperties at a class level with stereotype annotations.
As a part of good practices EnableConfigurationProperties or any configuration thing should be part of Configuration class of or main spring boot class so any developer can easily figure out those configuration instead of going any specific service class and then check.
In your case, yo can use #EnableConfigurationProperties annotation in conjunction with #SpringBootApplication annotation.

How to set up a spring based java library?

I am trying to create a java library that uses spring. But I am not able to find any resources that shows you how to set up component scan and load all beans when you do not have a main entry point into the application.
A common approach used by spring is to define a #EnableXXXX and let the client of your library to enable it by annotating it on their configuration class. Something like :
#Target(ElementType.TYPE)
#Import(FooLibarayConfiguration.class)
#Documented
public #interface EnableFooLibaray{
}
#ComponentScan("xxxxx")
#Configuration
public class FooLibarayConfiguration{
}
And client enables your library by :
#EnableFooLibaray
#Configuration
public class Application{
}
The #Import actually supports a more dynamic way to include the bean settings for your library. You can refer to many existing #EnableXXX provided by spring such as #EnableAsync , #EnableWebSecurity , #EnableTransactionManagement , #EnableCaching etc. for many examples.

Does #ComponentScan override #SpringBootApplication?

My #SpringBootApplication annotation is present in com.abc.def package.
According to this article , using #SpringBootApplication annotation is equivalent to using #Configuration, #EnableAutoConfiguration, and #ComponentScan with their default attributes :-
https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/using-boot-using-springbootapplication-annotation.html
If i supply my own #ComponentScan, does it add new packages to the default value or completely override the default ?
#SpringBootApplication annotation can component scan classes from packages under Application class belong to . Also you can add #ComponentScan to scan classes not under package Application class belongs to.
#ComponentScan("external.pkg")
#SpringBootApplication
class MyApp{
}
the annotation #SpringBootApplication contains #EnableAutoConfiguration annotation, which will scan through your jar, class, and maven dependencies in the classpath to register beans. If you declare beans cross packages and only want to use specific beans from specific packages, then use #ComponentScan
#ComponentScan gives you a fine-grained package or class level control on what beans you declared would be registered.
The #SpringBootApplication annotation doesn't allow all the filter customizations supported by #ComponentScan. We observed in our application that spring boot was automatically loading a #Configuration class from a library outside the package hierarchy of the class annotated with #SpringBootApplication.
If you want more control, one idea is to omit the #SpringBootApplication annotation and just specify the individual annotations #Configuration, #EnableAutoConfiguration, and #ComponentScan with your desired customizations.
More information is available in Spring Boot's Documentation:
18. Using the #SpringBootApplication Annotation
Good luck

How is #ConfigurationProperties-annotated classes detected automatically with #SpringBootApplication Annotation

I am learning Spring Boot and have a question with one example in the reference documentation.
Following section of the documentation mentions
6. Using the #SpringBootApplication Annotation
A single #SpringBootApplication annotation can be used to enable those
three features, that is:
#EnableAutoConfiguration: enable Spring Boot’s auto-configuration
mechanism
#ComponentScan: enable #Component scan on the package where the
application is located (see the best practices)
#Configuration: allow to register extra beans in the context or import
additional configuration classes
and the following example to replace this single annotation by any of the features that it enables is bit confusing for me . The example
package com.example.myapplication;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration(proxyBeanMethods = false)
#EnableAutoConfiguration
#Import({ MyConfig.class, MyAnotherConfig.class })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Explanation for the example
In this example, Application is just like any other Spring Boot
application except that #Component-annotated classes and
#ConfigurationProperties-annotated classes are not detected
automatically and the user-defined beans are imported explicitly (see
#Import).
The only major difference I see in the example code above is that it does not have #ComponentScan annotation. I also read in the comments section of an SO answer (Stephane Nicoll May 5 '17 at 11:07) that #Component annotation is not recommended officially to auto detect #ConfigurationProperties. So my assumption is that Spring framework classes with #ConfigurationProperties are not annotated with #Component.
Also I checked the #SpringBootApplication annotation source and couldn't identify anything that should enable the automatic detection of #ConfigurationProperties annotated classes.
The reference document 2.8.3. Enabling #ConfigurationProperties-annotated types section shows the following way to scan and autodetect #ConfigurationProperties
#SpringBootApplication
#ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
public class MyApplication {
}
With all these details , I would like to understand
Why is it explicitly mentioned for this example that #ConfigurationProperties-annotated classes are not detected automatically ? and How is #ConfigurationProperties annotated classes automatically detected when #SpringBootApplication is used.
Additional note : I saw a small difference between the prior version of the documentation and the current one. The following reference is missing the current one
Keep in mind that the #EnableConfigurationProperties annotation is
also automatically applied to your project so that any existing bean
annotated with #ConfigurationProperties is configured from the
Environment
Following is what I understand from my analysis.
#ConfigurationProperties annotated types can be registered to the ApplicationContext by
Annotating the class with #ConfigurationProperties with an
annotation that falls in the scope of #ComponentScan (
#Component, #Service and the like ) . Not recommended as per the comment from Stephane Nicoll , which makes sense to me now.
Using annotation
#EnableConfigurationProperties . For this to
work the class annotated with #EnableConfigurationProperties
should be annotated with an annotation that falls in the scope of
#ComponentScan ( #Component, #Service and the like )
Using annotation #ConfigurationPropertiesScan and by making sure
the classes annotated with #ConfigurationProperties is placed
under its radar. The usage is similar to #ComponentScan .
Now , when we replace #SpringBootApplication with individual annotations that it enables and omit #ComponentScan (as in example) , the #EnableConfigurationProperties way (Point 2) of registering the types with #ConfigurationProperties will not work. This probably answers both my questions on why and how .
This was explicitly mentioned probably because the connection between #EnableConfigurationProperties and #ComponentScan is not that obvious for people like me.
Additional details.
The registration of #ConfigurationProperties annotated types when we use #EnableConfigurationProperties happens through of the EnableConfigurationPropertiesRegistrar class that is imported by the annotation
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Import(EnableConfigurationPropertiesRegistrar.class)
public #interface EnableConfigurationProperties {..}

where to place componentscan

I have spring mvc application with complete annotations. Where is the best place to add #componentScan? Let me know any of these recommended
class that extends AbstractAnnotationConfigDispatcherServletInitializer?
class that extend WebMvcConfigurationSupport
class that extend WebSecurityConfigurerAdapter
I placed in 2 without security and workign fine. When I added security, I got problem with security config not able to find userdetails service. Then I moved to 3.
I found other issues with security and put code to just reuturn null instead of
securityconfig object from getRootConfigClasses(). Then I got issues of controllers not found. I am able to fix it to put componenentscan in 2.
I just want to know any links and how it works. Is it ok to put #componentscan in all of these 3? Appreciate your help.
It depends on your project's package tree and what you want to scan. If you want to scan all annoted classes with such annotations like : #Configuration, #Component, #Repository, etc... put #ComponentScan at the top of your package tree.
You can also use the basePackages attribute to specify where to start the scanning.
Say you have an application packages organized like this :
com.app.config,com.app.config.web, com.app.services, com.app.web.controllers
If you want to scan all annoted classes, put the class annoted with #ComponentScan in com.app package.
If you want to scan only controller, add #ComponentScan(basePackages="com.app.web.controllers")
It's up to you to decide.

Resources