In a spring boot application who are reactive and use jwt, in my spring-cloud-gateway, I have this code.
#EnableDiscoveryClient
#SpringBootApplication
public class GatewayServiceApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServiceApplication.class, args);
}
}
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SpringSecurityWebFluxConfig {
private final UserServiceImpl userService;
private final JwtTokenUtil tokenUtil;
private static final String[] AUTH_WHITELIST = {
"/resources/**",
"/webjars/**",
"/authorize/**",
"/favicon.ico"};
public SpringSecurityWebFluxConfig(JwtTokenUtil tokenUtil, UserServiceImpl userService) {
this.tokenUtil = tokenUtil;
this.userService = userService;
}
..
}
#Service
public class UserServiceImpl implements ReactiveUserDetailsService, UserService {
private final UserRepository userRepository;
public UserServiceImpl(final UserRepository userRepository) {
this.userRepository = userRepository;
}
...
}
#Repository
public interface UserRepository extends ReactiveCrudRepository<User, Integer>{
}
public class JWTHeadersExchangeMatcher implements ServerWebExchangeMatcher {
#Override
public Mono<MatchResult> matches(final ServerWebExchange exchange) {
}
}
public class JWTReactiveAuthenticationManager implements ReactiveAuthenticationManager {
...
public JWTReactiveAuthenticationManager(final PasswordEncoder passwordEncoder, final UserServiceImpl userService) {
this.passwordEncoder = passwordEncoder;
this.userService = userService;
}
}
public class JwtTokenUtil {
...
}
public class TokenAuthenticationConverter implements Function<ServerWebExchange, Mono<Authentication>> {
private final JwtTokenUtil tokenProvider;
public TokenAuthenticationConverter(JwtTokenUtil tokenProvider) {
this.tokenProvider = tokenProvider;
}
}
public class TokenAuthenticationConverter implements Function<ServerWebExchange, Mono<Authentication>> {
private final JwtTokenUtil tokenProvider;
public TokenAuthenticationConverter(JwtTokenUtil tokenProvider) {
this.tokenProvider = tokenProvider;
}
}
[ main] onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling
refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'springSecurityWebFluxConfig' defined in
file
[/home/mac/Development/project/reactive-cloud/gateway-service/build/classes/java/main/com/example/gatewayservice/config/SpringSecurityWebFluxConfig.class]:
Unsatisfied dependency expressed through constructor parameter 1;
nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'userServiceImpl' defined in file
[/home/mac/Development/project/reactive-cloud/gateway-service/build/classes/java/main/com/example/gatewayservice/service/UserServiceImpl.class]:
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.example.gatewayservice.repository.UserRepository' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations: {} 2019-06-27 11:47:23.336 INFO 53073 --- [
main] ConditionEvaluationReportLoggingListener :
Tried to put autowired
on UserServiceImpl in SpringSecurityWebFluxConfig class
on UserRepository in UserServiceImpl class
on UserServiceImpl in JWTReactiveAuthenticationManager.class
but get same error
Edit
if i use
#EnableR2dbcRepositories to GatewayServiceApplication, I dont't have this error but it search about DatabaseClient
Please add #Autowired annotation in UserServiceImpl class for UserRepository variable in constitute constructor or variable level.
Related
2020-09-23T15:28:00.3483912Z java.lang.IllegalStateException: Failed to load ApplicationContext
2020-09-23T15:28:00.3489821Z Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'salecChannelEventProcessor' defined in file [/home/runner/work/calculation-service/calculation-service/target/classes/com/demo/calculation/saleschannel/SalecChannelEventProcessor.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'de.demo.json.schema.JsonValidator' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
import de.demo.json.schema.JsonValidator;
#Configuration
#ComponentScan( basePackages = {
"com.demo",
"de.demo" },
excludeFilters = {
#ComponentScan.Filter( Configuration.class )
} )
#ImportResource("classpath:/spring-context.xml")
#Import({SwaggerConfig.class, SalesChannelSqsConfig.class})
public class SpringMvcConfig extends WebMvcConfigurationSupport {
#Autowired private ApplicationContext applicationContext;
#Bean( name = "objectMapper" )
public ObjectMapper getObjectMapper( JacksonService jacksonService ) {
return jacksonService.getObjectMapper();
}
#Bean(name = "jsonValidator")
public JsonValidator jsonValidator() {
return new JsonValidator();
}
}
#Component
#Slf4j
#RequiredArgsConstructor
public class SalesChannelUpdateListerner {
#NonNull
private final SalesChannelService salesChannelService;
#NonNull
private final SalecChannelEventProcessor salecChannelEventProcessor;
#SqsListener(value = "${sales.channel.update.queue.name}", deletionPolicy = ON_SUCCESS)
#SneakyThrows
public void receiveSalesChannelUpdateEvent(
#NotificationMessage EnvelopedMessage envelopedMessage) {
log.debug("Received message from sales channel update event queue : {}"
}
#Component
#Slf4j
#RequiredArgsConstructor
public class SalecChannelEventProcessor {
private static final String MESSAGE_TYPE = "sales_channel_update";
#NonNull
private final ObjectMapper objectMapper;
#NonNull
private final JsonValidator jsonValidator;
#SneakyThrows(JsonProcessingException.class)
public boolean isValid(EnvelopedMessage envelopedMessage) {
if (!MESSAGE_TYPE.equals(envelopedMessage.getType())) {
return false;
}
return jsonValidator.validate(envelopedMessage);
}
You need to create the JsonValidator bean. You need to change yor SalecChannelEventProcessor to be:
#Component
#Slf4j
#RequiredArgsConstructor
public class SalecChannelEventProcessor {
private static final String MESSAGE_TYPE = "sales_channel_update";
#NonNull
private final ObjectMapper objectMapper;
#Bean
public JsonValidator jsonValidator(){
return new JsonValidator();
}
#SneakyThrows(JsonProcessingException.class)
public boolean isValid(EnvelopedMessage envelopedMessage) {
if (!MESSAGE_TYPE.equals(envelopedMessage.getType())) {
return false;
}
return jsonValidator().validate(envelopedMessage);
}
}
These are the following configuration that I have done in my project.
#Configuration
public class AppMongoConfig {
#Autowired private MongoDbFactory mongoDbFactory;
#Autowired private MongoMappingContext mongoMappingContext;
#Bean
public MappingMongoConverter mappingMongoConverter() {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
converter.setTypeMapper(new DefaultMongoTypeMapper(null));
return converter;
}
}
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(MultipleMongoProperties.class)
public class MultipleMongoConfig {
private final MultipleMongoProperties mongoProperties;
#Primary
#Bean(name = "primaryMongoTemplate")
public MongoTemplate primaryMongoTemplate() throws Exception {
return new MongoTemplate(primaryFactory(this.mongoProperties.getPrimary()));
}
#Bean(name = "secondaryMongoTemplate")
public MongoTemplate secondaryMongoTemplate() throws Exception {
return new MongoTemplate(secondaryFactory(this.mongoProperties.getSecondary()));
}
#Bean
#Primary
public MongoDbFactory primaryFactory(final MongoProperties mongo) throws Exception {
return new SimpleMongoDbFactory(new MongoClientURI(mongo.getUri()));
}
#Bean
public MongoDbFactory secondaryFactory(final MongoProperties mongo) throws Exception {
return new SimpleMongoDbFactory(new MongoClientURI(mongo.getUri()));
}
}
#Data
#ConfigurationProperties(prefix = "mongodb")
public class MultipleMongoProperties {
private MongoProperties primary = new MongoProperties();
private MongoProperties secondary = new MongoProperties();
}
#Configuration
#EnableMongoRepositories(
basePackages = {"com.student.repository.primary"},
mongoTemplateRef = "primaryMongoTemplate")
public class PrimaryMongoConfig {}
#Configuration
#EnableMongoRepositories(
basePackages = {"com.student.repository.secondary"},
mongoTemplateRef = "secondaryMongoTemplate")
public class SecondaryMongoConfig {}
Repository code:
public interface StudentDAO {
Student save(StudentInfo studentInfo);
}
#Repository
public class StudentDAOImpl implements StudentDAO {
#Autowired #Qualifier("primaryMongoTemplate")
private MongoTemplate mongoTemplate;
#Override public StudentInfo save(StudentInfo userWatchlist) {
return mongoTemplate.save(userWatchlist);
}
}
Integration Testing code:
#RunWith(SpringRunner.class)
#DataMongoTest(includeFilters = #Filter(Repository.class))
public class WatchListDAOImplIT {
#Autowired private StudentDAO studentDAO;
#Test
public void save() {
StudentInfo studentInfo = getStudentInfo();
StudentInfo dbUserWatchlist = watchListDAO.save(studentInfo);
Assert.assertEquals(studentInfo.getId(), dbUserWatchlist.getId());
}
private StudentInfo getStudentInfo() {
StudentInfo studentInfo = new StudentInfo();
studentInfo.setId(9999999l);
return studentInfo;
}
}
Which is giving me following error:-
java.lang.IllegalStateException: Failed to load ApplicationContext
at
org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
at
org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
at
org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:
: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'appMongoConfig': Unsatisfied dependency
expressed through field 'mongoDbFactory'; nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'primaryFactory' defined in class path
resource [.../config/mongo/MultipleMongoConfig.class]: Unsatisfied
dependency expressed through method 'primaryFactory' parameter 0;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'org.springframework.boot.autoconfigure.mongo.MongoProperties'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations: {}
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'org.springframework.boot.autoconfigure.mongo.MongoProperties'
available: expected at least 1 bean which
I use spring boot 2 application with spring data jpa and hibernate with postgres
package com.acmor.togy.repository.util.postgres
#Component
public class HStoreParameter implements FormatParameter{
...
}
package com.acmor.togy.repository.util;
public interface FormatParameter {
String format(Map<String, String> properties);
}
package com.acmor.togy.repository.util;
public class AbstractRepository<T, ID> extends SimpleJpaRepository<T, ID> {
private ThreadLocal<Map<String, Object>> parameters = new ThreadLocal<>();
#Autowired
private FormatParameter formatParameter;
public AbstractRepository(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
}
public AbstractRepository(Class domainClass, EntityManager em) {
super(domainClass, em);
}
}
package com.acmor.togy.repository;
#Repository
public class EnumsRepositoryImpl extends AbstractRepository implements EnumsRepositoryCustom {
}
public interface EnumsRepositoryCustom {
...
}
I created a basic test
#RunWith(SpringRunner.class)
public class EnumsRepositoryCustomTest {
#Autowired
private EnumsRepositoryCustom enumsRepository;
#Test
public void test_advanced_search_using_properties() {
EnumsSearch search = new EnumsSearch();
...
Page<Enums> page = enumsRepository.search(search, PageRequest.of(0, 10));
...
}
}
When I run test I get
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.acmor.togy.repository.util.FormatParameter' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I have a implementation of FormatParameter, it's HStoreParameter
I have a spring boot 2.0.0 M2 application who run well.
I use autowired on constructor
#RequestMapping(value = "/rest")
#RestController
public class AddressRestController extends BaseController{
private final AddressService AddressService;
#Autowired
public AddressRestController(final AddressService AddressService) {
this.AddressService = AddressService;
}
...
}
#Service
public class AddressServiceImpl extends BaseService implements AddressService {
#Autowired
public AddressServiceImpl(final AddressRepository AddressRepository) {
this.AddressRepository = AddressRepository;
}
private final AddressRepository AddressRepository;
...
}
public interface AddressRepository extends JpaRepository<Address, Integer>, AddressRepositoryCustom {
}
#Repository
public class AddressRepositoryImpl extends SimpleJpaRepository implements AddressRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Autowired
public AddressRepositoryImpl(EntityManager em) {
super(Address.class, em);
}
...
}
When i try to run a basic test
#RunWith(SpringJUnit4ClassRunner.class)
public class AddressServiceTest {
#Autowired
private AddressService service;
#MockBean
private AddressRepository restTemplate;
#Test
public void getAddress(){
MockitoAnnotations.initMocks(this);
Pageable page = PageRequest.of(0, 20);
Page<Address> pageAdr = mock(Page.class);
given(this.restTemplate.findAll(page)).willReturn(pageAdr);
Page<AddressDto> pageDto = service.getAddress(page);
}
}
I get this error
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name
'com.sonos.arcor.service.AddressServiceTest': Unsatisfied dependency
expressed through field 'service'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type ''com.sonos.arcor.service.AddressService'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I don't understand why i get this error.
You need to annotate the test with SpringBootTest so that spring initialize an application context
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications
#SpringBootTest
#RunWith(SpringJUnit4ClassRunner.class)
public class AddressServiceTest {
// the remaining test
}
Also you do not need MockitoAnnotations.initMocks(this);
Spring takes care of the mock handling
When [#MockBean is]used on a field, the instance of the created mock will also be
injected. Mock beans are automatically reset after each test method
see Mocking and spying beans
I upgraded my SpringBoot 1.3.5 to 1.5.2 RELEASE and am suddenly getting an error when my beans are being wired. I am not doing constructor injection anywhere, so the circular reference makes no sense.
Here is my main class:
#EnableGlobalMethodSecurity(securedEnabled = true)
#SpringBootApplication
public class MyApplication extends WebMvcConfigurerAdapter {
...
}
I have many #Configuration classes in my /src/java/../configuration/ such as
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
// default strength = 10
return new BCryptPasswordEncoder();
}
...
}
and
#Configuration
#ComponentScan()
public class MyApplicationConfiguration {
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
return new SessionTimeoutEmbeddedServletContainerCustomizer();
}
...
}
I also have a CustomUserDetailsService that does use the passwordEncoder, but its never been a problem prior:
#Component
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
#Autowired
private UserRepository userRepository;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private PasswordResetTokenRepository passwordTokenRepository;
...
}
And then I am using Spock for my IntegrationTests by extending
#ContextConfiguration(classes = MyApplication, initializers = ConfigFileApplicationContextInitializer.class)
#WebAppConfiguration
#SpringBootTest
public class BaseSpecification extends Specification {
def setup() {
RestAssured.port = serverPort;
}
#Value('${local.server.port}')
private int serverPort;
}
So all this worked with Spring-Boot 1.3.5 but now that I upgraded I get the following when I build or run tests:
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'customUserDetailsService': Unsatisfied
dependency expressed through field 'passwordEncoder'; nested exception
is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'passwordEncoder': Requested bean is
currently in creation: Is there an unresolvable circular reference?
... Caused by:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'passwordEncoder': Requested bean is
currently in creation: Is there an unresolvable circular reference?
UPDATE
Based upon Andy comment, I moved some dependencies around and think I have it fixed.
Removed this from WebSecurityConfig and put them in MyApplicationConfiguration:
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
return;
}
#Bean
public PasswordEncoder passwordEncoder() {
// default strength = 10
return new BCryptPasswordEncoder();
}
While I do not have any constructor injection, it would seem the new Spring Security does it DI differently. I cannot confirm it, but this is no longer an issue.