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
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