When you start messing around with Spring's auto-proxy stuff, you often run into this behaviour as documented:
Classes that implement the
BeanPostProcessor interface are
special, and so they are treated
differently by the container. All
BeanPostProcessors and their directly
referenced beans will be instantiated
on startup, as part of the special
startup phase of the
ApplicationContext, then all those
BeanPostProcessors will be registered
in a sorted fashion - and applied to
all further beans. Since AOP
auto-proxying is implemented as a
BeanPostProcessor itself, no
BeanPostProcessors or directly
referenced beans are eligible for
auto-proxying (and thus will not have
aspects 'woven' into them.
For any such bean, you should see an
info log message: “Bean 'foo' is not
eligible for getting processed by all
BeanPostProcessors (for example: not
eligible for auto-proxying)”.
In other words, if I write my own BeanPostProcessor, and that class directly references other beans in the context, then those referenced beans will not be eligible for auto-proxying, and a message is logged to that effect.
My problem is that tracking down where that direct reference is can be very difficult, since the "direct reference" can in fact be a chain of transitive dependencies that ends up taking in half the beans in the application context. All Spring gives you is that single info message, and it's not really much help, beyond telling you when a bean has been caught in this web of references.
The BeanPostProcessor I'm developing does have direct references to other beans, but it's a very limited set of references. Despite this, pretty much every bean in my context is then being excluded from being auto-proxied, according to the log messages, but I can't see where that dependency is happening.
Has anyone found a better way of tracking this down?
Follow this recipe:
Open BeanPostProcessorChecker in your IDE (it's an inner class of AbstractApplicationContext)
Set a breakpoint on if (logger.isInfoEnabled()) { in the method postProcessAfterInitialization
Run your code
When you hit the breakpoint, look for calls to getBean(String,Class<T>) in your stack trace.
One of these calls will try to create a BeanPostProcessor. That bean should be the culprit.
Background
Imagine this situation:
public class FooPP implements BeanPostProcessor {
#Autowire
private Config config;
}
When Spring has to create config (since it's a dependency of FooPP), it has a problem: The contract says that all BeanPostProcessor must be applied to every bean that is being created. But when Spring needs config, there is at least one PP (namely FooPP) which isn't ready for service!
This gets worse when you use an #Configuration class to define this bean:
#Configuration
public class BadSpringConfig {
#Lazy #Bean public Config config() { return new Config(); }
#Lazy #Bean public FooPP fooPP() { return new FooPP(); }
}
Every configuration class is a bean. That means to build a bean factory from BadSpringConfig, Spring needs to apply the post-processor fooPP but in order to do that, it first needs the bean factory ...
In this example, it's possible to break one of the cyclic dependencies. You can make FooPP implement BeanFactoryAware to get Spring inject the BeanFactory into the post processor. That way, you don't need autowiring.
Later in the code, you can lazily ask for the bean:
private LazyInit<Config> helper = new LazyInit<Config>() {
#Override
protected InjectionHelper computeValue() {
return beanFactory.getBean( Config.class );
}
};
#Override
public Object postProcessBeforeInitialization( Object bean, String beanName ) throws BeansException {
String value = helper.get().getConfig(...);
}
(source for LazyInit)
To break the cycle between the bean factory and the post processor, you need to configure the post processor in an XML config file. Spring can read that and build all the structures without getting confused.
Just to bring some closure to this question, the collapse of the uninitialized object graph was caused by the BeanPostProcessor using #Autowired to get its dependencies, and the autowire mechanism effectively caused every other bean definition to be initialized before my BeanPostProcessor got a chance to have a say in the matter. The solution is not to use autowiring for your BPPs.
Not sure if it's of any help, but the Eclipse Spring IDE's
graph view looks like it could be helpful in sorting out bean references..
Related
I'm learning spring and I'm stuck with the bean lifecycle !
When creating a bean, spring gives us several maners to interact with it. We can use one or all of :
BeanFactoryPostProcessor
BeanPostProcessor#postProcessBeforeInitialization
#PostConstruct
InitializingBean#afterPropertiesSet
#Bean#initMethod
BeanPostProcessor#postProcessAfterInitialization
Spring calls them in the order above
My question is : why all these points of interaction ? and when to use each of them (the use case) ?
A BeanFactoryPostProcessor and a BeanPostProcessor are quite different beast and also apply to different things.
A BeanFactoryPostProcessor will operate on the metadata for a bean (I like to call it the recipe) and will post process that. A well know example is the PropertySourcesPlaceholderConfigurer which will inject/replace all #Value in configuration with the value. A BeanFactoryPostProcessor operates on the metadata and thus before any bean has been created.
The BeanPostProcessor can be applied to a bean and can even replace a bean. Spring AOP uses this quite a lot. An example is the PersistenceExceptionTranslationPostProcessor, when a bean has been created it will pass through this BeanPostProcessor and when it is annotated with #Persistence the bean will be replaced by a proxy. This proxy adds exception translation (i.e. it will convert JpaException and SQLException to the Spring DataAccessException). This is done in a BeanPostProcessor. And can be be done before the init-callbacks are called (the postProcessBeforeInitializationor after they have been called thepostProcessAfterInitialization). The PersistenceExceptionTranslationPostProcessorwill run in thepostProcessAfterInitialization` as it needs to be certain the bean has been initialized.
The ServletContextAwareProcessor will run right after the object has been created to inject the ServletContext as early as possible as the initializing of a bean might depend on it.
The 3 callbacks for initializing a bean are more or less the same but are called in sequence because they have been included in later versions. It starter with only an interface InitializingBean and init-method in xml (later also added to #Bean and the annotation support was added when annotations became a thing.
You need init methods to initialize a bean, you might want to check if all properties have been set (like a required datasource) or you might want to start something. A DataSource (especially a connection pool) is a good example to initialize. After all dependencies have been injected you want to start the pool so it will open the connections. Now as you probably cannot modify the code you want to use the init-method if you control the code you probably want to add #PostConstruct. If you are writing an framework that depends on Spring I would use the InitializingBean.
Next to those 3 init methods you also have the destruction counter-parts. The DisposableBean interface (and destroy-method) and the #PreDestroy annotation. Again when you stop your application you also want to close the connections in your connection pool and you want to probably call the close method on the DataSource. A perfect sample of a destruction callback.
I'm working on a Spring application using beans of different scopes. Many beans are singletons, other request or custom scoped. Especially using those custom scopes makes it sometimes difficult to find out which scope can be safely injected into which other scope or when e.g. a Provider<T> needs to be used.
I am aware that I can just create scope proxies for all beans that are basically not singletons, but in many cases that does not seem to be necessary. For example, a bean might only be supposed to be injected into other beans of the same scope, but not everyone working on the project might be aware of that. Thus, it would be great if one could somehow prevent "misuse" of those beans, especially if one might not always recognize the mistake in time.
So my question is: Is there some way to define which scoped can be safely injected into which scope and then prevent beans with narrower scope from directly (without using Provider<T>) being injected into e.g. singleton beans?
It looks like this can be achieved fairly simple using a custom BeanPostProcessor. Within the postProcessBeforeInitialization, you can simply check the scope of the bean and the scope of all dependencies. Here is a simple example:
#Component
public class BeanScopeValidator implements BeanPostProcessor {
private final ConfigurableListableBeanFactory configurableBeanFactory;
#Autowired
public BeanScopeValidator(ConfigurableListableBeanFactory configurableBeanFactory) {
this.configurableBeanFactory = configurableBeanFactory;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
String beanScope = configurableBeanFactory.getBeanDefinition(beanName).getScope();
String[] dependenciesForBean = configurableBeanFactory.getDependenciesForBean(beanName);
for (String dependencyBeanName : dependenciesForBean) {
String dependencyBeanScope = configurableBeanFactory.getBeanDefinition(dependencyBeanName).getScope();
// TODO: Check if the scopes are compatible and throw an exception
}
return bean;
}
}
This example is still very basic and is not really convenient to use. Most prominently, it lacks the capability of defining which scope can be injected into which other scope. Thus I've created a more complete example here. Using this project, the following injections are allowed by default:
Singletons can be injected into everything
Everything can be injected into prototypes
AOP proxies can be injected into everything
Everything can be injected into beans of the same scope
If you want to allow a bean to be injected into another scope, it needs to be explicitly allowed by using a respective annotation:
#Bean
#Scope("prototype")
#InjectableInto("singleton")
MyBean getMyBean(){
//...
}
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).
This is an example from the Spring documentation, section 6.12.5:
#Configuration
public class ServiceConfig {
#Autowired
private AccountRepository accountRepository;
#Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
My question is: why must it happen that accountRepository is created before it's used by new TransferServiceImpl()? Offhand, I don't see how Spring could know that the second one depends on the first one being set up (unless it goes through the transferService() bytecode). Is it because something about the order in which Spring does things guarantees that the #Autowired variable is processed before the #Bean method could possibly be called? What is the processing order? What kinds of circumstances could cause Spring to process these out of order?
The reason I'm asking is that I have a case where something like this isn't working, i.e. the new is being executed with a null argument. Either the #Autowired variable is being set up too late, or it isn't set up at all (my guess is the latter, based on some log4j.logger.org.springframework.beans debugging output, but I'm not sure). The situation is of course much more complex--it's a largish application, and there are a few more #Autowired and #Bean definitions in the configuration class. Using #DependsOn hasn't helped. It will take a lot of time to narrow down the problem by deleting code until I can get a minimal example, but I wanted to see if I could get some insight into the problem by learning more details about how Spring processes things, before starting down the difficult code reduction path.
why must it happen that accountRepository is created before it's used
by new TransferServiceImpl()?
It doesn't. accountRepository may be seen to be null.
From the note in the documentation you linked (its more current version)
Make sure that the dependencies you inject that way are of the
simplest kind only. #Configuration classes are processed quite early
during the initialization of the context and forcing a dependency to
be injected this way may lead to unexpected early initialization.
Whenever possible, resort to parameter-based injection as in the
example above.
Also, be particularly careful with BeanPostProcessor and
BeanFactoryPostProcessor definitions via #Bean. Those should usually
be declared as static #Bean methods, not triggering the instantiation
of their containing configuration class. Otherwise, #Autowired and
#Value won’t work on the configuration class itself since it is being
created as a bean instance too early.
In summary, a Configuration class will end up being just another bean in the application context. As such, it will be processed by all registered BeanPostProcessor beans.
#Autowired is processed by AutowiredAnnotationBeanPostProcessor. Presumably, you're using AnnotationConfigApplicationContext which registers one automatically.
In your example, which is incomplete since
...but determining exactly where the autowired bean definitions are
declared is still somewhat ambiguous
However, we can assume some other configuration provided a bean definition for a AccountRepository bean. Once the application context instantiates the ServiceConfig bean, it can then post process it and inject #Autowired targets.
The only reason an #Autowired target could be null in a #Configuration bean instance is that you tried to read it before an AutowiredAnnotationBeanPostProcessor could process/inject it.
Consider a circular dependency. Take the #Configuration class in your snippet with an additional #ComponentScan of the following classes
#Component
class AccountRepository {
public AccountRepository(Foo foo) {}
}
#Component
class Foo {
public Foo(TransferService ts) {}
}
The #Configuration bean get initialized. AutowiredAnnotationBeanPostProcessor kicks off to process the accountRepository field. It looks for an AccountRepository bean and tries to initialize it. It needs a Foo bean to instantiate it (for constructor injection). It looks for a Foo bean and tries to initialize it. It needs a TransferService bean to instantiate it (for constructor injection). It looks for a TransferService bean and finds the #Bean factory method. It invokes it. The accountRepository hasn't been initialized yet, so remains null. You can verify this by putting a breakpoint in the #Bean method and browsing the stack trace.
Had you used a parameter injection as suggested in the quote above
Whenever possible, resort to parameter-based injection as in the example above.
Spring would've crashed and warned you
Caused by:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'accountRepository': Requested bean is
currently in creation: Is there an unresolvable circular reference?
That's the workaround I ended up doing
I can't currently explain this.
I just move accountRepository to the method's param and annotated with #Autowired to solve this problem. But I don't know why. I think the reason is about Spring's init order.
#Configuration
public class ServiceConfig {
#Bean
public TransferService transferService(#Autowired AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
I've moved my code from Spring's XML configuration to Java Configuration. I have everything working, but I have a question about how I implemented prototype beans - mainly, while what I'm doing works, is it the best way to do this? Somehow it just feels off!
I wrote the bean class this way:
#Component
#Scope("prototype")
public class ProtoBean {
...
}
Then to use the bean - this is the part that I'm just not sure about, although it does work:
#Component
public class BeanUser implements ApplicationContextAware {
ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext context)throws BeansException
{
this.context = context;
}
public void getProtoBean() {
ProtoBean protoBean = context.getBean(ProtoBean.class);
}
}
This gets me a prototyped bean, and in unit tests I just mocked the context, called setApplicationContext with the mock, and had the getBean call of the mock return a mock ProtoBean. So all is well.
I did this in the XML by using a factory, but that didn't seem to work too well, so this is where I ended up. But is there a way to do this without the context? Or just a better way?
Thanks!
I don't think is so much an issue of Spring XML vs Java-base configuration, but one of matching dependency scopes. Since Spring can only do dependency injection on the singleton-scoped bean at creation time, you have to lookup the prototype-scoped bean on demand. Of course the current bean-lookup approach works, but creates a dependency on the ApplicationContext. I can suggest a few other possibilities but the root of the issue is really what is involved in producing a ProtoBean, and what trade-offs should you accept.
You could make BeanUser itself prototype-scoped, which would allow you to wire in the ProtoBean as a member. Of course the trade-off is you now have the same problem on the clients of BeanUser, but sometimes that would not be a problem.
Another path could be using something like a singleton-scoped ProtoBeanFactory to provide ProtoBean instances, and hiding dependency lookups within the ProtoBeanFactory.
Finally, you could use a scoped-proxy bean to effectively hide the factory. It uses AOP to do this, and isn't always clear to others what sort of voodoo you have going. With XML you'd use <aop:scoped-proxy/> on the bean declaration. For annotations you'd use:
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")