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

java開發(fā)AOP基礎(chǔ)JdkDynamicAopProxy

 更新時(shí)間:2023年07月03日 14:27:32   作者:王偵  
這篇文章主要為大家介紹了java開發(fā)AOP基礎(chǔ)JdkDynamicAopProxy源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1.示例代碼

public class Main {
    public static void main(String[] args) {
        //1. 創(chuàng)建被代理對象
        Cat cat = new Cat();
        System.out.println("--------------------");
        //2. 創(chuàng)建Spring 代理工廠對象 ProxyFactory
        //   ProxyFactory 是Config + Factory 的存在,持有Aop操作所有的生產(chǎn)資料
        ProxyFactory proxyFactory = new ProxyFactory(cat);
        //3. 添加方法攔截器
        MyPointcut pointcut = new MyPointcut();
        proxyFactory.addAdvisor(new DefaultPointcutAdvisor(pointcut, new MethodInterceptor01()));
        proxyFactory.addAdvisor(new DefaultPointcutAdvisor(pointcut, new MethodInterceptor02()));
        //4. 獲取代理對象
        Animal proxy = (Animal) proxyFactory.getProxy();
        proxy.eat();
        System.out.println("--------------------");
        proxy.go();
    }
}

結(jié)果,只有eat()方法被加強(qiáng)了:

--------------------
methodInterceptor01 begin
MethodInterceptor02 begin
貓貓 吃 貓糧!
MethodInterceptor02 end
methodInterceptor01 end
--------------------
貓貓 跑跑~!

切點(diǎn):

  • 1)匹配所有類
  • 2)匹配eat()方法
public class MyPointcut implements Pointcut {
    @Override
    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                return true;
            }
        };
    }
    @Override
    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                if(method.getName().equals("eat")) {
                    return true;
                }
                return false;
            }
            @Override
            public boolean isRuntime() {
                return false;
            }
            @Override
            public boolean matches(Method method, Class<?> targetClass, Object... args) {
                return false;
            }
        };
    }
}

2.ProxyFactory#getProxy()

    public Object getProxy() {
        //主要分析 JdkDynamicAopProxy, 假設(shè) createAopProxy 返回的就是 JdkDynamicAopProxy
        return createAopProxy().getProxy();
    }

2.1 ProxyCreatorSupport#createAopProxy

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

    /**
     * config 就是我們的ProxyFactory對象,咱們說過 ProxyFactory
     * 它是一個(gè)配置管理對象,保存著 創(chuàng)建 代理對象所有的生產(chǎn)資料呢。
     */
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        //條件一:config.isOptimize()   暫且不管
        //條件二:config.isProxyTargetClass()  true 強(qiáng)制使用cglib 動(dòng)態(tài)代理
        //條件三:hasNoUserSuppliedProxyInterfaces(config)
        // 說明被代理對象 沒有實(shí)現(xiàn)任何接口,沒有辦法使用JDK動(dòng)態(tài)代理,只能使用cglib動(dòng)態(tài)代理
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            //條件成立:說明targetClass是接口 或者 已經(jīng)是被代理過的類型了,只能使用Jdk動(dòng)態(tài)代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            //執(zhí)行到else 什么情況?   targetClass 是實(shí)現(xiàn)了接口情況下,
            // 會(huì)走這個(gè)分支!咱們大多數(shù)情況都是 面向接口 編程,所以主要分析 JdkDynamicAopProxy
            return new JdkDynamicAopProxy(config);
        }
    }

2.2 JdkDynamicAopProxy#getProxy()

    @Override
    public Object getProxy() {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }
    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
        }
        // 獲取需要代理的接口數(shù)組
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        // 查找當(dāng)前所有的需要代理的接口,看看 是否有 
        // equals 方法 和 hashcode 方法,如果有,就打個(gè)標(biāo)記。
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        // classLoader :類加載器
        // proxiedInterfaces :生成的代理類 需要 實(shí)現(xiàn)的接口集合
        // this? : JdkDynamicAopProxy   該類 實(shí)現(xiàn)了 InvocationHandler 接口
        // 該方法最終會(huì)返回一個(gè) 代理類 對象。
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

生成的代理類類似于下面所示:

public final class $proxy0 extends Proxy implements Animal {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    public $proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    public final void eat() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

所有方法最后都經(jīng)由InvocationHandler#invoke進(jìn)行處理

3.調(diào)用流程JdkDynamicAopProxy#invoke

    /**
     * Implementation of {@code InvocationHandler.invoke}.
     * <p>Callers will see exactly the exception thrown by the target,
     * unless a hook method throws an exception.
     *   * @param proxy 代理對象
     *   * @param method 目標(biāo)對象的方法
     *   * @param args 目標(biāo)對象方法對應(yīng)的參數(shù)
     */
    @Override
    @Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        // 獲取到創(chuàng)建ProxyFactory時(shí) 提供的 target
        TargetSource targetSource = this.advised.targetSource;
        // 真正的target 的一個(gè)引用
        Object target = null;
        try {
            // 條件成立,說明代理類實(shí)現(xiàn)的哪些接口 沒有定義equals方法,
            // 并且當(dāng)前method 是 equals方法的話,就使用JdkDynamicAopProxy 提供的 equals方法。
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            // 條件成立,說明代理類實(shí)現(xiàn)的哪些接口 沒有定義hashCode方法,
            // 并且當(dāng)前method 是 hashCode方法的話,就使用JdkDynamicAopProxy 提供的 equals方法。
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }
            // 保存返回值
            Object retVal;
            //this.advised.exposeProxy 如果是true,
            // 就要把當(dāng)前這個(gè)代理對象,暴漏 到Aop上下文內(nèi)。
            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }
            // Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            // 根據(jù)targetSource拿到真正的目標(biāo)對象
            target = targetSource.getTarget();
            // 獲取到目標(biāo)對象的 class
            Class<?> targetClass = (target != null ? target.getClass() : null);
            // Get the interception chain for this method.
            // 其實(shí) 這里是最關(guān)鍵的地方,查找適合該方法的 所有方法攔截器。
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            // Check whether we have any advice. If we don't, we can fallback on direct
            // reflective invocation of the target, and avoid creating a MethodInvocation.
            if (chain.isEmpty()) {
                // We can skip creating a MethodInvocation: just invoke the target directly
                // Note that the final invoker must be an InvokerInterceptor so we know it does
                // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                // 直接調(diào)用目標(biāo)對象的目標(biāo)方法。
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            // 說明有匹配當(dāng)前method的方法攔截器,所以要做增強(qiáng)處理了。
            else {
                // We need to create a method invocation...
                MethodInvocation invocation =
                        new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                // 核心!注釋 :ReflectiveMethodInvocation
                retVal = invocation.proceed();
            }
            // Massage return value if necessary.
            // 方法 返回值類型
            Class<?> returnType = method.getReturnType();
            // 如果目標(biāo)方法 返回 目標(biāo)對象,這里 做個(gè)替換,返回 代理對象。
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // 將 上次設(shè)置的proxy 再次 設(shè)置回去到 AopContext中。
                // 因?yàn)楫?dāng)前代理對象的方法已經(jīng)完事了,需要回到再上一層邏輯了,
                // 這里是一個(gè)恢復(fù)現(xiàn)場的邏輯。
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

3.1獲取目標(biāo)方法的攔截器

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
        MethodCacheKey cacheKey = 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);
        }
        return cached;
    }

DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice

    /**
     * 該方法的目的,就是查找出來適合當(dāng)前方法 增強(qiáng)!
     * @param config ProxyFactory,它掌握著AOP的所有資料呢
     * @param method 目標(biāo)對象的方法
     * @param targetClass 目標(biāo)對象的類型
     */
    @Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, @Nullable Class<?> targetClass) {
        // This is somewhat tricky... We have to process introductions first,
        // but we need to preserve order in the ultimate list.
        //AdvisorAdapterRegistry 接口有兩個(gè)作用,一個(gè)作用是
        // 可以向里面注冊 AdvisorAdapter 適配器
        // 適配器目的:1. 將非Advisor 類型的 增強(qiáng),包裝成為Advisor
        //           2. 將Advisor 類型的增強(qiáng) 提取出來對應(yīng) MethodInterceptor
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        // 獲取出來 ProxyFactory 內(nèi)部 持有的 增強(qiáng)信息
        // 1. addAdvice()
        // 2. AddAdvisor()  最終 在ProxyFactory 內(nèi) 都會(huì)包裝成 Advisor 的。
        Advisor[] advisors = config.getAdvisors();
        List<Object> interceptorList = new ArrayList<>(advisors.length);
        // 真實(shí)的目標(biāo)對象類型
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        Boolean hasIntroductions = null;
        for (Advisor advisor : advisors) {
            //條件成立:說明當(dāng)前advisor是包含 切點(diǎn) 信息的,
            // 所以 這個(gè)if內(nèi)部的邏輯,就是做匹配算法。
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                // 轉(zhuǎn)換成 可以獲取到切點(diǎn)信息的接口。
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                // 條件二:成立,說明當(dāng)前被代理對象的class 匹配 
                // 當(dāng)前 Advisor 成功,這一步 只是class 匹配成功。
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    // 獲取 切點(diǎn)信息 的 方法匹配器
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    boolean match;
                    if (mm instanceof IntroductionAwareMethodMatcher) {
                        if (hasIntroductions == null) {
                            hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                        }
                        match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
                    }
                    else {
                        // 如果 目標(biāo)方法 匹配成功 ,那么match = true,靜態(tài)匹配成功。
                        match = mm.matches(method, actualClass);
                    }
                    //靜態(tài)匹配成功的話,再檢查是否需要 運(yùn)行時(shí)匹配。
                    if (match) {
                        // 提取出來 advisor內(nèi)持有的攔截器信息
                        MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                        // 是否運(yùn)行時(shí)匹配?
                        if (mm.isRuntime()) {
                            // Creating a new object instance in the getInterceptors() method
                            // isn't a problem as we normally cache created chains.
                            for (MethodInterceptor interceptor : interceptors) {
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        }
                        else {
                            // 將當(dāng)前advisor內(nèi)部的方法攔截器 追加到 interceptorList
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            }
            // 引介增強(qiáng)
            else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            // 說明當(dāng)前 Advisor 匹配全部class 全部 method
            else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
        // 返回所有匹配當(dāng)前method的方法攔截器
        return interceptorList;
    }

DefaultAdvisorAdapterRegistry#getInterceptors

    @Override
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            if (adapter.supportsAdvice(advice)) {
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[0]);
    }
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
    private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
    /**
     * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
     */
    public DefaultAdvisorAdapterRegistry() {
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }

以MethodBeforeAdviceAdapter為例

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }
    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }
}

看看 MethodBeforeAdviceInterceptor#invoke

public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    }

3.2 調(diào)用ReflectiveMethodInvocation#proceed

    @Override
    @Nullable
    public Object proceed() throws Throwable {
        // We start with an index of -1 and increment early.
        //條件成立:說明方法攔截器 全部都已經(jīng)調(diào)用過了。
        // 接下來 需要執(zhí)行 目標(biāo)對象的目標(biāo)方法。
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            // 調(diào)用連接點(diǎn)
            return invokeJoinpoint();
        }
        // 獲取下一個(gè)方法攔截器
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        // 條件成立:說明 方法攔截器 需要做 運(yùn)行時(shí)匹配,很少用到運(yùn)行時(shí)匹配。
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
            if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                return proceed();
            }
        }
        // 大部分情況,咱們都是執(zhí)行else 。
        else {
            // It's an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.
            // 讓當(dāng)前方法攔截器執(zhí)行,并且將 this 傳遞了 進(jìn)去,this? MethodInvocation
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

總結(jié)

  • 1)ProxyFactory是所有核心要素的匯集地,包括被代理對象、增強(qiáng)器Advisor
  • 2)JdkDynamicAopProxy作為InvocationHandler是所有方法調(diào)用的入口
  • 3)調(diào)用鏈路

step1.JdkDynamicAopProxy#invoke

獲取匹配該方法的攔截器鏈;

調(diào)用ReflectiveMethodInvocation;

step2.ReflectiveMethodInvocation#proceed

根據(jù)currentInterceptorIndex逐個(gè)進(jìn)行調(diào)用;

最終調(diào)用至被代理的方法;

step3.MethodInterceptor#invoke(this),這里的this就是ReflectiveMethodInvocation
  AspectJAroundAdvice、AspectJMethodBeforeAdvice、AspectJAfterAdvice

step4.最后反射調(diào)用至被代理的方法

以上就是java開發(fā)AOP基礎(chǔ)JdkDynamicAopProxy的詳細(xì)內(nèi)容,更多關(guān)于java AOP JdkDynamicAopProxy的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring Data JPA自動(dòng)生成表時(shí)列順序混亂的最新解決辦法

    Spring Data JPA自動(dòng)生成表時(shí)列順序混亂的最新解決辦法

    文章主要介紹了Spring Boot 3.3.5版本中SpringDataJPA自動(dòng)生成表時(shí)列順序混亂的問題,以及如何通過替換Hibernate實(shí)現(xiàn)來解決這個(gè)問題,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • java之swing表格實(shí)現(xiàn)方法

    java之swing表格實(shí)現(xiàn)方法

    這篇文章主要介紹了java之swing表格實(shí)現(xiàn)方法,以實(shí)例形式分析了swing構(gòu)建表格的方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • SpringBoot自定義配置項(xiàng)過程

    SpringBoot自定義配置項(xiàng)過程

    在SpringBoot項(xiàng)目中,通過在application.properties文件中添加配置項(xiàng),然后使用@ConfigurationProperties注解將這些配置項(xiàng)與實(shí)體Bean進(jìn)行綁定,可以實(shí)現(xiàn)配置項(xiàng)與實(shí)體類字段的自動(dòng)關(guān)聯(lián),進(jìn)而方便地讀取配置文件中的數(shù)據(jù),這種方法不僅簡化了配置管理
    2024-11-11
  • java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化

    java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化的相關(guān)資料,文中的示例代碼講解詳細(xì),對我們深入了解java有一定的幫助,感興趣的小伙伴可以了解下
    2023-11-11
  • Java Socket+多線程實(shí)現(xiàn)多人聊天室功能

    Java Socket+多線程實(shí)現(xiàn)多人聊天室功能

    這篇文章主要為大家詳細(xì)介紹了Java Socket+多線程實(shí)現(xiàn)多人聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 設(shè)計(jì)模式之中介者模式_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    設(shè)計(jì)模式之中介者模式_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了設(shè)計(jì)模式之中介者模式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • SpringBoot項(xiàng)目運(yùn)行jar包啟動(dòng)的步驟流程解析

    SpringBoot項(xiàng)目運(yùn)行jar包啟動(dòng)的步驟流程解析

    這篇文章主要介紹了SpringBoot項(xiàng)目運(yùn)行jar包啟動(dòng)的步驟流程,本文分步驟通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-07-07
  • 如何在mybatis中向BLOB字段批量插入數(shù)據(jù)

    如何在mybatis中向BLOB字段批量插入數(shù)據(jù)

    這篇文章主要介紹了如何在mybatis中向BLOB字段批量插入數(shù)據(jù)的相關(guān)知識(shí),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-10-10
  • SpringBoot基于Swagger2構(gòu)建API文檔過程解析

    SpringBoot基于Swagger2構(gòu)建API文檔過程解析

    這篇文章主要介紹了SpringBoot基于Swagger2構(gòu)建API文檔過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Java 如何從spring容器中獲取注入的bean對象

    Java 如何從spring容器中獲取注入的bean對象

    這篇文章主要介紹了Java 如何從spring容器中獲取注入的bean對象,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-11-11

最新評論