Spring中的增強

来源:http://www.cnblogs.com/zhangzongle/archive/2016/10/10/5944906.html
-Advertisement-
Play Games

在Spring中,目前我學習了幾種增強的方式,和大家分享一下 一:前置增強和後置增強 源碼介紹: 1.User.java package cn.zhang.entity; public class User { private Integer id; // 用戶ID private String u ...


在Spring中,目前我學習了幾種增強的方式,和大家分享一下

之前的話:

1.AOP  (Aspect  Oriented Programming  面向切麵編程)

   在軟體業,AOP為Aspect Oriented Programming的縮寫,意為:面向切麵編程,通過預編譯方式和運行期動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

面向對象編程是從【靜態角度】考慮程式的結構,而面向切麵編程是從【動態角度】考慮程式運行過程。
AOP底層,就是採用【動態代理】模式實現的。採用了兩種代理:JDK動態代理和CGLIB動態代理。

基本術語(一些名詞):
(1)切麵(Aspect)
切麵泛指[*交叉業務邏輯*]。事務處理和日誌處理可以理解為切麵。常用的切麵有通知(Advice)與顧問(Advisor)。實際就是對主業務邏輯的一種增強。

(2)織入(Weaving)
織入是指將切麵代碼插入到目標對象的過程。代理的invoke方法完成的工作,可以稱為織入。

(3) 連接點(JoinPoint)
連接點是指可以被切麵織入的方法。通常業務介面的方法均為連接點

(4)切入點(PointCut)
切入點指切麵具體織入的方法
註意:被標記為final的方法是不能作為連接點與切入點的。因為最終的是不能被修改的,不能被增強的。

(5)目標對象(Target)
目標對象指將要被增強的對象。即包含主業務邏輯的類的對象。

(6)通知(Advice)
通知是切麵的一種實現,可以完成簡單的織入功能。通知定義了增強代碼切入到目標代碼的時間點,是目標方法執行之前執行,還是執行之後執行等。切入點定義切入的位置,通知定義切入的時間。

(7)顧問(Advisor)
顧問是切麵的另一種實現,能夠將通知以更為複雜的方式織入到目標對象中,是將通知包裝為更複雜切麵的裝配器。

AOP是一種思想,而非實現
AOP是基於OOP,而又遠遠高於OOP,主要是將主要核心業務和交叉業務分離,交叉業務就是切麵。例如,記錄日誌和開啟事務。

一:前置增強和後置增強

源碼介紹:

1.User.java

package cn.zhang.entity;

public class User {
    private Integer id; // 用戶ID
    private String username; // 用戶名
    private String password; // 密碼
    private String email; // 電子郵件
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    
}
View Code

2.IDao.java

package cn.zhang.dao;
//定義介面
import cn.zhang.entity.User;

public interface IDao {
    //定義方法
    public void save(User user);
}
View Code

3.UserDao.java

package cn.zhang.dao.impl;
//實現介面
import cn.zhang.dao.IDao;
import cn.zhang.entity.User;

public class UserDao implements IDao  {

    @Override
    //實現方法
    public void save(User user) {
        System.out.println("save success!");        
    }

}
View Code

4.IUserBiz.java

package cn.zhang.biz;
//業務介面
import cn.zhang.entity.User;

public interface IUserBiz {
    //待處理的方法
     public void save(User user);
}
View Code

5.UserBiz.java

package cn.zhang.biz.impl;
//業務介面的實現類
import cn.zhang.biz.IUserBiz;
import cn.zhang.dao.IDao;
import cn.zhang.entity.User;

public class UserBiz implements IUserBiz {
    //引入IDao介面
    private IDao dao;
    @Override
    //實現方法
    public void save(User user) {
        dao.save(user);
    }
    //dao 屬性的setter訪問器,會被Spring調用,實現設值註入
    public IDao getDao() {
        return dao;
    }
    public void setDao(IDao dao) {
        this.dao = dao;
    }

}
View Code

6.LoggerAfter.java(後置增強)

package cn.zhang.aop;
//後置增強
import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class LoggerAfter implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object arg0, Method arg1, Object[] arg2,
            Object arg3) throws Throwable {
            System.out.println("後置增強代碼");        
    }

}
View Code

7.LoggerBefore.java(前置增強)

package cn.zhang.aop;
//前置增強
import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice;

public class LoggerBefore implements MethodBeforeAdvice {
    private static final Logger log = Logger.getLogger(LoggerBefore.class);
    @Override
    public void before(Method arg0, Object[] arg1, Object arg2)
            throws Throwable {
        log.info("前置內容AAA");    
        System.out.println("前置增強代碼");
    }

}
View Code

8.applicationContext.xml(Spring配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        ">
    <bean id="dao" class="cn.zhang.dao.impl.UserDao" />
    <bean id="biz" class="cn.zhang.biz.impl.UserBiz">
        <property name="dao" ref="dao"></property>
    </bean>
    <!-- 定義前置增強組件 -->
    <bean id="loggerBefore" class="cn.zhang.aop.LoggerBefore" />
    <!-- 定義後置增強組件 -->
    <bean id="loggerAfter" class="cn.zhang.aop.LoggerAfter" />
    <!-- 針對AOP的配置 -->
    <aop:config>
        <aop:pointcut id="pointcut"
            expression="execution(public void save(cn.zhang.entity.User))" />
        <!-- 將增強處理和切入點結合在一起,在切入點處插入增強處理,完成"織入" -->
        <aop:advisor pointcut-ref="pointcut" advice-ref="loggerBefore" />
        <aop:advisor pointcut-ref="pointcut" advice-ref="loggerAfter" />
    </aop:config>

</beans>  
View Code

當然,針對AOP的配置也可以使用代理對象 ProxyFactoryBean 代理工廠bean來實現,在測試類中:IUserBiz biz=(IUserBiz)ctx.getBean("serviceProxy");

<!-- 代理對象 ProxyFactoryBean 代理工廠bean -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="targetName" value="biz"></property>
        <property name="interceptorNames" value="loggerBefore,loggerAfter"></property>
    </bean>
View Code

9.MyTest.java

package cn.zhang.test;
//測試類
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.zhang.biz.IUserBiz;
import cn.zhang.entity.User;

public class MyTest {
public static void main(String[] args) {
    
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    IUserBiz biz=(IUserBiz)ctx.getBean("biz");
    User user=new User();
    biz.save(user);
    System.out.println("success!");
}
}
View Code

10.log4j.properties(日誌的配置文件)

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=info, stdout
View Code

當然,別忘了引入我們需要的jar包啊!

常用的jar:

 

二:異常拋出增強和環繞增強

源碼介紹:

1.User.java

package cn.zhang.entity;

public class User {
    private Integer id; // 用戶ID
    private String username; // 用戶名
    private String password; // 密碼
    private String email; // 電子郵件    
    
    public User() {
        super();
        // TODO Auto-generated constructor stub
    }
    
    public User(Integer id, String username, String password, String email) {
        super();
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    
}
View Code

2.UserService.java

package cn.zhang.service;

public class UserService {

    public void delete() {
        //int i = 5 / 0;//製造一個錯誤,用於測試異常拋出增強
        System.out.println("delete success!");
    }
}
View Code

3.ErrorLog.java(異常拋出增強)

package cn.zhang.aop;
//異常拋出增強
import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.springframework.aop.ThrowsAdvice;

public class ErrorLog implements ThrowsAdvice {
    private static final Logger log = Logger.getLogger(ErrorLog.class);
    public void afterThrowing(Method method, Object[] args, Object target,
            RuntimeException e){
        log.error(method.getName() + " 方法發生異常:" + e);
    }
}
View Code

4.AroundLog(環繞增強)

package cn.zhang.aop;
//環繞增強
import java.lang.reflect.Method;
import java.util.Arrays;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;

public class AroundLog implements MethodInterceptor {
    
    private static final Logger log = Logger.getLogger(AroundLog.class);
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
    
        Object target=invocation.getThis();//獲取被代理對象
        Method method = invocation.getMethod();//獲得被代理方法
        Object[] args = invocation.getArguments();//獲得方法參數
        
        System.out.println("調用"+target+"的"+method.getName()+"方法。方法參數:"+Arrays.toString(args));
        Object result;//調用目標方法,獲取目標方法返回值
        try {
            result = invocation.proceed();
            System.out.println("調用" + target + "的" + method.getName()
                    + "方法。方法返回值:" + result);
            return result;
        } catch (Exception e) {
            log.error(method.getName()+"方法發生異常:"+e);
            throw e;
        }
        
    }

}
View Code

5.applicationContext.xml(Spring配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        ">
    <bean id="service" class="cn.zhang.service.UserService" />
    <!-- 異常拋出增強 -->
    <!-- <bean id="error" class="cn.zhang.aop.ErrorLog"/> -->
    <!-- 環繞增強 -->
    <bean id="error" class="cn.zhang.aop.AroundLog"/>
    
    <aop:config>
        <aop:pointcut expression="execution(public void delete())"
            id="pointcut" />
        <aop:advisor advice-ref="error" pointcut-ref="pointcut" />
    </aop:config>
</beans> 
View Code

6.MyTest.java

package cn.zhang.test;
//測試類
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.zhang.service.UserService;

public class MyTest {
    public static void main(String[] args) {
        
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        UserService service = (UserService) ctx.getBean("service");
        try {
            service.delete();
        } catch (Exception e) {
            System.out.println("錯誤了");
        }
        System.out.println("success!");
    }
}
View Code

三:註解增強方式實現前置增強和後置增強

源碼介紹:

1.UserService.java

package cn.service;
//業務處理類
public class UserService {
    //方法
    public void delete() {
        System.out.println("delete success!");
    }
}
View Code

2.AnnotationAdvice.java(註解增強)

package cn.aop;
//註解增強
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationAdvice {
    
    // 定義前置增強
    @Before("execution(* cn.service.UserService.*(..))")
    public void before(JoinPoint jp) {
        System.out.println("調用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法,參數:"+jp.getArgs()+",參數個數:"+jp.getArgs().length);
        System.out.println("before");
    }
    // 定義後置增強
    @AfterReturning(pointcut="execution(* cn.service.UserService.*(..))",returning="returnValue")
    public void afterReturning(JoinPoint jp,Object returnValue) { 
        System.out.println("調用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法,參數:"+jp.getArgs()+",返回值為:"+returnValue);
        System.out.println("after");
    }
}
View Code

註:

java.lang.Object[] getArgs():獲取連接點方法運行時的入參列表
Signature getSignature() :獲取連接點的方法簽名對象
java.lang.Object getTarget() :獲取連接點所在的目標對象
java.lang.Object getThis() :獲取代理對象本身

3.applicationContext.xml(Spring配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        ">
        
    <bean id="service" class="cn.service.UserService" />

    <bean id="error" class="cn.aop.AnnotationAdvice" />

    <!-- 針對AOP的配置 -->
    <aop:aspectj-autoproxy />
</beans> 
View Code

4.MyTest.java

package cn.test;
//註解增強測試
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.service.UserService;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService biz=(UserService)ctx.getBean("service");
        biz.delete();
        System.out.println("success!");
    }

}
View Code

四: 

通知Advice是Spring提供的一種切麵(Aspect)。但其功能過於簡單,只能
將切麵織入到目標類的所有目標方法中,無法完成將切麵織入到指定目標方法中。

顧問Advisor是Spring提供的另一種切麵。其可以完成更為複雜的切麵織入功能。PointcutAdvisor是顧問的一種,可以指定具體
的切入點。顧問將通知進行了包裝,會根據不同的通知類型,在不同的時間點,將切麵織入到不同的切入點。
PointcutAdvisor介面有兩個較為常用的實現類:
*:NameMatchMethodPointcutAdvisor 名稱匹配方法切入點顧問
*:RegexpMethodPointcutAdvisor 正則表達式匹配方法切入點顧問
<property name="pattern" value=".*do.*"></property> 表示方法全名(包名,介面名,方法名)
運算符 名稱 意義
. 點號 表示任意單個字元
+ 加號 表示前一個字元出現一次或者多次
* 星號 表示前一個字元出現0次或者多次
=====預設Advisor自動代理生成器
DefaultAdvisorAutoProxyCreator
=====BeanName自動代理生成器
BeanNameAutoProxyCreator

實例:

 

源碼介紹:

1.ISomeService.java

package service;
//介面
public interface ISomeService {
    //待實現的方法
   public void doFirst();
   public void doSecond();
}
View Code

2.SomeServiceImpl.java

package service;
//介面實現類
public class SomeServiceImpl implements ISomeService {
    //實現介面定義的方法
    @Override
    public void doFirst() {
        System.out.println("方法A");
    }

    @Override
    public void doSecond() {
        System.out.println("方法B");    
    }

}
View Code

3.MyMethodBeforeAdvice.java

package aop;
//前置增強
import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
    
    @Override
    public void before(Method method, Object[] args, Object target)
            throws Throwable {
            System.out.println("目標方法執行之前執行");
    }

}
View Code

4.applicationContext.xml(Spring配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 目標對象 -->
    <bean id="someService" class="service.SomeServiceImpl"></bean>

    <!-- 切麵:通知 -->
    <bean id<

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 模型 1. 獲取key ftok() 2. 創建/獲取信號量集 semget() 3. 初始化信號量集 semctl() 4. 操作信號量集 semop() 3. 刪除信號量集 semctl() 使用的頭文件: ftok() pathname :文件名 proj_id : 1~255的一個數,表示p ...
  • 剛開始學習python,首先要瞭解一下python解釋器。 什麼是python解釋器? 編寫python代碼保存後,我們會得到一個以.py為擴展名的文本文件。要運行此文件,就需要python解釋器去執行.py文件。這裡,我們介紹3種解釋器。 1、CPython 當我們從Python官方網站下載並安裝 ...
  • 本文章向碼農們介紹 php 給圖片加水印的兩種方法,感興趣的碼農可以參考一下本文章的源代碼。 方法一:PHP最簡單的加水印方法 方法二:php給圖片加文字水印 原文地址:http://www.manongjc.com/article/593.html ...
  • 建議106:動態代理可以使代理模式更加靈活 Java的反射框架提供了動態代理(Dynamic Proxy)機制,允許在運行期對目標類生成代理,避免重覆開發。我們知道一個靜態代理是通過主題角色(Proxy)和具體主題角色(Real Subject)共同實現主題角色(Subject)的邏輯的,只是代理角 ...
  • python的字典是一個非常方便的數據結構,使用它我們可以輕易的根據姓名(鍵)來找到他的成績,排名等(值),而不用去遍歷整個數據集。 例如:{'Lee': [1, 100], 'Jane': [2, 98]...} 但是在使用字典的過程中產生了一些問題,那就是,字典本身是不管你錄入的順序的 當有這種 ...
  • xml已經被json逐漸替代,現在用的api都是用貌似用的json,但是有些老的網站還是在用xml。 這裡預設xml文件為:address.xml,存放在和讀取的php文件相同級別目錄,xml內容如下: xml讀取方式一: xml讀取方式二: ...
  • 在這個問題中,我們期望得到的結果是找到這三輪比賽中,每輪都進球的球員都有誰。下麵用python來模擬一下,先生成一批數據: 如上代碼所示我們生成了三輪比賽的數據,想要得到三輪比賽中,哪位球員在每輪比賽都進球,有這麼幾種方法: 一. 遍歷 這種方法效率不高,並且笨重 二. 與運算 與運算清晰明瞭,利用 ...
  • 1.IOC和DI IOC和DI是Spring核心思想不同方面的描述,IOC和DI是差不多的概念,重要特征是介面依賴,是把對象關係推遲到運行時去確定 1.1控制反轉(Inversion of Control): 控制反轉是一個重要的面向以對象編程的法則來削減電腦程式的耦合問題,也是輕量級Spring ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...