在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一個(gè)優(yōu)秀的Controller層邏輯

Android編程精選 ? 來源:稀土掘金技術(shù)社區(qū) ? 作者:gelald ? 2022-08-03 10:55 ? 次閱讀

一個(gè)優(yōu)秀的Controller層邏輯

說到 Controller,相信大家都不陌生,它可以很方便地對(duì)外提供數(shù)據(jù)接口。它的定位,我認(rèn)為是「不可或缺的配角」,說它不可或缺是因?yàn)闊o(wú)論是傳統(tǒng)的三層架構(gòu)還是現(xiàn)在的COLA架構(gòu),Controller 層依舊有一席之地,說明他的必要性;說它是配角是因?yàn)?Controller 層的代碼一般是不負(fù)責(zé)具體的邏輯業(yè)務(wù)邏輯實(shí)現(xiàn),但是它負(fù)責(zé)接收和響應(yīng)請(qǐng)求

從現(xiàn)狀看問題

Controller 主要的工作有以下幾項(xiàng)

  • 接收請(qǐng)求并解析參數(shù)
  • 調(diào)用 Service 執(zhí)行具體的業(yè)務(wù)代碼(可能包含參數(shù)校驗(yàn))
  • 捕獲業(yè)務(wù)邏輯異常做出反饋
  • 業(yè)務(wù)邏輯執(zhí)行成功做出響應(yīng)
//DTO
@Data
publicclassTestDTO{
privateIntegernum;
privateStringtype;
}


//Service
@Service
publicclassTestService{

publicDoubleservice(TestDTOtestDTO)throwsException{
if(testDTO.getNum()<=?0){
thrownewException("輸入的數(shù)字需要大于0");
}
if(testDTO.getType().equals("square")){
returnMath.pow(testDTO.getNum(),2);
}
if(testDTO.getType().equals("factorial")){
doubleresult=1;
intnum=testDTO.getNum();
while(num>1){
result=result*num;
num-=1;
}
returnresult;
}
thrownewException("未識(shí)別的算法");
}
}


//Controller
@RestController
publicclassTestController{

privateTestServicetestService;

@PostMapping("/test")
publicDoubletest(@RequestBodyTestDTOtestDTO){
try{
Doubleresult=this.testService.service(testDTO);
returnresult;
}catch(Exceptione){
thrownewRuntimeException(e);
}
}

@Autowired
publicDTOidsetTestService(TestServicetestService){
this.testService=testService;
}
}

如果真的按照上面所列的工作項(xiàng)來開發(fā) Controller 代碼會(huì)有幾個(gè)問題

  • 參數(shù)校驗(yàn)過多地耦合了業(yè)務(wù)代碼,違背單一職責(zé)原則
  • 可能在多個(gè)業(yè)務(wù)中都拋出同一個(gè)異常,導(dǎo)致代碼重復(fù)
  • 各種異常反饋和成功響應(yīng)格式不統(tǒng)一,接口對(duì)接不友好

改造 Controller 層邏輯

統(tǒng)一返回結(jié)構(gòu)

統(tǒng)一返回值類型無(wú)論項(xiàng)目前后端是否分離都是非常必要的,方便對(duì)接接口的開發(fā)人員更加清晰地知道這個(gè)接口的調(diào)用是否成功(不能僅僅簡(jiǎn)單地看返回值是否為 null 就判斷成功與否,因?yàn)橛行┙涌诘脑O(shè)計(jì)就是如此),使用一個(gè)狀態(tài)碼、狀態(tài)信息就能清楚地了解接口調(diào)用情況

//定義返回?cái)?shù)據(jù)結(jié)構(gòu)
publicinterfaceIResult{
IntegergetCode();
StringgetMessage();
}

//常用結(jié)果的枚舉
publicenumResultEnumimplementsIResult{
SUCCESS(2001,"接口調(diào)用成功"),
VALIDATE_FAILED(2002,"參數(shù)校驗(yàn)失敗"),
COMMON_FAILED(2003,"接口調(diào)用失敗"),
FORBIDDEN(2004,"沒有權(quán)限訪問資源");

privateIntegercode;
privateStringmessage;

//省略get、set方法和構(gòu)造方法
}

//統(tǒng)一返回?cái)?shù)據(jù)結(jié)構(gòu)
@Data
@NoArgsConstructor
@AllArgsConstructor
publicclassResult<T>{
privateIntegercode;
privateStringmessage;
privateTdata;

publicstaticResultsuccess(Tdata){
returnnewResult<>(ResultEnum.SUCCESS.getCode(),ResultEnum.SUCCESS.getMessage(),data);
}

publicstaticResultsuccess(Stringmessage,Tdata){
returnnewResult<>(ResultEnum.SUCCESS.getCode(),message,data);
}

publicstaticResultfailed(){
returnnewResult<>(ResultEnum.COMMON_FAILED.getCode(),ResultEnum.COMMON_FAILED.getMessage(),null);
}

publicstaticResultfailed(Stringmessage){
returnnewResult<>(ResultEnum.COMMON_FAILED.getCode(),message,null);
}

publicstaticResultfailed(IResulterrorResult){
returnnewResult<>(errorResult.getCode(),errorResult.getMessage(),null);
}

publicstaticResultinstance(Integercode,Stringmessage,Tdata){
Resultresult=newResult<>();
result.setCode(code);
result.setMessage(message);
result.setData(data);
returnresult;
}
}

統(tǒng)一返回結(jié)構(gòu)后,在 Controller 中就可以使用了,但是每一個(gè) Controller 都寫這么一段最終封裝的邏輯,這些都是很重復(fù)的工作,所以還要繼續(xù)想辦法進(jìn)一步處理統(tǒng)一返回結(jié)構(gòu)

統(tǒng)一包裝處理

Spring 中提供了一個(gè)類 ResponseBodyAdvice ,能幫助我們實(shí)現(xiàn)上述需求

ResponseBodyAdvice 是對(duì) Controller 返回的內(nèi)容在 HttpMessageConverter 進(jìn)行類型轉(zhuǎn)換之前攔截,進(jìn)行相應(yīng)的處理操作后,再將結(jié)果返回給客戶端。那這樣就可以把統(tǒng)一包裝的工作放到這個(gè)類里面。

publicinterfaceResponseBodyAdvice<T>{
booleansupports(MethodParameterreturnType,Class>converterType);

@Nullable
TbeforeBodyWrite(@NullableTbody,MethodParameterreturnType,MediaTypeselectedContentType,Class>selectedConverterType,ServerHttpRequestrequest,ServerHttpResponseresponse);
}
  • supports:判斷是否要交給 beforeBodyWrite 方法執(zhí)行,ture:需要;false:不需要
  • beforeBodyWrite:對(duì) response 進(jìn)行具體的處理
//如果引入了swagger或knife4j的文檔生成組件,這里需要僅掃描自己項(xiàng)目的包,否則文檔無(wú)法正常生成
@RestControllerAdvice(basePackages="com.example.demo")
publicclassResponseAdviceimplementsResponseBodyAdvice<Object>{
@Override
publicbooleansupports(MethodParameterreturnType,Class>converterType){
//如果不需要進(jìn)行封裝的,可以添加一些校驗(yàn)手段,比如添加標(biāo)記排除的注解
returntrue;
}


@Override
publicObjectbeforeBodyWrite(Objectbody,MethodParameterreturnType,MediaTypeselectedContentType,Class>selectedConverterType,ServerHttpRequestrequest,ServerHttpResponseresponse){
//提供一定的靈活度,如果body已經(jīng)被包裝了,就不進(jìn)行包裝
if(bodyinstanceofResult){
returnbody;
}
returnResult.success(body);
}
}

經(jīng)過這樣改造,既能實(shí)現(xiàn)對(duì) Controller 返回的數(shù)據(jù)進(jìn)行統(tǒng)一包裝,又不需要對(duì)原有代碼進(jìn)行大量的改動(dòng)

參數(shù)校驗(yàn)

Java API 的規(guī)范 JSR303 定義了校驗(yàn)的標(biāo)準(zhǔn) validation-api ,其中一個(gè)比較出名的實(shí)現(xiàn)是 hibernate validation ,spring validation 是對(duì)其的二次封裝,常用于 SpringMVC 的參數(shù)自動(dòng)校驗(yàn),參數(shù)校驗(yàn)的代碼就不需要再與業(yè)務(wù)邏輯代碼進(jìn)行耦合了

@PathVariable 和 @RequestParam 參數(shù)校驗(yàn)

Get 請(qǐng)求的參數(shù)接收一般依賴這兩個(gè)注解,但是處于 url 有長(zhǎng)度限制和代碼的可維護(hù)性,超過 5 個(gè)參數(shù)盡量用實(shí)體來傳參對(duì) @PathVariable@RequestParam 參數(shù)進(jìn)行校驗(yàn)需要在入?yún)⒙暶骷s束的注解

如果校驗(yàn)失敗,會(huì)拋出 MethodArgumentNotValidException 異常

@RestController(value="prettyTestController")
@RequestMapping("/pretty")
@Validated
publicclassTestController{

privateTestServicetestService;

@GetMapping("/{num}")
publicIntegerdetail(@PathVariable("num")@Min(1)@Max(20)Integernum){
returnnum*num;
}

@GetMapping("/getByEmail")
publicTestDTOgetByAccount(@RequestParam@NotBlank@EmailStringemail){
TestDTOtestDTO=newTestDTO();
testDTO.setEmail(email);
returntestDTO;
}

@Autowired
publicvoidsetTestService(TestServiceprettyTestService){
this.testService=prettyTestService;
}
}

校驗(yàn)原理

在 SpringMVC 中,有一個(gè)類是 RequestResponseBodyMethodProcessor ,這個(gè)類有兩個(gè)作用(實(shí)際上可以從名字上得到一點(diǎn)啟發(fā))

  • 用于解析 @RequestBody 標(biāo)注的參數(shù)
  • 處理 @ResponseBody 標(biāo)注方法的返回值

解析 @RequestBoyd 標(biāo)注參數(shù)的方法是 resolveArgument

publicclassRequestResponseBodyMethodProcessorextendsAbstractMessageConverterMethodProcessor{
/**
*ThrowsMethodArgumentNotValidExceptionifvalidationfails.
*@throwsHttpMessageNotReadableExceptionif{@linkRequestBody#required()}
*is{@codetrue}andthereisnobodycontentorifthereisnosuitable
*convertertoreadthecontentwith.
*/
@Override
publicObjectresolveArgument(MethodParameterparameter,@NullableModelAndViewContainermavContainer,
NativeWebRequestwebRequest,@NullableWebDataBinderFactorybinderFactory)throwsException{

parameter=parameter.nestedIfOptional();
//把請(qǐng)求數(shù)據(jù)封裝成標(biāo)注的DTO對(duì)象
Objectarg=readWithMessageConverters(webRequest,parameter,parameter.getNestedGenericParameterType());
Stringname=Conventions.getVariableNameForParameter(parameter);

if(binderFactory!=null){
WebDataBinderbinder=binderFactory.createBinder(webRequest,arg,name);
if(arg!=null){
//執(zhí)行數(shù)據(jù)校驗(yàn)
validateIfApplicable(binder,parameter);
//如果校驗(yàn)不通過,就拋出MethodArgumentNotValidException異常
//如果我們不自己捕獲,那么最終會(huì)由DefaultHandlerExceptionResolver捕獲處理
if(binder.getBindingResult().hasErrors()&&isBindExceptionRequired(binder,parameter)){
thrownewMethodArgumentNotValidException(parameter,binder.getBindingResult());
}
}
if(mavContainer!=null){
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX+name,binder.getBindingResult());
}
}

returnadaptArgumentIfNecessary(arg,parameter);
}
}

publicabstractclassAbstractMessageConverterMethodArgumentResolverimplementsHandlerMethodArgumentResolver{
/**
*Validatethebindingtargetifapplicable.
*

Thedefaultimplementationchecksfor{@code@javax.validation.Valid}, *Spring's{@linkorg.springframework.validation.annotation.Validated}, *andcustomannotationswhosenamestartswith"Valid". *@parambindertheDataBindertobeused *@paramparameterthemethodparameterdescriptor *@since4.1.5 *@see#isBindExceptionRequired */ protectedvoidvalidateIfApplicable(WebDataBinderbinder,MethodParameterparameter){ //獲取參數(shù)上的所有注解 Annotation[]annotations=parameter.getParameterAnnotations(); for(Annotationann:annotations){ //如果注解中包含了@Valid、@Validated或者是名字以Valid開頭的注解就進(jìn)行參數(shù)校驗(yàn) Object[]validationHints=ValidationAnnotationUtils.determineValidationHints(ann); if(validationHints!=null){ //實(shí)際校驗(yàn)邏輯,最終會(huì)調(diào)用HibernateValidator執(zhí)行真正的校驗(yàn) //所以SpringValidation是對(duì)HibernateValidation的二次封裝 binder.validate(validationHints); break; } } } }

@RequestBody 參數(shù)校驗(yàn)

Post、Put 請(qǐng)求的參數(shù)推薦使用 @RequestBody 請(qǐng)求體參數(shù)

對(duì) @RequestBody 參數(shù)進(jìn)行校驗(yàn)需要在 DTO 對(duì)象中加入校驗(yàn)條件后,再搭配 @Validated 即可完成自動(dòng)校驗(yàn)如果校驗(yàn)失敗,會(huì)拋出 ConstraintViolationException 異常

//DTO
@Data
publicclassTestDTO{
@NotBlank
privateStringuserName;

@NotBlank
@Length(min=6,max=20)
privateStringpassword;

@NotNull
@Email
privateStringemail;
}

//Controller
@RestController(value="prettyTestController")
@RequestMapping("/pretty")
publicclassTestController{

privateTestServicetestService;

@PostMapping("/test-validation")
publicvoidtestValidation(@RequestBody@ValidatedTestDTOtestDTO){
this.testService.save(testDTO);
}

@Autowired
publicvoidsetTestService(TestServicetestService){
this.testService=testService;
}
}

校驗(yàn)原理

聲明約束的方式,注解加到了參數(shù)上面,可以比較容易猜測(cè)到是使用了 AOP 對(duì)方法進(jìn)行增強(qiáng)

而實(shí)際上 Spring 也是通過 MethodValidationPostProcessor 動(dòng)態(tài)注冊(cè) AOP 切面,然后使用 MethodValidationInterceptor 對(duì)切點(diǎn)方法進(jìn)行織入增強(qiáng)

publicclassMethodValidationPostProcessorextendsAbstractBeanFactoryAwareAdvisingPostProcessorimplementsInitializingBean{

//指定了創(chuàng)建切面的Bean的注解
privateClassvalidatedAnnotationType=Validated.class;

@Override
publicvoidafterPropertiesSet(){
//為所有@Validated標(biāo)注的Bean創(chuàng)建切面
Pointcutpointcut=newAnnotationMatchingPointcut(this.validatedAnnotationType,true);
//創(chuàng)建Advisor進(jìn)行增強(qiáng)
this.advisor=newDefaultPointcutAdvisor(pointcut,createMethodValidationAdvice(this.validator));
}

//創(chuàng)建Advice,本質(zhì)就是一個(gè)方法攔截器
protectedAdvicecreateMethodValidationAdvice(@NullableValidatorvalidator){
return(validator!=null?newMethodValidationInterceptor(validator):newMethodValidationInterceptor());
}
}

publicclassMethodValidationInterceptorimplementsMethodInterceptor{
@Override
publicObjectinvoke(MethodInvocationinvocation)throwsThrowable{
//無(wú)需增強(qiáng)的方法,直接跳過
if(isFactoryBeanMetadataMethod(invocation.getMethod())){
returninvocation.proceed();
}

Class[]groups=determineValidationGroups(invocation);
ExecutableValidatorexecVal=this.validator.forExecutables();
MethodmethodToValidate=invocation.getMethod();
Set>result;
try{
//方法入?yún)⑿r?yàn),最終還是委托給HibernateValidator來校驗(yàn)
//所以SpringValidation是對(duì)HibernateValidation的二次封裝
result=execVal.validateParameters(
invocation.getThis(),methodToValidate,invocation.getArguments(),groups);
}
catch(IllegalArgumentExceptionex){
...
}
//校驗(yàn)不通過拋出ConstraintViolationException異常
if(!result.isEmpty()){
thrownewConstraintViolationException(result);
}
//Controller方法調(diào)用
ObjectreturnValue=invocation.proceed();
//下面是對(duì)返回值做校驗(yàn),流程和上面大概一樣
result=execVal.validateReturnValue(invocation.getThis(),methodToValidate,returnValue,groups);
if(!result.isEmpty()){
thrownewConstraintViolationException(result);
}
returnreturnValue;
}
}

自定義校驗(yàn)規(guī)則

有些時(shí)候 JSR303 標(biāo)準(zhǔn)中提供的校驗(yàn)規(guī)則不滿足復(fù)雜的業(yè)務(wù)需求,也可以自定義校驗(yàn)規(guī)則

自定義校驗(yàn)規(guī)則需要做兩件事情

  • 自定義注解類,定義錯(cuò)誤信息和一些其他需要的內(nèi)容
  • 注解校驗(yàn)器,定義判定規(guī)則
//自定義注解類
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy=MobileValidator.class)
public@interfaceMobile{
/**
*是否允許為空
*/
booleanrequired()defaulttrue;

/**
*校驗(yàn)不通過返回的提示信息
*/
Stringmessage()default"不是一個(gè)手機(jī)號(hào)碼格式";

/**
*Constraint要求的屬性,用于分組校驗(yàn)和擴(kuò)展,留空就好
*/
Class[]groups()default{};
Class[]payload()default{};
}

//注解校驗(yàn)器
publicclassMobileValidatorimplementsConstraintValidator<Mobile,CharSequence>{

privatebooleanrequired=false;

privatefinalPatternpattern=Pattern.compile("^1[34578][0-9]{9}$");//驗(yàn)證手機(jī)號(hào)

/**
*在驗(yàn)證開始前調(diào)用注解里的方法,從而獲取到一些注解里的參數(shù)
*
*@paramconstraintAnnotationannotationinstanceforagivenconstraintdeclaration
*/
@Override
publicvoidinitialize(MobileconstraintAnnotation){
this.required=constraintAnnotation.required();
}

/**
*判斷參數(shù)是否合法
*
*@paramvalueobjecttovalidate
*@paramcontextcontextinwhichtheconstraintisevaluated
*/
@Override
publicbooleanisValid(CharSequencevalue,ConstraintValidatorContextcontext){
if(this.required){
//驗(yàn)證
returnisMobile(value);
}
if(StringUtils.hasText(value)){
//驗(yàn)證
returnisMobile(value);
}
returntrue;
}

privatebooleanisMobile(finalCharSequencestr){
Matcherm=pattern.matcher(str);
returnm.matches();
}
}

自動(dòng)校驗(yàn)參數(shù)真的是一項(xiàng)非常必要、非常有意義的工作。JSR303 提供了豐富的參數(shù)校驗(yàn)規(guī)則,再加上復(fù)雜業(yè)務(wù)的自定義校驗(yàn)規(guī)則,完全把參數(shù)校驗(yàn)和業(yè)務(wù)邏輯解耦開,代碼更加簡(jiǎn)潔,符合單一職責(zé)原則。

更多關(guān)于 Spring 參數(shù)校驗(yàn)請(qǐng)參考:

https://juejin.cn/post/6856541106626363399

自定義異常與統(tǒng)一攔截異常

原來的代碼中可以看到有幾個(gè)問題

  • 拋出的異常不夠具體,只是簡(jiǎn)單地把錯(cuò)誤信息放到了 Exception
  • 拋出異常后,Controller 不能具體地根據(jù)異常做出反饋
  • 雖然做了參數(shù)自動(dòng)校驗(yàn),但是異常返回結(jié)構(gòu)和正常返回結(jié)構(gòu)不一致

自定義異常是為了后面統(tǒng)一攔截異常時(shí),對(duì)業(yè)務(wù)中的異常有更加細(xì)顆粒度的區(qū)分,攔截時(shí)針對(duì)不同的異常作出不同的響應(yīng)

而統(tǒng)一攔截異常的目的一個(gè)是為了可以與前面定義下來的統(tǒng)一包裝返回結(jié)構(gòu)能對(duì)應(yīng)上,另一個(gè)是我們希望無(wú)論系統(tǒng)發(fā)生什么異常,Http 的狀態(tài)碼都要是 200 ,盡可能由業(yè)務(wù)來區(qū)分系統(tǒng)的異常

//自定義異常
publicclassForbiddenExceptionextendsRuntimeException{
publicForbiddenException(Stringmessage){
super(message);
}
}

//自定義異常
publicclassBusinessExceptionextendsRuntimeException{
publicBusinessException(Stringmessage){
super(message);
}
}

//統(tǒng)一攔截異常
@RestControllerAdvice(basePackages="com.example.demo")
publicclassExceptionAdvice{

/**
*捕獲{@codeBusinessException}異常
*/
@ExceptionHandler({BusinessException.class})
publicResulthandleBusinessException(BusinessExceptionex){
returnResult.failed(ex.getMessage());
}

/**
*捕獲{@codeForbiddenException}異常
*/
@ExceptionHandler({ForbiddenException.class})
publicResulthandleForbiddenException(ForbiddenExceptionex){
returnResult.failed(ResultEnum.FORBIDDEN);
}

/**
*{@code@RequestBody}參數(shù)校驗(yàn)不通過時(shí)拋出的異常處理
*/
@ExceptionHandler({MethodArgumentNotValidException.class})
publicResulthandleMethodArgumentNotValidException(MethodArgumentNotValidExceptionex){
BindingResultbindingResult=ex.getBindingResult();
StringBuildersb=newStringBuilder("校驗(yàn)失敗:");
for(FieldErrorfieldError:bindingResult.getFieldErrors()){
sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(",");
}
Stringmsg=sb.toString();
if(StringUtils.hasText(msg)){
returnResult.failed(ResultEnum.VALIDATE_FAILED.getCode(),msg);
}
returnResult.failed(ResultEnum.VALIDATE_FAILED);
}

/**
*{@code@PathVariable}和{@code@RequestParam}參數(shù)校驗(yàn)不通過時(shí)拋出的異常處理
*/
@ExceptionHandler({ConstraintViolationException.class})
publicResulthandleConstraintViolationException(ConstraintViolationExceptionex){
if(StringUtils.hasText(ex.getMessage())){
returnResult.failed(ResultEnum.VALIDATE_FAILED.getCode(),ex.getMessage());
}
returnResult.failed(ResultEnum.VALIDATE_FAILED);
}

/**
*頂級(jí)異常捕獲并統(tǒng)一處理,當(dāng)其他異常無(wú)法處理時(shí)候選擇使用
*/
@ExceptionHandler({Exception.class})
publicResulthandle(Exceptionex){
returnResult.failed(ex.getMessage());
}

}

總結(jié)

做好了這一切改動(dòng)后,可以發(fā)現(xiàn) Controller 的代碼變得非常簡(jiǎn)潔,可以很清楚地知道每一個(gè)參數(shù)、每一個(gè) DTO 的校驗(yàn)規(guī)則,可以很明確地看到每一個(gè) Controller 方法返回的是什么數(shù)據(jù),也可以方便每一個(gè)異常應(yīng)該如何進(jìn)行反饋

這一套操作下來后,我們能更加專注于業(yè)務(wù)邏輯的開發(fā),代碼簡(jiǎn)潔、功能完善,何樂而不為呢?

審核編輯:湯梓紅


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Controller
    +關(guān)注

    關(guān)注

    0

    文章

    398

    瀏覽量

    57349
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4872

    瀏覽量

    69915

原文標(biāo)題:Controller層代碼這么寫,簡(jiǎn)潔又優(yōu)雅!

文章出處:【微信號(hào):AndroidPush,微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    請(qǐng)問下lcd controller.c如何調(diào)用s3c2440 lcd controller.c的?

    下lcd_controller.c如何調(diào)用s3c2440_lcd_controller.c的?我有點(diǎn)看不懂這三個(gè)文件的相互關(guān)系,求大佬詳細(xì)解析下思路
    發(fā)表于 03-21 07:45

    嵌入式系統(tǒng)的硬件

    1、硬件,是整個(gè)嵌入式系統(tǒng)的根本,如果現(xiàn)在單片機(jī)及接口這塊很熟悉,并且能用C和匯編語(yǔ)言來編程的話,從嵌入式系統(tǒng)的硬件走起來相對(duì)容易,硬件也是驅(qū)動(dòng)的基礎(chǔ),
    發(fā)表于 12-22 08:07

    列舉個(gè)memory controller的示例

    。下面舉個(gè)memory controller的示例,這個(gè)memory controller接收請(qǐng)求,并對(duì)特定內(nèi)存地址執(zhí)行某些操作。開始斷
    發(fā)表于 08-31 14:56

    RLC邏輯架構(gòu),RLC子模型

    RLC邏輯架構(gòu) RLC子模型
    發(fā)表于 09-18 15:23 ?1184次閱讀

    個(gè)簡(jiǎn)單的組合邏輯編寫

    個(gè)簡(jiǎn)單的組合邏輯編寫 實(shí)驗(yàn)內(nèi)容:完成實(shí)驗(yàn)內(nèi)容:
    發(fā)表于 02-08 14:37 ?1070次閱讀

    邏輯網(wǎng)絡(luò)拓?fù)浒l(fā)現(xiàn)方法研究

    分析了傳統(tǒng)基于簡(jiǎn)單網(wǎng)管協(xié)議的網(wǎng)絡(luò)拓?fù)浒l(fā)現(xiàn)方法和不足,提出了邏輯網(wǎng)絡(luò)拓?fù)浒l(fā)現(xiàn)方法研究
    發(fā)表于 05-26 16:16 ?29次下載
    <b class='flag-5'>邏輯</b><b class='flag-5'>層</b>網(wǎng)絡(luò)拓?fù)浒l(fā)現(xiàn)方法研究

    如何成為個(gè)優(yōu)秀的電子工程師

    如何成為個(gè)優(yōu)秀的電子工程師,感興趣的小伙伴們可以看看。
    發(fā)表于 08-05 16:09 ?19次下載

    網(wǎng)絡(luò)和傳輸的區(qū)別

    在協(xié)議棧中,傳輸協(xié)議為不同主機(jī)上運(yùn)行的進(jìn)程提供邏輯通信,而網(wǎng)絡(luò)協(xié)議為不同主機(jī)提供邏輯通信。這個(gè)區(qū)別很微妙,但卻非常重要。下面我們用家人
    發(fā)表于 11-02 17:21 ?9376次閱讀
    網(wǎng)絡(luò)<b class='flag-5'>層</b>和傳輸<b class='flag-5'>層</b>的區(qū)別

    個(gè)內(nèi)存條的6PCB設(shè)計(jì)

    個(gè)內(nèi)存條的6PCB設(shè)計(jì)
    發(fā)表于 03-20 09:24 ?0次下載

    整理個(gè)添加光繪的簡(jiǎn)單方法

    ,再添加相應(yīng)的子。所以今天整理個(gè)相對(duì)簡(jiǎn)單的方法,按照下面的操作流程可以導(dǎo)出個(gè)模板,以后只要把模板導(dǎo)入新的brd文件就可以了。
    的頭像 發(fā)表于 07-03 10:19 ?2385次閱讀

    如何把Controller代碼寫的更優(yōu)雅?

    本篇主要要介紹的就是 controller 的處理,個(gè)完整的后端請(qǐng)求由4部分組成。
    的頭像 發(fā)表于 11-01 10:09 ?1050次閱讀

    Controller代碼就該這么寫

    個(gè)優(yōu)秀Controller 邏輯 從現(xiàn)狀看問題 改造
    的頭像 發(fā)表于 11-21 10:28 ?1016次閱讀

    收藏:從零開始搭建個(gè)通用業(yè)務(wù)架構(gòu)

    總的來說我的通用架構(gòu)還是以三架構(gòu)為基礎(chǔ)進(jìn)行演變的,在經(jīng)典的三架構(gòu)中,最上層的是controller,中間是service,下層是dao。在我的架構(gòu)中,最上層是網(wǎng)關(guān)
    的頭像 發(fā)表于 04-03 09:42 ?667次閱讀

    個(gè)晶體管上的通用邏輯元件及其應(yīng)用

    提出了種僅在個(gè)晶體管上制作的正負(fù)邏輯通用無(wú)源邏輯元件。邏輯元件至少有兩
    的頭像 發(fā)表于 06-10 17:11 ?1311次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>個(gè)</b>晶體管上的通用<b class='flag-5'>邏輯</b>元件及其應(yīng)用

    javaweb三架構(gòu)和mvc架構(gòu)

    JavaWeb三架構(gòu)和MVC架構(gòu)是當(dāng)前Web開發(fā)領(lǐng)域中常用的兩種架構(gòu)模式。 、JavaWeb三架構(gòu) JavaWeb三架構(gòu)是將
    的頭像 發(fā)表于 11-22 16:41 ?2071次閱讀
    主站蜘蛛池模板: 伊人色强在线网 | 国产午夜剧场 | 日本高清色视频www 日本高清色视频在线观看免费 | 羞羞视频靠逼视频大全 | 婷婷六月天在线 | 午夜影院入口 | 欧美一区二区三区男人的天堂 | 日本亚洲高清乱码中文在线观看 | tom影院亚洲国产 | 在线网站 看片 网站 | 色丁香在线 | 国产大乳孕妇喷奶水在线观看 | 人人插人人爽 | 狠狠狠狠狠狠 | 色在线视频网站 | 美国一级毛片不卡无毒 | 激情综合视频 | 男生女生靠逼视频 | www.jizz在线观看 | 成人国内精品久久久久影院 | 日韩欧美中文字幕在线视频 | 天天爽天天爽 | 哺乳期xxxx视频 | 亚洲综合色在线观看 | 欧美日a| 好吊788gaoco| 色婷婷色丁香 | 中国一级特黄真人毛片 | 欧美色老头 | 在线看片你懂得 | 日本aaaaa毛片在线视频 | 色狠狠综合 | 色色视频网 | 婷婷丁香啪啪 | 国产在线视欧美亚综合 | 狠狠色丁香久久综合网 | 一区二区三区亚洲 | 亚洲成人免费 | 国产亚洲第一伦理第一区 | 在线网站黄色 | 黄色一级a毛片 |