How to instantiate a service class in a non component class - java

I have my service class which does a post call. I would like to instantiate that bean/ autowire it to create a object in another class which is not a component or configuration class.
#Service
public class SavePayload {
// Rest Post Call implementation
}
public class PayloadRecord
implements Record {
private String payload;
PayloadProcessor payloadProcessor = new PayloadProcessor();
public PayloadRecord(String payload) {
this.payload = payload;
}
#SneakyThrows
#Override
public boolean isValid() throws ValidationException {
payloadProcessor.savePayload(payload);
return true;
}
#Override
public byte[] getBytes(Charset charset) {
return payload.getBytes(StandardCharsets.UTF_8);
}
#Override
public String getID() {
return payload;
}
#Override
public String toString() {
return payload;
}
private static class PayloadProcessor {
#Autowired
private SavePayload savePayload;
}
}
I'm using a template which will do the record processing. As soon as I got message received I'm assigning it to Payload in Payload Record which is non component class. I would like to initialize the SavePayload service. Save payload service is returning null.

Create an application context aware class so you can get the current context, something like:
#Component
public class ContextAwareClass implements ApplicationContextAware {
private static ApplicationContext ctx;
public static ApplicationContext getApplicationContext() {
return ctx;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
ctx = applicationContext;
}
}
Then, just get the context and get the bean like:
public class YourRegularNoSpringComponentClass {
public void doSomething() {
System.out.println(ContextAwareClass
.getApplicationContext()
.getBean("savePayload")
);
}
}
Above will print the bean if it exist in your context. In your case you would simple use it rather than print it.
Hope this helps!

You will have to create an instance of ApplicationContext
You can explore
AnnotationConfigApplicationContext applicationContext= new AnnotationConfigApplicationContext();
and then use.
SavePayload savePayload = applicationContext.getBean("savePayload");

Related

Autowired object populated using ThreadLocalTargetSource does not populate for every class

My Spring Boot application implements the TenantStore example for storing data in ThreadLocalTargetSource detailed in this link
#Bean(destroyMethod = "destroy")
public ThreadLocalTargetSource threadLocalTenantStore() {
ThreadLocalTargetSource result = new ThreadLocalTargetSource();
result.setTargetBeanName("tenantStore");
return result;
}
The working example allows for the TenantStore object to be set and injected by the Spring Framework. My version of the TenantFilter class described in that article sets the properties of the TenantStore object whenever a Servlet request is made
#Autowired
private TenantStore tenantStore;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
String token = (String) request.getAttribute(ACCESS_TOKEN_VALUE);
if (token != null) {
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
if (oAuth2AccessToken.getAdditionalInformation() != null) {
String tenantName = (String) oAuth2AccessToken.getAdditionalInformation().get("tenant");
storeTenantInThread(tenantName);
}
}
}
chain.doFilter(request, response);
} catch (ResourceNotFoundException e) {
log.error(e.getMessage());
} finally {
clearTenant();
}
}
private void storeTenantInThread(String tenantName) {
tenantStore.setName(tenantName);
}
private void clearTenant() {
tenantStore.clear();
}
I then have a number of services where TenantStore is autowired and in each of these services the TenantStore contains the information that was populated in the doFilter() method. Except for one class. For some reason the properties of the TenantStore in this class are still null. The name of the class affected is MyCacheService and the architecture is as follows:
#RestController
#RequestMapping("/here")
public class MyController {
#Autowired
private MyService myService
#GetMapping
public ResponseEntity myGetMethod(#RequestParam("text") String text) {
myService.myMethod(text);
return new ResponseEntity(Http.OK);
}
}
#Service
public class MyService {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyOtherService myOtherService;
public void myMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
myOtherService.myOtherMethod(text);
}
}
#Service
public class MyOtherService {
#Autowired
private TenantStore tenantStore;
#Autowired
private Map<String, MyComponent> myComponents;
public void myOtherMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
MyComponent useThisComponent = myComponents.get("componentName");
useThisComponent.myComponentMethod(text);
}
}
#Component("componentName")
public class MyComponent {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyCacheService myCacheService;
public void myComponentMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
entityAliasCacheService.myCacheMethod(String text);
}
}
#Service
public class MyCacheService {
#Autowired
private TenantStore tenantStore;
public void myCacheMethod(String text) {
System.out.println(tenantStore.getName()); //DOES NOT WORK - tenantStore object is not null but the name property is
}
}
From what I can guess, for some reason the TenantStore in MyCacheService is being populated in a different thread, though I've no idea why.
I noticed similar behaviour. I fixed the issue by adding a bean dependancy
#Service
#DependsOn("proxiedThreadLocalTargetSource") // asks Spring to first load proxy bean
public class MyCacheService {
where proxiedThreadLocalTargetSource bean is defined like in the OP's example -
#Primary
#Bean(name = "proxiedThreadLocalTargetSource")
public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
ProxyFactoryBean result = new ProxyFactoryBean();
result.setTargetSource(threadLocalTargetSource);
return result;
}
So, by adding the dependancy, Spring knows that it should load MyCacheService bean after the proxiedThreadLocalTargetSource. Without this dependancy, I noticed that TenantStore got injected instead of the proxy bean.
Getting instance of TenantStore from org.springframework.context.ApplicationContext
First implement ApplicationContextAware like as below
#Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext context() {
return context;
}
}
And your MyCacheService Will be like this:
public class MyCacheService {
public void myCacheMethod(String text) {
TenantStore tenantStore = ApplicationContextUtil.context().getBean(TenantStore.class);
System.out.println(tenantStore.getName());
}
}

Custom AsyncUncaughtExceptionHandler

Using the following configuration for #Async methods :
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
//Just to experiment
return new SimpleAsyncTaskExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
Is there a way to "get" the ability to autowire (or similar) Services ?
I'd like to use such Services to record errors in database and use common services.
Non working sample :
#Component //seems pointless
public class CustomAsyncExceptionHandler extends ServiceCommons implements AsyncUncaughtExceptionHandler {
protected Logger LOG = LoggerFactory.getLogger(this.getClass());
#Autowired
private MyService myService; //always null
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
//null pointer !
myService.doSomething(throwable);
}
}
When using not in #Async methods, #ControllerAdvice global exception handler allows #Autowired fields. Why not in this case ? Is this because of async thread management ?
I just faced this problem and solved this way:
#Configuration
#EnableAsync
public class MyAsyncConfigurer implements AsyncConfigurer {
private CustomAsyncExceptionHandler customAsyncExceptionHandler;
//...
//other code here
//...
#Autowired
public void setCustomAsyncExceptionHandler(CustomAsyncExceptionHandler customAsyncExceptionHandler) {
this.customAsyncExceptionHandler = customAsyncExceptionHandler;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return this.customAsyncExceptionHandler;
}
}
Custom async exception handler annotated with #Component:
#Component
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private MyMailService myMailService;
#Autowired
public void setMyMailService(MyMailService myMailService) {
this.myMailService= myMailService;
}
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
myMailService.sendMailToAdmin(throwable, method.getName());
}
}
IoC injects both, myMailService and customAsyncExceptionHandler, correctly whith no errors.
I don't think my solution is the most elegant, but tell me what you think. the idea is to bypass the automatic injection mechanism by using ApplicationContextAware interface. My first attempt was to make my AsyncUncaughtExceptionHandler implementing class to also implement ACAware. But that didn't work. Somehow this class, even annotated as Component or Service seems to live a bit outside the Spring environment. So I did this:
#Configuration
#EnableAsync
public class DemoAsyncConfigurer implements AsyncConfigurer, ApplicationContextAware {
private ApplicationContext applicationContext;
And in the same class:
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
DemoAsyncExceptionHandler demoHandler = new DemoAsyncExceptionHandler(); // you can't add the parameter in this constructor, for some reason...
demoHandler.setApplicationContext(applicationContext);
return demoHandler;
}
/**
*
* {#inheritDoc}
*/
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
My DemoAsyncExceptionHandler has the following:
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void handleUncaughtException(Throwable throwable, Method method, Object... params) {
UserService userService = this.applicationContext.getBean("userService", UserService.class);
// call userService method
That worked! Hope I have helped

Spring Bean Factory Using Class Name

I have an interface/implementation like so:
public interface Processor {
void processMessage(Message m);
}
#Component
public class FooAProcessor implements Processor {
private FooA fooA;
public FooAProcessor(FooA fooA) {
this.fooA = fooA;
}
#Override
public void processMessage(Message m) {
//do stuff
}
}
#Component
public class FooBProcessor implements Processor {
private FooA fooA;
public FooBProcessor(FooA fooA) {
this.fooA = fooA;
}
#Override
public void processMessage(Message m) {
//do stuff
}
}
The FooA bean is simple, like this:
#Component
public class FooA {
//stuff
}
And the message class:
public class Message {
private Class clazz;
}
I am pulling messages off a queue. I need to provide a concrete Processor to handle the different types of messages appropriately. Here's the message receiver:
public class MessageReceiver {
public void handleMessage(Message m) {
Processor processor = //get concrete implementation from Message clazz
processor.processMessage(m);
}
}
How exactly can I use the class name/object to define a concrete implementation of Processor?
My first thought was to develop some sort of a factory that takes in a class and provides the concrete implementation. Something like this:
#Component
public class ProcessorFactory {
private FooAProcessor fooAProcessor;
private FooBProcessor fooBProcessor;
public ProcessorFactory(FooAProcessor fooAProcessor, FooBProcessor fooBProcessor) {
this.fooAProcessor = fooAProcessor;
this.fooBProcessor = fooBProcessor;
}
public Processor getFactory(Class clazz) {
if(clazz.isAssignableFrom(FooAProcessor.class)) {
return fooAProcessor;
}
}
}
Or to use the application context like this:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getBean(clazz);
Is this the best way to go about this problem? Is there a better practice?
You can inject ApplicationContext into your factory and get beans from there:
#Component
public class Factory {
#Autowired ApplicationContext applicationContext;
public Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
}
Or you can put your processors into map and get them from it:
#Component
public class ProcessorFactory {
private final Processor fooAProcessor;
private final Processor fooBProcessor;
private final Map<Class<T extends Processor>, Processor> beanMap;
public ProcessorFactory (Processor fooAProcessor, Processor fooBProcessor) {
this.fooAProcessor = fooAProcessor;
this.fooBProcessor = fooBProcessor;
this.beanMap = new HashMap(2);
}
#PostConstruct
public void init() {
beanMap.put(FooAProcessor.class, fooAProcessor);
beanMap.put(FooBProcessor.class, fooBProcessor);
}
public Processor getProcessor(Class<T extends Processor> clazz) {
return beanMap.get(clazz);
}
}
I recommend to not rely on class when working with spring context but use beanNames instead.

How to inject ANY information about test in spring testing?

I would like some of my beans know something about test. SOMETHING. May be test class name or some of it's methods.
For example, suppose my test class has a method
public String getTestName() {
return getClass().getSimpleName();
}
This method returns test name and can be overridden.
Is it possible to inject this name into some beans of Spring context, to use during test?
For example, with autowire feature:
#Autowired
public String testName;
not only in test class, but in other beans too.
UPDATE
Below are two (failed) attempts to implement injecting testInstance. May be there are some convenient ways to do that?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestClassAwareTry._Config.class)
#TestExecutionListeners(value = { TestClassAwareTry._Listener.class },
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public class TestClassAwareTry {
/**
* Interface to tag beans, who want to know if they are in test
*/
public interface TestInstanceAware {
void setTestInstance(Object value);
}
/**
* Sample bean, which would like to know if it is in test
*/
public static class MyBean implements TestInstanceAware {
private Object testInstance;
{
System.out.println("MyBean constructed");
}
public void setTestInstance(Object value) {
this.testInstance = value;
System.out.println("testInstance set");
}
public Object getTestInstance() {
return testInstance;
}
}
/**
* Attempt to inject testInstance with a bean, implementing {#link BeanPostProcessor}
*/
public static class TestInstanceInjector implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if( bean instanceof TestInstanceAware ) {
TestInstanceAware aware = (TestInstanceAware) bean;
// we don't have access to test instance here
// otherwise I would write
//Object testInstance = getTestInstance();
//aware.setTestInstance(testInstance);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
/**
* Attempt to inject testInstance with test execution listener
*/
public static class _Listener extends AbstractTestExecutionListener {
#Override
public void prepareTestInstance(TestContext testContext) throws Exception {
Object testInstance = testContext.getTestInstance();
ApplicationContext context = testContext.getApplicationContext();
// we don't have setBean() method
// I would write if I have
// context.setBean("testInstance", context);
}
}
/**
* Java-based configuration
*/
#Configuration
public class _Config {
#Bean
public MyBean myBean() {
return new MyBean();
}
#Bean
public TestInstanceInjector testInstanceInjector() {
return new TestInstanceInjector();
// I would acquire test instance here and pass it to constructor, if I can
}
}
#Autowired
public MyBean myBean;
#Test
public void testInjected() {
assertSame( this, myBean.getTestInstance());
}
}
I've ended up creating ContextCustomizerFactory that registers BeanPostProcessor
package com.company.testing.base.spring;
import java.util.List;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
public class TestAwareContextCustomizerFactory implements ContextCustomizerFactory {
#Override
public ContextCustomizer createContextCustomizer(
Class<?> testClass, List<ContextConfigurationAttributes> configAttributes) {
return (context, mergedConfig) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.addBeanPostProcessor(
new TestInstanceAwareBeanPostProcessor(mergedConfig.getTestClass()));
};
}
}
TestInstanceAwareBeanPostProcessor
public class TestInstanceAwareBeanPostProcessor implements BeanPostProcessor {
private final Class<?> testClass;
TestInstanceAwareBeanPostProcessor(Class<?> testClass) {
this.testClass = testClass;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof TestClassAware) {
((TestClassAware) bean).setTestClass(testClass);
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
resources/META-INF/spring.factories
# ContextCustomizerFactories for the Spring TestContext Framework
org.springframework.test.context.ContextCustomizerFactory = \
com.company.testing.base.spring.TestAwareContextCustomizerFactory
The only way I've been able to do this is by delaying creation of the subject until you are in the test method and to have the bean in the prototype scope.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { LiveConfig.class, DevConfig.class})
#ActiveProfiles("Dev")
public class MeTest {
#Autowired
public ApplicationContext context;
#Autowired
DevConfig devConfig;
#Rule
public TestName nameRule = new TestName();
#Before
public void setName() {
devConfig.setSettings(nameRule.getMethodName());
}
#Test
public void test() {
Bean subject = context.getBean(Bean.class);
System.out.println(subject.settings);
assertThat(subject.settings, is(nameRule.getMethodName()));
}
#Test
public void test2() {
Bean subject = context.getBean(Bean.class);
System.out.println(subject.settings);
assertThat(subject.settings, is(nameRule.getMethodName()));
}
}
#Configuration
class LiveConfig {
#org.springframework.context.annotation.Bean
public String getSettings() {
return "/some/real/file.txt";
}
#org.springframework.context.annotation.Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Bean getBean() {
return new Bean();
}
}
#Configuration
class DevConfig {
private String settings;
#org.springframework.context.annotation.Bean
#Profile("Dev")
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public String getSettings() {
return settings;
}
public void setSettings(String settings) {
this.settings = settings;
}
}
class Bean {
public Bean() {
System.out.println("Bean");
}
String settings;
#Autowired
void setSettings(String settings) {
System.out.println("Settings: " + settings);
this.settings = settings;
}
}
This uses Profiles to change what Live sees and what the tests see, and the a NameRule to get the name. It is clunky.
I would NOT use the TestName rule, but rather the TemporaryFolder rule and use that to set whatever setting your application uses for the output folder. I'd also only use DI in a test in very rare cases (i.e. full blown integration tests).
Do you mean like this?
public class MyTest {
#Test
public void testName() {
MyBean b = new MyBean(MyTest.class.getSimpleName());
b.doSomething();
}
}
You can achieve this in a more elegant way using Spring Boot Auto configuration feature by making yours, this way:
define a Configuration class that exposes or registers your bean this way:
#Configuration
public class MyBeanProviderConfiguration {
#ConditionalOnMissingBean
#Bean
public MyBean myBean() {
// return a fully initialised MyBean instance
}
}
Then define a custom annotation Spring Boot like, say #AutoConfigureMyBean this way:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#ImportAutoConfiguration(MyBeanProviderConfiguration.class)
public #interface AutoConfigureMyBean {}
Then you can use this in your Spring test, here is an example:
#RunWith(SpringRunner.class)
#AutoConfigureMyBean
public class MyTest {
#Autowired
MyBean myBean;
}
Or also declare your MyBean #Autowired dependent bean in a regular Spring test (using a Config class), A MyBean instance will be automatically injected into it.

#Autowired and static method

I have #Autowired service which has to be used from within a static method. I know this is wrong but I cannot change the current design as it would require a lot of work, so I need some simple hack for that. I can't change randomMethod() to be non-static and I need to use this autowired bean. Any clues how to do that?
#Service
public class Foo {
public int doStuff() {
return 1;
}
}
public class Boo {
#Autowired
Foo foo;
public static void randomMethod() {
foo.doStuff();
}
}
You can do this by following one of the solutions:
Using constructor #Autowired
This approach will construct the bean requiring some beans as constructor parameters. Within the constructor code you set the static field with the value got as parameter for constructor execution. Sample:
#Component
public class Boo {
private static Foo foo;
#Autowired
public Boo(Foo foo) {
Boo.foo = foo;
}
public static void randomMethod() {
foo.doStuff();
}
}
Using #PostConstruct to hand value over to static field
The idea here is to hand over a bean to a static field after bean is configured by spring.
#Component
public class Boo {
private static Foo foo;
#Autowired
private Foo tFoo;
#PostConstruct
public void init() {
Boo.foo = tFoo;
}
public static void randomMethod() {
foo.doStuff();
}
}
You have to workaround this via static application context accessor approach:
#Component
public class StaticContextAccessor {
private static StaticContextAccessor instance;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void registerInstance() {
instance = this;
}
public static <T> T getBean(Class<T> clazz) {
return instance.applicationContext.getBean(clazz);
}
}
Then you can access bean instances in a static manner.
public class Boo {
public static void randomMethod() {
StaticContextAccessor.getBean(Foo.class).doStuff();
}
}
What you can do is #Autowired a setter method and have it set a new static field.
public class Boo {
#Autowired
Foo foo;
static Foo staticFoo;
#Autowired
public void setStaticFoo(Foo foo) {
Boo.staticFoo = foo;
}
public static void randomMethod() {
staticFoo.doStuff();
}
}
When the bean gets processed, Spring will inject a Foo implementation instance into the instance field foo. It will then also inject the same Foo instance into the setStaticFoo() argument list, which will be used to set the static field.
This is a terrible workaround and will fail if you try to use randomMethod() before Spring has processed an instance of Boo.
The easiest way to create a static context is naturally, when the application starts up. This will prevent the need for an unnatural implementation with an additional class.
#SpringBootApplication
public class MyApplication {
private static ApplicationContext appContext;
public static void main(String[] args) {
appContext = SpringApplication.run(MyApplication.class, args);
}
public static ApplicationContext getAppContext() {
return appContext;
}
}
Then, anywhere you need to access a bean statically, you can use the ApplicationContext to get the instance of the class.
public class Boo {
public static void randomMethod() {
MyApplication.getAppContext()
.getBean(Foo.class).doStuff();
}
}
Regards..
It sucks but you can get the bean by using the ApplicationContextAware interface. Something like :
public class Boo implements ApplicationContextAware {
private static ApplicationContext appContext;
#Autowired
Foo foo;
public static void randomMethod() {
Foo fooInstance = appContext.getBean(Foo.class);
fooInstance.doStuff();
}
#Override
public void setApplicationContext(ApplicationContext appContext) {
Boo.appContext = appContext;
}
}
This builds upon #Pavel's answer, to solve the possibility of Spring context not being initialized when accessing from the static getBean method:
#Component
public class Spring {
private static final Logger LOG = LoggerFactory.getLogger (Spring.class);
private static Spring spring;
#Autowired
private ApplicationContext context;
#PostConstruct
public void registerInstance () {
spring = this;
}
private Spring (ApplicationContext context) {
this.context = context;
}
private static synchronized void initContext () {
if (spring == null) {
LOG.info ("Initializing Spring Context...");
ApplicationContext context = new AnnotationConfigApplicationContext (io.zeniq.spring.BaseConfig.class);
spring = new Spring (context);
}
}
public static <T> T getBean(String name, Class<T> className) throws BeansException {
initContext();
return spring.context.getBean(name, className);
}
public static <T> T getBean(Class<T> className) throws BeansException {
initContext();
return spring.context.getBean(className);
}
public static AutowireCapableBeanFactory getBeanFactory() throws IllegalStateException {
initContext();
return spring.context.getAutowireCapableBeanFactory ();
}
}
The important piece here is the initContext method. It ensures that the context will always get initialized. But, do note that initContext will be a point of contention in your code as it is synchronized. If your application is heavily parallelized (for eg: the backend of a high traffic site), this might not be a good solution for you.
Use AppContext. Make sure you create a bean in your context file.
private final static Foo foo = AppContext.getApplicationContext().getBean(Foo.class);
public static void randomMethod() {
foo.doStuff();
}

Categories

Resources