My problem is that I need to get an authentication token before I make a call to an API. The api must be called on start up of application. However I am having trouble in that both calls are made at the same time thus creating an error of not having the authentication token before making the api call.
I basically need the tokenUtilityClass to create the token before instantiating the Paypal class. I have tried the #Preconstruct and the #Lazy annotation but neither are working for me.
I have a boolean value of validToken which returns true once the authentication token has been created.
This is what my Springboot configuration file looks like
#Autowired
private TokenUtilityClass tokenUtilityClass;
#Bean ResourceConfig resourceConfig() {
return new ResourceConfig().registerClasses(Version1Api.class); }
#PostConstruct
public void postConstruct() {
tokenUtilityClass.tokenTimer();
}
#DependsOn("TokenUtilityClass")
#ConditionalOnProperty(name ="tokenUtilityClass.validToken", havingValue ="true")
#Lazy
public Paypal eventPublisherBean() {
return new Paypal();
}
Would anyone have any ideas about initializing the Paypal class only after the authentication token has been generated.
All help would be appreciated
What you declared cannot work :
#DependsOn("TokenUtilityClass")
#ConditionalOnProperty(name ="tokenUtilityClass.validToken", havingValue ="true")
#Lazy
because tokenUtilityClass.validToken is not a property but a bean method while
ConditionalOnProperty expects to a property.
Would anyone have any ideas about initializing the Paypal class only
after the authentication token has been generated.
Concretely, you cannot achieve it straightly with Spring because beans are instantiated as soon as these are required by other beans in their dependencies.
But declaring just that :
#Bean
#Lazy
public Paypal eventPublisherBean() {
return new Paypal();
}
could work if that bean is never used as a eager loaded dependency of other beans but rather required at the good time for you : after the token was retrieved.
To achieve that you have two ways :
1) don't use dependency injection for PayPal instance but use exclusively bean factory.
Not tested but it sounds conform to the #Lazy javadoc :
If this annotation is not present on a #Component or #Bean definition,
eager initialization will occur. If present and set to true, the #Bean
or #Component will not be initialized until referenced by another bean
or explicitly retrieved from the enclosing BeanFactory.
So inject BeanFactory in the bean responsible to get the token and use that factoryto initialize the bean after the token was retrieved.
Something like that :
#Service
public class TokenUtility {
private BeanFactory factory;
public TokenUtility(BeanFactory factory){
this.factory = factory;
}
public Token retrieveToken(){
// call service to get the token
//...
// init the bean now -(it will also invoke its #PostConstruct method)
PayPal payPal = beanFactory.getBean(PayPal.class);
}
}
2) A straighter alternative to the BeanFactory is declaring the bean as a lazy dependency :
#Service
public class TokenUtility {
#Lazy
#Autowired
Paypal paypal;
public Token retrieveToken(){
// call service to get the token
//...
// init the bean by invoking any method on that
paypal.init();
}
}
I created just now a very basic project (Java 11) to test that :
https://github.com/ebundy/lazy-init-spring-example/.
When you execute spring-boot:run you should get :
2020-02-26 09:38:05.883 INFO 7002 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 682 ms
TokenUtility init with status code=200
PayPal init
You are looking for #Order annotation.
#Component
#Order(1)
public class First {
#PostConstruct
public void init() {
System.out.println("first");
}
}
#Component
#Order(2)
public class Second {
#PostConstruct
public void init() {
System.out.println("second");
}
}
#Order works with both types of declarations: #Component/#Service/#Repository/#Controller and with #Bean
However, if you have a clear dependency of one bean on the other bean, then use #DependsOn annotation.
#Component("first")
public class First {
public First() { // inject dependencies here if any
System.out.println("The very first thing")
}
#PostConstruct
public void init() {
System.out.println("first");
}
}
#Component
#DependsOn({"first"})
public class Second {
#PostConstruct
public void init() {
System.out.println("second");
}
}
You can find more info here
It looks like you are sharing the code inside class with #Configuration annotation. It seems you mark TokenUtilityClass with #Component (or similar) annotation.
The issue with that is that #PostConstruct is connected to your Configuration class, not to TokenUtilityClass. I suggest moving #PostConstruct method to TokenUtilityClass.
You can just have token refreshing functionality and make sure token exists upon creating another bean.
#Component
class TokenUtilityClass {
private String freshToken;
private Instant freshUntil;
public String getToken() {
if (freshUntil != null && Instant.now().isBefore(freshUntil)) {
refreshToken();
}
return freshToken;
}
public void refreshToken() {
// do something
}
}
#Configuration
class ConfigurationClass {
#Bean public Paypal eventPublisherBean(TokenUtilityClass tokenUtilityClass) {
String token = tokenUtilityClass.getToken();
// do something with token and return your bean
return new Paypal();
}
}
Related
I am new at spring MVC framework and i am currently working in a web application that uses a session scoped bean to control some data flow.
I can access these beans in my application context using #Autowired annotation without any problem in the controllers. The problem comes when I use a class in service layer that does not have any request mapping (#RequestMapping, #GetMapping nor #PostMapping) annotation.
When I try to access the application context directly or using #Autowired or even the #Resource annotation the bean has a null value.
I have a configuration class as follow:
#Configuration
#EnableAspectJAutoProxy
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, basePackages = "com.quantumx.nitididea.NITIDideaweb.repository")
public class AppConfig implements WebMvcConfigurer {
#Bean (name = "lastTemplate")
#SessionScope
public LastTemplate getlastTemplate() {
return new LastTemplate();
}
//Some extra code
}
The POJO class is defined as :
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I have a Test class that is annotated as service and does not have any request mapping annotated method:
//#Controller
#Service
public class Test {
// #Autowired
// private ApplicationContext context;
// #Autowired
#Resource(name = "lastTemplate")
public LastTemplate lastTemplate;
// #Autowired
// public void setLastTemplate(LastTemplate lastTemplate) {
// this.lastTemplate = lastTemplate;
// }
public Test() {
}
// #RequestMapping("/test")
public String testing() {
// TemplateForma last = (TemplateForma) context.getBean("lastInsertedTemplate");
// System.out.println(last);
System.out.println(lastTemplate);
// System.out.println(context.containsBean("lastTemplate"));
// System.out.println(context.getBean("lastTemplate"));
System.out.println("Testing complete");
return "Exit from testing method";
// return "/Messages/Success";
}
}
As you can see, there is a lot of commented code to show all the ways i have been trying to access my application context, using an Application context dependency, autowiring, declaring a resource and trying with a request mapping. The bean is null if no controller annotation and request mapping method is used and throws a java null pointer exception when I use the context getBean() methods.
Finally I just test my class in a controller that i have in my app:
#RequestMapping("/all")
public String showAll(Model model) {
Test test = new Test();
test.testing();
return "/Administrator/test";
}
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked. How can access my application context in a service class without mapping a request via controller?
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked
It should have worked in this case.
How can access my application context in a service class without mapping a request via controller?
Try one of these :-
#Autowired private ApplicationContext appContext;
OR
Implement ApplicationContextAware interface in the class where you want to access it.
Edit:
If you still want to access ApplicationContext from non spring managed class. Here is the link to article which shows how it can be achieved.
This page gives an example to get spring application context object with in non spring managed classes as well
What worked for me is that session scoped bean had to be removed in the application configuration declaration and moved to the POJO definition as follows:
#Component
#SessionScope
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I just call the bean using #Autowired annotation.
I am creating a REST service in Spring boot. I am creating a bean in config class and trying to use in service class by auto wiring, but I am always getting null, I have tried in constructor injection as well but not working. Below is the code,
Main app
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
REST controller
#RestController
#RequestMapping("/v1")
public class RestController {
#Autowired
private Service service;
Service
#Service
public class ServiceImpl implements Service {
//This is the bean
#Autowired
private Record record;
public ServiceImpl() { //-----------------> tried injecting in constructor as well
System.out.println(record); //-------------------> null
}
Config class
#Configuration
public class AppConfig {
#Bean
public Record record() {
return new Record("test");
}
}
I noted whenever I remove the record() from config class I get below error
required a bean of type 'com.ns.service.Record' that could not be found
And after adding the method the error is not reported but null is returned, which indirectly means record() is considered as returning the required bean.
I can't find what I am doing wrong please advise.
Project folder structure
I think you're doing everything right conceptually
Spring creates an object first and only after that injects the values (technically done in bean post processors):
So try this:
#Service
public class ServiceImpl implements Service {
//This is the bean
#Autowired
private Record record;
public ServiceImpl() {
// here the record is null - not injected yet
System.out.println(record);
}
#PostConstruct
public void checkThisOut() {
// here print the record
}
You say you've tried constructor injection as well - it should work because spring has to inject something into the constructor of a bean (ServiceImpl) or fail. Please show the code snippet
One this that might be wrong in some level (although it doesn't sound like this from your description) is that you have to put all the #Configuration/#Service annotated classes in the package that is the same or underneath the package where you've created the main class annotated with #SpringBootApplication annotation. It instructs spring boot where to look for the beans.
So make sure your classes obey this rule...
Suppose I have a lazy Spring managed bean MyBean within a custom scope like this:
#Configuration
public class MyConfiguration {
#Scope("custom")
#Lazy
#Bean
MyBean myBean () {
return new MyBean();
}
}
and another Spring managed bean:
#Component
class MyBeanCounter{
void checkIfMyBeanIsInstantiated () {
// Check if there is an instance of MyBean within Spring context
}
}
Within checkIfMyBeanIsInstantiated I want to check if there is an instance of MyBean within Spring context without triggering the bean creation.
The obvious idea is to inject MyBean like this:
#Component
class MyBeanCounter {
#Autowired
MyBean myBean;
void checkIfMyBeanIsInstantiated () {
if (myBean != null) {// this doesn't trigger the bean creation
// there is an instance
}
}
}
The problem with the above solution is that I have to refresh the MyBeanCounter every time the MyBean instance changes according to it's custom scope.
The above solution doesn't work because #Autowired MyBean myBean; does instantiate the bean. Replacing it with #Lazy #Autowired MyBean myBean; still doesn't work since I end up with an injected proxy.
Is there any solution?
Since you seem to not actually need or want the instance in the check, just do
#Configuration
public class MyConfiguration {
#Scope("custom")
#Lazy
#Bean
MyBean myBean (MyBeanCounter counter) {
counter.setBeanInstantiated();
return new MyBean();
}
}
#Component
class MyBeanCounter {
#Setter
private boolean beanInstantiated;
void checkIfMyBeanIsInstantiated () {
if (beanInstantiated) {
// there is an instance
}
}
}
#Component
class MyBeanCounter {
#Autowired
private ApplicationContext appContext;
void checkIfMyBeanIsInstantiated () {
String[] beans= appContext.getBeanDefinitionNames();
// now loop the beans and if it is Null it has not been Instantiated
// beans will contain all the beans name
}
}
For the "refresh" issue, make sure your scope uses a proxy which will help you retrieve the correct bean instance. You can add proxyMode = ScopedProxyMode.TARGET_CLASS to your #Scope annotation. This way you won't need to reload the MyBeanCounter so that it uses the new instance.
As for checking if the bean has bean initialized, you can directly check if it's different to null. A non initialized bean is equals to null.
So your checkIfMyBeanIsInstantiated could be as follows:
void checkIfMyBeanIsInstantiated () {
if(myBean == null) {
// Not initialized
return;
}
// Initialized
}
Also make sure you add required = false to your #Autowired annotation:
#Autowired(required = false)
MyBean myBean;
If required is not set to false (it's default value is true), myBean will be instantiated right away because Spring see it as a required component for MyBeanCounter. By setting it to false and with using #Lazy, Spring will only initialize it when it's called.
But make sure you always test it for null to avoid a NullPointerException.
I stuck with a simple refactoring from plain Java to Spring. Application has a "Container" object which instantiates its parts at runtime. Let me explain with the code:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
public void load() {
// repeated several times depending on external data/environment
RuntimeBean beanRuntime = createRuntimeBean();
runtimeBeans.add(beanRuntime);
}
public RuntimeBean createRuntimeBean() {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
}
}
Basically, during load container asks some external system to provide him information about number and configuration of each RuntimeBean and then it create beans according to given spec.
The problem is: usually when we do in Spring
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
Container container = (Container) context.getBean("container");
our object is fully configured and have all dependencies injected. But in my case I have to instantiate some objects which also needs dependency injection after I execute load() method.
How can I achieve that?
I am using a Java-based config. I already tried making a factory for RuntimeBeans:
public class BeanRuntimeFactory {
#Bean
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
Expecting #Bean to work in so called 'lite' mode. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html Unfortunately, I found no difference with simply doing new RuntimeBean();
Here is a post with a similar issue: How to get beans created by FactoryBean spring managed?
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Configurable.html but it looks like a hammer in my case.
I also tried ApplicationContext.getBean("runtimeBean", args) where runtimeBean has a "Prototype" scope, but getBean is an awful solution.
Update 1
To be more concrete I am trying to refactor this class:
https://github.com/apache/lucene-solr/blob/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java
#see #load() method and find "return create(cd, false);"
Update 2
I found quite interesting thing called "lookup method injection" in spring documentation:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-lookup-method-injection
And also an interesting jira ticket https://jira.spring.io/browse/SPR-5192 where Phil Webb says https://jira.spring.io/browse/SPR-5192?focusedCommentId=86051&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-86051 that javax.inject.Provider should be used here (it reminds me Guice).
Update 3
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html
Update 4
The issue with all these 'lookup' methods is they don't support passing any arguments.. I also need to pass arguments as I would do with applicationContext.getBean("runtimeBean", arg1, arg2). Looks like it was fixed at some point with https://jira.spring.io/browse/SPR-7431
Update 5
Google Guice have a neat feature for it called AssistedInject. https://github.com/google/guice/wiki/AssistedInject
Looks like I found a solution. As I am using java based configuration it is even simpler than you can imagine. Alternative way in xml would be lookup-method, however only from spring version 4.1.X as it supports passing arguments to the method.
Here is a complete working example:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
private RuntimeBeanFactory runtimeBeanFactory;
public void load() {
// repeated several times depending on external data/environment
runtimeBeans.add(createRuntimeBean("Some external info1"));
runtimeBeans.add(createRuntimeBean("Some external info2"));
}
public RuntimeBean createRuntimeBean(String info) {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
return runtimeBeanFactory.createRuntimeBean(info);
}
public void setRuntimeBeanFactory(RuntimeBeanFactory runtimeBeanFactory) {
this.runtimeBeanFactory = runtimeBeanFactory;
}
}
public interface RuntimeBeanFactory {
RuntimeBean createRuntimeBean(String info);
}
//and finally
#Configuration
public class ApplicationConfiguration {
#Bean
Container container() {
Container container = new Container(beanToInject());
container.setBeanRuntimeFactory(runtimeBeanFactory());
return container;
}
// LOOK HOW IT IS SIMPLE IN THE JAVA CONFIGURATION
#Bean
public BeanRuntimeFactory runtimeBeanFactory() {
return new BeanRuntimeFactory() {
public RuntimeBean createRuntimeBean(String beanName) {
return runtimeBean(beanName);
}
};
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
RuntimeBean runtimeBean(String beanName) {
return new RuntimeBean(beanName);
}
}
class RuntimeBean {
#Autowired
Container container;
}
That's it.
Thanks everyone.
i think that your concept is wrong by using
RuntimeBean beanRuntime = createRuntimeBean();
you are bypassing Spring container and resorting to using regular java constructor therefore any annotations on factory method are ignored and this bean is never managed by Spring
here is the solution to create multiple prototype beans in one method, not pretty looking but should work, I autowired container in RuntimeBean as proof of autowiring shown in log also you can see in log that every bean is new instance of prototype when you run this .
'
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Container container = (Container) context.getBean("container");
container.load();
}
}
#Component
class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
#Autowired
ApplicationContext context;
#Autowired
private ObjectFactory<RuntimeBean> myBeanFactory;
public void load() {
// repeated several times depending on external data/environment
for (int i = 0; i < 10; i++) {
// **************************************
// COMENTED OUT THE WRONG STUFFF
// RuntimeBean beanRuntime = context.getBean(RuntimeBean.class);
// createRuntimeBean();
//
// **************************************
RuntimeBean beanRuntime = myBeanFactory.getObject();
runtimeBeans.add(beanRuntime);
System.out.println(beanRuntime + " " + beanRuntime.container);
}
}
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
// #Component
class RuntimeBean {
#Autowired
Container container;
} '
A simple approach:
#Component
public class RuntimeBeanBuilder {
#Autowired
private ApplicationContext applicationContext;
public MyObject load(String beanName, MyObject myObject) {
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext) applicationContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
if (beanRegistry.containsSingleton(beanName)) {
return beanRegistry.getSingleton(beanName);
} else {
beanRegistry.registerSingleton(beanName, myObject);
return beanRegistry.getSingleton(beanName);
}
}
}
#Service
public MyService{
//inject your builder and create or load beans
#Autowired
private RuntimeBeanBuilder builder;
//do something
}
Instead of using SingletonBeanRegistry you can use this:
BeanFactory beanFactory = configContext.getBeanFactory();
Anyway SingletonBeanBuilder extends HierarchicalBeanFactory and HierarchicalBeanFactory extends BeanFactory
You don't need the Container because all of the runtime objects should be created, held and managed by ApplicationContext. Think about a web application, they are much the same. Each request contains external data/environment info as you mentioned above. What you need is a prototype/request scoped bean like ExternalData or EnvironmentInfo which can read and hold runtime data through a static way, let's say a static factory method.
<bean id="externalData" class="ExternalData"
factory-method="read" scope="prototype"></bean>
<bean id="environmentInfo" class="EnvironmentInfo"
factory-method="read" scope="prototype/singleton"></bean>
<bean class="RuntimeBean" scope="prototype">
<property name="externalData" ref="externalData">
<property name="environmentInfo" ref="environmentInfo">
</bean>
If you do need a container to save the runtime objects, code should be
class Container {
List list;
ApplicationContext context;//injected by spring if Container is not a prototype bean
public void load() {// no loop inside, each time call load() will load a runtime object
RuntimeBean bean = context.getBean(RuntimeBean.class); // see official doc
list.add(bean);// do whatever
}
}
Official doc Singleton beans with prototype-bean dependencies.
It is possible to register beans dynamically by using BeanFactoryPostProcesor. Here you can do that while the application is booting (spring's application context has been initialized). You can not register beans latest, but on the other hand, you can make use of dependency injection for your beans, as they become "true" Spring beans.
public class DynamicBeansRegistar implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (! (beanFactory instanceof BeanDefinitionRegistry)) {
throw new RuntimeException("BeanFactory is not instance of BeanDefinitionRegistry");
}
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// here you can fire your logic to get definition for your beans at runtime and
// then register all beans you need (possibly inside a loop)
BeanDefinition dynamicBean = BeanDefinitionBuilder.
.rootBeanDefinition(TheClassOfYourDynamicBean.class) // here you define the class
.setScope(BeanDefinition.SCOPE_SINGLETON)
.addDependsOn("someOtherBean") // make sure all other needed beans are initialized
// you can set factory method, constructor args using other methods of this builder
.getBeanDefinition();
registry.registerBeanDefinition("your.bean.name", dynamicBean);
}
#Component
class SomeOtherClass {
// NOTE: it is possible to autowire the bean
#Autowired
private TheClassOfYourDynamicBean myDynamicBean;
}
As presented above, you can still utilize Spring's Dependency Injection, because the post processor works on the actual Application Context.
My project is currently protected using Apache Shiro, and would like to add Shiro annotations for a cleaner source code.
Apache Shiro asks you to include DefaultAdvisorAutoProxyCreator in order for Spring AOP to detect it.
My configuration is as follows:
#Configuration
#ComponentScan("com.mcac0006.flip")
#EnableWebMvc
public class AppContextConfiguration {
#Autowired
private JdbcRealm shiroRealm;
#Bean(name="shiroFilter")
public ShiroFilterFactoryBean getShiroFilter() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(getSecurityManager());
return bean;
}
#Bean(name="securityManager")
public DefaultSecurityManager getSecurityManager() {
return new DefaultWebSecurityManager(shiroRealm);
}
#Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor a = new AuthorizationAttributeSourceAdvisor();
a.setSecurityManager(getSecurityManager());
return a;
}
#Bean
#DependsOn("authorizationAttributeSourceAdvisor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
final DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
return defaultAdvisorAutoProxyCreator;
}
}
In this example shiroRealm remains null and is causing all its dependants to fail. If I comment-out the last method defaultAdvisorAutoProxyCreator(), shiroRealm is instantiated just fine.
What am I overlooking??
Thanks guys.
DefaultAdvisorAutoProxyCreator is a BeanPostProcessor. Beans of that type are instantiated and initialized before other beans. However, in this case, your #Bean method is also annotated with #DependsOn in which case the bean depends on another bean to be initialized before its own initialization can take place.
So before defaultAdvisorAutoProxyCreator can run, authorizationAttributeSourceAdvisor must be run. That method now depends on getSecurityManager(), so that must also be ran first. When it is ran, your #Autowired field isn't processed yet because the BeanPostProcessor beans haven't all been initialized yet. (A BeanPostProcessor processes injections for #Autowired.)
If you had a completely separate bean and checked the state of the shiroRealm, you would see it as non-null at that point.