Is passing a service as a parameter bad practice? - java

Suppose I want to use a service in a POJO class, like an implementation of some sort, can I just pass this service as a parameter to this POJO? Or would that be bad practice?
#Service
public class MyService {
// Inject AnotherService in this service
public MyService(AnotherService anotherService) {
// Now pass that service in a POJO
SomeImplementation impl = new SomeImplementation(anotherService);
}
}
public class SomeImplementation {
public SomeImplementation(AnotherService anotherService) {
// start using the AnotherService here...
}
}
For this example I used Java and Spring, but this question applies to all languages with dependency injection.

I would say that it's just not making use of the framework you're operating within. That's exactly what DI is for: letting the container handle the components and their relations (eg. if you inject sth multiple times, DI helps you avoid multiple instantiations).
Now, in your case, you can use the #Configurable annotation, which adds a POJO component to the Spring context, and so lets you inject stuff into it.

Related

How to set the attributes to a generic #Service class in Spring Boot?

I'm new to Spring Boot, so bare me with my basic question here.
I want to build a generic #Service class that has well defined methods that don't even need to be overwritten.
The only thing this class needs is to adjust its attributes based on which Controller method was called. Basically, this class works as a Job handler that needs to adjust some parameters so its methods can perform what they're supposed to compute. The job will always have the same workflow, calling the methods in the same order, but it will obtain different results depending on the parameters/attributes it receives, which, as I said before, are defined by the controller methods.
The only attribute it has beside the ones that adjust the job's workflow is an autowired #Repository object that will save the results of the job in a database.
Maybe I could simply instantiate an Job Handler object and call a constructor with the paramaters I need for the job, but I don't know what is the "Spring way" of doing this, considering how Spring works with dependency injection and I need a #Repository object embbeded into the Job Handler service.
I would really appreciate if anyone could write a sample code/example so I could understand how this can be done with Spring Boot so I don't have to duplicate code or Service Classes.
The Spring way for this case would be to create a Bean of your JobHandler, where you inject the necessary dependencies, like your Repository:
#Configuration
class MyConfiguration {
#Bean
MyJobHandler myJobHandler(MyRepository myRepository) {
return new MyJobHandler (myrepository);
}
}
Alternatively, if you do not want a configuration class, you could declare your JobHandler as a Component and inject the repository in the constructor:
#Component
class MyJobHandler {
private MyRepository myRepository;
public MyJobHandler myJobHandler(MyRepository myRepository) {
this.myRepository = myRepository;
}
}

Dependency injection according to conditions

My controller:
#RestController
#RequestMapping("/mypath")
public class MyController {
#Autowired
MyServiceInterface service;
#PostMapping("/{source}")
void myControllerFunc(#PathVariable String source, #RequestBody MyObject obj) {
...
Object myServiceObj = service.myServiceFunc(param);
...
}
}
My Service Interface:
public interface MyServiceInterface {
Object myServiceFunc(String param);
}
My Service Implemantations:
#Service
public class MyServiceOne {
Object myServiceFunc(String param) {
...
}
}
#Service
public class MyServiceTwo {
void myServiceFunc(String param) {
...
}
}
My spring-boot version : 1.5.7
I want to inject the service according to my path variable ("source") . If source = one, inject MyServiceOne or if source = two, inject MyServiceTwo.
Is this possible?
It sounds like you need both of these to be available and each method invocation on the controller can choose a different one. So wire up both implementations, with a qualifier to distinguish them. Use the path variable in the controller method and let it decide programmatically which service to call.
I don't think it's possible and reasonable.
A #RestControllers is by nature a singleton. It gets configured at startup and remains the same for every request.
The expression /{source} is being evaluated during a request at runtime, when the controller has already been set up.
The options to consider:
Inject both services and, in the method, decide which one to pick.
Create two separate controllers for each service.
Utilise the application context and extract beans from there.
As described in Get bean from ApplicationContext by qualifier, you could add qualifiers to each service implementations and have something like this in the myControllerFunc:
BeanFactoryAnnotationUtils.qualifiedBeanOfType(ctx.getBeanFactory(), MyServiceInterface.class, source)

What is the right design pattern to get a prototype-bean from a component-bean?

I am just wondering what a good architecture design looks like.
Let's say we have a CarRepository which manages all beans of type Car in a car rental application.
Car beans are of type prototype
CarRepository bean is of type repository (singleton)
Now, the CarRepository is asked to create a new Car bean, e.g. when the rental company has bought a new car.
Of course, I could implement ApplicatioContextAware and use context.getBean("car"), but for me, it doesn't fit well to the idea of dependency injection. What is best-practice for injecting a shorter-lived bean into a singleton?
Update: Maybe I should add an example to make it more clear.
#Repository
public class CarRepository {
private List<Car> cars;
public void registerNewCar(int id, String model) {
// I don't want to access the Spring context via ApplicationContextAware here
// Car tmp = (Car) context.getBean("car");
// car.setId(id);
// car.setModel(model);
// cars.add(tmp);
}
}
#Scope("prototype")
public class Car {
private int id;
private String model;
// getter and setters
}
Spring offers a mechanism that handles injecting a shorter-lived bean in a longer-lived one. It's called a scoped proxy. How it works is that the singleton is injected with a proxy that will handle method calls by searching the shorter scope (like session or request) for a bean instance and delegating to that instance.
You didn't specify, if you are using xml or annotations to configure your application or what version of Spring you are using. You can read more about configuring the scope proxy with xml in the reference guide. I'm going to give you an example how to configure it with annotations in a Spring 4-ish environment.
For me the best way is to use the meta-annotations mechanism. It allows you to create your own annotations that will be later used by Spring to configure your app. For example:
#Retention(RUNTIME)
#Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.TARGET_CLASS)
public #interface SessionScoped{
}
Such an annotation, when specified on a #Component (or #Service or any other specialization) class or a #Bean method in your Java config will cause that bean to be injected as a proxy. For example:
#Configuration
#EnableAspectJAutoProxy
public class MyConfig{
#SessionScoped
#Bean
public MyClass myBean(){
// return your bean
}
}
All that being said, your example really makes me think you should be working with entities (Car) and repositories. See Spring Data if you are designing the model layer and you want to store Cars data in your database etc.
If you do not want to use context.getBean(...), then, you can construct the car using new Car(...) as it will have same effect.
These are only two ways!

Tapestry5 use Inject service with constructor args

I'm a Tapestry5 user and wondering how I would #Inject a service class with a few arguments. From my understanding, using #Inject to inject a service class is very similar to instantiating a class with new MyClass();. The problem I seem to be having is I'm not sure how to pass the arguments into the service.
Example
Using Tapestry Servce
public class Main {
#Inject
private MyService myService;
public Main() {
//Where would I pass in my arguements?
this.myService();
//I can't seem to do this by passing the arg's in through
//this.myService(arg1, arg2) unless I may be missing something.
}
}
Traditional Usage
public class Main {
public Main() {
//In this example I can pass my arg's into the constructor.
MyService myService = new MyService(arg1, arg2);
}
}
You are not quite right in assuming that #Inject is similar to instantiation. You might somewhat argue this when your service is annotated with #Scope(ScopeConstants.PERTHREAD) but even then, tapestries IoC will instantiate the service for you. I find that most of my services are instantiated only once by tapestry and #Inject'ing them gives me a reference to this service. If you want to #Inject a service you will first need to define it with your AppModule. The simplest way to make your service available though the IoC is to bind it like so in your AppModule:
public static void bind(ServiceBinder binder) {
binder.bind(ServiceInterface.class, ServiceImplementation.class)
}
Then in your pages/components you can #Inject the interface like:
#Inject
private ServiceInterface service;
If your service then needs constructor arguments, you can create a constructor in your ServiceImplementation.class taking your required arguments. If those arguments are in themselves bound services, tapestry will figure this out and you're done. If these arguments are not services known to Tapetsry and you can't bind them for whatever reason, you can create a build method in your AppModule:
/**
* These methods may in them selves take bound services as arguments helping you build your new service
*/
public ServiceInterface buildServiceInterface(AnotherBoundService service2) {
...
return new ServiceImplementation(service2, someMoreArgsIfRequired)
}
Might you not want to use the IoC, you can always just instantiate the service in your page/component because they are just simple pojo's. Do have a look at the IoC documentation. It nicely outlines all powerful features at your disposal.

Spring start a transaction with object created by new

I have a POJO class with a method annotated with #Transactional
public class Pojo {
#Transactional
public void doInTransaction() {
...
}
}
Spring declarative transaction management is based on AOP but I don't have any experience with that. My question is:
Is it possible that when invoking the (new Pojo).doInTransaction() alone, Spring will start a Transaction.
Spring declarative transaction
management is based on APO but I don't
have any experience with that.
I would recommend to start working with it and you will get the experience of using transaction advices using AOP. A good starting point is here.
Is it possible that when invoking the
(new Pojo).doInTransaction() alone,
Spring will start a Transaction.
No, you can't expect Spring to be aware of a bean that you manually invoked. However, it sounds like that you are wanting to avoid declarative transaction management and do programmatic transaction management. There is a way to do that with Spring using the Transaction Template. Is that what you were looking for?
It is somewhat possible, but in a cumbersome way: You must use the AutowireCapableBeanFactory mechanism.
Here is a transactional class as example
public interface FooBar{
void fooIze(Object foo);
}
public class FooBarImpl implements FooBar{
#Transactional
#Override
public void fooIze(final Object foo){
// do stuff here
}
}
And here is how we can use it:
public class FooService implements ApplicationContextAware{
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(
final ApplicationContext applicationContext){
this.applicationContext = applicationContext;
}
public void serviceMethod(){
//declare variable as interface, initialize to implementation
FooBar fooBar = new FooBarImpl();
// try to use it, won't work, as it's not a proxy yet
Object target = new Object[0];
fooBar.fooIze(target); // no transaction
// now let spring create the proxy and re-assign the variable
// to the proxy:
fooBar = // this is no longer an instance of FooBarImpl!!!
(FooBar) applicationContext
.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(fooBar,
"someBeanName");
fooBar.fooIze(fooBar); // this time it should work
}
}
This is not a best practice. For one thing, it makes your application highly aware of the Spring Framework and also, it violates the dependency injection principles. So use this only if there is no other way!
Yes, it is possible. Spring does not require the use of dynamic proxies for #Transactional to work. Instead, you can use "true AOP", as provided by AspectJ.
For the details, see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#transaction-declarative-aspectj
The way Spring handle the transaction through Annotation is using AOP as you've said.
The AOP bit is implemented using Dynamic Proxies (see doc)
So in order to do so you'll need to retrieve an instance of your class (Pojo here) through the spring container since to make it work, Spring will return you a Dynamic Proxy over your Pojo that will automatically surround any annotated method with the transaction management code.
If you simply do a
Pojo p = new Pojo();
p.doInTransaction();
Spring doesn't have any role to play here and your method call won't be inside a transaction.
so what you need to do is something like this
ApplicationContext springContext = ...;
Pojo p = (Pojo) springContext.getBean("your.pojo.id");
p.doInTransaction();
Note: this is an example, you should prefer dependency injection instead of retrieving your bean manually from the context
By doing so, and with a properly configured Spring Context, Spring should have lookout your classes to scan for transactional annotation and automatically wrapped your beans into annotation aware dynamic proxies instances. From your point of view that doesn't change anything, you'll still cast your object to your own Classes, but if you try to print out the class name of your spring context Pojo bean, you'll get something as Proxy$... and not your original class name.
Have a look at this link anyway : link text

Categories