In the context of an IoC container such as Spring, I am looking for a way to inject some dependencies/properties into a class' instantiation. Not all properties of the object can be set using dependency injection, and the object is created dynamically in response to an application event. If all dependencies can be injected via the container, then a Spring managed bean would be ideal.
For instance, the class defined below must be annotated as a #Component (or more specialized annotation) for component scanning and dependency injection to work. But it has a couple of properties (name and attempts) that can only set be dynamically, by the application code and not the container. But if I have to use an endpoint and a restTemplate (which are already managed by the IoC container), providing them to this object via constructor or setter methods is not convenient.
public class SomeClass {
#Autowired
private RestTemplate restTemplate;
#Autowired
private String endpoint;
private String name;
private int attempts;
public SomeClass(String name, int attempts) {
this.name = name;
this.attempts = attempts;
}
// public getter and setter methods
}
Since there are some dynamically set properties, I cannot use the new keyword to instantiate the class and still reap the benefits of DI and IoC. Or can I?
You could use a factory. Something like the following:
public class SomeClass {
private RestTemplate restTemplate;
private String endpoint;
private String name;
private int attempts;
public SomeClass(String name, int attempts, RestTemplate restTemplate,
String endpoint) {
this.name = name;
this.attempts = attempts;
this.restTemplate = restTemplate;
this.endpoint = endpoint;
}
}
#Component
public class SomeClassFactory {
#Autowired
private RestTemplate restTemplate;
#Autowired
private String endpoint;
public SomeClass create(String name, int attempts) {
return new SomeClass(name, attempts, restTemplate, endpoint);
}
}
SomeClass instance = someClassFactory.create("beep", 0);
If I don't misunderstood you, you need to set the values in the constructor.
You can do it creating the bean from the context and setting the values:
context.getBean("beanname", arg1, arg2....);
Related
I'm still learning programming languages java and spring, during learning, I saw a new way to create dependency injection before I use this type.
#RestController
#RequestMapping("/api/car")
public class CarController {
final CarServiceImp carServiceImp;
final RestTemplate restTemplate;
public CarController(CarServiceImp carServiceImp, RestTemplate restTemplate) {
this.carServiceImp = carServiceImp;
this.restTemplate = restTemplate;
}
#GetMapping("/getcars")
public Response getCarByPage(#RequestParam Integer page,#RequestParam Integer size,#RequestParam String price) {
return carServiceImp.getCarByPage(page,size,price);
}
#GetMapping("/car")
public Response getAllCarFromAutoShop(#RequestParam(name = "autshopId") Integer id) {
return carServiceImp.getAllCarFromAutoShop(id);
}
#PostMapping
public Response addAllCar(#RequestBody List<Car> carList) {
return carServiceImp.addAll(carList);
}
#PostMapping("/one")
public Response addOneCar(#RequestBody Car car) {
return carServiceImp.addOne(car);
}
}
here new way of using DI my question when using access modifier with DI and annotation #Autowired with constructor, and which one is comfortable for use by the way thank you for your answer
#RestController
#RequestMapping("/api/car")
public class CarController {
private final CarServiceImp carServiceImp;
private final RestTemplate restTemplate;
#Autowired
public CarController(CarServiceImp carServiceImp, RestTemplate restTemplate) {
this.carServiceImp = carServiceImp;
this.restTemplate = restTemplate;
}
#GetMapping("/getcars")
public Response getCarByPage(#RequestParam Integer page,#RequestParam Integer
size,#RequestParam String price) {
return carServiceImp.getCarByPage(page,size,price);
}
#GetMapping("/car")
public Response getAllCarFromAutoShop(#RequestParam(name = "autshopId") Integer id) {
return carServiceImp.getAllCarFromAutoShop(id);
}
#PostMapping
public Response addAllCar(#RequestBody List<Car> carList) {
return carServiceImp.addAll(carList);
}
#PostMapping("/one")
public Response addOneCar(#RequestBody Car car) {
return carServiceImp.addOne(car);
}
}
Before Spring 4.3 the #Autowired annotation was needed. Since then, it is optional if there is only one constructor.
There are three options for injection in spring: field injection, setter injection and the preferable constructor injection (the one you used).
The constructor injection is to be preferred, because you can be certain that all needed dependencies are injected, if the constructor expects all required dependencies as parameters. This way the class will not be instanciated without the required dependencies.
The visibility of fields in a class should always be private, if not needed otherwise.
if I have a class like this:
#Service
#Scope("prototype")
public class TraderStarter {
private String address;
}
and TraderStarter should be created X times, X is dynamically determined by databases. How should I get these beans?
Only like this?
#Component("SpringContextUtil")
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
#SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
}
using SpringContextUtil .getBean() and set parameters for every prototype TraderStarter?
Thanks a lot.
In a nutshell, prototype means that the new bean is created upon each request for this bean (injections into different classes / getBean call).
So if you want to create these prototype beans, someone triggers this process.
Of course one way is to use the ApplicationContext but IMO its a bad approach, since your code becomes coupled to spring.
You cannot inject prototype into the singleton, this doesn't work.
However you can use javax.inject.Provider interface that is integrated with Spring:
Here is an example:
// from your example
// you can use injection here and everything, its a regular spring bean
#Service
#Scope("prototype")
public class TraderStarter {
private String address;
public void setAddress(String address) {
this.address = address;
}
}
///////////////////////
#Component
public class MyDbManager {
private Provider<TraderStarter> traderStarterProvider;
public List<TraderStarter> dynamicallyCreateBeans() {
List<String> addresses = dbManager.findAllAddresses();// to to the db, get the data
return
addresses.stream()
.map(this::createTraderStarter) // <-- creates different beans!
.collect(Collectors.toList());
}
private TraderStarter createTraderStarter(String address) {
TraderStarter ts = provider.get();
ts.setAddress(address);
return ts;
}
}
There are other methods as well with factories (see ObjectFactory) and creating proxies (Lookup method and scoped proxy), but IMO this is the most clean approach.
In any case if you opt for other solutions, read this tutorial
I have a Rest Controller in which I initialise a service like this :
class Config {
#Value(${"number.of.books"})
private final static String numberOfBooks;
}
class MyController {
private final Service myService = new ServiceImplementation(Config.numberOfBooks)
public ResponseEntity methodA() { ... }
}
The numberOfBooks field has a initialisation value but when it's passed in the ServiceImplementation constructor it comes null.
I'm thinking I'm missing something obvious over here.
What is the mistake and which would be the best practice to inject a value from a property file into a constructor?
I recommend you to directly inject numberOfBooks in your ServiceImplementation, as follows:
public class ServiceImplementation implements Service {
#Value("${number.of.books}")
private String numberOfBooks;
}
Otherwise use setter injection for static variables, as follows:
#Component
class Config {
public static String numberOfBooks;
#Value("${number.of.books}")
public void setNumberOfBooks(String numberOfBooks) {
numberOfBooks = numberOfBooks;
}
}
After studying a little I've found out that the dependency injection happens after the constructor has been called. This being said the approach used was to use Autowired on my services constructor.
class ServiceImplementation implements Service {
private final String numberOfBooks;
#Autowired
private ServiceImplementation(Config config) {
this.numberOfBooks = config.getNumberOfBooks();
}
}
In this way Spring creates the dependency tree and makes sure that Config is not null when injected.
I have a model for service to use. But the autowired annotation returns null value. However this autowire works well in service. Is there any to do in the Module class?
public class Module{
private int id;
private String name;
private ArrayList<Function> functions;
#Autowired
private SysModuleLgDao sysModuleLgDao;
public Module() {
sysModuleLgDao.doSth();
}
}
This is my repo class:
#Repository
public interface SysModuleLgDao extends JpaRepository<SysModuleLgEntity, Long> {
public List<SysModuleLgEntity> findByModuleLgIdAndLanguageId(long moduleLgId,long languageId);
}
Object's constructor is called before Spring has a chance to do the wiring, so sysModuleLgDao reference is in its default state (i.e. null). Spring's context loading happens in a later phase, long after the default constructor has been called.
You can work around the problem by performing doSth in a different lifecycle phase, after the Spring bean has been constructed. This can be achieved via the #PostContstruct annotation:
public class Module {
#Autowired
private SysModuleLgDao sysModuleLgDao;
#PostConstruct
public void initialise() {
// at this point sysModuleLgDao is already wired by Spring
sysModuleLgDao.doSth();
}
}
More details here: Why use #PostConstruct?
I have an object reads configuration properties like this:
#ApplicationScoped
public class Configuration {
#Inject
#Config(value = "endpoint.base", defaultValue = "http://localhost:52885/consumers")
private String base;
public String getBase() { return base; }
}
this object is injected to a service object like this:
public class LoyaltyService {
final Sender sender;
final Configuration config;
#Inject
public LoyaltyService(Sender sender, Configuration config) {
this.sender = sender;
this.config = config;
}
}
I am now testing this service object with Mockito. I want to mock the Sender object, but I don't want to mock the configuration, or at least I just want to use the default value defined inside the object.
How can I do that in a Test object?
For example, I tried the following:
public class LoyaltyServiceTest {
#Mock
private Sender sender;
#Inject
private Configuration config;
private LoyaltyService target;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
when (sender.post(anyString(), anyString())).thenReturn("Post Success");
target =new LoyaltyService(sender, config);
}
}
It doesn't seem CDI will register the Config object at all. How does this work? Thanks!
It doesn't seem CDI will register the Config object at all.
The CDI beans are not initialised when running the test, only the mocked objects are.
MockitoAnnotations.initMocks only initializes
objects annotated with Mockito annotations for given testClass: #Mock, #Spy, #Captor, #InjectMocks.
You need to use a CDI test framework like cdi-unit or Pax Exam in your test class to create the non-mocked beans for you.