文章目录

  • 一、Root 表作用
  • 二、生成 Root 表
  • 三、完整注解处理器代码 及 生成的 Java 代码 ( 仅供参考 )
    • 1、注解处理器代码
    • 2、app 模块中的注解类生成的 Java 源码
    • 3、library2 模块中的注解类生成的 Java 源码
  • 四、博客资源

组件化系列博客 :

  • 【Android 组件化】从模块化到组件化
  • 【Android 组件化】使用 Gradle 实现组件化 ( Gradle 变量定义与使用 )
  • 【Android 组件化】使用 Gradle 实现组件化 ( 组件模式与集成模式切换 )
  • 【Android 组件化】使用 Gradle 实现组件化 ( 组件 / 集成模式下的 Library Module 开发 )
  • 【Android 组件化】路由组件 ( 路由组件结构 )
  • 【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )
  • 【Android 组件化】路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )
  • 【Android 组件化】路由组件 ( 注解处理器参数选项设置 )
  • 【Android 组件化】路由组件 ( 构造路由表中的路由信息 )
  • 【Android 组件化】路由组件 ( 使用 JavaPoet 生成路由表类 )
  • 【Android 组件化】路由组件 ( 组件间共享的服务 )

一、Root 表作用


注解处理器 为每个 Module 模块生成一个路由表 , 该模块下凡是被 @Route 标注的路由节点都在该路由表中维护 ;

package kim.hsl.router;import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.library2.StringService;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.router_annotation.model.RouteBean;public class Router_Group_library2 implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteBean> atlas) {atlas.put("library2", new RouteBean(RouteBean.Type.ISERVICE, StringService.class, "/library2/StringService", "library2"));}
}

模块名称是 app , 则路由表的名称是 Router_Group_app.java ;

一个模块中的路由表可能有多个 , 需要为若干路由表再生成一个 Root 表, 用于作为路由表的导航 ;

生成的 Root 表样式 : 其中 “app” 是组名 , Router_Group_app.class 是 app 组对应的路由表类 ;

package kim.hsl.router;import java.lang.Class;
import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.route_core.template.IRouteRoot;public class Router_Root_app implements IRouteRoot {@Overridepublic void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {routes.put("app", Router_Group_app.class);}
}

二、生成 Root 表


定义 IRouteRoot 接口 , 所有的 Root 类都实现该接口 , 该接口定义在 route-core 模块中 ;

package kim.hsl.route_core.template;import java.util.Map;public interface IRouteRoot {void loadInto(Map<String, Class<? extends IRouteGroup>> routes);
}

IRoot 接口定义位置 :

首先 , 获取 IRouteGroup 和 IRouteRoot 接口的节点 ;

// 获取 kim.hsl.route_core.template.IRouteGroup 类节点
TypeElement iRouteGroup = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteGroup");
// 获取 kim.hsl.route_core.template.IRouteRoot 类节点
TypeElement iRouteRoot = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteRoot");

其次 , 生成 loadInto 函数的参数 Map<String, Class<? extends IRouteGroup>> routes ;

// 生成参数类型名称
// Map<String,Class<? extends IRouteGroup>> routes>
ParameterizedTypeName routesTypeName = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ParameterizedTypeName.get(ClassName.get(Class.class),WildcardTypeName.subtypeOf(ClassName.get(iRouteGroup)))
);// 生成参数
// Map<String,Class<? extends IRouteGroup>> routes> routes
ParameterSpec rootParameterSpec = ParameterSpec.builder(routesTypeName, "routes").build();

再次 , 生成函数及函数体 ;

// 生成函数
// public void loadInfo(Map<String,Class<? extends IRouteGroup>> routes> routes)
MethodSpec.Builder loadIntoMethodBuilder = MethodSpec.methodBuilder("loadInto").addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(rootParameterSpec);// 生成函数体
for (Map.Entry<String, String> entry : mRootMap.entrySet()) {loadIntoMethodBuilder.addStatement("routes.put($S, $T.class)",entry.getKey(),ClassName.get("kim.hsl.router", entry.getValue()));
}

最后 , 生成 Router_Root_app 类 , 并写出到文件中 ;

// 生成 Root 类
String rootClassName = "Router_Root_" + mModuleName;// 创建 Java 类
TypeSpec typeSpec = TypeSpec.classBuilder(rootClassName).addSuperinterface(ClassName.get(iRouteRoot)).addModifiers(PUBLIC).addMethod(loadIntoMethodBuilder.build()).build();// 生成 Java 源文件
JavaFile javaFile = JavaFile.builder("kim.hsl.router", typeSpec).build();// 写出到文件中
try {javaFile.writeTo(mFiler);
} catch (IOException e) {e.printStackTrace();
}

三、完整注解处理器代码 及 生成的 Java 代码 ( 仅供参考 )


1、注解处理器代码

package kim.hsl.router_compiler;import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;import kim.hsl.router_annotation.Route;
import kim.hsl.router_annotation.model.RouteBean;import static javax.lang.model.element.Modifier.PUBLIC;// 注解处理器接收的参数
@SupportedOptions("moduleName")
// 自动注册注解处理器
@AutoService(Processor.class)
// 支持的注解类型
@SupportedAnnotationTypes({"kim.hsl.router_annotation.Route"})
// 支持的 Java 版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class RouterProcessor extends AbstractProcessor {/*** 注解处理器中使用 Messager 对象打印日志*/private Messager mMessager;/*** 用于写出生成的 Java 代码*/private Filer mFiler;/*** 注解节点工具*/private Elements mElementUtils;/*** 类工具*/private Types mTypeUtils;/*** 获取的 moduleName 参数*/private String mModuleName;/*** 管理路由信息* 键 ( Key ) : 路由分组名称* 值 ( Value ) : 路由信息集合*/private HashMap<String, ArrayList<RouteBean>> mGroupMap = new HashMap<>();/*** 管理 路由表信息* 键 ( Key ) : 组名* 值 ( Value ) : 类名*/private Map<String, String> mRootMap = new TreeMap<>();/*** 该函数在初始化时调用 , 相当于构造函数* @param processingEnvironment*/@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);// 获取打印日志接口this.mMessager = processingEnvironment.getMessager();// 测试日志打印mMessager.printMessage(Diagnostic.Kind.NOTE, "Messager Print Log");this.mFiler = processingEnvironment.getFiler();this.mElementUtils = processingEnvironment.getElementUtils();this.mTypeUtils = processingEnvironment.getTypeUtils();// 获取 moduleName 参数// 先获取 注解处理器 选项Map<String, String> options = processingEnvironment.getOptions();if (options != null){mModuleName = options.get("moduleName");mMessager.printMessage(Diagnostic.Kind.NOTE, "打印 moduleName 参数 : " + mModuleName);}}/*** 该函数在注解处理器注册时自动执行, 是处理注解的核心函数** Set<? extends TypeElement> set 参数 : 该集合表示使用了相关注解的节点的集合** @param set* @param roundEnvironment* @return*/@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {if (set == null || set.isEmpty()){// 如果没有检测到注解 , 直接退出return false;}// 获取被 @Route 注解的节点// 这些 注解节点 都是类节点 , TypeElement 类型的Set<? extends Element> routeElements = roundEnvironment.getElementsAnnotatedWith(Route.class);generateRouteClass(routeElements);// 生成 路由组件 分组表 对应的 Java 路由表 类generateGroupTable();// 生成 Root 路由表 , 组名 <-> 路由表类generateRootTable();return true;}/*** 生成 Root 表*/private void generateRootTable() {// 获取 kim.hsl.route_core.template.IRouteGroup 类节点TypeElement iRouteGroup = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteGroup");// 获取 kim.hsl.route_core.template.IRouteRoot 类节点TypeElement iRouteRoot = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteRoot");// 生成参数类型名称// Map<String,Class<? extends IRouteGroup>> routes>ParameterizedTypeName routesTypeName = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ParameterizedTypeName.get(ClassName.get(Class.class),WildcardTypeName.subtypeOf(ClassName.get(iRouteGroup))));// 生成参数// Map<String,Class<? extends IRouteGroup>> routes> routesParameterSpec rootParameterSpec = ParameterSpec.builder(routesTypeName, "routes").build();// 生成函数// public void loadInfo(Map<String,Class<? extends IRouteGroup>> routes> routes)MethodSpec.Builder loadIntoMethodBuilder = MethodSpec.methodBuilder("loadInto").addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(rootParameterSpec);// 生成函数体for (Map.Entry<String, String> entry : mRootMap.entrySet()) {loadIntoMethodBuilder.addStatement("routes.put($S, $T.class)",entry.getKey(),ClassName.get("kim.hsl.router", entry.getValue()));}// 生成 Root 类String rootClassName = "Router_Root_" + mModuleName;// 创建 Java 类TypeSpec typeSpec = TypeSpec.classBuilder(rootClassName).addSuperinterface(ClassName.get(iRouteRoot)).addModifiers(PUBLIC).addMethod(loadIntoMethodBuilder.build()).build();// 生成 Java 源文件JavaFile javaFile = JavaFile.builder("kim.hsl.router", typeSpec).build();// 写出到文件中try {javaFile.writeTo(mFiler);} catch (IOException e) {e.printStackTrace();}}/*** 生成 路由组件 分组表 对应的 Java 类*/private void generateGroupTable() {// 获取要生成的类 需要实现的接口节点TypeElement iRouteGroup = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteGroup");// 打印类节点全类名mMessager.printMessage(Diagnostic.Kind.NOTE,"打印 路由表 需要实现的接口节点 iRouteGroup : " + iRouteGroup.getQualifiedName());// 生成参数类型 Map<String, RouteBean> atlasParameterizedTypeName atlasType = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ClassName.get(RouteBean.class));// 生成参数 Map<String, RouteBean> atlasParameterSpec atlasValue = ParameterSpec.builder(atlasType, "atlas").build();// 遍历 HashMap<String, ArrayList<RouteBean>> mGroupMap = new HashMap<>() 路由分组// 为每个 路由分组 创建一个类for (Map.Entry<String, ArrayList<RouteBean>> entry : mGroupMap.entrySet()){// 创建函数 loadIntoMethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("loadInto").addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addParameter(atlasValue);// 函数体中的代码生成// 获取 ArrayList<RouteBean> 数据ArrayList<RouteBean> groupRoutes = entry.getValue();// 组名String groupName = "";// 生成函数体代码for (RouteBean routeBean : groupRoutes){// 获取组名groupName = routeBean.getRouteGroup();// $S 表示字符串// $T 表示类// $L 表示字面量 , 原封不动的字符串替换methodBuilder.addStatement("atlas.put($S, new $T($T.$L, $T.class, $S, $S))",// $S 字符串 : "main"routeBean.getRouteGroup(),// $T 类名 : RouteBeanClassName.get(RouteBean.class),// $T 类名 : TypeClassName.get(RouteBean.Type.class),// $L 字面量 : ACTIVITYrouteBean.getType(),// $T 类名 : kim.hsl.component.MainActivity 类ClassName.get((TypeElement) routeBean.getElement()),// $S 字符串 : "/app/MainActivity"routeBean.getRouteAddress(),// $S 字符串 : "app"routeBean.getRouteGroup());}// 创建类// 构造类名  Router_Group_mainString groupClassName = "Router_Group_" + groupName;// 创建类TypeSpec typeSpec = TypeSpec.classBuilder(groupClassName).addSuperinterface(ClassName.get(iRouteGroup)).addModifiers(PUBLIC).addMethod(methodBuilder.build()).build();// 生成 Java 源码文件JavaFile javaFile = JavaFile.builder("kim.hsl.router", typeSpec).build();// 将 Java 源文件写出到相应目录中try {mMessager.printMessage(Diagnostic.Kind.NOTE,"输出文件 : " + groupClassName);javaFile.writeTo(mFiler);} catch (IOException e) {e.printStackTrace();mMessager.printMessage(Diagnostic.Kind.NOTE,"输出文件出现异常");}finally {mMessager.printMessage(Diagnostic.Kind.NOTE,"输出文件完毕");}// 统计路由表信息mRootMap.put(groupName, groupClassName);}}private void generateRouteClass(Set<? extends Element> routeElements) {// 获取 android.app.Activity 类型的注解节点TypeElement activityElement = mElementUtils.getTypeElement("android.app.Activity");// 获取 组件间共享服务 的接口, 该接口仅用于表示组件类型TypeElement iServiceElement = mElementUtils.getTypeElement("kim.hsl.route_core.template.IService");// 处理 @Route(path = "app/MainActivity") 节点for (Element element : routeElements) {// 获取 Route 注解Route route = element.getAnnotation(Route.class);// 路由表中的单个路由对象RouteBean routeBean = null;// 判断 typeMirror 注解节点是否是 Activity 类型if (mTypeUtils.isSubtype(element.asType(), activityElement.asType())) {// 该节点是 android.app.Activity 类型的routeBean = new RouteBean(RouteBean.Type.ACTIVITY,    // 路由对象类型element,         // 路由节点null,    // 类对象route.path(),   // 路由地址route.group()); // 路由组}else if (mTypeUtils.isSubtype(element.asType(), iServiceElement.asType())) {// 该节点是 kim.hsl.route_core.template.IService 类型的routeBean = new RouteBean(RouteBean.Type.ISERVICE,    // 路由对象类型element,         // 路由节点null,    // 类对象route.path(),   // 路由地址route.group()); // 路由组}else{// 该节点不是 android.app.Activity 类型的throw new RuntimeException("@Route 注解节点类型错误");}// 检查路由地址checkRouteAddress(routeBean);// 打印路由信息mMessager.printMessage(Diagnostic.Kind.NOTE,"打印路由信息 : " + routeBean.toString());// 处理路由信息分组routeGroup(routeBean);}}/*** 处理路由信息分组* @param routeBean*/private void routeGroup(RouteBean routeBean) {// 首先从 groupMap 集合中获取该分组的所有 路由信息ArrayList<RouteBean> routeBeans = mGroupMap.get(routeBean.getRouteGroup());if (routeBeans == null){// 如果从 mGroupMap 获取的该分组的路由信息集合为空// 则创建新集合, 放置路由信息, 并加入到 mGroupMap 中routeBeans = new ArrayList<>();routeBeans.add(routeBean);mGroupMap.put(routeBean.getRouteGroup(), routeBeans);}else{// 从 mGroupMap 获取的路由分组对应的路由信息集合不为空// 直接添加 路由信息 即可routeBeans.add(routeBean);}}/*** 验证路由地址* @Route(path = "/app/MainActivity")* @param routeBean*/private void checkRouteAddress(RouteBean routeBean){// 获取路由地址String routeAddress = routeBean.getRouteAddress();// 获取路由分组String routeGroup = routeBean.getRouteGroup();// 验证路由地址是否以 "/" 开头if (!routeAddress.startsWith("/")) {throw new RuntimeException("路由地址 " + routeAddress + " 格式错误");}// 如果路由地址的分组为空 ,// 则截取第 0 和 第 1 个 "/" 之间的字符串作为分组名称if (routeGroup == null || "".equals(routeGroup)){String group = routeAddress.substring(routeAddress.indexOf("/", 0) + 1,routeAddress.indexOf("/", 1));if (group == null || "".equals(group)){throw new RuntimeException("路由地址 " + routeAddress + " 获取分组错误");}// 打印组名mMessager.printMessage(Diagnostic.Kind.NOTE,"打印路由地址 " + routeAddress + " 的组名为 " + group);// 正式设置路由地址分组routeBean.setRouteGroup(group);}}
}

2、app 模块中的注解类生成的 Java 源码

Module 模块中 , 使用注解生成的源码 , 都在对应模块的 " build\generated\ap_generated_sources\debug\out\ " 目录中 ;

app 中的注解类 :

@Route(path = "/app/MainActivity")
public class MainActivity extends Activity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}
}

生成的 路由表 源码 : 生成源码路径 D:\002_Project\002_Android_Learn\Component\app\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Group_app.java ;

package kim.hsl.router;import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.component.MainActivity;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.router_annotation.model.RouteBean;public class Router_Group_app implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteBean> atlas) {atlas.put("app", new RouteBean(RouteBean.Type.ACTIVITY, MainActivity.class, "/app/MainActivity", "app"));}
}

生成的 Root 表源码 : 生成源码路径 D:\002_Project\002_Android_Learn\Component\app\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Root_app.java ;

package kim.hsl.router;import java.lang.Class;
import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.route_core.template.IRouteRoot;public class Router_Root_app implements IRouteRoot {@Overridepublic void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {routes.put("app", Router_Group_app.class);}
}

3、library2 模块中的注解类生成的 Java 源码

Module 模块中 , 使用注解生成的源码 , 都在对应模块的 " build\generated\ap_generated_sources\debug\out\ " 目录中 ;

library2 中的注解类 :

package kim.hsl.library2;import android.util.Log;import kim.hsl.base.ComponentService;
import kim.hsl.router_annotation.Route;@Route(path = "/library2/StringService")
public class StringService implements ComponentService {@Overridepublic void doSomething() {Log.i("StringService", "library2 组件中的 StringService 服务 ");}
}

生成的 路由表 源码 : 生成源码路径 D:\002_Project\002_Android_Learn\Component\library2\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Group_library2.java ;

package kim.hsl.router;import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.library2.StringService;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.router_annotation.model.RouteBean;public class Router_Group_library2 implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteBean> atlas) {atlas.put("library2", new RouteBean(RouteBean.Type.ISERVICE, StringService.class, "/library2/StringService", "library2"));}
}

生成的 Root 表源码 : 生成源码路径 D:\002_Project\002_Android_Learn\Component\library2\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Root_library2.java ;

package kim.hsl.router;import java.lang.Class;
import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.route_core.template.IRouteRoot;public class Router_Root_library2 implements IRouteRoot {@Overridepublic void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {routes.put("library2", Router_Group_library2.class);}
}

四、博客资源


博客源码 :

  • GitHub : https://github.com/han1202012/Component
  • CSDN 下载 :

【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )相关推荐

  1. swift 组件化_打造完备的iOS组件化方案:如何面向接口进行模块解耦?

    作者 | 黑超熊猫zuik,一个修行中的 iOS 开发,喜欢搞点别人没搞过的东西,钻研过逆向工程.VIPER 架构和组件化. 关于组件化的探讨已经有不少了,在之前的文章 iOS VIPER架构实践(三 ...

  2. vue 如何处理两个组件异步问题_Vue异步组件处理路由组件加载状态的解决方案...

    vue.js 组件 组件(Component)是 Vue.js 最强大的功能之一. 组件可以扩展 HTML 元素,封装可重用的代码. 在大型单页面应用中,处于对性能的考虑和首屏加载速度的要求,我们一般 ...

  3. React - 一般组件 withRouter 的使用(让一般组件具备路由组件特有的API属性)

    React - 一般组件 withRouter 的使用(让一般组件具备路由组件特有的API属性) 一. withRouter 理解 二. withRouter 使用 一. withRouter 理解 ...

  4. android 组件化_Android 组件化路由框架设计(仿Arouter)

    前言 在组件化开发中一个必须要面对的问题就是组件间页面跳转,实现的方法有很多,简单的可以通过反射获取,但是比较耗费性能,也可以通过隐式跳转,但是随着页面的增多,过滤条件会随之增多,后期维护麻烦.那还有 ...

  5. [Android]如何做一个崩溃率少于千分之三噶应用app(22)-组件化路由跳转

    大家好,我是苍王.以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章. [Android]如何做一个崩溃率少于千分之三噶应用app--章节列表 大家应该有看完我的第十二编简书 ...

  6. iOS 组件化 —— 路由设计思路分析

    原文 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高.为了更好的应对各种需求,开发人员从软件工程的角度,将App架构由原来简单的MVC变成MVVM,VIPER等复杂架构.更换适合业 ...

  7. android两个项目依赖关系图,关于android:Android组件化项目搭建遇到的问题记录

    1. ARouter 的依赖问题 What went wrong: Execution failed for task ':app:kaptDebugKotlin'. A failure occurr ...

  8. Android组件化之组件通信

    Demo地址:https://github.com/751496032/ComponentDemo 本文是续上一篇Android组件化方案实践与思考文章一些思考,主要是针对组件间通信,比如: 每个组件 ...

  9. Android组件化专题 - 组件化配置

    demo地址 Android组件化专题,详细讲解组件化的使用及配置,以及实现的原理. 本文章讲解了组件化的由来及配置,下期讲解页面路由跳转及路由原理与apt 1. 组件化的由来 模块化.组件化和插件化 ...

最新文章

  1. Hibernate Criterion
  2. 攻防世界(Pwn) PWN100
  3. 下列哪个适合做链栈_很多朋友在问:多层实木生态板和颗粒板哪个更适合做衣柜呢?...
  4. labview连接mysql数据库_labview使用DSN与数据库的连接包括access,mysql
  5. Linux 命令之 mv -- 移动文件/重命名文件
  6. c语言入门经典案例,c语言入门经典案例及飞源代码.doc
  7. 教你两招搞定百度文库无需下载券
  8. 小凡虚拟机桥接到pc步骤实施
  9. 如何使用ARCHART XGantt表示无工作日的日历
  10. Matlab学习笔记(一)--数值数据
  11. 如何求子网掩码,默认网关地址,网络地址
  12. App内嵌H5活动页面携带用户token
  13. 高级程序员必会的程序设计原则 —— 墨菲定律及防呆设计
  14. SpringBoot整合——阿里云对象存储(OSS)
  15. 考研路上的那些一战二战三战成功与失败的故事系列之一
  16. c语言程序压缩解压缩文件夹,【转】使用VC++压缩解压缩文件夹
  17. 微信小程序解密失败的可能原因
  18. sql注入之联合注入
  19. 信息系统项目管理师-项目沟通管理
  20. 关于Android 12 适配,看这篇就够了

热门文章

  1. EDM营销之邮件投递篇
  2. PB初体验 class one
  3. Python3 中 爬网页 \uxxx 问题
  4. box_sizing
  5. [福大软工] W班 总成绩排行榜
  6. 201521123011《Java程序设计》第6周学习总结
  7. mybatis like的用法
  8. java 关于分页的实现
  9. 小F的2013应届校招历程小结
  10. 解决不同浏览器的css兼容问题