How to #autowire some bean into JsonSerializer? - java

I am using lazy loading with hibernate in my web app.
I would like to load some objects from the database at the parsing stage of the server response
#Component
public class DesignSerializer extends JsonSerializer<Design> {
#Autowired
IDesignService designService; <-- is null
}
Which is totally understandable because DesignSerializer is being instantiated with the "new" operator for each object.
I am sure there is a way to inject my bean into that serializer when ever it is created, I just don't know how.
Can you guys help me or point me in the right direction.

Solution is SpringBeanAutowiringSupport if you are using Spring Framework 2.5+.
public class DesignSerializer extends JsonSerializer<Design> {
#Autowired
IDesignService designService;
}
public DesignSerializer(){
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
...
}
I Hope that help you

We had the same problem with JsonSerializer and Spring autowiring. The solution that worked for us was to make two constructors. One for Spring which sets the dependency as a static field, and another one that is used by the Jackson initialisation.
This works because the Spring dependency injection (autowiring) happens before Jackson initialises the serializer.
#Component
public class MyCustomSerializer extends JsonSerializer<String> {
private static IDesignService designService;
// Required by Jackson annotation to instantiate the serializer
public MyCustomSerializer() { }
#Autowired
public MyCustomSerializer(IDesignService designService) {
this.designService = designService;
}
#Override
public void serialize(String m, JsonGenerator gen, SerializerProvider s) {
gen.writeObject(MyCustomSerializer.designService.method(..));
}
}

I Solved the problem by creating a static field in a different bean and then #Autowire its setter method.
#Service("ToolBox")
#Transactional
public class ToolBox
{
static Logger logger = Logger.getLogger(ToolBox.class);
private static IService service;
#Autowired
public void setService(IService service)
{
ToolBox.service = service;
}
public static IService getService()
{
return ToolBox.service;
}}
like shown in this thread: Can you use #Autowired with static fields?

Related

Spring Boot: how to inject dependencies into a class called by a library?

I'm using Kinesis Client Library (KCL) and Spring boot. To use KCL, I have to implement a class (I named it RecordProcessor) for interface IRecordProcessor. And KCL will call this class and process records from kinesis. But when I tried to use dependency injection, I found it was not succeeded.
Here's the snippet for RecordProcessor:
#Component
public class RecordProcessor implements IRecordProcessor {
#Autowired
private SingleRecordProcessor singleRecordProcessor;
#Override
public void initialize(String shardId) {
...
}
#Override
public void processRecords(List<Record> records, IRecordProcessorCheckpointer checkpointer) {
...
}
}
I use Class SingleRecordProcessor to process single each record from kinesis. And this is my SingleRecordProcessor class snippet:
#Component
public class SingleRecordProcessor {
private Parser parser;
private Map<String, Table> tables;
public SingleRecordProcessor() {
}
#Autowired
private void setParser(Parser parser) {
this.parser = parser;
}
#Autowired
private void setTables(Map<String, Table> tables) {
this.tables = tables;
}
public void process(String record) {
...
}
}
I want to let spring framework automatically inject the SingleRecordProcessor instance into the class and use it. But I found that the field singleRecordProcessor is null.
Any idea why the dependency injection is failed? Or is it impossible to inject dependencies into a class which is called by other framework (in this case it's KCL)? Any suggestions will be appreciated! Really need some help please!!
[UPDATE]:
Sorry for not expressing the error clearly. The error was NullPointerException. I tried to inject singleRecordProcessor and call method process() on it. I think the injection was not successful so the instance singleRecordProcessor is null and there comes the NullPointerException.
More information is as follows:
I have a major class called Application
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./app.pid"));
ConfigurableApplicationContext ctx = application.run(args);
}
}
And I have the MainProcessor class which will call KCL.
#Service
public final class MainProcessor {
#EventListener(ApplicationReadyEvent.class)
public static void startConsumer() throws Exception {
init();
IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
Worker worker = new Worker(recordProcessorFactory, kinesisClientLibConfiguration);
...
worker.run(); // this line will call KCL library and eventually call ProcessorRecord class.
}
}
[UPDATE2]
RecordProcessorFactory only has one method like this
#Component
public class RecordProcessorFactory implements IRecordProcessorFactory {
#Autowired
RecordProcessor recordProcessor;
#Override
public IRecordProcessor createProcessor() {
return recordProcessor;
}
}
It creates a new RecordProcessor instance for KCL to use it.
You should autowire an instance of this into your MainProcessor:
#Component
public class RecordProcessorFactory {
#Lookup IRecordProcessor createProcessor() { return null; }
}
Spring will instantiate a RecordProcessorFactory for you, and replace the implementation of createProcessor() in it with one that will return a new IRecordProcessor each time it's called. Both the factory and the processors will be Spring beans - which is what you want.

Why is my Spring Boot #Autowired MyBatis static mapper null?

I have the following class, but Spring and MyBatis-Spring-Boot-Starter will not autowire my mapper.
When I run the request, I get the output from the println()
sourceMapper = null
Model
public class Source {
#Autowired
public static SourceMapper sourceMapper; #### Why isn't this set?
public static Source findOrCreate(String url) {
...
System.out.println("sourceMapper = " + sourceMapper);
source = sourceMapper.findByHost(host);
...
}
}
I followed the examples as closely as possible.
http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/
The other #Autowired Mappers in the main #Controller class that handles requests work, even though they are private.
This is the Mapper class
package ...mapper;
#Mapper
public interface SourceMapper {
...
I ran into this issue again with a new model and mapper. I tried to follow Why is my Spring #Autowired field null? and the code sample, but it was still null! I tried #Configurable, #Service, #Component.
Model
#Configurable
public class Domain {
#Autowired
private static DomainMapper domainMapper;
public static void incrementCounter(String host) {
...
Domain d = getDomainMapper().find(host, thisMonth);
public static DomainMapper getDomainMapper() {
return domainMapper;
public static void setDomainMapper(DomainMapper domainMapper) {
Domain.domainMapper = domainMapper;
Mapper
#Mapper
public interface DomainMapper {
MyBatis 3.4.5, MyBatis Spring 1.3.1, MyBatis Spring Boot Autoconfigure 1.3.1, MyBatis Spring Boot Starter 1.3.1
I fixed it with
private static DomainMapper getDomainMapper() {
// https://stackoverflow.com/a/52997701/148844
if (domainMapper == null)
domainMapper = MyApplication.getApplicationContext().getBean(DomainMapper.class);
return domainMapper;
And
MyApplication
#Autowired // for AWS
private static ApplicationContext context;
// I believe this only runs during an embedded Tomcat with `mvn spring-boot:run`.
// I don't believe it runs when deploying to Tomcat on AWS.
public static void main(String[] args) {
context = SpringApplication.run(MyApplication.class, args);
But I don't like it!
Spring will only try to inject a bean for you if another bean requires it.
Your class Source is just a normal class with bunch of static methods.
Hence it's not under creation control of Spring.
If you want to inject SourceMapper to your Source, you should mark Source with #Component or #Service so that the container will know it should create a bean of Source type for you and give you an instance of SourceMapper.
Moreover, the SourceMapper should be declared non-static, to prevent if a class access the variable before injected. And static field can be injected only if it use field setter injection.

Retrofit Autoconfiguration in Spring

I am currently writing a Spring Boot autoconfiguration for Retrofit 2. What I am trying to do is to write some sort of an interface builder that is able instantiate an interface that is annotated with some annotation for autowiring just like Spring Data does it with repositories. As I cannot find any resources on how to do this (or if it can even be done with Spring), I would like to ask for your thoughts on that. Below is for an interface that I would like to instantiate.
My replacement for #Repository is #Retrofit the rest is just "ordinary" code you would write for any Retrofit repository.
The kind of interface I would like to autowire:
#Retrofit
public interface Api {
#GET("usernames")
String[] getUsernames();
}
An example for autowiring:
#SpringBootApplication
public class TestApplication {
#Autowired
private Api api;
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean
CommandLineRunner runner() {
return args -> {
System.out.println(api.getUsernames());
};
}
}
As I said I found a solution for my problem.
First we need an auto configuration class that is loaded by Spring Boot - as stated here - by adding the file META-INF/spring.factories with the content that is shown below. This auto configuration loads a registrar which itself searches for classes annotated with #Retrofit via a component provider. At last the registrar creates instances of RetrofitFactoryBean for each instance that could be found while this factory bean creates the Retrofit proxies itself.
The auto configuration
#Configuration
#Import(RetrofitRegistrar.class)
public class RetrofitAutoConfiguration {
#Bean
#ConditionalOnMissingBean
public Retrofit retrofit() {
return new Retrofit.Builder().build();
}
}
META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
spring.retrofit.RetrofitAutoConfiguration
The imported registrar
public class RetrofitRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
#Setter
private BeanFactory beanFactory;
#Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
List<String> basePackages = AutoConfigurationPackages.get(this.beanFactory);
RetrofitComponentProvider provider = new RetrofitComponentProvider(registry);
basePackages.stream()
.map(provider::findCandidateComponents)
.flatMap(Set::stream)
.forEach(comp -> register(comp, registry));
}
private void register(BeanDefinition component, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.
rootBeanDefinition(RetrofitFactoryBean.class);
builder.addConstructorArgValue(component.getBeanClassName());
registry.registerBeanDefinition(
component.getBeanClassName().toLowerCase(), builder.getBeanDefinition());
}
}
The component provider
class RetrofitComponentProvider extends ClassPathScanningCandidateComponentProvider {
#Getter
private BeanDefinitionRegistry registry;
public RetrofitComponentProvider(BeanDefinitionRegistry registry) {
super(false);
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
this.registry = registry;
addIncludeFilter(new AnnotationTypeFilter(Retrofit.class, true, true));
}
#Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return true;
}
}
The factory bean
#Component
#RequiredArgsConstructor
public class RetrofitFactoryBean extends AbstractFactoryBean<Object> {
#Getter
private final Class<?> objectType;
private final retrofit2.Retrofit retrofit;
#Override
protected Object createInstance() throws Exception {
return retrofit.create(objectType);
}
}
The #Getter, #Setter and #RequiredArgsConstructor annotations are provided by ProjectLombok
Let me ask you firstly to avoid reinventing the wheel by creating a new Spring annotation (yours here is #Retrofit. However, it is absolutely okay to use retrofit with spring there is nothing to prevent it. You can simply try to use an existing Spring annotation which can be #Component as you can see in this question
you can autowire your interface without facing problems.
Hope this helps.

How to disable Spring autowiring in unit tests for #Configuration/#Bean usage

I want configure a component test using spring-test configuration inner class (#Configuration). Tested components has some services which I'd like to mock for the test. These services are classes (no interface used) and have spring annotations (#Autowired) in them. Mockito can easily mock them, however, I found no way of disabling spring autowiring.
Example how I can easily reproduce:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Test
public void test() {
}
#Configuration
#ImportResource("/spring/component-config.xml")
static class Beans {
#Bean
ThirdPartyService createThirdPartyService() {
return mock(ThirdPartyService.class);
}
}
}
public class ThirdPartyService {
#Autowired
Foo bar;
}
public class TestedBean {
#Autowired
private ThirdPartyService service;
}
In this example "TestBean" represents the service to be mocked. I would NOT like "bar" to be injected by spring! #Bean(autowire = NO) does not help (in fact, that's the default value).
(Please save me from "use interfaces!" comments - the mocked service can be 3rd party which I can't do anything with.)
UPDATE
Springockito partially solves the problem, as long as you don't have to have anything else to configure (so you can't use configuration class with Springockito - it does not support it), but use mocks only.
Still looking for pure spring solution, if there's any...
Here is my solution to your problem:
import static org.mockito.Mockito.mockingDetails;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MockitoSkipAutowireConfiguration {
#Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
and then just
#Import(MockitoSkipAutowireConfiguration.class)
in your test #Configuration and you are all set
I solved it by creating FactoryBean for my bean instead of just mocking bean. At this way Spring don't try to autowire fields.
Factory bean helping class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MockitoFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
#Override public T getObject() throws Exception {
return mock(clazz);
}
#Override public Class<T> getObjectType() {
return clazz;
}
#Override public boolean isSingleton() {
return true;
}
}
Actual test context part:
#Configuration
public class TestContext {
#Bean
public FactoryBean<MockingService> mockingService() {
return new MockitoFactoryBean<>(MockingService.class);
}
}
Check Spring profiles. You don't need to disable auto wiring, you need to inject different beans for different configuration.
You could add the mocked service manually to the spring application context via org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingleton. This way the mock is not post-processed by spring and spring does not attempt to autowire the mock. The mock itself will be injected into your tested bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Autowired
private ThirdPartyService thirdPartyServiceMock;
#Test
public void test() {
}
#Configuration
static class Beans {
#Autowired
private GenericApplicationContext ctx;
#Bean
TestedBean testedBean() {
ctx.getBeanFactory().registerSingleton("thirdPartyService", mock(ThirdPartyService.class));
return new TestedBean();
}
}
public static class ThirdPartyService {
#Autowired
Object bar;
}
public static class TestedBean {
#Autowired
private ThirdPartyService service;
}
}
I am in quite the same situation.
What I found that if you do not set the context loader by #ContextConfiguration annotation on your test class, the default context loader will be used, which derived from AbstractGenericContextLoader. I had a look at its source and turned out it registers all the bean post processors which are responsible for reading annotations such #Autowired. In other words, annotation config is enabled by default.
So the main problem is that there are two configurations which are in conflict: in the java config we said that autowiring is not needed, while the autowired annotation tells the opposite. The real question is how to disable the annotation processing in order to eliminate the undesired configuration.
As far as I know there is no such spring implementation of ContextLoader which would not be derived from AbstractGenericContextLoader so I guess the only we can do is to write our own. It would be something like this:
public static class SimpleContextLoader implements ContextLoader {
#Override
public String[] processLocations(Class<?> type, String... locations) {
return strings;
}
#Override
public ApplicationContext loadContext(String... locations) throws Exception {
// in case of xml configuration
return new ClassPathXmlApplicationContext(strings);
// in case of java configuration (but its name is quite misleading)
// return new AnnotationConfigApplicationContext(TestConfig.class);
}
}
Of course it would be worth to spend more time to find out how to implement ContextLoader properly.
Cheers,
Robert
There are so many ways of doing this, I'm pretty sure that this answer will be incomplete, but here are a few options...
As currently seems to be recommended practice, use constructor injection for your services rather than autowiring the fields directly. This makes testing like this so much easier.
public class SomeTest {
#Mock
private ThirdPartyService mockedBean;
#Before
public void init() {
initMocks(this);
}
#Test
public void test() {
BeanUnderTest bean = new BeanUnderTest(mockedBean);
// ...
}
}
public class BeanUnderTest{
private ThirdPartyService service;
#Autowired
public BeanUnderTest(ThirdPartyService ThirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
}
By doing that, you can also mix up autowired and mocked services by autowiring into the test itself and then constructing the beans under test with the most useful mix of autowired and mocked beans.
A reasonable alternative is to use Spring profiles to define stub services. This is particularly useful when wish to use the same stubbed features in multiple tests:
#Service
#Primary
#Profile("test")
public class MyServiceStub implements MyService {
// ...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
#ActiveProfiles({"test"})
public class SomeTest {
// ...
}
By using the #Primary annotation, it ensures that this stub bean will be used instead of any other bean implementing the MyService interface. I tend to use this approach for things like email services, where by changing profile, I'm able to switch between a real mail server and Wiser.

How to write tag in my spring project?

I want to write my tag (extends TagSupport) in my spring framework. In my tag class, will use some service which should auto inject by spring. But I always get null, seems spring can't inject service instance in my tag class.
The code is like the following:
public class FetchTagNameTag extends TagSupport {
#Autowired
private TaskService taskService;
...
taskService is always null.
How can I resolve this?
Thanks.
Have a try by utilizing RequestContextAwareTag. It will offer you methods to obtain RequestContext and then WebApplicaitonContext. Have a look at here.
JSP tag objects are not managed by Spring, they are managed by the servlet container. As a result, you cannot autowire stuff into your tags.
If you need to get hold of beans from the spring appcontext, then your Spring MVC controller needs to set the bean as a request attribute (using request.setAttribute()), so that the tag object can get hold of it.
Annotate your Tag-Implementation with #Configurable and add <context:component-scan base-package="your.webapp"> to your Spring-Configuration.
Check out these spring packages in the spring reference docs and in the spring source:
org.springframework.web.servlet.tags
org.springframework.web.servlet.tags.form
If nothing else, those will show you how the spring developers wrote the spring tags.
What you could do is create a static method like this:
public static void autowireAllFor(Object target) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(...yourBeanFactory...);
bpp.processInjection(target);
}
and then for your tag you could do
public class YourTag extends TagSupport {
#Autowired
private SomeBean someBean;
public YourTag() {
YourHelperClass.autowireAllFor(this);
}
}
The obvious disadvantage of this approach is that you have to do this for every constructor, but as TagSupport only has one, it should not be a problem. You can go even one step further and create a helper superclass which always guarantees autowiring:
public class SpringTagSupport extends TagSupport {
public SpringTagSupport() {
super();
YourHelperClass.autowireAllFor(this);
}
}
The rest is as easy as extending your classes from SpringTagSupport.
First I write this:
public abstract class SpringSuportedTag extends SimpleTagSupport{
protected WebApplicationContext _applicationContext;
protected WebApplicationContext getSpringContext(){
PageContext pageContext = (PageContext) getJspContext();
if(_applicationContext==null){
_applicationContext = RequestContextUtils.getWebApplicationContext(
pageContext.getRequest(),
pageContext.getServletContext()
);
initCustomBeans();
}
return _applicationContext;
}
protected abstract void initCustomBeans();
/**
* Deprecated for inserting extra logic. Use {#link #doTagWithSpring()} instead.
*/
#Override
#Deprecated
public void doTag() throws JspException, IOException {
getSpringContext();
doTagWithSpring();
}
abstract void doTagWithSpring() throws JspException, IOException;
}
And usage:
public class SlotTag extends SpringSuportedTag {
// #Resource(name="userDetailHolder")
// not work here
UserDetailHolder userDetail;
private String slotname;
public String getSlotname() {
return slotname;
}
public void setSlotname(String slotname) {
this.slotname = slotname;
}
#Override
void doTagWithSpring() throws JspException, IOException {
PageContext pageContext = (PageContext) getJspContext();
String userDetailCode = pageContext.getAttribute(InitWidgetUserTag.KAY_USERDETAIL, PageContext.PAGE_SCOPE).toString();
userDetail.init(userDetailCode);
String pageID = pageContext.getAttribute(InitWidgetUserTag.KAY_PAGEID, PageContext.PAGE_SCOPE).toString();
getJspContext().getOut().println("<b>slot for user:"+userDetail.getUserId()+"</b>");
}
#Override
protected void initCustomBeans() {
userDetail = (UserDetailHolder) getSpringContext().getBean("userDetailHolder");
}
}
It's work.
But than i found this:
Spring supported Tag Libraries. Truth in my progect I still use own solution.
Use :-
import org.springframework.web.servlet.tags.RequestContextAwareTag;
public class FetchTagNameTag extends RequestContextAwareTag {
// #Autowired
// private TaskService taskService;
#Override
protected int doStartTagInternal() throws Exception {
TaskService taskService= getRequestContext().getWebApplicationContext().getBean(TaskService.class);
return 0;
}

Categories