I've implemented a factory bean in Spring to instantiate different subclasses of a superclass. The problem I have is that superclass properties aren't being #Autowired (I guess due to the new command in the factory methods). This is my code:
#Component
public class ConfigBeanImpl implements ConfigBean{
#Override
public String expandParam(String param) {
return String.format("expanded %s", param);
}
}
public abstract class FactoryBean {
#Autowired
protected ConfigBean configBean;
private String property;
protected FactoryBean() {
this.property = configBean.expandParam("property");
}
public abstract String getProperty();
public static FactoryBean GET(int id) {
return new FactoryBeanGet(id);
}
public static FactoryBean POST(String param){
return new FactoryBeanPost(param);
}
}
public class FactoryBeanGet extends FactoryBean {
private int id;
protected FactoryBeanGet(int id) {
this.id = id;
}
#Override
public String getProperty() {
return Integer.toString(id);
}
}
public class FactoryBeanPost extends FactoryBean {
private String param;
protected FactoryBeanPost(String param) {
this.param = param;
}
#Override
public String getProperty() {
return param;
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"});
FactoryBean bean = (FactoryBean) context.getBean("factoryBeanGet", 12);
System.out.println(bean.getProperty());
bean = (FactoryBean) context.getBean("factoryBeanPost", "test param");
System.out.println(bean.getProperty());
}
}
And the applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="com.spring" />
<bean id="factoryBeanGet" scope="prototype" class="com.spring.bean.FactoryBean"
factory-method="GET">
</bean>
<bean id="factoryBeanPost" scope="prototype" class="com.spring.bean.FactoryBean"
factory-method="POST">
</bean>
The protected ConfigBean configBean property in abstract class FactoryBean isn't being #Autowired and hence is null and the constructor throws a NullPointerException. If I place it inside each of the subclasses, it works fine, but it'd be duplicated code. Is there a way to solve this or am I doing something wrong?
Put yourself in Spring's shoes. It must instantiate FactoryBean, and then initialize its configBean field. So, the first thing it will do is call the constructor. And then, once the object exists, it will initialize the object's field. It obviously can't initialize the field if the object doesn't exist yet. So, at the time the constructor is called, the field is still null.
Use constructor injection, or use a method annotated witb #PostConstruct to call the configBean.
That said, the private property field that you're trying to initialize is not used anywhere, so you could as well remove it, and remove the configBean field as well.
Related
Here my (simplified) code before explaining my problem :
foo.bar.MyFile
public class MyFile extends MyFileAbstract {
#Value("${FILE_PATH}")
private String path;
[...]
public MyFile(final Date date, final String number, final List<MyElement> elements) {
this.date = date;
this.number = number;
this.elements = elements;
}
#Override
public String getPath() {
return path;
}
[...]
}
foo.bar.MyService
#Service
public class MyService {
[...]
public String createFolder(MyFileAbstract file) throws TechnicalException {
[...]
String path = file.getPath();
[...]
}
[...]
}
the call of service
[...]
#Autowired
MyService service;
public void MyMethod() {
MyFile file = new MyFile();
service.createFolder(file);
[...]
}
[...]
I use a context XML to configure Spring :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<context:property-placeholder
file-encoding="utf-8"
location="file:///[...]/MyProperties.properties" />
<context:annotation-config />
<context:component-scan base-package="foo.bar.classes" />
[...]
</beans>
The problem is that the variable path is null at runtime in both MyService and MyFile file when a instantiate MyFile to call my service MyService.
I am looking a solution to inject my property ${FILE_PATH} inside MyFile.
Here my environment :
Apache Tomcat 7
Java 8
Spring 4.1.6.RELEASE
I have seen that Spring AOP with #Configurable bean could resolve this but don't want to change my Java Agent because I don't want to modify the configuration on the production server.
And I don't know how to use #Service on MyFile with my custom constructor.
Any idea is welcome.
You can add to your MyService
#Autowired
private Environment environment;
and just get the value
environment.getProperty("FILE_PATH");
After that you can set it to the file if necessary.
#Service
public class BeanUtilityService implements ApplicationContextAware {
#Autowired
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Create a Utility class as a service , create a static method and get the bean from the context.Then use that bean to get the properties required
use #PropertySource annotation
#PropertySource("classpath:config.properties") //use your property file name
public class MyFile extends MyFileAbstract {
#Value("${FILE_PATH}")
private String path;
[...]
public MyFile(final Date date, final String number, final List<MyElement> elements) {
this.date = date;
this.number = number;
this.elements = elements;
}
#Override
public String getPath() {
return path;
}
[...]
}
Hy
I'm looking for a way to 'simplify'/shorten my spring configuration.
I' ve got this Generic service that looks something like:
public class GenericService<T> {
private Class<T> targetClass;
public void setTargetClass(Class<T> targetClass) {this.targetClass = targetClass;}
public void doSomething() {...}
}
and in my spring-config.xml file I have
<bean id="entity1Service" class="GenericService">
<property name="targetClass" value="model.Entity1" />
</bean>
<bean id="entity2Service" class="GenericService">
<property name="targetClass" value="model.Entity2" />
</bean>
...
I'm trying to build a factory that will build all these services for me so that I could write something like this in my spring-config.xml
<bean class="ServiceFactory">
<property name="targets">
<list>
<value>model.Entity1</value>
<value>model.Entity2</value>
</list>
</property>
</bean>
which would generate 2 beans (one named entity1Service, the other entity2Service). Get-it?
How would I start? I've looked at BeanFactory (not to be confused with FactoryBean!) but fail to see how to hookup everything up.
It would be even better if my factory could scan my packages and generate a service when it finds an entity (either through annotation or interface implementation), a little like #EnableJpaRepositories annotation in spring-data does for all JpaRepository interfaces.
Thanks for any insights, examples, pointers...
w.
I believe I've figured it out and posting my result for future references.
public class GenericBeanGenerator <T, G> implements BeanFactoryPostProcessor, BeanPostProcessor {
/**
* The type of the bean to create
*/
private Class<T> type;
/**
* The list of generic values. Can be String, Class or whatever
*/
private Iterable<G> generics;
private Map<String, G> beanNameToGeneric = new HashMap<String, G>();
public GenericBeanGenerator(Class<T> type, G[] generics) {
this.type = type;
this.generics = Arrays.asList(generics);
}
public GenericBeanGenerator(Class<T> type, Iterable<G> generics) {
this.type = type;
this.generics = generics;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// for each 'generic' value, add a bean definition to the beanFactory with the appropriate bean name
for(G generic : generics) {
String beanName = getBeanName(generic);
beanNameToGeneric.put(beanName, generic);
((DefaultListableBeanFactory) beanFactory).registerBeanDefinition(beanName, new AnnotatedGenericBeanDefinition(type) );
}
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (type.isInstance(bean) && beanNameToGeneric.containsKey(beanName)) {
#SuppressWarnings("unchecked")
T instance = (T) bean;
initiliaze(beanName, beanNameToGeneric.get(beanName), instance);
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* Convert a 'generic' value to a string in order to name the bean
*/
public String getBeanName(G generic) {
return generic.toString();
}
/**
* Initialize the bean if needed.
*/
public void initiliaze(String beanName, G generic, T instance) {
}
}
Now if I want a number of generics services that extends a class like...
class GenericService<T> {
Class<T> entityClass;
public void setEntityClass(Class<T> clazz) {
this.entityClass = clazz;
}
....
}
I could have something like this in one of my #Configuration beans
class Config {
#Bean
public static GenericBeanGenerator<GenericService, Class<?>> genericServiceGenerator() {
List<Class<?>> entities = Arrays.asList(A.class, B.class);
return new GenericBeanGenerator<GenericService, Class<?>>(GenericService.class, entities) {
#Override
public String getBeanName(Class<?> generic) {
return generic.getSimpleName() + "Service";
}
#Override
public void initiliaze(String beanName, Class<?> generic, GenericService instance) {
instance.setEntityClass(generic);
}
};
}
}
This would generate two beans named AService and BService for my entities A and B.
And even more powerfull: If my entities all implement an interface named Idable, I could generate all my service beans if I used something like this to scan my packages:
BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
ClassPathBeanDefinitionScanner s = new ClassPathBeanDefinitionScanner(bdr);
TypeFilter tf = new AssignableTypeFilter(Idable.class);
s.addIncludeFilter(tf);
s.scan("org.willix.model");
entities = bdr.getBeanDefinitionNames();
You can try doing it programmatically:
public class Application {
#Autowired
private ApplicationContext applicationContext;
public void loadBeans() {
NewBean newBean = applicationContext.getAutowireCapableBeanFactory().createBean(NewBean.class);
}
}
You should also be able to autowire these beans after they have been created.
Edit:
You can name these beans using an annotation in the bean's class:
#Component("NewBean1")
public class NewBean1 implements GenericService<T> {
}
And then when you autowire it, use the #Qualifier annotation
public class BeanController {
#Autowired
#Qualifier("NewBean1")
private GenericService<T> bean;
}
A class
public class A {
private String name;
public A() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
BeanFactory class
public class BeanFactory implements InitializingBean, DisposableBean{
private A a;
public BeanFactory(){
}
public BeanFactory(A a){
this.a = a;
}
public void printAName(){
System.out.println("Class BeanFactory: beanFactory.printAName -> a.getName() = " + a.getName());
}
}
Main
public class Main {
public static void main(String[] args) {
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"ApplicationContext.xml");
BeanFactory beanFactory = applicationContext.getBean("beanFactory",
BeanFactory.class);
beanFactory.printAName();
}
}
ApplicationContext
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<bean id="beanFactory" class="testSpring.BeanFactory">
<constructor-arg ref="a1"/>
</bean>
<bean id="a1" class="testSpring.A">
<property name="name" value="I am A!"></property>
</bean>
</beans>
Result of run: Class BeanFactory: beanFactory.printAName -> a.getName() = I am A!
Like you can see, here I don't use no annotation. But the code works thanks to xml file.
So xml doesn't need annotation..? Can I use one or the other?
If I would use, in this application, the annotation (#Autowired for example) instead of bean xml, it's possible? Can you show me how?
Or the annotation must require xml reference?
So.. annotation and xml must be used together? Thanks
You should use annotation configuration, this is the idea
#Component
class Bean1 {
public Bean1() {
System.out.println(getClass());
}
}
#Configuration
#ComponentScan("test")
public class Config {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
}
}
For details see Spring docs
Given the following MyConstructorClass:
#Component
public class MyConstructorClass{
MyObj var;
public MyConstructorClass( MyObj constrArg ){
this.var = var;
}
...
}
How can I autowire a field that requires a constructor argument from a previously #Autowired field? Below is an example.
Note - this question, I believe, is similar to this one, except that my constructor argument is not a String. This code sample is slightly modified from this question.
#Service
public class MyBeanService{
#Autowired
CustomObject customObj; // no arguments to constructor
#Autowired
MyConstructorClass myConstructorClass; // requires `customObj` as an argument
....
}
How can I modify MyBeanService to properly construct myConstructorClass with customObj?
You just need to annotated the constructor of MyConstructorClass with #Autowired:
#Component
public class MyConstructorClass {
final private CustomObject customObj;
#Autowired
public MyConstructorClass(CustomObject customObj) {
this.customObj = customObj;
}
}
Another alternative, (without adding the #Autowired constructor to MyConstructorClass) is to use a #Configuration bean:
#Configuration
public class MyConfiguration {
#Bean
public CustomObject customObj() {
return customObj;
}
#Bean
public MyConstructorClass myConstructorClass() {
return new MyConstructorClass(customObj());
}
#Bean
public MyBeanService myBeanService() {
return new MyBeanService();
}
}
you can use the <constructor-arg> in your servlet
<bean id="myConstructorClass" class="package.MyConstructorClass">
<constructor-arg>
<bean class="package.CustomObject"/>
</constructor-arg>
</bean>
If this is the only place you need to use that class could use #PostConstruct to instantiate itself. I think there is a better solution, but this is off the top of my head.
#Service
public class MyBeanService{
#Autowired
CustomObject customObj;
MyConstructorClass myConstructorClass;
#PostConstruct
public void construct()
{
myConstructorClass = new MyConstructorClass(customObj);
}
}
In the constructor, you can refer to other beans defined in Spring.
<bean id="myConstructorClass" class="package.MyConstructorClass">
<constructor-arg index="0" ref="customObj"/>
</bean>
I have the following code:
#Component
public class MainBean {
#Autowired
private MyTask myTask
#Autowired
private TaskScheduler taskScheduler
public void start() {
String str = "Print something to console";
//somehow call constructor and pass str argument??
taskScheduler.execute(myTask);
}
}
#Component
public class MyTask implements Runnable {
private String str;
#Autowired
public MyTask(String str) {
this.str = str;
}
#Override
public void run() {
System.out.println(str);
}
}
I want to call MyTask and pass the str argument to the constructor. How can I do this? I cant find any good examples anywhere.
If I understand correctly what you are trying to do, a good solution would be the following:
#Component
public class MainBean {
#Autowired
private MyTaskFactory myTaskFactory
#Autowired
private TaskScheduler taskScheduler
public void start() {
String str = "Print something to console";
taskScheduler.execute(myTaskFactory.getTask(str));
}
}
public class MyTaskFactory {
public MyTask getTask(String str) {
return new MyTask(str);
}
}
#Configuration
public class MyTaskFactoryConfig {
#Bean
public MyTaskFactory myTaskFactory() {
return new MyTaskFactory();
}
}
Note that MyTask will then be changed to:
public class MyTask implements Runnable {
private String str;
public MyTask(String str) {
this.str = str;
}
#Override
public void run() {
System.out.println(str);
}
}
As seen from annotations, you've configured MyTask to be Spring managed. Also, str property is Spring managed, it should be injected by Spring to MyTask.
So, whenever you need MyTask instance, you don't create it yourself.
You #Autowire wherever you need.
public class ClassThatNeedsMyTaskInstances{
#Autowired
MyTask myTask;
}
But beware that by default, MyTask will be a singleton, so you may want to change its scope to prototype.
But this may be a good case where you don't let Spring manage MyTasks lifecycle. Instead you manage it yourself by creating instances using new, or factory
The following examples are for injecting the string from and XML and a Java-based configuration:
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="MyTaskConstructor" class="java.lang.String">
<constructor-arg type="char[]" value="My value"></constructor-arg>
</bean>
<bean id="myTask" class="com.package.MyTask">
<constructor-arg ref="MyTaskConstructor"/>
</bean>
</beans>
Java-based
#Configuration
public class MyTaskConfig {
#Bean
public String getMyTaskConstructor() {
return "My value";
}
#Bean
public MyTask myTask() {
return new MyTask(getMyTaskConstructor());
}
}