Using Hibernate 4's Integrator pattern and Spring's dependency injection - java

I'm used to using Spring to do my dependency injection like so:
<context:component-scan base-package="org.emmerich.myapp" />
and then annotating my dependent classes with Autowired like so:
public class DependentClass {
#Autowired
private Dependency dependency;
}
However, with the changes in Hibernate 4.0, we're now advised to use the new Integrator interface for service discovery. This includes adding event listeners for triggers such as postUpdate, postDelete etc.
Unfortunately, this doesn't play nicely with dependency injection through annotated dependencies. I have the following setup:
An integrator I have defined to add my listener to the ServiceFactory. This is referenced in the file META-INF/services/org.hibernate.integrator.spi.Integrator.
public class MyIntegrator implements Integrator {
private MyListener listener;
public MyIntegrator() {
listener = new MyListener();
}
#Override
public void integrate(Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
final EventListenerRegistry eventRegistry =
serviceRegistry.getService(EventListenerRegistry.class);
eventRegistry.prependListeners(EventType.POST_COMMIT_INSERT, listener);
}
I also have defined the class MyListener, which looks like your typical event listener.
#Component
public class MyListener implements PostInsertEventListener {
#Autowired
private Dependent dependent;
public void onPostInsert(PostInsertEvent event) {
// dependent == null
}
}
Unforunately, as shown by the comment, this doesn't work. I guess it's because I'm instantiating MyListener inside MyIntegrator, it doesn't pick up the component and doesn't autowire components. However, if I try this:
#Component
public class MyIntegrator {
#Autowired
private MyListener listener;
...
}
Then the listener isn't autowired.
Firstly, it feels wrong whilst using Spring to have to do new MyListener(). I expect to be able to define that as an autowired dependency and have Spring create a singleton for me. My question is this:
What's the best approach to using dependency injection with the new Integrator interface? The Integrators are used to build a SessionFactory, and so when they're asked to integrate themselves I guess there isn't an application context available. Because of that, any beans I require in the Integrator need to be created the "old fashioned" way and won't receive the autowiring on them.
I'm quite new to the world of Spring, would you say this is something that I should expect to see? I understand that I'm in a different scope of the application when I'm in the SessionFactory, but is there a way to obtain a reference to the bean and enable autowire even though I'm creating it via new?
The solution I came up with used ApplicationContextAware. It meant that MyListener received a reference to the ApplicationContext whenever the context was available, and I referenced the beans from the context on method calls, rather than on bean construction. Creating a bean with new doesn't limit this, so Spring still gives me the application context:
#Component
public class MyListener implements PostInsertEventListener, ApplicationContextAware {
private static ApplicationContext context;
public void onPostInsert(PostInsertEvent event) {
// getDependent() == correct!
}
public void setApplicationContext(ApplicationContext context) throws BeanException {
this.context = context;
}
public Dependent getDependent() {
return context.getBean(Dependent.class);
}
}
Is there a better way?

As stated in the comment i went another way of integrating Spring managed HibernateEventListeners. Here's the code:
The identifier interface for Spring managed Hibernate event listeners:
public interface HibernateEventListener { }
The HibernateIntegrator:
#Service
public class HibernateSpringIntegrator {
private static final Logger log = LoggerFactory.getLogger(HibernateSpringIntegrator.class);
#Autowired
private HibernateEntityManagerFactory entityManagerFactory;
#Autowired
private HibernateSpringIntegratorRegistry hibernateSpringIntegratorRegistry;
#PostConstruct
public void registerListeners() {
log.debug("Registering Spring managed HibernateEventListeners");
EventListenerRegistry listenerRegistry = ((SessionFactoryImpl) entityManagerFactory
.getSessionFactory()).getServiceRegistry().getService(
EventListenerRegistry.class);
List<HibernateEventListener> eventListeners = hibernateSpringIntegratorRegistry
.getHibernateEventListeners();
for (HibernateEventListener hel : eventListeners) {
log.debug("Registering: {}", hel.getClass());
if (PreInsertEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.PRE_INSERT,
(PreInsertEventListener) hel);
}
if (PreUpdateEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.PRE_UPDATE,
(PreUpdateEventListener) hel);
}
if (PreDeleteEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.PRE_DELETE,
(PreDeleteEventListener) hel);
}
if (PostInsertEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.POST_INSERT,
(PostInsertEventListener) hel);
}
if (PostUpdateEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.POST_UPDATE,
(PostUpdateEventListener) hel);
}
if (PostDeleteEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.POST_DELETE,
(PostDeleteEventListener) hel);
}
// Currently we do not need other types of eventListeners. Else this method needs to be extended.
}
}
}
The "Registry":
#Component
public class HibernateSpringIntegratorRegistry {
#Autowired(required = false)
private List<HibernateEventListener> hibernateEventListeners;
public List<HibernateEventListener> getHibernateEventListeners() {
if (hibernateEventListeners == null) {
return Collections.emptyList();
}
return hibernateEventListeners;
}
}
And here's an example implementation:
#Component
public class MailGenerationEventListener implements HibernateEventListener,
PostDeleteEventListener, PostInsertEventListener, PostUpdateEventListener {
#Override
public void onPostDelete(PostDeleteEvent event) {
Class<?> entityClass = event.getEntity().getClass();
...
}
#Override
public void onPostInsert(PostInsertEvent event) {
Class<?> entityClass = event.getEntity().getClass();
...
}
#Override
public void onPostUpdate(PostUpdateEvent event) {
Class<?> entityClass = event.getEntity().getClass();
...
}
}

During an upgrade from hibernate 3.6 to 4.2, we needed to have a custom validator that uses spring-managed beans by doing the following configuration:
<!-- Make our validators use DI if necessary -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- other props -->
<property name="hibernateProperties">
<map>
<entry key="javax.persistence.validation.factory" value-ref="validator" />
</map>
</property>
</bean>

Related

spring dependency injection is not working in a class annotated with #WebListener

I tried constructor based Dependency injection in TestAppListener class which implements ServletContextListener.
I got this error
Exception sending context initialized event to listener instance of class [com.example.listener.TestAppListener].
I searched stack overflow but I couldn't find any solution for this scenario. Please any one help me in sort out this. I placed Implementation classes in META-INF.services folder also. My understanding is there is some problem in constructor dependency injection but this way of DI is need for my situation, because in real time I want to create datasource connection inside startup method.
Find all my classes below which i'm using,
#WebListener
public class TestAppListener implements ServletContextListener {
private static TestDao dao;
public TestAppListener(TestDao dao){
this.dao = dao;
}
public TestAppListener(){}
#Override
public void contextInitialized(ServletContextEvent sce) {
dao = ServiceLoader.load(TestDao.class).iterator().next();
dao.startUp();
System.out.println("Context initialized method called");
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Context destroyed method called");
dao.shutDown();
}
}
public interface TestDao {
void startUp();
void shutDown();
}
public class TestDaoImpl implements TestDao {
#Override
public void startUp() {
System.out.println("Database is initialized");
}
#Override
public void shutDown() {
System.out.println("Database is initialized");
}
}
#Configuration
public class SpringConfig {
public SpringConfig() {
}
#Bean
public ServletListenerRegistrationBean<ServletContextListener> listenerRegistrationBean() {
ServletListenerRegistrationBean<ServletContextListener> bean = new ServletListenerRegistrationBean<>();
bean.setListener(new TestAppListener());
return bean;
}
}
The Servlet #WebListeners are handled by Servlet containers(tomcat/Jetty) when the container is starting. So they know nothing about Spring.
For more details, see discussion in this issue.
A workaround solution is replace the #WebListener with Spring's #Component, thus you can inject Spring beans(declare your Dao as spring beans) into the listeners directly.
BTW, you have to add #ServletComponentScan on your Spring Boot application class to activate it.
I created an example project to demo #WebServlet, #WebFilter and #WebListener.
Change private static TestDao dao to private final TestDao dao. Spring doesn't allow statics to be used as targets for injection. Also, your TestDaoImpl class needs to be a component or a bean defined in a Spring configuration file.

Is it possible to set name as a variable in springboot, like #Bean(name = "abc")? can we pass value of abc dynamically? [duplicate]

Does anyone know if I should be able to use property placeholder as an expression in a Qualifier? I can't seem to get this working.
I am using spring 3.0.4.
#Controller
public class MyController {
#Autowired
#Qualifier("${service.class}")
Service service;
}
#Service
#Qualifier("ServiceA")
ServiceA implements Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
#Service
#Qualifier("ServiceB")
ServiceB implements Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
XML:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:/etc/config.properties"/>
</bean>
config.properties:
config.properties
service.class=serviceB
This works. You can leave off the service names if you just use the default spring bean name. serviceA vs ServiceA, etc.
#Controller
class MyController {
#Autowired(required=false)
#Qualifier("Service")
Service service;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("app-ctx.xml", MyController.class);
for(String s:context.getBeanDefinitionNames()){
System.out.println(s);
for(String t:context.getAliases(s)){
System.out.println("\t" + t);
}
}
context.getBean(MyController.class).service.print();
}
}
public interface Service {
void print();
}
#Service(value="ServiceA")
public class ServiceA implements example.Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
#Service(value="ServiceB")
public class ServiceB implements example.Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
XML:
<beans>
<alias name="${service.class}" alias="Service"/>
<context:property-placeholder location="example/app.properties"/>
<context:component-scan base-package="example"/>
<beans>
Props:
service.class=ServiceB
This solution works without XML and with properties file.
Yours classes improved:
MyController.java:
#Controller
public class MyController {
#Autowired
public MyController(#Qualifier("MyServiceAlias") MyService myService) {
myService.print();
}
}
ServiceA.java:
#Service("serviceA")
public class ServiceA implements MyService {
#Override
public void print() {
System.out.println("printing ServiceA.print()");
}
}
ServiceB.java:
#Service("serviceB")
public class ServiceB implements MyService {
#Override
public void print() {
System.out.println("printing ServiceB.print()");
}
}
application.properties (here you can change which class will be loaded):
service.class=serviceA
And important configuration file AppConfig.java:
#Configuration
public class AppConfig {
#Autowired
private ApplicationContext context;
#Bean
public MyService MyServiceAlias(#Value("${service.class}") String qualifier) {
return (MyService) context.getBean(qualifier);
}
}
Additional explanations:
Use #Qualifier only for field which will be autowired. For services, to specify bean name, use #Service.
If you want standard bean name you don't need to use #Service with specyify name. For example, standard bean name for ServiceA is serviceA (not ServiceA - see big first letter), so #Service("serviceA") redundant (#Service is enough).
I based AppConfig on this answer: Spring Bean Alias in JavaConfig.
This solution is better than this Spring Qualifier and property placeholder, because you don't need XML.
Tested on Spring Boot 1.5.7.
I would venture to guess the answer is no, just based on the write ups in a few javadoc pages. For example, see the docs for #Value:
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/annotation/Value.html
Notice they make special mention of using expressions in the annotation. For comparison, the docs for #Qualifier:
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/annotation/Qualifier.html
Which make no mention of expressions. Obviously not a definitive answer (but spring is generally very good on documentation). Also, if expressions were supported in the #Qualifier annotation I would expect they work the same way as the #Value annotation (just based on spring being a very consistent framework).
Spring 3.1 has the new profile bean feature, which seems like it can accomplish something like what you're trying to do. Here's a write up for that:
http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/
As a workarround, you can set the desired Spring service implementation based on its name in your config.properties.
#Controller
public class MyController {
//add a String which will hold the name of the service to implement
#Value("${service.class}")
private String serviceToImplement;
Service service;
// now autowire spring service bean based on int name using setter
#Autowired
public void setService(ApplicationContext context) {
service = (Service) context.getBean(serviceToImplement);
}
}
#Service
#Qualifier("ServiceA")
ServiceA implements Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
#Service
#Qualifier("ServiceB")
ServiceB implements Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
config.properties
service.class=serviceB
Maybe give this a whirl:
#Controller
public class MyController {
private String serviceId;
#Value("${serviceId}")
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
#Autowired
#Qualifier(serviceId)
Service service;
}

Inject spring bean dynamically

In a java-spring web-app I would like to be able to dynamically inject beans.
For example I have an interface with 2 different implementations:
In my app I'm using some properties file to configure injections:
#Determines the interface type the app uses. Possible values: implA, implB
myinterface.type=implA
My injections actually loaded conditionally relaying on the properties values in the properties file. For example in this case myinterface.type=implA wherever I inject MyInterface the implementation that will be injected will be ImplA (I accomplished that by extending the Conditional annotation).
I would like that during runtime - once the properties are changed the following will happen (without server restart):
The right implementation will be injected. For example when setting myinterface.type=implB ImplB will be injected where-ever MyInterface is used
Spring Environment should be refreshed with the new values and re-injected as well to beans.
I thought of refreshing my context but that creates problems.
I thought maybe to use setters for injection and re-use those setters once properties are re-configured. Is there a working practice for such a requirement?
Any ideas?
UPDATE
As some suggested I can use a factory/registry that holds both implementations (ImplA and ImplB) and returns the right one by querying the relevant property.
If I do that I still have the second challenge - the environment. for example if my registry looks like this:
#Service
public class MyRegistry {
private String configurationValue;
private final MyInterface implA;
private final MyInterface implB;
#Inject
public MyRegistry(Environmant env, MyInterface implA, MyInterface ImplB) {
this.implA = implA;
this.implB = implB;
this.configurationValue = env.getProperty("myinterface.type");
}
public MyInterface getMyInterface() {
switch(configurationValue) {
case "implA":
return implA;
case "implB":
return implB;
}
}
}
Once property has changed I should re-inject my environment. any suggestions for that?
I know I can query that env inside the method instead of constructor but this is a performance reduction and also I would like to think of an ider for re-injecting environment (again, maybe using a setter injection?).
I would keep this task as simple as possible. Instead of conditionally load one implementation of the MyInterface interface at startup and then fire an event that triggers dynamic loading of another implementation of the same interface, I would tackle this problem in a different way, that is much simpler to implement and maintain.
First of all, I'd just load all possible implementations:
#Component
public class MyInterfaceImplementationsHolder {
#Autowired
private Map<String, MyInterface> implementations;
public MyInterface get(String impl) {
return this.implementations.get(impl);
}
}
This bean is just a holder for all implementations of the MyInterface interface. Nothing magic here, just common Spring autowiring behavior.
Now, wherever you need to inject a specific implementation of MyInterface, you could do it with the help of an interface:
public interface MyInterfaceReloader {
void changeImplementation(MyInterface impl);
}
Then, for every class that needs to be notified of a change of the implementation, just make it implement the MyInterfaceReloader interface. For instance:
#Component
public class SomeBean implements MyInterfaceReloader {
// Do not autowire
private MyInterface myInterface;
#Override
public void changeImplementation(MyInterface impl) {
this.myInterface = impl;
}
}
Finally, you need a bean that actually changes the implementation in every bean that has MyInterface as an attribute:
#Component
public class MyInterfaceImplementationUpdater {
#Autowired
private Map<String, MyInterfaceReloader> reloaders;
#Autowired
private MyInterfaceImplementationsHolder holder;
public void updateImplementations(String implBeanName) {
this.reloaders.forEach((k, v) ->
v.changeImplementation(this.holder.get(implBeanName)));
}
}
This simply autowires all beans that implement the MyInterfaceReloader interface and updates each one of them with the new implementation, which is retrieved from the holder and passed as an argument. Again, common Spring autowiring rules.
Whenever you want the implementation to be changed, you should just invoke the updateImplementations method with the name of the bean of the new implementation, which is the lower camel case simple name of the class, i.e. myImplA or myImplB for classes MyImplA and MyImplB.
You should also invoke this method at startup, so that an initial implementation is set on every bean that implements the MyInterfaceReloader interface.
I solved a similar issue by using org.apache.commons.configuration.PropertiesConfiguration and org.springframework.beans.factory.config.ServiceLocatorFactoryBean:
Let VehicleRepairService be an interface:
public interface VehicleRepairService {
void repair();
}
and CarRepairService and TruckRepairService two classes that implements it:
public class CarRepairService implements VehicleRepairService {
#Override
public void repair() {
System.out.println("repair a car");
}
}
public class TruckRepairService implements VehicleRepairService {
#Override
public void repair() {
System.out.println("repair a truck");
}
}
I create an interface for a service factory:
public interface VehicleRepairServiceFactory {
VehicleRepairService getRepairService(String serviceType);
}
Let use Config as configuration class:
#Configuration()
#ComponentScan(basePackages = "config.test")
public class Config {
#Bean
public PropertiesConfiguration configuration(){
try {
PropertiesConfiguration configuration = new PropertiesConfiguration("example.properties");
configuration
.setReloadingStrategy(new FileChangedReloadingStrategy());
return configuration;
} catch (ConfigurationException e) {
throw new IllegalStateException(e);
}
}
#Bean
public ServiceLocatorFactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean serviceLocatorFactoryBean = new ServiceLocatorFactoryBean();
serviceLocatorFactoryBean
.setServiceLocatorInterface(VehicleRepairServiceFactory.class);
return serviceLocatorFactoryBean;
}
#Bean
public CarRepairService carRepairService() {
return new CarRepairService();
}
#Bean
public TruckRepairService truckRepairService() {
return new TruckRepairService();
}
#Bean
public SomeService someService(){
return new SomeService();
}
}
By using FileChangedReloadingStrategy your configuration be reload when you change the property file.
service=truckRepairService
#service=carRepairService
Having the configuration and the factory in your service, let you can get the appropriate service from the factory using the current value of the property.
#Service
public class SomeService {
#Autowired
private VehicleRepairServiceFactory factory;
#Autowired
private PropertiesConfiguration configuration;
public void doSomething() {
String service = configuration.getString("service");
VehicleRepairService vehicleRepairService = factory.getRepairService(service);
vehicleRepairService.repair();
}
}
Hope it helps.
If I understand you correctly then the goal is not to replace injected object instances but to use different implementations during interface method call depends on some condition at run time.
If it is so then you can try to look at the Sring TargetSource mechanism in combination with ProxyFactoryBean. The point is that proxy objects will be injected to beans that uses your interface, and all the interface method calls will be sent to TargetSource target.
Let's call this "Polymorphic Proxy".
Have a look at example below:
ConditionalTargetSource.java
#Component
public class ConditionalTargetSource implements TargetSource {
#Autowired
private MyRegistry registry;
#Override
public Class<?> getTargetClass() {
return MyInterface.class;
}
#Override
public boolean isStatic() {
return false;
}
#Override
public Object getTarget() throws Exception {
return registry.getMyInterface();
}
#Override
public void releaseTarget(Object target) throws Exception {
//Do some staff here if you want to release something related to interface instances that was created with MyRegistry.
}
}
applicationContext.xml
<bean id="myInterfaceFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="MyInterface"/>
<property name="targetSource" ref="conditionalTargetSource"/>
</bean>
<bean name="conditionalTargetSource" class="ConditionalTargetSource"/>
SomeService.java
#Service
public class SomeService {
#Autowired
private MyInterface myInterfaceBean;
public void foo(){
//Here we have `myInterfaceBean` proxy that will do `conditionalTargetSource.getTarget().bar()`
myInterfaceBean.bar();
}
}
Also if you want to have both MyInterface implementations to be Spring beans, and the Spring context could not contains both instances at the same time then you can try to use ServiceLocatorFactoryBean with prototype target beans scope and Conditional annotation on target implementation classes. This approach can be used instead of MyRegistry.
P.S.
Probably Application Context refresh operation also can do what you want but it can cause other problems such as performance overheads.
This may be a duplicate question or at least very similar, anyway I answered this sort of question here: Spring bean partial autowire prototype constructor
Pretty much when you want a different beans for a dependency at run-time you need to use a prototype scope. Then you can use a configuration to return different implementations of the prototype bean. You will need to handle the logic on which implementation to return yourself, (they could even be returning 2 different singleton beans it doesn't matter) But say you want new beans, and the logic for returning the implementation is in a bean called SomeBeanWithLogic.isSomeBooleanExpression(), then you can make a configuration:
#Configuration
public class SpringConfiguration
{
#Bean
#Autowired
#Scope("prototype")
public MyInterface createBean(SomeBeanWithLogic someBeanWithLogic )
{
if (someBeanWithLogic .isSomeBooleanExpression())
{
return new ImplA(); // I could be a singleton bean
}
else
{
return new ImplB(); // I could also be a singleton bean
}
}
}
There should never be a need to reload the context. If for instance, you want the implementation of a bean to change at run-time, use the above. If you really need to reload your application, because this bean was used in constructors of a singleton bean or something weird, then you need to re-think your design, and if these beans are really singleton beans. You shouldn't be reloading the context to re-create singleton beans to achieve different run-time behavior, that is not needed.
Edit The first part of this answer answered the question about dynamically injecting beans. As asked, but I think the question is more of one: 'how can I change the implementation of a singleton bean at run-time'. This could be done with a proxy design pattern.
interface MyInterface
{
public String doStuff();
}
#Component
public class Bean implements MyInterface
{
boolean todo = false; // change me as needed
// autowire implementations or create instances within this class as needed
#Qualifier("implA")
#Autowired
MyInterface implA;
#Qualifier("implB")
#Autowired
MyInterface implB;
public String doStuff()
{
if (todo)
{
return implA.doStuff();
}
else
{
return implB.doStuff();
}
}
}
You can use #Resource annotation for injection as originally answered here
e.g.
#Component("implA")
public class ImplA implements MyInterface {
...
}
#Component("implB")
public class ImplB implements MyInterface {
...
}
#Component
public class DependentClass {
#Resource(name = "\${myinterface.type}")
private MyInterface impl;
}
and then set the implementation type in properties file as -
myinterface.type=implA
Be aware that - if interesting to know about - FileChangedReloadingStrategy makes your project highly dependent on the deployment conditions: the WAR/EAR should be exploded by container and your should have direct access to the file system, conditions that are not always met in all situations and environments.
You can use Spring #Conditional on a property value. Give both Beans the same name and it should work as only one Instance will be created.
Have a look here on how to use #Conditional on Services and Components:
http://blog.codeleak.pl/2015/11/how-to-register-components-using.html
public abstract class SystemService {
}
public class FooSystemService extends FileSystemService {
}
public class GoSystemService extends FileSystemService {
}
#Configuration
public class SystemServiceConf {
#Bean
#Conditional(SystemServiceCondition.class)
public SystemService systemService(#Value("${value.key}") value) {
switch (value) {
case A:
return new FooSystemService();
case B:
return new GoSystemService();
default:
throw new RuntimeException("unknown value ");
}
}
}
public class SystemServiceCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return true;
}
}

How to create a list filled with instances of a prototype bean using annotations?

From this Q/A: How to define a List bean in Spring? I know I can define a List<Foo> fooList filled with Foo bean instances but using XML configuration. Here's an example:
public interface Foo {
//methods here...
void fooMethod();
}
#Service("foo")
#Scope("prototype")
public class FooImpl implements Foo {
//fields and methods...
#Override
public void fooMethod() {
//...
}
}
#Service("fooCache")
#Scope
public class FooCacheImpl implements Foo {
//fields and methods...
#Override
public void fooMethod() {
//retrieves data from some cache
//...
}
}
#Service("fooWS")
#Scope("prototype")
public class FooWSImpl implements Foo {
//fields and methods...
#Override
public void fooMethod() {
//retrieves data from web service
//...
}
}
I can configure a client through XML:
<bean id="fooClient" class="some.package.FooClient">
<property name="fooList">
<list>
<bean ... /> <!-- This may be fooImpl -->
<bean ... /> <!-- This may be fooCacheImpl -->
<bean ... /> <!-- This may be fooWSImpl -->
<!-- I can have more beans here -->
</list>
</property>
</bean>
I want to know if this can be done with annotations only, no need to define the bean through XML. Something like this:
#Component
#Scope("prototype")
public class FooClient {
//which annotation(s) to use here to fill this list with FooImpl instances?
//I understand that if I have two implementations of Foo I may use a #Qualifier
//or use another list to note the different implementations.
private List<Foo> fooList;
public void bar() {
for (Foo foo : fooList) {
foo.fooMethod();
}
}
}
I think it would be better a solution that doesn't involve injecting the ApplicationContext nor the BeanFactory so FooClient is not tightly coupled to Spring classes. Also, for my case, I cannot use any Java EE classes like javax.inject.Provider as shown in this blog post: Spring 2.5.x+3.0.x: Create prototype instances from code.
What about using a Factory Bean?
I know you mentioned you did not want to be too coupled to spring - with a factory bean your bean containing the list is not so coupled - just your factory is.
Something like
#Component("fooList")
class ListFactory<List<Foo>> implements FactoryBean, ApplicationContextAware {
ApplicationContext context;
public List<Foo>> getObject() {
List<Foo> list = new ArrayList();
list.add(context.getBean("foo");
list.add(context.getBean("foo");
return list;
}
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
public boolean isSingleton() {
return false;
}
}
#Component
#Scope("prototype")
class FooClient {
#Inject
#Named("footList")
private List<Foo> fooList;
public void bar() {
for (Foo foo : fooList) {
foo.fooMethod();
}
}
}
Haven't tried it myself, or had the scenario where I've needed it so I'm not sure it will work.
If you're doing it in the code directly, then I think using the PostConstruct annotation would be the way to go:
#Component
#Scope("prototype")
public class FooClient {
....
#PostConstruct
public void init() throws Exception {
fooList = new ArrayList<Foo>();
fooList.add(new FooImpl());
}
I think using this approach would be more flexible, since I think you will struggle with annotations only if the FooImpl objects themselves require additional configuration.
That's a limitation (or feature) of prototype scope. The docs say this
In contrast to the other scopes, Spring does not manage the complete
lifecycle of a prototype bean: the container instantiates, configures,
and otherwise assembles a prototype object, and hands it to the
client, with no further record of that prototype instance.
So after Spring hands it off to you, it doesn't keep any reference to it and therefore cannot autowire any of them into your fooList. If you did add #Autowired
#Autowired
private List<Foo> fooList;
it would just create a new FooImpl object and autowire that as the single element in your List.
If you're trying to keep a reference of all the Foo instances created, you'll most likely have to do it yourself.
You can use method injection as this:
public class PrototypeClient {
protected abstract PrototypeBean createPrototype();
private List<PrototypeBean> createPrototypeList() {
int listSize = calculateListSize();
List<Prototype> result = new ArrayList<Prototype(listSize);
for (int i = 0; i < listSize; i++) {
result.add(createPrototype());
}
return result;
}
private int calculateListSize() {
// do your stuff here
}
// ...
}
and have an Spring config as:
<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="prototypeBean"
class="fully.qualified.class.name.of.Prototype"
scope="prototype" />
<bean id="prototyeClient"
class="fully.qualified.class.name.of.PrototypeClient">
<lookup-method name="createPrototype" bean="prototypeBean"/>
</bean>
</beans>

Spring: Delegate to custom proxy wrapper for interface injection

In a very large legacy application I have interfaces and classes that do not implement those interfaces.
The interfaces are generated based on the class so the signatures are the same (except the interface adds another exception on top) and the names are similar (so it's easy to find the class name from the interface name).
To get an implementation of the interface we do a bunch of processing and logging calls but basically use java.lang.reflect.Proxy to delegate to the class. Simplified it looks like this:
// This will create a proxy and invoke handler that calls HelloWorld.doSomething
HelloWorldInterface i = MyProxyUtil.getInstance(HelloWorldInterface.class);
i.doSomething();
public interface HelloWorldInterface {
public void doSomething() throws Exception;
}
public class HelloWorld {
public void doSomething() {
//something
}
}
Is it possible with Spring annotation processing, to generically #Autowire all fields of type *Interface and have spring use MyProxyUtil.getInstance(*Interface.class) to inject the implementation?
Such that
#Autowire HelloWorldInterface a;
HelloWorldInterface b = MyProxyUtil.getInstance(HelloWorldInterface.class);
#Autowire AnotherInterface c;
AnotherInterface d = MyProxyUtil.getInstance(AnotherInterface.class);
a == b
c == d
Yes, you need to implement a AutowireCandidateResolver.
For example:
public class ProxyAutowiredCandidateResolver extends SimpleAutowireCandidateResolver {
#Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
String dependencyClassName = descriptor.getDependencyType().getSimpleName();
if (dependencyClassName.endsWith("Interface")) {
return MyProxyUtil.getInstance(descriptor.getDependencyType());
}
return super.getSuggestedValue(descriptor);
}
}
You could use a BeanFactoryPostProcessor to configure it in the application context:
public class AutowireCandidateResolverConfigurer implements BeanFactoryPostProcessor {
private AutowireCandidateResolver autowireCandidateResolver;
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory bf = (DefaultListableBeanFactory) beanFactory;
bf.setAutowireCandidateResolver(autowireCandidateResolver);
}
public AutowireCandidateResolver getAutowireCandidateResolver() {
return autowireCandidateResolver;
}
public void setAutowireCandidateResolver(
AutowireCandidateResolver autowireCandidateResolver) {
this.autowireCandidateResolver = autowireCandidateResolver;
}
}
<bean id="autowireCandidateResolverConfigurer" class="AutowireCandidateResolverConfigurer">
<property name="autowireCandidateResolver">
<bean class="ProxyAutowiredCandidateResolver" />
</property>
</bean>
If I'm reading this correctly, you should be able to define them in a JavaConfig #Configuration annotated class and then use them elsewhere.
From the docs (Spring):
#Configuration
public class AppConfig {
#Bean
public MyService myService() {
return new MyServiceImpl();
}
}
You could do something similar:
#Configuration
public class InterfaceConfig {
#Bean
public HelloWorldInterface helloWorldInterface() {
return MyProxyUtil.getInstance(HelloWorldInterface.class);
}
}
At that point, Spring would use that definition whenever that bean was needed.
You'd have to link in the #Configuration class somehow (classpath scanning, programmatically, etc), but that depends on how you're setting up your application context.
I think this should work. I've used JavaConfig quite a bit, but never quite like this. But it seems reasonable.

Categories