I am working on a project using Spring Data JPA (on Tomcat 7). I'am implementing a BeanFactoryPostProcessor to dynamically create my DataSources. But the problem is that my DataSource's information (name, url, etc..) is stored in a database itself.
#Component
class DatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
// This doesn't work
#Autowired DatabaseService databaseService;
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// my code here ...
// ...
}
}
As you can see, i was trying to inject a service which can get me a list of all DataSources from my database, but it doesn't work. Is the anyway to connect to the database and get that list within the BeanFactoryPostProcessor class? Any other workaround will be welcome. :)
BeanFactoryPostProcessors are a very special kind of concept in Spring. They are components that operate on BeanDefinition instances, which are a metamodel of the bean instances to be created.
That means, that at the point in time when the BFPPs are invoked, no bean instances have been created yet as the metamodel is about to be post processed (as the name suggests). Hence beans depended on by the BFPP will be initialized extremely early in the lifecycle of the container. Thus it's highly recommended to not depend on application components from BFPPs or - if really required - only on beans that don't necessarily trigger the creation of a lot of downstream components.
That said, you shouldn't depend on especially repositories from BFPPs as they usually require the creation of a lot of infrastructure components. I'd recommend getting the configuration properties that are required to connect to the configuration database (JDBC URL, username, password, etc.) and just create a throw-away DataSource that's only used to create a new BeanDefinition for a new DataSource that's going to be used by the application eventually.
So here are the recommended steps (from the top of my head - might need some tweaking):
drop the autowiring of a DataSource
configure an #PropertySource pointing to the properties containing the coordinates to connect
inject the values of that PropertySource into the constructor of the BFPP:
class YourBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public YourBeanFactoryPostProcessor(#Value("#{properties.url}) String url, …) {
// assign to fields
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 1. Create throw-away DataSource
// 2. Create JdbcTemplate
// 3. Use template to lookup configuration properties
// 4. Create BeanDefinition for DataSource using properties just read
// 5. Register BeanDefinition with the BeanFactory
}
}
Related
I'm confused at this point, and i know all spring boot applications beans are singleton, according to my understanding if we have class annotated with #Service annotation that bean can be #Autowired in only one class (correct me if i'm wrong) here is the code that works fine, but i'm trying to understand how it works? how one bean can be #Autowired in two different classes?
How SampleService bean can be #Autowired in SampleController2 and SampleController3 at a time ?
And is this recommended approach? and in this case two threads can parallely change the data inside bean?
SampleController2
#RestController
#RequestMapping(value="samplemock")
public class SampleController2 {
#Autowired
private SampleService2 sampleservice2;
#RequestMapping(value="/mock1",method=RequestMethod.GET)
public void mockCall1() {
sampleservice2.m1();
}
}
SampleController3
#RestController
#RequestMapping(value="samplemock2")
public class SampleController3 {
#Autowired
private SampleService2 sampleservice2;
#RequestMapping(value="/mock1",method=RequestMethod.GET)
public void mockCall1() {
sampleservice2.m1();
}
}
SampleService2
#Service
public class SampleService2 {
public void m1() {
System.out.println("bean is autowired");
}
}
Here is a simplified view of what Spring does on startup:
// Create bean: sampleService2
SampleService2 sampleService2 = new SampleService2();
// Create bean: sampleController2
SampleController2 sampleController2 = new SampleController2();
sampleController2.sampleservice2 = sampleService2; // because #Autowired
// Create bean: sampleController3
SampleController3 sampleController3 = new SampleController3();
sampleController3.sampleservice2 = sampleService2; // because #Autowired
As you can see, the singleton bean sampleService2 is autowired into both sampleController2 and sampleController3.
The beans are added to a repository, so you can look them up by name or type at any later point in time.
By default, as you mentioned, all Spring beans are singletons, but your second assumption is wrong: the same bean can be autowired in many other beans.
In fact that's the whole point of them being singletons.
That also means two different threads could change the state of the same bean indeed. You would most of the time want to keep your beans stateless for that reason.
If you really ever need to have one different instance of a bean for each place where it is autowired, you can change the scope of that bean to prototype. See Spring bean scopes docs.
The intention behind dependency injection and inversion of control is simple:
You define your injectables (like services) once, and they are instantiated once (unless you specify otherwise).
Those injectables are then used everywhere applicable, and you don't control their lifecycle, scope or state.
While I feel like the last point answers your primary question fairly tacitly, I'll elaborate - in a DI context, the only thing that really matters are enforceable contracts. That is to say, if your service subscribes to a specific type of contract, and you have a component which wishes to inject a service which fulfills that contract, then your DI layer should faithfully register a service which can fulfill that contract.
You get into fun and exciting stuff with bean priority, qualifiers and application profiles at that point, but this is the general idea.
For a concrete example: javax.sql.DataSource is an interface which is implemented by many JDBC-backed solutions, such as MySQL, Postgres, Oracle, and others. If you wish to have two different beans which talk to two different databases, but you want to be able to use those interchangeably, then you define a bean of type DataSource to use and configure which data source gets created. Again, this does involve things like #Qualifier to ensure you wire in the most specific bean at the most appropriate time.
Also, that last point is fairly important to answer this part of your question:
... and in this case two threads can parallely change the data inside bean?
It is very unwise to create an injectable bean with its own inherent state. That is, if you have SampleService attach itself to some sort of cached state with a collection inside of it, you're basically violating expectations since you don't know when or how often that collection is going to have elements added to it or removed from it.
The better convention is to have beans which can reference stateful services, but don't store that state in the bean itself (such as a database connection, but not entire database tables).
I have 2 datasources say dataSourceA and dataSourceB but based upon few calculations, I need to execute the same query in different schemas. Also, it is going to be executed in either of the schemas.
so, at DAO layer , I have one setDataSource() method which is #autowired to the dataSourceA, thus,returning the JDBCTemplate with former DataSource. How can I implement the dataSourceB changes using the same JDBCTemplate as it will be difficult to change at every DAO layer as entire application change will be required.
You could you inject both datasources and select the datasource inside your method according to your logic:
public class SomeDaoImpl implements SomeDao {
private final JdbcTemplate jdbcTemplateA;
private final JdbcTemplate jdbcTemplateB;
#Autowired
public SomeDaoImpl(JdbcTemplate jdbcTemplateA, JdbcTemplate jdbcTemplateB) {
// injecting both JdbcTemplate instances
this.jdbcTemplateA = jdbcTemplateA;
this.jdbcTemplateB = jdbcTemplateB;
}
public void businessLogicMethod(...) {
// choosing the actual template to be used according to your logic
JdbcTemplate jdbcTemplate = chooseTemplate(...);
// now using the template to execute a query
jdbcTemplate.execute(...);
}
}
Another option would be to instantiate two SomeDaoImpl instances and inject one JdbcTemplate into each of them, and select the DAO instance in your service layer.
But both these solutions have a flaw: transaction is usually initiated in the service layer (with an interceptor, for example), and it has no idea that you are going to route your requests to another datasource; so it could happen that a transaction starts on one datasource, but the query is executed on another one.
So the clearest solution would be to go one level up and instantiate 2 services, in each of them DAOs with different JdbcTemplate instances. Of course, 2 transaction managers will have to be configured and carefully wired (for example, via #Transactional("transactionManagerA")). More information on this here Spring - Is it possible to use multiple transaction managers in the same application?
I've read about AbstractRoutingDataSource and the standard ways to bind a datasource dynamically in this article:
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return CustomerContextHolder.getCustomerType();
}
}
It uses a ThreadLocal context holder to "set" the DataSource:
public class CustomerContextHolder {
private static final ThreadLocal<CustomerType> contextHolder =
new ThreadLocal<CustomerType>();
public static void setCustomerType(CustomerType customerType) {
Assert.notNull(customerType, "customerType cannot be null");
contextHolder.set(customerType);
}
public static CustomerType getCustomerType() {
return (CustomerType) contextHolder.get();
}
// ...
}
I have a quite complex system where threads are not necessarily in my control, say:
Scheduled EJB reads a job list from the database
For each Job it fires a Spring (or Java EE) batch job.
Each job have its origin and destination databases (read from a central database).
Multiple jobs will run in parallel
Jobs may be multithreaded.
ItemReader will use the origin data source that was set for that specific job (origin data source must be bound to some repositories)
ItemWriter will use the destination data source that was set for that specific job (destination data source must also be bound to some repositories).
So I'm feeling somewhat anxious about ThreadLocal, specially, I'm not sure if the same thread will be used to handle multiple jobs. If that happens origin and destination databases may get mixed.
How can I "store" and bind a data source dynamically in a safe way when dealing with multiple threads?
I could not find a way to setup Spring to play nice with my setup and inject the desired DataSource, so I've decided to handle that manually.
Detailed solution:
I changed my repositories to be prototypes so that a new instance is constructed every time that I wire it:
#Repository
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
I've introduced new setDataSource and setSchema methods in top level interfaces / implementations that are supposed to work with multiple instances / schemas.
Since I'm using spring-data-jdbc-repository my setDataSource method simple wraps the DataSource with a new JdbcTemplate and propagate the change.
setJdbcOperations(new JdbcTemplate(dataSource));
My implementation is obtaining the DataSources directly from the application server:
final Context context = new InitialContext();
final DataSource dataSource = (DataSource) context.lookup("jdbc/" + dsName);
Finally, for multiples schemas under the same database instance, I'm logging in with a special user (with the correct permissions) and using a Oracle command to switch to the desired schema:
getJdbcOperations().execute("ALTER SESSION SET CURRENT_SCHEMA = " + schema);
While this goes against the Dependency inversion principle it works and is handling my concurrency requirements very well.
Is it possible to inject beans to a JPA #Entity using Spring's dependency injection?
I attempted to #Autowire ServletContext but, while the server did start successfully, I received a NullPointerException when trying to access the bean property.
#Autowired
#Transient
ServletContext servletContext;
You can inject dependencies into objects not managed by the Spring container using #Configurable as explained here: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-atconfigurable.
As you've realized by now, unless using the #Configurable and appropriate AspectJ weaving configuration, Spring does not inject dependencies into objects created using the new operator. In fact, it doesn't inject dependencies into objects unless you've retrieved them from the ApplicationContext, for the simple reason that it simply doesn't know about their existence. Even if you annotate your entity with #Component, instantiation of that entity will still be performed by a new operation, either by you or a framework such as Hibernate. Remember, annotations are just metadata: if no one interprets that metadata, it does not add any behaviour or have any impact on a running program.
All that being said, I strongly advise against injecting a ServletContext into an entity. Entities are part of your domain model and should be decoupled from any delivery mechanism, such as a Servlet-based web delivery layer. How will you use that entity when it's accessed by a command-line client or something else not involving a ServletContext? You should extract the necessary data from that ServletContext and pass it through traditional method arguments to your entity. You will achieve a much better design through this approach.
Yes, of course you can. You just need to make sure the entity is also registered as a Spring managed bean either declaratively using <bean> tags (in some spring-context.xml) or through annotations as shown below.
Using annotations, you can either mark your entities with #Component (or a more specific stereotype #Repository which enables automatic exception translation for DAOs and may or may not interfere with JPA).
#Entity
#Component
public class MyJAPEntity {
#Autowired
#Transient
ServletContext servletContext;
...
}
Once you've done that for your entities you need to configure their package (or some ancestor package) for being scanned by Spring so that the entities get picked up as beans and their dependencies get auto wired.
<beans ... xmlns:context="..." >
...
<context:component-scan base-package="pkg.of.your.jpa.entities" />
<beans>
EDIT : (what finally worked and why)
Making the ServletContext static. (remove #Autowired)
#Transient
private static ServletContext servletContext;
Since, JPA is creating a separate entity instance i.e. not using the Spring managed bean, it's required for the context to be shared.
Adding a #PostConstruct init() method.
#PostConstruct
public void init() {
log.info("Initializing ServletContext as [" +
MyJPAEntity.servletContext + "]");
}
This fires init() once the Entity has been instantiated and by referencing ServletContext inside, it forces the injection on the static property if not injected already.
Moving #Autowired to an instance method but setting the static field inside.
#Autowired
public void setServletContext(ServletContext servletContext) {
MyJPAEntity.servletContext = servletContext;
}
Quoting my last comment below to answer why do we have to employ these shenanigans:
There's no pretty way of doing what you want since JPA doesn't use the Spring container to instantiate its entities. Think of JPA as a separate ORM container that instantiates and manages the lifecycle of entities (completely separate from Spring) and does DI based on entity relationships only.
After a long time I stumbled across this SO answer that made me think of an elegant solution:
Add to your entities all the #Transient #Autowired fields you need
Make a #Repository DAO with this autowired field:
#Autowired private AutowireCapableBeanFactory autowirer;
From your DAO, after fetching the entity from DB, call this autowiring code:
String beanName = fetchedEntity.getClass().getSimpleName();
autowirer.autowireBean(fetchedEntity);
fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);
Your entity will then be able to access the autowired fields as any #Component can.
a standard case - you have a controller (#Controller) with #Scope("session").
classes put in the session usually are expected to implement Serializable so that they can be stored physically in case the server is restarted, for example
If the controller implements Serializable, this means all services (other spring beans) it is referring will also be serialized. They are often proxies, with references to transaction mangers, entity manager factories, etc.
It is not unlikely that some service, or even controller, hold a reference to the ApplicationContext, by implementing ApplicationContextAware, so this can effectively mean that the whole context is serialized. And given that it holds many connections - i.e. things that are not serializable by idea, it will be restored in corrupt state.
So far I've mostly ignored these issues. Recently I thought of declaring all my spring dependencies transient and getting them back in readResolve() by the static utility classes WebApplicationContextUtils and such that hold the request/ServletContext in a ThreadLocal. This is tedious, but it guarantees that, when the object is deserialized, its dependencies will be "up to date" with the current application context.
Is there any accepted practice for this, or any guidelines for serializing parts of the spring context.
Note that in JSF, managed beans (~controllers) are stateful (unlike action-based web frameworks). So perhaps my question stands more for JSF, than for spring-mvc.
In this presentation (around 1:14) the speaker says that this issue is resolved in spring 3.0 by providing a proxy of non-serializable beans, which obtains an instance from the current application context (on deserialization)
It appears that bounty didn't attract a single answer, so I'll document my limited understanding:
#Configuration
public class SpringConfig {
#Bean
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
MyService myService() {
return new MyService();
}
#Bean
#Scope("request")
public IndexBean indexBean() {
return new IndexBean();
}
#Bean
#Scope("request")
public DetailBean detailBean() {
return new DetailBean();
}
}
public class IndexBean implements Serializable {
#Inject MyService myService;
public void doSomething() {
myService.sayHello();
}
}
public class MyService {
public void sayHello() {
System.out.println("Hello World!");
}
}
Spring will then not inject the naked MyService into IndexBean, but a serializable proxy to it. (I tested that, and it worked).
However, the spring documentation writes:
You do not need to use the <aop:scoped-proxy/> in conjunction with beans that are scoped as singletons or prototypes. If you try to create a scoped proxy for a singleton bean, the BeanCreationException is raised.
At least when using java based configuration, the bean and its proxy can be instantiated just fine, i.e. no Exception is thrown. However, it looks like using scoped proxies to achieve serializability is not the intended use of such proxies. As such I fear Spring might fix that "bug" and prevent the creation of scoped proxies through Java based configuration, too.
Also, there is a limitation: The class name of the proxy is different after restart of the web application (because the class name of the proxy is based on the hashcode of the advice used to construct it, which in turn depends on the hashCode of an interceptor's class object. Class.hashCode does not override Object.hashCode, which is not stable across restarts). Therefore the serialized sessions can not be used by other VMs or across restarts.
I would expect to scope controllers as 'singleton', i.e. once per application, rather than in the session.
Session-scoping is typically used more for storing per-user information or per-user features.
Normally I just store the 'user' object in the session, and maybe some beans used for authentication or such. That's it.
Take a look at the spring docs for configuring some user data in session scope, using an aop proxy:
http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes-other-injection
Hope that helps
I recently combined JSF with Spring. I use RichFaces and the #KeepAlive feature, which serializes the JSF bean backing the page. There are two ways I have gotten this to work.
1) Use #Component("session") on the JSF backing bean
2) Get the bean from ELContext when ever you need it, something like this:
#SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
return (T) FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(FacesContext.getCurrentInstance().getELContext(), null, beanName);
}
After trying all the different alternatives suggested all I had to do was add aop:scoped-proxy to my bean definition and it started working.
<bean id="securityService"
class="xxx.customer.engagement.service.impl.SecurityContextServiceImpl">
<aop:scoped-proxy/>
<property name="identityService" ref="identityService" />
</bean>
securityService is injected into my managedbean which is view scoped. This seems to work fine. According to spring documentation this is supposed to throw a BeanCreationException since securityService is a singleton. However this does not seems to happen and it works fine. Not sure whether this is a bug or what the side effects would be.
Serialization of Dynamic-Proxies works well, even between different JVMs, eg. as used for Session-Replication.
#Configuration public class SpringConfig {
#Bean
#Scope(proxyMode = ScopedProxyMode.INTERFACES)
MyService myService() {
return new MyService();
}
.....
You just have to set the id of the ApplicationContext before the context is refreshed (see: org.springframework.beans.factory.support.DefaultListableBeanFactory.setSerializationId(String))
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// all other initialisation part ...
// before! refresh
ctx.setId("portal-lasg-appCtx-id");
// now refresh ..
ctx.refresh();
ctx.start();
Works fine on Spring-Version: 4.1.2.RELEASE