前面主要描述碰到的问题,文章后面会给出我的proguard-maven-plugin插件的完整配置

proguard-maven-plugin 插件版本 2.3.1,proguard-base版本7.0.0

  1. 1.混淆后自己全部代码没有被放入混淆后的jar包里(jar\BOOT-INF\classes 里面不包含com)

原因: proguard-maven-plugin插件放到了spring-boot-maven-plugin插件后面,应该是因为spring-boot-maven-plugin放在前面会先执行spring-boot-maven-plugin的repackage再执行proguard-maven-plugin的混淆,混淆后没有重新repackage

解决:把proguard-maven-plugin插件放到spring-boot-maven-plugin前面

2.DataSource,Aop,swagger等相关配置类混淆后导致的运行报错

原因:比如Aop是因为用@Around(value = "apiLog()")指定apiLog方法对应的@Pointcut作为切入点,但是因为apiLog方法被混淆成a导致找不到对应apiLog方法。就不一一列举了

解决:我是直接把所有配置类放到framework下面,然后proguard-maven-plugin配置让framework 下面的类和方法全部不混淆,这样省事点

-keep class com.zzz.framework.** {*;
}

3.aop事务控制,事务失效问题

原因:因为aop pointcut 是根据包路径和方法名(update,find)拦截,service 混淆后 包路径和方法名都变成 a,b导致拦截不到

解决:不混淆包路径和方法public方法名,如下

#不混淆service下的public方法名
-keepclassmembers class com.zzz.api.*.service.** {public *** *(...);}
#不混淆service包名
-keeppackagenames com.zzz.api.*.service.**

4.运行项目beanName冲突问题

原因:混淆后的类名都是a,b,c,默认注册到ioc中是以类名作为BeanName,BeanName不能重复。

解决:重新定义beanName的生成策略,如下

public class Application {public static void main(String[] args) {new SpringApplicationBuilder(Application.class).beanNameGenerator(new ProGuardBeanNameGenerator()).run(args);}
}/*** 由于需要混淆代码,混淆后类都是A B C,spring 默认是把A B C当成BeanName,BeanName又不能重复导致报错* 所以需要重新定义BeanName生成策略* 不能重写generateBeanName方法,因为有些Bean会自定义BeanName,所以这些情况还需要走原来的逻辑**/
public class ProGuardBeanNameGenerator extends AnnotationBeanNameGenerator {/*** 重写buildDefaultBeanName* 其他情况(如自定义BeanName)还是按原来的生成策略,只修改默认(非其他情况)生成的BeanName带上 包名** @param definition* @return*/@Overridepublic String buildDefaultBeanName(BeanDefinition definition) {return definition.getBeanClassName();}
}

5.swagger 文档看不到api,以及接口请求参数controller 参数绑定报错

原因:

1)swagger 文档看不到api:因为我swagger配置的api 是通过.apis(RequestHandlerSelectors.basePackage(packageName))指定了具体包名,controller混淆后包名变成a,b,c所以扫描不到接口自然就生成不了文档

2)api接口请求出错:spring接口参数绑定是根据参数名称,混淆后参数名称会变成var1等,绑定的时候找不到原来定义的参数名就报错了.

解决:proguard-maven-plugin配置所有controller public 方法不参与混淆以及参数也不参与混淆,如下


#所有controller类的public方法你参与混淆
<option>-keepclassmembers class com.zzz.api.*.controller.* {public *** *(...);}</option>
#不混淆controller的包路径
<option>-keeppackagenames com.zzz.api.*.controller</option>#参数不参与混淆
<option>-keepparameternames</option>

6.spring controller 参数绑定问题

原因:spring参数绑定有些是根据controller的方法参数名称绑定的,参数名称混淆后就会绑定不上

解决:

方法1:Controller用如@RequestParam的方式绑定

方法2:

①.配置-keepparameternames,这个参数可以让不被混淆的方法也不会混淆其参数,对interface无效.

<option>-keepparameternames</option>

②. 因为第①步的配置,那只要配置controller里的方法不混淆,那么方法参数也就不会混淆了,如下

-keepclassmembers class com.zzz.api.*.controller.* {public *** *(...);}

7.mybatis 参数绑定报错org.apache.ibatis.binding.BindingException

原因:和第6点类似,因为方法参数被混淆,所以导致mybatis的mapper(是interface)定义的接口参数Id被混淆成 var1,mybatis xml #{id}绑定的时候只能找到var1于是BindingException.但是不能用第6点的方法2,因为keepparameternames 能让keep的class参数不混淆,却不能让interface的参数不参与混淆,而mybatis的mapper就是interface

解决:

方法1:mapper的参数 用 @Param("id") 强行绑定
方法2:此方法也可以解决第6点的问题

①. 配置 -keepattributes MethodParameters ,这个参数可以让不管有没有参与混淆的类的方法参数都不参与混淆,对类和接口都生效,但是这样会导致即使参与混淆的类也无法混淆参数

-keepattributes MethodParameters

②. 上面的配置是可以让所有方法参数不被proguard混淆,但是有些项目在java编译的时候却可能会对方法参数混淆(好像是针对interface方法参数的混淆,不针对class).所以可以配置编译时不混淆方法参数(-parameters),如下所示(这个根据自己项目实际情况配置,好像是和idea版本有关系)

   <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><compilerArgument>-parameters</compilerArgument></configuration></plugin>

8. CreateProcess error=206, 文件名或扩展名太长。

原因:因为window的cmd有长度限制,而proguard混淆时依赖太多的jar包导致命令行过长

解决:在proguard-maven-plugin的configuration中加入下面配置,这个配置会把jar包放到临时目录以便缩短命令行

<putLibraryJarsInTempDir>true</putLibraryJarsInTempDir>

9.其他模块依赖混淆(推荐B方案)

① A方案(此处其他模块不混淆非私有的方法名和类名和变量名,只混淆方法里的代码及私有方法)

这里是其他模块的混淆配置

如果有zzz-web和zzz-common两个模块,zzz-web是springboot项目,zzz-common包含一些公用的代码,也包含有@Component相关类,zzz-web依赖zzz-common,

这种情况我对zzz-common的混淆是不混淆public的方法名和类名,只混淆方法里面的代码,否则假设zzz-common 有 StringUtils.isNotBlank方法,混淆后变成a.b,那么在zzz-web里面调用 StringUtils.isNotBlank要用a.b

配置方法:zzz-common pom.xml配置proguard-maven-plugin,且option包含如下配置

#混淆方法里面的代码,不混淆非私有的方法名和类名和变量名,否则被其他模块依赖时调用的类和方法名都需要用a,b,c
-keep class ** {!private *** *;!private *** *(...);
}

问题:由于proguard混淆zzz-common后生成的jar包有问题,导致zzz-common 里面注解@Component的类无法被springboot扫描注册到BeanFactory,所以如果用@Autowired注入这些类会导致启动的时候not found这些bean。

解决:

方法一:配置让zzz-web打包时依赖zzz-common不是以jar的方式,而是把zzz-common的代码放到zzz-web的jar包里面,配置如下:

1).在zzz-web的pom.xml里面给proguard-maven-plugin的configuration增加assembly配置

 <plugin><groupId>com.github.wvengen</groupId><artifactId>proguard-maven-plugin</artifactId><configuration><!-- 把依赖的项目其他模块(com.zzz)的class放入当前生成jar包
(由于对其他模块进行混淆生成的jar包格式不对,导致用jar的形式放入 当前项目jar的lib里运行会扫描不到其他模块的相关bean,所以配置把其他模块相关的jar的class放入当前jar包里可以避免这样的问题,另一种解决方案是其他依赖包在用proguard混淆成jar包后对该jar包重新打包) --><assembly><inclusions><inclusion><library>true</library><!-- groupId是zzz-common模块的groupId --><groupId>com.zzz</groupId><artifactId>*</artifactId></inclusion></inclusions></assembly></configuration></plugin>

2).在zzz-web的pom.xml里给spring-boot-maven-plugin增加excludeGroupIds排除 zzz-common的groupId让zzz-common的jar包不会放入zzz-web的lib里

<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><!-- 如果没有该配置,devtools不会生效 --><fork>true</fork><!--start:混淆配置 由于配置混淆插件,会把其他模块的依赖包混淆后直接放入 springboot最后生成的jar包,所以就不需要把其他模块的jar包放到lib里了--><excludeGroupIds>com.zzz</excludeGroupIds><!-- end:混淆配置 --></configuration></plugin>

方法二:修改proguard-maven-plugin插件的源码,让proguard-maven在用proguard混淆zzz-common之后生成 zzz-common.jar后对zzz-common.jar重新打包.这样生成的jar包就可以让springboot使用.

修改方法:把proguard-maven-plugin:2.3.1 ProGuardMojo.java 第700行 “if ((assembly != null) && (hasInclusionLibrary)) { 给注释掉即可。结尾的 “}”也要注释

我修改源码后的资源下载链接

这个if里面的代码可以对proguard生成的jar重新打包,但是需要配置方法1中的assembly配置才会触发,注释掉让他默认都会对proguard生成的jar重新打包就可以解决proguard的jar不能用的问题

方法三:自己写个maven插件重新打包或者网上找个能用的插件(至少我没找到)

② B方案

在混淆前 先用maven-assembly-plugin插件把 zzz-common和zzz-web 合并打包成一个jar包,之后再用proguard-maven-plugin对这个jar包进行混淆,然后用spring-boot-maven-plugin对混淆后的jar repackage;因为是先把zzz-common和zzz-web合并成一个jar后再进行混淆,所以就不存在上面说的不能混淆方法等问题。

注意:pom.xml的插件顺序要按maven-assembly-plugin->proguard-maven-plugin->spring-boot-maven-plugin,否则上面的执行顺序不一样可能导致最后生成的jar有问题。

配置如下:

1)在zzz-web的pom.xml增加maven-assembly-plugin插件

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>3.3.0</version><configuration><!-- 打包生成的包不拼接xml里的id 为后缀 --><appendAssemblyId>false</appendAssemblyId><!--打包文件路径--><descriptors><descriptor>${project.basedir}/assembly.xml</descriptor></descriptors></configuration><executions><execution><id>assembly-package</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin>

assembly.xml

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"><!--项目标识,设置的话,生成后的zip文件会加上此后缀--><id>${project.version}</id><!--打包格式--><formats><format>jar</format></formats><!--压缩包下是否生成和项目名相同的根目录--><includeBaseDirectory>false</includeBaseDirectory><fileSets><fileSet><directory>${project.build.directory}/classes</directory><outputDirectory>/</outputDirectory></fileSet></fileSets><dependencySets><!-- 把依赖的项目其他模块代码放到jar包里,方便对其他模块代码 --><dependencySet><outputDirectory>/</outputDirectory><includes><include>com.zzz:*</include></includes><unpack>true</unpack></dependencySet></dependencySets></assembly>

2).在zzz-web的pom.xml里给spring-boot-maven-plugin增加excludeGroupIds排除 zzz-common的groupId让zzz-common的jar包不会放入zzz-web的lib里

<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><!-- 如果没有该配置,devtools不会生效 --><fork>true</fork><!--start:混淆配置 由于配置混淆插件,会把其他模块的依赖包混淆后直接放入 springboot最后生成的jar包,所以就不需要把其他模块的jar包放到lib里了--><excludeGroupIds>com.zzz</excludeGroupIds><!-- end:混淆配置 --></configuration></plugin>

 


完整配置

我的proguard-maven-plugin插件的相关配置

1.pom.xml

                <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>3.3.0</version><configuration><!-- 打包生成的包不拼接xml里的id 为后缀 --><appendAssemblyId>false</appendAssemblyId><!--打包文件路径--><descriptors><descriptor>${project.basedir}/assembly.xml</descriptor></descriptors></configuration><executions><execution><id>assembly-package</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin><!-- 代码混淆 --><plugin><groupId>com.github.wvengen</groupId><artifactId>proguard-maven-plugin</artifactId><version>2.3.1</version><executions><execution><!-- 打包的时候开始混淆--><phase>package</phase><goals><goal>proguard</goal></goals></execution></executions><configuration><injar>${project.build.finalName}.jar</injar><!--输出的jar--><outjar>${project.build.finalName}.jar</outjar><!-- 是否混淆--><obfuscate>true</obfuscate><proguardInclude>${basedir}/proguard.cfg</proguardInclude><injarNotExistsSkip>true</injarNotExistsSkip><!-- 把依赖的项目其他模块的class放入当前打的jar包(proguard由于对其他模块进行混淆生成的jar包格式不对,导致用jar的形式放入 lib 运行会扫描不到相关bean,所以配置把相关jar的class放入当前jar包可以避免这样的问题,另一种解决方案是其他依赖包在用proguard混淆成jar包后对该jar包重新打包) --><assembly><inclusions><inclusion><library>true</library><!-- 对应模块的groupId --><groupId>com.zzz</groupId><artifactId>*</artifactId></inclusion></inclusions></assembly><options><!--指定java版本号--><option>-target 1.8</option><!--不做收缩(删除注释、未被引用代码)--><option>-dontshrink</option><!--默认是开启的,这里关闭字节码级别的优化--><option>-dontoptimize</option><!--混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代--><option>-adaptclassstrings</option><!--混淆时不生成大小写混合的类名,默认是可以大小写混合--><option>-dontusemixedcaseclassnames</option><!--                            指定将相同的混淆名称分配给具有相同名称的类成员,并将不同混淆名称分配给名称不同的类成员,(我猜)假设两个类有相同的类成员(如方法名),则混淆后名称一样--><!--                            <option>-useuniqueclassmembernames</option>--><!-- 忽略warn消息--><option>-ignorewarnings</option><!--对异常、注解信息在runtime予以保留,不然影响springboot启动--><option>-keepattributesExceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option><!--不混淆所有interface接口--><!--                        <option>-keepnames interface **</option>--><!--                            <option>-keep interface * extends * { *; }</option>--><!--保留枚举成员及方法--><option>-keepclassmembers enum * { *; }</option><!-- 不参与混淆的类参数也不混淆,controller如果参数也混淆会导致传参映射不上 --><option>-keepparameternames</option><!--不混淆带有main 方法的类 网上的配置我没用--><!--                            <option>-keepclasseswithmembers public class * {--><!--                                public static void main(java.lang.String[]);}--><!--                            </option>--><!--百度的配置,注释掉没出问题,忽略note消息,如果提示javax.annotation有问题,那麽就加入以下代码--><!--                            <option>-dontnote javax.annotation.**</option>--><!--                            <option>-dontnote sun.applet.**</option>--><!--                            <option>-dontnote sun.tools.jar.**</option>--><!--                            <option>-dontnote org.apache.commons.logging.**</option>--><!--                            <option>-dontnote javax.inject.**</option>--><!--                            <option>-dontnote org.aopalliance.intercept.**</option>--><!--                            <option>-dontnote org.aopalliance.aop.**</option>--><!--                            <option>-dontnote org.apache.logging.log4j.**</option>--><!--                            <option>-keep class org.apache.logging.log4j.util.* { *; }</option>--><!--                            <option>-dontwarn org.apache.logging.log4j.util.**</option>--><!--不混淆所有类,保存原始定义的注释,网上找到的配置,我注释掉没出现任何问题--><!--                            <option>-keepclassmembers class * {--><!--                                @org.springframework.beans.factory.annotation.Autowired *;--><!--                                @org.springframework.beans.factory.annotation.Value *;--><!--                                @org.springframework.web.bind.annotation.PostMapping *;--><!--                                @org.springframework.web.bind.annotation.DeleteMapping *;--><!--                                @org.springframework.stereotype.Repository *;--><!--                                @com.shark.example.configuration.log *;--><!--                                }--><!--                            </option>--></options><libs><!-- 添加依赖 java8--><lib>${java.home}/lib/rt.jar</lib><lib>${java.home}/lib/jce.jar</lib></libs></configuration><dependencies><!-- https://mvnrepository.com/artifact/net.sf.proguard/proguard-base --><dependency><groupId>com.guardsquare</groupId><artifactId>proguard-base</artifactId><version>7.0.0</version></dependency></dependencies></plugin><!-- spring-boot-maven-plugin一定要放在proguard-maven-plugin后面,否则会导致最后jar包不包含项目代码 --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><!-- 如果没有该配置,devtools不会生效 --><fork>true</fork> <!--start:混淆配置 由于配置混淆插件,会把其他模块的依赖包混淆后直接放入 springboot最后生成的jar包,所以就不需要把其他模块的jar包放到lib里了--><excludeGroupIds>com.zzz</excludeGroupIds><!-- end:混淆配置 --></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><!-- 配置编译时不混淆方法名,需要时在配置 --><!-- <compilerArgument>-parameters</compilerArgument> --></configuration></plugin>

2.proguard.cfg(和pom.xml同目录)

#所有类(包括接口)的方法参数不混淆(包括没被keep的) 如果参数混淆了 mybatis mapper 参数绑定会出错(如#{id})
-keepattributes MethodParameters#入口程序类不能混淆,混淆会导致springboot启动不了
-keep class com.zzz.Application {*;}#由于spring会根据参数名称绑定参数,如果参数名称被混淆了,参数绑定是会报错,所以不混淆controller的所有的public方法(也可以不混淆类名)
#因为配置了'keepparameternames'所以不混淆方法时参数也不会混淆
-keepclassmembers class com.zzz.api.*.controller.* {public *** *(...);}
#但是因为swagger配置了根据包路径进行扫描,所以如果混淆了会导致swagger扫描不到混淆后的包,所以不混淆controller的包路径
-keeppackagenames com.zzz.api.*.controller#实体类不混淆不然会导致mybatis xml 配置的实体类找不到
#如果是spring jpa 用到@Query 也会导致找不到相关类
-keep class com.zzz.api.*.entity.** {*;}
-keep class com.zzz.api.*.dto.** {*;}
-keep class com.zzz.api.*.vo.** {*;}#mybatis mapper不混淆 否则会导致xml 配置的mapper找不到
-keep class com.zzz.api.*.mapper.** {*;
}#不混淆spring jpa的 repository,否则就无法根据方法名查询了
-keep class com.dataexa.fzty.deduction.repository.** {*;
}#保留service的所有公共方法名,由于使用AOP控制事务,根据拦截get,update等方法进行事务控制,所以需要不混淆service下的public方法名
-keepclassmembers class com.zzz.api.*.service.** {public *** *(...);}
#不混淆service包名,由于使用AOP(POINTCUT=serivce包名包名)控制事务,所以需要保留service的包名防止找不到service
-keeppackagenames com.zzz.api.*.service.**#frannework下都是一些配置类比如datasource,aopconfig,swaggerconfig如果混淆会导致各种启动报错,
#比如用@Around(value = "apiLog()")指定apiLog方法对应的@Pointcut作为切入点,但是因为apiLog方法被混淆成a导致找不到对应@Pointcut
#所以全部不混淆 省心点
-keep class com.zzz.framework.** {*;
}#注解了Aspect的都不混淆,由于把framework下的所有类都不混淆,所以此配置就可有可无了
#-keep @org.aspectj.lang.annotation.Aspect class * {
#    *;
#}#保留Serializable序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {static final long serialVersionUID;private static final java.io.ObjectStreamField[] serialPersistentFields;private void writeObject(java.io.ObjectOutputStream);private void readObject(java.io.ObjectInputStream);java.lang.Object writeReplace();java.lang.Object readResolve();
}

3.assembly.xml

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"><!--项目标识,设置的话,生成后的zip文件会加上此后缀--><id>${project.version}</id><!--打包格式--><formats><format>jar</format></formats><!--压缩包下是否生成和项目名相同的根目录--><includeBaseDirectory>false</includeBaseDirectory><fileSets><fileSet><directory>${project.build.directory}/classes</directory><outputDirectory>/</outputDirectory></fileSet></fileSets><dependencySets><!-- 把依赖的项目其他模块代码放到jar包里,方便对其他模块代码 --><dependencySet><outputDirectory>/</outputDirectory><includes><include>com.zzz:*</include></includes><unpack>true</unpack></dependencySet></dependencySets></assembly>

springboot proguard 代码混淆相关推荐

  1. java代码proguard代码混淆GUI使用,附带混淆map映射

    proguard代码混淆GUI使用,附带混淆map映射 混淆结果预览 本例使用的是Proguard 7.2.2,可以官网直接下载 链接:https://pan.baidu.com/s/1PejprpE ...

  2. proguard代码混淆问题

    记录自己遇到的代码混淆问题(spring boot项目代码混淆): 1.由于引入框架,比如JDO,spring boot,有些方法是框架去调用,所以这些是不能够被混淆的. 比如自己编写的转换类: pu ...

  3. [进来复制]springboot项目代码混淆,帮你安排的明白的混淆代码!

    首先感谢俩篇博文博主本文是基于以下俩个博文进行的,楼主也是忙活了大半天整这个混淆,本以为可以快速完成的,没成想耗时耗力. 详细的springboot结合proguard做代码混淆,感谢. 其他博主的踩 ...

  4. java 混淆 ant_Ant编译android,并Proguard代码混淆,v4混淆配置

    忙了三四周,总算把购开心的ios版(appStore搜"购开心")成功翻译成了android版,UI效果基本保持一致,上一张UI效果图吧,很养眼的: 还不错吧,进入正题吧. 以前一 ...

  5. springboot项目代码混淆和反编译教程·附软件连接

    对springboot项目进行代码混淆,可以防止别人通过反编译项目查看代码,即使反编译了查看的也是混淆后的看不懂的代码. 一定程度保证了项目源码安全性. 下面分享代码混淆步骤和反编译操作 Allato ...

  6. ProGuard代码混淆器如何使用

  7. 最新版SpringBoot结合ProGuard实现代码混淆

    SpringBoot结合ProGuard实现代码混淆 参考案例 前言 ProGuard集成 1.maven的配置 2.相关异常解决 注意事项 参考案例 1.Springboot+proguard+ma ...

  8. Android代码混淆工具Proguard学习

    概述 Proguard代码混淆工具:可以对代码进行去冗余压缩,代码优化,代码混淆等.在Android中的主要应用就是对代码混淆:就是将类名,方法名,Field名变成如a,b,c或者1,2,3等难以阅读 ...

  9. Android Progurad 代码混淆

    ref: ProGuard基础语法和打包配置.md https://github.com/D-clock/Doc/blob/master/Android/Gradle/3_ProGuard%E5%9F ...

最新文章

  1. 出现module ‘xgboost‘ has no attribute ‘DMatrix‘的临时解决方法
  2. java异常体系分类(面试)
  3. 嘘,Python 优化提速的 8 个小技巧
  4. android 设置folder类型,正确配置你的 Android 项目
  5. 嵌入生活的嵌入式,超市里的电子价签
  6. 须使用visual c 内联汇编语言开发,在VisualC 中使用内联汇编
  7. Windows 未能启动。原因可能是最近更改了硬件或软件。解决此问题的步骤
  8. c语言程序隔断,别再砌墙了!20种方法让隔断在你家C位出场
  9. 寻星计划|Apache Doris 社区4月职位广场,IDG资本、Shopee 多个岗位
  10. python爬虫怎么保存图片_使用Python爬虫怎么将网页图片保存到本地
  11. 从小程序升级成独立APP,“小鹅拼拼”如何帮鹅厂“拼”未来?
  12. 上级对下级用通知合适吗_切记!这几句话千万不要对领导说
  13. 为什么建议将成员属性设置为私有
  14. 2022届互联网校招薪资开奖,拼多多最高年薪 75 万!
  15. 不要996!程序员创建955.WLB不加班公司名单,GitHub周榜第二
  16. SQL Server菜鸟入门
  17. 鸿蒙时代的人物有哪些,鸿蒙时代四大创世神兽,龙都是它们的后代,麒麟都排不上号...
  18. 5 个无聊 Python 程序,用 Python 整蛊你的朋友们吧
  19. 专精特新中小企业认定标准
  20. 【医学图像处理】CT成像技术之图像质量

热门文章

  1. js 监听URL地址变化
  2. Android SDK Tutorials系列 - Hello Localization
  3. 在奔向5G的道路上 高通用了哪些洪荒之力
  4. 皮皮仔!在 vscode 里操作数据库~
  5. 新型基础测绘与实景三维中国建设技术文件【4】基础地理实体数据元数据
  6. 佛山日电服务器维修,11月3日在线维护公告
  7. YOYOPlayer:linux下开源的千千静听
  8. Uni-app实现仿网易云音乐播放
  9. 地面沉降数值模拟应用与案例分析
  10. 宿松中学2021高考成绩查询,宿松中学隆重举行2021届高三毕业典礼