I am trying to create a FactoryBean to instantiate an object. I want this object to be then treated as singleton by Spring.
My factory bean implementation itself will not ensure that the object it returns on getObject will always be singleton. It will simply return a new object every time getObject() is called.
As per the documentation for the isSingleton method, "Is the object managed by this factory a singleton? That is, will getObject() always return the same object (a reference that can be cached)? If a FactoryBean indicates to hold a singleton object, the object returned from getObject() might get cached by the owning BeanFactory. Hence, do not return true unless the FactoryBean always exposes the same reference."
If I want to enforce the resulting object from FactoryBean to be singleton, should I set isSingleton to be true ? Wouldn't it conflict with the definition which says "Hence, do not return true unless the FactoryBean always exposes the same reference" ?
Sample FactoryBean
public class SampleFactoryBean implements FactoryBean<SampleObject> {
#Override
public SampleObject getObject() throws Exception {
return new SampleObject();
}
#Override
public Class<?> getObjectType() {
return SampleObject.class;
}
#Override
public boolean isSingleton() {
return true;
}
}
Related
I have defined a conditional bean :
#Conditional(CustomClass.class)
public class CustomClass implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
#Bean(name = "bean1")
public DataSource dummy() {
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource;
}
}
In another class, I am injecting bean1 as follows:
public anotherClass(#Qualifier("bean1") DataSource dataSource) {
}
But, when the conditional class match method returns, false,
i get:
"No qualifying bean of type 'javax.sql.DataSource' available"
If match returns true, there is no error.
Isn't the purpose of conditional beans to load beans conditionally ?
Yes, that is the purpose of conditional bean loading.
You get an error when the matches method returns false because there is no longer a bean available for autowiring. You should define a DataSource for that case as well (so a bean of type DataSource is available event if your current matches returns false).
You could do that in another #Configuration that extends Condition and for which the matches method would return the opposite value of your currently defined matches.
I am newbie to spring and I am trying to modify my app to implement spring framework.
My request is to create a new bean for every new request and then refer that bean later in the code, for setting the values to it from a singleton bean.
I am trying to declare the bean as prototype and refer that bean in my singleton bean using lookup method.
But my problem was when trying to get the created prototype bean later for setting the values, I see its creating new bean again when getting the bean.
#Component
public class PersonTransaction {
#Autowired
PersonContext btContext;
#Autowired
PersonMapper personMapper;
public void setPersonMapper(PersonViewMapper personMapper) {
this.personMapper = personMapper;
}
public PersonBTContext createContext() throws ContextException {
return btContext = getInitializedPersonBTInstance();
}
private PersonBTContext getContext() throws ContextException{
return this.btContext;
}
public void populateView(UserProfileBean userProfile) throws ContextException {
personMapper.populateView(userProfile,getContext());
}
#Lookup(value="personContext")
public PersonBTContext getInitializedPersonBTInstance(){
return null;
}
}
below is my prototype class
#Component("personContext")
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PersonContext extends ReporterAdapterContext {
private List<Person> persons = null;
private Person person = null;
private List<String> attributes = null;
private boolean multiplePersons = false;
private boolean attributeSelected = false;
public boolean isMultiple() {
return multiplePersons;
}
public boolean isAttributeSelected() {
return attributeSelected;
}
private void setAttributeSelected(boolean attributeSelected) {
this.attributeSelected = attributeSelected;
}
// remaining getters/setters
}
when i call createContext from singleton PersonTransaction class, it should create new prototype bean and how can get the created prototype bean later by calling getContext() method (what i am doing by this.btContext is returning new bean again, I guess !!)..
Need help in getting the created prototype bean later for setting the values.
appreciate ur help..
You want to create a request scoped bean, not a prototype scoped bean. Take a look at Quick Guide to Spring Bean Scopes which describes different bean scopes, including the request scope:
#Bean
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public PersonContext personContext() {
return new PersonContext();
}
This should simplify your logic as long as you can discard the bean after the request is processed.
In a spring based project I am working on, there's a layer of functionality for calling web service. For each web service operation, a method is created with almost same code but with some different, operation specific, information(e.g. service name, operation name, namespaces, etc).
I am replacing this layer with interfaces and annotated methods. For example, below code is provided for operation "fetchBar" of web service("foo").
package a.b.c.webservices;
#WebService(service="foo", namespace="...")
public interface FooWebService {
#WebServiceOperation(operation="fetchBar")
BarRespons fetchBar(BarRequest request) throws WebServiceException;
}
Now I want, with some mechanism, spring allow me to create dynamic proxy beans from some specified package(s) and I can use following code to call web service.
package a.b.c.business;
import a.b.c.webservices.FooWebService;
public class FooBusiness {
#Autowired
FooWebService fooWebService;
public Bar getBar() {
Bar bar = null;
BarRequest request;
//create request
BarResponse response = fooWebService.fetchBar(request);
//extrac bar from response
return bar;
}
}
To achieve this I have created dynamic beans instances using java.lang.reflect.Proxy.newProxyInstance by providing it implementation of InvocationHandler. But Autowiring doesn't work in provided implementation of invocationHandler and in its further dependencies.
I tried following ways to achieve this.
Implemented BeanFactoryPostProcessor.postProcessBeanFactory and registered beans using ConfigurableListableBeanFactory.registerSingleton method.
Implemented ImportBeanDefinitionRegistrar.registerBeanDefinitions and tried to use BeanDefinitionRegistry.registerBeanDefinition but I am confused how to provide correct Bean definition that supports Autowiring.
Can any one tell me what is missing? Please guide me if I am not going in right direction.
Here's how I implemented all the functionality that creates beans of 'WebService' annotated interfaces and also supports Autowiring inside proxy implementation. (package declaration and import statements are omitted in below code)
First of all I created WebService and WebServiceOperation annotation.
WebService Annotation
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface WebService {
String service();
String namespace();
}
WebService Operation Annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface WebServiceOperation {
String operation();
}
Next step is to scan all WebService annotated interfaces from specified packages. Spring provides ClassPathScanningCandidateComponentProvider for package scanning but it does not detect interfaces. Please see this question and it's answer for more details. So I extended ClassPathScanningCandidateComponentProvider and overrode isCandidateComponent method.
ClassPathScanner
public class ClassPathScanner extends ClassPathScanningCandidateComponentProvider {
public ClassPathScanner(final boolean useDefaultFilters) {
super(useDefaultFilters);
}
#Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isIndependent();
}
}
At this point I created EnableWebServices annotation to enable web services and to provide web service packages that contain WebService annotated interfaces.
EnableWebServices Annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import({
WebServiceProxyConfig.class,
WebServiceProxyBeansRegistrar.class
})
public #interface EnableWebServices {
#AliasFor("basePackages")
String[] value() default {};
#AliasFor("value")
String[] basePackages() default {};
}
This annotation can be applied to some Configuration annotated class with packages to scan interfaces, as below.
#EnableWebServices({
"a.b.c.webservices",
"x.y.z.webservices"
})
It's time to think about dynamic proxy creation that will invoke actual web service from information given in WebService and WebServiceOperation annotations. Java provides a mechanism to create dynamic proxy which requires to provide implementation of InvocationHandler interface and provide logic in its invoke method. I named this implementaiton as WebServiceProxy
Suppose a bean of type 'TheWebServiceCaller' contains all nasty logic to call a web service. I just have inject it and to invoke it's call method with a TheWebServiceInfo (extracted from WebService and WebServiceOperation annotations) and request object.
TheWebServiceInfo(Suppose all fields have getters and setters)
public class TheWebServiceInfo {
private String service;
private String namespace;
private String operation;
}
WebServiceProxy
public class WebServiceProxy implements InvocationHandler {
#Autowired
private TheWebServiceCaller caller;
#Override
public Object invoke(Object target, Method method, Object[] args) throws Exception {
Object request = (null != args && args.length > 0) ? args[0] : null;
WebService webService = method.getDeclaringClass().getAnnotation(WebService.class);
WebServiceOperation webServiceOperation = method.getAnnotation(WebServiceOperation.class);
TheWebServiceInfo theInfo = createTheWebServiceInfo(webService, webServiceOperation);
return caller.call(theInfo, request);
}
private TheWebServiceInfo createTheWebServiceInfo(WebService webService, WebServiceOperation webServiceOperation) {
TheWebServiceInfo theInfo = new TheWebServiceInfo();
theInfo.setService(webService.service());
theInfo.setNamespace(webService.namespace());
theInfo.setOperation(webServiceOperation.operation());
return theInfo;
}
}
Implementaion of InvocationHandler is passed to Proxy.newProxyInstance (along with some other information) to create proxy objects. I need separat proxy objectes for each WebService annotated interface. I will now create a factory to proxy instances creation and name is as 'WebServiceProxyBeanFactory'. Instances created by this factory will become beans for corresponding WebService annotated interfaces.
A bit later, I will expose 'WebServiceProxy' and WebServiceProxyBeanFactory as beans. In 'WebServiceProxyBeanFactory', I will inject WebServiceProxy and used it. Please note that createWebServiceProxyBean uses generics. This is important.
WebServiceProxyBeanFactory
public class WebServiceProxyBeanFactory {
#Autowired
WebServiceProxy webServiceProxy;
#SuppressWarnings("unchecked")
public <WS> WS createWebServiceProxyBean(ClassLoader classLoader, Class<WS> clazz) {
return (WS) Proxy.newProxyInstance(classLoader, new Class[] {clazz}, webServiceProxy);
}
}
If you remember, earlier I have imported WebServiceProxyConfig in EnableWebServices annotations. WebServiceProxyConfig is used to expose WebServiceProxy and WebServiceProxyBeanFactory as beans.
WebServiceProxyConfig
#Configuration
public class WebServiceProxyConfig {
#Bean
public WebServiceProxy webServiceProxy() {
return new WebServiceProxy();
}
#Bean(name = "webServiceProxyBeanFactory")
public WebServiceProxyBeanFactory webServiceProxyBeanFactory() {
return new WebServiceProxyBeanFactory();
}
}
Now everything is in place. it's time to write a hook to start scanning Web service packages and register dynamic proxies as beans. I will provide implementation of ImportBeanDefinitionRegistrar.
WebServiceProxyBeansRegistrar
#Configuration
public class WebServiceProxyBeansRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {
private ClassPathScanner classpathScanner;
private ClassLoader classLoader;
public WebServiceProxyBeansRegistrar() {
classpathScanner = new ClassPathScanner(false);
classpathScanner.addIncludeFilter(new AnnotationTypeFilter(WebService.class));
}
#Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
#Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
String[] basePackages = getBasePackages(importingClassMetadata);
if (ArrayUtils.isNotEmpty(basePackages)) {
for (String basePackage : basePackages) {
createWebServicProxies(basePackage, registry);
}
}
}
private String[] getBasePackages(AnnotationMetadata importingClassMetadata) {
String[] basePackages = null;
MultiValueMap<String, Object> allAnnotationAttributes =
importingClassMetadata.getAllAnnotationAttributes(EnableWebServices.class.getName());
if (MapUtils.isNotEmpty(allAnnotationAttributes)) {
basePackages = (String[]) allAnnotationAttributes.getFirst("basePackages");
}
return basePackages;
}
private void createWebServicProxies(String basePackage, BeanDefinitionRegistry registry) {
try {
for (BeanDefinition beanDefinition : classpathScanner.findCandidateComponents(basePackage)) {
Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
WebService webService = clazz.getAnnotation(WebService.class);
String beanName = StringUtils.isNotEmpty(webService.bean())
? webService.bean() : ClassUtils.getShortNameAsProperty(clazz);
GenericBeanDefinition proxyBeanDefinition = new GenericBeanDefinition();
proxyBeanDefinition.setBeanClass(clazz);
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addGenericArgumentValue(classLoader);
args.addGenericArgumentValue(clazz);
proxyBeanDefinition.setConstructorArgumentValues(args);
proxyBeanDefinition.setFactoryBeanName("webServiceProxyBeanFactory");
proxyBeanDefinition.setFactoryMethodName("createWebServiceProxyBean");
registry.registerBeanDefinition(beanName, proxyBeanDefinition);
}
} catch (Exception e) {
System.out.println("Exception while createing proxy");
e.printStackTrace();
}
}
}
In this class, I extracted all packages provided in EnableWebServices annotation. for each extracted package, I used ClassPathScanner to scan. (Here logic can be refined to filter only WebService annotated interfaces). For each detected interface, I have registered a bean definitions. Please note I have used webServiceProxyBeanFactory and called its createWebServiceProxyBean with classLoader and type of interface. This factory method, when invoked by spring later, will return bean of same type as that of interface, so bean with correct type is registered. This bean can be injected anywhere with interface type. Moreover, WebServiceProxy can inject and use any other bean. So autowiring will also work as expected.
Is your InvocationHandler a bean? You should create it as a bean, not just a simple object to get Autowired working
I was thinking about the same problem but in a slightly more lightweight context. I don't need to load dynamicaly all the webservice clients. So instead I used a FactoryBean and within this factory bean I constructed the dynamic proxy. Here is one example where Autowiring of the service works:
public class CurrencyServiceWithDynamicProxy extends AbstractFactoryBean<CurrencyService> {
ServiceClientConfiguration clientConfiguration;
Object proxy;
#Autowired
public CurrencySyncFactoryDynamicProxy(ServiceClientConfigurationProvider serviceClientConfigurationProvider) {
this.clientConfiguration = serviceClientConfigurationProvider.createClientConfig("currency");
proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { getObjectType() }, new MyInvocationHandler());
}
#Override
public Class<CurrencySync> getObjectType() {
// TODO Auto-generated method stub
return CurrencyService.class;
}
#Override
public CurrencySync createInstance() throws Exception {
// do some creational logic
return (CurrencySync)proxy;
}
public CurrencySync createService() {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(getObjectType());
factory.getFeatures().add(som features);
return getObjectType().cast(factory.create());
}
}
With respect of the accepted answer this factory example can easily be extended into a more dynamic version.
What is the use of afterPropertiesSet() life cycle method of spring bean? For what kind of activities it can be used in real world applications?
As the method name afterPropertiesSet() hints, it will be invoked by the BeanFactory after all the properties have been provided, and can be used to initialize a bean or making sure is in a valid state.
Let's say I have a bean with a String property that must not be null or empty.
public class MyBean implements InitializingBean {
private String myString;
public void afterPropertiesSet() {
if(myString == null || myString.isEmpty()){
throw IllegalStateException("myString must have text.");
}
}
public void setMyString(String myString) {
this.myString = myString;
}
}
Of course this is only a very simple code to demonstrate how can be used in real applications.
HTH,
Jose Luis
I'm trying the get Bean class name without initialize the bean.
I need to know the class , I could get the bean from applicationContext and to check the class name from the bean instance, But I want to know the class with out actually creating/init the bean..
Is it possible ?
Object bean = applicationContext.getBean("beanName");
bean.getClass();
You can't do this after creating the ApplicationContext. Most ApplicationContext implementations will refresh() themselves and force instantiation of the beans.
What you can do is create a BeanFactoryPostProcessor in which you get the target bean definition and check the bean class.
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String className = beanFactory.getBeanDefinition("").getBeanClassName();
}
But note, as the javadoc for getBeanClassName() states
Hence, do not consider this to be the definitive bean type at runtime
but rather only use it for parsing purposes at the individual bean
definition level.
So use it with a grain of salt.
If you give us more details as to what you are trying to accomplish, there might be alternatives.
The code provided by Sotirious will not work for the beans that have parent beans and for the beans that are defined using Java Config or using #Component annotation (and similar annotatios like #Service, #Repository, #Component).
Just an extension which checks if it is AnnotatedBeanDefinition or if the bean has a parent:
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
String beanClassName = getBeanClassName(beanDefinitionName, beanFactory);
}
}
private String getBeanClassName(String beanName, ConfigurableListableBeanFactory beanFactory) {
String beanClassName;
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (beanDefinition instanceof AnnotatedBeanDefinition) {
AnnotationMetadata metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
beanClassName = metadata.getClassName();
} else {
beanClassName = beanDefinition.getBeanClassName();
while (beanClassName == null) {
BeanDefinition parentBeanDefinition = beanFactory.getBeanDefinition(beanDefinition.getParentName());
beanClassName = parentBeanDefinition.getBeanClassName();
beanDefinition = parentBeanDefinition;
}
}
return beanClassName;
}
Note, this approach will not work in the case factory method is used. As Java Doc says:
Also, this may just be the class that a factory method is called on, or it may even be empty in case of a factory bean reference that a method is called on. Hence, do not consider this to be the definitive bean type at runtime but rather only use it for parsing purposes at the individual bean definition level.