clzola
clzola

Reputation: 2025

How to inject service in controller's method?

I have problem with injecting service in controller's method. I have following service:

@Service
@RequiredArgsConstructor
public class CreateFranchiseUseCase {
    private final FranchiseService franchiseService;
    private final UserService userService;
    private final ACLEntryService aclEntryService;
    private final FranchiseDTOMapper franchiseDTOMapper;

    public Franchise execute(CreateFranchiseDTO dto) {
        // ...
    }
}

And then in Controller:

@RestController
@RequiredArgsConstructor
public class FranchiseController {

    // ...
    
    @PostMapping("/api/franchises")
    public ResponseEntity<Object> createFranchise(CreateFranchiseDTO dto, CreateFranchiseUseCase useCase) {
        Frachise franchise = useCase.execute(dto);
        // ...
    }
}

When I call this endpoint I get NullPointerException. The exception occurs in execute method and debugging the method I discovered that all attributes: franchiseService, userService, aclEntryService and franchiseDTOMapper are NULL.

All classes: FranchiseService, UserService and ACLEntryService are annotated with @Service annotation. And FranchiseDTOMapper is annotated with @Mapper(componentModel = "spring")

Does Spring support Method Injection? Alternativly is there any way I can request instance of CreateFranchiseUseCase class inside createFranchise() method like so:

@PostMapping("/api/franchises")
public ResponseEntity<Object> createFranchise(CreateFranchiseDTO dto) {
    CreateFranchiseUseCase useCase = container.get(CreateFranchiseUseCase.class) // <-- something like this
    Frachise franchise = useCase.execute(dto);
    // ...
}

Upvotes: 2

Views: 10989

Answers (3)

Does Spring support Method Injection?

No. Receivable objects by arguments is listed here:

You can use ApplicatonContext#getBean():

    @Autowired
    private ApplicationContext context;

    @PostMapping("/api/franchises")
    public ResponseEntity<Object> createFranchise(CreateFranchiseDTO dto) {
        reateFranchiseUseCase useCase = context.getBean(CreateFranchiseUseCase.class);
        // ...
    }

If CreateFranchiseUseCase object instanciation by each request is required, you need to add @Scope(WebApplicationContext.SCOPE_REQUEST):

@Service
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class CreateFranchiseUseCase {

Upvotes: 3

pldpsk
pldpsk

Reputation: 1

You can inject it like this, but consider injecting service through your controller constructor.

@PostMapping("/api/franchises")
public ResponseEntity<Object> createFranchise(CreateFranchiseDTO dto) {
    CreateFranchiseUseCase useCase = new CreateFranchiseUseCase();
    Frachise franchise = useCase.execute(dto);
    // ...
}

edit: But remember that you have to inject other services to CreateFranchiseUseCase. You can just do this in constructor like that:

public CreateFranchiseUseCase () {
    FranchiseService franchiseService = new FranchiseService();
    UserService userService = new UserService();
    ACLEntryService aclEntryService = new ACLEntryService();
    FranchiseDTOMapper franchiseDTOMapper = new FranchiseDTOMapper();
}

or just inject it without constructor like that:

@Service
@RequiredArgsConstructor
public class CreateFranchiseUseCase {
    private final FranchiseService franchiseService = new FranchiseService();
    private final UserService userService = new UserService();
    private final ACLEntryService aclEntryService = new ACLEntryService();
    private final FranchiseDTOMapper franchiseDTOMapper = new FranchiseDTOMapper();

    public Franchise execute(CreateFranchiseDTO dto) {
        // ...
    }
}

Upvotes: 0

Jan Schmitz
Jan Schmitz

Reputation: 1641

As far as I know it is not possible to let Spring inject Objects into methods.
Instead you can let Spring inject the object through the constructor of your controller.

This would lead to something like this:

@RestController
public class FranchiseController {

    private final CreateFranchiseUseCase createFranchiseUseCase;

    public FranchiseController(CreateFranchiseUseCase createFranchiseUseCase) {
        this.createFranchiseUseCase = createFranchiseUseCase;
    }

    @PostMapping("/api/franchises")
    public ResponseEntity<Object> createFranchise(CreateFranchiseDTO dto) {
        Frachise franchise = createFranchiseUseCase.execute(dto);
        // ...
    }
}

This should also work even when you use Lombok to create your constructor.

Upvotes: 1

Related Questions