Search code examples
springspring-bootannotationsspring-annotations

Why should I use @Service stereotype in Spring Boot?


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

Solution

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