I’m writing some tool class which should be a singleton. However, the functionality of the class requires dependencies on some service interfaces, i.e. PullRequestService. I’m facing the dilemma about how can I make it both singleton and correctly import those services.
When I was reading some documents of Java Bean, I realized that the Inject annotation can also be applied to a public setter. I tried writing something employing the setter, but it’s no use. Any ideas?
public class ConflictStrategy extends AbstractMergeStrategy implements IMergeStrategy {
static public final IMergeStrategy instance = new ConflictStrategy();
@ComponentImport
private PullRequestService pullRequestService; // This is null in runtime
private ConflictStrategy() {}
@Inject
public void setComponents(PullRequestService pullRequestService) {
this.pullRequestService = pullRequestService;
}
public MergeCheckResponse inspect(MergeCheckRequest request) {
PullRequest pullRequest = request.getPullRequest();
if (pullRequestCanMerge(pullRequest).isConflicted()) {
return buildResponse(false, "Pull request conflicts");
} else {
return buildResponse(true, "No merge conflicts");
}
}
private PullRequestMergeability pullRequestCanMerge(PullRequest pullRequest) {
final long pullRequestID = pullRequest.getId();
final int repoID = pullRequest.getFromRef().getRepository().getId();
return this.pullRequestService.canMerge(repoID,pullRequestID);
}
}
Hi @tommy.zhang,
You can define your tool class as a @Component
, have a look at this tutorial for how to use @Component
and @Autowired
. Spring scanning has been defined here. The complete tutorial could be found here.
Hope this helps.
Cheers,
Justin
1 Like
To make it clear: that xsd file is the file listed in the given example of plugin-context.xml
. And @autowired
as well as @ComponentImport
on setters help nothing, which implies pullRequestService
is still null. I doubt the 404 of the xsd is to blame.
The following code is what I have employed now:
@Component("ConflictStrategy")
public class ConflictStrategy extends AbstractMergeStrategy implements IMergeStrategy {
static public final IMergeStrategy instance = new ConflictStrategy();
private PullRequestService pullRequestService;
private ConflictStrategy() {}
@Autowired
public void setComponents(@ComponentImport PullRequestService pullRequestService) {
this.pullRequestService = pullRequestService;
}
public MergeCheckResponse inspect(MergeCheckRequest request) {
PullRequest pullRequest = request.getPullRequest();
if (pullRequestCanMerge(pullRequest).isConflicted()) {
return buildResponse(false, "Pull request conflicts");
} else {
return buildResponse(true, "No merge conflicts");
}
}
private PullRequestMergeability pullRequestCanMerge(PullRequest pullRequest) {
final long pullRequestID = pullRequest.getId();
final int repoID = pullRequest.getFromRef().getRepository().getId();
return this.pullRequestService.canMerge(repoID,pullRequestID);
}
}
I am not sure static public final IMergeStrategy instance = new ConflictStrategy();
and setting the constructor as private
is the correct way of defining a component.
Have a look at this tutorial to see how a Component
can be defined and usage of TodoService
could be found here.
After changes your class should look something like this:
@Component("ConflictStrategy")
public class ConflictStrategy extends AbstractMergeStrategy implements IMergeStrategy {
private PullRequestService pullRequestService;
@Autowired
public ConflictStrategy(@ComponentImport PullRequestService pullRequestService) {
this.pullRequestService = pullRequestService;
}
....
What are you trying to accomplish with this plugin?
Hi @jthomas,
Thanks for your reply. I’m writing a plugin to merge a pull-request impersonating a designated ApplicationUser if some checks performed on that PR get satisfied. One of those checks is whether PR conflicts or not. But that’s not the main problem I’m facing.
Yes, I understand making the constructor public and add @Autowired
annotation on the constructor as well as add @ComponentImport
annotation on the constructor parameters is the working solution on how to inject service components.
However, according to the Javadoc of Spring, it is possible to add @Autowired
on public setter methods to import the dependent service components. You may feel wired that why I want to use a setter to import components rather than directly do it on a public constructor. While that’s related to the singleton design pattern.
One common way to implement the singleton pattern in Java is declaring a private static field referring to the only instance of the class, designing a public static method called getInstance()
to instantiate or retrieve the already instantiated instance. To prevent getting an instance of the singleton class without getInstance()
, the constructor of the class should be private.
My version of the singleton classes is not that standard. Rather than declare the static instance field as private, I declared it as public to get rid of the getInstance()
method. However, the instance still requires a correct instantiation or a dummy instantiation followed by a correct initialization. The former requires @Autowired
and @ComponentImport
on a public constructor, the latter requires a dummy constructor and @Autowired
as well as @ComponentImport
on a public setter.
But, the @Autowired
and @ComponentImport
on the public setter does not work.
Sincerely,
Tommy
Hi @a.belostotskiy
Thanks for your reply. Yes, that’s helpful for me. I was thinking about getting rid of the static field as it initialized before those Java Beans.
Tommy