I understand that @Service is a type of @Component just as @Controller and @Repository are. I also understand that these type-level (class level) stereotypes help component scan pick up different types in your spring project.
I noticed that a class I created FileResourceManager that handles business logic works just fine without @Service. I want to better understand what this stereotype does, and why it is useful. If it helps to explain @Component please do.
Here is an example class I was tempted to add @Service to because it prepares files from information gathered from a @Controller classes for a package irrelevant to spring that runs business logic
@Service
public class FileResourceManager {
private ConfigProp configProp;
@Autowired
private FormModel formModel;
public FileResourceManager(FormModel formModel) {
this.formModel = formModel;
}
public FormModel getformModel() {
return formModel;
}
public void init() throws IOException {
System.out.println("Initializing resources...");
clearDirectories();
writeTextboxToInputDirectories();
writeMultifileToInputDirectories();
}
private void clearDirectories() throws IOException {
configProp = configProp.getInstance();
RwUtils.clearDirectory(System.getProperty("user.dir")+configProp.getProperty("input.dir")+"input/");
RwUtils.clearDirectory(System.getProperty("user.dir")+configProp.getProperty("input.dir")+"file/");
}
private void writeMultifileToInputDirectories() throws IOException {
configProp = configProp.getInstance();
String inputDir = System.getProperty("user.dir")+configProp.getProperty("input.dir")+"input/";
String fileDir = System.getProperty("user.dir")+configProp.getProperty("input.dir")+"file/";
RwUtils.writeMultipartIntoFile(fileDir,formModel.getFile1());
RwUtils.writeMultipartIntoFile(fileDir,formModel.getFile2());
RwUtils.writeMultipartIntoFile(fileDir,formModel.getFile3());
for (MultipartFile record: formModel.getFiles4()) {
RwUtils.writeMultipartIntoFile(inputDir,record);
}
}
private void writeTextboxToInputDirectories() throws IOException {
configProp = configProp.getInstance();
String inputDir = System.getProperty("user.dir")+configProp.getProperty("input.dir")+"input/";
String fileDir = System.getProperty("user.dir")+configProp.getProperty("input.dir")+"file/";
if(formModel.getFile1File().isEmpty()) RwUtils.writeStringToFile(fileDir+"file1.txt",formModel.getFile1Text());
if(formModel.getFile2File().isEmpty()) RwUtils.writeStringToFile(fileDir+"file2.txt",formModel.getFile2Text());
if(formModel.getFile3File().isEmpty()) RwUtils.writeStringToFile(fileDir+"file3.txt",formModel.getFile3Text());
int i = 1;
String recordFileStr;
for (String record:formModel.getOldJsonText().split(";;;")) {
recordFileStr = inputDir+"textboxRecord"+i+".json";
RwUtils.writeStringToFile(recordFileStr,record);
i++;
}
}
// this class calls logic from a package that has no relation to spring-boot framework
public void runBusinessLogic() throws IOException, InvalidEntryException {
// logic
}
public void download(ZipOutputStream zippedOut) throws IOException {
ConfigProp configProp = ConfigProp.getInstance();
String downloadFormat = getformModel().getDownloadFormat();
FileSystemResource resource;
ZipEntry zipEntry;
File dir;
for (String dirStr:downloadFormat.split("-")) {
dir = new File(System.getProperty("user.dir")+configProp.getProperty("output.dir")+dirStr);
for (File file:dir.listFiles()) {
resource = new FileSystemResource(System.getProperty("user.dir")+configProp.getProperty("output.dir")+dirStr+"/"+file.getName());
zipEntry = new ZipEntry(file.getName());
// Configure the zip entry, the properties of the file
zipEntry.setSize(resource.contentLength());
zipEntry.setTime(System.currentTimeMillis());
// etc.
zippedOut.putNextEntry(zipEntry);
// And the content of the resource:
StreamUtils.copy(resource.getInputStream(), zippedOut);
zippedOut.closeEntry();
}
}
zippedOut.finish();
}
public static void main(String[] args) throws IOException {
// used to test runBusinessLogic() method
}
}
Here is an example service class where @Service seems significant
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private MyUserRepository repo;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MyUser user = repo.findByUsername(username);
if(user==null)
throw new UsernameNotFoundException("Username not found");
return new UserPrincipal(user);
}
public MyUser getUser(String username) throws UsernameNotFoundException {
MyUser user = repo.findByUsername(username);
if(user==null)
throw new UsernameNotFoundException("Username not found");
return user;
}
public MyUser createUser(MyUser user) {
String email = user.getEmail();
String username = user.getUsername();
if(repo.existsUserByEmail(email)) {
user = repo.findByEmail(email);
} else if(repo.existsUserByUsername(username)) {
user = repo.findByUsername(username);
} else {
this.repo.save(user);
user = repo.findByEmail(email);
}
return user;
}
}
If a java class is annotated with @Service
, Spring will manage it and it can enjoy the benefits provided by Spring such as applying some AOP magics (such as those @Transactional
, @Async
, @PreAuthorize
etc. stuff) to it. Also other instance can use @Autowired
to access it rather than DIY (i.e. dependency injection idea). Think about that if your object is consisted of many dependencies which in turn consisted of many dependencies , configuring that instance to have a correct dependencies graph manually may not be an enjoyable thing to do. Not to mention the dependencies object should be come from the correct scope.
On the other hand, if a java class does not need any features provided by Spring , perhaps it is very simple or it is only be used by some class internally , you can keep it simple and simply create it by yourself such as :
ResourceManager = new FileResourceManager(formModel);
So it depends on if you need the spring features on a class. If you do not need it, you can keep it simple do not need to annotate @Service
on it.