LiveData原理解析和仿写一个LiveDataBus

2024-02-17 17:08

本文主要是介绍LiveData原理解析和仿写一个LiveDataBus,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

引入LiveData:

implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"

学习内容:

//这个实际上还是继承了LiveData
public class MutableLiveData<T> extends LiveData<T> {@Overridepublic void postValue(T value) {super.postValue(value);}@Overridepublic void setValue(T value) {super.setValue(value);}
}

开始使用

//初始化
MutableLiveData<String> liveData = new MutableLiveData<>();
//有两种发送值的方式
//第一种
liveData.setValue("123");
//第二种
liveData.postValue("456");

接下来从源码的角度来分析这两个方法的区别。
liveData.postValue

	private final Runnable mPostValueRunnable = new Runnable() {@Overridepublic void run() {Object newValue;synchronized (mDataLock) {newValue = mPendingData;mPendingData = NOT_SET;}//noinspection unchecked//postValue切换到主线程,最终还是调用了setValue()setValue((T) newValue);}};protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;}if (!postTask) {return;}//可以看到这里我们将赋值的操作切换到主线程,然后再执行。ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);}

所以liveData.postValue可以再任意的线程调用,因为最终会切换到主线程。
liveData.setValue

 @MainThreadprotected void setValue(T value) {//断言的方式判断当前是不是主线程assertMainThread("setValue");//每次设置值的时候都会+1mVersion++;mData = value;//分发更新的值dispatchingValue(null);}

所以liveData.setValue只能再主线程中调用。
然后当我们调用了setValue()之后,接着看dispatchingValue这个方法

public abstract class LiveData<T> {static final int START_VERSION = -1;//观察者的 mapprivate SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =new SafeIterableMap<>();private int mVersion = START_VERSION;
......void dispatchingValue(@Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated = true;return;}mDispatchingValue = true;do {mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {//因为initiator = null 所以执行这个方法 遍历每个observerfor (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {//执行这个方法considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue = false;}//最后调用观察者的回调的方法private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.//// we still first check observer.active to keep it as the entrance for events. So even if// the observer moved to an active state, if we've not received that event, we better not// notify for a more predictable notification order.if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}//这个判断非常重要哦//刚开始的时候这两个值都为-1//所以第一次进来就会return//但setValue()执行后,mVersion就+1了,所以就不会return了//这样保证了观察者的回调方法不会因为生命周期的多次回调而调用多次//这个判断也是我们下文仿写LiveDataBus的重要判断条件if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;//noinspection unchecked//调用观察者的回调方法,进行更新的操作。observer.mObserver.onChanged((T) mData);}//生命周期改变时会回调
//GenericLifecycleObserver.onStateChanged方法(是感知到生命周期的重要原因)
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {@NonNullfinal LifecycleOwner mOwner;LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {super(observer);mOwner = owner;}@Overrideboolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}//生命周期改变时会回调这个方法@Overridepublic void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {removeObserver(mObserver);return;}//执行父类里的方法activeStateChanged(shouldBeActive());}@Overrideboolean isAttachedTo(LifecycleOwner owner) {return mOwner == owner;}@Overridevoid detachObserver() {mOwner.getLifecycle().removeObserver(this);}}private abstract class ObserverWrapper {final Observer<? super T> mObserver;boolean mActive;int mLastVersion = START_VERSION;ObserverWrapper(Observer<? super T> observer) {mObserver = observer;}abstract boolean shouldBeActive();boolean isAttachedTo(LifecycleOwner owner) {return false;}void detachObserver() {}void activeStateChanged(boolean newActive) {if (newActive == mActive) {return;}// immediately set active state, so we'd never dispatch anything to inactive// ownermActive = newActive;boolean wasInactive = LiveData.this.mActiveCount == 0;LiveData.this.mActiveCount += mActive ? 1 : -1;if (wasInactive && mActive) {onActive();}if (LiveData.this.mActiveCount == 0 && !mActive) {onInactive();}if (mActive) {//分发值 需要更新dispatchingValue(this);}}}
}

到这里观察的回调在哪里调用了,我们就搞清了,我们暂时先不要管onStateChanged再生命周期为什么回调,下文会介绍的不要急。
接下来就是设置观察者了。

liveData.observe(this, new Observer<String>() {@Overridepublic void onChanged(String s) {//在这里处理你自己的逻辑}});

我们就来看这个方法内部是怎么执行的。

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");//判断当前活动是否被销毁,被销毁直接返回,不注册if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);if (existing != null && !existing.isAttachedTo(owner)) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}//所以这里就是等于在activity里写如下的代码//AppCompatActivity.getLifecycle().addObserver(wrapper);//这跟之前我们监听activity生周期的方法一样呢//这就是LiveData为什么能感知生命周期的原因owner.getLifecycle().addObserver(wrapper);}

可以看到这个owner,有的小伙会问,欸?为什么是这个形参的类型是LifecycleOwner这是因为我们的AppCompatActivity继承了FragmentActivity继承了ComponentActivity的实现了LifecycleOwner。

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {......
}public class FragmentActivity extends ComponentActivity implementsViewModelStoreOwner,ActivityCompat.OnRequestPermissionsResultCallback,ActivityCompat.RequestPermissionsRequestCodeValidator {
}public class ComponentActivity extends Activityimplements LifecycleOwner, KeyEventDispatcher.Component {
}

现在我们再来看一种代码

MutableLiveData<String> liveData = new MutableLiveData<>();
liveData.setValue("123");
liveData.observe(this, new Observer<String>() {@Overridepublic void onChanged(String s) {}
});

这段代码注意 我们是先发送值,然后注册监听者,大家认为我们可以在onChanged回调时接收到“123”吗?答案是肯定的,但这有点不符合常理了,毕竟我们先发送值,但这时候还没有注册事件监听者,后来才注册的事件监听者,也能收到,这就有点奇怪了,我们来看一下LiveData是怎么实现的。

这就要牵扯到下面这段代码了

class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {@NonNullfinal LifecycleOwner mOwner;LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {super(observer);mOwner = owner;}@Overrideboolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}@Override//每次生命周期变化都会调用public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {removeObserver(mObserver);return;}//调用父类里的方法activeStateChanged(shouldBeActive());}.....
}
private abstract class ObserverWrapper {final Observer<? super T> mObserver;boolean mActive;int mLastVersion = START_VERSION;.......void activeStateChanged(boolean newActive) {if (newActive == mActive) {return;}// immediately set active state, so we'd never dispatch anything to inactive// ownermActive = newActive;boolean wasInactive = LiveData.this.mActiveCount == 0;LiveData.this.mActiveCount += mActive ? 1 : -1;if (wasInactive && mActive) {onActive();}if (LiveData.this.mActiveCount == 0 && !mActive) {onInactive();}if (mActive) {//调用更新的值分发dispatchingValue(this);}}}.....
}

这段逻辑,相信从上面看到下面的小伙伴都有印象,所以弄清楚onStateChanged为什么在生命周期改变时会被调用。
owner.getLifecycle().addObserver(wrapper);这句话就是我们观察者被注册到监听者的方法。

public class FragmentActivity extends ComponentActivity implementsViewModelStoreOwner,ActivityCompat.OnRequestPermissionsResultCallback,ActivityCompat.RequestPermissionsRequestCodeValidator {.....@Overridepublic Lifecycle getLifecycle() {return super.getLifecycle();}......
}
public class ComponentActivity extends Activityimplements LifecycleOwner, KeyEventDispatcher.Component {private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);@Overridepublic Lifecycle getLifecycle() {return mLifecycleRegistry;}}

所以当我们getLifecycle().addObserver()得到的对象是LifecycleRegistry.addObserver()

	@Overridepublic void addObserver(@NonNull LifecycleObserver observer) {//获取当前activity处在那个生命周期//DESTROYED:0  INITIALIZED:1State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;//下面这两行代码跟LiveData.observe()里的两行代码差不多的ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);if (previous != null) {return;}LifecycleOwner lifecycleOwner = mLifecycleOwner.get();if (lifecycleOwner == null) {// it is null we should be destroyed. Fallback quicklyreturn;}boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;State targetState = calculateTargetState(observer);mAddingObserverCounter++;while ((statefulObserver.mState.compareTo(targetState) < 0&& mObserverMap.contains(observer))) {pushParentState(statefulObserver.mState);statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));popParentState();// mState / subling may have been changed recalculatetargetState = calculateTargetState(observer);}if (!isReentrance) {// we do sync only on the top level.//到这了sync();}mAddingObserverCounter--;}private void sync() {LifecycleOwner lifecycleOwner = mLifecycleOwner.get();if (lifecycleOwner == null) {Log.w(LOG_TAG, "LifecycleOwner is garbage collected, you shouldn't try dispatch "+ "new events from it.");return;}while (!isSynced()) {mNewEventOccurred = false;// no need to check eldest for nullability, because isSynced does it for us.//if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {backwardPass(lifecycleOwner);}Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();if (!mNewEventOccurred && newest != null&& mState.compareTo(newest.getValue().mState) > 0) {forwardPass(lifecycleOwner);}}mNewEventOccurred = false;}private void forwardPass(LifecycleOwner lifecycleOwner) {Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =mObserverMap.iteratorWithAdditions();//从观察者map 迭代所有的观察者while (ascendingIterator.hasNext() && !mNewEventOccurred) {Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();ObserverWithState observer = entry.getValue();while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred&& mObserverMap.contains(entry.getKey()))) {pushParentState(observer.mState);//然后就调用这个方法observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));popParentState();}}}private void backwardPass(LifecycleOwner lifecycleOwner) {Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =mObserverMap.descendingIterator();while (descendingIterator.hasNext() && !mNewEventOccurred) {Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();ObserverWithState observer = entry.getValue();while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred&& mObserverMap.contains(entry.getKey()))) {Event event = downEvent(observer.mState);pushParentState(getStateAfter(event));observer.dispatchEvent(lifecycleOwner, event);popParentState();}}}static class ObserverWithState {State mState;GenericLifecycleObserver mLifecycleObserver;ObserverWithState(LifecycleObserver observer, State initialState) {mLifecycleObserver = Lifecycling.getCallback(observer);mState = initialState;}void dispatchEvent(LifecycleOwner owner, Event event) {State newState = getStateAfter(event);mState = min(mState, newState);//然后到这儿就会回调我们在LiveData里的LifecycleBoundObserver的onStateChanged方法mLifecycleObserver.onStateChanged(owner, event);mState = newState;}}

所以sync()方法最终会导致我们的事件回调(记下这个方法名)
然后我们继续

public class ComponentActivity extends Activityimplements LifecycleOwner, KeyEventDispatcher.Component {......@Override@SuppressWarnings("RestrictedApi")protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);//看到这里,我们把自己传了进去ReportFragment.injectIfNeededIn(this);}......
}
public class ReportFragment extends Fragment {private static final String REPORT_FRAGMENT_TAG = "androidx.lifecycle"+ ".LifecycleDispatcher.report_fragment_tag";public static void injectIfNeededIn(Activity activity) {// ProcessLifecycleOwner should always correctly work and some activities may not extend// FragmentActivity from support lib, so we use framework fragments for activities//这里我们看到我们启动了一个ReportFragmentandroid.app.FragmentManager manager = activity.getFragmentManager();if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();// Hopefully, we are the first to make a transaction.manager.executePendingTransactions();}}@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);dispatchCreate(mProcessListener);dispatch(Lifecycle.Event.ON_CREATE);}@Overridepublic void onStart() {super.onStart();dispatchStart(mProcessListener);dispatch(Lifecycle.Event.ON_START);}@Overridepublic void onResume() {super.onResume();dispatchResume(mProcessListener);dispatch(Lifecycle.Event.ON_RESUME);}private void dispatch(Lifecycle.Event event) {Activity activity = getActivity();if (activity instanceof LifecycleRegistryOwner) {((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);return;}if (activity instanceof LifecycleOwner) {Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();if (lifecycle instanceof LifecycleRegistry) {((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);}}}
.......
}

我们可以看到在ReportFragment的生命周期中都有同一个方法dispatch(),而且最终都会调用getLifecycle().handleLifecycleEvent(event)

 public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {State next = getStateAfter(event);moveToState(next);}private void moveToState(State next) {if (mState == next) {return;}mState = next;if (mHandlingEvent || mAddingObserverCounter != 0) {mNewEventOccurred = true;// we will figure out what to do on upper level.return;}mHandlingEvent = true;//看这个sync(),上面我们就见过这个方法,所以就可以说通了sync();mHandlingEvent = false;}

正是通过这个handleLifecycleEvent调用了sync然后再调用了dispatchEvent然后调用了mLifecycleObserver.onStateChanged

但是我们LiveData一般都不是用来监听生命周期的,都是用来组件间通信的所以我就来仿照LiveDataBus写一个自己的组件通信,但是我们有的时候不需要粘性事件(未注册的监听者不因该监听到注册前所发送过的事件),所以我们需要自己实现一个可以控制黏性的事件通知者。

//黏性事件
public class MyLiveDataBus {private static MyLiveDataBus instance = new MyLiveDataBus();private Map<String, MyMutableLiveData<Object>> liveDataMap;public static MyLiveDataBus getInstance(){return instance;}private MyLiveDataBus(){liveDataMap = new HashMap<>();}public synchronized<T> MyMutableLiveData<T> with(String key,Class<T> clazz){if(!liveDataMap.containsKey(key)){liveDataMap.put(key,new MyMutableLiveData<Object>());}return (MyMutableLiveData<T>) liveDataMap.get(key);}private static class MyMutableLiveData<T> extends MutableLiveData<T> {private boolean isSticky = false;public void observe(@NonNull LifecycleOwner owner, boolean isSticky , @NonNull Observer<? super T> observer) {this.isSticky = isSticky ;observe(owner,observer);}@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {super.observe(owner, observer);try {if(!isSticky ){hook((Observer<T>) observer);}} catch (Exception e) {e.printStackTrace();}}//让mLastVersion和mVersion相等,这样可以在注册监听的时候就不会回调onStateChanged了private void hook(Observer<T> observer) throws Exception {Class<LiveData> liveDataClass = LiveData.class;Field mObserversField = liveDataClass.getDeclaredField("mObservers");mObserversField.setAccessible(true);Object mObservers = mObserversField.get(this);Method get = mObservers.getClass().getDeclaredMethod("get", Object.class);get.setAccessible(true);Object invokeEntry = get.invoke(mObservers, observer);Object observerWrapper = null;if(invokeEntry!=null && invokeEntry instanceof Map.Entry){observerWrapper = ((Map.Entry)invokeEntry).getValue();}if(observerWrapper == null){throw new NullPointerException("ObserverWrapper不能为空");}Class<?> superclass = observerWrapper.getClass().getSuperclass();Field mLastVersionField = superclass.getDeclaredField("mLastVersion");mLastVersionField.setAccessible(true);Field mVersionField = liveDataClass.getDeclaredField("mVersion");mVersionField.setAccessible(true);Object o = mVersionField.get(this);mLastVersionField.set(observerWrapper,o);}}
}

在Activity中

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MutableLiveData<String> liveData = MyLiveDataBus.getInstance().with("key",String.class);liveData.postValue("123");}}public class TwoActivity extends AppCompatActivity {LiveDataBus2.BusMutableLiveData<String> liveData;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);liveData = LiveDataBus2.getInstance().with("code", String.class);liveData.observe(TwoActivity.this,false, new Observer<String>() {@Overridepublic void onChanged(String s) {Log.e("!!!!!!!!",s);}});}}

这样我们就可以实现MainActivity组件和TwoActivity组件之间的通信了,可以不在使用Intent(大小限制)了。

追记:
LiveData当我们继承自Activity就会报错(之前的都是继承了AppCompatActivity)

public class MainActivity extends Activity {
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyLiveDataBus.MyMutableLiveData<String> liveData = MyLiveDataBus.getInstance().with("key",String.class);//这个observe就会报错 因为继承了ActivityliveData.observe(this, new Observer<String>() {@Overridepublic void onChanged(String s) {}});liveData.setValue("123");}
}

那我们需要怎么修改呢

public class MainActivity extends Activity {LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);@SuppressLint("RestrictedApi")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ReportFragment.injectIfNeededIn(this);MyLiveDataBus.MyMutableLiveData<String> liveData = MyLiveDataBus.getInstance().with("key",String.class);liveData.observe(this, new Observer<String>() {@Overridepublic void onChanged(String s) {}});liveData.setValue("123");}@NonNull@Overridepublic Lifecycle getLifecycle() {return lifecycleRegistry;}}}

这篇关于LiveData原理解析和仿写一个LiveDataBus的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/718454

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

Spring MVC使用视图解析的问题解读

《SpringMVC使用视图解析的问题解读》:本文主要介绍SpringMVC使用视图解析的问题解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC使用视图解析1. 会使用视图解析的情况2. 不会使用视图解析的情况总结Spring MVC使用视图