I wonder if there is a way to get #Required working when doing the configuration by annotations. I turned my configuration up-and-down and back again but nothing seems to work for me. I'm using Spring 3.1
My basic configuration looks like this:
#Configuration
public class SpringConfig {
#Bean
public MailSender mailSender() {
MailSender MailSender = new MailSender();
// mailSender.setBean(dlMailSender);
return mailSender;
}
#Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
// setting som props
return myBean;
}
}
MailSender is here:
#Configurable
public class MailSender {
private MyBean myBean;
#Required
public void setMyBean(MyBean myBean) {
this.myBean = myBean;
}
}
I'm testing it with this junit:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SpringConfig.class }, loader = AnnotationConfigContextLoader.class)
public class MailSenderTest {
#Test
public void test_main_beans_exists() {
// when then given
}
}
Thanks for any help
Short answer - this is not even theoretically possible.
When using XML-based, bean definitions with their dependencies are completely managed by application context. Spring is able to check, what is being set and what is not being set.
When using annotation-based configuration, you are setting the dependencies yourself. There is no way how Spring can even know what you are doing with the bean before returning it from the factory method.
If you want to check whether the bean is correctly initialized, use InitializingBean or #PostConstruct and implement self-checking method. Spring is doing this regularly in its own beans.
Related
I am creating a custom AutoConfiguration for Spring Boot. One of the features I was attempting to create was to create one or more Beans dynamically and adding them to the ApplicationContext at runtime.
The problem I ran into was with Autowiring. My #SpringBootApplication class autowires those beans, and since they do not exist yet, autowire fails.
My first solution was to put #Lazy on the autowire, and that solved my problem.
However, I ran into something interesting. I added two beans that I was looking for into the AutoConfiguration code, and of course, it worked. By accident, I only removed one of the beans and re-ran my code. It worked.
#SpringBootApplication
public class SpringBootDemoApplication {
#Autowired
#Qualifier("some_name")
private MyClass myClass;
#Autowired
#Qualifier("another_name")
private MyClass anotherClass;
...
}
#Configuration
public class MyAutoConfigurationClass {
#Bean(name="some_class")
public MyClass myClass () {
return null;
}
}
So the short of it is this. If I defined only one of the beans in my autoconfiguration class, this seems to satisfy Autowired and it does not blow up and when I dynamically add my other beans, both beans are found.
The stipulation is that the Autowired bean that is first, must be the bean that is defined in my autoconfiguration class.
I am running the following:
Spring Boot Starter 1.5.7-RELEASE
Various Spring Framework 4.3.11-RELEASE
Is this a bug? Or is this the way Autowired is supposed to work?
#SpringBootApplication
public class SpringBootDemoApplication {
#Autowired
#Qualifier("myclass")
private MyClass myClass;
#Autowired
#Qualifier("anotherMyClass")
private MyClass anotherMyClass;
...
}
#Configuration
public class MyAutoConfiguration {
private ConfigurableApplicationContext applicationContext;
private final BeanFactory beanFactory;
#Autowired
private MyClassFactory myClassFactory;
public MyAutoConfiguration(ApplicationContext applicationContext, BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
#PostConstruct
public void init() throws IOException, SQLException {
this.myClassFactory.create(this.applicationContext);
}
// without this #Bean definition SpringBoot will recieve the following error and stop
// AnnotationConfigEmbeddedWebApplicationContext - Exception encountered during context initialization
#Bean(name="myClass")
public DataSource anyNameWillDoItDoesntMatter() {
return null;
};
}
#Component
class MyClassFactory {
public void create(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().registerSingleton(name, value);
}
}
So is this expected behavior of #Autowired?
There have been several arguments around not using ApplicationContext.getBean() to get a bean reference, of which most are based on logic that it violates the principles of Inversion of control.
Is there a way to get reference to prototype scoped bean without calling context.getBean() ?
Consider to use Spring Boot!
Than you can do something like this...
Runner:
#SpringBootApplication
public class Runner{
public static void main(String[] args) {
SpringApplication.run(Runner.class, args);
}
}
Some Controller:
#Controller
public class MyController {
// Spring Boot injecting beans through #Autowired annotation
#Autowired
#Qualifier("CoolFeature") // Use Qualifier annotation to mark a class, if for example
// you have more than one concreate class with differant implementations of some interface.
private CoolFeature myFeature;
public void testFeature(){
myFeature.doStuff();
}
}
Some cool feature:
#Component("CoolFeature") // To identify with Qualifier
public class CoolFeature{
#Autowired
private SomeOtherBean utilityBean;
public void doStuff(){
// use utilityBean in some way
}
}
No XML files to handle.
We can still access context for manual configurations if needed.
Suggested reading:
Spring Boot Reference
Pro Spring Boot
This type of problem can be solved using method injection, which is described in more detail here: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-method-injection
This is the most common approach to create prototype bean:
abstract class MyService {
void doSome() {
OtherService otherService = getOtherService();
}
abstract OtherService getOtherService();
}
#Configuration
class Config {
#Bean
public MyService myService() {
return new MyService() {
OtherService getOtherService() {
return otherService();
}
}
}
#Bean
#Scope("prototype")
public OtherService otherService() {
return new OtherService();
}
}
I have an application with 2 Contexts. Parent for web agnostic business logic and ChildContext (implicitly created by dispatcher servlet) for web logic.
My setup loks like
#Configuration
public class BusinessConfig {
#Bean
public ObjectMapper jacksonMapper() { return new ObjectMapper() }
}
and
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ObjectMapper objectMapper; // <- is null for some reason
#Override
public configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper); // <- bang!
messageConverters.add(converter);
}
}
I need the the object mapper in the parent context, as I use it also in security configuration. But can someone explain me, why the #Autowired objectMapper is null? Its created in the parent context (the fact that the parent exists is even logged by spring at startup). Also #Autowired has required=true by default, so it should not blow up in the configure method (it should have blown up in construction of the context, if the bean wasn't there for some reason).
It seems to me that there might be some lifecycle problem in spring - in a sense that it calls the overridden methods first, and then #Autowires the dependencies... I have also tried to #Autowire the BusinessConfig (should be perfectly legal according to documentation - the result was the same (null)).
What should I do to make this working?
Thanks in advance!
EDIT - ISSUE FOUND
I found the issue. Unfortunately it had nothing to do with WebMvcConfigurerAdapter nor #Configuration. It was caused by premature initialization of context triggered by missing static modifier for propertyPlaceholderConfigurer... I have created issue in Spring core jira (https://jira.spring.io/browse/SPR-14382)
What about simply renaming the bean declaration method to match with the autowired bean?
#Configuration
public class BusinessConfig {
#Bean
public ObjectMapper objectMapper() { return new ObjectMapper() }
}
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ObjectMapper objectMapper;
[...]
}
Just recently started looking into Spring and specifically its latest features, like Java config etc.
I have this somewhat strange issue:
Java config Snippet:
#Configuration
#ImportResource({"classpath*:application-context.xml","classpath:ApplicationContext_Output.xml"})
#Import(SpringJavaConfig.class)
#ComponentScan(excludeFilters={#ComponentScan.Filter(org.springframework.stereotype.Controller.class)},basePackages = " com.xx.xx.x2.beans")
public class ApplicationContextConfig extends WebMvcConfigurationSupport {
private static final Log log = LogFactory.getLog(ApplicationContextConfig.class);
#Autowired
private Environment env;
#Autowired
private IExtendedDataSourceConfig dsconfig;
#PostConstruct
public void initApp() {
...
}
#Bean(name="transactionManagerOracle")
#Lazy
public DataSourceTransactionManager transactionManagerOracle() {
return new DataSourceTransactionManager(dsconfig.oracleDataSource());
}
IExtendedDataSourceConfig has two implementations which are based on spring active profile one or the other in instantiated. For this example let say this is the implementation :
#Configuration
#PropertySources(value = {
#PropertySource("classpath:MYUI.properties")})
#Profile("dev")
public class MYDataSourceConfig implements IExtendedDataSourceConfig {
private static final Log log = LogFactory.getLog(MYDataSourceConfig.class);
#Resource
#Autowired
private Environment env;
public MYDataSourceConfig() {
log.info("creating dev datasource");
}
#Bean
public DataSource oracleDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl(env.getProperty("oracle.url"));
dataSource.setUsername(env.getProperty("oracle.user"));
dataSource.setPassword(env.getProperty("oracle.pass"));
return dataSource;
}
The problem is that when transactionManagerOracle bean is called, (even if I try to mark it as lazy) dsconfig variable value appears to be null.
I guess #beans are processed first and then all Autowires, is there a fix for this? How do I either tell spring to inject dsconfig variable before creating beans, or somehow create #beans after dsconfig is injected?
You can just specify DataSource as method parameter for the transaction manager bean. Spring will then automatically inject the datasource, which is configured in the active profile:
#Bean(name="transactionManagerOracle")
#Lazy
public DataSourceTransactionManager transactionManagerOracle(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
If you still want to do this through the configuration class, specify that as parameter:
public DataSourceTransactionManager transactionManagerOracle(IExtendedDataSourceConfig dsconfig) {}
In both ways you declare a direct dependency to another bean, and Spring will make sure, that the dependent bean exists and will be injected.
I have Spring bean with annotations:
#Named
#Scope("session")
And this bean property:
#Autowired
ApplicationContext appContext;
The Spring configuration file has entry (that works for other anotations/injections):
<context:component-scan base-package="my.package.name" />
Why appContext is null after such code and configuration?
I am trying to get ApplicationContext (to call getBean(...) on it) and this can be quite involved task (judging from other discussions) in previous Spring versions (e.g. one is required to get ServletContext in Spring web application to create ApplicationContext and getting ServletContext can be quite involved task for beans that don't directly access HTTP Request objects). In Spring 3.x, as I understand, simple #Autwired injection can be used. How AppContext can be accessed?
Here the first problem is you are using #Named which is Java EE annotation and as for as I know Spring yet to support Java EE annotations. Hence instead of using #Named try to use Spring annotation #Service, #Component, #Repository etc.
Here is the example for you I have used JSF Managed bean as well to show how to integrate beans.
#ManagedBean(name="myBacking")
#RequestScoped
public class MyBacking {
private String myText;
#ManagedProperty(value="#{mySpring}")
MySpringBean mySpring;
public String getMyText() {
myText = mySpring.getText();
return myText;
}
public void setMyText(String myText) {
this.myText = myText;
}
public MySpringBean getMySpring() {
return mySpring;
}
public void setMySpring(MySpringBean mySpring) {
this.mySpring = mySpring;
}
}
#Service("mySpring")
#Scope("request")
public class MySpringBean {
#Autowired
MySecond mySecond;
public String getText(){
return "Hello KP" + mySecond.appObj();
}
}
#Service
#Scope("request")
public class MySecond {
#Autowired
ApplicationContext applicationContext;
public String appObj(){
MyThrid mythird =(MyThrid)applicationContext.getBean("myThrid");
return "My Second Bean calld "+ mythird.getTxt();
}
}
#Service
public class MyThrid {
public String getTxt(){
return "from thrid Bean";
}
}