百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

一文带你搞懂React-native源码解析

bigegpt 2024-08-18 14:09 2 浏览

作者:Peter酱

转发链接:https://mp.weixin.qq.com/s/dLA6v4FoIqNDR8L_v_G6fg

正式开始

  • 环境准备:Node、Watchman、Xcode 和 CocoaPods & XCode ,稳定的代理工具(如果没有稳定的代理工具,基本上可以考虑放弃了)
  • 生成项目
npx react-native init App
cd App 
yarn cd 
cd ios 
pod install (注意不要+sudo,此处必须全局开启代理,否则下载会失败)
cd ..
yarn ios 
  • 如果yarn ios后无法看到Simulator有APP,使用xCode找到这个项目的ios目录的.xcworkspace

?

注意 0.60 版本之后的主项目文件是.xcworkspace,不是.xcodeproj。

?

  • 然后用xCode打开build,成功后模拟器就会出现APP,打开即可进入
  • ??:一定不要升级xCode高版本,跟我的版本保持一致最好,因为高版本xCode的voip唤醒激活会出现电话界面

如果你的环境是windows或者安卓,请参考官网

正式开始

  • 启动后,发现APP这样
  • 我们打开主入口的index.js文件
/**
 * @format
 */

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);
  • 默认使用AppRegistry.registerComponent帮我们注册了一个组件(今天不对原理做过多讲解,有兴趣的可以自己搭建一个React-native脚手架,你会对整套运行原理、流程有一个真正的了解)
  • 接下来看APP组件
import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
} from 'react-native';

import {
  Header,
  LearnMoreLinks,
  Colors,
  DebugInstructions,
  ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';

const App: () => React$Node = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={styles.scrollView}>
          <Header />
          {global.HermesInternal == null ? null : (
            <View style={styles.engine}>
              <Text style={styles.footer}>Engine: Hermes</Text>
            </View>
          )}
          <View style={styles.body}>
            <View style={styles.sectionContainer}>
              <Text style={styles.sectionTitle}>Step One</Text>
              <Text style={styles.sectionDescription}>
                Edit <Text style={styles.highlight}>App.js</Text> to change this
                screen and then come back to see your edits.
              </Text>
            </View>
            <View style={styles.sectionContainer}>
              <Text style={styles.sectionTitle}>See Your Changes</Text>
              <Text style={styles.sectionDescription}>
                <ReloadInstructions />
              </Text>
            </View>
            <View style={styles.sectionContainer}>
              <Text style={styles.sectionTitle}>Debug</Text>
              <Text style={styles.sectionDescription}>
                <DebugInstructions />
              </Text>
            </View>
            <View style={styles.sectionContainer}>
              <Text style={styles.sectionTitle}>Learn More</Text>
              <Text style={styles.sectionDescription}>
                Read the docs to discover what to do next:
              </Text>
            </View>
            <LearnMoreLinks />
          </View>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
 ...
});

export default App;

我们今天只看react-native这个库,默认导出的内容.

  • 即下面这段代码
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
} from 'react-native';
  • 打开react-native源码
'use strict';

import typeof Button from './Libraries/Components/Button';
....

export type HostComponent<T> = _HostComponentInternal<T>;

const invariant = require('invariant');
const warnOnce = require('./Libraries/Utilities/warnOnce');

module.exports = {
  // Components
 
 
  get Button(): Button {
    return require('./Libraries/Components/Button');
  },
  ...
};

if (__DEV__) {
  // $FlowFixMe This is intentional: Flow will error when attempting to access ART.
  Object.defineProperty(module.exports, 'ART', {
    configurable: true,
    get() {
      invariant(
        false,
        'ART has been removed from React Native. ' +
          "It can now be installed and imported from '@react-native-community/art' instead of 'react-native'. " +
          'See https://github.com/react-native-community/art',
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access ListView.
  Object.defineProperty(module.exports, 'ListView', {
    configurable: true,
    get() {
      invariant(
        false,
        'ListView has been removed from React Native. ' +
          'See https://fb.me/nolistview for more information or use ' +
          '`deprecated-react-native-listview`.',
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access SwipeableListView.
  Object.defineProperty(module.exports, 'SwipeableListView', {
    configurable: true,
    get() {
      invariant(
        false,
        'SwipeableListView has been removed from React Native. ' +
          'See https://fb.me/nolistview for more information or use ' +
          '`deprecated-react-native-swipeable-listview`.',
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access WebView.
  Object.defineProperty(module.exports, 'WebView', {
    configurable: true,
    get() {
      invariant(
        false,
        'WebView has been removed from React Native. ' +
          "It can now be installed and imported from 'react-native-webview' instead of 'react-native'. " +
          'See https://github.com/react-native-community/react-native-webview',
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access NetInfo.
  Object.defineProperty(module.exports, 'NetInfo', {
    configurable: true,
    get() {
      invariant(
        false,
        'NetInfo has been removed from React Native. ' +
          "It can now be installed and imported from '@react-native-community/netinfo' instead of 'react-native'. " +
          'See https://github.com/react-native-community/react-native-netinfo',
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access CameraRoll.
  Object.defineProperty(module.exports, 'CameraRoll', {
    configurable: true,
    get() {
      invariant(
        false,
        'CameraRoll has been removed from React Native. ' +
          "It can now be installed and imported from '@react-native-community/cameraroll' instead of 'react-native'. " +
          'See https://github.com/react-native-community/react-native-cameraroll',
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access ImageStore.
  Object.defineProperty(module.exports, 'ImageStore', {
    configurable: true,
    get() {
      invariant(
        false,
        'ImageStore has been removed from React Native. ' +
          'To get a base64-encoded string from a local image use either of the following third-party libraries:' +
          "* expo-file-system: `readAsStringAsync(filepath, 'base64')`" +
          "* react-native-fs: `readFile(filepath, 'base64')`",
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access ImageEditor.
  Object.defineProperty(module.exports, 'ImageEditor', {
    configurable: true,
    get() {
      invariant(
        false,
        'ImageEditor has been removed from React Native. ' +
          "It can now be installed and imported from '@react-native-community/image-editor' instead of 'react-native'. " +
          'See https://github.com/react-native-community/react-native-image-editor',
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access TimePickerAndroid.
  Object.defineProperty(module.exports, 'TimePickerAndroid', {
    configurable: true,
    get() {
      invariant(
        false,
        'TimePickerAndroid has been removed from React Native. ' +
          "It can now be installed and imported from '@react-native-community/datetimepicker' instead of 'react-native'. " +
          'See https://github.com/react-native-community/datetimepicker',
      );
    },
  });

  // $FlowFixMe This is intentional: Flow will error when attempting to access ViewPagerAndroid.
  Object.defineProperty(module.exports, 'ViewPagerAndroid', {
    configurable: true,
    get() {
      invariant(
        false,
        'ViewPagerAndroid has been removed from React Native. ' +
          "It can now be installed and imported from '@react-native-community/viewpager' instead of 'react-native'. " +
          'See https://github.com/react-native-community/react-native-viewpager',
      );
    },
  });
}
  • 我删了一些导入和get定义,方便阅读
  • 这个源码文件大概有650行,module.export暴露出来了很多东西,但是,区分多种
    • 一种是Components组件
    • 一种是API
    • 一种是Plugins
    • 一种是Prop Types
    • 还有一种是最后的DEV环境下,

逐个攻破

  • 首先是组件
  • 其次是API
  • 然后是Plugins
  • 然后是Prop types
  • 最后是DEV环境下的对旧版本的部分API使用方式警告

可以看到入口文件中的一些API

  • 例如
  get AppRegistry(): AppRegistry {
    return require('./Libraries/ReactNative/AppRegistry');
  },
  • 图片
  get Image(): Image {
    return require('./Libraries/Image/Image');
  },

拿Image组件源码示例

  • 找到./Libraries/Image/Image源码
  • 脚手架应该根据是react-native run ios 还是 安卓,选择加载对应js,我们找到Image.ios.js文件,只有200行,今天重点主攻下
  • 默认暴露
module.exports = ((Image: any): React.AbstractComponent<
  ImagePropsType,
  React.ElementRef<typeof RCTImageView>,
> &
  ImageComponentStatics);
  • Image对象
  • Image组件真正展示的
  return (
    <RCTImageView
      {...props}
      ref={forwardedRef}
      style={style}
      resizeMode={resizeMode}
      tintColor={tintColor}
      source={sources}
    />
  );
  • 找到RCTImageView,ImageViewNativeComponent.js这个文件
let ImageViewNativeComponent;

if (global.RN$Bridgeless) {
  ImageViewNativeComponent = codegenNativeComponent<NativeProps>(
    'RCTImageView',
  );
} else {
  ImageViewNativeComponent = requireNativeComponent<NativeProps>(
    'RCTImageView',
  );
}

module.exports = (ImageViewNativeComponent: HostComponent<NativeProps>);
  • 真正展示的是ImageViewNativeComponent,关于上面这段源码我查阅了一些的外文资料和其他源码,最终发现了一个注释
const NativeModules = require('../BatchedBridge/NativeModules');
const turboModuleProxy = global.__turboModuleProxy;

export function get < T: TurboModule > (name: string): ? T {
    if (!global.RN$Bridgeless) {
        // Backward compatibility layer during migration.
        const legacyModule = NativeModules[name];
        if (legacyModule != null) {
            return ((legacyModule: any): T);
        }
    }
    if (turboModuleProxy != null) {
        const module: ? T = turboModuleProxy(name);
        return module;
    }
    return null;
}

export function getEnforcing < T: TurboModule > (name: string): T {
    const module = get(name);
    return module;
}
  • 即Backward compatibility layer during migration.,即迁移过程中向后兼容,即兼容性处理
  • 这个codegenNativeComponent就是图片展示最终的一环,我们去看看是什么

忽略类型等其它空值警告判断,直入主题

  let componentNameInUse =
    options && options.paperComponentName
      ? options.paperComponentName
      : componentName;

  if (options != null && options.paperComponentNameDeprecated != null) {
    if (UIManager.getViewManagerConfig(componentName)) {
      componentNameInUse = componentName;
    } else if (
      options.paperComponentNameDeprecated != null &&
      UIManager.getViewManagerConfig(options.paperComponentNameDeprecated)
    ) {
      componentNameInUse = options.paperComponentNameDeprecated;
    } else {
      throw new Error(
        `Failed to find native component for either ${componentName} or ${options.paperComponentNameDeprecated ||
          '(unknown)'}`,
      );
    }
  }

  // If this function is run at runtime then that means the view configs were not
  // generated with the view config babel plugin, so we need to require the native component.
  //
  // This will be useful during migration, but eventually this will error.
  return (requireNativeComponent<Props>(
    componentNameInUse,
  ): HostComponent<Props>);
  • 还是 要先看UIManager.getViewManagerConfig
'use strict';

import type {Spec} from './NativeUIManager';

interface UIManagerJSInterface extends Spec {
  +getViewManagerConfig: (viewManagerName: string) => Object;
  +createView: (
    reactTag: ?number,
    viewName: string,
    rootTag: number,
    props: Object,
  ) => void;
  +updateView: (reactTag: number, viewName: string, props: Object) => void;
  +manageChildren: (
    containerTag: ?number,
    moveFromIndices: Array<number>,
    moveToIndices: Array<number>,
    addChildReactTags: Array<number>,
    addAtIndices: Array<number>,
    removeAtIndices: Array<number>,
  ) => void;
}

const UIManager: UIManagerJSInterface =
  global.RN$Bridgeless === true
    ? require('./DummyUIManager') // No UIManager in bridgeless mode
    : require('./PaperUIManager');

module.exports = UIManager;
  • 进入PaperUIManager找到getViewManagerConfig
 getViewManagerConfig: function(viewManagerName: string): any {
    if (
      viewManagerConfigs[viewManagerName] === undefined &&
      NativeUIManager.getConstantsForViewManager
    ) {
      try {
        viewManagerConfigs[
          viewManagerName
        ] = NativeUIManager.getConstantsForViewManager(viewManagerName);
      } catch (e) {
        viewManagerConfigs[viewManagerName] = null;
      }
    }

    const config = viewManagerConfigs[viewManagerName];
    if (config) {
      return config;
    }

    // If we're in the Chrome Debugger, let's not even try calling the sync
    // method.
    if (!global.nativeCallSyncHook) {
      return config;
    }

    if (
      NativeUIManager.lazilyLoadView &&
      !triedLoadingConfig.has(viewManagerName)
    ) {
      const result = NativeUIManager.lazilyLoadView(viewManagerName);
      triedLoadingConfig.add(viewManagerName);
      if (result.viewConfig) {
        getConstants()[viewManagerName] = result.viewConfig;
        lazifyViewManagerConfig(viewManagerName);
      }
    }

    return viewManagerConfigs[viewManagerName];
  },
  • viewManagerConfigs初始化是一个空对象,key-value形式存储、管理这些原生视图配置
  • 我突然发现我错了路线,因为React-native虽然是用js写代码,不过最终都是转换成原生控件,回到主题的第一个代码底部
  return (requireNativeComponent<Props>(
    componentNameInUse,
  ): HostComponent<Props>);
  • 最最关键的是:requireNativeComponent,根据componentName去加载原生组件,找到源码
'use strict';

const createReactNativeComponentClass = require('../Renderer/shims/createReactNativeComponentClass');
const getNativeComponentAttributes = require('./getNativeComponentAttributes');
import type {HostComponent} from '../Renderer/shims/ReactNativeTypes';

const requireNativeComponent = <T>(uiViewClassName: string): HostComponent<T> =>
  ((createReactNativeComponentClass(uiViewClassName, () =>
    getNativeComponentAttributes(uiViewClassName),
  ): any): HostComponent<T>);

module.exports = requireNativeComponent;

?

最重要的加载原生组件的代码找到了

?

 ((createReactNativeComponentClass(uiViewClassName, () =>
    getNativeComponentAttributes(uiViewClassName),
  ): any): HostComponent<T>)

解析`createReactNativeComponentClass

  • 给createReactNativeComponentClass传入uiViewClassName即组件name,传入回调函数,返回getNativeComponentAttributes(uiViewClassName)
  • 找到源码createReactNativeComponentClass
/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * @flow strict-local
 */

'use strict';

import {ReactNativeViewConfigRegistry} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';

import type {ViewConfigGetter} from './ReactNativeTypes';

const {register} = ReactNativeViewConfigRegistry;

/**
 * Creates a renderable ReactNative host component.
 * Use this method for view configs that are loaded from UIManager.
 * Use createReactNativeComponentClass() for view configs defined within JavaScript.
 *
 * @param {string} config iOS View configuration.
 * @private
 */
const createReactNativeComponentClass = function(
  name: string,
  callback: ViewConfigGetter,
): string {
  return register(name, callback);
};

module.exports = createReactNativeComponentClass;
  • 跟我预想一样,向register函数传入name和cb,注册成功后触发callback(getNativeComponentAttributes)
  • 找到ReactNativePrivateInterface.js里面的ReactNativeViewConfigRegistry
  get ReactNativeViewConfigRegistry(): ReactNativeViewConfigRegistry {
    return require('../Renderer/shims/ReactNativeViewConfigRegistry');
  },
  • 再找到register方法
exports.register = function(name: string, callback: ViewConfigGetter): string {
  invariant(
    !viewConfigCallbacks.has(name),
    'Tried to register two views with the same name %s',
    name,
  );
  invariant(
    typeof callback === 'function',
    'View config getter callback for component `%s` must be a function (received `%s`)',
    name,
    callback === null ? 'null' : typeof callback,
  );
  viewConfigCallbacks.set(name, callback);
  return name;
};
  • 重点:viewConfigCallbacks.set(name, callback);viewConfigCallbacks是一个Map类型(ES6),key-value数据结构,怎么理解这段代码,看注释:
按名称注册本机视图/组件。
提供了一个回调函数来从UIManager加载视图配置。
回调被延迟直到视图被实际呈现。
  • 至此,加载原生组件逻辑配合之前的UImanager,getViewManagerConfig那块源码就解析完了。
  • 这是我们传入的cb(回调函数),获取原生组件属性
function getNativeComponentAttributes(uiViewClassName: string): any {
  const viewConfig = UIManager.getViewManagerConfig(uiViewClassName);

  invariant(
    viewConfig != null && viewConfig.NativeProps != null,
    'requireNativeComponent: "%s" was not found in the UIManager.',
    uiViewClassName,
  );

  // TODO: This seems like a whole lot of runtime initialization for every
  // native component that can be either avoided or simplified.
  let {baseModuleName, bubblingEventTypes, directEventTypes} = viewConfig;
  let nativeProps = viewConfig.NativeProps;
  while (baseModuleName) {
    const baseModule = UIManager.getViewManagerConfig(baseModuleName);
    if (!baseModule) {
      warning(false, 'Base module "%s" does not exist', baseModuleName);
      baseModuleName = null;
    } else {
      bubblingEventTypes = {
        ...baseModule.bubblingEventTypes,
        ...bubblingEventTypes,
      };
      directEventTypes = {
        ...baseModule.directEventTypes,
        ...directEventTypes,
      };
      nativeProps = {
        ...baseModule.NativeProps,
        ...nativeProps,
      };
      baseModuleName = baseModule.baseModuleName;
    }
  }

  const validAttributes = {};

  for (const key in nativeProps) {
    const typeName = nativeProps[key];
    const diff = getDifferForType(typeName);
    const process = getProcessorForType(typeName);

    validAttributes[key] =
      diff == null && process == null ? true : {diff, process};
  }

  // Unfortunately, the current setup declares style properties as top-level
  // props. This makes it so we allow style properties in the `style` prop.
  // TODO: Move style properties into a `style` prop and disallow them as
  // top-level props on the native side.
  validAttributes.style = ReactNativeStyleAttributes;

  Object.assign(viewConfig, {
    uiViewClassName,
    validAttributes,
    bubblingEventTypes,
    directEventTypes,
  });

  if (!hasAttachedDefaultEventTypes) {
    attachDefaultEventTypes(viewConfig);
    hasAttachedDefaultEventTypes = true;
  }

  return viewConfig;
}
  • 至此,一个完整的React-native组件解析从加载、注册、展现整个过程就解析完了。

推荐React 学习相关文章

常见的8个问题带你进阶 React

分析 React 组件的渲染性能「实践」

实践React Router v5:完整指南

前端必备的20种基本React工具「干货」

8个顶级React.js免费模板

推荐36种免费React模板和主题「干货」

「笔记」React Hooks 深入细品系列

这就是你日思夜想的 React 原生动态加载「值得收藏」

「干货满满」React Hooks 最佳实践

手把手教你如何实现一个React水印组件「实践」

「实践」React 中必会的 10 个概念

「干货」深入浅出React组件逻辑复用的那些事儿

手把手教你从Mixin深入到HOC再到Hook【React】

深入Facebook 官方React 状态管理器Recoil讲解

手把手教你实践搭建React组件库「超详细」

在 React 中自动复制文本到剪贴板「实践」

「干货满满」从零实现 react-redux

深入详解大佬用33行代码实现了React

让你的 React 组件性能跑得再快一点「实践」

React源码分析与实现(三):实践 DOM Diff

React源码分析与实现(一):组件的初始化与渲染「实践篇」

React源码分析与实现(二):状态、属性更新->setState「实践篇」

细说React 核心设计中的闪光点

手把手教你10个案例理解React hooks的渲染逻辑「实践」

React-Redux 100行代码简易版探究原理

手把手深入教你5个技巧编写更好的React代码【实践】

React 函数式组件性能优化知识点指南汇总

13个精选的React JS框架

深入浅出画图讲解React Diff原理【实践】

【React深入】React事件机制

Vue 3.0 Beta 和React 开发者分别杠上了

手把手深入Redux react-redux中间件设计及原理(上)【实践】

手把手深入Redux react-redux中间件设计及原理(下)【实践】

前端框架用vue还是react?清晰对比两者差异

为了学好 React Hooks, 我解析了 Vue Composition API

【React 高级进阶】探索 store 设计、从零实现 react-redux

写React Hooks前必读

深入浅出掌握React 与 React Native这两个框架

可靠React组件设计的7个准则之SRP

React Router v6 新特性及迁移指南

用React Hooks做一个搜索栏

你需要的 React + TypeScript 50 条规范和经验

手把手教你绕开React useEffect的陷阱

浅析 React / Vue 跨端渲染原理与实现

React 开发必须知道的 34 个技巧【近1W字】

三张图详细解说React组件的生命周期

手把手教你深入浅出实现Vue3 & React Hooks新UI Modal弹窗

手把手教你搭建一个React TS 项目模板

全平台(Vue/React/微信小程序)任意角度旋图片裁剪组件

40行代码把Vue3的响应式集成进React做状态管理

手把手教你深入浅出React 迷惑的问题点【完整版】

作者:Peter酱

转发链接:https://mp.weixin.qq.com/s/dLA6v4FoIqNDR8L_v_G6fg

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...