最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring AOP實(shí)現(xiàn)原理解析

 更新時(shí)間:2017年12月14日 08:49:08   作者:大啊璐  
這篇文章主要為大家詳細(xì)介紹了Spring AOP的實(shí)現(xiàn)原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

什么是AOP

AOP(Aspect-OrientedProgramming,面向方面編程),可以說(shuō)是OOP(Object-Oriented Programing,面向?qū)ο缶幊蹋┑难a(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來(lái)建立一種對(duì)象層次結(jié)構(gòu),用以模擬公共行為的一個(gè)集合。當(dāng)我們需要為分散的對(duì)象引入公共行為的時(shí)候,OOP則顯得無(wú)能為力。也就是說(shuō),OOP允許你定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系。例如日志功能。日志代碼往往水平地散布在所有對(duì)象層次中,而與它所散布到的對(duì)象的核心功能毫無(wú)關(guān)系。對(duì)于其他類(lèi)型的代碼,如安全性、異常處理和透明的持續(xù)性也是如此。這種散布在各處的無(wú)關(guān)的代碼被稱(chēng)為橫切(cross-cutting)代碼,在OOP設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個(gè)模塊的重用。 

而AOP技術(shù)則恰恰相反,它利用一種稱(chēng)為“橫切”的技術(shù),剖解開(kāi)封裝的對(duì)象內(nèi)部,并將那些影響了多個(gè)類(lèi)的公共行為封裝到一個(gè)可重用模塊,并將其名為“Aspect”,即方面。所謂“方面”,簡(jiǎn)單地說(shuō),就是將那些與業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。AOP代表的是一個(gè)橫向的關(guān)系,如果說(shuō)“對(duì)象”是一個(gè)空心的圓柱體,其中封裝的是對(duì)象的屬性和行為;那么面向方面編程的方法,就仿佛一把利刃,將這些空心圓柱體剖開(kāi),以獲得其內(nèi)部的消息。而剖開(kāi)的切面,也就是所謂的“方面”了。然后它又以巧奪天功的妙手將這些剖開(kāi)的切面復(fù)原,不留痕跡。 

使用“橫切”技術(shù),AOP把軟件系統(tǒng)分為兩個(gè)部分:核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)。業(yè)務(wù)處理的主要流程是核心關(guān)注點(diǎn),與之關(guān)系不大的部分是橫切關(guān)注點(diǎn)。橫切關(guān)注點(diǎn)的一個(gè)特點(diǎn)是,他們經(jīng)常發(fā)生在核心關(guān)注點(diǎn)的多處,而各處都基本相似。比如權(quán)限認(rèn)證、日志、事務(wù)處理。Aop 的作用在于分離系統(tǒng)中的各種關(guān)注點(diǎn),將核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)分離開(kāi)來(lái)。正如Avanade公司的高級(jí)方案構(gòu)架師Adam Magee所說(shuō),AOP的核心思想就是“將應(yīng)用程序中的商業(yè)邏輯同對(duì)其提供支持的通用服務(wù)進(jìn)行分離。” 

實(shí)現(xiàn)AOP的技術(shù),主要分為兩大類(lèi):一是采用動(dòng)態(tài)代理技術(shù),利用截取消息的方式,對(duì)該消息進(jìn)行裝飾,以取代原有對(duì)象行為的執(zhí)行;二是采用靜態(tài)織入的方式,引入特定的語(yǔ)法創(chuàng)建“方面”,從而使得編譯器可以在編譯期間織入有關(guān)“方面”的代碼。

AOP使用場(chǎng)景

AOP用來(lái)封裝橫切關(guān)注點(diǎn),具體可以在下面的場(chǎng)景中使用: 

Authentication 權(quán)限
Caching 緩存
Context passing 內(nèi)容傳遞
Error handling 錯(cuò)誤處理
Lazy loading 懶加載
Debugging  調(diào)試
logging, tracing, profiling and monitoring 記錄跟蹤 優(yōu)化 校準(zhǔn)
Performance optimization 性能優(yōu)化
Persistence  持久化
Resource pooling 資源池
Synchronization 同步
Transactions 事務(wù)

AOP相關(guān)概念

方面(Aspect):一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)實(shí)現(xiàn)可能另外橫切多個(gè)對(duì)象。事務(wù)管理是J2EE應(yīng)用中一個(gè)很好的橫切關(guān)注點(diǎn)例子。方面用Spring的 Advisor或攔截器實(shí)現(xiàn)。 

連接點(diǎn)(Joinpoint): 程序執(zhí)行過(guò)程中明確的點(diǎn),如方法的調(diào)用或特定的異常被拋出。 

通知(Advice): 在特定的連接點(diǎn),AOP框架執(zhí)行的動(dòng)作。各種類(lèi)型的通知包括“around”、“before”和“throws”通知。通知類(lèi)型將在下面討論。許多AOP框架包括Spring都是以攔截器做通知模型,維護(hù)一個(gè)“圍繞”連接點(diǎn)的攔截器鏈。Spring中定義了四個(gè)advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice 

切入點(diǎn)(Pointcut): 指定一個(gè)通知將被引發(fā)的一系列連接點(diǎn)的集合。AOP框架必須允許開(kāi)發(fā)者指定切入點(diǎn):例如,使用正則表達(dá)式。 Spring定義了Pointcut接口,用來(lái)組合MethodMatcher和ClassFilter,可以通過(guò)名字很清楚的理解, MethodMatcher是用來(lái)檢查目標(biāo)類(lèi)的方法是否可以被應(yīng)用此通知,而ClassFilter是用來(lái)檢查Pointcut是否應(yīng)該應(yīng)用到目標(biāo)類(lèi)上 

引入(Introduction): 添加方法或字段到被通知的類(lèi)。 Spring允許引入新的接口到任何被通知的對(duì)象。例如,你可以使用一個(gè)引入使任何對(duì)象實(shí)現(xiàn) IsModified接口,來(lái)簡(jiǎn)化緩存。Spring中要使用Introduction, 可有通過(guò)DelegatingIntroductionInterceptor來(lái)實(shí)現(xiàn)通知,通過(guò)DefaultIntroductionAdvisor來(lái)配置Advice和代理類(lèi)要實(shí)現(xiàn)的接口 

目標(biāo)對(duì)象(Target Object): 包含連接點(diǎn)的對(duì)象。也被稱(chēng)作被通知或被代理對(duì)象。POJO 

AOP代理(AOP Proxy): AOP框架創(chuàng)建的對(duì)象,包含通知。 在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。 

織入(Weaving): 組裝方面來(lái)創(chuàng)建一個(gè)被通知對(duì)象。這可以在編譯時(shí)完成(例如使用AspectJ編譯器),也可以在運(yùn)行時(shí)完成。Spring和其他純Java AOP框架一樣,在運(yùn)行時(shí)完成織入。

Spring AOP組件

下面這種類(lèi)圖列出了Spring中主要的AOP組件

如何使用Spring AOP

可以通過(guò)配置文件或者編程的方式來(lái)使用Spring AOP。 

配置可以通過(guò)xml文件來(lái)進(jìn)行,大概有四種方式:
1. 配置ProxyFactoryBean,顯式地設(shè)置advisors, advice, target等
2. 配置AutoProxyCreator,這種方式下,還是如以前一樣使用定義的bean,但是從容器中獲得的其實(shí)已經(jīng)是代理對(duì)象
3. 通過(guò)<aop:config>來(lái)配置
4. 通過(guò)<aop: aspectj-autoproxy>來(lái)配置,使用AspectJ的注解來(lái)標(biāo)識(shí)通知及切入點(diǎn) 

也可以直接使用ProxyFactory來(lái)以編程的方式使用Spring AOP,通過(guò)ProxyFactory提供的方法可以設(shè)置target對(duì)象, advisor等相關(guān)配置,最終通過(guò) getProxy()方法來(lái)獲取代理對(duì)象 

具體使用的示例可以google. 這里略去

Spring AOP代理對(duì)象的生成

Spring提供了兩種方式來(lái)生成代理對(duì)象: JDKProxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據(jù)AdvisedSupport對(duì)象的配置來(lái)決定。默認(rèn)的策略是如果目標(biāo)類(lèi)是接口,則使用JDK動(dòng)態(tài)代理技術(shù),否則使用Cglib來(lái)生成代理。下面我們來(lái)研究一下Spring如何使用JDK來(lái)生成代理對(duì)象,具體的生成代碼放在JdkDynamicAopProxy這個(gè)類(lèi)中,直接上相關(guān)代碼:

/** 
  * <ol> 
  * <li>獲取代理類(lèi)要實(shí)現(xiàn)的接口,除了Advised對(duì)象中配置的,還會(huì)加上SpringProxy, Advised(opaque=false) 
  * <li>檢查上面得到的接口中有沒(méi)有定義 equals或者h(yuǎn)ashcode的接口 
  * <li>調(diào)用Proxy.newProxyInstance創(chuàng)建代理對(duì)象 
  * </ol> 
  */ 
  public Object getProxy(ClassLoader classLoader) { 
    if (logger.isDebugEnabled()) { 
      logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource()); 
    } 
    Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised); 
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); 
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 
} 

那這個(gè)其實(shí)很明了,注釋上我也已經(jīng)寫(xiě)清楚了,不再贅述。 

下面的問(wèn)題是,代理對(duì)象生成了,那切面是如何織入的?
我們知道InvocationHandler是JDK動(dòng)態(tài)代理的核心,生成的代理對(duì)象的方法調(diào)用都會(huì)委托到InvocationHandler.invoke()方法。而通過(guò)JdkDynamicAopProxy的簽名我們可以看到這個(gè)類(lèi)其實(shí)也實(shí)現(xiàn)了InvocationHandler,下面我們就通過(guò)分析這個(gè)類(lèi)中實(shí)現(xiàn)的invoke()方法來(lái)具體看下Spring AOP是如何織入切面的。

publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable { 
    MethodInvocation invocation = null; 
    Object oldProxy = null; 
    boolean setProxyContext = false; 
  
    TargetSource targetSource = this.advised.targetSource; 
    Class targetClass = null; 
    Object target = null; 
  
    try { 
      //eqauls()方法,具目標(biāo)對(duì)象未實(shí)現(xiàn)此方法 
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){ 
        return (equals(args[0])? Boolean.TRUE : Boolean.FALSE); 
      } 
  
      //hashCode()方法,具目標(biāo)對(duì)象未實(shí)現(xiàn)此方法 
      if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){ 
        return newInteger(hashCode()); 
      } 
  
      //Advised接口或者其父接口中定義的方法,直接反射調(diào)用,不應(yīng)用通知 
      if (!this.advised.opaque &&method.getDeclaringClass().isInterface() 
          &&method.getDeclaringClass().isAssignableFrom(Advised.class)) { 
        // Service invocations onProxyConfig with the proxy config... 
        return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args); 
      } 
  
      Object retVal = null; 
  
      if (this.advised.exposeProxy) { 
        // Make invocation available ifnecessary. 
        oldProxy = AopContext.setCurrentProxy(proxy); 
        setProxyContext = true; 
      } 
  
      //獲得目標(biāo)對(duì)象的類(lèi) 
      target = targetSource.getTarget(); 
      if (target != null) { 
        targetClass = target.getClass(); 
      } 
  
      //獲取可以應(yīng)用到此方法上的Interceptor列表 
      List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass); 
  
      //如果沒(méi)有可以應(yīng)用到此方法的通知(Interceptor),此直接反射調(diào)用 method.invoke(target, args) 
      if (chain.isEmpty()) { 
        retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args); 
      } else { 
        //創(chuàng)建MethodInvocation 
        invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); 
        retVal = invocation.proceed(); 
      } 
  
      // Massage return value if necessary. 
      if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy) 
          &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { 
        // Special case: it returned"this" and the return type of the method 
        // is type-compatible. Notethat we can't help if the target sets 
        // a reference to itself inanother returned object. 
        retVal = proxy; 
      } 
      return retVal; 
    } finally { 
      if (target != null && !targetSource.isStatic()) { 
        // Must have come fromTargetSource. 
        targetSource.releaseTarget(target); 
      } 
      if (setProxyContext) { 
        // Restore old proxy. 
        AopContext.setCurrentProxy(oldProxy); 
      } 
    } 
  } 

主流程可以簡(jiǎn)述為:獲取可以應(yīng)用到此方法上的通知鏈(Interceptor Chain),如果有,則應(yīng)用通知,并執(zhí)行joinpoint; 如果沒(méi)有,則直接反射執(zhí)行joinpoint。而這里的關(guān)鍵是通知鏈?zhǔn)侨绾潍@取的以及它又是如何執(zhí)行的,下面逐一分析下。 

首先,從上面的代碼可以看到,通知鏈?zhǔn)峭ㄟ^(guò)Advised.getInterceptorsAndDynamicInterceptionAdvice()這個(gè)方法來(lái)獲取的,我們來(lái)看下這個(gè)方法的實(shí)現(xiàn):

public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) { 
          MethodCacheKeycacheKey = new MethodCacheKey(method); 
          List<Object>cached = this.methodCache.get(cacheKey); 
          if(cached == null) { 
              cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( 
                        this,method, targetClass); 
              this.methodCache.put(cacheKey,cached); 
          } 
          returncached; 
     } 

可以看到實(shí)際的獲取工作其實(shí)是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()這個(gè)方法來(lái)完成的,獲取到的結(jié)果會(huì)被緩存。

下面來(lái)分析下這個(gè)方法的實(shí)現(xiàn):

/** 
  * 從提供的配置實(shí)例config中獲取advisor列表,遍歷處理這些advisor.如果是IntroductionAdvisor, 
  * 則判斷此Advisor能否應(yīng)用到目標(biāo)類(lèi)targetClass上.如果是PointcutAdvisor,則判斷 
  * 此Advisor能否應(yīng)用到目標(biāo)方法method上.將滿足條件的Advisor通過(guò)AdvisorAdaptor轉(zhuǎn)化成Interceptor列表返回. 
  */ 
  publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) { 
    // This is somewhat tricky... we have to process introductions first, 
    // but we need to preserve order in the ultimate list. 
    List interceptorList = new ArrayList(config.getAdvisors().length); 
  
    //查看是否包含IntroductionAdvisor 
    boolean hasIntroductions = hasMatchingIntroductions(config,targetClass); 
  
    //這里實(shí)際上注冊(cè)一系列AdvisorAdapter,用于將Advisor轉(zhuǎn)化成MethodInterceptor 
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); 
  
    Advisor[] advisors = config.getAdvisors(); 
    for (int i = 0; i <advisors.length; i++) { 
      Advisor advisor = advisors[i]; 
      if (advisor instanceof PointcutAdvisor) { 
        // Add it conditionally. 
        PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor; 
        if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) { 
          //TODO: 這個(gè)地方這兩個(gè)方法的位置可以互換下 
          //將Advisor轉(zhuǎn)化成Interceptor 
          MethodInterceptor[]interceptors = registry.getInterceptors(advisor); 
  
          //檢查當(dāng)前advisor的pointcut是否可以匹配當(dāng)前方法 
          MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher(); 
  
          if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) { 
            if(mm.isRuntime()) { 
              // Creating a newobject instance in the getInterceptors() method 
              // isn't a problemas we normally cache created chains. 
              for (intj = 0; j < interceptors.length; j++) { 
                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm)); 
              } 
            } else { 
              interceptorList.addAll(Arrays.asList(interceptors)); 
            } 
          } 
        } 
      } else if (advisor instanceof IntroductionAdvisor){ 
        IntroductionAdvisor ia =(IntroductionAdvisor) advisor; 
        if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) { 
          Interceptor[] interceptors= registry.getInterceptors(advisor); 
          interceptorList.addAll(Arrays.asList(interceptors)); 
        } 
      } else { 
        Interceptor[] interceptors =registry.getInterceptors(advisor); 
        interceptorList.addAll(Arrays.asList(interceptors)); 
      } 
    } 
    return interceptorList; 
} 
 

這個(gè)方法執(zhí)行完成后,Advised中配置能夠應(yīng)用到連接點(diǎn)或者目標(biāo)類(lèi)的Advisor全部被轉(zhuǎn)化成了MethodInterceptor. 

接下來(lái)我們?cè)倏聪碌玫降臄r截器鏈?zhǔn)窃趺雌鹱饔玫摹?br />

 if (chain.isEmpty()) { 
        retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args); 
      } else { 
        //創(chuàng)建MethodInvocation 
        invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); 
        retVal = invocation.proceed(); 
      } 

從這段代碼可以看出,如果得到的攔截器鏈為空,則直接反射調(diào)用目標(biāo)方法,否則創(chuàng)建MethodInvocation,調(diào)用其proceed方法,觸發(fā)攔截器鏈的執(zhí)行,來(lái)看下具體代碼

public Object proceed() throws Throwable { 
    // We start with an index of -1and increment early. 
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) { 
      //如果Interceptor執(zhí)行完了,則執(zhí)行joinPoint 
      return invokeJoinpoint(); 
    } 
  
    Object interceptorOrInterceptionAdvice = 
      this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); 
     
    //如果要?jiǎng)討B(tài)匹配joinPoint 
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){ 
      // Evaluate dynamic method matcher here: static part will already have 
      // been evaluated and found to match. 
      InterceptorAndDynamicMethodMatcher dm = 
        (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice; 
      //動(dòng)態(tài)匹配:運(yùn)行時(shí)參數(shù)是否滿足匹配條件 
      if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) { 
        //執(zhí)行當(dāng)前Intercetpor 
        returndm.interceptor.invoke(this); 
      } 
      else { 
        //動(dòng)態(tài)匹配失敗時(shí),略過(guò)當(dāng)前Intercetpor,調(diào)用下一個(gè)Interceptor 
        return proceed(); 
      } 
    } 
    else { 
      // It's an interceptor, so we just invoke it: The pointcutwill have 
      // been evaluated statically before this object was constructed. 
      //執(zhí)行當(dāng)前Intercetpor 
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); 
    } 
} 

代碼也比較簡(jiǎn)單,這里不再贅述。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring AOP在web應(yīng)用中的使用方法實(shí)例

    Spring AOP在web應(yīng)用中的使用方法實(shí)例

    這篇文章主要給大家介紹了關(guān)于Spring AOP在web應(yīng)用中的使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring AOP具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 編輯器Ueditor和SpringBoot 的整合方法

    編輯器Ueditor和SpringBoot 的整合方法

    本文通過(guò)實(shí)例代碼給大家介紹了編輯器Ueditor和SpringBoot 的整合方法,需要的朋友參考下吧
    2017-08-08
  • TKmybatis的框架介紹和原理解析

    TKmybatis的框架介紹和原理解析

    tkmybatis是在mybatis框架的基礎(chǔ)上提供了很多工具,讓開(kāi)發(fā)更加高效,下面來(lái)看看這個(gè)框架的基本使用,后面會(huì)對(duì)相關(guān)源碼進(jìn)行分析,感興趣的同學(xué)可以看一下,挺不錯(cuò)的一個(gè)工具
    2020-12-12
  • Mybatis中特殊SQL的執(zhí)行

    Mybatis中特殊SQL的執(zhí)行

    這篇文章主要介紹了Mybatis中特殊SQL的執(zhí)行,介紹內(nèi)容包括模糊查詢、批量刪除、動(dòng)態(tài)設(shè)置表名、添加功能獲取自增的主鍵等相關(guān)資料,需要的小伙伴可以參考一下
    2022-04-04
  • Java中AutoCloseable接口使用小結(jié)

    Java中AutoCloseable接口使用小結(jié)

    Java7引入的AutoCloseable接口,用于自動(dòng)管理資源的關(guān)閉,避免資源泄露,下面就來(lái)介紹一下Java中AutoCloseable接口使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-01-01
  • Java如何使用Jetty實(shí)現(xiàn)嵌入式的Servlet容器

    Java如何使用Jetty實(shí)現(xiàn)嵌入式的Servlet容器

    這篇文章主要介紹了Java使用Jetty實(shí)現(xiàn)嵌入式的Servlet容器,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,下面我們來(lái)一起了解一下吧
    2019-06-06
  • Java 超詳細(xì)講解hashCode方法

    Java 超詳細(xì)講解hashCode方法

    在 Java 應(yīng)用程序執(zhí)行期間,在同一對(duì)象上多次調(diào)用 hashCode 方法時(shí),必須一致地返回相同的整數(shù),前提是對(duì)象上 equals 比較中所用的信息沒(méi)有被修改。從某一應(yīng)用程序的一次執(zhí)行到同一應(yīng)用程序的另一次執(zhí)行,該整數(shù)無(wú)需保持一致
    2022-04-04
  • 如何在 Linux 上搭建 java 部署環(huán)境(安裝jdk/tomcat/mysql) + 將程序部署到云服務(wù)器上的操作)

    如何在 Linux 上搭建 java 部署環(huán)境(安裝jdk/tomcat/mys

    這篇文章主要介紹了如何在 Linux 上搭建 java 部署環(huán)境(安裝jdk/tomcat/mysql) + 將程序部署到云服務(wù)器上的操作),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-01-01
  • Java8 List集合如何移除滿足條件的元素

    Java8 List集合如何移除滿足條件的元素

    這篇文章主要介紹了Java8 List集合如何移除滿足條件的元素,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • java生成隨機(jī)數(shù)(字符串)示例分享

    java生成隨機(jī)數(shù)(字符串)示例分享

    這篇文章主要介紹了java生成隨機(jī)數(shù)(字符串)示例分享,需要的朋友可以參考下
    2014-03-03

最新評(píng)論

佳木斯市| 阳泉市| 桐庐县| 宜兰县| 湖北省| 普定县| 江山市| 娄烦县| 任丘市| 广灵县| 河东区| 峨眉山市| 叙永县| 久治县| 临清市| 灵石县| 壤塘县| 大兴区| 武夷山市| 荃湾区| 淳安县| 凤冈县| 肇州县| 年辖:市辖区| 敦化市| 巴中市| 安塞县| 广灵县| 双城市| 贡山| 石屏县| 佛学| 依安县| 宜都市| 三亚市| 蓬溪县| 贵南县| 阿勒泰市| 亳州市| 临安市| 松阳县|