安卓架构核心Handler原理解析

这篇具有很好参考价值的文章主要介绍了安卓架构核心Handler原理解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Handler原理

当使用handler去发送一个消息的时候,查看调用栈,发现最终会调用到MessageQueue.equeueMessage,那么说既然是个queue肯定就有增和删,equeueMessage就对应它的增,再看看是谁在调用MessageQueue
不妨猜想一下,既然平常写这个handler都在主线程,而且主线程的入口就在mainmain方法中肯定有关于MessageQueue使用者的信息,而且大概率可能是一个生产者消费者模式,有人不断放message,有人不断拿message,那么我们去看看他的main方法。

一个App从哪开始执行

每个app都有自己的虚拟机,每个虚拟机都会去执行自己的main方法,

为什么每个app要有自己的虚拟机呢

主要是起到隔离的作用,一个app的生死并不会影响其他app或者系统

当我们在点击桌面的某个app

ancher(app)->zygote->jvm->ActivityThread.main()

进入main方法,到底谁再用是怎么工作的

通过排查发现Looper类中有一个MessageQueue,所以确定是Looper在使用这个queue

Looper的创建和启动

 public static void main(String[] args) {
        //省略
        //创建Looper
        Looper.prepareMainLooper();
 
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
 
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
 
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
 
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         
        //启动loop
        Looper.loop();
 
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Looper的创建

Looper.prepareMainLooper();
public static void prepareMainLooper() {
        //准备创建Looper
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
prepare(false);
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
     
        //static final ThreadLocal<looper> sThreadLocal = new ThreadLocal<looper>();
        //ActivityThread通过ThreadLocal这种方式获取Looper
        //创建Looper,往sThreadLocal放了一个Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }
 
Looper构造方法
private Looper(boolean quitAllowed) {
    //创建一个消息队列
        mQueue = new MessageQueue(quitAllowed);
    //得到当前线程
        mThread = Thread.currentThread();
    }

看一下传入的quitAllowed这个参数的作用
我们进入MessageQueue构造方法,再看quit方法

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
 
//调用quit方法可以终止loop
void quit(boolean safe) {
        //如果是true,这个if就会命中,抛出异常,使得quit不能被调用
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
       //省略
    }

Looper的创建时通过ThreadLocal来创建的,所以说在一个线程中肯定只有一个Looper,而且Looper在构造方法中创建的MessageQueue所以一个Looper持有一个MessageQueue

Looper启动

public static void loop() {
         
        final Looper me = myLooper();//return sThreadLocal.get();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }
 
        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;
 
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
 
        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);
 
        boolean slowDeliveryDetected = false;
 
        for (;;) {
            //拿取message
            Message msg = queue.next(); // might block
             
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
 
            try {
                 //省略
                 //分发msg,处理msg
                msg.target.dispatchMessage(msg);
 
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //省略
         
            //重置msg,把其内部的属性初始化,添加进message池
            msg.recycleUnchecked();
        }
    }

取到消息后为何message知道执行哪个handler

msg.target.dispatchMessage(msg); Message的target什么时候被初始化(默认情况)

当我们调用sendMessage后发生了什么

//1
public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
//2
 public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
//3
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        //在哪个线程操作就获取哪个线程的MessageQueue其根本原因是ThreadLocl保证了其唯一
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
//4
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        //赋值
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
 
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //插队
        return queue.enqueueMessage(msg, uptimeMillis);
    }
 

最终我们找到了这个msg.target,所以说你用哪个handler调用的sendMessage就给target赋值成哪个,如果想灵活使用,推荐调用setTarget(Handler target)再调用Message的sendToTarget()

  public void setTarget(Handler target) {
        this.target = target;
    }
 
public void sendToTarget() {
        target.sendMessage(this);
    }
现在我们就知道是哪个handler要调用dispatchMessage(msg),进入dispatchMessage(msg)
   public void dispatchMessage(@NonNull Message msg) {
       //默认不设置的话为空
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //默认不设置的话为空
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //最终回调到了handleMessage
            handleMessage(msg);
        }
    }

MessageQueue的工作原理

MessageQueue的特点

MessageQueue单链表实现的优先级队列

增操作

根据when优先,插入排序

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
 
        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
 
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
 
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //插入排序
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
 
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
取操作
 Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
 
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //等待
            nativePollOnce(ptr, nextPollTimeoutMillis);
 
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //取到消息并和当前时间对比
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        //计算等待时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
 
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //省略
        }
    }

唤醒和阻塞

MessageQueue是一个容器,结合2.2.2,不处理Message有两种情况,而且都会调用nativePollOnce(long ptr, int timeoutMillis)阻塞

/*ptr 线程的id信息   timeoutMillis 等待时间*/
private native void nativePollOnce(long ptr, int timeoutMillis);

1.Message没有到执行时间

 if (now < msg.when) {
     // Next message is not ready.  Set a timeout to wake up when it is ready.
     //计算等待时间
     nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
 }

timeoutMillis不为-1时,到达等待时间后自动唤醒

2.MessageQueue为空 msg为空

if (msg != null) {
 //省略       
} else {
    // No more messages.
    nextPollTimeoutMillis = -1;
}

timeoutMillis为-1,此时不会自动唤醒,需要我们调用增操作进行唤醒,增操作会判断是否需要唤醒

if (needWake) {
     nativeWake(mPtr);
}
 
private native static void nativeWake(long ptr);

3.底层的nativePollOnce(long ptr, int timeoutMillis)和nativeWake(long ptr)

安卓架构核心Handler原理解析
安卓架构核心Handler原理解析

4.子线程无Message一定要调用quit(boolean safe)

quit(boolean safe) -> nativeWake(mPtr) -> next().if (mQuitting) { return null;} -> loop().if (msg == null) {return; }结束死循环

Message的创建

Message是一个链表的节点结构,有next属性,每一个Message在使用后都不会去销毁这个Message,而是调用 msg.recycleUnchecked();回收掉msg,会链在复用链上。

void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;
        //插入复用链表
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

1.new Message()申请内存,最简单粗暴的方法区

使用new,不断的去new会导致内存抖动

2.调用obtain

Message内部会维护一个重置后的链节点头,通过这个节点来获取重置后的Message进行复用,避免了重复申请内存

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

handler的内存泄漏问题

普通的内部类,在使用时会持有外部引用,并不是说持有外部引用就一定会导致内存泄漏,对象的回收最终是根据GC根可达算法来判断的

如果说一个Message的执行时间特别长,我们根据2.4.1.4可以知道,message.target就是我们的handler,此时的引用链为

message->handler->activity导致activity不能被回收,造成内存泄漏

方法一:通过程序逻辑来进行保护。

1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。

2.如果你的Handler是被delayMessage持有了引用,那么使用相应的HandlerremoveCallbacks()方法,把消息对象从消息队列移除就行了。

方法二:将Handler声明为静态类。

在创建Handler时让其持有一个Activity的弱引用,当GC时弱引用是一定会被回收的

static class MyHandler extends Handler {
    WeakReference<activity> mActivityReference;
 
    MyHandler(Activity activity) {
        mActivityReference= new WeakReference<activity>(activity);
    }
 
    @Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
           //操作//
        }
    }
}

handler的同步屏障机制

MessageQueue中可能说又必须要立即处理的任务,这时候MessageQueue是怎么拿取异步任务的呢

首先往MessageQueue中插入一个TargetnullMessage,就相当于告诉MessageQueue我现在队列里有异步消息,你需要去执行异步消息,那么MessageQueuenext的时候就知道我现在要找异步消息去执行,

//取操作时对标志位的判断,这么看的话就是说如果target为null就相当于有同步消息要处理
if (msg != null && msg.target == null) {
    // Stalled by a barrier.  Find the next asynchronous message in the queue.
    //获取异步任务
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
}
 
 
//发送屏障消息往队列头插入标志位
 private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            //并没有对target赋值
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
            Message prev = null;
            Message p = mMessages;
             
            //插入标志位
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
         
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
 
//移除屏障消息
 public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();
 
            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }
 

我们设置异步消息通过调用MessagesetAsynchronous(boolean async)

 public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }

子线程中使用Looper

使用HandlerThread,它直接给我们封装好了Looper的初始化和获取

   //run中初始化Loop
   public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            //初始化完毕,唤醒线程
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
     
    //获取Loop
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
 
        boolean wasInterrupted = false;
 
        // If the thread has been started, wait until the looper has been created.
         
        synchronized (this) {
            //如果没有初始化好,阻塞
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    wasInterrupted = true;
                }
            }
        }
 
        /*
         * We may need to restore the thread's interrupted flag, because it may
         * have been cleared above since we eat InterruptedExceptions
         */
        if (wasInterrupted) {
            Thread.currentThread().interrupt();
        }
 
        return mLooper;
    }

加锁解决的什么问题呢,如果说不加锁情况调用了getLooper()run()又没有执行到给Loop初始化,那不就是空指针了吗。

IntentService中对HandlerThread的使用

IntentService内部有一个HandlerThread,能够保证任务按部就班的在一个线程上运行,这种按部就班的运行其实就是对线程的控制

IntentService怎么实现的处理异步请求呢

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    @UnsupportedAppUsage
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
     
    //handler
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
 
        @Override
        public void handleMessage(Message msg) {
            //回调我们重写的方法
            onHandleIntent((Intent)msg.obj);
            //杀死IntentService,自杀通过启动id(所以说执行完就死,没有内存泄漏)
            stopSelf(msg.arg1);
        }
    }
 
 
    public IntentService(String name) {
        super();
        mName = name;
    }
 
 
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        //创建一个 HandlerThread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        //启动线程
        thread.start();
        //获取Looper
        mServiceLooper = thread.getLooper();
        //绑定Looper
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
 
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //获取一个Message
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        //给msg初始化属性
        msg.obj = intent;
        //发送消息最终回调到上面的ServiceHandler,从而回调到onHandleIntent()
        mServiceHandler.sendMessage(msg);
    }
 
 
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        //启动服务会调用onStartCommand()从而调用onStart()
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
 
    @Override
    public void onDestroy() {
        //结束Loop
        mServiceLooper.quit();
    }
 
 
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
 
    //具体的逻辑,需要我们继承实现
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
 

到这本篇文章就结束了

原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下}

👍 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!}

⭐️ 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!}

✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!}
文章来源地址https://www.toymoban.com/news/detail-406624.html

到了这里,关于安卓架构核心Handler原理解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring源码(二)Spring底层架构核心概念解析

    BeanDefinition表示 Bean定义 ,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如: class,表示Bean类型 scope,表示Bean作用域,单例或原型等 lazyInit:表示Bean是否是懒加载 initMethodName:表示Bean初始化时要执行的方法 destroyMethodName:表示Bean销毁时要执行的方法 在Spring中,我

    2024年02月15日
    浏览(35)
  • 【Spring专题】Spring之底层架构核心概念解析

    本节课的内容,是后续看Spring源码所必备的概念、类精讲,防止后续看源码的过程中,遇到不会的、看不懂的还得单独跳出来学习。所以,大家好好理解一下这些概念,可以大大地帮助后学源码阅读。 另外还有一点需要 特别声明 : 接口的作用,有时候是用来约束、规范行为

    2024年02月13日
    浏览(34)
  • Quartz核心原理之架构及基本元素介绍

    Quartz是一个作业调度框架,它可以与J2EE和J2SE应用相结合,也可以单独使用。它能够创建多个甚至数万个jobs这样复杂的程序,jobs可以做成标准的java组件或EJBS。Quartz很容易上手,创建一个任务仅需实现Job接口,该接口只有一个方法void execute(JobExecutionContext context) throws JobExecu

    2024年02月05日
    浏览(36)
  • Spring - Spring底层核心原理解析

    1. Bean的生命周期底层原理 2. 依赖注入底层原理 3. 初始化底层原理 4. 推断构造方法底层原理 5. AOP底层原理 6. Spring事务底层原理 对于这三行代码应该,大部分同学应该都是比较熟悉,这是学习Spring的hello world。可是,这三行代码底层都做了什么,比如: 第一行代码,会构造一

    2024年02月07日
    浏览(46)
  • 【Spring】Spring底层核心原理解析

    简单代码: spring.xml内容: AppConfig.class内容: AppConfig.class替代了spring.xml文件,表示spring的配置文件; ApplicationContext在Spring、SpringMVC、SpringBoot中的创建方式: Spring,通过 ClassPathXmlApplicationContext 创建; SpringMVC,通过 XmlWebApplicationContext 创建; SpringBoot,通过 AnnotationConfigAppl

    2024年02月15日
    浏览(32)
  • SpringBoot核心运行原理解析之-------@EnableAutoConfiguration

    我们通常在使用Spring Boot时,只需要引入对应的starters,Spring Boot启动时变回自动加载相关依赖,配置相应的初始化参数,以最快捷,简单的形式对第三方软件进行集成,这边是Spring Boot的自动配置功能。下图是Spring Boot实现该运作机制涉及的核心部分: 上图简单描述了Spring Bo

    2024年02月04日
    浏览(28)
  • 【Spring专题】Spring底层核心原理解析

    Spring啊,可以说是我们大部分Java玩家【最熟悉的陌生人】了吧。八个字形容:似懂非懂,会也不会 你说简单应用,我们大家都会,那真要展开说两句的话,那只能来这么两句:这是第一句,接着是第二句,好了我说完了。 但是啊xdm, 据说Spring是一份非常非常非常优秀的源码

    2024年02月13日
    浏览(45)
  • 走进Spring的世界 —— Spring底层核心原理解析(一)

    这是学习Spring的hello world。可是,这三行代码底层都做了什么,比如: 第一行代码,会构造一个ClassPathXmlApplicationContext对象,ClassPathXmlApplicationContext该如何理解,调用该构造方法除开会实例化得到一个对象,还会做哪些事情? 第二行代码,会调用ClassPathXmlApplicationContext的ge

    2024年02月07日
    浏览(49)
  • Elasticsearch权威指南:深度解析搜索技术核心概念、原理及实践

    作者:禅与计算机程序设计艺术 2010年,当时仅仅30岁的Elasticsearch创始人黄文坚就率先发布了开源分布式搜索引擎Elasticsearch。从此, Elasticsearch 名扬天下,成为了当前搜索领域的翘楚。随着 Elasticsearch 的快速崛起,越来越多的人开始关注并应用 Elasticsearch 来进行搜索服务。

    2024年02月10日
    浏览(61)
  • Clickhouse分布式表引擎(Distributed)写入核心原理解析

    Clickhouse分布式表引擎(Distributed)写入核心原理解析 Clickhouse分布式表引擎(Distributed)查询核心原理解析 Distributed表引擎是分布式表的代名词,它自身不存储任何数据,而是作为数据分片的透明代理,能够自动路由数据至集群中的各个节点 ,所以Distributed表引擎需要和其他数

    2023年04月27日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包