ARouter源码分析
源码看过好几遍了,但是总是会忘记,特此记录下
先从注解处理器开始
BaseProcessor是其他三个注解处理器的抽象类,子类去实现process方法。
在其中的init方法中会获取我们的module模块中.gradle文件填写的AROUTER_MODULE_NAME对应的value。如果处理过的moduleName的值是空会抛出异常
if (StringUtils.isNotEmpty(moduleName)) {
moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", ""); logger.info("The user has configuration the module name, it was [" + moduleName + "]");
} else {
logger.error(NO_MODULE_NAME_TIPS);
throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
}
然后就是初始化一些工具
mFiler = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
typeUtils = new TypeUtils(types, elementUtils);
logger = new Logger(processingEnv.getMessager());
RouteProcessor用来处理Route这个注解
//其实这个key是我们设置的group组名,缺省情况下是path的第一个单词,并不是每个module下的路由集合,
//例如:一个module下我们的2个Route值分别设置了group="group1"和 group="group2",那么这个Map中就会有2个值分别存储
//group1分组下的路由集合和group2下面的路由集合,也会为我们生成2个类设置。后面代码会解析
private Map<String, Set<RouteMeta>> groupMap = new HashMap<>(); // ModuleName and routeMeta. 这个官方的注释有问题
//这个其实是一个索引表,key是组名,value是上面注释中的生成的类名,也就是通过这个key就能找到上面的路由集合。这个是每一个module对应一个类。
private Map<String, String> rootMap = new TreeMap<>();
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
//扫描项目拿到所有带Route注解的元素
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
try {
logger.info(">>> Found routes, start... <<<");
this.parseRoutes(routeElements); } catch (Exception e) {
logger.error(e);
}
return true;
} return false;
}
解析上面代码中的parseRoutes(Set<? extends Element> routeElements)方法
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Interface of ARouter
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
ClassName routeTypeCn = ClassName.get(RouteType.class);
获取类元素对应的Type类型 关于type和Element参考:https://www.jianshu.com/p/899063e8452e
/*
Build input type, format as :
```Map<String, Class<? extends IRouteGroup>>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*
```Map<String, RouteMeta>```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
利用javaport创建Map<String, Class<? extends IRouteGroup>> 和 Map<String, RouteMeta>方法这个参数类型
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build();
到此就创建了
Map<String, Class<? extends IRouteGroup>> routes 和 Map<String, RouteMeta> atlas 以及 Map<String, RouteMeta> providers完整参数
/*
Build method : 'loadInto'
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
创建方法:loadIntoMethodOfRootBuilder <--> loadInto(Map<String, Class<? extends IRouteGroup>> routes) 后面创建ARouter$$Root$$moduleName类使用
for (Element element : routeElements) {
TypeMirror tm = element.asType();
Route route = element.getAnnotation(Route.class);
RouteMeta routeMeta; // Activity or Fragment
if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
// Get all fields annotation by @Autowired
//带Autowirde注解属性的名称和类型(name,String类型)(sex,boolean属性)后面的Integer是枚举类型
Map<String, Integer> paramsType = new HashMap<>();
Map<String, Autowired> injectConfig = new HashMap<>();
//这个用来处理子级中带Autowirde注解
injectParamCollector(element, paramsType, injectConfig); if (types.isSubtype(tm, type_Activity)) {
// Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
} else {
// Fragment
logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
}
//Activity或者fragment中的带Autowird的属性也会放进路由中
routeMeta.setInjectConfig(injectConfig);
} else if (types.isSubtype(tm, iProvider)) { // IProvider
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) { // Service
logger.info(">>> Found service route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
} else {
throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
}
//验证path的准确性和填充路由表groupMap
categories(routeMeta);
}
private void categories(RouteMeta routeMete) {
//验证通过后
if (routeVerify(routeMete)) {
logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());
if (CollectionUtils.isEmpty(routeMetas)) {
Set<RouteMeta> routeMetaSet = new TreeSet<>(new Comparator<RouteMeta>() {
@Override
public int compare(RouteMeta r1, RouteMeta r2) {
try {
return r1.getPath().compareTo(r2.getPath());
} catch (NullPointerException npe) {
logger.error(npe.getMessage());
return 0;
}
}
});
//填充路由表
routeMetaSet.add(routeMete);
//把路由表和相应的group对应
groupMap.put(routeMete.getGroup(), routeMetaSet);
} else {
//如果已经存在组了,就直接添加到路由表中
routeMetas.add(routeMete);
}
} else {
logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");
}
}
//验证path的准确性
private boolean routeVerify(RouteMeta meta) {
String path = meta.getPath();
//如果path是空或者path没有以"/"开始验证失败
if (StringUtils.isEmpty(path) || !path.startsWith("/")) { // The path must be start with '/' and not empty!
return false;
}
//group缺省情况用path中的第一个单词作为group值
if (StringUtils.isEmpty(meta.getGroup())) { // Use default group(the first word in path)
try {
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (StringUtils.isEmpty(defaultGroup)) {
return false;
}
//为路由信息设置组名
meta.setGroup(defaultGroup);
return true;
} catch (Exception e) {
logger.error("Failed to extract default group! " + e.getMessage());
return false;
}
}
return true;
}
经过上面的方法之后groupMap就被赋值成功了,下面就是类的生成了。
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
创建 loadIntoMethodOfProviderBuilder <--->loadInto(Map<String, Class<? extends IRouteGroup>> providers )后面创建ARouter$$Providers$$moduleName类使用
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey(); MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec); List<RouteDoc> routeDocList = new ArrayList<>(); // Build group method body
Set<RouteMeta> groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
RouteDoc routeDoc = extractDocInfo(routeMeta); ClassName className = ClassName.get((TypeElement) routeMeta.getRawType()); switch (routeMeta.getType()) {
case PROVIDER: // Need cache provider's super class
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
routeDoc.addPrototype(tm.toString()); if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself.
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
}
}
break;
default:
break;
} // Make map body for paramsType
StringBuilder mapBodyBuilder = new StringBuilder();
Map<String, Integer> paramsType = routeMeta.getParamsType();
Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig();
if (MapUtils.isNotEmpty(paramsType)) {
List<RouteDoc.Param> paramList = new ArrayList<>(); for (Map.Entry<String, Integer> types : paramsType.entrySet()) {
mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); "); RouteDoc.Param param = new RouteDoc.Param();
Autowired injectConfig = injectConfigs.get(types.getKey());
param.setKey(types.getKey());
param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());
param.setDescription(injectConfig.desc());
param.setRequired(injectConfig.required()); paramList.add(param);
} routeDoc.setParams(paramList);
}
String mapBody = mapBodyBuilder.toString(); loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase()); routeDoc.setClassName(className.toString());
routeDocList.add(routeDoc);
} // 有几个组就生成了几个类ARouter$$Group$$groupName
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler); logger.info(">>> Generated group: " + groupName + "<<<");
//填充rootMap key是组名 value是上面生成的类名
rootMap.put(groupName, groupFileName);
docSource.put(groupName, routeDocList);
}
经过这个遍历系统为我们生成了
public class ARouter$$Group$$组名 implement IRooteGroup{
public void loadInto(Map<String,RouteMata> atlas){
atlas.put("/order/OrderActivity", RouteMeta 对象)
atlas.put("/order/OrderDetailActivity", RouteMeta 对象)
当外部调用这个方法的时候就会得到整个路由信息了
}
}
if (MapUtils.isNotEmpty(rootMap)) {
// Generate root meta by group name, it must be generated before root, then I can find out the class of group.
for (Map.Entry<String, String> entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
// Output route doc
if (generateDoc) {
docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
docWriter.flush();
docWriter.close();
}
// 创建ARouter$$Providers$$模块名这个类 loadInto 方法中的Map key是整个类名 值是RouteMeta
String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(providerMapFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IProviderGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfProviderBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated provider map, name is " + providerMapFileName + " <<<");
// 创建Arouter$$Route$$模块名 key是组名 值是 Arouter$$Group$$组名 对应的class
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
经过上面的操作就生成了
public class ARouter$$Providers$$modulejava implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
}
}
public class ARouter$$Root$$modulejava implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("m2", ARouter$$Group$$m2.class);
routes.put("module", ARouter$$Group$$module.class);
routes.put("test", ARouter$$Group$$test.class);
routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
}
}
至此我们也能发现ARouter$$Root$$模块名 和 ARouter$$Providers$$模块名 是有几个模块就会创建几个类,但是ARouter$$Group$$组名则是有几个组就创建几个类
并且ARouter$$Group$$组名这个类里面也会存储provider路由信息,ARouter$$Providers$$模块名也会存储provider信息。也就是说我们既可以通过path查找provider信息
也可以通过全类名查找provider信息。
至此 Route这个注解的功能就分析完毕,等待应用程序注册的时候调用我们上面创建类的方法,把数据存储到仓库中即可
Autowired这个注解比较简单就是利用反射调用我们的getIntent.get方法去赋值。
Interceptor这个注解用来帮助我们生成ARouter$$Interceptors$$modulejava这样的类
public class ARouter$$Interceptors$$modulejava implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
//key 是优先级 值是我们添加的拦截器的class
interceptors.put(7, Test1Interceptor.class);
interceptors.put(90, TestInterceptor90.class);
}
}
当我们调用ARouter.init(application)的时候
1.启动线程池扫描包下面的class对象得到类的集合 有缓存不用每次都扫描
2.解析类集合如果是以ARouter$$Root开头的类利用class.forName获取IRouterRoot对象,调用loadInto方法把里面的Map赋值给数据仓库中的Map
填充:WareHouse中的Map<String,class<? extends IRoteGroup>> groupsIndex
3.以ARoute$$Interceptors开头就调用IInterceptors的loadInto方法填充WareHouse中的Map<Integer,class<? extends IRoteGroup>>interceptorsIndex
4.以ARoute$$Providers开头的就调用IProvidersGroup的loadInto方法填充WareHouse中的Map<String,RouteMeta>providersIndex
这个时候肯定会有疑问:我们生成的ARouter$$Group$$组名中的loadInto方法怎么没有调用?
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
初始化完成后调用了下面的这个方法
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
//给我们暴露了一个可以预处理事情的PathReplaceService这个IProvider接口,如果我们实现了这个接口就可以提前做一些事情主要是路径替换--一个hook点吧
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path), true);
}
}
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService 这个系统的拦截器里面利用线程池处理我们自己的拦截器
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//这个同样是一个预处理点如果我们设置了这个provider,就可以截获并取消这个跳转
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
} // Set context to postcard.
postcard.setContext(null == context ? mContext : context); try {
//这个里面完成了对postcard信息的完善:路径class 是否是绿色通道等,数据仓库里面剩余map的填充
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage()); if (debuggable()) {
// Show friendly tips for user.
runInMainThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
//异常发生后会回调我们的丢失方法
if (null != callback) {
callback.onLost(postcard);
} else {
// No callback for this invoke, then we use the global degrade service.
//如果没有设置回调还可以通过实现下面的接口来实现回调,这个是全局的
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
} return null;
} if (null != callback) {
callback.onFound(postcard);
}
//只有非绿色通道才可以进行拦截处理
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
//这个里面是开启线程池按照优先级调用我们的拦截器
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
} /**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
} logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
//看下真正的路由实现
return _navigation(postcard, requestCode, callback);
} return null;
}
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext(); switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras()); // Set flags.
int flags = postcard.getFlags();
if (0 != flags) {
intent.setFlags(flags);
} // Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} // Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
} // Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
}); break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class<?> fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
} return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
} return null;
}
ARouter源码分析的更多相关文章
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
- ABP源码分析二:ABP中配置的注册和初始化
一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
- ABP源码分析四:Configuration
核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...
随机推荐
- getElementsByClassName()获取不到值
在这种方式下,虽然使用了getElementsByClassName方法,但是并不能获得到值.从执行顺序上来说,在HTML还没有执行的时候JS就已经开始执行了,所以获得的值不能够获得到.因此,如果遇到 ...
- Java (新)将Excel数据读取到ListMap
Java (新)将Excel数据读取到ListMap Maven依赖: pom.xml <!-- excel --> <dependency> <groupId>o ...
- kafka 学习
https://kafka.apache.org/quickstart C:\W_O_R_K\kafka_2.12-2.2.0\kafka_2.12-2.2.0\bin\windows\zookeep ...
- C# DataGridView 新增列 新增行 操作函数 - [ 自律相互分享,共促一起进步 - 社会的正常运维就这么简单,何以权,何以钱...- 张光荣2010年谈社会改正提出的正能量]
功能: 一.列相关: 1.追加列,左插列,右插列, 2.删除列 二.行相关: 1.追加行,上插行,下插行 2.删除行,删除所有空行,清空所有数据... 原理:根据对鼠标于 DataGridView 点 ...
- Matlab %陆
第六章 MATLAB IN ENGINEERING Polynomial Differentiation多项式微分 %幂级数 f(x) = x^3-2x-5; p = [1 0 -2 -5] %自 ...
- pip install keras==2.0.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install keras==2.0.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
- centos虚拟机yum update报错Another app is currently holding the yum lock; waiting for it to exit...
1.运行yum update报错 [root@localhost ~]# yum update已加载插件:fastestmirror, langpacks/var/run/yum.pid 已被锁定,P ...
- ELK集群基础环境初始化
集群基础环境初始化 1.准备虚拟机 192.168.1.7 192.168.1.6192.168.1.183 2.切换为国内centos源 3.修改sshd服务优化 [root@elk01 ~]# s ...
- 模拟多路开关mux的数据耦合问题
1.前言 最近在做有关sensor的项目时遇到了一个关于多路选择器引起的数据耦合问题,具体的问题现象和解决方案如下: 2.多路开关的介绍 2.1 概述 多路开关:在多路被测试的信号公用一路A/D转换器 ...
- vite不能用@做为路径的解决方法
vite创建vue3后,发现原来用@做为路径的不能用了,报错信息是 Internal server error: Failed to resolve import "@ 在网上查了一下资料, ...