[Day 05] - 用Spring Boot 建立Service

一般而言,网站程序大多会是这样的架构:
https://ithelp.ithome.com.tw/upload/images/20210919/20128973GkwYpGQIUY.png
https://developer.mozilla.org/en-US/docs/Glossary/MVC

就是大家所熟知的MVC

这次实作的是前後端分离
因此View的部分先不管,专心弄Model跟Controller

昨天已经建立了User的entity跟repository
今天来做service

建立一个UserService.java

@Service
@RequiredArgsConstructor
public class UserService {
    
    private final UserRepo userRepo;

    public List<User> getAllUsers(){
        return userRepo.findAll();
    }
    public Optional<User> getOneUser(String email){
        return userRepo.findUserByEmail(email);
    }
    public void addUser(User user){
        userRepo.insert(user);
    }
}

在这支程序中我用了两个annotation

@Service 
@RequiredArgsConstructor

@Service是用来标记这是一个Service层的class,
Spring会将其建立并纳入Spring Container内等待调用
藉此达到控制反转(IoC)的效果

相似的annotation还有以下几样
@Repository
@Service
@Controller
这些全都是@Component的子延伸,目的都是建立物件丢到Spring Container里面

@RequiredArgsConstructor则lombok的annotation,跟刚刚spring boot的annotation没有关系
透过它可以在程序中自动建立建构子并且只包含定义为final的变数
也就是刚刚程序里面的
private final UserRepo userRepo;
相似的有@AllArgsConstructor、@NorgsConstructor就不再多做解释了

如果没用@RequiredArgsConstructor的话
就要自己写建构子了:

    public UserService(UserRepo userRepo){
        this.userRepo=userRepo;
    }

而这段又等同於

	@Autowired
    public UserService(UserRepo userRepo){
        this.userRepo=userRepo;
    }

因为在Spring4.3後的版本,对於只有一个建构子的情况下,
Spring 会自动帮你寻找Spring Container中是否有相配的bean,所以不用再加@Autowored

虽然也可以直接写

@Autowired 
private UserRepo userRepo;

这种写法叫Field Injection,很方便,
但这样的做法在特定情况下可能会造成程序执行时物件还没被注入导致nullpointerException
而且会提高这个class跟spring容器的耦合,可能会不好做单元测试
总之是官方不建议的写法,

而我最初的写法叫作Constructor Injection,是以建构子来注入的做法

还有另一种注入叫做Setter Injection,看起来就比较少人用,有兴趣的人可以再深入研究看看

最後再做个小补充,
Spring是将有相关注解(@Component、@Repository...etc)
的class在Spring初始执行时注册为一个Bean,
然後放到Spring Container内,
当有其他程序需要使用时
可以使用**@Autowired**注解来直接取用Spring Container中相对应的bean,
就不用再自己new一个物件了

但是我在UserRepo没有下@Repository的注解呀
为什麽@Autowired能够调用的到UserRepo呢?

这是因为UserRepo继承了MongoRepository
而其底层继承了Repository
只要继承Repository,Spring就会自动为其装配为一个bean,因此我们不用定义@Repository
程序就能在Spring Container中找到UserRepo。

这样层层继承下来,不会因此而让每个介面都被装配成bean吗
UserRepo←MongoRepository←PagingAndSortingRepository←CrudRepository←Repository
https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/repository/MongoRepository.html

因为这些介面都有定义@NoRepositoryBean,告诉Spring这些介面没有要被做成bean
所以没这个问题。

第5天了,原本想利用连假多写几天的稿备用,但是光是查文件就花了好多时间...
虽然Spring Boot写起来容易,但背後要懂的知识点还真是不少...
/images/emoticon/emoticon20.gif


<<:  DAY05 - API串接常见问题 - CORS - 概念篇 (2)

>>:  学习Python纪录Day5 - tuple、list和dictionary的应用

EP 17: The MenuItem of ListView binds Command in ViewModel - Way 2

Hello, 各位 iT邦帮忙 的粉丝们大家好~~~ 本篇是 Re: 从零开始用 Xamarin 技...

JavaScript Day19 - AJAX(1)

AJAX AJAX(Asynchronous JavaScript And XML):使用非同步资料...

iOS APP 开发 OC 第二十天,自动释放池

tags: OC 30 day 自动释放池的原理 存入到自动释放池中的对象,在自动释放池被销毁的时候...

30天零负担轻松学会制作APP介面及设计【DAY 22】

大家好,我是YIYI,今天我要来聊聊我的架构图。 文字版架构图 以COVER放在第一层 第二层是HO...

Vue.js指令(v-on)绑定(DAY29)

v-on (事件绑定) v-on是用来绑定event事件的指令,就像之前javascript介绍过...