How to configure spring-ioc dependency Injection in a personal way? - spring

I'm currently working on a web application using Angular 7 for front-end, spring-boot for back-end (in which I'm developing a restful web service).
I'm using #Autowired annotation to inject my services into each other and my rest controller. The problem is that in some of my services, there are some attributes that get shared when the injection is done. How do I prevent that?
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
#Service
public class ServiceA {
private boolean test;
public ServiceA (){
test = true;
}
public changeValues(){
test = false;
}
}
#Service
public class ServiceB {
#Autowired
private ServiceA serivceA;
public void method1() {
serviceA.changeValues();
}
}
#Service
public class ServiceC {
#Autowired
private ServiceA serivceA;
public void method2(){
if(serviceA.getTest()){
doSomethingNeeded();
}
}
}
public class Application{
#Autowired
private ServiceB b;
#Autowired
private ServiceC c;
public static void main(String[] args) {
b.method1();
c.method2();
}
}
In this case the method doSomethingNeeded() in ServiceC won't be able to be executed because the ressource 'test' of ServiceA is shared between both ServiceB and ServiceC. How do I prevent That?
P.S. In my case, the dependency injections are way too complex for applying any modifications to the services, that's why I need a way to configure spring-ioc dependency injection in a way to create instances of private attributes to each client session.

Spring Beans are by default Singletons and the should not contain state.
Single Page Applications (like you create with Angular) should anyway hold the state on the client side and pass the information with every request.
The main reason is when your backend is stateless it's easy to scale and is more reliable because if a backend service is restarted you don't loose anything.

You just need change the scope of ServiceA to prototype by adding #Scope(scopeName = "prototype"):
#Scope(scopeName = "prototype")
#Service
public class ServiceA {
}
Then when ServiceB and ServiceC are instantiated , separated ServiceA will be created and inject into them.
P.S. Please note that a new instance of prototype bean will only be created during ServiceB and ServiceC are instantiated. It does not mean that a new instance of prototype bean will always be created whenever you access them. You need to use one of these technique if you want such behaviour.

Related

Spring Autowired works before proxies are created

As far as I understood, Spring manages autowiring mechanism with AutowiredAnnotationBeanPostProcessor on postProcessBeforeInitialization stage. But how does it inject proxies that ought to be created on postProcessAfterInitialization stage?
EDIT 1
Suppose I have this Spring configuration
#Service
class RegularBean {
// injected on postProcessBeforeInitialization stage
#Autowired
private TransactionBean tBean;
// invoked in between of postProcessBeforeInitialization and postProcessAfterInitialization
#PostConstruct
void init() {
tBean.transactionMethod();
}
}
#Service
class TransactionBean {
// transactional proxy is created on postProcessAfterInitialization stage
#Transactional
public void transactionMethod() { ... }
}
Transactional proxy is created on postProcessAfterInitialization stage. But #PostConstruct is called right before it. Is injected tBean wrapped with transactional proxy? If it so, then why? Because it should not be. If it is not wrapped, then how transactions are going to be handled in the future?
Suppose that I replace field-injection with constructor-injection. Will it change the behavior somehow?
When you use autowiring on method or field ,Spring Container not always create and inject the required field/attribute instance. Spring internally create smart proxies and inject the proxies to your bean. This smart proxy will resolve the bean at later point and delegate call to actual bean during method invocation. This is a common strategy we use to resolve request and session scoped beans to singleton instance (Say service layer beans) using Scope annotation.
Adding small snippet to show Spring internally create proxies for Object. Consider a classic circular dependency case when we use Constructor injection.
#Component
public class CircularDependencyA {
private CircularDependencyB circB;
public CircularDependencyA(#Lazy CircularDependencyB circB) {
System.out.println("CircularDependencyA Ctr ->"+circB.getClass().getName());
this.circB = circB;
}
}
#Component
public class CircularDependencyB {
private CircularDependencyA circA;
public CircularDependencyB(CircularDependencyA circA) {
System.out.println("CircularDependencyB Ctr ->"+circA.getClass().getName());
this.circA = circA;
}
}
#Configuration
#ComponentScan(basePackages = { "com.example.springdemo.cd" })
public class TestConfig {
}
public class TestCircularDependency {
public static void main(String[] args) {
try(AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(TestConfig.class);){
}
}
}
Based on our hints Spring is creating Proxy(using CGLIB) for CircularDependencyB Object and see the op from the CircularDependencyA Constructor
CircularDependencyA Ctr ->com.example.springdemo.cd.CircularDependencyB$$EnhancerBySpringCGLIB$$e6be3b79
Thanks

What is the best way to inject a singleton service into a JAX-RS/Jersey resource?

For example, what if several resource endpoints need access to some message bus to handle requests? Surely there is some way to register a singleton service class and inject it into the resources when the service class itself is NOT a resource but used by the resources.
All of the examples I've seen with providers or custom HK2 bindings refer to resources.
The closest thing I found to what I'm looking for was with this question:
Trouble creating a simple singleton class in Jersey 2 using built-in Jersey dependency injection
What is the best JAX-RS/Jersey way of doing this?
Note that the programmatic way would be most useful, I'm not using an xml file to configure the server.
If your platform supports EJB, you could use the #Singleton EJB (javax.ejb package, not javax.inject), and inject it on your resources with the #EJB annotation. Singleton EJB have also outofthebox concurrency access control.
On plain Jersey, you can use CDI application context. Declare the service class with an #ApplicationScoped annotation and inject it on your resources with #Inject. CDI will only instantiate one bean.
If you cannot annotate the service class, you can create a method that provides your service implementation an annotate it with #Produces and #ApplicationScoped.
#Produces
#ApplicationScoped
public MyService produceService() {
// instantiate your service client
}
And then use it on your resources, with:
#Inject
private MyService
Answer credit goes to #areus the answer provided here.
However, I'm providing my own answer so that I can share the code.
The Service Bean
#Singleton
public final class MyServiceBean
{
private static final AtomicInteger INSTANCES = new AtomicInteger();
private final AtomicInteger calls = new AtomicInteger();
public MyServiceBean()
{
INSTANCES.incrementAndGet();
}
public String getMessage()
{
return String.format("MyServiceBean{INSTANCES=%d, CALLED=%d}", INSTANCES.get(), calls.incrementAndGet());
}
}
The Resource Class
#Path("/messages")
public final class MyResource
{
#Inject
private MyServiceBean bean;
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response handle()
{
return Response.ok(this.bean.getMessage())
.type(MediaType.TEXT_PLAIN_TYPE)
.build();
}
}
HK2 Binder
public final class MyServiceBeanBinder extends AbstractBinder
{
#Override
protected void configure()
{
bind(MyServiceBean.class).to(MyServiceBean.class).in(Singleton.class);
}
}
Then just register the binder and the resource like so:
final ResourceConfig config = new ResourceConfig();
config.register(MyResource.class);
config.register(new MyServiceBeanBinder());
Starting the server and hitting the resource multiple times yields:
MyServiceBean{INSTANCES=1, CALLED=1}
MyServiceBean{INSTANCES=1, CALLED=2}
MyServiceBean{INSTANCES=1, CALLED=3}
MyServiceBean{INSTANCES=1, CALLED=4}
MyServiceBean{INSTANCES=1, CALLED=5}

Add one additional bean to "#WebMvcTest"

I have a controller and a test using #WebMvcTest and its running fine. Now i needed to add a little validation logic and for this i #Autowired an additional bean (a #Component, a MapstructMapper).
As expected now the test is failing due to #WebMvcTest. (No components are discovered)
Is there a way to add one bean to the context created?
Since i am using #MockBeans to mock service layer: is there a way to delegate all mock calls to a real object? With this i could mock the mapper and delegate to real mapper?!
A simple way of getting additional beans in the context is via using nested configuration classes within test classes
#TestConfiguration
static class AdditionalConfig {
#Bean
public SomeBean getSomeBean() {
return new SomeBean());
}
}
Example:
Scenario - If you have some Controller say ProductController and you have the corresponding slice-test for the class say ProductionControllerTest
#RestController
public class ProductController {
#Autowired
private IProductService productService;
#Autowired
private IProductValidator productValidator;
#GetMapping("product")
public Product getProduct(#RequestParam Long id) {
Product product = productService.getProduct(id); // you are using mockBean of productService
productValidator.validateProduct(product); // you need real bean of productValidator
return product;
}
}
Corresponding slide test class with an additional bean configuration
#RunWith(SpringRunner.class)
#WebMvcTest
public class ProductControllerSliceTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private IProductService productService;
#Autowired
private ApplicationContext applicationContext;
#TestConfiguration
static class AdditionalConfig {
#Bean
public IProductValidator productValidator() {
return new ProductValidator();
}
}
#Test
public void testProductGetById() throws Exception {
Product testProductWithID1L = new Product(1L, "testProduct");
when(productService.getProduct(anyLong())).thenReturn(testProductWithID1L);
mockMvc.perform(get("/product")
.param("id", "1")).andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("name")
.value("testProduct"))
.andExpect(MockMvcResultMatchers.jsonPath("id")
.value("1"));
}
}
Additional thoughts on your scenario: If you really intend to do the unit testing of the controller class then ideally you should mock all the additional dependencies of class that you are testing.
Ideally, the intention of the unit test is to only test the behavior of the object/class under test. All the dependent classes behavior or external calls should be mocked.
When you start testing several classes together under one test, you are moving more towards a component test or integration test
A very simple solution is to annotate your test class with #Import specifying the class(es) of the additional bean(s) you want to use in your test, as stated in documentation:
Typically #WebMvcTest is used in combination with #MockBean or #Import
to create any collaborators required by your #Controller beans.
e.g.
#WebMvcTest(MyController.class)
#Import(SomeOtherBean.class)
public class SourcingOrganisationControllerTests {
// The controller bean is made available via #WebMvcTest
#Autowired
private MyController myController;
// Additional beans (only one in this case) are made available via #Import
#Autowired
private SomeOtherBean someOtherBean;
}

Using #SpringApplicationConfiguration annotation to injecting mocks into a Spring bean

There is the spring-boot application that uses spring-aop. proxy-target-class is true.
I'm trying to create a test for a service class. This service depends on a component class. I want to inject a mock into the service instead of the real component.
I found some similar questions:
Mocking a property of a CGLIB proxied service not working
Injecting Mockito mocks into a Spring bean
I choose this answer to the last question, and I have tried to implement this approach. I chose it because it is not tied to the implementation details of the proxy classes and I can easily use a config class in other tests.
Below there is the example which simulates the real problem.
#org.aspectj.lang.annotation.Aspect
#org.springframework.stereotype.Component
public class Aspect {
#Before("within(demo.Service)")
public void someAdvice() {
System.out.println("advice");
}
}
#org.springframework.stereotype.Service
public class Service {
#Autowired
private Component component;
public void action() {
System.out.println(component.action());
}
}
#org.springframework.stereotype.Component
public class Component {
public String action() {
return "real action";
}
}
#SpringApplicationConfiguration
public class ServiceTest extends BaseTest {
#Autowired
Service service;
#Test
public void testAction() {
service.action();
}
#Configuration
public static class Config {
#Mock Component mock;
public Config() {
MockitoAnnotations.initMocks(this);
}
#Bean
public Component component() {
Mockito.when(mock.action()).thenReturn("mock action");
return mock;
}
}
}
Complete example: https://github.com/eds0404/spring-inject-mock-into-proxy
The above code is not working as I expect, the service does not use mock ("real action" will be printed if you run test). But the above code works fine if the Component class is not marked with #Component annotation, and its objects are created by the method with #Been annotation.
How to solve this issue? If this is wrong approach, what is best practice?

How to Initialize Jersey Application (ResourceConfig) With Spring?

I'm using Jersey 2 and Spring, and I'm trying to initialize my Jersey application (i.e. the class derived from ResourceConfig) with parameters from the Spring context.
Background: I have a single Jersey application that I build (i.e. a single WAR) and I deploy it across a server cluster with different Spring configurations on different servers to enable or disable different parts of the server, e.g. some of the servers have /search resources turned on, etc. This was really easy in Jersey 1.0: I just put,
<context:component-scan base-package="com.mycompany.resources.search"/>
in a Spring config to have Jersey scan that particular package and enable the JAX-RS resource providers in it.
Now in Jersey 2.0 the Spring <context:component-scan ... /> doesn't work, so resources have to be programmatically registered in a startup class derived from ResourceConfig:
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.mycompany.resources.search");
}
}
So far so good, but I need to conditionally scan that package, and I can't figure out how to get any Spring configuration into the MyApplication class. I thought that constructor injection might work:
public class MyApplication extends ResourceConfig {
#Autowired
public MyApplication(#Qualifier("my-config") MyConfiguration myConfiguration) {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
However HK2 complains that it can't find a default constructor to use... so this indicates to me that DI is in play in the construction of this class, but that the DI isn't using Spring.
Similarly, using the the Spring bean lifecycle doesn't work:
public class MyApplication extends ResourceConfig implements InitializingBean {
#Autowired
private MyConfiguration myConfiguration;
public MyApplication() {
}
#Override
public void afterPropertiesSet() throws Exception {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
(The afterPropertiesSet method isn't called.)
So now I'm stuck: is there any way to configure a Jersey ResourceConfig application object using Spring?
UPDATE:
I accepted #JohnR's answer below but I'll also include my eventual solution which I think is a bit cleaner. #JohnR's answer was to have the object initialized twice: first by Spring and then by Jersey/HK2. When Spring initializes the object you cache the dependencies in a static member, and then when Jersey/HK2 initializes it later you can retrieve the dependencies.
I ended up doing this:
public class MyApplication extends ResourceConfig {
public MyApplication() {
ApplicationContext rootCtx = ContextLoader.getCurrentWebApplicationContext();
MyConfiguration myConfiguration = rootCtx.getBean(MyConfiguration.class);
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.whatever");
}
}
}
Rather than having the object initialized twice, we let Jersey/HK2 initialize it but then we retrieve the dependencies from Spring.
Both solutions are vulnerable to timing: they both assume that Spring is initialized before Jersey/HK2.
Expanding on my previous comment:
Trying to extend ResourceConfig is dangerous if you don't know what you're doing. Jersey becomes unpredictable, and if you try to subclass it into an Abstract class, Jersey crashes.
Instead, the JAX-RS specification provides us with a very useful interface called Feature: It allows you to register any classes you want as if you were configuring your own application. Furthermore, you don't need to use the awkward AbstractBinder, you just specify what contracts you register your classes with.
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
// Don't use #Component here, we need to inject the Spring context manually.
public class MySpringFeature implements Feature {
#Context
private ServletContext servletContext;
private ApplicationContext applicationContext;
#Autowired
private MySecurityDAO mySecurityDAO;
#Autowired
private MySpringResponseFilter myResponseFilter;
#Override
public boolean configure(FeatureContext context) {
if(this.servletContext == null) {
return false; // ERROR!
}
this.applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if(this.applicationContext == null) {
return false; // ERROR!
}
// This is where the magic happens!
AutowireCapableBeanFactory bf = applicationContext.getAutowireCapableBeanFactory();
bf.autowireBean(this);
// From here you can get all the beans you need
// Now we take a Spring bean instance,
// and register it with its appropriate JAX-RS contract
context.register(myResponseFilter, ContainerResponseFilter.class);
// Or, we could do this instead:
SomeSecurityFilter mySecurityFilter = new SomeSecurityFilter();
mySecurityFilter.setSecurityDAO(mySecurityDAO);
context.register(mySegurityFilter, ContainerRequestFilter.class);
// Or even this:
SomeOtherSpringBean someOtherBean = applicationContext.getBean(SomeOtherSpringBean.class);
context.register(someOtherBean, SomeOtherJerseyContract.class);
// Success!
return true;
}
}
And in your ResourceConfig:
public class MyApplication extends ResourceConfig() {
public MyApplication() {
register(MySpringFeature.class);
}
}
Ta-da!
So now I'm stuck: is there any way to configure a Jersey
ResourceConfig application object using Spring?
I don't think you can configure Jersey to obtain your ResourceConfig from Spring as a Spring managed bean. It's a bit hackish, but you could do something like this. Note that you'll end up with two instance of your ResourceConfig: one managed by Spring and another by Jersey:
public class MyApplication extends ResourceConfig {
// static, available to all instances
private static MyConfiguration myConfiguration;
public MyApplication() {
// when Spring creates the first instance of MyApplication, myConfiguration
// will be null because the setter wasn't called yet
if (myConfiguration != null)
{
// second instance created by jersey... Spring will have autowired
// the first instance, and myConfiguration is static
if (myConfiguration.isEnabled())
packages("com.mycompany.resources.search");
}
}
#Autowired
public void setMyConfiguration(MyConfiguration config)
{
// instance level setter saves to a static variable to make it available for
// future instances (i.e. the one created by jersey)
MyApplication.myConfiguration = config;
}
}
Again, this is fairly hackish. You'll want to make sure Spring is initialized before Jersey and look closely at any threading issues that could occur during initialization.

Resources