// My Factory class
#Component
public class UserRewardAccountValidatorFactory {
#Autowired
private VirginAmericaValidator virginAmericaValidator;
private static class SingletonHolder {
static UserRewardAccountValidatorFactory instance = new UserRewardAccountValidatorFactory();
}
public static UserRewardAccountValidatorFactory getInstance() {
return SingletonHolder.instance;
}
private UserRewardAccountValidatorFactory() {}
public PartnerValidator getPartnerValidator(Partner partner){
return virginAmericaValidator;
}
}
// My Validator class
#Service
public class VirginAmericaValidator implements PartnerValidator {
#Override
public void validate(String code) throws InvalidCodeException{
//do some processing if processing fails throw exception
if (code.equals("bad".toString())){
throw new InvalidCodeException();
}
}
}
//Usage:
PartnerValidator pv = UserRewardAccountValidatorFactory.getInstance().getPartnerValidator(partner);
if (pv != null){
try {
pv.validate(userRewardAccount);
} catch (InvalidCodeException e){
return buildResponse(ResponseStatus.INVALID_USER_REWARD_ACCOUNT, e.getMessage());
}
}
My package scan level is at much higher level. Whats happening is my virginAmericaValidator is always empty. Why is #Autowired annotation not working.
Your current approach will not work with Spring as you are ultimately using new UserRewardAccountValidatorFactory to create the instance which essentially bypasses Spring context altogether. Two approaches that should possibly work are these:
a. Using a factory-method and using xml to define your bean:
<bean class="package.UserRewardAccountValidatorFactory" name="myfactory" factory-method="getInstance"/>
This will essentially return the instance that you are creating back as a Spring bean and should get autowired cleanly.
b. Using Java #Configuration based mechanism:
#Configuration
public class MyBeanConfiguration {
#Bean
public UserRewardAccountValidatorFactory myFactory() {
return UserRewardAccountValidatorFactory.getInstance();
}
}
Related
I have the following pseudo code:
#Bean
public List<BeanB> beanB(
List<BeanA> beansA) {
List<BeanB> beansB = new ArrayList<>();
for (BeanA beanA : beansA) {
beansB.add(new BeanB(beanA))
}
return beansB;
}
#Bean
public BeanC beanC(
List<BeanB> beansB) {
return new BeanC(beansB);
}
Now the challenge is when the list of BeansB is constructed post construct is not invoked on those beans in the list. Is there any idiomatic way to trigger post construct invocation on those beans.
This does not work well as #PostConstruct is called by Spring. and from your code, for List<BeanB>, Spring will only try to find #PostConstruct in List. If it can find it, it will execute the #PostConstruct code.
I suspect you are writing beanA as follow in case you have #PostConstruct in beanA's class like below.
#Bean
public BeanA beanA_1() {
return new BeanA();
}
#Bean
public BeanA beanA_2() {
return new BeanA();
}
// which naming List of Bean B as beanB does not seems a good idea though
#Bean
public List<BeanB> beanB(List<BeanA> beansA) {
List<BeanB> beansB = new ArrayList<>();
for (BeanA beanA : beansA) {
beansB.add(new BeanB(beanA))
}
return beansB;
}
To make it work, there are multiple ways, I will just suggests some of them.
Creating a new Class, says BeanBCollectionWrappe to wrap the List of BeanB. And to make the code a bit more robust, I am implementing InitializingBean instead of #PostConstruct which is essentially the same, but allow me to ensure the method is afterPropertiesSet but not any methods.
public class BeanBCollectionWrapper implements InitializingBean {
private List<BeanB> beansB;
public void afterPropertiesSet() throws Exception {
for (BeanB beanB: beansB) {
beanB.afterPropertiesSet();
}
}
// getter and setter
}
public class BeanB implements InitializingBean {
public void afterPropertiesSet() throws Exception {
// ...
}
}
The Wrapper code can definitely be a bit better by use of Generics, like replacing BeanB to <T extends InitializingBean>.
And for the last Part,
#Bean
public BeanC beanC(BeanBCollectionWrapper wrapper) {
return new BeanC(wrapper.getBeansB());
}
Another method can be done by implementing BeanPostProcessor.
For example,
#Component
public class BeanBListBeanPostProcessor implements BeanPostProcessor{
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("beanB")) {
List<BeanB> beanBList = (List) bean;
for (BeanB beanB : beanBList) {
try {
beanB.afterPropertiesSet();
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
return null;
}
}
public class BeanB implements InitializingBean {
public void afterPropertiesSet() throws Exception {
// ...
}
}
Assume I am creating a PrinterService class that has a AbstractPrinter object. AbstractPrinter is subclassed by classes such as HPPrinter, FilePrinter etc.
The exact kind of printer object to be used is mentioned in the RequestParam object passed to my Controller (it is a request attribute).
Is there any way I can inject the right kind of concrete printer class using Spring?
All the other dependencies are injected using #Autowired annotation. How to inject this one?
You can create and load a factory of AbstractPrinter objects during container startup as shown below and dynamically call the respective the AbstractPrinter's print() (or your own method) based on the input parameter (comes from controller) to the service.
In the below code for PrinterServiceImpl class, the main point is that all of the List<AbstractPrinter> will be injected by Spring container (depends upon how many implementation classes you provide like HPPrinter, etc..). Then you will load those beans into a Map during container startup with printerType as key.
#Controller
public class YourController {
#Autowired
private PrinterService printerService;
public X myMethod(#RequestParam("input") String input) {
printerService.myServiceMethod(input);
//return X
}
}
PrinterServiceImpl class:
public class PrinterServiceImpl implements PrinterService {
#Autowired
private List<AbstractPrinter> abstractPrinters;
private static final Map<String,AbstractPrinter> myPrinters = new HashMap<>();
#PostConstruct
public void loadPrinters() {
for(AbstractPrinter printer : abstractPrinters) {
myPrinters.put(printer.getPrinterType(), printer);
}
}
//Add your other Autowired dependencies here
#Override
public void myServiceMethod(String input){//get input from controller
AbstractPrinter abstractPrinter= myPrinters.get(input);
abstractPrinter.print();//dynamically calls print() depending on input
}
}
HPPrinter class:
#Component
public class HPPrinter implements AbstractPrinter {
#Override
public String getPrinterType() {
return "HP";
}
#Override
public void print() {
// Your print code
}
}
FilePrinter class:
#Component
public class FilePrinter implements AbstractPrinter {
#Override
public String getPrinterType() {
return "FILE";
}
#Override
public void print() {
// Your print code
}
}
You could create a dedicated PrinterService instance per AbstractPrinter concrete class. For example you could achieve this using Spring configuration which follow the factory pattern:
#Configuration
public class PrinterServiceConfiguration {
#Autowired
private HPPrinter hpPrinter;
#Autowired
private FilePrinter filePrinter;
#Bean
public PrinterService hpPrinterService() {
return new PrinterService(hpPrinter);
}
#Bean
public PrinterService filePrinterService() {
return new PrinterService(filePrinter);
}
public PrinterService findPrinterService(PrinterType type){
if (type == HP)
return hpPrinterService();
....
}
}
Then in your controller, inject PrinterServiceConfiguration then call findPrinterService with the right printer type.
Don't forget to add PrinterServiceConfiguration at your configuration #Import.
If the list of printer is dynamic you could switch to prototype bean :
#Configuration
public class PrinterServiceConfiguration {
#Autowired
private List<AbstractPrinter> printers;
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public PrinterService createPrinterService(PrinterType type){
return new PrinterService(findPrinterByType(type));
}
private Printer findPrinterByType(PrinterType type) {
// iterate over printers then return the printer that match type
// throw exception if no printer found
}
}
While overrding a Javaconfig Bean by extending the original #Configuration class, I would like to add a #DependsOn for the new Bean definition.
However, this depends-on seems not to be taken in account. here is a TestCase reproducing my issues:
public class SpringTest {
#Test
public void testDependsOnTakenInAccount() {
AnnotationConfigApplicationContext ctx2 = new AnnotationConfigApplicationContext(AConfig.class, CConfig.class);
Assert.assertEquals("overriden", ctx2.getBean("bean"));
}
#Configuration
public static class AConfig {
#Bean
public Object bean() {
return "not overriden";
}
}
#Configuration
public static class CConfig extends AConfig {
protected boolean isInitialized = false;
#Bean
public Void doInit() {
isInitialized = true;
return null;
}
#Bean
#DependsOn("doInit")
public Object bean() {
if (!isInitialized) {
throw new RuntimeException("Not initialized");
}
return "overriden";
}
}
}
Is this an expected behavior? If yes, how can I add dependency while overriding a bean?
For me seems like a bug.
When overriding a #Bean factory method in a Configuration class, the parent BeanDefinition wins and get registered on the BeanFactory overriding the child one.
So you cannot configure the bean with annotaions (because it will be overriden).
The following Test result on
expected:<[doInit]> but was:<[otherBean]>
#RunWith(JUnit4ClassRunner.class)
public class DependOnTest {
#Test
public void testBeanDefinitionOverriding() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
BeanDefinition bd = ctx.getBeanDefinition("bean");
Assert.assertEquals("doInit", bd.getDependsOn()[0]);
}
#Configuration
public static class ParentConfig {
#Bean
#DependsOn("otherBean")
public String bean() {
return "not overriden";
}
#Bean
public String otherBean() {
return "otherBean";
}
}
#Configuration
public static class Config extends ParentConfig {
#Bean
public String doInit() {
return "doInit";
}
#Bean
#DependsOn("doInit")
public String bean() {
return "overriding";
}
}
}
I think that problem start on ConfigurationClassParser:
// recursively process the configuration class and its superclass hierarchy
do {
metadata = doProcessConfigurationClass(configClass, metadata);
}
while (metadata != null);
That result on overriden method added to CongurationClass.beanMethods
It could be fixed checking if the beanMethod was already added from a superclass in ConfigurationClass.addBeanMethod()
public void addBeanMethod(BeanMethod method) {
// Check if already added a bean method from superclass
for (BeanMethod beanMethod : beanMethods) {
if (beanMethod.getMetadata().getMethodName().equals(method.getMetadata().getMethodName()) &&
!(beanMethod.getMetadata().getDeclaringClassName()).equals(method.getMetadata().getDeclaringClassName()))
// log and return.
return;
}
this.beanMethods.add(method);
}
As pointed out by Jose Luis Martin, this has been confirmed as a bug by Spring team.
I've workarounded it with:
#DependsOn("doInit")
#Bean
public Void notOverridingBean() {
return null;
}
#Bean
public Object bean(Object notOverridingBean) {
return "overriden";
}
an alternative is to override the bean in another #Configuration class.
I have a Spring class.
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager {
GenericXmlApplicationContext context;
#PersistenceContext
private EntityManager em;
public DatabaseManager(GenericXmlApplicationContext context) {
this.context = context;
}
....
} //end of class DatabaseManager
I have SpringUtil class
public class SpringUtil {
public static GenericXmlApplicationContext loadSpringContext(String springXmlFile) {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
context.load(springXmlFile);
context.refresh();
return context;
} //end of loadSpringContext()
} //end of class SpringUtil
Now in main i am using some thing like
public class Regulator {
public static void main( String[] args ) {
Test test = new Test;
test.start();
} //end of main()
} //end of class Regulator
Here is test class
public class Test {
public void start() {
String springXmlFile = "classpath:spring/plcb-app-context-xml.xml";
GenericXmlApplicationContext context = SpringUtil.loadSpringContext(springXmlFile);
} //end of reportStudent()
} //end of class Test
But i am getting error that
Could not instantiate bean class [...DatabaseManager]: No default constructor
found; nested exception is java.lang.NoSuchMethodException:
...DatabaseManager.<init>()
I want that when DatabaseManager class created then spring context taht i am creating using SpringUtil.loadSpringContext(springXmlFile) must pass to it. How can i do it ?
Thanks
Edit
-------------------
public void switchDataSource(DatabaseType databaseType) {
DriverManagerDataSource dataSource = null;
if (databaseType == DatabaseType.LEGACY) {
dataSource = (DriverManagerDataSource)context.getBean("myLegacyDataSource");
} else if (databaseType == DatabaseType.LS360) {
dataSource = (DriverManagerDataSource)context.getBean("myLs360DataSource");
}
LocalContainerEntityManagerFactoryBean emf = context.getBean("myEmf", LocalContainerEntityManagerFactoryBean.class);
emf.setDataSource(dataSource);
}
#SuppressWarnings("unchecked")
#Transactional(readOnly=true)
public List<Object> getResultList(String query, Class mappingClass) throws Exception {
Query emQuery = em.createNativeQuery(query, mappingClass);
return emQuery.getResultList();
} //end of findTraineeFromLegacy()
Actually i have these two methods in my DatabaseManager class. I am setting context so i can get bean from the context in switchDataSource() method.
One thing that i can do is remove instance filed and change the method to
public void switchDataSource(DatabaseType databaseType, GenericXmlApplicationContext context) {
....
}
This is why i am doing this ?
Thanks
Have a no-arg constructor for DatabaseManager.
Implements ApplicationContextAware in DatabaseManager. Spring will know this bean needs to be notified of the application context:
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager implements ApplicationContextAware {
private ApplicationContext context;
public DatabaseManager() {...}
#Override
public void setApplicationContext(ApplicationContext appContext) {
this.context = appContext;
}
} //end of class DatabaseManager
however, double think if you really need that injected. In most case you are doing something wrong.
Update:
For your requirement in your update, which you want your DB Manager to switch datasource base on input type, although it doesn't seems very normal doing such thing, you can simply have your DB Manager injected with a Map and do whatever you want, instead of injecting the app context.
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager implements ApplicationContextAware {
#Resource("&emfBean")
private LocalContainerEntityManagerFactoryBean emfBean;
#Resource("dbManagerDsMap")
private Map<DatabaseType, Datasource> dsMapping;
public DatabaseManager() {...}
public void switchDataSource(DatabaseType databaseType) {
emfBean.setDatasource(dsMapping.get(databaseType));
}
} //end of class DatabaseManager
However I strongly suggest you not doing such thing. Consider having individual entityManagerFactory for each DB you are connecting to, and use the correct emf to connect to DB, instead doing this weird switching logic. I believe it is not supposed to be changed after your application start.
I am trying to do a one time initialization of my webapp. I need a singleton for the ApplicationListener class, so I set the scope to Singleton, but it is creating multiple instances. This BootStrapper is not defined in any other xml config files.
I know that the default scope is singleton, but had to add #Scope("singleton") because it was not a singleton. Even with this annotation, it still creates multiple instances.
Here is my ApplicationListener.
#Component
#Scope("singleton")
public class BootStrapper implements ApplicationListener<ContextRefreshedEvent> {
Am I missing anything?
To have a callback that is invoked after the bean is initialized, use #PostConstruct.
#Component
public class BootStrapper() {
#PostConstruct
public void doSomething() {
System.out.println("I am initalized!");
}
}
Try like this:
#Configuration
public class TestService
{
private Properties properties;
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public Properties getAppProperties()
{
try
{
if (properties == null)
{
properties = ServiceUtils.loadProperties();
}
return properties;
}
catch (Exception e)
{
LOGGER.logCaughtException("Exception Occured while loading App Properties.", e);
}
}
}