I have some spring component:
#Component
#ConditionalOnBean(value={JdbcTemplate.class})
public class DictionaryHandler implements Handler {
private final JdbcTemplate jdbcTemplate;
#Autowired
public DictionaryHandler(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//some methods
}
In debug, I see, that JdbcTemplate bean was created:
But, my bean not created.It doesn't go into the constructor. But why? Bean JdbcTemplate is exist, but my bean not created.
Does the condition work wrong? I don't want create DictionaryHandler if JdbcTemplate is missing. Without this I get an error.
You should use #ConditionalOnBean on auto configuration classes only (otherwise the order is unspecified)
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.
Since your JdbcTemplate bean is defined inside JdbcTemplateAutoConfiguration class so I assume that JdbcTemplateAutoConfiguration is marked with #Configuration.
In that case, you can ensure the instantiate of your bean by config:
#Configuration
#AutoConfigureAfter(JdbcTemplateAutoConfiguration.class)
public class DicationaryHandlerConfiguration {
#Bean
#ConditionalOnBean(JdbcTemplate.class)
public DictionaryHandler handler(JdbcTemplate jdbcTemplate) {
return new DictionaryHandler(jdbcTemplate)
}
}
public class DictionaryHandler implements Handler {
private final JdbcTemplate jdbcTemplate;
public DictionaryHandler(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//some methods
}
Check the documentation for ConditionalOnBean. It runs once and if the bean it requires not yet created - it will not trigger. The order of bean creation matters. You can try to lower priority of your component with #Order annotation or increase priority of you configuration/component class which holds JdbcTemplate bean.
From Annotation Type ConditionalOnBean:
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.
In your case, the problem is DictionaryHandler bean is attempted to be created before processing the configuration class and given that JdbcTemplate bean is not yet in DI container, your DictionaryHandler is not getting instantiated.
One workaround could be to initialize DictionaryHandler in the same configuration class as JdbcTemplate.
Also, you can use different configuration class, but you'll have to use #DependsOn annotation.
An example:
#Component
#ConditionalOnBean(name = "bean1")
class AnotherBean {
}
#Configuration
class Config {
#Bean
public Object bean1() {
return new Object();
}
}
In the previous example AnotherBean will not be created, but it will be in this way:
#Configuration
class Config {
#Bean
public Object bean1() {
return new Object();
}
#Bean
#ConditionalOnBean(name = "bean1")
public AnotherBean anotherBean() {
return new AnotherBean();
}
}
Related
I am learning concepts of Spring & I came across #Bean & #Component annotations. I want to know what will happen in below scenario:
#Configuration
class ConfigClass {
#Bean
public ComponentClass ComponentClass() {
return new ComponentClass(someDependency1, someDependency2, someDependency3);
}
}
#Component
class ComponentClass{
private SomeDependency1 sd1;
private SomeDependency2 sd2;
private SomeDependency3 sd3;
public ComponentClass(SomeDependency1 sd1, SomeDependency2 sd2, SomeDependency3 sd3) {
/* initialize here */
}
}
I have declared ComponentClass as #Component which means it is a spring bean now. But I have also defined a #Bean for it in config class separately.
Which of these beans will be actually used as by default Spring is singleton?
What happens when I remove #Component?
Spring will notice a mistake and throw NoUniqueBeanDefinitionException during application startup.
If you remove #Component annotation it will work as expected, #Bean will be used for initialization.
I have a Spring service which is checking database entries. To minimize my repository calls both find methods are "#Cacheable". But when I try to init my service bean while my configuration class has a CacheManager bean definition I get following NoSuchBeanDefinitionException:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'foo.mediacode.directory.MediaCodeDirectoryService' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:353)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1093)
at foo.mediacode.directory.MediaCodeDirectoryService.implementation(MediaCodeDirectoryService.java:63)
at foo.campaigntree.directory.CampaignTreeDirectoryService.<init>(CampaignTreeDirectoryService.java:18)
... 15 more
If I take out the CacheManager bean definition, I can init my service bean and it runs without any problems and caching!
Here is my code:
Configuration
...
#Configuration
#EnableCaching
#EnableJpaRepositories(...)
#PropertySource({...})
public class MediaCodeDirectoryServiceConfig {
private static Logger configLogger = Logger.getLogger(MediaCodeDirectoryServiceConfig.class.getName());
#Value("${jpa.loggingLevel:FINE}")
private String loggingLevel;
#Value("${mysql.databaseDriver}")
private String dataBaseDriver;
#Value("${mysql.username}")
private String username;
#Value("${mysql.password}")
private String password;
#Value("${mysql.databaseUrl}")
private String databaseUrl;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
...
}
#Bean
public MediaCodeDirectoryService mediaCodeDirectoryService() {
return new MediaCodeDirectoryService();
}
#Bean
public CacheManager mediaCodeCacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("mediaCodeMappingRegexCache"),
new ConcurrentMapCache("mediaCodeMappingsCache")));
return cacheManager;
}
#Bean
public JpaTransactionManager transactionManager() {
...
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
...
}
public DataSource getDataSource() {
...
}
public JpaDialect getJpaDialect() {
...
}
public Properties getEclipseLinkProperty() {
...
}
public JpaVendorAdapter getJpaVendorAdapter() {
...
}
}
Service
....
public class MediaCodeDirectoryService implements MediaCodeDirectoryServiceApi {
...
#Autowired
private MediaCodeDirectoryRepository repo;
#SuppressWarnings("resource")
public static MediaCodeDirectoryServiceApi implementation() {
if (INSTANCE == null) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MediaCodeDirectoryServiceConfig.class);
INSTANCE = ctx.getBean(MediaCodeDirectoryService.class);
}
return INSTANCE;
}
...
Repository
...
#Repository
public interface MediaCodeDirectoryRepository extends CrudRepository<MediaCodeDao, Integer> {
#Cacheable("mediaCodeMappingRegexes")
#Query("SELECT m FROM #{#entityName} m WHERE (m.fooId = :fooId) AND (m.isRegex = :isRegex) ORDER BY (m.orderId DESC, m.id ASC)")
List<MediaCodeDao> findByfooIdAndIsRegexOrderByOrderIdDescAndIdAsc(#Param("fooId") int fooId, #Param("isRegex") boolean isRegex);
#Cacheable("mediaCodeMappings")
List<MediaCodeDao> findByMediaCode(String MediaCode, Pageable pageable);
}
When I debug into DefaultListableBeanFactory I can find within beanDefinitionMap my mediaCodeDirectoryService and also within beanDefinitionNames mediaCodeDirectoryService appears. But DefaultListableBeanFactory.getBean(...) cannot resolve name and namedBean in line 364 is null.
When I try to get the context via String like:
INSTANCE = (MediaCodeDirectoryService) ctx.getBean("mediaCodeDirecotryService")
I avoid the NoSuchBeanDefinitionException but I run into an other one.
Anybody here has an idea on what might be the cause of this? Did I missed something in my configuration? Thx!
Caching is applied through AOP. For AOP Spring uses a proxy based approach and the default is to create interface based proxies.
public class MediaCodeDirectoryService implements MediaCodeDirectoryServiceApi {... }
With this class definition at runtime you will get a dynamically created class (Proxy$51 or something along those lines) which implements all interfaces but it isn't a MediaCodeDirectoryService. It is however a MediaCodeDirectoryServiceApi.
You have 2 ways of fixing this, either program to interfaces (which you should have been doing anyway because you have defined interfaces) instead of concrete classes or use class based proxies.
The first option involves you changing your code in the places the directly #Autowire or get an instance of MediaCodeDirectoryService to use MediaCodeDirectoryServiceApi instead (which imho you should already do, why else define an interface). Now you will get the proxy injected and everything will work.
The second option involves you setting proxyTargetClass=true on your #EnableCaching annotation. Then instead of an interface based proxy you will get a class based proxy.
#EnableCaching(proxyTargetClass=true)
I am reading beginning spring (wiley press) book. In chapter 2 there is an example
about Java configuration and #Autowired. It provides this #Configuration class
#Configuration
public class Ch2BeanConfiguration {
#Bean
public AccountService accountService() {
AccountServiceImpl bean = new AccountServiceImpl();
return bean;
}
#Bean
public AccountDao accountDao() {
AccountDaoInMemoryImpl bean = new AccountDaoInMemoryImpl();
//depedencies of accountDao bean will be injected here...
return bean;
}
#Bean
public AccountDao accountDaoJdbc() {
AccountDaoJdbcImpl bean = new AccountDaoJdbcImpl();
return bean;
}
}
and this regular bean class
public class AccountServiceImpl implements AccountService {
#Autowired
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
...
}
When I run the code, it works. But I expected an exception because I have defined 2 beans with the same type in the configuration.
I realized it works like this:
if Spring encounters multiple beans with same type it checks field name.
if it finds a bean with the name of the target field, it injects that bean into the field.
Isn't this wrong? Is there a bug in Spring's handling of Java configuration?
The documentation explains this
For a fallback match, the bean name is considered a default qualifier
value. Thus you can define the bean with an id "main" instead of the
nested qualifier element, leading to the same matching result.
However, although you can use this convention to refer to specific
beans by name, #Autowired is fundamentally about type-driven injection
with optional semantic qualifiers. This means that qualifier values,
even with the bean name fallback, always have narrowing semantics
within the set of type matches; they do not semantically express a
reference to a unique bean id
So, no, it's not a bug, that is the intended behavior. The bean id (name) will be used as a fallback if a by-type autowiring doesn't find a single matching bean.
I recently started working at a place that uses Java configuration for Spring as opposed to XML and so far I'm loving it.
My question is the following:
If we have a #Configuration annotated class A that imports another #Configuration annotated class B, what is the proper, type-safe way for a bean defined in A to depend on a bean defined in B.
Here's an example I saw in a blog (https://blog.codecentric.de/en/2012/07/spring-dependency-injection-styles-why-i-love-java-based-configuration/):
#Configuration
public class PartnerConfig {
#Bean
public PartnerService partnerService() {
return new PartnerServiceImpl();
}
}
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Autowired
private PartnerConfig partnerConfig;
#Bean
public CashingService cashingService() {
return new CashingServiceImpl(partnerConfig.partnerService());
}
}
As a second part to my question, if I was to do the above, would Spring interpret as a bean dependency? That is, when I do
partnerConfig.partnerService()
in the example above, am I getting Spring to fetch me the partnerService bean, or am I just calling a regular java method and creating a new instance of the PartherService (which is NOT what I want, since the bean should be a singleton) ?
EDIT:
It has been suggested to use a #Qualifier. Would this work?
#Configuration
public class PartnerConfig {
#Bean
#MyCustomQualifier
public PartnerService partnerService() {
return new PartnerServiceImpl();
}
}
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Bean
public CashingService cashingService(#MyCustomQualifier PartnerService partnerService) {
return new CashingServiceImpl(partnerService);
}
}
I recommend giving the docs a read: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html
Refer to the section:
#Bean Methods in #Configuration Classes
This sums it up very well.
Typically, #Bean methods are declared within #Configuration classes. In this case, bean methods may reference other #Bean methods in the same class by calling them directly. This ensures that references between beans are strongly typed and navigable.
Also take a look at: http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
Section:
Composing #Configuration classes
Just add the dependency as an argument to the #Bean annotated method and remove the autowiring of the configuration.
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Bean
public CashingService cashingService(PartnerService partnerService) {
return new CashingServiceImpl(partnerService);
}
}
or simply autowire the PartnerService instead of the configuration.
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Autowire
private PartnerService partnerService;
#Bean
public CashingService cashingService() {
return new CashingServiceImpl(partnerService);
}
}
I am trying to learn how to use transaction in java spring. I am a java novice so please bear with me :-) The unit test im trying to achieve below is testing:
rollback if a runtime exception is thrown.
The problem im having is a
java.lang.NullPointerException?
Ok here goes.. I have stripped out some code to help improve readability
TutorialTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class TutorialTest {
#Autowired
private Dao dao;
#Test
public void that_if_a_runtime_exception_is_thrown_transaction_rolledback(){
User u = new User();
u.setUsername("FAIL_TEST");
u.setPhone("0161");
u.setEmail("FAIL_TEST#gmail.com");
//dao.addUser(u); // -- this will insert if uncommented out so I know it works
OuterService os = new OuterService();
os.addUserThrowError(u);
}
}
OuterService.java
#ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class OuterService {
#Autowired
private Dao dao;
#Transactional
public void addUserThrowError(User user) throws RuntimeException{
dao.addUser(user); // gives me a java.lang.NullPointerException?
throw new RuntimeException("This should roll back DB entry");
}
}
Beans are declared in
AppConfig.java
#Configuration
#EnableTransactionManagement
#ComponentScan(value = {"com.training.spring.tx.tutorial.dao",
"com.training.spring.tx.tutorial.service"})
public class AppConfig {
public DataSource dataSource() {
// Create a BasicDataSource object and configure database
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/spring_training_tx");
dataSource.setUsername("training");
dataSource.setPassword("training");
return dataSource;
}
#Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
#Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(transactionManager().getDataSource());
}
}
First of all, to quote the javadoc of #ContextConfiguration
#ContextConfiguration defines class-level metadata that is used to
determine how to load and configure an ApplicationContext for
integration tests.
Consider how you are using it on OuterService. Does it seem right? Is OuterService meant to be used to load and configure an ApplicationCOntext for integration tests? Unless I'm missing something essential, the answer is: No.
So what is OuterService? It's some kind of service. You seem to want to use it as a bean. What is a bean? A bean is an object whose lifecycle is managed by Spring. This includes instantiation of the bean class, initialization of the object, post processing, and, finally, destruction of the object.
If you created the object like so
OuterService os = new OuterService();
then Spring is not involved. You created the object and there is no way for Spring to hook into that. You cannot therefore expect Spring to autowire its field
#Autowired
private Dao dao;
And since you haven't initialized the field, it remains null, which causes the NullPointerException.
So how do we get an OuterService bean, which is managed by Spring? You either declare a #Bean method for OuterService or you annotate OuterService with #Component or any of its specializations and component-scan the package it is in. You then inject the bean into any other bean that uses it. For example,
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class TutorialTest {
#Autowired
private Dao dao;
#Autowired
private OuterService os;
You can then use that variable directly.