I have a problem to select the correct spring bean that should get injected.
I need a way to tell the spring container what bean to inject depeding on the call to a previous class. I do all the spring bean wiring in xml.
My question: is this possible and if it is any reference on an implementation?
I have created some sample code to illustrate what i´m trying to accomplish. Feel free to change it so that it will work to get the correct ReportHeader bean injected depending on the selected reportType during runtime.
public enum ReportType{
Credit,
Annul
}
public class ReportService {
private ReportHeaderService reportHeaderService;
private ReportType reportType;
public ReportService (){}
public setReportType(ReportType reportType){
this.reportType = reportType;
}
public void setReportHeaderService(ReportHeaderService reportHeaderService){
this.reportHeaderService = reportHeaderService;
}
private void generateHeader(){
//i would like to call my service like this and have the correct bean injected to ReportHeader.
reportHeaderService.generateHeader(reportType)
}
}
public class ReportHeaderService {
private ReportHeader reportHeader;
//this will call the injected bean that needs to be selected accoring to the ReportType
public void generateHeader(ReportType type){
reportHeader.createHeader();
}
}
public interface ReportHeader{
public void createHeader();
}
public class CreditReportHeader implements ReportHeader{
public void createHeader(){
..dostuff();
}
}
public class AnnulReportHeader implements ReportHeader{
public void createHeader(){
..dostuff();
}
}
Consider injecting a Map<ReportType, ReportHeader> to ReportHeaderService, so that generateHeader works as:
public class ReportHeaderService {
private Map<ReportType, ReportHeader> reportHeaderMap;
public void generateHeader(ReportType type){
ReportHeader reportHeader = reportHeaderMap.get(type);
if (reportHeader != null) {
reportHeader.createHeader();
}
}
}
You can define a ReportHeaderFactory to get the ReportHeader according to ReportType:
public class ReportHeaderFactory {
private CreditReportHeader creditReportHeader;
private AnnulReportHeader annulReportHeader;
public ReportHeader getReportHeader(ReportType reportType) {
switch (reportType) {
case Credit:
return creditReportHeader;
case Annul:
return annulReportHeader;
default:
throw new IllegalArgumentException("No Such Header");
}
}
}
Re-define the ReportHeaderService with an instance of ReportHeaderFactory:
public class ReportHeaderService {
//private ReportHeader reportHeader;
private ReportHeaderFactory headerFactory;
//this will call the injected bean that needs to be selected accoring to the ReportType
public void generateHeader(ReportType type){
//reportHeader.createHeader();
headerFactory.getReportHeader(type);
}
}
As you are doing all the spring bean wiring in xml, you just need to make below entries in the config file:
<bean id="ReportHeaderService" class="x.y.ReportHeaderService">
<property name="headerFactory" ref="headerFactory" />
</bean>
<bean id="headerFactory" class="x.y.ReportHeaderFactory">
<property name="creditReportHeader" ref="creditReportHeader" />
<property name="annulReportHeader" ref="annulReportHeader" />
</bean>
<bean id="creditReportHeader" class="x.y.CreditReportHeaderImpl" />
<bean id="annulReportHeader" class="x.y.AnnulReportHeaderImpl" />
Related
I have a bit of code that is working :
#Component
public class MessageUtil {
#Autowired
#Qualifier("processMessages")
private ReloadableConfig config;
public String createMessage() {
return config.getPropertyStr("message.simple.signature");
}
}
The bean processMessages is defined here :
<bean id="processMessages" class="com.company.framework.resources.ReloadableConfig">
<property name="basename" value="classpath:com/company/aaa/bbb/domain/service/processMessages"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds" value="60"/>
</bean>
Then I created some new classes :
public abstract class MessageBuilder {
#Autowired
#Qualifier("processMessages")
protected ReloadableConfig config;
public abstract String createMessage();
}
#Component
public class SimpleMessageBuilder extends MessageBuilder {
private String template;
private void setTemplate() {
template = config.getPropertyStr("message.simple.signature");
}
#Override
public String createMessage() {
setTemplate();
return template;
}
}
I now have a NullPointerException because in setTemplate(), config is null.
What's the problem in the second code ?
#Autowired doesn't work neither on field neither on constructors of abstract classes. It works on setter of abstract class but be sure to make it final because if overwritten by concrete class behavior is unstable. An abstract class isn't component-scanned since it can't be instantiated without a concrete subclass.
i have this main class
public class Draw {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Triangle t = (Triangle) context.getBean("triangle");
Triangle t1 = (Triangle) context.getBean("triangle");
t.show();
System.out.println(t == t1);
}
}
TRIANGLE CLASS
#Service
#Scope("prototype")
public class Triangle {
private Point pointa;
public Point getPointa() {
return pointa;
}
public Triangle(Point pointa) {
this.pointa = pointa;
}
public void show() {
System.out.println("POINT A (" + pointa.getX() + "," + pointa.getY() + ")");
}
#Override
public String toString() {
return "Triangle [pointa=" + pointa + "]";
}
}
AND SPRING.XML
<context:component-scan base-package="com.spring.demo" />
<mvc:annotation-driven />
<context:component-scan base-package="com.spring.demo" />
<bean id="triangle" class="com.spring.demo.Triangle" autowire="constructor">
</bean>
<bean id="pointabc" class="com.spring.demo.Point">
<property name="x" value="0" />
<property name="y" value="0" />
</bean>
<context:annotation-config />
i know that to make a bean prototype we use scope="prototype" in spring.xml.
but i want to make bean prototype by using #scope("prototype") only.
i don't know why the above code is not working correctly.
I've researched a lot but couldn't find any answer
System.out.println(t == t1); this gives me true while ideally it should be false.
Declaring Triangle as prototype has no effect as it is instantiated in your spring.xml as singleton.
The key is to ask for a new prototype bean everytime you need one.
This can be achieved by using a configuration class (and removing <bean id="triangle" ... from spring.xml:
package com.spring.demo;
#Configuration
public class MyProvider {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Triangle create() {
return new Triangle();
}
}
Usage is as follows:
package com.spring.demo;
#Component
public class MySingleton {
#Autowired
private MyProvider myProvider;
#PostConstruct // will execute this method once app context loaded
public void doStuffThatNeedsAPrototypeBeanInstance() {
Triangle t1 = myProvider.create();
Triangle t2 = myProvider.create();
...
}
}
Adding a #PostConstruct has the effect that the method is called once the application context has been initialized, as alternative to running things in the main method. This way, execution is inside a spring bean with the benefit of easily accessing other spring beans through annotations.
As ApplicationContext has several implementations, ClassPathXmlApplicationContext one is used for XML bean configuration.
In XML ways you simply set the scope attribute in bean element.
AnnotationConfigApplicationContext is implementation of ApplicationContext used for Java Based Configuration means for annotation based configuration like #Bean etc.
If you want to use annotation way, then you need to define Java Based Configuration and need to use AnnotationConfigApplicationContext object.
#Configuration
public class AppConfiguration{
#Bean
#Scope("prototype")
public Triangle triangle(){
return new Triangle();
}
}
Then obtained the bean as below.
ApplicationContext ctx =
new AnnotationConfigApplicationContext(AppConfiguration.class);
Triangle triangle= ctx.getBean(Triangle.class);
for annotation configuration we use AnnotationConfigApplicationContext an implementation of Abstract Application Context
public class Scope{
public static void main(String[] args) {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(TeacherConfig.class);
Teacher tea=(Teacher) context.getBean("teacher");
Teacher tea1=(Teacher) context.getBean("teacher");
System.out.println(tea==tea1);
}
}
TEACHER CLASS
public class Teacher {
public void print(String msg){
System.out.println("TEACHER -->"+msg);
}
TeacherConfig
#Configuration
public class TeacherConfig {
#Bean(name = "teacher")
#Scope(value=ConfigurableBeanFactory.SCOPE_SINGLETON)
public Teacher Teacher() {
return new Teacher();
}
}
#Configuration to tell Spring that this is the core Spring configuration file equivalent to spring.xml, and define bean via #Bean.
OUTPUT OF System.out.println(tea==tea1); is TRUE
By extending the answer given by #AshishUpadhyay, I made couple of changes in code. The above answer is for singleton scope. For prototype need to make few changes.
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
public class Scope{
public static void main(String[] args) {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(TeacherConfig.class);
Teacher tea=(Teacher) context.getBean(Teacher.class);
Teacher tea1=(Teacher) context.getBean(Teacher.class);
System.out.println(tea);
System.out.println(tea1);
System.out.println(tea==tea1);
}
}
Teacher class:
public class Teacher {
public void print(String msg) {
System.out.println("TEACHER -->" + msg);
}
}
TeacherConfig class:
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
#Configuration
public class TeacherConfig {
#Bean()
#Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Teacher Teacher() {
return new Teacher();
}
}
After running the Scope class the output is false which is expected in case of prototype.
Hope this will help someone.
I have a singleton service class like the below:
#Service
public class SingletonClass {
// the context is prototype scope
private Context context;
public void run(){
context.setXyz();
}
public void runThis(){
context.getXyz();
}
// setter,getter methods for context
}
How can I achieve this in a thread-safe way with a lookup-method injection?
I want to share the state across methods. Get new context with every method and used in subsequent methods.
You should modify your Singleton class as follows
#Service
public abstract class SingletonClass{
protected abstract Context createContext();
private Context context;
#PostConstruct
public void init() {
context = createContext();
}
public void run(){
context().setXyz();
}
public void runThis(){
context().getXyz();
}
public void runThisWithNewContext() {
createContext().getXyz();
}
}
And your xml config will look like,
<bean id="context" class="Context" scope="prototype" />
<bean id="singletonClass" class="SingletonClass">
<lookup-method name="createContext" bean="context" />
</bean>
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>
I have a requestScope bean, regular Java bean, and Spring configuration just like the code shown below. May I know how can I access the bean's property from regular Java code that is configured with Spring?
Managed Bean
#ManagedBean(name="requestBean")
public class RequestBean {
private String theID;
/** getter and setter of theID **/
}
Spring Bean
public class SpringBean {
private RequestBean theBean = null;
// how could I access the RequestBean.theID from this class ??
}
Spring configuration
<bean id="springBean" class="org.huahsin.SpringBean"/>
Maybe I do not understand the question, but I think it is simply by calling the getter:
public class SpringBean {
private RequestBean theBean = null;
...
public void aCallingMethod() {
if (theBean != null) {
theBean.getTheId(); // <-- here it is !!
}
}
}
EDIT
To inject the request bean into the Spring bean, either you use an #Autowired annotation:
public class SpringBean {
#Autowired
private RequestBean theBean = null;
...
}
Either you inject it in your XML file:
<bean id="springBean" class="org.huahsin.SpringBean">
<property name="theBean" ref="requestBean"/>
</bean>