Exclude subpackages from Spring autowiring? - spring

Is there a simple way to exclude a package / sub-package from autowiring in Spring 3.1?
E.g., if I wanted to include a component scan with a base package of com.example is there a simple way to exclude com.example.ignore?
(Why? I'd like to exclude some components from my integration tests)

I'm not sure you can exclude packages explicitly with an <exclude-filter>, but I bet using a regex filter would effectively get you there:
<context:component-scan base-package="com.example">
<context:exclude-filter type="regex" expression="com\.example\.ignore\..*"/>
</context:component-scan>
To make it annotation-based, you'd annotate each class you wanted excluded for integration tests with something like #com.example.annotation.ExcludedFromITests. Then the component-scan would look like:
<context:component-scan base-package="com.example">
<context:exclude-filter type="annotation" expression="com.example.annotation.ExcludedFromITests"/>
</context:component-scan>
That's clearer because now you've documented in the source code itself that the class is not intended to be included in an application context for integration tests.

I am using #ComponentScan as follows for the same use case. This is the same as BenSchro10's XML answer but this uses annotations. Both use a filter with type=AspectJ
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ImportResource;
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages = { "com.example" },
excludeFilters = #ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "com.example.ignore.*"))
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

For Spring 4 I use the following
(I am posting it as the question is 4 years old and more people use Spring 4 than Spring 3.1):
#Configuration
#ComponentScan(basePackages = "com.example",
excludeFilters = #Filter(type=FilterType.REGEX,pattern="com\\.example\\.ignore\\..*"))
public class RootConfig {
// ...
}

It seems you've done this through XML, but if you were working in new Spring best practice, your config would be in Java, and you could exclude them as so:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "net.example.tool",
excludeFilters = {#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = {JPAConfiguration.class, SecurityConfig.class})
})

This works in Spring 3.0.5. So, I would think it would work in 3.1
<context:component-scan base-package="com.example">
<context:exclude-filter type="aspectj" expression="com.example.dontscanme.*" />
</context:component-scan>

I think you should refactor your packages in more convenient hierarchy, so they are out of the base package.
But if you can't do this, try:
<context:component-scan base-package="com.example">
...
<context:exclude-filter type="regex" expression="com\.example\.ignore.*"/>
</context:component-scan>
Here you could find more examples: Using filters to customize scanning

One thing that seems to work for me is this:
#ComponentScan(basePackageClasses = {SomeTypeInYourPackage.class}, resourcePattern = "*.class")
Or in XML:
<context:component-scan base-package="com.example" resource-pattern="*.class"/>
This overrides the default resourcePattern which is "**/*.class".
This would seem like the most type-safe way to ONLY include your base package since that resourcePattern would always be the same and relative to your base package.

Just an addition to existing answers.
If you want to exclude classes from sub-packages but not from the base package then you can change "com.example.ignore.* to "com.example.ignore.*..*" as follows
Verified this in spring-boot: 2.4.1
Taken code snippet from this answer
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ImportResource;
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages = { "com.example" },
excludeFilters = #ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "com.example.ignore.*..*"))
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

You can also use #SpringBootApplication, which according to Spring documentation does the same functionality as the following three annotations:
#Configuration, #EnableAutoConfiguration #ComponentScan
in one annotation.
#SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}

You can also include specific package and excludes them like :
Include and exclude (both)
#SpringBootApplication
(
scanBasePackages = {
"com.package1",
"com.package2"
},
exclude = {org.springframework.boot.sample.class}
)
JUST Exclude
#SpringBootApplication(exclude= {com.package1.class})
public class MySpringConfiguration {}

Related

Consider defining a bean of type 'org.springframework.cloud.openfeign.FeignContext' in your configuration

I am trying to run the application but this error keeps prompting.
Description:
Parameter 0 of constructor in com.clientui.clientui.controller.ClientController required a bean of type 'org.springframework.cloud.openfeign.FeignContext' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.cloud.openfeign.FeignContext' in your configuration.
Here is the code:
Main
package com.clientui.clientui;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#SpringBootApplication
#EnableFeignClients("com.clientui")
public class ClientuiApplication {
public static void main(String[] args) {
SpringApplication.run(ClientuiApplication.class, args);
}
}
Controller
package com.clientui.clientui.controller;
import com.clientui.clientui.beans.ProductBean;
import com.clientui.clientui.proxies.MicroserviceProduitsProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
#Controller
public class ClientController {
private final MicroserviceProduitsProxy produitsProxy;
public ClientController(MicroserviceProduitsProxy produitsProxy){
this.produitsProxy = produitsProxy;
}
#RequestMapping("/")
public String accueil(Model model){
List<ProductBean> produits = produitsProxy.listeDesProduits();
model.addAttribute("produits", produits);
return "Accueil";
}
}
I had the same problem when updating the spring-boot version to 3.0.0, I think it's some compatibility bug with spring cloud and spring boot's autoconfigure.
I solved it by adding the annotation #ImportAutoConfiguration({FeignAutoConfiguration.class}) in the application, in your case:
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
#SpringBootApplication
#EnableFeignClients("com.clientui")
#ImportAutoConfiguration({FeignAutoConfiguration.class})
public class ClientuiApplication {
public static void main(String[] args) {
SpringApplication.run(ClientuiApplication.class, args);
}
}
I use Spring Boot 3.0.0 and faced the same issue and resolved it by using 2022.0.0-RC2 version of spring-cloud-dependencies. (https://docs.spring.io/spring-cloud/docs/2022.0.0-RC2/reference/html/). It should work with Spring Boot 3.0.0.
If you are using Maven add this to your dependencyManagement section in pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2022.0.0-RC2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Note: For the time I am writing this answer 2022.0.0-RC2 version is not available in central repository but you can find it in Spring Lib M repository so you should also add it to your repositories section in pom.xml:
<repository>
<id>lib-m</id>
<name>Spring Lib M</name>
<url>https://repo.spring.io/libs-milestone/</url>
</repository>
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
#ImportAutoConfiguration({FeignAutoConfiguration.class})
Adding these line on main app fixes the issue.
If you are using Spring 3.0.0 the "spring.factories" has been removed. The automatic import of autoconfigurations is broken if your dependency uses this.
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide#auto-configuration-files
The one way to fix this on your side without modifying the dependecy itself is using #ImportAutoConfiguration({ list of autoconfiguration classes }) explicitly. You can also use new functionality "imports file" provided by Spring. The right way is to migrate "spring.factories" to "imports file" on the dependency side.

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

How to Register beans from all sub packages in Spring boot app?

I am creating new spring boot + jpa + thymeleaf application, I am using many modules which are placed in sub packages. I have structure like ge.my.project where is placed my main class with #SpringBootApplication and under this package I have sub packages ge.my.project.dao,ge.my.project.service,ge.my.project.controller and so on where are placed my beans.#SpringBootApplication scans only beans under base(ge.my.project) package but I want to scan beans from all sub packages.
I tried many variants to scan sub packages :
#ComponentScan(basePackages = {"ge.my.project.controller","ge.my.project.service","ge.my.project.configuration"})
and
#ComponentScan({"ge.my.project.controller","ge.my.project.service","ge.my.proj
ect.configuration"})
and
#ComponentScan("ge.my.project.*")
but nothing works , When I am trying to inject beans using #Autowired
I am getting error like this Consider defining a bean of type 'ge.my.project.service.ServiceTypeService' in your configuration.
Here is my main class
package ge.my.project;
import ge.ufc.inhouseProjects.controller.ServiceTypeController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
//#ComponentScan(basePackages = {"ge.my.project.controller","ge.my.project.service","ge.my.project.configuration"})
#ComponentScan({"ge.my.project.*"})
#EntityScan({"ge.my.project.entity"})
#EnableJpaRepositories("ge.my.project.dao")
public class InhouseProjectsApplication {
public static void main(String[] args) {
/*ApplicationContext applicationContext = */SpringApplication.run(InhouseProjectsApplication.class, args);
}
}
Here is my full project https://github.com/JavaGeoGroup/inhouseProjects.git
Which is the clearest way to scan all project beans in spring boot application?
You need to annotate the implementation and not the interface. So in your case the #Service annotation needs to be set on ServiceTypeDaoImpl-class instead of ServiceTypeDao-interface.
You need to annotate your beans (your implementation of interfaces, not spring data repo interfaces) with the #Component annotation or one of its sub-annotations like #Repository, #Service, #Controller, #Configuration.
From the javadoc of #Component:
* Indicates that an annotated class is a "component".
* Such classes are considered as candidates for auto-detection
* when using annotation-based configuration and classpath scanning.
*
* <p>Other class-level annotations may be considered as identifying
* a component as well, typically a special kind of component:
* e.g. the {#link Repository #Repository} annotation or AspectJ's
* {#link org.aspectj.lang.annotation.Aspect #Aspect} annotation.
Specific extensions to your code-example:
Spring beans start with a small case, so your interface "ServiceTypeDao"will be registred as "serviceTypeDao" as long as you dont set a specific name in the "#Service" annotation.

Spring data repository not found at compile time

I am trying to use Spring data and repositories in a Spring Boot application, but I have an error when compiling the project.
Here is my Entity :
package fr.investstore.model;
import javax.persistence.Id;
...
#Entity
public class CrowdOperation {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long id;
#Enumerated(EnumType.STRING)
public RepaymentType repaymentType;
...
}
And the corresponding Repository:
package fr.investstore.repositories;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import fr.investstore.model.CrowdOperation;
public interface CrowdOperationRepository extends CrudRepository<CrowdOperation, Long> {
}
I use it in a WS controller, generating a repository through the Autowired annotation:
package fr.investstore.ws;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
...
#Controller
#EnableAutoConfiguration
public class SampleController {
#Autowired
private CrowdOperationRepository crowdOperationRepository;
#RequestMapping(path = "/", method = RequestMethod.GET)
#ResponseBody
public String getOperations(#RequestParam(required=true, defaultValue="Stranger") String name) {
crowdOperationRepository.save(new CrowdOperation());
return "Hello " + name;
}
}
And the code of the application:
package fr.investstore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import fr.investstore.ws.SampleController;
#SpringBootApplication
public class InvestStoreApplication {
public static void main(String[] args) {
SpringApplication.run(SampleController.class, args);
}
}
But when compiling the project I get:
APPLICATION FAILED TO START
Description: Field crowdOperationRepository in
fr.investstore.ws.SampleController required a bean of type
'fr.investstore.repositories.CrowdOperationRepository' that could not
be found.
Action: Consider defining a bean of type
'fr.investstore.repositories.CrowdOperationRepository' in your
configuration.
Woudn't Spring automatically generate a bean for the repository through the interface?
How can I resolve this?
EDIT: I also tried to put the Repository annotation (from org.springframework.stereotype.Repository) onto CrowdOperationRepository, but I got the same error
While creating a spring-boot application, we need to keep some point in our mind like
Always keep main class (class with `#SpringBootApplication annotation) on the top level package and other classes should lie under sub-packages.
Always mark your bean classes with proper annotation e.g. all repositories should be marked by #Repository annotation, all service implementation classes should be marked with #Service, other component classes should be marked by #Component, class which defines our beans should be marked as #Configuration
Enable the feature which you are using e.g. #EnableJpaRepositories, #EnableTransactionManagement, #EnableJpaAuditing, these annotations also provides functionality which let us define which package spring needs to scan.
So in your case, you need to mark InvestStoreApplication class with #EnableJpaRepositories annotation and CrowdOperationRepository with #Repository.
you have to tell your spring boot application to load JPA repositories.
copy this one to your application class
it will auto-scan your JPA repository and load it in your spring container even if you do not define your interface with #Repository it will wire that bean in your dependent class.
#EnableJpaRepositories(basePackages = { "fr.investstore.repositories" })
Thank to #JBNizet for his comment, that made it working.
I create this answer since he did not:
Replace SpringApplication.run(SampleController.class, args); with SpringApplication.run(InvestStoreApplication.class, args);. And remove the useless #EnableAutoConfiguration on your controller.
Annotating your entity class as shown as spring hint below to allow spring get a valid repository bean
Spring Data JPA - Could not safely identify store assignment for repository candidate interface com.xxxxx.xxxxRepository.
If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: javax.persistence.Entity, javax.persistence.MappedSuperclass (preferred),
or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository.
2022-05-06 12:32:12.623 [ restartedMain] INFO [.RepositoryConfigurationDelegate:201 ] - Finished Spring Data repository scanning in 3 ms. Found 0 JPA repository interfaces.

Filter specific packages in #ComponentScan

I want to switch from XML based to Java based configuration in Spring. Now we have something like this in our application context:
<context:component-scan base-package="foo.bar">
<context:exclude-filter type="annotation" expression="o.s.s.Service"/>
</context:component-scan>
<context:component-scan base-package="foo.baz" />
But if I write something like this...
#ComponentScan(
basePackages = {"foo.bar", "foo.baz"},
excludeFilters = #ComponentScan.Filter(
value= Service.class,
type = FilterType.ANNOTATION
)
)
... it will exclude services from both packages. I have the strong feeling I'm overlooking something embarrassingly trivial, but I couldn't find a solution to limit the scope of the filter to foo.bar.
You simply need to create two Config classes, for the two #ComponentScan annotations that you require.
So for example you would have one Config class for your foo.bar package:
#Configuration
#ComponentScan(basePackages = {"foo.bar"},
excludeFilters = #ComponentScan.Filter(value = Service.class, type = FilterType.ANNOTATION)
)
public class FooBarConfig {
}
and then a 2nd Config class for your foo.baz package:
#Configuration
#ComponentScan(basePackages = {"foo.baz"})
public class FooBazConfig {
}
then when instantiating the Spring context you would do the following:
new AnnotationConfigApplicationContext(FooBarConfig.class, FooBazConfig.class);
An alternative is that you can use the #org.springframework.context.annotation.Import annotation on the first Config class to import the 2nd Config class. So for example you could change FooBarConfig to be:
#Configuration
#ComponentScan(basePackages = {"foo.bar"},
excludeFilters = #ComponentScan.Filter(value = Service.class, type = FilterType.ANNOTATION)
)
#Import(FooBazConfig.class)
public class FooBarConfig {
}
Then you would simply start your context with:
new AnnotationConfigApplicationContext(FooBarConfig.class)

Resources