I asked a question more specific to my case about 2 hours ago, but I realised I'm not really addressing my problem at the root cause.
I have a Spring application that uses Spring Security. Throughout my application, (Controllers, service classes etc) I'm using dependency injection and it all works fine. However, I recently started configuring Spring Security, and I can't inject any dependencies inside the classes in my "security" package. Online I read somewhere: "Also when you use #Autowired in the class of which you created a new instance, the Spring context will not be known to it and thus most likely this will also fail" and I was wondering if this maybe had something to do with my issue. My spring configuration basically has one "starting-point", that is the following class:
#Component
#Service
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.register(SecSecurityConfig.class);
sc.addListener(new ContextLoaderListener(root));
sc.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
}
}
This code is run on startup. As you can see, it is registering the SecSecurityConfig.class which is where I configure Spring Security. Inside of that class and onwards (all classes it uses and all classes that those classes use) I can't inject any dependencies. I was wondering if anyone could tell me what the problem could be. Sorry if I'm unclear or incorrect - please tell me so, I find the concept of DI somewhat hard to grasp. My component-scan in XML is: <context:component-scan base-package="com.qars"/> which is the package that my security package is also in.
Also all my classes are annotated with #Component or #Service
Related
I have seen many great workarounds to create Flyway JavaMigrations and injecting Spring Beans using #DependsOn and ApplicationContextAware (e.g. https://stackoverflow.com/a/48242865/5244937).
However a part of the Flyway 6 documentation claims Dependency Injection would be possible natively for Spring Beans:
https://flywaydb.org/documentation/api/hooks#java-based-migrations-as-spring-beans
https://github.com/flyway/flyway/issues/1062
Is is true? How would this work?
Mark your migrations as #Component and put them in a folder that is scanned by spring (e.g. within your application package and not in db.migrations). This will ensure #Autowired can be used because the bean is instantiated by spring. (The migrations in db.migrations will be scanned by flyway automatically and are not instantiated by spring.)
Then implement a FlywayConfigurationCustomizer to add the migrations by loading them from the spring context:
#Configuration
class FlywayConfiguration implements FlywayConfigurationCustomizer {
#Autowired
private ApplicationContext applicationContext;
#Override
public void customize(FluentConfiguration configuration) {
JavaMigration[] migrationBeans = applicationContext
.getBeansOfType(JavaMigration.class)
.values().toArray(new JavaMigration[0]);
configuration.javaMigrations(migrationBeans);
}
}
I've got a Spring WebFlux application running successfully as a standalone spring boot application.
I am attempting to run the same application in a Tomcat container, and following the documentation, I've created a class that extends AbstractReactiveWebInitializer. The class requires that I implement a method getConfigClasses that would return classes normally annotated with #Configuration. If the working spring boot app started with a class called ApplicationInitializer, then the resulting implementations would look like this:
#SpringBootApplication(scanBasePackages = "my.pkg")
#EnableDiscoveryClient
#EnableCaching
public class ApplicationInitializer {
public static void main(String... args) {
SpringApplication.run(ApplicationInitializer.class, args);
}
}
and
public class ServletInitializer extends AbstractReactiveWebInitializer {
#Override
protected Class<?>[] getConfigClasses() {
return new Class[] {ApplicationInitializer.class};
}
}
When deployed, the only thing that starts is ApplicationInitializer, none of the autoconfigured Spring Boot classes (Cloud Config, DataSource, etc) ever kick off.
The documenation states this is the class I need to implement, I just expected the remainder of the spring environment to "just work".
How should I be using this class to deploy a Reactive WebFlux Spring Boot application to a Tomcat container ?
Edit:
After some additional research, I've narrowed it down to likely just Cloud Config. During bean post processing on startup, the ConfigurationPropertiesBindingPostProcessor should be enriched with additional property sources (from cloud config), but it appears to be the default Spring properties instead, with no additional sources.
The misisng properties is causing downstream beans to fail.
Spring Boot does not support WAR packaging for Spring WebFlux applications.
The documentation you're referring to is the Spring Framework doc; Spring Framework does support that use case, but without Spring Boot.
you can extend SpringBootServletInitializer, add add reactive servlet on onStartup method
I have a Spring MVC application, using Hibernate for my entities persistence management. I am able to build, deploy and run it on some application server such as glashfish or tomcat, all is fine.
Now, I want to convert it into a Spring Boot application. I added the following class:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(Application.class, args);
}
}
and added the spring-boot, spring-boot-autoconfigure, and spring-boot-starter-tomcat dependencies to my pom.
Alas, when trying to run the application, I get the following error:
BeanCurrentlyInCreationException: Error creating bean with name
'MyClassDAO': Bean with name 'MyClassDAO' has been injected into
other beans [MyOtherClassDAO] in its raw version as part of a circular
reference, but has eventually been wrapped. This means that said other
beans do not use the final version of the bean. This is often the result
of over-eager type matching - consider using 'getBeanNamesOfType' with
the 'allowEagerInit' flag turned off, for example.
I don't know how to use 'getBeanNamesOfType' and set the allowEagerInit off (I do not use XML configuration). Of course, I'm not sure this would solve my issue anyway.
Any ideas on how I can fix this?
If it's really some intilization issue then i believe the you must be having your class in other package and due to some cache issues it doesn't include those classes in class path to scan in container so to do this manually you can put a annotation just below #springbootapllication is #EnableComponentScan("enter the package name of the class which is not initializing") also on that dao class put #service or #component annotation to let the application include then in container
I'm running a Spring application in a Servlet 3.0+ environment to programmatically configure the servlet context using all Java configuration. My question (with details below): how is a project structured to support component scanning for both root and web application contexts without duplicating component initialization?
As I understand it, there are two contexts in which to register Spring beans. First, the root context is where non-servlet-related components go. For example batch jobs, DAOs, etc. Second, the servlet context is where servlet-related components go such as controllers, filters, etc.
I've implemented a WebApplicationInitializer to register these two contexts just as the JavaDoc in WebApplicationInitializer specifies with a AppConfig.class and DispatcherConfig.class.
I want both to automatically find their respective components so I've added #ComponentScan to both (which is resulting in my Hibernate entities being initiated twice). Spring finds these components by scanning some specified base package. Does that mean I need to put all my DAO-related objects in a separate package from the controllers? If so, that'd be quite inconvenient as I generally like to package by functionality (as opposed to type).
Code snippets...
WebApplicationInitializer:
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(WebAppConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
AppConfig:
#Configuration
#ComponentScan
public class AppConfig {
}
WebAppConfig:
#Configuration
#EnableWebMvc
#EnableSpringDataWebSupport
#ComponentScan(basePackageClasses = AppConfig.class)
public class WebAppConfig extends WebMvcConfigurerAdapter {
}
Just define what you want to scan for in each configuration. In general your root configuration should scan for everything but #Controllers and your web configuration should only detect #Controllers.
You can accomplish this by using the includeFilters and excludeFilters attributes of the #ComponentScan annotation. When using include filters, in this case, you also need to disable using the default filters by setting useDefaultFilters to false.
#Configuration
#ComponentScan(excludeFilters={#Filter(org.springframework.stereotype.Controller.class)})
public class AppConfig {}
And for your WebConfig
#Configuration
#EnableWebMvc
#EnableSpringDataWebSupport
#ComponentScan(basePackageClasses = AppConfig.class, useDefaultFilters=false, includeFilters={#Filter(org.springframework.stereotype.Controller.class)})
public class WebAppConfig extends WebMvcConfigurerAdapter {}
In addition, you need to import the #Filter annotation:
import static org.springframework.context.annotation.ComponentScan.Filter;
The short answer is:
Yes, you should define seperate component scans per context, thus modeling your project differently and extracting the DAO into a seperate name-space (package).
The longer answer is split into two parts, one regarding the servlet contexts and the other regarding modeling a project.
Regarding root and servlet contexts: You have defined correctly the different responsibilities of root context and servlet context. This is an understanding most developers tend to miss and it is very important.
Just to clarify a bit more on this subject, you can create one root context and several (0+) servlet contexts. All of the beans defined in your root context will be available for all servlet contexts but beans defined in each servlet context will be not be shared with other servlet contexts (different spring containers).
Now since you have multiple spring containers and you use component scan on the same packages then beans are created twice, once per container.
To avoid this you can do a few things:
Use one container and not two, meaning you can define only the root container or only the servlet container (I have seen many applications like this).
Seperate your beans into different places and provide each container its unique component scan. remember that all of the beans defined in the root container are available to use in the servlet container (you do not need to define them twice).
Lastly, a few words regarding project modeling. I personally like to write my project in layers, thus separating my code into controllers (application layer), business logic (bl layer) and DAO (database layer).
Modeling like this helps in many ways, including this root/servlet context issue you have encountered.
There are tons of information regarding layered architecture, here is a quick peed: wiki-Multilayered_architecture
I have a web application that use Sring IoC framework.
I use the Java configuration for Spring, and I only use #Configuration annoted module definition (no DI related tags elsewhere in the code).
The Spring registry is built on web application start-up thanks to (a bit modified version of) Spring context loader listener, and the contextConfigLocation param in web.xml configured to point to the #Configuration annotated class.
All that is good and I get a AnnotationConfigWebApplicationContext.
Now, I want to have plugins in my application, that will have their own #Configuration annotated configuration classes, and will use some of the main application services. BUT I don't want to have main application to be modified to load these new modules.
So, I thought that I could simply use the package searching of annotated class for that, but now, it seems that I can use two beans with the same type, even if they have different ids, and clearly AnnotationConfigWebApplicationContext doc states that:
Note: In case of multiple #Configuration classes, later #Bean definitions will override ones defined in earlier loaded files. This can be leveraged to deliberately override certain bean definitions via an extra Configuration class.
I don't want that, because modules should be able to contribute alternative version of services, not (alwways) override existing one - especcially if I want to have a "moduleDef" bean.
I tried to use differents approach on that, but the hierachy of Context and related services is just to big for me.
So, does anybody know how I could reach my goal ?
Thanks
You can have multiple beans of the same type, but You cannot have 2 or more beans with the same ID in a single Spring ApplicationContext - no matter if You use XML or JavaConfig.
The overriding mechanism matches the bean ID's, so all You need to do is to ensure unique ID, i.e.: coreModuleDef, someOtherModuleDef, anotherModuleDef. I don't think You need the ID of each module definition to be identical? What should be sufficient is the type to be the same, but not ID.
You can also turn off the overriding mechanism by setting allowBeanDefinitionOverriding to false on Your AnnotationConfigWebApplicationContext to get an exception if You accidentally override a bean:
public class MyDispatcherServlet extends DispatcherServlet {
#Override
protected void postProcessWebApplicationContext(
ConfigurableWebApplicationContext wac) {
((AnnotationConfigWebApplicationContext) wac)
.setAllowBeanDefinitionOverriding(false);
}
}
or:
public class MyContextLoaderListener extends ContextLoaderListener {
#Override
protected void customizeContext(
ServletContext servletContext,
ConfigurableWebApplicationContext applicationContext) {
((AnnotationConfigWebApplicationContext) wac)
.setAllowBeanDefinitionOverriding(false);
}
}