about the order of spring bean initialing - java

I have a custom spring-boot starter project which is used by rest controller, the auto configuration class is used for creating several(according to config value in application.yml) Storage Context instance as spring singleton, so I have to create them dynamically in setBeanFactory method of BeanFactoryAware by :
#Import(StorageContextProperties.class)
public class StorageAutoConfiguration implements BeanFactoryAware {
......
#Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
......
StorageContextProperties config = beanFactory.getBean(StorageContextProperties.class);
config.getProfiles().stream().forEach(p -> {
......
((SingletonBeanRegistry) beanFactory).registerSingleton(profile.name, instance);
the problem is that the method is not been called before the controller #autowired event, so it will complain there is no StorageContext instance, I have also tried BeanFactoryPostProcessor and InitializingBean interface, neither of them works.
But, I notice if I just add some special #Bean method into the auto config class, let's say :
#Import(StorageContextProperties.class)
public class StorageAutoConfiguration implements BeanFactoryAware{
#Bean
public MethodValidationPostProcessor validationPostProcessor2() {
return new MethodValidationPostProcessor();
}
......
#Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
then the setBeanFactory() will be called before the controller, it seems that spring needs MethodValidationPostProcessor instance, so it created shared instance of singleton bean: StorageAutoConfiguration, also create StorageContextProperties instance and call setBeanFactory().
the code works if I add above #Bean method. well things also goes well in this way, but I don't like the style since I actually have no need for MethodValidationPostProcessor.
is there any elegant(without #Bean method) way to achieve it?
my requirements are :
before the controller creating event.
1 create StorageContextProperties ( it's a #ConfigurationProperties class by #Import)
2 some callback I can call registerSingleton() to create my Storage Context instances
Thanks!
[UPDATED1]
I still don't find any way to make it works, but I changed the code as :
#Configuration
#Import(StorageContextProperties.class)
#ConditionalOnProperty(prefix = "avalon.thiton.storage.config", name = "masterKey")
public class StorageAutoConfiguration implements BeanFactoryAware {
private static Logger log = LoggerFactory.getLogger(StorageAutoConfiguration.class);
#Bean
#Order(Ordered.LOWEST_PRECEDENCE)
#ConditionalOnMissingBean(MethodValidationPostProcessor.class)
public MethodValidationPostProcessor placeholder() {
return new MethodValidationPostProcessor();
}
......
It makes me feel..... better.

Hi I've had similar problem when I was trying to initialize custom beans using BeanFactoryAware interface - The #Configuration annotated class was like:
#Configuration
#EnableConfigurationProperties(SomeProps.class)
class MyConfiguration implements BeanFactoryAware {
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
...
...
}
#Bean
MyBean myBean(){
return new MyBean();
}
}
It did not run this configuration class code before MyBean was needed and NoSuchBeanDefinitionException were thrown.
To fix this I've changed BeanFactoryAware interface to extending AbstractBeanFactoryAwareAdvisingPostProcessor like that:
#Configuration
#EnableConfigurationProperties(SomeProps.class)
class MyConfiguration extends AbstractBeanFactoryAwareAdvisingPostProcessor {
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
...
...
}
#Bean
MyBean myBean(){
return new MyBean();
}
}

Related

Register bean (with a custom bean name) programmatically

My goal is to register bean (with a custom bean name) programmatically.
#ComponentScan({ "com.test" })
public class AppConfiguration {
//#Bean("test-bean")
#Bean
public Definition definition() {
return () -> Test.class;
}
}
#Named
#Scope("prototype")
public class Test extends DefinitionActor<?> {
....
}
Here, I use Akka and hence I have to go with #Scope("prototype").
I don't want to hard code the bean name to test-bean for some reason.
Hence, I am using the BeanPostProcessor.
#Component
public class BeanDefinitionRegistryPP implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
private Environment env;
#Override
public void setEnvironment(final Environment environment) {
this.env = environment;
}
#Override
public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry) throws BeansException {
}
I am getting AKKA error if I go with this way.
Caused by: akka.actor.ActorInitializationException: You cannot create an instance of [com.test.Test] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.
at akka.actor.ActorInitializationException$.apply(Actor.scala:181) ~[akka-actor_2.11-2.4.19.jar:na]
The bean post processor work with non-Akka project. Is there any other way to set the bean name programmatically even I use prototype scope.

Overriding beans in Java-based spring configuration hierarchy

Let's assume we have an application that can be customized for some customers. The application is using Java-based spring configuration (a.k.a. Java config) for dependency injection. The application consists of modules and their submodules. Each module and submodule has its own #Configuration class which is imported by parent configuration using #Import. This creates the following hierarchy:
MainConfig
----------+---------------- ....
| |
ModuleAConfig ModuleBConfig
|--------------------|
| |
SubModuleA1Config SubModuleA2Config
For example ModuleAConfig looks like this:
#Configuration
#Import({SubModuleA1Config.class, SubModuleA2Config.class})
public class ModuleAConfig {
// some module level beans
}
Let's say that SubModuleA1Config defines bean someBean of type SomeBean:
#Configuration
public class SubModuleA1Config {
#Bean
public SomeBean someBean() { return new SomeBean(); }
}
Now I want to customize the application for Customer1 (C1) - I want to use C1SomeBean (extending SomeBean) instead of SomeBean as someBean.
How can I achieve this with minimum duplication?
One of my ideas was to prepare alternative hierarchy with C1Config inheriting from MainConfig, C1ModuleAConfig from ModuleAConfig and C1SubModuleA1Config from SubModuleA1Config. C1SubModuleA1Config would override someBean() method returning C1SomeBean. Unfortunately with Spring 4.0.6 I get something like:
Overriding bean definition for bean 'someBean': replacing [someBean defined in class C1SubmoduleA1Config] with [someBean defined in class SubModuleA1Config]
and indeed SomeBean class is returned from context instead of C1SomeBean. This is clearly not what I want.
Note that you cannot override #Import extending configuration classes.
If you want to select which imports to use at runtime, you could use a #ImportSelector instead.
However, #Configuration classes are not more that spring (scoped) managed factories so as you already have a factory method for someBean you don't need to go even further:
#Configuration
public class SubModuleA1Config {
#Autowired
private Environment env;
#Bean
public SomeBean someBean() {
String customerProperty = env.getProperty("customer");
if ("C1".equals(customerProperty))
return new C1SomeBean();
return new SomeBean();
}
}
Update
Using a ImportSelector:
class CustomerImportSelector implements ImportSelector, EnvironmentAware {
private static final String PACKAGE = "org.example.config";
private static final String CONFIG_CLASS = "SubModuleConfig";
private Environment env;
#Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String customer = env.getProperty("customer");
return new String[] { PACKAGE + "." + customer + "." + CONFIG_CLASS };
}
#Override
public void setEnvironment(Environment environment) {
this.env = environment;
}
}
#Configuration
#Import(CustomerImportSelector.class)
public class ModuleAConfig {
// some module level beans
}
However, as every customer has a a separate package, consider also using #ComponentScan. This will pick the configuration class present and don't need a extra configuration property.
#Configuration
#ComponentScan(basePackages="org.example.customer")
public class SubModuleA1Config {
#Autowired
private CustomerFactory customerFactory;
#Bean
public SomeBean someBean() {
return customerFactory.someBean();
}
}
public interface CustomerFactory {
SomeBean someBean();
}
#Component
public class C1CustomerFactory implements CustomerFactory {
#Override
public SomeBean someBean() {
return new C1SomeBean();
}
}

Autowired property is null - Spring Boot Configuration

I am stuck with null values in an autowired property. I am hoping I could get some help.
We are using for the project spring-boot version 0.5.0.M6.
The four configuration files with beans are in one package and are sorted by "area":
Data source configuration
Global method security configuration (as we use Spring-ACL)
MVC configuration
Spring Security configuration
The main method that bootstraps everything is in the following file:
#EnableAspectJAutoProxy
#EnableSpringConfigured
#EnableAutoConfiguration(exclude = {
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
SecurityAutoConfiguration.class,
ThymeleafAutoConfiguration.class,
ErrorMvcAutoConfiguration.class,
MessageSourceAutoConfiguration.class,
WebSocketAutoConfiguration.class
})
#Configuration
#ComponentScan
public class IntegrationsImcApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = SpringApplication.run(
IntegrationsImcApplication.c lass, args);
}
}
The first file that holds the data source configuration beans is as follows (I have omitted some method body parts to make it more readable):
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#Configuration
public class RootDataSourceConfig
extends TomcatDataSourceConfiguration
implements TransactionManagementConfigurer {
#Override
public DataSource dataSource() {
return jpaDataSource();
}
public PlatformTransactionManager annotationDrivenTransactionManager() {
return jpaTransactionManager();
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
#Bean(name="jpaDataSource")
public DataSource jpaDataSource() {......}
#Bean(name = {"transactionManager","txMgr"})
public JpaTransactionManager jpaTransactionManager() {......}
#Bean(name = "entityManagerFactory")
public EntityManagerFactory jpaEmf() {......}
}
And here is the next configuration file, that depends on the data source from above. It has about 20 beans related to ACL configuration, but it fails on the firsts bean that uses data source:
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
public class RootGlobalMethodSecurityConfig
extends GlobalMethodSecurityConfiguration
implements Ordered {
#Autowired
public DataSource dataSource;
#Override
public int getOrder() {
return IntegrationsImcApplication.ROOT_METHOD_SECURITY_CO NFIG_ORDER;
}
#Bean
public MutableAclService aclService()
throws CacheException, IOException {
MutableJdbcAclService aclService = new MutableJdbcAclService(
dataSource, aclLookupStrategy(), aclCache());
aclService.setClassIdentityQuery("SELECT ##IDENTITY");
aclService.setSidIdentityQuery("SELECT ##IDENTITY");
return aclService;
}
...................................
}
Basically invoking aclService() throws an error as dataSource is null. We have tried ordering the configuration files by implementing the Ordered interface. We also tried using #AutoConfigureAfter(RootDataSourceConfig.class) but this did not help either. Instead of doing #Autowired on the DataSource we also tried injecting the RootDataSourceConfig class itself, but it was still null. We tried using #DependsOn and #Ordered on those beans but again no success. It seems like nothing can be injected into this configuration.
The console output at the startup is listing the beans in the order we want them, with data source being the first. We are pretty much blocked by this.
Is there anything weird or unique we are doing here that is not working? If this is as designed, then how could we inject data source differently?
Repo: github
Eager initialization of a bean that depends on a DataSource is definitely the problem. The root cause is nothing to do with Spring Boot or autoconfiguration, but rather plain old-fashioned chicken and egg - method security is applied via an aspect which is wrapped around your business beans by a BeanPostProcessor. A bean can only be post processed by something that is initialized very early. In this case it is too early to have the DataSource injected (actually the #Configuration class that needs the DataSource is instantiated too early to be wrapped properly in the #Configuration processing machinery, so it cannot be autowired). My proposal (which only gets you to the same point with the missing AuthenticationManager) is to declare the GlobalMethodSecurityConfiguration as a nested class instead of the one that the DataSource is needed in:
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
protected static class ActualMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Autowired
#Qualifier("aclDaoAuthenticationProvider")
private AuthenticationProvider aclDaoAuthenticationProvider;
#Autowired
#Qualifier("aclAnonymousAuthenticationProvider")
private AnonymousAuthenticationProvider aclAnonymousAuthenticationProvider;
#Autowired
#Qualifier("aclExpressionHandler")
private MethodSecurityExpressionHandler aclExpressionHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(aclDaoAuthenticationProvider);
auth.authenticationProvider(aclAnonymousAuthenticationProvider);
}
#Override
public MethodSecurityExpressionHandler createExpressionHandler() {
return aclExpressionHandler;
}
}
i.e. stick that inside the RootMethodSecurityConfiguration and remove the #EnableGlobalMethodSecurity annotation from that class.
I might have resolved the problem.
GlobalMethodSecurityConfiguration.class has the following setter that tries to autowire permission evaluators:
#Autowired(required = false)
public void setPermissionEvaluator(List<PermissionEvaluator> permissionEvaluators) {
....
}
And in my case the aclPermissionEvaluator() bean needs aclService() bean, which in turn depends on another autowired property: dataSource. Which seems not to be autowired yet.
To fix this I implemented BeanFactoryAware and get dataSource from beanFactory instead:
public class RootMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration implements BeanFactoryAware {
private DataSource dataSource;
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.dataSource = beanFactory.getBean("dataSource", DataSource.class);
}
....
}
After this, other exception showed up, whereSecurityAutoConfiguration.class is complaining about missing AuthenticationManager, so I just excluded it from #EnableAutoConfiguration. I am not sure if its ideal, but I have custom security configuration, so this way everything works ok.

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.

What is the spring way to autowire factory created instances?

I have a controller which is supposed to create version dependend instances (currently not implemented).
#Controller
public class ReportController {
#Autowired
private ReportCompFactory reportCompFactory;
public ModelAndView getReport() {
I_Report report = reportCompFactory.getObject();
^^^^^<- no autowiring in this instance
}
...
}
The Factory looks like this:
#Component
public class ReportCompFactory implements FactoryBean<I_Report> {
#Override
public I_Report getObject() throws BeansException {
return new ReportComp();
}
#Override
public Class<?> getObjectType() {
return I_Report.class;
}
#Override
public boolean isSingleton() {
return false;
}
}
The created instances fields (#Autowired annotated ) are not set.
What should I do, is FactoryBean the right interface to implement?
I would prefer a solution which doesn't involve xml-configurations.
The component itself:
ReportComp implements I_Report {
#Autowired
private ReportDao reportDao;
^^^^^^^<- not set after creation
...
}
}
Spring doesn't perform autowiring if you create your objects. Here are a few options
define the bean to be of scope prototype - this will make the factory redundant (this is applicable in case you simply want instantiation in the factory)
inject the ReportDao in the factory, and set it to the ReportComp via a setter
inject ApplicationContext in the factory and do ctx.getAutowireCapableBeanFactory().autowireBean(instance)

Categories