欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring?Service中的@Service注解的使用小結(jié)

 更新時間:2024年11月25日 09:17:42   作者:阿乾之銘  
本文主要介紹了Spring?Service中的@Service注解的使用小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

@Service注解是Spring框架中用于標(biāo)識業(yè)務(wù)邏輯層(Service層)的注解。它是Spring組件掃描機(jī)制的一部分,表明這個類包含業(yè)務(wù)邏輯,并且應(yīng)該由Spring容器管理為一個Spring Bean。它與@Component類似,都是標(biāo)識一個類為Spring管理的Bean,但@Service通常用于專門標(biāo)識業(yè)務(wù)邏輯類。

1. @Service的基本功能

@Service是一個特殊的@Component,它本質(zhì)上是@Component的派生注解。通過使用 @Service,我們可以告訴Spring容器去自動掃描和注冊這些類為Bean,供依賴注入使用。

@Service
public class UserService {
    public User getUserById(Long id) {
        // 業(yè)務(wù)邏輯代碼
        return new User(id, "John Doe");
    }
}

在這個例子中,UserService類被@Service注解標(biāo)識,Spring會將它作為Bean注冊到應(yīng)用上下文中。

2. 如何與@Autowired結(jié)合使用 

 @Autowired注解用于將Spring容器中的Bean自動注入到其他類的字段、構(gòu)造器或方法中。@Autowired可以用于控制器、服務(wù)層或其他任何需要依賴注入的地方。

代理對象的獲取是通過 Spring 的依賴注入機(jī)制實(shí)現(xiàn)的。你在使用的業(yè)務(wù)類(如 UserService)被 Spring 掃描到并管理為 Bean 后,Spring 會自動為它生成代理對象,并將該代理對象注入到你需要的地方。

這里依賴注入的是代理對象。 

2.1常見的@Autowired用法

使用場景

@Autowired 一般用于注入其他 Spring 容器管理的 Bean,適用于以下場景:

  • 服務(wù)類之間的依賴:當(dāng)一個服務(wù)類依賴于另一個服務(wù)類時。
  • 控制器類依賴服務(wù)類:在 Web 應(yīng)用中,控制器通常需要調(diào)用服務(wù)類。
  • 服務(wù)類依賴數(shù)據(jù)訪問層:使用 @Autowired 將數(shù)據(jù)訪問層(例如 Repository 類)注入到服務(wù)類中。

2.1.1構(gòu)造器注入(推薦方式)

使用構(gòu)造器注入能確保依賴在類實(shí)例化時就被正確注入,并且方便進(jìn)行單元測試。

@RestController
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return ResponseEntity.ok(user);
    }
}

@Autowired注解使用在構(gòu)造函數(shù)前。

當(dāng)你在構(gòu)造函數(shù)的參數(shù)中將經(jīng)過@Service注解的類的對象作為參數(shù),spring容器會自動幫你創(chuàng)建這個類(UserService)的實(shí)例,然后把這個創(chuàng)建好的實(shí)例對象引用給該構(gòu)造函數(shù)所攜帶的參數(shù) userService,相當(dāng)于就是自動進(jìn)行了UserService userService =new UserService();

這里的依賴注入更精確的說,是對構(gòu)造函數(shù)的參數(shù)進(jìn)行依賴注入。

然后現(xiàn)在userService就是被創(chuàng)建好了的對象,然后再將這個對象的值賦值給這個類的成員變量private final UserService userService(這里的this.userService就是指這個類內(nèi)部的變量private final UserService userService中的userService,為什么要這樣做呢?因?yàn)槟惝?dāng)時依賴注入的對象是構(gòu)造函數(shù)參數(shù)中的對象,就會導(dǎo)致它是作為局部變量,一旦構(gòu)造函數(shù)執(zhí)行完畢,這些局部變量就會被釋放,所以你需要有一個地方來存儲這個實(shí)例(保存到類的成員變量中),以便在類的其他方法中使用它。這就是為什么要在@Autowired注解依賴注入之前先定義private final UserService userService這個成員變量。 

2.1.2字段注入 

使用@Autowired直接注入到類的成員變量中。這是最常見但不推薦的方式,因?yàn)樗沟靡蕾囮P(guān)系不那么顯式,并且在單元測試中可能不太靈活。

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return ResponseEntity.ok(user);
    }
}

@Autowired注解使用在類的成員變量之前。 

直接對你所創(chuàng)建的成員變量進(jìn)行依賴注入,相當(dāng)于private UserService userService = new UserService();

2.1.3Setter注入 

通過提供一個setter方法來注入依賴,盡管使用頻率較低,但它可以在某些需要動態(tài)設(shè)置依賴的場景中使用。

@RestController
public class UserController {

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return ResponseEntity.ok(user);
    }
}

 @Autowired注解使用在類的setter方法之前。

這種方式其實(shí)跟構(gòu)造器注入很相似,@Autowired注解都是用在函數(shù)之前,依賴注入都是對方法中的參數(shù)進(jìn)行依賴注入。

只不過唯一的區(qū)別就是構(gòu)造器注入是在你創(chuàng)建對象的時候會自動對成員變量userService進(jìn)行賦值,而這中方式則是在你調(diào)用userService的setter方法時才會對userService進(jìn)行賦值。

所以這種依賴注入方式一般不用。

2.1.4 不需要@Autowired注解的情況

1. 構(gòu)造函數(shù)注入

從Spring 4.3開始,如果一個Bean只有一個構(gòu)造函數(shù),Spring會自動使用該構(gòu)造函數(shù)進(jìn)行依賴注入,無需@Autowired注解。

@Service
public class UserService {
    private final UserRepository userRepository;

    // 不需要@Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

2. 單一實(shí)現(xiàn)的接口

如果一個接口只有一個實(shí)現(xiàn)類,Spring會自動將這個實(shí)現(xiàn)類注入到需要該接口的地方,無需額外配置。

public interface MessageService {
    String getMessage();
}

@Service
public class EmailService implements MessageService {
    public String getMessage() {
        return "Email message";
    }
}

@Controller
public class MessageController {
    private final MessageService messageService;

    // 自動注入EmailService,無需@Autowired
    public MessageController(MessageService messageService) {
        this.messageService = messageService;
    }
}

 3. @Configuration類中的@Bean方法

在@Configuration類中定義的@Bean方法可以直接使用其他Bean作為參數(shù),Spring會自動注入。

@Configuration
public class DatabaseConfig {
    @Bean
    public DataSource dataSource() {
        // 創(chuàng)建并返回DataSource
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        // Spring自動注入上面定義的dataSource
        return new JdbcTemplate(dataSource);
    }
}

ResponseEntity<User>是什么類型?

ResponseEntity<User>是一個Spring框架中的泛型類,用于構(gòu)建HTTP響應(yīng)。它表示一個封裝了HTTP響應(yīng)的實(shí)體,包含了HTTP狀態(tài)碼、響應(yīng)頭、以及響應(yīng)體。

  • 泛型參數(shù) <User> 指的是響應(yīng)體的類型。在這個例子中,<User>表示HTTP響應(yīng)體中會返回一個User類型的對象。

  • ResponseEntity的主要功能是可以更靈活地控制HTTP響應(yīng):

    • 狀態(tài)碼:你可以使用ResponseEntity指定HTTP狀態(tài)碼,比如200(OK)、404(Not Found)等。
    • 響應(yīng)頭:你可以添加自定義的響應(yīng)頭。
    • 響應(yīng)體:響應(yīng)的內(nèi)容可以是任何類型,在這個例子中是User類型的對象。

 ResponseEntity的構(gòu)造方法和常用方法:

  • ResponseEntity.ok(T body):返回200狀態(tài)碼,響應(yīng)體是傳入的對象。
  • ResponseEntity.status(HttpStatus status):自定義狀態(tài)碼,結(jié)合.body(T body)可以設(shè)置響應(yīng)體。
  • ResponseEntity.notFound():返回404狀態(tài)碼。
  • ResponseEntity.noContent():返回204狀態(tài)碼,不帶響應(yīng)體。

2.2 @Service與@Autowired結(jié)合的典型場景

場景1:控制層注入Service層

在Spring MVC的控制層(@Controller@RestController)中,業(yè)務(wù)邏輯通常委托給服務(wù)層處理。這種場景下,控制層會通過@Autowired注解注入@Service標(biāo)識的類。

// UserService.java
@Service
public class UserService {

    public User getUserById(Long id) {
        return new User(id, "John Doe");
    }
}

// UserController.java
@RestController
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return ResponseEntity.ok(user);
    }
}
  • UserController中,UserService通過@Autowired注解進(jìn)行構(gòu)造器注入,確保UserController可以調(diào)用UserService中的業(yè)務(wù)邏輯方法。

場景2:服務(wù)層之間相互調(diào)用

在復(fù)雜的業(yè)務(wù)場景中,一個Service類可能會依賴另一個Service類,這時也可以使用@Autowired進(jìn)行注入。

// OrderService.java
@Service
public class OrderService {

    public String processOrder(Long orderId) {
        return "Order processed: " + orderId;
    }
}

// PaymentService.java
@Service
public class PaymentService {

    private final OrderService orderService;

    @Autowired
    public PaymentService(OrderService orderService) {
        this.orderService = orderService;
    }

    public String makePayment(Long orderId) {
        String result = orderService.processOrder(orderId);
        return "Payment completed for " + result;
    }
}

PaymentService依賴于OrderService,通過構(gòu)造器注入的方式,將OrderService作為依賴注入到PaymentService中。

2.3 依賴注入的高級使用場景

2.3.1 使用@Qualifier區(qū)分多個Bean

在某些情況下,如果Spring容器中有多個同類型的Bean(例如多個@Service),需要通過@Qualifier注解來明確指定注入的具體Bean。

@Service("basicOrderService")
public class BasicOrderService implements OrderService {
    // 實(shí)現(xiàn)邏輯
}

@Service("advancedOrderService")
public class AdvancedOrderService implements OrderService {
    // 實(shí)現(xiàn)邏輯
}

@Service
public class PaymentService {

    private final OrderService orderService;

    @Autowired
    public PaymentService(@Qualifier("basicOrderService") OrderService orderService) {
        this.orderService = orderService;
    }

    // 業(yè)務(wù)邏輯
}
  • 通過@Qualifier("basicOrderService"),我們指定注入的具體實(shí)現(xiàn)類BasicOrderService。

2.3.2結(jié)合@Primary注解

@Primary注解用于標(biāo)識在多個相同類型的Bean中優(yōu)先注入某個Bean。如果沒有使用@Qualifier指定Bean,Spring會注入@Primary標(biāo)注的Bean。

@Service
@Primary
public class BasicOrderService implements OrderService {
    // 實(shí)現(xiàn)邏輯
}

@Service
public class AdvancedOrderService implements OrderService {
    // 實(shí)現(xiàn)邏輯
}
  • BasicOrderService標(biāo)注了@Primary,因此在沒有指定@Qualifier的情況下,Spring會優(yōu)先注入BasicOrderService。

2.4 @value注解進(jìn)行單個屬性的依賴注入 

2.4,1 基本用法:從 application.properties 中讀取值

步驟

  • 在 application.properties 文件中定義鍵值對。
  • 使用 @Value 注解將對應(yīng)的值注入到類的字段中。

示例

application.properties 文件

url=http://example.com
port=8080
enableFeature=true

使用 @Value 注解:

@Service
public class MyService {

    @Value("${url}")
    private String appUrl;

    @Value("${port}")
    private int port;

    @Value("${enableFeature}")
    private boolean enableFeature;
}

2.4.2 默認(rèn)值

在某些情況下,如果配置文件中沒有定義相應(yīng)的屬性值,可以使用 @Value 指定默認(rèn)值,避免出現(xiàn) null 值。

示例

@Service
public class MyService {

    // 如果myapp.url沒有定義,將使用默認(rèn)值 "http://localhost"
    @Value("${myapp.url:http://localhost}")
    private String appUrl;
}

3. 與事務(wù)管理的結(jié)合 

在Spring框架中,@Service注解與事務(wù)管理的結(jié)合是業(yè)務(wù)邏輯層非常重要的功能。事務(wù)管理保證了在處理多步驟的業(yè)務(wù)操作時,數(shù)據(jù)的一致性和完整性。例如,在處理銀行轉(zhuǎn)賬等業(yè)務(wù)時,如果其中的一個步驟失敗,整個事務(wù)應(yīng)該回滾,以保證系統(tǒng)中的數(shù)據(jù)狀態(tài)正確。Spring通過@Transactional注解結(jié)合@Service,為開發(fā)者提供了簡潔而強(qiáng)大的事務(wù)管理能力。 

3.1 @Transactional注解的作用

@Transactional是Spring用于聲明式事務(wù)管理的核心注解。它可以用于類或方法上,指示Spring應(yīng)該為該類或方法的操作啟用事務(wù)。事務(wù)管理保證了業(yè)務(wù)邏輯中的多個操作要么全部成功,要么全部失敗,這樣可以保證數(shù)據(jù)的一致性。

  • 當(dāng)某個方法被標(biāo)記為@Transactional時,Spring會將該方法及其中涉及的數(shù)據(jù)庫操作(例如插入、更新、刪除)放在一個事務(wù)中。
  • 如果方法執(zhí)行過程中出現(xiàn)異常,Spring會自動回滾事務(wù),保證數(shù)據(jù)庫不會被部分更新。
  • 如果方法執(zhí)行成功,事務(wù)將提交,數(shù)據(jù)庫中的更改將永久生效。

3.2 Spring AOP(面向切面編程)與事務(wù)管理

Spring的事務(wù)管理機(jī)制是通過AOP(面向切面編程)來實(shí)現(xiàn)的。以下是事務(wù)管理的基本流程:

  • AOP代理對象:當(dāng)Spring啟動時,它會通過AOP為標(biāo)記了@Transactional的方法或類生成代理對象。這些代理對象會攔截對方法的調(diào)用。
  • 事務(wù)開始:當(dāng)代理對象檢測到對標(biāo)記了@Transactional的方法的調(diào)用時,它會在方法執(zhí)行前開啟一個事務(wù)。
  • 方法執(zhí)行:方法執(zhí)行過程中,Spring會暫時保存所有對數(shù)據(jù)庫的操作,并等待方法的最終結(jié)果來決定是否提交或回滾。
  • 提交或回滾:如果方法執(zhí)行成功(沒有拋出異常),Spring會提交事務(wù);如果方法執(zhí)行過程中拋出異常,則回滾事務(wù)。

3.3 @Transactional的不同應(yīng)用方式

@Transactional可以作用于類級別方法級別,它們的行為略有不同。

3.3.1 類級別的@Transactional

如果在類上應(yīng)用@Transactional,則該類中的所有公共方法都將自動包含事務(wù)管理。每次調(diào)用該類的公共方法時,Spring都會自動開啟一個事務(wù),方法執(zhí)行成功時提交事務(wù),方法執(zhí)行失敗時回滾事務(wù)。

@Service
@Transactional  // 應(yīng)用于整個類
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    public void placeOrder(Order order) {
        // 保存訂單
        orderRepository.save(order);
    }

    public void cancelOrder(Long orderId) {
        // 取消訂單邏輯
        Order order = orderRepository.findById(orderId).orElseThrow();
        order.setStatus("Cancelled");
        orderRepository.save(order);
    }
}

在這個例子中,OrderService類中的所有公共方法都會被事務(wù)管理。當(dāng)方法被調(diào)用時,事務(wù)將自動開啟;如果方法執(zhí)行失敗(拋出異常),事務(wù)將回滾;如果方法執(zhí)行成功,事務(wù)將提交。

3.3.2  方法級別的@Transactional

如果你不想對整個類的所有方法都使用事務(wù)管理,可以將@Transactional僅應(yīng)用于特定方法。在這種情況下,只有被標(biāo)記的方法會執(zhí)行事務(wù)管理。

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional  // 僅應(yīng)用于此方法
    public void placeOrder(Order order) {
        // 保存訂單的業(yè)務(wù)邏輯
        orderRepository.save(order);
    }

    public void cancelOrder(Long orderId) {
        // 不使用事務(wù)的邏輯
        Order order = orderRepository.findById(orderId).orElseThrow();
        order.setStatus("Cancelled");
        orderRepository.save(order);
    }
}

在此例中,placeOrder方法具有事務(wù)管理,而cancelOrder方法則不會啟用事務(wù)管理。

3.4 @Service與@Transactional的結(jié)合

通過將@Transactional@Service結(jié)合使用,Spring能夠自動管理業(yè)務(wù)邏輯中的事務(wù)。常見的場景是,當(dāng)業(yè)務(wù)邏輯涉及多個數(shù)據(jù)庫操作(如插入、更新、刪除)時,如果某個操作失敗,事務(wù)可以回滾,從而保證數(shù)據(jù)一致性。 

示例代碼: 

@Service
public class BankService {

    @Autowired
    private AccountRepository accountRepository;

    @Transactional
    public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
        // 1. 扣除付款方賬戶的金額
        Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
        fromAccount.setBalance(fromAccount.getBalance() - amount);
        accountRepository.save(fromAccount);

        // 2. 增加收款方賬戶的金額
        Account toAccount = accountRepository.findById(toAccountId).orElseThrow();
        toAccount.setBalance(toAccount.getBalance() + amount);
        accountRepository.save(toAccount);
    }
}

代碼詳細(xì)解釋 

@Transactional注解:當(dāng)調(diào)用transferMoney方法時,Spring框架會利用@Transactional注解為該方法開啟一個事務(wù)

  • 事務(wù)的開始:Spring使用AOP(面向切面編程)技術(shù),在transferMoney方法調(diào)用時攔截并啟動事務(wù)。
  • 作用:確保該方法內(nèi)的所有操作作為一個整體,要么全部成功,要么全部失敗。如果方法內(nèi)部任何地方發(fā)生異?;蝈e誤,Spring將回滾事務(wù),撤銷已經(jīng)執(zhí)行的操作。
  • 目標(biāo):保護(hù)數(shù)據(jù)一致性,避免銀行轉(zhuǎn)賬這種操作中一部分成功、一部分失敗的現(xiàn)象

從數(shù)據(jù)庫中獲取付款方賬戶

  • accountRepository.findById(fromAccountId):從數(shù)據(jù)庫中查找ID為fromAccountId的賬戶(付款方)。
  • orElseThrow():如果找不到該賬戶,則拋出異常,事務(wù)會因?yàn)楫惓6貪L。

扣除余額

  • fromAccount.setBalance(fromAccount.getBalance() - amount):從賬戶的余額中扣除amount,模擬銀行轉(zhuǎn)賬中的付款操作。

保存更新后的付款方賬戶

  • accountRepository.save(fromAccount):將修改后的fromAccount保存回?cái)?shù)據(jù)庫,但此時實(shí)際的數(shù)據(jù)庫操作并沒有立即持久化,數(shù)據(jù)仍在事務(wù)中,等待事務(wù)提交。
  • 注意:在事務(wù)提交前,雖然代碼已經(jīng)調(diào)用save方法,但對數(shù)據(jù)庫的實(shí)際寫操作還沒有發(fā)生。

事務(wù)提交

  • 如果transferMoney方法執(zhí)行到最后一步,沒有拋出異常,則Spring會自動提交事務(wù),將所有的數(shù)據(jù)庫操作(付款方賬戶的扣款和收款方賬戶的存款)一并生效。
  • 提交事務(wù)時accountRepository.save(fromAccount)accountRepository.save(toAccount)中的數(shù)據(jù)庫更改才會被實(shí)際寫入到數(shù)據(jù)庫中。

3.5 @Transactional的事務(wù)屬性

@Transactional提供了多個屬性,用來細(xì)化事務(wù)的管理行為。常用屬性包括:

3.5.1 propagation(傳播行為)

示例:

定義當(dāng)前事務(wù)方法是否應(yīng)該運(yùn)行在一個現(xiàn)有的事務(wù)中,或者是否應(yīng)該創(chuàng)建一個新的事務(wù)。

常見的傳播屬性:

  • REQUIRED:默認(rèn)值。如果當(dāng)前有事務(wù),使用當(dāng)前事務(wù);如果沒有,創(chuàng)建一個新事務(wù)。
  • REQUIRES_NEW:每次都會創(chuàng)建一個新事務(wù),并暫停當(dāng)前事務(wù)。
  • SUPPORTS:如果當(dāng)前有事務(wù),則支持當(dāng)前事務(wù);如果沒有事務(wù),也可以不使用事務(wù)。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAccount(Account account) {
    accountRepository.save(account);
}

3.5.2 isolation(隔離級別)

定義數(shù)據(jù)庫操作之間的隔離程度,防止臟讀、不可重復(fù)讀和幻讀等問題。

常見的隔離級別:

  • READ_UNCOMMITTED:最低的隔離級別,可能會出現(xiàn)臟讀。
  • READ_COMMITTED:保證讀取的數(shù)據(jù)是已提交的,防止臟讀。
  • REPEATABLE_READ:同一個事務(wù)中多次讀取相同的數(shù)據(jù)結(jié)果相同,防止不可重復(fù)讀。
  • SERIALIZABLE:最高的隔離級別,防止幻讀。

示例:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateAccount(Account account) {
    accountRepository.save(account);
}

3.5.3 timeout(超時)

定義事務(wù)的超時時間,如果事務(wù)在指定的時間內(nèi)沒有完成,將會回滾。

示例:

@Transactional(timeout = 30)  // 超時時間為30秒
public void performLongRunningTask() {
    // 執(zhí)行耗時任務(wù)
}

3.5.4 readOnly(只讀事務(wù))

如果事務(wù)只進(jìn)行查詢操作而不進(jìn)行更新,readOnly = true可以優(yōu)化性能。

示例:

@Transactional(readOnly = true)
public Account getAccount(Long accountId) {
    return accountRepository.findById(accountId).orElseThrow();
}

 3.5.5 rollbackFor 和 noRollbackFor

默認(rèn)情況下,Spring的事務(wù)管理機(jī)制只會在運(yùn)行時異常(RuntimeException)或Error發(fā)生時回滾事務(wù)。而對于受檢異常(Checked Exception),Spring不會自動回滾,除非你顯式地配置rollbackFor屬性來指定。

  • rollbackFor:指定哪些異常會導(dǎo)致事務(wù)回滾,默認(rèn)情況下,只有未捕獲的運(yùn)行時異常會導(dǎo)致回滾。
  • noRollbackFor:指定哪些異常不會導(dǎo)致事務(wù)回滾。

示例:

@Transactional(rollbackFor = {Exception.class})
public void riskyOperation() throws Exception {
    // 執(zhí)行可能拋出受檢異常的操作
}

這個寫法表示只要拋出了Exception類型或它的子類異常,Spring就會回滾事務(wù)。 

這里的Exception只是代指異常的類型,實(shí)際使用的時候要加入實(shí)際的異常類型,比如:SQLException(受檢異常)或DataAccessException(運(yùn)行時異常) 。

  • Exception.class 表示 Exception 這個類的類對象。在Java中,每個類都有一個對應(yīng)的類對象Class對象),它可以通過.class語法獲得。Exception.class 表示 Java 的 Exception 類本身,而不是 Exception 的一個實(shí)例。

  • {Exception.class} 是一個包含 Exception.class 的數(shù)組,這是 Java 數(shù)組的簡寫形式。你可以在數(shù)組中放入多個類類型,像這樣:{Exception.class, IOException.class},表示這是一個由多個類對象組成的數(shù)組。

3.6 @Service與@Transactional的常見使用場景

  • 銀行交易:轉(zhuǎn)賬、提款等涉及多個數(shù)據(jù)庫操作的業(yè)務(wù),如果其中一步失敗,整個交易應(yīng)該回滾。
  • 訂單處理:在電商系統(tǒng)中,訂單的創(chuàng)建、庫存的減少、支付的處理等步驟應(yīng)該作為一個整體事務(wù)來執(zhí)行。
  • 批量更新:批量插入、更新、刪除數(shù)據(jù)的操作需要在一個事務(wù)中執(zhí)行,以保證操作的原子性。

4. 作用域與生命周期

4.1作用域(Scope)

作用域決定了Spring容器如何管理Bean的實(shí)例。Spring默認(rèn)會為每個@Service Bean分配一個單例作用域(singleton),即整個應(yīng)用程序中只有一個實(shí)例。但如果需要,Spring允許我們?yōu)?code>@Service Bean設(shè)置其他作用域。

常見的作用域: 

4.1.1 singleton(單例,默認(rèn)作用域)

  • 默認(rèn)作用域:當(dāng)你使用@Service時,如果不指定作用域,Spring會默認(rèn)使用singleton作用域。
  • 單例模式:表示Spring容器中每個Bean在整個應(yīng)用中只有一個實(shí)例。無論你在應(yīng)用的哪個部分引用這個Bean,Spring都會返回同一個實(shí)例。
  • 優(yōu)點(diǎn):單例模式節(jié)省內(nèi)存和提高性能,因?yàn)樵谡麄€應(yīng)用生命周期中只有一個實(shí)例。 

示例:

@Service
public class UserService {
    // 默認(rèn)是 singleton
}

singleton作用域下,Spring在啟動時創(chuàng)建UserService實(shí)例,并在整個應(yīng)用程序中共享同一個實(shí)例。 

4.1.2 prototype(原型作用域)

  • 多實(shí)例模式:每次需要這個Bean時,Spring都會創(chuàng)建一個新的實(shí)例。
  • 使用場景:當(dāng)你希望每次訪問時都能獲得一個新的@Service對象實(shí)例,而不是共享一個單例。
  • 注意:Spring僅負(fù)責(zé)創(chuàng)建新實(shí)例,不管理Bean的生命周期(如銷毀)。因此,使用prototype時,Bean的銷毀需要手動處理。

示例:

@Service
@Scope("prototype")
public class ReportService {
    // 每次注入時都會創(chuàng)建一個新實(shí)例
}

prototype作用域下,每次請求ReportService時,Spring都會創(chuàng)建一個新的實(shí)例。

4.1.3 request(僅適用于Web應(yīng)用)

  • 請求作用域:表示每次HTTP請求都會創(chuàng)建一個新的Bean實(shí)例。這個作用域僅在Web應(yīng)用中使用,常用于處理與單個HTTP請求相關(guān)的業(yè)務(wù)邏輯。
  • 使用場景:當(dāng)你希望每個HTTP請求都有一個獨(dú)立的@Service實(shí)例時使用。

因?yàn)橹挥?strong>Web應(yīng)用(Web應(yīng)用通常是基于HTTP協(xié)議運(yùn)行的應(yīng)用,比如使用Spring MVC的Web應(yīng)用或Spring Boot中的Web應(yīng)用。)才能處理HTTP請求,并與請求和響應(yīng)交互。

所以,這種針對HTTP請求的作用域才僅適用于Web應(yīng)用。

示例:

@Service
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class RequestScopedService {
    // 每次HTTP請求都會創(chuàng)建一個新的實(shí)例
}

value屬性指定了Bean的具體作用域類型。

WebApplicationContext.SCOPE_REQUEST:這是一個Spring提供的常量,用來表示request作用域。

 你也可以直接寫成字符串"request",效果是一樣的:@Scope("request");

4.1.4 session(僅適用于Web應(yīng)用):

  • 會話作用域:在一個HTTP會話(HttpSession)內(nèi)共享同一個Bean實(shí)例。當(dāng)會話結(jié)束時,Bean也會銷毀。
  • 使用場景:當(dāng)你希望一個用戶的會話中始終共享同一個@Service實(shí)例時使用。

示例:

@Service
@Scope(value = WebApplicationContext.SCOPE_SESSION)
public class SessionScopedService {
    // 在一個會話中,實(shí)例是共享的
}

4.1.5 總結(jié) 

作用域類型適用范圍實(shí)例化頻率生命周期適用場景
singleton所有應(yīng)用容器啟動時創(chuàng)建一個實(shí)例全局共享默認(rèn)作用域,適用于大部分情況
prototype所有應(yīng)用每次請求時創(chuàng)建新實(shí)例由容器外管理適合需要每次調(diào)用時生成新對象的場景
requestWeb應(yīng)用每次HTTP請求時創(chuàng)建新實(shí)例HTTP請求范圍內(nèi)適用于每個HTTP請求需要獨(dú)立狀態(tài)的場景
sessionWeb應(yīng)用每個HTTP會話創(chuàng)建新實(shí)例HTTP會話范圍內(nèi)適用于用戶登錄會話、購物車等需要在會話內(nèi)共享的場景
  • singleton:當(dāng)你需要全局共享的Bean,或者Bean是無狀態(tài)的,適合使用singleton,這是Spring的默認(rèn)作用域。
  • prototype:當(dāng)你希望每次請求時都創(chuàng)建新的Bean實(shí)例,且這個Bean的狀態(tài)每次都不同,適合使用prototype,如處理大量獨(dú)立任務(wù)時。
  • request:在Web應(yīng)用中,適合處理與每個HTTP請求相關(guān)的Bean,比如每個請求都有獨(dú)立的表單驗(yàn)證或請求參數(shù)處理。
  • session:在Web應(yīng)用中,適合管理與用戶會話相關(guān)的Bean,比如購物車、用戶登錄狀態(tài)。

4.2 生命周期(Lifecycle)

@Service Bean的生命周期受Spring容器管理,它的生命周期包括創(chuàng)建、初始化、使用和銷毀幾個階段。具體的生命周期步驟如下:

  • 創(chuàng)建(Bean的實(shí)例化)

    • 當(dāng)Spring容器啟動時,它會掃描所有帶有@Service注解的類,并為每個類創(chuàng)建一個實(shí)例(singleton作用域下)。這個過程稱為實(shí)例化,即Spring在內(nèi)存中為這個類分配空間并創(chuàng)建它的對象。
  • 初始化(Bean的初始化)

    • 當(dāng)Bean被實(shí)例化后,Spring會調(diào)用它的初始化方法(如果有),如@PostConstruct。初始化方法用于配置Bean的初始狀態(tài)。

    示例:

    @Service
    public class UserService {
    
        @PostConstruct
        public void init() {
            // 初始化邏輯
            System.out.println("UserService初始化");
        }
    }
    

    在這個例子中,當(dāng)UserService類被實(shí)例化后,init()方法會被調(diào)用來完成初始化操作。

  • 使用(Bean的使用)

    • 初始化完成后,Bean會被注入到需要使用它的類中。例如,@Autowired注解會在依賴注入時將Bean注入到控制器或其他服務(wù)中,供業(yè)務(wù)邏輯使用。
    • 在使用過程中,Bean的實(shí)例會參與業(yè)務(wù)邏輯的處理,并與其他組件協(xié)作。
  • 銷毀(Bean的銷毀)

    • 當(dāng)Spring容器關(guān)閉時,Spring會調(diào)用Bean的銷毀方法(如果有),如@PreDestroy。
    • 這種銷毀通常只適用于singleton作用域的Bean,因?yàn)?code>prototype作用域的Bean銷毀需要手動處理。

    示例:

    @Service
    public class UserService {
    
        @PreDestroy
        public void destroy() {
            // 銷毀邏輯
            System.out.println("UserService銷毀");
        }
    }
    

    在這個例子中,當(dāng)Spring容器關(guān)閉時,destroy()方法會被調(diào)用,執(zhí)行資源釋放或清理操作。

4.3 Bean生命周期的詳細(xì)流程

@Service為例,它的完整生命周期流程如下:

  • Spring掃描并發(fā)現(xiàn)@Service Bean。
  • 實(shí)例化Bean:Spring使用默認(rèn)構(gòu)造函數(shù)(或其他指定構(gòu)造函數(shù))創(chuàng)建該類的實(shí)例。
  • 依賴注入:通過@Autowired將該類的依賴注入(如Repository或其他@Service類)。
  • 初始化Bean:Spring調(diào)用Bean的初始化方法(如@PostConstruct)。
  • Bean的使用:Bean被注入到控制器或其他類中,并執(zhí)行相應(yīng)的業(yè)務(wù)邏輯。
  • 銷毀Bean:當(dāng)Spring容器關(guān)閉時,Spring會調(diào)用Bean的銷毀方法(如@PreDestroy),完成清理工作。

4.4 設(shè)置自定義的作用域和生命周期管理

通過結(jié)合@Scope和生命周期回調(diào)(如@PostConstruct、@PreDestroy),你可以對@Service Bean的作用域和生命周期進(jìn)行細(xì)粒度控制。

@PostConstruct 和 @PreDestroy 注解

  • @PostConstruct:這是一個JDK提供的注解,定義在javax.annotation包中。它用來標(biāo)記在Bean被創(chuàng)建并且依賴注入完成后要執(zhí)行的初始化方法。Spring會在Bean實(shí)例化之后自動調(diào)用這個方法。

  • @PreDestroy:這是與@PostConstruct類似的注解,也來自javax.annotation包,用來標(biāo)記在Bean銷毀之前需要執(zhí)行的清理方法。Spring會在容器關(guān)閉時自動調(diào)用這個方法,用于釋放資源或執(zhí)行一些關(guān)閉操作。

@Service
@Scope("prototype")
public class PrototypeService {

    @PostConstruct
    public void init() {
        // 初始化邏輯
        System.out.println("PrototypeService 初始化");
    }

    @PreDestroy
    public void cleanup() {
        // 清理邏輯
        System.out.println("PrototypeService 銷毀");
    }
}

在這個例子中,PrototypeService的作用域是prototype,因此每次注入都會創(chuàng)建一個新實(shí)例。init()方法在實(shí)例創(chuàng)建時被調(diào)用,而cleanup()方法不會被自動調(diào)用,因?yàn)?code>prototype作用域的Bean需要手動管理其銷毀。

  • @Service:標(biāo)識服務(wù)層組件,由Spring管理其生命周期。
  • 作用域:控制Spring如何管理Bean的實(shí)例,默認(rèn)是singleton,還可以選擇prototype、requestsession等作用域。
  • 生命周期:Spring負(fù)責(zé)Bean的創(chuàng)建、初始化、使用和銷毀,開發(fā)者可以通過回調(diào)方法(如@PostConstruct@PreDestroy)在生命周期的關(guān)鍵時刻執(zhí)行自定義邏輯。

5. 自定義服務(wù)名稱

雖然默認(rèn)情況下,@Service會以類名的小寫形式將類注冊為Spring容器中的Bean,但可以通過顯式指定Bean的名稱。

@Service("customUserService")
public class UserService {
    // 業(yè)務(wù)邏輯
}
  • 現(xiàn)在這個Service類在Spring容器中的Bean名稱為customUserService,可以通過這個名稱來進(jìn)行注入。

6. 與AOP(面向切面編程)的結(jié)合

此處只講解了@Service注解與AOP結(jié)合使用時的具體流程,關(guān)于AOP的詳細(xì)內(nèi)容請查看AOP(面向切面編程) 

6.1 AOP 與 @Service 結(jié)合:生成代理對象

AOP 的核心在于為某些特定的類或方法(例如帶有日志、事務(wù)等橫切關(guān)注點(diǎn)的類或方法)創(chuàng)建代理對象。代理對象是指 Spring 在運(yùn)行時為目標(biāo)對象(例如 UserService)生成的一個增強(qiáng)版本,這個版本可以在方法執(zhí)行的前后或異常時插入自定義的邏輯(切面)。

當(dāng) Spring 容器掃描到 @Service 注解的類時,會根據(jù)是否配置了 AOP 相關(guān)的切面邏輯,為這個類生成代理對象。

步驟

  • Spring 檢查是否有任何切面(Aspect)應(yīng)用于 UserService 這樣的 @Service 類。切面通常通過 @Aspect 注解定義,描述在哪些方法或類上插入橫切邏輯。
  • 如果有匹配的切面,Spring 使用動態(tài)代理機(jī)制為 UserService 生成代理對象。

生成的代理對象代替了原來的 UserService Bean,Spring 容器中保存的實(shí)際上是這個代理對象,而不是直接的 UserService 實(shí)例。

6.2 方法調(diào)用時的代理行為

當(dāng)使用者調(diào)用 UserService 中的方法時,實(shí)際上是通過代理對象進(jìn)行調(diào)用,而不是直接調(diào)用 UserService 實(shí)例。代理對象會攔截這個方法調(diào)用,并根據(jù) AOP 的切面邏輯決定是否執(zhí)行切面的增強(qiáng)邏輯。

步驟

  • 使用者調(diào)用 UserService中的方法時,代理對象會攔截這個方法調(diào)用。
  • 代理對象檢查方法是否匹配切入點(diǎn)(Pointcut)。如果該方法匹配切入點(diǎn)條件(例如 execution(* com.example.service.UserService.*(..))),則會執(zhí)行對應(yīng)的通知邏輯(Advice)。
  • 根據(jù) AOP 的配置,代理對象會在方法執(zhí)行的不同階段插入增強(qiáng)邏輯,例如:
    • 在方法執(zhí)行之前插入前置通知(@Before)。
    • 在方法執(zhí)行之后插入后置通知(@After)。
    • 如果方法拋出異常,執(zhí)行異常通知(@AfterThrowing)。

6.3 織入切面邏輯

代理對象在方法調(diào)用前后,會根據(jù)切入點(diǎn)匹配情況自動織入相應(yīng)的切面邏輯。

示例切面

@Aspect
@Component
public class LoggingAspect {

    // 定義一個切入點(diǎn),匹配 UserService 中的所有方法
    @Pointcut("execution(* com.example.service.UserService.*(..))")
    public void userServiceMethods() {}

    // 前置通知:在方法執(zhí)行之前執(zhí)行日志記錄
    @Before("userServiceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("開始執(zhí)行方法: " + joinPoint.getSignature().getName());
    }

    // 后置通知:在方法執(zhí)行之后執(zhí)行日志記錄
    @After("userServiceMethods()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("方法執(zhí)行結(jié)束: " + joinPoint.getSignature().getName());
    }
}

步驟

  • 在代理對象攔截 UserService.registerUser() 方法調(diào)用時,它首先會執(zhí)行 LoggingAspect 中定義的前置通知(@Before)。這里會記錄日志,表示方法的開始。
  • 代理對象接著調(diào)用 UserService 的實(shí)際 registerUser() 方法,執(zhí)行核心業(yè)務(wù)邏輯。
  • 方法執(zhí)行完畢后,代理對象執(zhí)行后置通知(@After),記錄日志,表示方法結(jié)束。
  • 如果方法在執(zhí)行過程中拋出異常,代理對象還會執(zhí)行異常通知(如果有定義)。

6.4 業(yè)務(wù)邏輯方法執(zhí)行

在切面(如日志、事務(wù)等)邏輯執(zhí)行完畢后,代理對象會繼續(xù)執(zhí)行實(shí)際的業(yè)務(wù)方法。這時代理對象的行為和直接調(diào)用 UserService 沒有區(qū)別,只不過在此之前或之后已經(jīng)插入了額外的橫切關(guān)注點(diǎn)邏輯。

6.5 方法結(jié)束后的后置邏輯

業(yè)務(wù)邏輯執(zhí)行完畢后,代理對象還會檢查是否有后續(xù)的切面邏輯要執(zhí)行。如果有定義 @After 或 @AfterReturning,它會執(zhí)行這些后置通知。

后置通知的觸發(fā)

  • 方法執(zhí)行結(jié)束后,代理對象執(zhí)行后置通知(如 @After),記錄方法結(jié)束的日志或執(zhí)行其他橫切關(guān)注點(diǎn)邏輯。
  • 如果方法拋出異常,代理對象會執(zhí)行 @AfterThrowing 通知,進(jìn)行異常處理或日志記錄。

6.6 @Service 與 AOP 的結(jié)合流程

  • Spring 容器掃描:Spring 容器掃描 @Service 注解的類,并將其注冊為 Bean。
  • 代理對象生成:如果有匹配的 AOP 切面,Spring 會為 UserService 生成代理對象。
  • 方法調(diào)用攔截:當(dāng)使用者調(diào)用 UserService 的方法時,代理對象攔截方法調(diào)用。
  • 織入切面邏輯:代理對象根據(jù)切入點(diǎn)匹配情況,在方法執(zhí)行前后或拋出異常時,織入切面邏輯(如日志、事務(wù)、權(quán)限校驗(yàn)等)。
  • 業(yè)務(wù)邏輯執(zhí)行:代理對象執(zhí)行 UserService 的實(shí)際業(yè)務(wù)邏輯。
  • 后置邏輯執(zhí)行:方法執(zhí)行完畢后,代理對象繼續(xù)執(zhí)行后置通知或異常處理邏輯。

 到此這篇關(guān)于Spring Service中的@Service注解的使用小結(jié)的文章就介紹到這了,更多相關(guān)Spring @Service注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC中解決@ResponseBody注解返回中文亂碼問題

    SpringMVC中解決@ResponseBody注解返回中文亂碼問題

    這篇文章主要介紹了SpringMVC中解決@ResponseBody注解返回中文亂碼問題, 小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • 詳解MybatisPlus集成nacos導(dǎo)致druid連接不上數(shù)據(jù)庫

    詳解MybatisPlus集成nacos導(dǎo)致druid連接不上數(shù)據(jù)庫

    這篇文章主要介紹了詳解MybatisPlus集成nacos導(dǎo)致druid連接不上數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 使用spring boot通過自定義注解打印所需日志

    使用spring boot通過自定義注解打印所需日志

    這篇文章主要介紹了使用spring boot通過自定義注解打印所需日志的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 淺談Java中SimpleDateFormat 多線程不安全原因

    淺談Java中SimpleDateFormat 多線程不安全原因

    SimpleDateFormat是Java中用于日期時間格式化的一個類,本文主要介紹了淺談Java中SimpleDateFormat 多線程不安全原因,感興趣的可以了解一下
    2024-01-01
  • Java中創(chuàng)建對象的6種方式

    Java中創(chuàng)建對象的6種方式

    大家好,本篇文章主要講的是Java中創(chuàng)建對象的6種方式,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下的相關(guān)資料
    2022-02-02
  • SpringBoot的API文檔生成工具SpringDoc使用詳解

    SpringBoot的API文檔生成工具SpringDoc使用詳解

    這篇文章主要為大家介紹了SpringBoot的API文檔生成工具SpringDoc使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 基于SpringBoot+Redis實(shí)現(xiàn)分布式鎖

    基于SpringBoot+Redis實(shí)現(xiàn)分布式鎖

    本文主要介紹了基于SpringBoot+Redis實(shí)現(xiàn)分布式鎖,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 解決異常:Invalid?keystore?format,springboot配置ssl證書格式不合法問題

    解決異常:Invalid?keystore?format,springboot配置ssl證書格式不合法問題

    這篇文章主要介紹了解決異常:Invalid?keystore?format,springboot配置ssl證書格式不合法問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Java使用BigDecimal解決小數(shù)計(jì)算問題

    Java使用BigDecimal解決小數(shù)計(jì)算問題

    Java中的BigDecimal是一個內(nèi)置類,用于精確表示任意大小的十進(jìn)制數(shù),它提供了一種處理浮點(diǎn)運(yùn)算精度問題的方法,特別適合金融、貨幣交易等需要高精度計(jì)算的場景,本文給大家介紹了java中如何使用BigDecimal解決小數(shù)計(jì)算問題,需要的朋友可以參考下
    2024-08-08
  • 通過實(shí)例解析Java class文件編譯加載過程

    通過實(shí)例解析Java class文件編譯加載過程

    這篇文章主要介紹了通過實(shí)例解析Java class文件編譯加載過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07

最新評論