首页> 搜索结果页
"手机qq java" 检索
共 942 条结果
PopupWindow(悬浮框)的基本使用
后一个用于显示信息的UI控件——PopupWindow(悬浮框),如果你想知道他长什么样子,你可以打开你手机的QQ,长按列表中的某项,这个时候后弹出一个黑色的小对话框,这种就是PopupWindow了,和AlertDialog对话框不同的是,他的位置可以是随意的;另外AlertDialog是非堵塞线程的,而PopupWindow则是堵塞线程的!而官方有这样一句话来介绍PopupWindow:A popup window that can be used to display an arbitrary view. The popup window isa floating container that appears on top of the current activity.1.相关方法解读1)几个常用的构造方法我们在文档中可以看到,提供给我们的PopupWindow的构造方法有九种之多,这里只贴实际开发中用得较多的几个构造方法:public PopupWindow (Context context)public PopupWindow(View contentView, int width, int height)public PopupWindow(View contentView)public PopupWindow(View contentView, int width, int height, boolean focusable)参数就不用多解释了吧,contentView是PopupWindow显示的View,focusable是否显示焦点2)常用的一些方法下面介绍几个用得较多的一些方法,其他的可自行查阅文档:setContentView(View contentView):设置PopupWindow显示的ViewgetContentView():获得PopupWindow显示的ViewshowAsDropDown(View anchor):相对某个控件的位置(正左下方),无偏移showAsDropDown(View anchor, int xoff, int yoff):相对某个控件的位置,有偏移showAtLocation(View parent, int gravity, int x, int y):相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移PS:parent这个参数只要是activity中的view就可以了!setWidth/setHeight:设置宽高,也可以在构造方法那里指定好宽高,除了可以写具体的值,还可以用WRAP_CONTENT或MATCH_PARENT,popupWindow的width和height属性直接和第一层View相对应。setFocusable(true):设置焦点,PopupWindow弹出后,所有的触屏和物理按键都由PopupWindows处理。其他任何事件的响应都必须发生在PopupWindow消失之后,(home 等系统层面的事件除外)。比如这样一个PopupWindow出现的时候,按back键首先是让PopupWindow消失,第二次按才是退出activity,准确的说是想退出activity你得首先让PopupWindow消失,因为不并是任何情况下按back PopupWindow都会消失,必须在PopupWindow设置了背景的情况下 。setAnimationStyle(int):设置动画效果2.使用代码示例运行效果图:实现关键代码:先贴下动画文件:anim_pop.xml:<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <alpha android:fromAlpha="0" android:toAlpha="1" android:duration="2000"> </alpha> </set>接着是popupWindow的布局:item_popip.xml:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/ic_pop_bg" android:orientation="vertical"> <Button android:id="@+id/btn_xixi" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dp" android:text="嘻嘻" android:textSize="18sp" /> <Button android:id="@+id/btn_hehe" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dp" android:text="呵呵" android:textSize="18sp" /> </LinearLayout>MainActivity.java:public class MainActivity extends AppCompatActivity { private Button btn_show; private Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = MainActivity.this; btn_show = (Button) findViewById(R.id.btn_show); btn_show.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { initPopWindow(v); } }); } private void initPopWindow(View v) { View view = LayoutInflater.from(mContext).inflate(R.layout.item_popup, null, false); Button btn_xixi = (Button) view.findViewById(R.id.btn_xixi); Button btn_hehe = (Button) view.findViewById(R.id.btn_hehe); //1.构造一个PopupWindow,参数依次是加载的View,宽高 final PopupWindow popWindow = new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); popWindow.setAnimationStyle(R.anim.anim_pop); //设置加载动画 //这些为了点击非PopupWindow区域,PopupWindow会消失的,如果没有下面的 //代码的话,你会发现,当你把PopupWindow显示出来了,无论你按多少次后退键 //PopupWindow并不会关闭,而且退不出程序,加上下述代码可以解决这个问题 popWindow.setTouchable(true); popWindow.setTouchInterceptor(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; // 这里如果返回true的话,touch事件将被拦截 // 拦截后 PopupWindow的onTouchEvent不被调用,这样点击外部区域无法dismiss } }); popWindow.setBackgroundDrawable(new ColorDrawable(0x00000000)); //要为popWindow设置一个背景才有效 //设置popupWindow显示的位置,参数依次是参照View,x轴的偏移量,y轴的偏移量 popWindow.showAsDropDown(v, 50, 0); //设置popupWindow里的按钮的事件 btn_xixi.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "你点击了嘻嘻~", Toast.LENGTH_SHORT).show(); } }); btn_hehe.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "你点击了呵呵~", Toast.LENGTH_SHORT).show(); popWindow.dismiss(); } }); } }
文章
容器
2023-03-20
Java正则表达式
❤ 作者主页:欢迎来到我的技术博客❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~* 如果文章对您有帮助,记得关注、点赞、收藏、评论⭐️⭐️⭐️ 您的支持将是我创作的动力,让我们一起加油进步吧!!!第一章 概述正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),是一种【文本模式(Pattern)】。正则表达式使用单个字符串来描述、匹配具有相同规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。正则表达式的核心功能就是处理文本。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。第二章 正则表达式基础语法一、元字符元字符是构造正则表达式的一种基本元素。. :匹配除换行符以外的任意字符\w:匹配字母或数字或下划线或汉字\s:匹配任意的空白符\d:匹配数字\b:匹配单词的开始或结束^:匹配字符串的开始$:匹配字符串的结束举例:匹配8位数字的QQ号码:^\d\d\d\d\d\d\d\d$匹配1开头11位数字的手机号码:^1\d\d\d\d\d\d\d\d\d\d$二、重复限定符正则表达式提供了对重复字符进行简写的方式:*:重复零次或更多次+:重复一次或更多次?:重复零次或一次{n}:重复n次{n,}:重复n次或更多次{n,m}:重复n到m次有了这些限定符之后,我们就可以对之前的正则表达式进行改造了,比如:匹配8位数字的QQ号码:^\d{8}$匹配1开头11位数字的手机号码:^1\d{10}$匹配银行卡号是14~18位的数字:^\d{14,18}$匹配以a开头的,0个或多个b结尾的字符串: ^ab*$三、分组限定符是作用在与他相邻的最左边的一个字符,那么问题来了,如果我想要ab同时被限定那怎么办呢?正则表达式中用小括号()来做分组,也就是括号中的内容会作为一个整体。如匹配字符串中包含0到多个ab开头:^(ab)*四、转义正则提供了转义的方式,也就是要把这些元字符、限定符或者关键字转义成普通的字符,做法很简答,就是在要转义的字符前面加个斜杠,也就是\即可。匹配字符串中包含0到多个(ab)开头:^(\(ab\))*匹配一个字符*:\*五、条件回到我们刚才的手机号匹配,我们都知道:国内号码都来自三大运营商,它们都有属于自己的号段。比如联通有130/131/132/155/156/185/186/145/176等号段,假如让我们匹配一个联通的号码,那按照我们目前所学到的正则,应该无从下手的,因为这里包含了一些并列的条件,也就是“或”,那么在正则中是如何表示“或”的呢?正则用符号 | 来表示或,也叫做分支条件,当满足正则里的分支条件的任何一种条件时,都会当成是匹配成功。那么我们就可以用或条件来处理这个问题:^(130|131|132|155|156|185|186|145|176)\d{8}$六、区间正则提供一个元字符中括号 [] 来表示区间条件。限定0到9 可以写成[0-9]限定A-Z 写成[A-Z]限定某些数字 [165]那上面的正则我们还改成这样:^((13[0-2])|(15[56])|(18[5-6])|145|176)\d{8}$七、反义前面说到元字符的都是要匹配什么什么,当然如果你想反着来,不想匹配某些字符,正则也提供了一些常用的反义元字符:元字符解释\W匹配任意不是字母,数字,下划线,汉字的字符\S匹配任意不是空白符的字符\D匹配任意非数字的字符\B匹配不是单词开头或结束的位置1匹配除了x以外的任意字符2匹配除了aeiou这几个字母以外的任意字符八、常见的正则表达式匹配中文字符的正则表达式:[\u4e00-\u9fa5]匹配形式:My name is Mary!匹配Email地址的正则表达式:^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$ 匹配形式: 51012324@qq.com 、abzzyi@163.com,yfavve@126.com匹配国内电话号码:\d{3}-\d{8}|\d{4}-\d{7} 匹配形式: 0511-4405222 或 021-87888822匹配腾讯QQ号:[1-9][0-9]{4,} 匹配形式:510180222匹配身份证:(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$ 匹配形式:142228199108252125匹配ip地址:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} 匹配形式:127.0.0.1匹配国内的手机号:^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$匹配形式:1388888888第三章Java正则表达式小知识:有些语言中,\ 表示:我就是一个普通的【反斜杠】,请不要给我任何特殊的意义。在 Java 中,\ 表示:我不是一个普通的【反斜杠】,我必须对紧随其后的字符进行转义,如果想将我视为普通反斜杠,请转义我。我们可以看以下的输出内容:System.out.print("\\"); // 输出为 \ System.out.print("\\\\"); // 输出为 \\Java中正则表达式的执行流程:一、正则表达式实例java.util.regex 包主要包括以下三个类:Pattern 类:正则表达式的编译表示形式。若要使用正则表达式必须将其【编译到此类】的实例中。然后,可以使用生成的模式对象创建 Matcher 对象。Matcher 类:Matcher 对象是对输入字符串进行【解释和匹配】操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。PatternSyntaxException:PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。以下实例中使用了正则表达式 . *china.* 用于查找字符串中是否包了 china子串: @Test public void testRegex{ String content = "I am itnanls,I'm from ydlclass."; String pattern = ".*itnanls.*"; boolean isMatch = Pattern.matches(pattern, content); System.out.println(isMatch); } 运行结果:true二、Matcher 类的方法1. 匹配方法该方法可以精确表明输入字符串中在哪能找到与之匹配的内容:序号方法及说明1start()返回匹配的起始索引2start(int group)返回在匹配操作期间,由给定组所捕获的子序列的起始索引3end()返回匹配字符末尾索引4end(int group)返回在匹配操作期间,由给定组所捕获的子序列的末尾索引下面是一个对单词 "cat" 出现在输入字符串中出现次数进行计数的例子:@Test public void testStart() { String regex = "cat"; String content = "cat cat dog dog cat"; Pattern pattern = Pattern.compile(regex); Matcher m = pattern.matcher(content); // 获取 matcher 对象 int count = 0; while (m.find()) { count++; System.out.println("Match number " + count); System.out.println("start(): " + m.start()); System.out.println("end(): " + m.end()); } }运行结果如下:Match number 1 start(): 0 end(): 3 Match number 2 start(): 4 end(): 7 Match number 3 start(): 16 end(): 192. 查找方法查找方法用来检查输入字符串并返回一个布尔值,表示是否找到该模式:序号方法说明1lookingAt()返回目标字符串前面部分与 Pattern 是否匹配2find()返回目标字符串中是否包含与 Pattern 匹配的子串3find(int start)从指定索引开始匹配查找。4matches()尝试将整个区域与模式匹配matches 和 lookingAt 方法都用来尝试匹配一个输入序列模式。它们的不同是 matches 要求整个序列都匹配,而lookingAt 不要求。lookingAt 方法虽然不需要整句都匹配,但是需要从第一个字符开始匹配。实例:@Test public void testMatches() { String regex = "Mary"; String content1 = "Mary"; String content2 = "Mary is very handsome !"; String content3 = "My name is Mary."; Pattern pattern = Pattern.compile(regex); Matcher matcher1 = pattern.matcher(content1); Matcher matcher2 = pattern.matcher(content2); Matcher matcher3 = pattern.matcher(content3); System.out.println("matches1(): " + matcher1.matches()); System.out.println("lookingAt1(): " + matcher1.lookingAt()); System.out.println("matches2(): " + matcher2.matches()); System.out.println("lookingAt2(): " + matcher2.lookingAt()); System.out.println("matches3(): " + matcher3.matches()); System.out.println("lookingAt3(): " + matcher3.lookingAt()); }运行结果如下:matches(): true lookingAt(): true matches(): false lookingAt(): true matches(): false lookingAt(): false3. 替换方法替换方法是替换输入字符串里文本的方法:序号方法说明1public String replaceAll(String replacement)替换模式与给定替换字符串相匹配的输入序列的每个子序列。2public String replaceFirst(String replacement)替换模式与给定替换字符串匹配的输入序列的第一个子序列。3public Matcher appendReplacement(StringBuffer sb, String replacement)实现非末尾的添加和替换步骤。4public StringBuffer appendTail(StringBuffer sb)实现末尾的添加和替换步骤。下面的例子来解释replaceAll和replaceFirst:@Test public void testReplace(){ String regex = "Mary"; String context = "My name is Mary, Mary is very handsome. "; String replacement = "Lucy"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(context); String result1 = m.replaceAll(replacement); System.out.println(result1); String result2 = m.replaceFirst(replacement); System.out.println(result2); }运结果 如下:My name is Lucy, itlils is very handsome. My name is Lucy, itnanls is very handsome. 下面的例子来解释appendReplacement和appendTail:@Test public void testAppend() { String REGEX = "a*b"; String INPUT = "aabfooaabfooabfooabkkk"; String REPLACE = "-"; Pattern p = Pattern.compile(REGEX); // 获取 matcher 对象 Matcher m = p.matcher(INPUT); StringBuffer sb = new StringBuffer(); m.find(); m.appendReplacement(sb, REPLACE); System.out.println(sb); m.find(); m.appendReplacement(sb, REPLACE); System.out.println(sb); m.appendTail(sb); System.out.println(sb); }运行结果如下:- -foo- -foo-fooabfooabkkk4. PatternSyntaxException 类的方法PatternSyntaxException 是一个异常类,它指示一个正则表达式模式中的语法错误。PatternSyntaxException 类提供了下面的方法来帮助我们查看发生了什么错误。序号方法说明1getDescription()获取错误的描述。2getIndex()获取错误的索引。3getPattern()获取错误的正则表达式模式。4getMessage()返回多行字符串,包含语法错误及其索引的描述、错误的正则表达式模式和模式中错误索引的可视化指示。String REGEX = "a*b[er";第四章 正则表达式进阶语法一、零宽断言【断言】就是说正则可以【断定】在指定内容的前面或后面会出现满足指定规则的内容。【零宽】 断言部分只确定位置不匹配任何内容,只是一种模式。内容宽度为零。我们来举个栗子:假设我们要用爬虫抓取csdn里的文章阅读量。通过查看源代码可以看到文章阅读量这个内容是这样的结构:"<span class="read-count">阅读数:641</span>"其中也就【641】这个是变量,也就是说不同文章不同的值,当我们拿到这个字符串时,需要获得这里边的【641】有很多种办法,但如果正则应该怎么匹配呢?下面先来讲几种类型的断言:几个概念:概念功能预测/先行(模式在前),要求后面的符合匹配回顾/后发(模式在后),要求前面的符合匹配正符合匹配负不符合匹配1. 正向先行断言零宽度正预测先行断言语法:(?=pattern)作用:匹配pattern表达式的前面内容,不返回本身。【正向先行断言】可以匹配表达式前面的内容,那意思就是(?=) 就可以匹配到前面的内容了。如果我们要匹配所有内容那就是:@Test public void testAssert1(){ String regex = ".+(?=</span>)"; String context = "<span class=\"read-count\">阅读数:641</span>"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(context); while (matcher.find()){ System.out.println(matcher.group()); } } //匹配结果:<span class="read-count">阅读数:641 //可是我们要的只是前面的数字呀,那也简单咯,匹配数字 \d,那可以改成: @Test public void testAssert2(){ String regex = "\\d+(?=</span>)"; String context = "<span class=\"read-count\">阅读数:641</span>"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(context); while (matcher.find()){ System.out.println(matcher.group()); } } //匹配结果: //6412. 正向后行断言零宽度正回顾后发断言,断言在前,模式在后语法:(?<=pattern)作用:匹配pattern表达式的后面的内容,不返回本身。有先行就有后行,先行是匹配前面的内容,那后行就是匹配后面的内容啦。上面的例子,我们也可以用后行断言来处理:@Test public void testAssert3(){ String regex = "(?<=<span class=\"read-count\">阅读数:)\\d+"; String context = "<span class=\"read-count\">阅读数:641</span>"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(context); while (matcher.find()){ System.out.println(matcher.group()); } }3. 负向先行断言零宽度负预测先行断言语法:(?!pattern)作用:匹配非pattern表达式的前面内容,不返回本身。有正向也有负向,负向在这里其实就是非的意思。举个栗子:比如有一句 “我爱祖国,我是祖国的花朵”。现在要找到不是'的花朵'前面的祖国。用正则就可以这样写:祖国(?!的花朵)4. 负向后行断言零宽度负回顾后发断言语法:(?<!pattern)作用:匹配非pattern表达式的后面内容,不返回本身。举个例子:比如有一句 “我爱祖国,我是祖国的花朵”。现在要找到不是'我爱'后面的祖国。用正则就可以这样写:(?<!我爱)祖国二、捕获和非捕获捕获组: 我们匹配子表达式的内容,并把匹配结果【以数字编号或组名的方式】保存到内存中,之后可以通过序号或名称来使用这些匹配结果。而根据命名方式的不同,又可以分为两种组:1. 数字编号捕获组:语法:(exp)解释:从表达式左侧开始,每出现一个左括号和它对应的右括号之间的内容为一个分组,在分组中,第0组为整个表达式,第一组开始为分组。比如固定电话的:020-85653333正则表达式为:(0\d{2})-(\d{8})按照左括号的顺序,这个表达式有如下分组:序号编号分组内容00(0\d{2})-(\d{8})020-8565333311(0\d{2})02020(\d{8})85653333String test = "020-85653333"; String reg="(0\\d{2})-(\\d{8})"; Pattern pattern = Pattern.compile(reg); Matcher mc= pattern.matcher(test); if(mc.find()){ System.out.println("分组的个数有:"+mc.groupCount()); for(int i=0;i<=mc.groupCount();i++){ System.out.println("第"+i+"个分组为:"+mc.group(i)); } } 输出结果: 分组的个数有:2 第0个分组为:020-85653333 第1个分组为:020 第2个分组为:85653333可见,分组个数是2,但是因为第0个为整个表达式本身,因此也一起输出了。2. 命名编号捕获组:语法:(?exp)解释:分组的命名由表达式中的name指定,比如区号也可以这样写:(?<quhao>0\\d{2})-(?<haoma>\\d{8})按照左括号的顺序,这个表达式有如下分组:序号名称分组内容00(0\d{2})-(\d{8})020-856533331quhao(0\d{2})0202haoma(\d{8})85653333用代码来验证一下:String test = "020-85653333"; String reg="(?<quhao>0\\d{2})-(?<haoma>\\d{8})"; Pattern pattern = Pattern.compile(reg); Matcher mc= pattern.matcher(test); if(mc.find()){ System.out.println("分组的个数有:"+mc.groupCount()); System.out.println(mc.group("quhao")); System.out.println(mc.group("haoma")); } 输出结果: 分组的个数有:2 分组名称为:quhao,匹配内容为:020 分组名称为:haoma,匹配内容为:856533333. 非捕获组:语法:(?:exp)解释:和捕获组刚好相反,它用来标识那些不需要捕获的分组。比如上面的正则表达式,程序不需要用到第一个分组,那就可以这样写:(?:0\\d{2})-(\\d{8})序号名称分组内容00(0\d{2})-(\d{8})020-8565333311(\d{8})85653333String test = "020-85653333"; String reg="(?:0\\d{2})-(\\d{8})"; Pattern pattern = Pattern.compile(reg); Matcher mc= pattern.matcher(test); if(mc.find()){ System.out.println("分组的个数有:"+mc.groupCount()); for(int i=0;i<=mc.groupCount();i++){ System.out.println("第"+i+"个分组为:"+mc.group(i)); } } 输出结果: 分组的个数有:1 第0个分组为:020-85653333 第1个分组为:85653333三、反向引用我们知道:捕获会返回一个捕获组,这个分组是保存在内存中,不仅可以在正则表达式外部通过程序进行引用,也可以【在正则表达式内部进行引用】,这种引用方式就是【反向引用】。根据捕获组的命名规则,反向引用可分为:普通捕获组反向引用:\k,通常简写为\number命名捕获组反向引用:\k,或者\k'name'我们可以举一个例子:比如要查找一串字母"aabbbbgbddesddfiid"里成对的字母,如果按照我们之前学到的正则,什么区间啊限定啊断言啊可能是办不到的。现在我们先用程序思维理一下思路:1)匹配到一个字母2)匹配第下一个字母,检查是否和上一个字母是否一样3)如果一样,则匹配成功,否则失败这里的思路中,在匹配下一个字母时,需要用到上一个字母进行比较,但是目前的知识实在办不到。这下子捕获就有用处啦,我们可以利用捕获把上一个匹配成功的内容用来作为本次匹配的条件即可。首先匹配一个字母:\w。我们需要做成分组才能捕获,因此写成这样:(\w)那这个表达式就有一个捕获组:(\w)然后我们要用这个捕获组作为条件,那就可以:(\w)\1这里的\1是什么意思呢?根据反向引用的数字命名规则,就需要 \k<1>或者\1,当然,通常都是是后者。我们来测试一下:@Test public void testRef(){ String context = "aabbxxccdddsksdhfhshh"; String regex = "(\\w)\\1"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(context); while (matcher.find()){ System.out.println(matcher.group()); } } 输出结果: aa bb xx cc dd hh再举个替换的例子,假如想要把字符串中abc换成a@Test public void testReplaceAll(){ String context = "abc aabc bc xxx mm"; String regex = "(a*)(b)(c)"; String res = context.replaceAll(regex, "$1"); System.out.println(res); } 输出结果: a aa xxx mm四、贪婪和非贪婪1. 贪婪匹配贪婪匹配: 当正则表达式中包含能接受重复的限定符时,该方式会匹配尽可能多的字符,这匹配方式叫做贪婪匹配。前面我们讲过重复限定符,其实这些限定符就是贪婪量词,比如表达式:\d{3,6}。用来匹配3到6位数字,在这种情况下,它是一种贪婪模式的匹配,也就是假如字符串里有6个数字可以匹配,那它就是全部匹配到。@Test public void testGreed(){ String regex = "\\d{3,6}"; String context ="61762828 176 2991 871"; System.out.println("文本:" + context); System.out.println("贪婪模式:"+ regex); Pattern pattern =Pattern.compile(regex); Matcher matcher = pattern.matcher(context); while(matcher.find()){ System.out.println("匹配结果:" + matcher.group(0)); } } 输出结果: 文本:61762828 176 2991 44 871 贪婪模式:\d{3,6} 匹配结果:617628 匹配结果:176 匹配结果:2991 匹配结果:871由结果可见:本来字符串中的“61762828”这一段,其实只需要出现3个(617)就已经匹配成功了的,但是他并不满足,而是匹配到了最大能匹配的字符,也就是6个。多个贪婪量词在一起时,如果字符串能满足他们各自最大程度的匹配时,就互不干扰,但如果不能满足时,会优先满足最大数量的匹配,剩余再分配下一个量词匹配。@Test public void testGreed2(){ String regex = "\\d{1,2}\\d{3,5}"; String context ="61762828 176 2991 871"; System.out.println("文本:" + context); System.out.println("贪婪模式:"+ regex); Pattern pattern =Pattern.compile(regex); Matcher matcher = pattern.matcher(context); while(matcher.find()){ System.out.println("匹配结果:" + matcher.group(0)); } } 输出结果: 文本:61762828 176 2991 871 贪婪模式:\d{1,2}\d{3,5} 匹配结果:6176282 匹配结果:29912. 懒惰匹配懒惰匹配:当正则表达式中包含能接受重复的限定符时,会匹配尽可能少的字符,这匹配方式叫做懒惰匹配。懒惰量词是在贪婪量词后面加个“?”代码说明*?重复任意次,但尽可能少重复+?重复1次或更多次,但尽可能少重复??重复0次或1次,但尽可能少重复{n,m}?重复n到m次,但尽可能少重复{n,}?重复n次以上,但尽可能少重复@Test public void testNotGreed(){ String reg="(\\d{1,2}?)(\\d{3,4})"; String test="61762828 176 2991 87321"; System.out.println("文本:"+test); System.out.println("贪婪模式:"+reg); Pattern p1 =Pattern.compile(reg); Matcher m1 = p1.matcher(test); while(m1.find()){ System.out.println("匹配结果:"+m1.group(0)); } } 输出结果: 文本:61762828 176 2991 87321 懒惰匹配:(\d{1,2}?)(\d{3,4}) 匹配结果:61762 匹配结果:2991 匹配结果:87321“61762” 是左边的懒惰匹配出6,右边的贪婪匹配出1762。"2991" 是左边的懒惰匹配出2,右边的贪婪匹配出991。"87321" 左边的懒惰匹配出8,右边的贪婪匹配出7321。第五章 作业通过scanner输入一个字符串,判断是否是一个邮箱?public class TestEmail { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); String email = scanner.next(); String regex = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(email); boolean matches = matcher.matches(); if(matches){ System.out.println("您输入的是一个邮箱!"); } else { System.out.println("您输入的不是邮箱!"); } } }将一个文件中的邮箱全部查找出来?@Test public void findEmail() throws IOException { StringBuilder sb = new StringBuilder(); // 1、将文件的内容读取到内存 InputStream in = new FileInputStream("D:\\user.txt"); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0){ sb.append(new String(buf,0,len)); } // 2、进行正则匹配 String regex = "[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(sb.toString()); while (matcher.find()){ System.out.println(matcher.group()); } }将一个文件中的电话的中间四个数字替换成xxxx? 例子 15236985456 --> 152xxxx5456文本如下:姓名 年龄 邮箱 电话 张小强 23 526845845@163.com 13759685424 丁新新 20 238011792@qq.com 18011023709 李银龙 20 liyinl1199w@163.com 17308811441 赵资本 19 anhuo69579@126.com 18234417225 李成刚 21 19713318@qq.com 13279906620 王铁柱 20 ykl3987671@163.com 18802836971 张龙虎 22 zh199715@gmail.com 13888906654 李洁一 18 nl897665@yahoo.com 19762297581 刘大志 20 197685551@qq.com 15299744196 杨天天 19 86765ytian@126.com 17663999002 陈承成 21 rr796232@hotmail.com 18137541864 @Test public void hidePhoneNumber() throws IOException { StringBuilder sb = new StringBuilder(); // 1、将文件的内容读取到内存 InputStream in = new FileInputStream("D:\\user.txt"); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0){ sb.append(new String(buf,0,len)); } // 2、进行正则匹配 String regex = "(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])(\\d{4})(\\d{4})"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(sb.toString()); String result = matcher.replaceAll("$1xxxx$3"); System.out.println(result); } *** **创作不易,如果有帮助到你,请给文章==点个赞和收藏==,让更多的人看到!!!**x ↩aeiou ↩
文章
数据采集  ·  数据可视化  ·  Java  ·  索引
2023-02-25
JAVA学习路线图 【黑马版】 上
版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/qq_41570658使用请点赞和关注,后期还有更多内容分享和更新,谢谢!电脑版:具体内容请点击右侧链接→→: - [ ] JAVA学习路线图【黑马版】手机版:三、Java学习路线图——工具篇
文章
Java
2023-02-20
DolphinScheduler教程(01)- 入门
1. 概述Apache DolphinScheduler 官网地址:https://dolphinscheduler.apache.org/Apache DolphinScheduler 是一个分布式、去中心化、易扩展的可视化DAG工作流任务调度系统,其致力于解决数据处理流程中错综复杂的依赖关系,使调度系统在数据处理流程中开箱即用。DolphinScheduler提供了许多易于使用的功能,可加快数据ETL工作开发流程的效率。其主要特点如下:通过拖拽以DAG 图的方式将 Task 按照任务的依赖关系关联起来,可实时可视化监控任务的运行状态;支持丰富的任务类型;支持工作流定时调度、依赖调度、手动调度、手动暂停/停止/恢复,同时支持失败重试/告警、从指定节点恢复失败、Kill 任务等操作;支持工作流全局参数及节点自定义参数设置;支持集群HA,通过 Zookeeper实现 Master 集群和Worker集群去中心化;支持工作流运行历史树形/甘特图展示、支持任务状态统计、流程状态统计;支持补数,并行或串行回填数据。2. 系统架构2.1 名词解释流程定义:通过拖拽任务节点并建立任务节点的关联所形成的可视化DAG流程实例:流程实例是流程定义的实例化,可以通过手动启动或定时调度生成,流程定义每运行一次,产生一个流程实例;任务实例:任务实例是流程定义中任务节点的实例化,标识着具体的任务执行状态;任务类型: 目前支持有SHELL、SQL、SUB_PROCESS(子流程)、PROCEDURE、MR、SPARK、PYTHON、DEPENDENT(依赖),同时计划支持动态插件扩展,注意:其中子 SUB_PROCESS 也是一个单独的流程定义,是可以单独启动执行的;调度方式: 系统支持基于cron表达式的定时调度和手动调度。命令类型支持:启动工作流、从当前节点开始执行、恢复被容错的工作流、恢复暂停流程、从失败节点开始执行、补数、定时、重跑、暂停、停止、恢复等待线程。其中 恢复被容错的工作流 和 恢复等待线程 两种命令类型是由调度内部控制使用,外部无法调用定时调度:系统采用 quartz 分布式调度器,并同时支持cron表达式可视化的生成依赖:系统不单单支持 DAG 简单的前驱和后继节点之间的依赖,同时还提供任务依赖节点,支持流程间的自定义任务依赖优先级 :支持流程实例和任务实例的优先级,如果流程实例和任务实例的优先级不设置,则默认是先进先出邮件告警:支持 SQL任务 查询结果邮件发送,流程实例运行结果邮件告警及容错告警通知失败策略:对于并行运行的任务,如果有任务失败,提供两种失败策略处理方式,继续是指不管并行运行任务的状态,直到流程失败结束。结束是指一旦发现失败任务,则同时Kill掉正在运行的并行任务,流程失败结束补数:补历史数据,支持区间并行和串行两种补数方式2.2 架构架构详解可以参考官网文档:https://dolphinscheduler.apache.org/zh-cn/blog/architecture-design.html3. 部署3.1 后端部署后端有2种部署方式,分别为自动化部署和编译源码部署。下面主要介绍下载编译后的二进制包一键自动化部署的方式完成DolphinScheduler后端部署。3.1.1 基础软件安装编译时,如果是编译源码:Node.js:必装Maven:必装(最好 3.6 版本)注意:EasyScheduler本身不依赖Hadoop、Hive、Spark、PostgreSQL,仅是会调用他们的Client,用于对应任务的运行。3.1.2 创建部署用户在所有需要部署调度的机器上创建部署用户(本次以node2、node3节点为例),因为worker服务是以 sudo -u {linux-user} 方式来执行作业,所以部署用户需要有 sudo 权限,而且是免密的。# 1 创建用户 useradd escheduler # 2 设置 escheduler 用户密码 passwd escheduler # 3 赋予sudo权限。编辑系统 sudoers 文件 # 如果没有编辑权限,以root用户登录,赋予w权限 # chmod 640 /etc/sudoers vi /etc/sudoers # 大概在100行,在root下添加如下 escheduler ALL=(ALL) NOPASSWD: ALL # 并且需要注释掉 Default requiretty 一行。如果有则注释,没有没有跳过 #Default requiretty ########### end ############ # 4 切换到 escheduler 用户 su escheduler 3.1.3 下载并解压# 1 创建安装目录 sudo mkdir /opt/DolphinScheduler # 2 将DolphinScheduler赋予给escheduler用户 sudo chown -R escheduler:escheduler /opt/DolphinScheduler # 3 下载后端。简称escheduler-backend cd /opt/DolphinScheduler wget https://github.com/apache/incubator-dolphinscheduler/releases/download/1.1.0/escheduler-1.1.0-backend.tar.gz # 4 解压 mkdir escheduler-backend mkdir escheduler tar -zxf escheduler-1.1.0-backend.tar.gz -C escheduler cd escheduler/ # 5 目录介绍 [escheduler@node2 escheduler]$ tree -L 1 . ├── bin # 基础服务启动脚本 ├── conf # 项目配置文件 ├── install.sh # 一键部署脚本 ├── lib # 项目依赖jar包,包括各个模块jar和第三方jar ├── script # 集群启动、停止和服务监控启停脚本 └── sql # 项目依赖sql文件 5 directories, 1 file 3.1.4 针对escheduler用户ssh免密配置# 1 配置SSH免密 # 1.1 node2 节点执行 # 有提示直接回车 ssh-keygen -t rsa # 拷贝到node2和node3。提示输入密码时,输入 escheduler 用户的密码 ssh-copy-id -i ~/.ssh/id_rsa.pub escheduler@node2 ssh-copy-id -i ~/.ssh/id_rsa.pub escheduler@node3 # 1.2 node3 节点执行 # 有提示直接回车 ssh-keygen -t rsa # 拷贝到node2和node3。提示输入密码时,输入 escheduler 用户的密码 ssh-copy-id -i ~/.ssh/id_rsa.pub escheduler@node2 ssh-copy-id -i ~/.ssh/id_rsa.pub escheduler@node3 3.1.5 数据库初始化执行以下命令创建数据库和账号:CREATE DATABASE escheduler DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci; -- 设置数据用户escheduler的访问密码为 escheduler,并且不对访问的ip做限制 -- 测试环境将访问设置为所有,如果是生产,可以限制只能子网段的ip才能访问('198.168.33.%') GRANT ALL PRIVILEGES ON escheduler.* TO 'escheduler'@'%' IDENTIFIED BY 'escheduler'; flush privileges; 创建表和导入基础数据 修改vim /opt/DolphinScheduler/escheduler/conf/dao/data_source.properties中的下列属性:# 大概在第 4 行修改MySQL数据库的url spring.datasource.url=jdbc:mysql://node1:3306/escheduler?characterEncoding=UTF-8 # 用户名。 spring.datasource.username=escheduler # 密码。填入上一步IDENTIFIED BY 后面设置的密码 spring.datasource.password=escheduler 执行创建表和导入基础数据脚本:# 前面已进入/opt/DolphinScheduler/escheduler-backend目录下,然后执行数据初始化脚本 # 最后看到 create escheduler success 表示数据库初始化成功 sh ./script/create_escheduler.sh 3.1.6 修改部署目录权限及运行参数# 1 修改conf/env/目录下的 .escheduler_env.sh 环境变量 vim conf/env/.escheduler_env.sh # 将对应的修改为自己的组件或框架的路径 export HADOOP_HOME=/opt/hadoop-3.1.2 export HADOOP_CONF_DIR=/opt/hadoop-3.1.2/etc/hadoop export SPARK_HOME1=/opt/spark-2.3.4-bin-hadoop2.7 #export SPARK_HOME2=/opt/soft/spark2 #export PYTHON_HOME=/opt/soft/python export JAVA_HOME=/usr/local/zulu8/ export HIVE_HOME=/opt/apache-hive-3.1.1-bin #export PATH=$HADOOP_HOME/bin:$SPARK_HOME1/bin:$SPARK_HOME2/bin:$PYTHON_HOME:$JAVA_HOME/bin:$HIVE_HOME/bin:$PATH export PATH=$HADOOP_HOME/bin:$SPARK_HOME1/bin:$JAVA_HOME/bin:$HIVE_HOME/bin:$PATH # ========== # CDH 版 # ========== #export HADOOP_HOME=/opt/cloudera/parcels/CDH/lib/hadoop #export HADOOP_CONF_DIR=/etc/hadoop/conf.cloudera.yarn #export SPARK_HOME1=/opt/cloudera/parcels/CDH/lib/spark ##export SPARK_HOME2=/opt/soft/spark2 ##export PYTHON_HOME=/opt/soft/python #export JAVA_HOME=/usr/local/zulu8/ #export HIVE_HOME=/opt/cloudera/parcels/CDH/lib/hive ##export PATH=$HADOOP_HOME/bin:$SPARK_HOME1/bin:$SPARK_HOME2/bin:$PYTHON_HOME:$JAVA_HOME/bin:$HIVE_HOME/bin:$PATH #export PATH=$HADOOP_HOME/bin:$SPARK_HOME1/bin:$JAVA_HOME/bin:$HIVE_HOME/bin:$PATH 修改 install.sh中的各参数,替换成自身业务所需的值,这里只列出了重要的修改项,其它默认不用改即可。# mysql配置 # mysql 地址,端口 mysqlHost="192.168.33.3:3306" # mysql 数据库名称 mysqlDb="escheduler" # mysql 用户名 mysqlUserName="escheduler" # mysql 密码 # 注意:如果有特殊字符,请用 \ 转移符进行转移 mysqlPassword="escheduler" # conf/config/install_config.conf配置 # 注意:安装路径,不要当前路径(pwd)一样。一键部署脚本分发到其它节点时的安装路径 installPath="/opt/DolphinScheduler/escheduler-backend" # 部署用户 # 注意:部署用户需要有sudo权限及操作hdfs的权限,如果开启hdfs,根目录需要自行创建 deployUser="escheduler" # zk集群 zkQuorum="192.168.33.3:2181,192.168.33.6:2181,192.168.33.9:2181" # 安装hosts # 注意:安装调度的机器hostname列表,如果是伪分布式,则只需写一个伪分布式hostname即可 ips="192.168.33.6,192.168.33.9" # conf/config/run_config.conf配置 # 运行Master的机器 # 注意:部署master的机器hostname列表 masters="192.168.33.6" # 运行Worker的机器 # 注意:部署worker的机器hostname列表 workers="192.168.33.6,192.168.33.9" # 运行Alert的机器 # 注意:部署alert server的机器hostname列表 alertServer="192.168.33.6" # 运行Api的机器 # 注意:部署api server的机器hostname列表 apiServers="192.168.33.6" # 用到邮箱发送邮件时务必配置上邮件服务,否则执行结果发送时会提示失败 # cn.escheduler.server.worker.runner.TaskScheduleThread:[249] - task escheduler # failure : send mail failed! java.lang.RuntimeException: send mail failed! # alert配置 # 邮件协议,默认是SMTP邮件协议 mailProtocol="SMTP" # 邮件服务host。以网易邮箱为例。QQ邮箱的服务为 smtp.qq.com mailServerHost="smtp.163.com" # 邮件服务端口。SSL协议端口 465/994,非SSL协议端口 25 mailServerPort="465" # 发送人。 # 网易邮箱在 客户端授权密码 获取,具体可以看下图 mailSender="*******yore@163.com" # 发送人密码 mailPassword="yore***" # 下载Excel路径 xlsFilePath="/home/escheduler/xls" #是否启动监控自启动脚本 # 开关变量,在1.0.3版本中增加,控制是否启动自启动脚本(监控master,worker状态,如果掉线会自动启动) # 默认值为"false"表示不启动自启动脚本,如果需要启动改为"true" monitorServerState="true" # 资源中心上传选择存储方式:HDFS,S3,NONE resUploadStartupType="HDFS" # 如果resUploadStartupType为HDFS,defaultFS写namenode地址,支持HA,需要将core-site.xml和hdfs-site.xml放到conf目录下 # 如果是S3,则写S3地址,比如说:s3a://escheduler,注意,一定要创建根目录/escheduler defaultFS="hdfs://192.168.33.3:8020" # resourcemanager HA配置,如果是单resourcemanager,这里为yarnHaIps="" yarnHaIps="192.168.33.3" # 如果是单 resourcemanager,只需要配置一个主机名称,如果是resourcemanager HA,则默认配置就好 singleYarnIp="192.168.33.3" # common 配置 # 程序路径 programPath="/opt/DolphinScheduler/escheduler-backend" #下载路径 downloadPath="/tmp/escheduler/download" # 任务执行路径 execPath="/tmp/escheduler/exec" # SHELL环境变量路径 shellEnvPath="$installPath/conf/env/.escheduler_env.sh" # 资源文件的后缀 resSuffixs="txt,log,sh,conf,cfg,py,java,sql,hql,xml" # api 配置 # api 服务端口 apiServerPort="12345" 如果使用hdfs相关功能,需要拷贝hdfs-site.xml和core-site.xml到conf目录下:cp $HADOOP_HOME/etc/hadoop/hdfs-site.xml conf/ cp $HADOOP_HOME/etc/hadoop/core-site.xml conf/ 如果 DolphinScheduler 已经安装,则可以通过设置部署的后端服务下的conf/alert.properties文件:#alert type is EMAIL/SMS alert.type=EMAIL # mail server configuration mail.protocol=SMTP # 以网易邮箱为例 mail.server.host=smtp.163.com # SSL协议端口 465/994,非SSL协议端口 25 mail.server.port=465 mail.sender=*******yore@163.com mail.passwd=yore*** # TLS mail.smtp.starttls.enable=false # SSL mail.smtp.ssl.enable=true #xls.file.path=/home/escheduler/xls xls.file.path=/home/escheduler/xls # Enterprise WeChat configuration enterprise.wechat.corp.id=xxxxxxxxxx enterprise.wechat.secret=xxxxxxxxxx enterprise.wechat.agent.id=xxxxxxxxxx enterprise.wechat.users=xxxxx,xxxxx enterprise.wechat.token.url=https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$corpId&corpsecret=$secret enterprise.wechat.push.url=https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$token enterprise.wechat.team.send.msg={\"toparty\":\"$toParty\",\"agentid\":\"$agentId\",\"msgtype\":\"text\",\"text\":{\"content\":\"$msg\"},\"safe\":\"0\"} enterprise.wechat.user.send.msg={\"touser\":\"$toUser\",\"agentid\":\"$agentId\",\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"$msg\"}} 3.1.7 执行脚本一键部署# 1 一键部署并启动 sh install.sh # 2 查看日志 [escheduler@node2 escheduler-backend]$ tree /opt/DolphinScheduler/escheduler-backend/logs /opt/DolphinScheduler/escheduler-backend/logs ├── escheduler-alert.log ├── escheduler-alert-server-node-b.test.com.out ├── escheduler-alert-server.pid ├── escheduler-api-server-node-b.test.com.out ├── escheduler-api-server.log ├── escheduler-api-server.pid ├── escheduler-logger-server-node-b.test.com.out ├── escheduler-logger-server.pid ├── escheduler-master.log ├── escheduler-master-server-node-b.test.com.out ├── escheduler-master-server.pid ├── escheduler-worker.log ├── escheduler-worker-server-node-b.test.com.out ├── escheduler-worker-server.pid └── {processDefinitionId} └── {processInstanceId} └── {taskInstanceId}.log # 3 查看Java进程 # 3.1 node2 # jps -l | grep escheduler [escheduler@node2 escheduler-backend]$ jps 31651 WorkerServer # worker服务 31784 ApiApplicationServer # api服务 31609 MasterServer # master服务 31743 AlertServer # alert服务 31695 LoggerServer # logger服务 # 3.2 node3 [escheduler@cdh3 DolphinScheduler]$ jps 26678 WorkerServer 26718 LoggerServer 错误1:如果查看/opt/DolphinScheduler/escheduler-backend/logs/escheduler-api-server-*.out日志报如下错误nohup: failed to run command ‘/bin/java’: No such file or directory 解决:将JAVA_HOME/bin下的java软连接到/bin下。(每个dolphinscheduler节点都执行)ln -s $JAVA_HOME/bin/java /bin/java 3.1.8 后端服务进程的说明由前面我们可以看到,后端服务正常启动后,共有 5 个进程:WorkerServer、ApiApplicationServer、MasterServer、AlertServer、LoggerServer 。另外还有一个 UI,具体说明如下:3.1.9 dolphinscheduler后端服务启停# 启动 /opt/DolphinScheduler/escheduler-backend/script/start_all.sh # 停止 /opt/DolphinScheduler/escheduler-backend/script/stop_all.sh 3.2 前端部署前端有3种部署方式,分别为自动化部署,手动部署和编译源码部署。这里主要使用自动化脚本方式部署DolphinScheduler前端服务。3.2.1 下载并解压# 1 下载 UI 前端。简称escheduler-ui # 在node2节点下的 /opt/DolphinScheduler wget https://github.com/apache/incubator-dolphinscheduler/releases/download/1.1.0/escheduler-1.1.0-ui.tar.gz # 2 解压 mkdir escheduler-ui tar -zxf escheduler-1.1.0-ui.tar.gz -C escheduler-ui cd escheduler-ui 3.2.2 执行自动化部署脚本执行自动化部署脚本。脚本会提示一些参数,根据提示完成安装。[escheduler@cdh2 escheduler-ui]$ sudo ./install-escheduler-ui.sh 欢迎使用easy scheduler前端部署脚本,目前前端部署脚本仅支持CentOS,Ubuntu 请在 escheduler-ui 目录下执行 linux 请输入nginx代理端口,不输入,则默认8888 :8888 请输入api server代理ip,必须输入,例如:192.168.xx.xx :192.168.33.6 请输入api server代理端口,不输入,则默认12345 :12345 ================================================= 1.CentOS6安装 2.CentOS7安装 3.Ubuntu安装 4.退出 ================================================= 请输入安装编号(1|2|3|4):2 …… Complete! port option is needed for add FirewallD is not running setenforce: SELinux is disabled 请浏览器访问:http://192.168.33.6:8888 使用自动化部署脚本会检查系统环境是否安装了Nginx,如果没有安装则会通过网络自动下载Nginx包安装,通过引导设置后的Nginx配置文件为/etc/nginx/conf.d/escheduler.conf。但生产环境一般法法访问外网,此时可以通过手动离线安装Nginx,然后进行一些配置即可。# 1 下载 Nginx 离线安装包 # 例如下载 Cento7 CPU指令为 x86版本的 wget http://nginx.org/packages/mainline/centos/7/x86_64/RPMS/nginx-1.17.6-1.el7.ngx.x86_64.rpm # 2 安装 rpm -ivh nginx-1.17.6-1.el7.ngx.x86_64.rpm 下面再手动在Nginx中添加一个DolphinSchedule 服务配置。因为在 /etc/nginx/nginx.conf(Nginx默认加载的配置文件)中有include /etc/nginx/conf.d/*.conf ;,所以我们可以在/etc/nginx/conf.d/下创建一个 conf后缀的配置文件,配置文件的文件名随意,例如叫 escheduler.conf。这里需要特别注意的是在 /etc/nginx/nginx.conf 配置文件中前面有一个配置 user nginx 如果启动Nginx的用户不是 nginx,一定要修改为启动Nginx的用户,否则代理的服务会报 403 的错误。这里我们在/etc/nginx/conf.d/escheduler.conf配置如下内容,重点在 server 中配置 listen(DolphinSchedule Web UI 的端口)、root (解压的escheduler-ui中的dist路径 )、proxy_pass (DolphinSchedule后台接口的地址)等信息。最后重启Nginx执行命令 systemctl restart nginx。server { listen 8888; # 访问端口 server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { root /opt/DolphinScheduler/escheduler-ui/dist; # 上面前端解压的dist目录地址(自行修改) index index.html index.html; } location /escheduler { proxy_pass http://192.168.33.6:12345; # 接口地址(自行修改) proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header x_real_ipP $remote_addr; proxy_set_header remote_addr $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_connect_timeout 4s; proxy_read_timeout 30s; proxy_send_timeout 12s; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } 问题1:上传文件大小限制编辑配置文件 vim /etc/nginx/nginx.conf# 更改上传大小 client_max_body_size 1024m 3.2.3 dolphinscheduler前端服务启停# 1 启动 systemctl start nginx # 2 状态 systemctl status nginx # 3 停止 #nginx -s stop systemctl stop nginx 4. 快速开始视频地址:https://www.bilibili.com/video/BV1d64y1s7eZ4.1 登录浏览器访问http://192.168.33.6:8888,如下图所示。在上述登陆页面默认的账户的用户名为 admin 密码为escheduler123,这个账户也是系统默认的管理员账户,登陆成功后可以修改密码。成功登陆有主页面如下所示4.2 创建的队列、租户创建一个队列:队列管理 -> 创建队列 -> 输入名称和队列值 -> 提交。创建租户:租户管理 -> 创建租户 -> 输入租户编码、租户名称和队列值 -> 提交。创建普通用户:用户管理 -> 创建用户 -> 输入用户名称、密码、租户名和邮箱,手机号选填 -> 提交。创建警告组:警告组管理 -> 创建警告组 -> 输入组名称、组类型(邮件、短信)-> 提交。使用普通用户登录(用户名和密码都是demo)。点击右上角用户名“退出”,重新使用普通用户登录。登陆成功的首页如下。4.3 创建项目点击页面头部的项目管理,进入项目页面,再点击创建项目,创建一个DolphinScheduler任务调度项目,在弹出的框中输入项目名称和描述,例如这里创建一个hello_dolphinScheduler名称的项目,最后点击提交。项目创建完毕后,在项目管理页面点击我们创建的项目,进入该项目的管理页面。点击工作流定义 -> 创建工作流 -> 在左侧工具栏可以选择( SHELL、USB_PROCESS、PROCEDURE、SQL、SPARK、MapReduce、PYTHON、DEPENDENT)。拖拽SHELL节点到画布,新增一个Shell任务,填写 节点名称、描述、脚本 字段;选择 任务优先级 ,级别高的任务在执行队列中会优先执行,相同优先级的任务按照先进先出的顺序执行;超时告警, 填写超时时长,当任务执行时间超过超时时长可以告警并且超时失败。(注意:这里的节点不是机器的节点,而应该是工作流的节点)确认修改完毕后,点击保存,此时设置DAG图名称,选择组租户,最后添加。未上线状态的工作流定义可以编辑,但是不可以运行,所以要执行工作流,需要先上线工作流点击”运行“,执行工作流。4.4 运行参数说明运行参数说明:失败策略:当某一个任务节点执行失败时,其他并行的任务节点需要执行的策略。”继续“表示:其他任务节点正常执行,”结束“表示:终止所有正在执行的任务,并终止整个流程。通知策略:当流程结束,根据流程状态发送流程执行信息通知邮件。流程优先级:流程运行的优先级,分五个等级:最高(HIGHEST),高(HIGH),中(MEDIUM),低(LOW),最低(LOWEST)。级别高的流程在执行队列中会优先执行,相同优先级的流程按照先进先出的顺序执行。worker分组: 这个流程只能在指定的机器组里执行。默认是Default,可以在任一worker上执行。通知组: 当流程结束,或者发生容错时,会发送流程信息邮件到通知组里所有成员。收件人:输入邮箱后按回车键保存。当流程结束、发生容错时,会发送告警邮件到收件人列表。抄送人:输入邮箱后按回车键保存。当流程结束、发生容错时,会抄送告警邮件到抄送人列表。点击任务实例可以查看每个任务的列表信息,点击操作栏,可以看到任务执行的日志信息。5. Worker分组worker分组,提供了一种让任务在指定的worker上运行的机制。管理员创建worker分组,在任务节点和运行参数中设置中可以指定该任务运行的worker分组,如果指定的分组被删除或者没有指定分组,则该任务会在任一worker上运行。worker分组内多个ip地址(不能写别名),以英文逗号分隔。用管理员用户(admin)登陆Web页面,点击 安全中心 -> Worker分组管理,如下图所示。创建Worker分组。填写组名称和IP,IP可以是多个,用英文逗号分割即可。例如下图,我们将Worker的IP分为了两组。6. 添加数据源脚本(一般是SQL脚本)执行时可能会用到一些数据源,例如MySQL、PostgreSQL、Hive、Impala、Spark、ClickHouse、Oracle、SQL Server,通过添加数据源在DolphinScheduler页面编写Job时直接选择,不用再指定驱动、连接、用户名和密码等信息,可以快速创建一个SQL脚本的工作流Job,同时这个数据源时用户隔离的,每个用户添加的数据源相互独立(admin用户除外,管理员用户可以看到所有用户添加的数据源)。下面我们以Impala为例,选择页面头部的 数据源中心 -> 添加数据源,会弹出下图 编辑数据源 弹窗,主要填写如下几项。因为Impala没有设置密码,用户为必填可以任意添加一个,在jdbc连接参数中必须添加 {"auth":"noSasl"}参数,否则会一直等待确认认证。7. 实例在项目管理下,点击工作流定义,在工具栏处选择最后一行的DEPENDENT定义一个带依赖的工作流Job,拖动到编辑面板,设置task的节点名为cdh2-task1,在 Worker分组 中选择执行的Worker节点为cdh2,编辑完这个Task后选择 确认添加。选择执行的Worker分组名,这里选择前面设置的cdh2组,确认添加,如下图所示。同样的方式设置第二个依赖Task,将其Worker分组设置到cdh3节点,并添加依赖为 且。接下来设置两个Shell执行脚本,cdh2-task11上执行task11,主要是在cdh2上执行一个hostname命令,打印执行节点的HostName。同样的方式,在依赖节点cdh3-task21上设置在cdh3执行,也是执行hostname命令。最后再在依赖节点cdh3-task21上添加一个SQL脚本,查询我们的豆瓣电影数据,具体操作如下在工具栏拖拽添加一个SQL脚本Task节点;节点名称可以叫:ck-task01,并添加描述信息;Worker分组:cdh3;数据源:CLICKHOUSE clickhouse-cdh3;sql类型选择查询。√表格;邮件信息:填写主题。收件人邮箱、抄送人邮箱;sql语句:SELECT m.id,m.movie_name,m.rating_num,m.rating_people,q.rank,q.quote FROM movie m LEFT JOIN quote q ON q.id=m.id ORDER BY m.rating_num DESC,m.rating_people DESC LIMIT 10; 各个task编写完毕后,选择右上角的 选择线条连接,工作流编写完毕后如下图,最后点击保存,输入DAG图名称,并选择租户,选择添加保存。回到工作流定义,可以看到新添加的当前用户的所有工作流列表,点击右侧的操作栏的 上线,然后点击 运行 执行我们的工作流。当然这里也可以添加 定时 调度。点击运行后,可以在 工作流实例 页面看到当前运行的Job的状态信息。每个工作可能会有多个Task构成,查看Task的执行信息可以在 任务实例 页面查看,操作栏可以查看这个task的执行日志信息。如果执行成功后,可以选择工作流的甘特图,在时间轴上查看执行状况。也可以查看工作流的执行的树形图信息,如下图。运行成功后填写的收件箱会接收到执行结果的一封邮件,这封邮件中包含了脚本执行的结果。8. 与 Azkaban 的对比9. 小结Apache DolphinScheduler是一个分布式、去中心化、易扩展的可视化DAG工作流任务调度系统,从上面的安装可以看到这个调度系统集成了ZooKeeper,很好的实现了去中心化,每个角色的服务可以起多个,从znode上可以看到masters和workers的一些元信息都注册在了上面,交由ZK去选举,当然它也是一个分布式的。如果某个服务挂了,ZooKeeper会在剩下的其它节点进行选举,例如当某些节点的Worker服务挂了,我们不用做任何处理,DolphinScheduler上依然可以正常提交和执行工作,在它的监控中心的页面可以看到,系统自动选举出了一个新的Work节点。# znode上的信息 [zk: localhost:2181(CONNECTED) 1] ls /escheduler [tasks_queue, dead-servers, masters, lock, workers, tasks_kill] 尤其可以多Worker进行分组以及添加数据源的功能,可以指定Wroker节点,直接指定改用户下的数据,执行SQL脚本,同时页面增加的监控中心、任务状态统计、流程状态统计、流程定义统计等也能很好的帮助我们管理和查看任务执行的信息和集群的状态。
文章
SQL  ·  分布式计算  ·  数据可视化  ·  前端开发  ·  应用服务中间件  ·  Shell  ·  持续交付  ·  调度  ·  nginx  ·  数据安全/隐私保护
2022-10-11
谷粒学院——Day12【整合阿里云短信服务、首页登录和注册】
❤ 作者主页:欢迎来到我的技术博客❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~* 如果文章对您有帮助,记得关注、点赞、收藏、评论⭐️⭐️⭐️ 您的支持将是我创作的动力,让我们一起加油进步吧!!!用户登录业务介绍一、单一服务器模式早期单一服务器,用户认证。缺点:单点性能压力,无法扩展。二、SSO(single sign on)模式分布式,SSO(single sign on)模式 优点:用户身份信息独立管理,更好的分布式管理。可以自己扩展安全策略。缺点:认证服务器访问压力较大。三、Token模式token是按照一定规则生成字符串,包含用户信息。业务流程图{用户访问业务时,必须登录的流程} 优点:无状态: token无状态,session有状态的。基于标准化: 你的API可以采用标准化的 JSON Web Token (JWT)缺点:占用带宽。无法在服务器端销毁。注:基于微服务开发,选择token的形式相对较多,因此使用token作为用户认证的标准。整合JWTtoken是按照一定的规则生成字符串,包含用户信息。JWT就是我们规定好了规则,使用JWT规则可以生成字符串,包含用户信息。一、使用JWT进行跨域身份验证1. 传统用户身份验证 Internet服务无法与用户身份验证分开。一般过程如下:用户向服务器发送用户名和密码。验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中。服务器向用户返回session_id,session信息都会写入到用户的Cookie。用户的每个后续请求都将通过在Cookie中取出session_id传给服务器。服务器收到session_id并对比之前保存的数据,确认用户的身份。这种模式最大的问题是,没有分布式架构,无法支持横向扩展。2. 解决方案session广播。将透明令牌存入cookie,将用户身份信息存入redis。另外一种灵活的解决方案:使用自包含令牌,通过客户端保存数据,而服务器不保存会话数据。 JWT是这种解决方案的代表。 二、JWT令牌1. 访问令牌的类型 2. JWT的组成典型的,一个JWT看起来如下图: 该对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。每一个子串表示了一个功能块,总共有以下三个部分:JWT头、有效载荷和签名哈希。JWT头JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。{ "alg": "HS256", "typ": "JWT" }在上面的代码中,alg属性 表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性 表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。有效载荷有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。iss:发行人 exp:到期时间 sub:主题 aud:用户 nbf:在此之前不可用 iat:发布时间 jti:JWT ID用于标识该JWT除以上默认字段外,我们还可以自定义私有字段,如下例:{ "sub": "1234567890", "name": "Helen", "admin": true }请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。JSON对象也使用Base64 URL算法转换为字符串保存。签名哈希签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret)在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。Base64URL算法如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。作为令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三个字符是"+","/"和"=",由于在URL中有特殊含义,因此Base64URL中对他们做了替换:"="去掉,"+"用"-"替换,"/"用"_"替换,这就是Base64URL算法。 3. JWT的原则JWT的原则是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,如下所示。{ "sub": "1234567890", "name": "Helen", "admin": true }之后,当用户与服务器通信时,客户在请求中发回JSON对象。服务器仅依赖于这个JSON对象来标识用户。为了防止用户篡改数据,服务器将在生成对象时添加签名。服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。 4. JWT的用法客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。 5. JWT问题和趋势JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库存储在客户端,不占用服务端的内存资源JWT默认不加密,但可以加密。生成原始令牌后,可以再次对其进行加密。当JWT未加密时,一些私密数据无法通过JWT传输。JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。JWT本身包含认证信息,token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。三、整合JWT令牌1. 在common_utils模块中添加jwt工具依赖<dependencies> <!-- JWT--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> </dependencies> 2. 创建JWT工具类public class JwtUtils { //token过期时间 public static final long EXPIRE = 1000 * 60 * 60 * 24; //秘钥,每个公司生成规则不一样 public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; //生成token字符串方法 public static String getJwtToken(String id, String nickname) { String JwtToken = Jwts.builder() //设置jwt头信息,红色部分,内容固定,不需要改 .setHeaderParam("typ", "JWT") .setHeaderParam("alg", "HS256") .setSubject("guli-user") .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) //设置过期时间 //设置token主体部分,存储用户信息,可设置多个值 .claim("id", id) .claim("nickname", nickname) //设置签名哈希(防伪标志) .signWith(SignatureAlgorithm.HS256, APP_SECRET) .compact(); return JwtToken; } /** * 判断token是否存在与有效 * @param jwtToken * @return */ public static boolean checkToken(String jwtToken) { if(StringUtils.isEmpty(jwtToken)) return false; try { Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 判断token是否存在与有效 * @param request * @return */ public static boolean checkToken(HttpServletRequest request) { try { String jwtToken = request.getHeader("token"); if(StringUtils.isEmpty(jwtToken)) return false; //根据设置的防伪码解析token Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /** * 根据token获取会员id * @param request * @return */ public static String getMemberIdByJwtToken(HttpServletRequest request) { String jwtToken = request.getHeader("token"); if(StringUtils.isEmpty(jwtToken)) return ""; //根据设置的防伪码解析token,获取对象 Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken); //获取token有效载荷【用户信息】 Claims claims = claimsJws.getBody(); return (String)claims.get("id"); } } 整合阿里云短信服务一、新建短信微服务1. 在service模块下创建子模块service-msm 2. 创建controller和service代码\ 3. 配置application.properties# 服务端口 server.port=8006 # 服务名 spring.application.name=service-msm # mysql数据库连接 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=abc123 # redis配置 spring.redis.host=192.168.152.128 spring.redis.port=6379 spring.redis.database= 0 spring.redis.timeout=1800000 spring.redis.lettuce.pool.max-active=20 spring.redis.lettuce.pool.max-wait=-1 #最大阻塞等待时间(负数表示没限制) spring.redis.lettuce.pool.max-idle=5 spring.redis.lettuce.pool.min-idle=0 #最小空闲 #返回json的全局时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 #mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl4. 创建启动类@ComponentScan({"com.atguigu"}) @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) //取消数据源自动配置 public class MsmApplication { public static void main(String[] args) { SpringApplication.run(MsmApplication.class, args); } }二、阿里云短信服务官方文档:https://help.aliyun.com/product/44282.html?spm=5176.10629532.0.0.38311cbeYzBm73三、编写发送短信接口1. 在service-msm的pom中引入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> </dependency> </dependencies>2. 编写controller@RestController @RequestMapping("/edumsm/msm") @CrossOrigin public class MsmController { @Autowired private MsmService msmService; @Autowired private RedisTemplate<String, String> redisTemplate; //发送短信的方法 @GetMapping("send/{phone}") public R sendMsm(@PathVariable String phone) { // 从redis获取验证码,如果能获取,直接返回 String code = redisTemplate.opsForValue().get(phone); if (!StringUtils.isEmpty(code)) { return R.ok(); } // 获取不到就阿里云发送 // 生成随机数,传递给阿里云进行发送 code = RandomUtil.getFourBitRandom(); Map<String, Object> param = new HashMap<>(); param.put("code", code); // 调用service发送短信的方法 boolean isSend = msmService.send(param, phone); if (isSend) { // 如果发送成功,把发送成功的code验证码保存到redis中,并设置有效时间,设置5分钟内有效 redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES); return R.ok(); } else { return R.error().message("短信发送失败"); } } }3. 编写service@Service public class MsmServiceImpl implements MsmService { //发送短信的方法 @Override public boolean send(Map<String, Object> param, String phone) { if (StringUtils.isEmpty(phone)) return false; //参数1:地域节点 //参数2:AccessKey ID //参数3:AccessKey Secret DefaultProfile profile = DefaultProfile.getProfile("default", "LTAI5tHxdu7qnz8kEVAd7fYc", "vlhzalqMhVeQcZVvHn4hGudffEL7EC"); DefaultAcsClient client = new DefaultAcsClient(profile); // 设置相关固定的参数 CommonRequest request = new CommonRequest(); //request.setProtocol(ProtocolType.HTTPS); request.setSysMethod(MethodType.POST); //提交方式,默认不能改 request.setSysDomain("dysmsapi.aliyuncs.com"); //请求阿里云哪里,默认不能改 request.setSysVersion("2017-05-25"); //版本号 request.setSysAction("SendSms"); //请求哪个方法 // 设置发送相关的参数 request.putQueryParameter("PhoneNumbers",phone); //设置要发送的【手机号】 request.putQueryParameter("SignName","在线教育网站"); //申请阿里云短信服务的【签名名称】 request.putQueryParameter("TemplateCode","SMS_154950909"); //申请阿里云短信服务的【模版中的 模版CODE】 request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param)); //要求传递的code验证码为jason格式,可以使用JSONObject.toJSONString()将map转为json格式 //最终发送 try { CommonResponse response = client.getCommonResponse(request); return response.getHttpResponse().isSuccess(); } catch (Exception e) { e.printStackTrace(); return false; } } }  用户登录注册接口【后端】一、新建用户微服务1. 在service模块下创建子模块service-ucenter 2. 使用代码生成器生成代码创建ucenter_member表# Host: 47.93.118.241:33306 (Version 5.7.21) # Date: 2019-11-15 11:58:11 # Generator: MySQL-Front 6.1 (Build 1.26) # # Structure for table "ucenter_member" # CREATE TABLE `ucenter_member` ( `id` CHAR(19) NOT NULL COMMENT '会员id', `openid` VARCHAR(128) DEFAULT NULL COMMENT '微信openid', `mobile` VARCHAR(11) DEFAULT '' COMMENT '手机号', `password` VARCHAR(255) DEFAULT NULL COMMENT '密码', `nickname` VARCHAR(50) DEFAULT NULL COMMENT '昵称', `sex` TINYINT(2) UNSIGNED DEFAULT NULL COMMENT '性别 1 女,2 男', `age` TINYINT(3) UNSIGNED DEFAULT NULL COMMENT '年龄', `avatar` VARCHAR(255) DEFAULT NULL COMMENT '用户头像', `sign` VARCHAR(100) DEFAULT NULL COMMENT '用户签名', `is_disabled` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否禁用 1(true)已禁用, 0(false)未禁用', `is_deleted` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除', `gmt_create` DATETIME NOT NULL COMMENT '创建时间', `gmt_modified` DATETIME NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='会员表'; # # Data for table "ucenter_member" # INSERT INTO `ucenter_member` VALUES ('1',NULL,'13700000001','96e79218965eb72c92a549dd5a330112','小三123',1,5,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-01 12:11:33','2019-11-08 11:56:01'),('1080736474267144193',NULL,'13700000011','96e79218965eb72c92a549dd5a330112','用户XJtDfaYeKk',1,19,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-02 12:12:45','2019-01-02 12:12:56'),('1080736474355224577',NULL,'13700000002','96e79218965eb72c92a549dd5a330112','用户wUrNkzAPrc',1,27,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-02 12:13:56','2019-01-02 12:14:07'),('1086387099449442306',NULL,'13520191388','96e79218965eb72c92a549dd5a330112','用户XTMUeHDAoj',2,20,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099520745473',NULL,'13520191389','96e79218965eb72c92a549dd5a330112','用户vSdKeDlimn',1,21,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099608825858',NULL,'13520191381','96e79218965eb72c92a549dd5a330112','用户EoyWUVXQoP',1,18,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099701100545',NULL,'13520191382','96e79218965eb72c92a549dd5a330112','用户LcAYbxLNdN',2,24,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099776598018',NULL,'13520191383','96e79218965eb72c92a549dd5a330112','用户dZdjcgltnk',2,25,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099852095490',NULL,'13520191384','96e79218965eb72c92a549dd5a330112','用户wNHGHlxUwX',2,23,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1106746895272849410','o1R-t5u2TfEVeVjO9CPGdHPNw-to',NULL,NULL,'檀梵\'',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/zZfLXcetf2Rpsibq6HbPUWKgWSJHtha9y1XBeaqluPUs6BYicW1FJaVqj7U3ozHd3iaodGKJOvY2PvqYTuCKwpyfQ/132',NULL,0,0,'2019-03-16 10:39:57','2019-03-16 10:39:57'),('1106822699956654081',NULL,NULL,NULL,NULL,NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 15:41:10','2019-03-16 15:41:10'),('1106823035660357634','o1R-t5i4gENwHYRb5lVFy98Z0bdk',NULL,NULL,'GaoSir',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJI53RcCuc1no02os6ZrattWGiazlPnicoZQ59zkS7phNdLEWUPDk8fzoxibAnXV1Sbx0trqXEsGhXPw/132',NULL,0,0,'2019-03-16 15:42:30','2019-03-16 15:42:30'),('1106823041599492098',NULL,NULL,NULL,NULL,NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 15:42:32','2019-03-16 15:42:32'),('1106823115788341250','o1R-t5l_3rnbZbn4jWwFdy6Gk6cg',NULL,NULL,'换个网名哇、',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/jJHyeM0EN2jhB70LntI3k8fEKe7W6CwykrKMgDJM4VZqCpcxibVibX397p0vmbKURGkLS4jxjGB0GpZfxCicgt07w/132',NULL,0,0,'2019-03-16 15:42:49','2019-03-16 15:42:49'),('1106826046730227714','o1R-t5gyxumyBqt0CWcnh0S6Ya1g',NULL,NULL,'我是Helen',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKDRfib8wy7A2ltERKh4VygxdjVC1x5OaOb1t9hot4JNt5agwaVLdJLcD9vJCNcxkvQnlvLYIPfrZw/132',NULL,0,0,'2019-03-16 15:54:28','2019-03-16 15:54:28'),('1106828185829490690','o1R-t5nNlou5lRwBVgGNJFm4rbc4',NULL,NULL,' 虎头',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKxCqRzuYWQmpwiaqQEjNxbC7WicebicXQusU306jgmfoOzUcFg1qaDq5BStiblwBjw5dUOblQ2gUicQOQ/132',NULL,0,0,'2019-03-16 16:02:58','2019-03-16 16:02:58'),('1106830599651442689','o1R-t5hZHQB1cbX7HZJsiM727_SA',NULL,NULL,'是吴啊',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJ9CsqApybcs7f3Dyib9IxIh0sBqJb7LicbjU4WticJFF0PVwFvHgtbFdBwfmk3H2t3NyqmEmVx17tRA/132',NULL,0,0,'2019-03-16 16:12:34','2019-03-16 16:12:34'),('1106830976199278593','o1R-t5meKOoyEJ3-IhWRCBKFcvzU',NULL,NULL,'我才是Helen',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83epMicP9UT6mVjYWdno0OJZkOXiajG0sllJTbGJ9DYiceej2XvbDSGCK8LCF7jv1PuG2uoYlePWic9XO8A/132',NULL,0,0,'2019-03-16 16:14:03','2019-03-16 16:14:03'),('1106831936900415490','o1R-t5jXYSWakGtnUBnKbfVT5Iok',NULL,NULL,'文若姬',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/3HEmJwpSzguqqAyzmBwqT6aicIanswZibEOicQInQJI3ZY1qmu59icJC6N7SahKqWYv24GvX5KH2fibwt0mPWcTJ3fg/132',NULL,0,0,'2019-03-16 16:17:52','2019-03-16 16:17:52'),('1106832491064442882','o1R-t5sud081Qsa2Vb2xSKgGnf_g',NULL,NULL,'Peanut',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 16:20:04','2019-03-16 16:20:04'),('1106833021442510849','o1R-t5lsGc3I8P5bDpHj7m_AIRvQ',NULL,NULL,'食物链终结者',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/MQ7qUmCprK9am16M1Ia1Cs3RK0qiarRrl9y8gsssBjIZeS2GwKSrnq7ZYhmrzuzDwBxSMMAofrXeLic9IBlW4M3Q/132',NULL,0,0,'2019-03-16 16:22:11','2019-03-16 16:22:11'),('1191600824445046786',NULL,'15210078344','96e79218965eb72c92a549dd5a330112','IT妖姬',1,5,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-05 14:19:10','2019-11-08 18:04:43'),('1191616288114163713',NULL,'17866603606','96e79218965eb72c92a549dd5a330112','xiaowu',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-05 15:20:37','2019-11-05 15:20:37'),('1195187659054329857',NULL,'15010546384','96e79218965eb72c92a549dd5a330112','qy',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-15 11:51:58','2019-11-15 11:51:58'); 生成代码public class CodeGenerator { @Test public void run() { // 1、创建代码生成器 AutoGenerator mpg = new AutoGenerator(); // 2、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir("D:\\IDEA\\guli_parent\\service\\service_ucenter" + "/src/main/java"); //输出目录 gc.setAuthor("jyu_zwy"); //作者名 gc.setOpen(false); //生成后是否打开资源管理器 gc.setFileOverride(false); //重新生成时文件是否覆盖 gc.setServiceName("%sService"); //去掉Service接口的首字母I gc.setIdType(IdType.ID_WORKER_STR); //主键策略 gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型 gc.setSwagger2(true);//开启Swagger2模式 mpg.setGlobalConfig(gc); // 3、数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/guli?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("abc123"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 4、包配置 PackageConfig pc = new PackageConfig(); //生成包:com.atguigu.eduservice pc.setModuleName("educenter"); //模块名 pc.setParent("com.atguigu"); //生成包:com.atguigu.controller pc.setController("controller"); pc.setEntity("entity"); pc.setService("service"); pc.setMapper("mapper"); mpg.setPackageInfo(pc); // 5、策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("ucenter_member");//根据数据库哪张表生成,有多张表就加逗号继续填写 strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略 strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀 strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略 strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作 strategy.setRestControllerStyle(true); //restful api风格控制器 strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符 mpg.setStrategy(strategy); // 6、执行 mpg.execute(); } } 3. 配置application.properties# 服务端口 server.port=8006 # 服务名 spring.application.name=service-ucenter # mysql数据库连接 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=123456 # redis spring.redis.host=192.168.152.128 spring.redis.port=6379 spring.redis.database= 0 spring.redis.timeout=1800000 spring.redis.lettuce.pool.max-active=20 spring.redis.lettuce.pool.max-wait=-1 #最大阻塞等待时间(负数表示没限制) spring.redis.lettuce.pool.max-idle=5 spring.redis.lettuce.pool.min-idle=0 #最小空闲 #返回json的全局时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 #配置mapper xml文件的路径 mybatis-plus.mapper-locations=classpath:com/atguigu/educenter/mapper/xml/*.xml #mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 4. 创建启动类@ComponentScan({"com.atguigu"}) @SpringBootApplication @MapperScan("com.atguigu.educenter.mapper") public class UcenterApplication { public static void main(String[] args) { SpringApplication.run(UcenterApplication.class, args); } } 二、创建登录和注册接口1. 创建LoginVo和RegisterVo用于数据封装LoginVo@Data @ApiModel(value="登录对象", description="登录对象") public class LoginVo { @ApiModelProperty(value = "手机号") private String mobile; @ApiModelProperty(value = "密码") private String password; }RegisterVo@Data @ApiModel(value="注册对象", description="注册对象") public class RegisterVo { @ApiModelProperty(value = "昵称") private String nickname; @ApiModelProperty(value = "手机号") private String mobile; @ApiModelProperty(value = "密码") private String password; @ApiModelProperty(value = "验证码") private String code; } 2. 创建controller编写登录和注册方法UcenterMemberController@RestController @CrossOrigin @RequestMapping("/educenter/member") public class UcenterMemberController { @Autowired private UcenterMemberService memberService; // 登录 @PostMapping("login") public R loginUser(@RequestBody LoginVo loginVo) { // 调用service方法实现登录 // 返回token值,使用jwt生成 String token = memberService.login(loginVo); return R.ok().data("token", token); } // 注册 @PostMapping("register") public R registerUser(@RequestBody RegisterVo registerVo) { memberService.register(registerVo); return R.ok(); } } 3. 创建service接口和实现类UcenterMemberServicepublic interface UcenterMemberService extends IService<UcenterMember> { // 登录的方法 String login(LoginVo loginVo); // 注册的方法 void register(RegisterVo registerVo); }UcenterMemberServiceImpl@Service public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService { @Autowired private RedisTemplate<String, String> redisTemplate; // 登录的方法 @Override public String login(LoginVo loginVo) { // 获取登录手机号和密码 String mobile = loginVo.getMobile(); String password = loginVo.getPassword(); // 手机号和密码非空判断 if (StringUtils.isEmpty(password) || StringUtils.isEmpty(mobile)){ throw new GuliException(20001,"手机号或密码为空"); } // 判断手机号是否正确 QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>(); wrapper.eq("mobile", mobile); UcenterMember mobileMember = baseMapper.selectOne(wrapper); // 判断查询对象是否为空 if (mobileMember == null) { // 没有这个手机号 throw new GuliException(20001, "手机号不存在"); } // 判断密码是否正确 // MD5加密是不可逆性的,不能解密,只能加密 //将获取到的密码经过MD5加密与数据库比较 if (!MD5.encrypt(password).equals(mobileMember.getPassword())) { throw new GuliException(20001, "密码错误"); } // 判断是否被禁用 if (mobileMember.getIsDisabled()) { throw new GuliException(20001, "用户被禁用,登录失败"); } // 登录成功 // 生成token字符串, 使用jwt工具类 String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname()); return jwtToken; } // 注册的方法 @Override public void register(RegisterVo registerVo) { // 获取注册的信息 String nickname = registerVo.getNickname(); //昵称 String code = registerVo.getCode(); //验证码 String mobile = registerVo.getMobile(); //手机号 String password = registerVo.getPassword(); //密码 // 非空判断 if (StringUtils.isEmpty(nickname) ||StringUtils.isEmpty(code) ||StringUtils.isEmpty(mobile) ||StringUtils.isEmpty(password)) { throw new GuliException(20001, "注册的数据有空值,注册失败"); } // 判断验证码 // 获取redis验证码 // String redisCode = redisTemplate.opsForValue().get(mobile); // if (!code.equals(redisCode)) { // throw new GuliException(20001, "验证码错误。注册失败"); // } // 直接写死一个验证码 if (!code.equals("1234")) { throw new GuliException(20001, "验证码错误。注册失败"); } // 判断手机号是否重复,表里面存在相同手机号则不进行添加 QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>(); wrapper.eq("mobile", mobile); Integer count = baseMapper.selectCount(wrapper); if (count > 0) { throw new GuliException(20001, "手机号重复,注册失败"); } // 注册成功 // 数据添加到数据库中 UcenterMember member = new UcenterMember(); member.setPassword(MD5.encrypt(password));//密码加密 member.setMobile(mobile); member.setNickname(nickname); member.setIsDisabled(false);//用户不禁用 member.setAvatar("https://zwy-edu.oss-cn-hangzhou.aliyuncs.com/2022/11/1638921791030300677.jpg"); baseMapper.insert(member); } }4. 测试登录  注册  三、创建接口根据token获取用户信息在 MemberApiController 中创建方法:// 根据token获取用户信息 @GetMapping("getMemberInfo") public R getMemberInfo(HttpServletRequest request) { // 调用jwt工具的方法。根据request对象获取头信息,返回用户id String memberId = JwtUtils.getMemberIdByJwtToken(request); // 查询数据库根据用户id获取用户信息 UcenterMember member = memberService.getById(memberId); return R.ok().data("userInfo", member); } 用户登录注册【前端】一、在nuxt环境中安装插件1.安装element-ui 和 vue-qriouslynpm install element-ui npm install vue-qriously2. 修改配置文件 nuxt-swiper-plugin.jsimport Vue from 'vue' import VueAwesomeSwiper from '../node_modules/vue-awesome-swiper/dist/ssr' import VueQriously from 'vue-qriously' import ElementUI from 'element-ui' //element-ui的全部组件 import 'element-ui/lib/theme-chalk/index.css'//element-ui的css Vue.use(ElementUI) //使用elementUI Vue.use(VueQriously) Vue.use(VueAwesomeSwiper)二、用户注册功能前端整合1. 在api文件夹中创建注册的js文件,定义接口register.jsimport request from '@/utils/request' export default{ //根据手机号码发送短信 sendCode(phone){ return request({ url: `/edumsm/msm/send/${phone}`, method: 'get' }) }, // 注册的方法 registerMember(formItem){ return request({ url: `/educenter/member/register`, method: 'post', data: formItem }) }, }2. 在pages文件夹中创建注册页面,调用方法在layouts创建布局页面sign.vue<template> <div class="sign"> <!--标题--> <div class="logo"> <img src="~/assets/img/logo.png" alt="logo" /> </div> <!--表单--> <nuxt /> </div> </template>创建注册页面register.vue<template> <div class="main"> <div class="title"> <a href="/login">登录</a> <span>·</span> <a class="active" href="/register">注册</a> </div> <div class="sign-up-container"> <el-form ref="userForm" :model="params"> <el-form-item class="input-prepend restyle" prop="nickname" :rules="[ { required: true, message: '请输入你的昵称', trigger: 'blur', }, ]" > <div> <el-input type="text" placeholder="你的昵称" v-model="params.nickname" /> <i class="iconfont icon-user" /> </div> </el-form-item> <el-form-item class="input-prepend restyle no-radius" prop="mobile" :rules="[ { required: true, message: '请输入手机号码', trigger: 'blur' }, { validator: checkPhone, trigger: 'blur' }, ]" > <div> <el-input type="text" placeholder="手机号" v-model="params.mobile" /> <i class="iconfont icon-phone" /> </div> </el-form-item> <el-form-item class="input-prepend restyle no-radius" prop="code" :rules="[ { required: true, message: '请输入验证码', trigger: 'blur' }, ]" > <div style="width: 100%; display: block; float: left; position: relative" > <el-input type="text" placeholder="验证码" v-model="params.code" /> <i class="iconfont icon-phone" /> </div> <div class="btn" style="position: absolute; right: 0; top: 6px; width: 40%" > <a href="javascript:" type="button" @click="getCodeFun()" :value="codeTest" style="border: none; background-color: none" >{{ codeTest }}</a > </div> </el-form-item> <el-form-item class="input-prepend" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]" > <div> <el-input type="password" placeholder="设置密码" v-model="params.password" /> <i class="iconfont icon-password" /> </div> </el-form-item> <div class="btn"> <input type="button" class="sign-up-button" value="注册" @click="submitRegister()" /> </div> <p class="sign-up-msg"> 点击 “注册” 即表示您同意并愿意遵守简书 <br /> <a target="_blank" href="http://www.jianshu.com/p/c44d171298ce" >用户协 议</a > 和 <a target="_blank" href="http://www.jianshu.com/p/2ov8x3">隐私政策</a> 。 </p> </el-form> <!-- 更多注册方式 --> <div class="more-sign"> <h6>社交帐号直接注册</h6> <ul> <li> <a id="weixin" class="weixin" target="_blank" href="http://huaan.free.idcfengye.com/api/ucenter/wx/login" ><i class="iconfont icon-weixin" /></a> </li> <li> <a id="qq" class="qq" target="_blank" href="#" ><i class="iconfont icon-qq" /></a> </li> </ul> </div> </div> </div> </template> <script> import "~/assets/css/sign.css"; import "~/assets/css/iconfont.css"; import registerApi from "@/api/register" export default { layout: "sign", //使用页面布局sign.vue data() { return { params: { // 封装注册输入数据 mobile: "", code: "", //验证码 nickname: "", password: "", }, sending: true, //是否发送验证码 second: 60, //倒计时间 codeTest: "获取验证码", }; }, methods: { // 通过输入手机号发送验证码 getCodeFun() { registerApi.sendCode(this.params.mobile) .then(Response => { this.sending = false // 调用倒计时的方法 this.timeDown() }) }, //倒计时 timeDown() { let result = setInterval(() => { --this.second; this.codeTest = this.second; if (this.second < 1) { clearInterval(result); this.sending = true; //this.disabled = false; this.second = 60; this.codeTest = "获取验证码"; } }, 1000); }, // 注册提交的方法 submitRegister() { registerApi.registerMember(this.params) .then(Response => { // 提示注册成功 this.$message({ type: "success", message: "注册成功", }) // 跳转登录页面 this.$router.push({ path: "/login" }) }) }, checkPhone(rule, value, callback) { //debugger if (!/^1[34578]\d{9}$/.test(value)) { return callback(new Error("手机号码格式不正确")); } return callback(); }, }, }; </script> 3. 修改nginx配置文件 4. 测试   三、用户登录功能前端整合1. 在api文件夹中创建登录的js文件,定义接口login.jsimport request from '@/utils/request' export default{ //登录的方法 submitLoginUser(userInfo){ return request({ url: `/educenter/member/login`, method: 'post', data: userInfo }) }, // 根据token获取用户信息 getLoginUserInfo(){ return request({ url: `/educenter/member/getMemberInfo`, method: 'get' }) }, }2. 在pages文件夹中创建登录页面,调用方法安装js-cookie插件npm install js-cookielogin.vue<template> <div class="main"> <div class="title"> <a class="active" href="/login">登录</a> <span>·</span> <a href="/register">注册</a> </div> <div class="sign-up-container"> <el-form ref="userForm" :model="user"> <el-form-item class="input-prepend restyle" prop="mobile" :rules="[ { required: true, message: '请输入手机号码', trigger: 'blur', }, { validator: checkPhone, trigger: 'blur' }, ]" > <div> <el-input type="text" placeholder="手机号" v-model="user.mobile" /> <i class="iconfont icon-phone" /> </div> </el-form-item> <el-form-item class="input-prepend" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]" > <div> <el-input type="password" placeholder="密码" v-model="user.password" /> <i class="iconfont icon-password" /> </div> </el-form-item> <div class="btn"> <input type="button" class="sign-in-button" value="登录" @click="submitLogin()" /> </div> </el-form> <!-- 更多登录方式 --> <div class="more-sign"> <h6>社交帐号登录</h6> <ul> <li> <a id="weixin" class="weixin" target="_blank" href="http://qy.free.idcfengye.com/api/ucenter/weixinLogin/login" ><i class="iconfont icon-weixin" /></a> </li> <li> <a id="qq" class="qq" target="_blank" href="#" ><i class="iconfont icon-qq" /></a> </li> </ul> </div> </div> </div> </template> <script> import "~/assets/css/sign.css"; import "~/assets/css/iconfont.css"; import cookie from "js-cookie"; import loginApi from "@/api/login"; export default { layout: "sign", data() { return { user: { //封装用于登录的用户对象 mobile: "", password: "", }, //用于获取接口传来的token中的对象 loginInfo: {}, }; }, methods: { submitLogin() { loginApi.submitLoginUser(this.user) .then(response => { if (response.data.success) { //把token存在cookie中、也可以放在localStorage中 //参数1:cookie名称,参数2:具体的值,参数3:作用范围 cookie.set('guli_token', response.data.data.token, { domain: 'localhost' }) //登录成功根据token获取用户信息 loginApi.getLoginUserInfo() .then(response => { this.loginInfo = JSON.stringify(response.data.data.userInfo) //将用户信息记录cookie cookie.set("guli_ucenter", this.loginInfo, { domain: "localhost" }) //跳转页面 window.location.href = "/"; //this.$router.push({path:'/'}) }); } }); }, checkPhone(rule, value, callback) { //debugger if (!/^1[34578]\d{9}$/.test(value)) { return callback(new Error("手机号码格式不正确")); } return callback(); }, }, }; </script> <style> .el-form-item__error { z-index: 9999999; } </style> 3. 在request.js添加拦截器,用于传递token信息import axios from 'axios' import { MessageBox, Message } from 'element-ui' import cookie from 'js-cookie' // 创建axios实例 const service = axios.create({ baseURL: 'http://localhost:9001', // api的base_url timeout: 20000 // 请求超时时间 }) // http request 拦截器 service.interceptors.request.use( config => { //debugger //判断cookie中是否有名称叫 guli_token的数据 if (cookie.get('guli_token')) { //把获取到的cookie值放到header中 config.headers['token'] = cookie.get('guli_token'); } return config }, err => { return Promise.reject(err); }) // http response 拦截器 service.interceptors.response.use( response => { //debugger if (response.data.code == 28004) { console.log("response.data.resultCode是28004") // 返回 错误代码-1 清除ticket信息并跳转到登录页面 //debugger window.location.href = "/login" return } else { if (response.data.code !== 20000) { //25000:订单支付中,不做任何提示 if (response.data.code != 25000) { Message({ message: response.data.message || 'error', type: 'error', duration: 5 * 1000 }) } } else { return response; } } }, error => { return Promise.reject(error.response) // 返回接口返回的错误信息 }) export default service4. 修改layouts中的default.vue页面显示登录之后的用户信息<script> import "~/assets/css/reset.css"; import "~/assets/css/theme.css"; import "~/assets/css/global.css"; import "~/assets/css/web.css"; import cookie from "js-cookie"; export default { data() { return { token: "", loginInfo: { id: "", age: "", avatar: "", mobile: "", nickname: "", sex: "", } } }, created() { this.showInfo() }, methods: { // 退出登录 logout() { //清空cookie值 cookie.set("guli_token", "", {domain: "localhost"}) cookie.set("guli_ucenter", "", {domain: "localhost"}) //跳转首页面 window.location.href = "/"; }, //创建方法从cookie中获取信息 showInfo() { //从cookie中获取信息 var userStr = cookie.get("guli_ucenter"); //转字符串转换成json对象(js对象) if (userStr) { this.loginInfo = JSON.parse(userStr); } } } }; </script> default.vue页面显示登录之后的用户信息<!-- / nav --> <ul class="h-r-login"> <li v-if="!loginInfo.id" id="no-login"> <a href="/login" title="登录"> <em class="icon18 login-icon"> </em> <span class="vam ml5">登录</span> </a> <a href="/register" title="注册"> <span class="vam ml5">注册</span> </a> </li> <li v-if="loginInfo.id" id="is-login-one" class="mr10"> <a id="headerMsgCountId" href="#" title="消息"> <em class="icon18 news-icon"> </em> </a> <q class="red-point" style="display: none"> </q> </li> <li v-if="loginInfo.id" id="is-login-two" class="h-r-user"> <a href="/ucenter" title> <img :src="loginInfo.avatar" width="30" height="30" class="vam picImg" alt /> <span id="userName" class="vam disIb">{{ loginInfo.nickname }}</span> </a> <a href="javascript:void(0);" title="退出" @click="logout()" class="ml5" >退 出</a > </li> <!-- /未登录显示第1 li;登录后显示第2,3 li --> </ul>  创作不易,如果有帮助到你,请给文章==点个赞和收藏==,让更多的人看到!!!==关注博主==不迷路,内容持续更新中。
文章
存储  ·  JSON  ·  算法  ·  前端开发  ·  JavaScript  ·  应用服务中间件  ·  API  ·  数据安全/隐私保护  ·  数据格式  ·  微服务
2023-02-25
如何画架构图?有什么是一定要有的,又有什么是不该有的?
架构图可以说是一个程序员的必备技能。做为一个在从业十多年中,画过无数的架构图的IT老司机,我来分享一下如何去画架构图?当我们想用一张或几张图来描述我们的系统时,是不是经常遇到以下情况:对着画布无从下手、删了又来?如何用一张图描述我的系统,并且让产品、运营、开发都能看明白?画了一半的图还不清楚受众是谁?画出来的图到底是产品图功能图还是技术图又或是大杂烩?图上的框框有点少是不是要找点儿框框加进来?布局怎么画都不满意……如果有同样的困惑,本文将介绍一种画图的方法论,来让架构图更清晰。先厘清一些基础概念1、什么是架构?架构就是对系统中的实体以及实体之间的关系所进行的抽象描述,是一系列的决策。架构是结构和愿景。系统架构是概念的体现,是对物/信息的功能与形式元素之间的对应情况所做的分配,是对元素之间的关系以及元素同周边环境之间的关系所做的定义。做好架构是个复杂的任务,也是个很大的话题,本篇就不做深入了。有了架构之后,就需要让干系人理解、遵循相关决策。2、什么是架构图?系统架构图是为了抽象地表示软件系统的整体轮廓和各个组件之间的相互关系和约束边界,以及软件系统的物理部署和软件系统的演进方向的整体视图。3、架构图的作用一图胜千言。要让干系人理解、遵循架构决策,就需要把架构信息传递出去。架构图就是一个很好的载体。那么,画架构图是为了:解决沟通障碍达成共识减少歧义Java 技术资源分享(包括 Java 高阶编程、架构师、SSM、微服务、Spring Cloud 、Spring全家桶)mp.weixin.qq.com/s?__biz=MzI0MDQ4MTM5NQ==&mid=2247520688&idx=2&sn=a13932c38fd16f7cff2d026bc3be673a&chksm=e918f4acde6f7dba3beead28fa0e5c31afaec00ef27b0c48ed426a1b06c75b4b51f5b9d0227b&token=1983967219&lang=zh_CN#rd正在上传…重新上传取消4、架构图分类搜集了很多资料,分类有很多,有一种比较流行的是4+1视图,分别为场景视图、逻辑视图、物理视图、处理流程视图和开发视图。★ 场景视图场景视图用于描述系统的参与者与功能用例间的关系,反映系统的最终需求和交互设计,通常由用例图表示。★ 逻辑视图逻辑视图用于描述系统软件功能拆解后的组件关系,组件约束和边界,反映系统整体组成与系统如何构建的过程,通常由UML的组件图和类图来表示。★ 物理视图物理视图用于描述系统软件到物理硬件的映射关系,反映出系统的组件是如何部署到一组可计算机器节点上,用于指导软件系统的部署实施过程。★ 处理流程视图处理流程视图用于描述系统软件组件之间的通信时序,数据的输入输出,反映系统的功能流程与数据流程,通常由时序图和流程图表示。★ 开发视图开发视图用于描述系统的模块划分和组成,以及细化到内部包的组成设计,服务于开发人员,反映系统开发实施过程。以上 5 种架构视图从不同角度表示一个软件系统的不同特征,组合到一起作为架构蓝图描述系统架构。无论开发、运维,这7种软件架构模式得知道mp.weixin.qq.com/s?__biz=MzI0MDQ4MTM5NQ==&mid=2247520378&idx=1&sn=583a3636d8331a6ab05226214b7a4da5&chksm=e918f566de6f7c704a397590f6f776d7753daa618cd14aa6fa9cd69afde4975b98aaae047773&token=1905487394&lang=zh_CN#rd正在上传…重新上传取消怎样的架构图是好的架构图上面的分类是前人的经验总结,图也是从网上摘来的,那么这些图画的好不好呢?是不是我们要依葫芦画瓢去画这样一些图?先不去管这些图好不好,我们通过对这些图的分类以及作用,思考了一下,总结下来,我们认为,在画出一个好的架构图之前, 首先应该要明确其受众,再想清楚要给他们传递什么信息 ,所以,不要为了画一个物理视图去画物理视图,为了画一个逻辑视图去画逻辑视图,而应该根据受众的不同,传递的信息的不同,用图准确地表达出来,最后的图可能就是在这样一些分类里。那么,画出的图好不好的一个直接标准就是:受众有没有准确接收到想传递的信息。明确这两点之后,从受众角度来说,一个好的架构图是不需要解释的,它应该是自描述的,并且要具备一致性和足够的准确性,能够与代码相呼应。画架构图遇到的常见问题1、方框代表什么?为什么适用方框而不是圆形,它有什么特殊的含义吗?随意使用方框或者其它形状可能会引起混淆。2、虚线、实线什么意思?箭头什么意思?颜色什么意思?随意使用线条或者箭头可能会引起误会。3、运行时与编译时冲突?层级冲突?架构是一项复杂的工作,只使用单个图表来表示架构很容易造成莫名其妙的语义混乱。本文推荐的画图方法C4 模型使用容器(应用程序、数据存储、微服务等)、组件和代码来描述一个软件系统的静态结构。这几种图比较容易画,也给出了画图要点,但最关键的是,我们认为,它明确指出了每种图可能的受众以及意义。下面的案例来自C4官网,然后加上了一些我们的理解,来看看如何更好的表达软件架构1、语境图(System Context Diagram)这是一个想象的待建设的互联网银行系统,它使用外部的大型机银行系统存取客户账户、交易信息,通过外部电邮系统给客户发邮件。可以看到,非常简单、清晰,相信不需要解释,都看的明白,里面包含了需要建设的系统本身,系统的客户,和这个系统有交互的周边系统。★ 用途这样一个简单的图,可以告诉我们,要构建的系统是什么;它的用户是谁,谁会用它,它要如何融入已有的IT环境。这个图的受众可以是开发团队的内部人员、外部的技术或非技术人员。即:构建的系统是什么谁会用它如何融入已有的IT环境★ 怎么画中间是自己的系统,周围是用户和其它与之相互作用的系统。这个图的关键就是梳理清楚待建设系统的用户和高层次的依赖,梳理清楚了画下来只需要几分钟时间。2、容器图(Container Diagram)容器图是把语境图里待建设的系统做了一个展开。上图中,除了用户和外围系统,要建设的系统包括一个基于java\spring mvc的web应用提供系统的功能入口,基于xamarin架构的手机app提供手机端的功能入口,一个基于java的api应用提供服务,一个mysql数据库用于存储,各个应用之间的交互都在箭头线上写明了。看这张图的时候,不会去关注到图中是直角方框还是圆角方框,不会关注是实线箭头还是虚线箭头,甚至箭头的指向也没有引起太多注意。我们有许多的画图方式,都对框、线的含义做了定义,这就需要画图的人和看图的人都清晰的理解这些定义,才能读全图里的信息,而现实是,这往往是非常高的一个要求,所以,很多图只能看个大概的含义。★ 用途这个图的受众可以是团队内部或外部的开发人员,也可以是运维人员。用途可以罗列为:展现了软件系统的整体形态体现了高层次的技术决策系统中的职责是如何分布的,容器间的是如何交互的告诉开发者在哪里写代码★ 怎么画用一个框图来表示,内部可能包括名称、技术选择、职责,以及这些框图之间的交互,如果涉及外部系统,最好明确边界。3、组件图(Component Diagram)组件图是把某个容器进行展开,描述其内部的模块。★ 用途这个图主要是给内部开发人员看的,怎么去做代码的组织和构建。其用途有:描述了系统由哪些组件/服务组成厘清了组件之间的关系和依赖为软件开发如何分解交付提供了框架4、类图(Code/Class Diagram)这个图很显然是给技术人员看的,比较常见,就不详细介绍了。案例分享下面是内部的一个实时数据工具的架构图。作为一个应该自描述的架构图,这里不多做解释了。如果有看不明白的,那肯定是还画的不够好。画好架构图可能有许多方法论,本篇主要介绍了C4这种方法,C4的理论也是不断进化的。但不论是哪种画图方法论,我们回到画图初衷,更好的交流,我们在画的过程中不必被条条框框所限制。简而言之,画之前想好:画图给谁看,看什么,怎么样不解释就看懂。补充:画图的工具有Keynote、Xmind、EdrawMax、Visio、OmniGraffle、Process On……作者:三画,阿里巴巴技术专家,梓敬、鹏升和余乐对此文亦有贡献。三画曾多年从事工作流引擎研发工作,现专注于高并发移动互联网应用的架构和开发。来源:阿里技术  参考:C4官网:https://c4model.com/为什么需要软件架构图:https://www.infoq.cn/article/GhprrUlOYyOqS8*FR1pH书籍:《程序员必读之软件架构》文中物理视图Download地址:Win(http://t.cn/EXAGBDW)、Mac(http://t.cn/EXAqtxI)作者:三画,阿里巴巴技术专家,梓敬、鹏升和余乐对此文亦有贡献。三画曾多年从事工作流引擎研发工作,现专注于高并发移动互联网应用的架构和开发。来源:阿里技术参考:C4官网:https://c4model.com/为什么需要软件架构图:https://www.infoq.cn/article/GhprrUlOYyOqS8*FR1pH书籍:《程序员必读之软件架构》
文章
存储  ·  运维  ·  架构师  ·  Java  ·  程序员  ·  测试技术  ·  UED  ·  微服务  ·  容器  ·  Spring
2023-02-26
Hive安装(图文并茂)
环境阿里云ECS服务器hadoop-2.7.2apache-hive-1.2.1注意、注意、注意:我的hive软件装在  /opt/module/  下,注意一下下面代码执行的目录前提请记住快照一下,无论文章写的在差,也有人点赞,无论文章写的在好,也还是有人看不懂,如果您没有搭建起来,也不至于毁你的环境请记住快照一下,无论文章写的在差,也有人点赞,无论文章写的在好,也还是有人看不懂,如果您没有搭建起来,也不至于毁你的环境请记住快照一下,无论文章写的在差,也有人点赞,无论文章写的在好,也还是有人看不懂,如果您没有搭建起来,也不至于毁你的环境hadoop安装成功并且运行起来,yarn必须启动起来apache-hive-1.2.1-bin.tar.gz下载链接:https://pan.baidu.com/s/1l6t2a3xL2f09GXfKOVJyng 提取码:adsw 复制这段内容后打开百度网盘手机App,操作更方便哦安装步骤将Hive的压缩包上传到Linux系统中的/opt/software中将Hive的压缩包解压到/opt/module中tar -zxvf apache-hive-1.2.1-bin.tar.gz -C /opt/module/进入 /opt/module中,并且将解压的的文件名称改为hive(方便后面写名字)[root@iZm5ea99qngm2v98asii1aZ module]# cd /opt/module/ [root@iZm5ea99qngm2v98asii1aZ module]# ls apache-hive-1.2.1-bin hadoop-2.7.2 [root@iZm5ea99qngm2v98asii1aZ module]# mv apache-hive-1.2.1-bin/ hive [root@iZm5ea99qngm2v98asii1aZ module]# ls hadoop-2.7.2 hive [root@iZm5ea99qngm2v98asii1aZ module]#修改 /exc/profile 文件,配置Hive的环境变量1. #HIVE_HOME 2. export HIVE_HOME=/opt/module/hive 3. export PATH=$PATH:$HIVE_HOME/bin修改前修改后使配置文件生效source /etc/profile在hive中配置hadoop的HADOOP_HOME和HIVE_CONF_DIR进入/opt/module/hive/conf  目录将 hive-env.sh.template 复制一份 并且命名为 hive-env.sh修改hive-env.sh文件其中 HADOOP_HOME的获取方式为HIVE_CONF_DIR的值为修改的这个文件的目录修改前修改后在HDFS上创建几个Hive规定的文件hadoop fs -mkdir /tmp hadoop fs -mkdir -p /user/hive/warehouse hadoop fs -chmod g+w /tmp hadoop fs -chmod g+w /user/hive/warehouse启动Hive进入/opt/module/hive/ 目录下cd /opt/module/hive/启动hivebin/hive退出hive(后面带分号)quit;Hive安装MySQL服务:将hive的数据存储到MySQL中,将hive和MySQL结合起来安装MySQL安装方式一:https://blog.csdn.net/qq_37171353/article/details/80040662安装方式二:用宝塔界面   宝塔面板 - 简单好用的Linux/Windows服务器运维管理面板步骤一:安装宝塔界面,注意看里面最后生成的用户名、密码、URL步骤二:和手机应用宝一样安装MySQL配置MySQL无主机登录目的解决在别的电脑上也能能连接到服务器MySQL解决办法grant all privileges on *.* to root@'%' identified by 'root';flush privileges;这样你就可以在任何机器上输入正确的ip,用户名,密码就可以访问数据库了,前提其他都没有问题(mysql服务开启,端口开放等等)修改hive的配置文件在/opt/module/hive/conf目录下创建一个hive-site.xml在hive-site.xml添加下面信息其中需要修改三处信息:jdbcURL,MySQL的用户名、密码 <value>jdbc:mysql://127.0.0.1:3306/metastore?createDatabaseIfNotExist=true</value> <value>root</value> <value>000000</value><?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://127.0.0.1:3306/metastore?createDatabaseIfNotExist=true</value> <description>JDBC connect string for a JDBC metastore</description> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.jdbc.Driver</value> <description>Driver class name for a JDBC metastore</description> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>root</value> <description>username to use against metastore database</description> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>000000</value> <description>password to use against metastore database</description> </property> <property> <name>hive.cli.print.header</name> <value>true</value> </property> <property> <name>hive.cli.print.current.db</name> <value>true</value> </property> </configuration>上传MySQL驱动(mysql-connector-java-5.1.27-bin.jar)将mysql-connector-java-5.1.27-bin上传到     ( /opt/module/hive/lib)  下mysql-connector-java-5.1.27-bin.jar下载链接链接:https://pan.baidu.com/s/1-qfK3nwrm07tg6zVKk1Vrw 提取码:737r 复制这段内容后打开百度网盘手机App,操作更方便哦初始化schematool(敲黑板)schematool -dbType mysql -initSchema否则会报下面的错误hive配置远程仓库异常:Unable to instantiate Logging initialized using configuration in jar:file:/bigdata/hive/apache-hive-1.2.1-bin/lib/hive-common-1.2.1.jar!/hive-log4j.properties Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient at org.apache.hadoop.hive.ql.session.SessionState.start(SessionState.java:522) at org.apache.hadoop.hive.cli.CliDriver.run(CliDriver.java:677) at org.apache.hadoop.hive.cli.CliDriver.main(CliDriver.java:621) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.hadoop.util.RunJar.run(RunJar.java:239) at org.apache.hadoop.util.RunJar.main(RunJar.java:153) Caused by: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient at org.apache.hadoop.hive.metastore.MetaStoreUtils.newInstance(MetaStoreUtils.java:1523) at org.apache.hadoop.hive.metastore.RetryingMetaStoreClient.<init>(RetryingMetaStoreClient.java:86) at org.apache.hadoop.hive.metastore.RetryingMetaStoreClient.getProxy(RetryingMetaStoreClient.java:132) at org.apache.hadoop.hive.metastore.RetryingMetaStoreClient.getProxy(RetryingMetaStoreClient.java:104) at org.apache.hadoop.hive.ql.metadata.Hive.createMetaStoreClient(Hive.java:3005) at org.apache.hadoop.hive.ql.metadata.Hive.getMSC(Hive.java:3024) at org.apache.hadoop.hive.ql.session.SessionState.start(SessionState.java:503) ... 8 more Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at org.apache.hadoop.hive.metastore.MetaStoreUtils.newInstance(MetaStoreUtils.java:1521) ... 14 more Caused by: javax.jdo.JDOException: Exception thrown calling table.exists() for hive.`SEQUENCE_TABLE` NestedThrowables: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Specified key was too long; max key length is 767 bytes at org.datanucleus.api.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:596) at org.datanucleus.api.jdo.JDOPersistenceManager.jdoMakePersistent(JDOPersistenceManager.java:732) at org.datanucleus.api.jdo.JDOPersistenceManager.makePersistent(JDOPersistenceManager.java:752) at org.apache.hadoop.hive.metastore.ObjectStore.setMetaStoreSchemaVersion(ObjectStore.java:6773) at org.apache.hadoop.hive.metastore.ObjectStore.checkSchema(ObjectStore.java:6670) at org.apache.hadoop.hive.metastore.ObjectStore.verifySchema(ObjectStore.java:6645) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.hadoop.hive.metastore.RawStoreProxy.invoke(RawStoreProxy.java:114) at com.sun.proxy.$Proxy6.verifySchema(Unknown Source) at org.apache.hadoop.hive.metastore.HiveMetaStore$HMSHandler.getMS(HiveMetaStore.java:572) at org.apache.hadoop.hive.metastore.HiveMetaStore$HMSHandler.createDefaultDB(HiveMetaStore.java:624) at org.apache.hadoop.hive.metastore.HiveMetaStore$HMSHandler.init(HiveMetaStore.java:461) at org.apache.hadoop.hive.metastore.RetryingHMSHandler.<init>(RetryingHMSHandler.java:66) at org.apache.hadoop.hive.metastore.RetryingHMSHandler.getProxy(RetryingHMSHandler.java:72) at org.apache.hadoop.hive.metastore.HiveMetaStore.newRetryingHMSHandler(HiveMetaStore.java:5762) at org.apache.hadoop.hive.metastore.HiveMetaStoreClient.<init>(HiveMetaStoreClient.java:199) at org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient.<init>(SessionHiveMetaStoreClient.java:74) 大功告成启动hivehive运行日志信息配置在  (/opt/module/hive/conf) 里有         hive-log4j.properties.template  复制一份并且重命名为hive-log4j.properties修改hive-log4j.properties文件修改前修改后
文章
SQL  ·  弹性计算  ·  分布式计算  ·  运维  ·  关系型数据库  ·  MySQL  ·  Hadoop  ·  Linux  ·  HIVE  ·  数据安全/隐私保护
2023-02-22
用JAVA实现Email和短信验证(上)
1.用JAVA实现Email验证1.1 Java实现EmailSender.java实现使用java来发送Email。package com.jerry; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.AuthenticationFailedException; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeUtility; import org.apache.commons.lang.StringUtils; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; /** * 实现邮件发送功能 * @author Administrator * */ public class EmailSender { private static final Logger logger = LogManager.getLogger(EmailSender.class); private String host; // 服务器地 private String from; // 发件人 private String to; // 收件人 多个收件人以,分隔 private String title; // 主题 private String content; // 内容 private List<File> attachmentlist ; //附件集 private String username; // 用户名 private String password; // 密码 /**发件人员工编号*/ private String sendEmployeeId; public String getSendEmployeeId() { return sendEmployeeId; } public void setSendEmployeeId(String sendEmployeeId) { this.sendEmployeeId = sendEmployeeId; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public String getTo() { return to; } public void setTo(String to) { this.to = to; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public List<File> getAttachmentlist() { return attachmentlist; } public void setAttachmentlist(List<File> attachmentlist) { this.attachmentlist = attachmentlist; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPort() { return port; } public void setPort(String port) { this.port = port; } private String port; public EmailSender(String host, String from, String to, String title, String content, List attachmentlist, String username, String password,String port) { this.host = host; this.from = from; this.to = to; this.title = title; this.content = content; this.attachmentlist = attachmentlist; this.username = username; this.password = password; this.port=port; } public EmailSender(String to, String title, String content, List attachmentlist) { this.to = to; this.title = title; this.content = content; this.attachmentlist = attachmentlist; } /** * 发送邮件 * @return 发送状态信息 index0:状态 0成功 1失败;index1:描述错误信息 */ public String[] sendMail(){ String[] result=new String[2]; Session session=null; Properties props = System.getProperties(); props.put("mail.smtp.host", host); props.put("mail.smtp.sendpartial", "true"); props.put("mail.smtp.port", port); if(StringUtils.isBlank(username)){//不需要验证用户名密码 session = Session.getDefaultInstance(props, null); }else{ props.put("mail.smtp.auth", "true"); EmailAuthenticator auth = new EmailAuthenticator(username, password); session = Session.getInstance(props, auth); } //设置邮件发送信息 try{ // 创建邮件 MimeMessage message = new MimeMessage(session); // 设置发件人地址 message.setFrom(new InternetAddress(from)); // 设置收件人地址(多个邮件地址) InternetAddress[] toAddr = InternetAddress.parse(to); message.addRecipients(Message.RecipientType.TO, toAddr); // 设置邮件主题 message.setSubject(title); // 设置发送时间 message.setSentDate(new Date()); // 设置发送内容 Multipart multipart = new MimeMultipart(); MimeBodyPart contentPart = new MimeBodyPart(); contentPart.setText(content); multipart.addBodyPart(contentPart); //设置附件 if(attachmentlist!=null && attachmentlist.size()>0){ for(int i = 0 ; i < attachmentlist.size();i++){ MimeBodyPart attachmentPart = new MimeBodyPart(); FileDataSource source = new FileDataSource(attachmentlist.get(i)); attachmentPart.setDataHandler(new DataHandler(source)); attachmentPart.setFileName(MimeUtility.encodeWord(attachmentlist.get(i).getName(), "gb2312", null)); multipart.addBodyPart(attachmentPart); } } message.setContent(multipart); //登录SMTP服务器 if (StringUtils.isBlank(username)) { // 不需验证 Transport.send(message); } else { // 需要验证 Transport transport = session.getTransport("smtp"); transport.connect(); transport.sendMessage(message, message.getAllRecipients()); transport.close(); } result[0]="0"; result[1]="发送成功"; logger.info("邮件发送成功!发送人:"+from); }catch(MessagingException mex){ result[0]="1"; result[1]="邮件服务器发生错误"; if(mex instanceof AuthenticationFailedException){ result[1]="用户名或密码错误"; } } catch (Exception e) { result[0]="1"; result[1]="系统异常"; } return result; } public static void main(String[] args){ String SNMPTServer = "smtp.126.com"; String from="xianggu625@126.com"; String to="xianggu625@126.com"; String title="发送测试报告"; String content="附件为测试报告"; String username="xianggu625@126.com"; String password="VXDUKEHFKHMCKHRT"; String port="25"; List list=new ArrayList(); //list.add(new File("C:\\myjava\\web\\junit.rar")); EmailSender sender=new EmailSender(SNMPTServer,from,to,title,content,list,username,password,port); String [] result = sender.sendMail(); System.out.println(result[1]+"ffffffffffffffff"); } } /** * class MyAuthenticator用于邮件服务器认证 构造器需要用户名、密码作参数 */ class EmailAuthenticator extends Authenticator { private String username = null; private String password = null; public EmailAuthenticator(String username, String password) { this.username = username; this.password = password; } public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }1.2 邮件服务器端设置注意:网易,QQ的邮箱password的设置为下图设置的密码。 用手机扫描后,页面会显示密码。 1.3 HTML代码email.html<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <title>找回密码</title> </head> <form method="post" action="jsp/email.jsp" name="myForm"> 用户名:<input type="text" name="username" maxlength="50" value=""><br> 邮箱:<input type="Email" name="Email" maxlength="50" value=""><br> <input type="submit" value="找回密码"> </form> </body> </html>1.4 jsp实现email.jsp<%@ page contentType="text/html; charset=gb2312" %> <%@ page language="java" %> <%@ page import="java.util.*" %> <%@ page import="java.io.File" %> <%@ page import="java.util.Random" %> <%@ page import="com.mysql.jdbc.Driver" %> <%@ page import="java.sql.*" %> <%@ page import="com.jerry.EmailSender" %> <%@ page import="cn.com.service.*" %> <%@ include file="db/checkfromdb.jsp"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <% String name=request.getParameter("username"); String to=request.getParameter("Email"); String code6 = String.valueOf(new Random().nextInt(999999)); String mycode=code.getSHA256StrJava(code6).toString(); Connection conn = connectDB(); ResultSet rs = getrs(name,to,"","",conn); if(!(rs.getString("mycount")).equals("0")){ String SNMPTServer = "smtp.126.com"; String from="xianggu625@126.com"; String title="找回验证码"; String content="您的验证码为"+code6; String username="xianggu625@126.com"; String password="DXDUYEHFJHMTKHRT"; String port="25"; List list=new ArrayList(); EmailSender sender=new EmailSender(SNMPTServer,from,to,title,content,list,username,password,port); String [] result = sender.sendMail(); if (result[1].equals("发送成功")){ %> <title>输入验证码</title> <script type="text/javascript" src="../js/sh256.js"></script> <script type="text/javascript" > function checkcode() { var my = document.forms["myForm"]["jym"].value; if (my.length!=6){ alert("验证码应该为6位!"); return false; } my = SHA256(my); if(my!="<%=mycode%>"){ alert("验证码错误!"); return false; }else return true; } </script> </head> <body> <form method="post" action="setpassword.jsp" name="myForm" onsubmit="return checkcode()"> 验证码:<input type="number" name="jym" maxlength="50" value=""><br> <input type="submit" value="提交"> </form> </body> </html> <% session.setAttribute("code" , mycode); } session.setAttribute("uername" , name); }else{ out.print("注册的用户名和Email不匹配,请<a href=\"../email.html\">,重新输入</a>"); } %>1.5 使用SH256散列我们对使用到的6位随机密码进行SH256散列,这个方法在code.java中定义。 package cn.com.service; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class code { /** * 利用java原生的摘要实现SHA256加密 * @param str 加密后的报文 * @return */ public static String getSHA256StrJava(String str){ MessageDigest messageDigest; String encodeStr = ""; try { messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(str.getBytes("UTF-8")); encodeStr = byte2Hex(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return encodeStr; } /** * 将byte转为16进制 * @param bytes * @return */ private static String byte2Hex(byte[] bytes){ StringBuffer stringBuffer = new StringBuffer(); String temp = null; for (int i=0;i<bytes.length;i++){ temp = Integer.toHexString(bytes[i] & 0xFF); if (temp.length()==1){ //1得到一位的进行补0操作 stringBuffer.append("0"); } stringBuffer.append(temp); } return stringBuffer.toString(); } }用户输入通过短信收到的6位编码,在JS端进行SH256编码后进行比对。注:更安全的做法可以通过Ajax方法来实现。1.6 安全编码为了安全性,方式用户在发包以后,黑客截包修改Email地址,所以需要获得Email地址后,需要校验是否与该用户注册的Email用户一致,所以在db目录下建立checkfromdb.jsp,完成这个功能。 <%@ page import="com.mysql.jdbc.Driver" %> <%@ page import="java.util.*" %> <%@ page import="java.sql.*" %> <%@ page language="java" %> <%@ page import="com.mysql.jdbc.Driver" %> <%@ page import="java.sql.*" %> <%! Connection connectDB(){ Connection conn=null; try{ //驱动程序名 String driverName="com.mysql.jdbc.Driver"; //数据库用户名 String userName="root"; //密码 String userPasswd="123456"; //数据库名 String dbName="ebusiness"; //联结字符串 String url="jdbc:mysql://localhost/"+dbName+"?user="+userName+"&password="+userPasswd; Class.forName(driverName).newInstance(); conn=DriverManager.getConnection(url); }catch(Exception e) { e.printStackTrace(); } return conn; } ResultSet getrs(String name,String email,String phone,String password,Connection conn){ ResultSet rs = null; try{ //表名 String tableName="goods_user"; String sql = ""; if(password.length()!=0){sql="select count(*) as mycount from "+tableName+" where username=? and password=?";} else if(email.length()!=0){sql="select count(*) as mycount from "+tableName+" where username=? and email=?";} else if(phone.length()!=0){sql="select count(*) as mycount from "+tableName+" where username=? and phone=?";} PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1,name); if(password.length()!=0)ps.setString(2,password); else if(email.length()!=0)ps.setString(2,email); else if(phone.length()!=0)ps.setString(2,phone); rs = ps.executeQuery(); rs.next(); }catch(Exception e) { e.printStackTrace(); } return rs; } %>
文章
安全  ·  JavaScript  ·  前端开发  ·  Java  ·  数据安全/隐私保护
2023-02-14
HttpClient学习整理
HttpClient简介HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。更多信息请关注http://hc.apache.org/HttpClient 功能介绍以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)支持自动转向支持 HTTPS 协议支持代理服务器等应用HttpClient来对付各种顽固的WEB服务器转自:http://blog.csdn.net/ambitiontan/archive/2006/01/06/572171.aspx一般的情况下我们都是使用IE或者Navigator浏览器来访问一个WEB服务器,用来浏览页面查看信息或者提交一些数据等等。所访问的这些页面有的仅仅是一些普通的页面,有的需要用户登录后方可使用,或者需要认证以及是一些通过加密方式传输,例如HTTPS。目前我们使用的浏览器处理这些情况都不会构成问题。不过你可能在某些时候需要通过程序来访问这样的一些页面,比如从别人的网页中“偷”一些数据;利用某些站点提供的页面来完成某种功能,例如说我们想知道某个手机号码的归属地而我们自己又没有这样的数据,因此只好借助其他公司已有的网站来完成这个功能,这个时候我们需要向网页提交手机号码并从返回的页面中解析出我们想要的数据来。如果对方仅仅是一个很简单的页面,那我们的程序会很简单,本文也就没有必要大张旗鼓的在这里浪费口舌。但是考虑到一些服务授权的问题,很多公司提供的页面往往并不是可以通过一个简单的URL就可以访问的,而必须经过注册然后登录后方可使用提供服务的页面,这个时候就涉及到COOKIE问题的处理。我们知道目前流行的动态网页技术例如ASP、JSP无不是通过COOKIE来处理会话信息的。为了使我们的程序能使用别人所提供的服务页面,就要求程序首先登录后再访问服务页面,这过程就需要自行处理cookie,想想当你用java.net.HttpURLConnection来完成这些功能时是多么恐怖的事情啊!况且这仅仅是我们所说的顽固的WEB服务器中的一个很常见的“顽固”!再有如通过HTTP来上传文件呢?不需要头疼,这些问题有了“它”就很容易解决了!我们不可能列举所有可能的顽固,我们会针对几种最常见的问题进行处理。当然了,正如前面说到的,如果我们自己使用java.net.HttpURLConnection来搞定这些问题是很恐怖的事情,因此在开始之前我们先要介绍一下一个开放源码的项目,这个项目就是Apache开源组织中的httpclient,它隶属于Jakarta的commons项目,目前的版本是2.0RC2。commons下本来已经有一个net的子项目,但是又把httpclient单独提出来,可见http服务器的访问绝非易事。Commons-httpclient项目就是专门设计来简化HTTP客户端与服务器进行各种通讯编程。通过它可以让原来很头疼的事情现在轻松的解决,例如你不再管是HTTP或者HTTPS的通讯方式,告诉它你想使用HTTPS方式,剩下的事情交给httpclient替你完成。本文会针对我们在编写HTTP客户端程序时经常碰到的几个问题进行分别介绍如何使用httpclient来解决它们,为了让读者更快的熟悉这个项目我们最开始先给出一个简单的例子来读取一个网页的内容,然后循序渐进解决掉前进中的所有问题。1. 读取网页(HTTP/HTTPS)内容下面是我们给出的一个简单的例子用来访问某个页面/** *最简单的HTTP客户端,用来演示通过GET或者POST方式访问某个页面 *@authorLiudong */ public class SimpleClient { public static void main(String[] args) throws IOException { HttpClient client = new HttpClient(); // 设置代理服务器地址和端口 //client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port); // 使用 GET 方法 ,如果服务器需要通过 HTTPS 连接,那只需要将下面 URL 中的 http 换成 https HttpMethod method=new GetMethod("http://java.sun.com"); //使用POST方法 //HttpMethod method = new PostMethod("http://java.sun.com"); client.executeMethod(method); //打印服务器返回的状态 System.out.println(method.getStatusLine()); //打印返回的信息 System.out.println(method.getResponseBodyAsString()); //释放连接 method.releaseConnection(); } }在这个例子中首先创建一个HTTP客户端(HttpClient)的实例,然后选择提交的方法是GET或者POST,最后在HttpClient实例上执行提交的方法,最后从所选择的提交方法中读取服务器反馈回来的结果。这就是使用HttpClient的基本流程。其实用一行代码也就可以搞定整个请求的过程,非常的简单!2、使用POST方式提交数据(httpClient3)httpclient使用了单独的一个HttpMethod子类来处理文件的上传,这个类就是MultipartPostMethod,该类已经封装了文件上传的细节,我们要做的仅仅是告诉它我们要上传文件的全路径即可,下面这里将给出关于两种模拟上传方式的代码第一种:模拟上传url文件(该方式也适合做普通post请求):/** * 上传url文件到指定URL * @param fileUrl 上传图片url * @param postUrl 上传路径及参数,注意有些中文参数需要使用预先编码 eg : URLEncoder.encode(appName, "UTF-8") * @return * @throws IOException */ public static String doUploadFile(String postUrl) throws IOException { if(StringUtils.isEmpty(postUrl)) return null; String response = ""; PostMethod postMethod = new PostMethod(postUrl); try { HttpClient client = new HttpClient(); client.getHttpConnectionManager().getParams() .setConnectionTimeout(50000);// 设置连接时间 int status = client.executeMethod(postMethod); if (status == HttpStatus.SC_OK) { InputStream inputStream = postMethod.getResponseBodyAsStream(); BufferedReader br = new BufferedReader(new InputStreamReader( inputStream)); StringBuffer stringBuffer = new StringBuffer(); String str = ""; while ((str = br.readLine()) != null) { stringBuffer.append(str); } response = stringBuffer.toString(); } else { response = "fail"; } } catch (Exception e) { e.printStackTrace(); } finally { // 释放连接 postMethod.releaseConnection(); } return response; }第二种:模拟文件上传到指定位置/** * 上传文件到指定URL * @param file * @param url * @return * @throws IOException */ public static String doUploadFile(File file, String url) throws IOException { String response = ""; if (!file.exists()) { return "file not exists"; } PostMethod postMethod = new PostMethod(url); try { //---------------------------------------------- // FilePart:用来上传文件的类,file即要上传的文件 FilePart fp = new FilePart("file", file); Part[] parts = { fp }; // 对于MIME类型的请求,httpclient建议全用MulitPartRequestEntity进行包装 MultipartRequestEntity mre = new MultipartRequestEntity(parts, postMethod.getParams()); postMethod.setRequestEntity(mre); //--------------------------------------------- HttpClient client = new HttpClient(); client.getHttpConnectionManager().getParams() .setConnectionTimeout(50000);// 由于要上传的文件可能比较大 , 因此在此设置最大的连接超时时间 int status = client.executeMethod(postMethod); if (status == HttpStatus.SC_OK) { InputStream inputStream = postMethod.getResponseBodyAsStream(); BufferedReader br = new BufferedReader(new InputStreamReader( inputStream)); StringBuffer stringBuffer = new StringBuffer(); String str = ""; while ((str = br.readLine()) != null) { stringBuffer.append(str); } response = stringBuffer.toString(); } else { response = "fail"; } } catch (Exception e) { e.printStackTrace(); } finally { // 释放连接 postMethod.releaseConnection(); } return response; }3. 处理页面重定向在JSP/Servlet编程中response.sendRedirect方法就是使用HTTP协议中的重定向机制。它与JSP中的的区别在于后者是在服务器中实现页面的跳转,也就是说应用容器加载了所要跳转的页面的内容并返回给客户端;而前者是返回一个状态码,这些状态码的可能值见下表,然后客户端读取需要跳转到的页面的URL并重新加载新的页面。就是这样一个过程,所以我们编程的时候就要通过HttpMethod.getStatusCode()方法判断返回值是否为下表中的某个值来判断是否需要跳转。如果已经确认需要进行页面跳转了,那么可以通过读取HTTP头中的location属性来获取新的地址。下面的代码片段演示如何处理页面的重定向client.executeMethod(post); System.out.println(post.getStatusLine().toString()); post.releaseConnection(); // 检查是否重定向 int statuscode = post.getStatusCode(); if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) || (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) || (statuscode ==HttpStatus.SC_SEE_OTHER) || (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) { // 读取新的 URL 地址 Header header=post.getResponseHeader("location"); if (header!=null){ Stringnewuri=header.getValue(); if((newuri==null)||(newuri.equals(""))) newuri="/"; GetMethodredirect=newGetMethod(newuri); client.executeMethod(redirect); System.out.println("Redirect:"+redirect.getStatusLine().toString()); redirect.releaseConnection(); }else System.out.println("Invalid redirect"); }我们可以自行编写两个JSP页面,其中一个页面用response.sendRedirect方法重定向到另外一个页面用来测试上面的例子。 4. 模拟登录开心网本小节应该说是HTTP客户端编程中最常碰见的问题,很多网站的内容都只是对注册用户可见的,这种情况下就必须要求使用正确的用户名和口令登录成功后,方可浏览到想要的页面。因为HTTP协议是无状态的,也就是连接的有效期只限于当前请求,请求内容结束后连接就关闭了。在这种情况下为了保存用户的登录信息必须使用到Cookie机制。以JSP/Servlet为例,当浏览器请求一个JSP或者是Servlet的页面时,应用服务器会返回一个参数,名为jsessionid(因不同应用服务器而异),值是一个较长的唯一字符串的Cookie,这个字符串值也就是当前访问该站点的会话标识。浏览器在每访问该站点的其他页面时候都要带上jsessionid这样的Cookie信息,应用服务器根据读取这个会话标识来获取对应的会话信息。对于需要用户登录的网站,一般在用户登录成功后会将用户资料保存在服务器的会话中,这样当访问到其他的页面时候,应用服务器根据浏览器送上的Cookie中读取当前请求对应的会话标识以获得对应的会话信息,然后就可以判断用户资料是否存在于会话信息中,如果存在则允许访问页面,否则跳转到登录页面中要求用户输入帐号和口令进行登录。这就是一般使用JSP开发网站在处理用户登录的比较通用的方法。这样一来,对于HTTP的客户端来讲,如果要访问一个受保护的页面时就必须模拟浏览器所做的工作,首先就是请求登录页面,然后读取Cookie值;再次请求登录页面并加入登录页所需的每个参数;最后就是请求最终所需的页面。当然在除第一次请求外其他的请求都需要附带上Cookie信息以便服务器能判断当前请求是否已经通过验证。说了这么多,可是如果你使用httpclient的话,你甚至连一行代码都无需增加,你只需要先传递登录信息执行登录过程,然后直接访问想要的页面,跟访问一个普通的页面没有任何区别,因为类HttpClient已经帮你做了所有该做的事情了,太棒了!下面的例子实现了模拟登陆开心网并向自己好友发送消息的功能。import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.httpclient.params.HttpMethodParams; class Login { public static String loginurl = "https://security.kaixin001.com/login/login_post.php"; static Cookie[] cookies = {}; static HttpClient httpClient = new HttpClient(); static String email = "xxx@qq.com";//你的email static String psw = "xxx";//你的密码 // 消息发送的action String url = "http://www.kaixin001.com/home/"; public static void getUrlContent() throws Exception { HttpClientParams httparams = new HttpClientParams(); httparams.setSoTimeout(30000); httpClient.setParams(httparams); httpClient.getHostConfiguration().setHost("www.kaixin001.com", 80); httpClient.getParams().setParameter( HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8"); PostMethod login = new PostMethod(loginurl); login.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); NameValuePair Email = new NameValuePair("loginemail", email);// 邮箱 NameValuePair password = new NameValuePair("password", psw);// 密码 // NameValuePair code = new NameValuePair( "code" // ,"????");//有时候需要验证码,暂时未解决 NameValuePair[] data = { Email, password }; login.setRequestBody(data); httpClient.executeMethod(login); int statuscode = login.getStatusCode(); System.out.println(statuscode + "-----------"); String result = login.getResponseBodyAsString(); System.out.println(result+"++++++++++++"); cookies = httpClient.getState().getCookies(); System.out.println("==========Cookies============"); int i = 0; for (Cookie c : cookies) { System.out.println(++i + ": " + c); } httpClient.getState().addCookies(cookies); // 当state为301或者302说明登陆页面跳转了,登陆成功了 if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) || (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) || (statuscode == HttpStatus.SC_SEE_OTHER) || (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) { // 读取新的 URL 地址 Header header = login.getResponseHeader("location"); // 释放连接 login.releaseConnection(); System.out.println("获取到跳转header>>>" + header); if (header != null) { String newuri = header.getValue(); if ((newuri == null) || (newuri.equals(""))) newuri = "/"; GetMethod redirect = new GetMethod(newuri); // redirect.setRequestHeader("Cookie", cookies.toString()); httpClient.executeMethod(redirect); System.out.println("Redirect:" + redirect.getStatusLine().toString()); redirect.releaseConnection(); } else System.out.println("Invalid redirect"); } else { // 用户名和密码没有被提交,当登陆多次后需要验证码的时候会出现这种未提交情况 System.out.println("用户没登陆"); System.exit(1); } } public static void sendMsg() throws Exception { // 登录后发消息 System.out.println("*************发消息***********"); String posturl = "http://www.kaixin001.com/msg/post.php"; PostMethod poster = new PostMethod(posturl); poster.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); poster.setRequestHeader("Cookie", cookies.toString()); NameValuePair uids = new NameValuePair("uids", "89600585");// 发送的好友对象的id,此处换成你的好友id NameValuePair content = new NameValuePair("content", "你好啊!");// 需要发送的信息的内容 NameValuePair liteeditor_0 = new NameValuePair("liteeditor_0", "你好啊!");// 需要发送的信息的内容 NameValuePair texttype = new NameValuePair("texttype", "plain"); NameValuePair send_separate = new NameValuePair("send_separate", "0"); NameValuePair service = new NameValuePair("service", "0"); NameValuePair[] msg = { uids, content, texttype, send_separate, service,liteeditor_0 }; poster.setRequestBody(msg); httpClient.executeMethod(poster); String result = poster.getResponseBodyAsString(); System.out.println(result+"++++++++++++"); //System.out.println(StreamOut(result, "iso8859-1")); int statuscode = poster.getStatusCode(); System.out.println(statuscode + "-----------"); if(statuscode == 301 || statuscode == 302){ // 读取新的 URL 地址 Header header = poster.getResponseHeader("location"); System.out.println("获取到跳转header>>>" + header); if (header != null) { String newuri = header.getValue(); if ((newuri == null) || (newuri.equals(""))) newuri = "/"; GetMethod redirect = new GetMethod(newuri); // redirect.setRequestHeader("Cookie", cookies.toString()); httpClient.executeMethod(redirect); System.out.println("Redirect:" + redirect.getStatusLine().toString()); redirect.releaseConnection(); } else System.out.println("Invalid redirect"); } poster.releaseConnection(); } public static String StreamOut(InputStream txtis, String code) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(txtis, code)); String tempbf; StringBuffer html = new StringBuffer(100); while ((tempbf = br.readLine()) != null) { html.append(tempbf + "\n"); } return html.toString(); } }5. 提交XML格式参数提交XML格式的参数很简单,仅仅是一个提交时候的ContentType问题,下面的例子演示从文件文件中读取XML信息并提交给服务器的过程,该过程可以用来测试Web服务。import java.io.File; import java.io.FileInputStream; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.EntityEnclosingMethod; import org.apache.commons.httpclient.methods.PostMethod; /** *用来演示提交XML格式数据的例子 */ public class PostXMLClient { public static void main(String[] args) throws Exception { File input = new File(“test.xml”); PostMethod post = new PostMethod(“http://localhost:8080/httpclient/xml.jsp”); // 设置请求的内容直接从文件中读取 post.setRequestBody( new FileInputStream(input)); if (input.length() < Integer.MAX_VALUE) post.setRequestContentLength(input.length()); else post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED); // 指定请求内容的类型 post.setRequestHeader( "Content-type" , "text/xml; charset=GBK" ); HttpClient httpclient = new HttpClient(); int result = httpclient.executeMethod(post); System.out.println( "Response status code: " + result); System.out.println( "Response body: " ); System.out.println(post.getResponseBodyAsString()); post.releaseConnection(); } }6. 访问启用认证的页面我们经常会碰到这样的页面,当访问它的时候会弹出一个浏览器的对话框要求输入用户名和密码后方可,这种用户认证的方式不同于我们在前面介绍的基于表单的用户身份验证。这是HTTP的认证策略,httpclient支持三种认证方式包括:基本、摘要以及NTLM认证。其中基本认证最简单、通用但也最不安全;摘要认证是在HTTP 1.1中加入的认证方式,而NTLM则是微软公司定义的而不是通用的规范,最新版本的NTLM是比摘要认证还要安全的一种方式。下面例子是从httpclient的CVS服务器中下载的,它简单演示如何访问一个认证保护的页面:import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.methods.GetMethod; public class BasicAuthenticationExample { public BasicAuthenticationExample() { } public static void main(String[] args) throws Exception { HttpClient client = new HttpClient(); client.getState().setCredentials( "www.verisign.com" , "realm" , new UsernamePasswordCredentials( "username" , "password" ) ); GetMethod get = new GetMethod( "https://www.verisign.com/products/index.html" ); get.setDoAuthentication( true ); int status = client.executeMethod( get ); System.out.println(status+ "\n" + get.getResponseBodyAsString()); get.releaseConnection(); } }7. 多线程模式下使用多线程同时访问httpclient,例如同时从一个站点上下载多个文件。对于同一个HttpConnection同一个时间只能有一个线程访问,为了保证多线程工作环境下不产生冲突,httpclient使用了一个多线程连接管理器的类:MultiThreadedHttpConnectionManager,要使用这个类很简单,只需要在构造HttpClient实例的时候传入即可,代码如下:MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();HttpClient client = new HttpClient(connectionManager);以后尽管访问client实例即可。httpClient完整封装HttpInvoke.java:封装了HttpClient调度的必要参数设置,以及post,get等常用方法import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.GetMethod; import java.util.Iterator; import java.util.Map; import java.net.SocketTimeoutException; import java.io.BufferedReader; import java.io.InputStreamReader; public class HttpInvoker { private Log logger = LogFactory.getLog(HttpInvoker.class); private static HttpInvoker httpInvoker = new HttpInvoker(); private HttpClient client = null; private String charset = "gbk"; private int timeout = 10000; private boolean useProxy = false; private String proxyHost = null; private int proxyPort; private String proxyUsername = null; private String proxyPassword = null; private boolean initialized = false; public static HttpInvoker getInstance() { return httpInvoker; } private HttpInvoker() { client = new HttpClient(new MultiThreadedHttpConnectionManager()); client.getParams().setParameter("http.protocol.content-charset", "gbk"); client.getParams().setContentCharset("gbk"); client.getParams().setSoTimeout(timeout); } public HttpInvoker(String charset, int timeout, boolean useProxy, String proxyHost, int proxyPort, String proxyUsername, String proxyPassword) { client = new HttpClient(new MultiThreadedHttpConnectionManager()); if(charset != null && !charset.trim().equals("")) { this.charset = charset; } if(timeout > 0) { this.timeout = timeout; } client.getParams().setParameter("http.protocol.content-charset", charset); client.getParams().setContentCharset(charset); client.getParams().setSoTimeout(timeout); if(useProxy && proxyHost != null && !proxyHost.trim().equals("") && proxyPort > 0) { HostConfiguration hc = new HostConfiguration(); hc.setProxy(proxyHost, proxyPort); client.setHostConfiguration(hc); if (proxyUsername != null && !proxyUsername.trim().equals("") && proxyPassword != null && !proxyPassword.trim().equals("")) { client.getState().setProxyCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxyUsername, proxyPassword)); } } initialized = true; logger.debug("HttpInvoker初始化完成"); } public synchronized void init() { if(charset != null && !charset.trim().equals("")) { client.getParams().setParameter("http.protocol.content-charset", charset); client.getParams().setContentCharset(charset); } if(timeout > 0) { client.getParams().setSoTimeout(timeout); } if(useProxy && proxyHost != null && !proxyHost.trim().equals("") && proxyPort > 0) { HostConfiguration hc = new HostConfiguration(); hc.setProxy(proxyHost, proxyPort); client.setHostConfiguration(hc); if (proxyUsername != null && !proxyUsername.trim().equals("") && proxyPassword != null && !proxyPassword.trim().equals("")) { client.getState().setProxyCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxyUsername, proxyPassword)); } } initialized = true; logger.debug("HttpInvoker初始化完成"); } public String invoke(String url) throws Exception { return invoke(url, null, false); } public String invoke(String url, Map params, boolean isPost) throws Exception { logger.debug("HTTP调用[" + (isPost?"POST":"GET") + "][" + url + "][" + params + "]"); HttpMethod httpMethod = null; String result = ""; try { if(isPost && params != null && params.size() > 0) { Iterator paramKeys = params.keySet().iterator(); httpMethod = new PostMethod(url); NameValuePair[] form = new NameValuePair[params.size()]; int formIndex = 0; while(paramKeys.hasNext()) { String key = (String)paramKeys.next(); Object value = params.get(key); if(value != null && value instanceof String && !value.equals("")) { form[formIndex] = new NameValuePair(key, (String)value); formIndex++; } else if(value != null && value instanceof String[] && ((String[])value).length > 0) { NameValuePair[] tempForm = new NameValuePair[form.length + ((String[])value).length - 1]; for(int i=0; i<formIndex; i++) { tempForm[i] = form[i]; } form = tempForm; for(String v : (String[])value) { form[formIndex] = new NameValuePair(key, (String)v); formIndex++; } } } ((PostMethod)httpMethod).setRequestBody(form); } else { if(params != null && params.size() > 0) { Iterator paramKeys = params.keySet().iterator(); StringBuffer getUrl = new StringBuffer(url.trim()); if(url.trim().indexOf("?") > -1) { if(url.trim().indexOf("?") < url.trim().length()-1 && url.trim().indexOf("&") < url.trim().length()-1) { getUrl.append("&"); } } else { getUrl.append("?"); } while(paramKeys.hasNext()) { String key = (String)paramKeys.next(); Object value = params.get(key); if(value != null && value instanceof String && !value.equals("")) { getUrl.append(key).append("=").append(value).append("&"); } else if(value != null && value instanceof String[] && ((String[])value).length > 0) { for(String v : (String[])value) { getUrl.append(key).append("=").append(v).append("&"); } } } if(getUrl.lastIndexOf("&") == getUrl.length()-1) { httpMethod = new GetMethod(getUrl.substring(0, getUrl.length()-1)); } else { httpMethod = new GetMethod(getUrl.toString()); } } else { httpMethod = new GetMethod(url); } } client.executeMethod(httpMethod); // result = httpMethod.getResponseBodyAsString(); BufferedReader reader = new BufferedReader(new InputStreamReader( httpMethod.getResponseBodyAsStream(),"ISO-8859-1")); String line = null; String html = null; while((line = reader.readLine()) != null){ if(html == null) { html = ""; } else { html += "\r\n"; } html += line; } if(html != null) { result = new String(html.getBytes("ISO-8859-1"), charset); } } catch (SocketTimeoutException e) { logger.error("连接超时[" + url + "]"); throw e; } catch (java.net.ConnectException e) { logger.error("连接失败[" + url + "]"); throw e; } catch (Exception e) { logger.error("连接时出现异常[" + url + "]"); throw e; } finally { if (httpMethod != null) { try { httpMethod.releaseConnection(); } catch (Exception e) { logger.error("释放网络连接失败[" + url + "]"); throw e; } } } return result; } public void setCharset(String charset) { this.charset = charset; } public void setTimeout(int timeout) { this.timeout = timeout; } public void setProxyHost(String proxyHost) { this.proxyHost = proxyHost; } public void setProxyPort(int proxyPort) { this.proxyPort = proxyPort; } public void setProxyUsername(String proxyUsername) { this.proxyUsername = proxyUsername; } public void setProxyPassword(String proxyPassword) { this.proxyPassword = proxyPassword; } public void setUseProxy(boolean useProxy) { this.useProxy = useProxy; } public synchronized boolean isInitialized() { return initialized; } }http访问网络的代理ip和端口,还有使用用户及密码都可以在Spring容器中注入进来:<bean id="httpInvoker" class="HttpInvoker"> <constructor-arg type="java.lang.String" value="gbk" /><!--useProxy--> <constructor-arg type="int" value="10000" /><!--useProxy--> <constructor-arg type="boolean" value="true" /><!--useProxy--> <!--代理地址 --> <constructor-arg type="java.lang.String" value="192.168.1.1" /> <constructor-arg type="int" value="8080" /> <constructor-arg type="java.lang.String" value="" /><!--用户名--> <constructor-arg type="java.lang.String" value="" /><!--密码--> </bean>使用方式:postMap<String,String> params = new HashMap<String,String>(); params.put("check", check); String result = httpInvoker.invoke( "someURL", params, true);使用方式:getString content = httpInvoker.invoke(url);参考资料:httpclient首页:    http://jakarta.apache.org/commons/httpclient/关于NTLM是如何工作:  http://davenport.sourceforge.net/ntlm.html--------------------------------------------HttpClient入门http://blog.csdn.net/ambitiontan/archive/2006/01/07/572644.aspxJakarta Commons HttpClient 学习笔记http://blog.csdn.net/cxl34/archive/2005/01/19/259051.aspxCookies,SSL,httpclient的多线程处理,HTTP方法http://blog.csdn.net/bjbs_270/archive/2004/11/05/168233.aspxHttpClient 学习整理https://download.csdn.net/download/qq_41570658/16036862
文章
XML  ·  安全  ·  Java  ·  应用服务中间件  ·  网络安全  ·  Apache  ·  数据安全/隐私保护  ·  数据格式  ·  容器  ·  Spring
2023-02-23
Java成品网站推荐 毕设从这起步就够了
【【雷霆战机】】〖http://pan.baidu.com/s/1kVstszX〗《解压源码后直接用AIDE打开就好,然后运行就ok了》【【跳转QQ源码】】〖http://pan.baidu.com/s/1cwDim6〗《今天给大家带来一个aide跳转qq的源码,以前我发的教程,很多人都不会,有些人想要源码,今天我写出来了,你们只需要用aide导入,把账号修改成你们的就可以了》【【跟随手指的小球】】〖http://pan.baidu.com/s/1kUVWaBd〗《面中的黑色小圆点会跟随手指的移动而移动》【【动态背景】】〖http://pan.baidu.com/s/1sl5BBHV〗《装逼必备,界面中的文字可以随意更换》【【高仿消灭星星】】〖http://pan.baidu.com/s/1gfh9QGN〗《java源码,无介绍说明哦!》【【HTLM编辑器】】〖http://pan.baidu.com/share/link?shareid=188250359&uk=3431096586〗【【AIDE两种更新】】〖http://pan.baidu.com/share/link?shareid=3023756445&uk=3431096586〗《简单的示范两种更新例子》【【魔法粒子】】〖http://pan.baidu.com/share/link?shareid=4006168674&uk=3431096586〗【【top圈圈助手】】〖http://pan.baidu.com/share/link?shareid=1565546939&uk=3431096586〗《接口如果失效了,打开http://wookat.cn/  刷取》【【情人节礼物】】〖http://pan.baidu.com/share/link?shareid=1489720338&uk=3431096586〗《需要的工具有:AIDE,下载链接: http://pan.baidu.com/share/link?shareid=63279186&uk=3431096586 €€使用教程:€  下载好我发的源码后,解压,然后用AIDE导入,修改里面的内容,然后打包,发给亲爱的TA€€资源介绍:€这是一个表白软件,修改好后,送给亲爱的TA给TA一个惊喜相信他(她)肯定会感动的。》【【com.SpringDay.zfgx】】〖http://pan.baidu.com/s/1eSlECU2〗《主要工具类在kill里面€按钮的美化在res/color/color里面》【【企信通ElM】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=552474239424469&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【夏普名片扫描】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=500423251934994&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【录音机源码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=812097814418497&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【手机卫士】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=572319775708173&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【星座连萌】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=672316151308309&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【生日管家】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=113399222159085&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【股文邦】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=978909745126791&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【课程表】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=523724777297508&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【高仿丁丁优惠券】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=368008904193281&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【高仿人人 网客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=76152012177785&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【高仿墨迹天气】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=836473590789976&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【高仿爱奇艺】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=978633456941342&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【高仿开心网】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=479173425428666&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【仿微信】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=705134680128133&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【高仿陌陌】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=415144182922850&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【魔音盒】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=668815289730444&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【安卓应用市场】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=106794552589&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【模仿网易新闻自动刷新】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=427289257982214&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【午叉的音乐播放器】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=1093735604638296&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【花香短信大全】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=293719267920040&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【EOE论坛客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=889760707749330&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Fanfoudroid(饭否网开源项目)】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=379331335003408&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【ImiFirewall】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=436757412146781&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MIUI便签】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=668561512185537&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MIUI录音机】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=1011055177576786&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MIUI指南针】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=634099436673190&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MIUI文件管理器】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=863951322729346&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MSD音乐播放器】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=1094279157247724&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Myfood订餐】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=112103476720438&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Oschina客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=477899329994896&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【PM25】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=659336380626611&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【PWP简洁大日历】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=296802380865698&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【PushMessage百度的】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=101079427943024&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Xabber客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=996288035051813&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【qiyi(高仿奇艺全套UI)】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=182642344029911&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【仿腾讯通讯录管理】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=737547992002140&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【便签软件源码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=1079800639586908&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【博客园客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=633424479271229&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【百度推聊】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=390911920510186&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【饭否网】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=514318205725698&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【173个java项目源码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=424321406214861&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《请确保手机有一定的存储空间。》【【18个IOS源码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=366683527539396&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《请确保手机有一定的存储空间。》【【应用源码之实现远程控制PC源代码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=12501666866568&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【应用源码之手机控制电脑wirelesskeyboard1.4(手机端源码)】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=617736127523408&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Android版FTP服务器SwiFTP源代码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=306349822897974&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Android高级应用源码和思维导图】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=291581583963511&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【RadialMenuWidget_20803】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=1031982718500785&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Apollo播放器】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=715094276297491&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【CBReader资讯阅读】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=243067847596592&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【stage2(Android高级应用开发-基础篇_上)】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=608782167935965&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《请确保手机有一定的存储空间。》【【top红包辅助】】〖https://pan.baidu.com/wap/link?&shareid=1357414562&uk=3431096586〗《一款自动快速的抢红包源码》【【Bmob登录注册功能实现!】】〖http://pan.baidu.com/s/1hsQlsXI〗《€Bmob登录注册源码并非本地的,它是采用Bmob后端云实现的。€€推荐大家使用Bmob后端云,免费好用!€€此源码需要使用解压工具解压,解压后打开AIDE导入项目。€€打开这个项目,你会有很多错误,这是因为你没有导库!€€另外,因为这个源码是采用Bmob后端云实现的,大家需要百度Bmob,注册一个账号,在Bmob后台创建一个项目,复制项目的ID替换源码中的ID。》【【底部导航栏源码】】〖http://pan.baidu.com/s/1nvHT5l3〗《资源介绍:啥都不说,就是炫酷!€€使用说明:此源码需要使用ZArchiver解压,ZArchiver,别名是超级解压缩工具,百度能找到。》【【毒皇工具箱源码】】〖http://pan.baidu.com/s/1mi7LuiO〗《毒皇工具箱源码集成多种功能丶特效,需要的就下载吧!€€使用说明:此源码需要使用ZArchiver解压,ZArchiver,别名是超级解压缩工具,百度能找到,我就不发链接了。€€解压密码:DuHuang666€€需要的库:Google官方Maven运行库.zip€http://pan.baidu.com/s/1geXxI75》【【QQ强制聊天】】〖http://pan.baidu.com/share/link?shareid=2232764045&uk=1383490902〗《点击界面会有绚丽圈圈!》【【Android取标题和全屏】】〖http://pan.baidu.com/share/link?shareid=1344342784&uk=3431096586〗《暂无介绍》〔YCode管理员〕【【Android延时执行方法】】〖http://pan.baidu.com/share/link?shareid=717626294&uk=3431096586〗《暂无介绍》〔YCode管理员〕【【本地注册】】〖http://pan.baidu.com/share/link?shareid=865795361&uk=3431096586〗《暂无介绍》〔YCode管理员〕【【wifi密码查看】】〖http://pan.baidu.com/share/link?shareid=3281296219&uk=1300544377〗《仅供学习》【【虚拟定位】】〖http://pan.baidu.com/share/link?shareid=2427999502&uk=1300544377〗《仅供学习》[2.38MB]¥开启¥【【跟随手指的小球】】〖http://pan.baidu.com/s/1kUVWaBd〗《面中的黑色小圆点会跟随手指的移动而移动》[22.85MB]【【动态背景】】〖http://pan.baidu.com/s/1sl5BBHV〗《装逼必备,界面中的文字可以随意更换》[437.70kb]【【高仿消灭星星】】〖http://pan.baidu.com/s/1gfh9QGN〗《java源码,无介绍说明哦!》[665.39KB]【【HTLM编辑器】】〖http://pan.baidu.com/share/link?shareid=188250359&uk=3431096586〗《暂无介绍》【【AIDE两种更新】】〖http://pan.baidu.com/share/link?shareid=3023756445&uk=3431096586〗《简单的示范两种更新例子》【【魔法粒子】】〖http://pan.baidu.com/share/link?shareid=4006168674&uk=3431096586〗《暂无介绍》【【top圈圈助手】】〖http://pan.baidu.com/share/link?shareid=1565546939&uk=3431096586〗《接口如果失效了,打开http://wookat.cn/  刷取》【【情人节礼物】】〖http://pan.baidu.com/share/link?shareid=1489720338&uk=3431096586〗《需要的工具有:AIDE,下载链接: http://pan.baidu.com/share/link?shareid=63279186&uk=3431096586 €€使用教程:€  下载好我发的源码后,解压,然后用AIDE导入,修改里面的内容,然后打包,发给亲爱的TA€€资源介绍:€这是一个表白软件,修改好后,送给亲爱的TA给TA一个惊喜相信他(她)肯定会感动的。》[11.9MB]【【com.SpringDay.zfgx】】〖http://pan.baidu.com/s/1eSlECU2〗《主要工具类在kill里面€按钮的美化在res/color/color里面》『0』[60k]【【企信通ElM】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=552474239424469&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[10.26MB]【【夏普名片扫描】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=500423251934994&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[8.04MB]【【录音机源码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=812097814418497&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[2.33MB]【【手机卫士】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=572319775708173&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[11.38mb]【【星座连萌】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=672316151308309&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[4.98MB]【【生日管家】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=113399222159085&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[10.41MB]【【股文邦】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=978909745126791&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[7.08MB]【【课程表】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=523724777297508&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[11.38MB]【【高仿丁丁优惠券】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=368008904193281&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[2.05MB]【【高仿人人 网客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=76152012177785&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[6.31MB]【【高仿墨迹天气】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=836473590789976&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[7.75MB]【【高仿爱奇艺】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=978633456941342&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【高仿开心网】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=479173425428666&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[58.49MB]【【仿微信】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=705134680128133&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[3.33MB]【【高仿陌陌】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=415144182922850&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[17.36MB]【【魔音盒】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=668815289730444&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【安卓应用市场】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=106794552589&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【模仿网易新闻自动刷新】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=427289257982214&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【午叉的音乐播放器】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=1093735604638296&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【花香短信大全】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=1&third=0&fsid=293719267920040&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【EOE论坛客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=889760707749330&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Fanfoudroid(饭否网开源项目)】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=379331335003408&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【ImiFirewall】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=436757412146781&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MIUI便签】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=668561512185537&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MIUI录音机】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=1011055177576786&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MIUI指南针】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=634099436673190&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MIUI文件管理器】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=863951322729346&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【MSD音乐播放器】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=1094279157247724&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Myfood订餐】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=112103476720438&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Oschina客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=477899329994896&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【PM25】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=659336380626611&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【PWP简洁大日历】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=296802380865698&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【PushMessage百度的】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=101079427943024&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Xabber客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=996288035051813&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【qiyi(高仿奇艺全套UI)】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=182642344029911&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【仿腾讯通讯录管理】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=737547992002140&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【便签软件源码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=1079800639586908&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【博客园客户端】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=633424479271229&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【百度推聊】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=390911920510186&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【饭否网】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=2&third=0&fsid=514318205725698&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【173个java项目源码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=424321406214861&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《请确保手机有一定的存储空间。》【【18个IOS源码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=366683527539396&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《请确保手机有一定的存储空间。》【【应用源码之实现远程控制PC源代码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=12501666866568&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【应用源码之手机控制电脑wirelesskeyboard1.4(手机端源码)】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=617736127523408&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Android版FTP服务器SwiFTP源代码】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=306349822897974&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》[813.06KB]【【Android高级应用源码和思维导图】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=291581583963511&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【RadialMenuWidget_20803】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=1031982718500785&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【Apollo播放器】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=715094276297491&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【CBReader资讯阅读】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=243067847596592&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《源码来自网络,暂无详细说明。》【【stage2(Android高级应用开发-基础篇_上)】】〖https://pan.baidu.com/wap/shareview?shareid=3450142049&uk=1300544377&page=3&third=0&fsid=608782167935965&num=20&dir=%2F%E6%88%91%E7%9A%84%E8%B5%84%E6%BA%90%2F%E6%B2%90%E6%99%A8%E8%B5%84%E6%BA%90(%E6%96%B0)%2FAndroid%E6%BA%90%E7%A0%815000%E5%A5%97%2F%E6%96%B0%E5%A2%9E%2050%E5%A5%97〗《请确保手机有一定的存储空间。》【【top红包辅助】】〖https://pan.baidu.com/wap/link?&shareid=1357414562&uk=3431096586〗《一款自动快速的抢红包源码》【【Bmob登录注册功能实现!】】〖http://pan.baidu.com/s/1hsQlsXI〗《€Bmob登录注册源码并非本地的,它是采用Bmob后端云实现的。€€推荐大家使用Bmob后端云,免费好用!€€此源码需要使用解压工具解压,解压后打开AIDE导入项目。€€打开这个项目,你会有很多错误,这是因为你没有导库!€€另外,因为这个源码是采用Bmob后端云实现的,大家需要百度Bmob,注册一个账号,在Bmob后台创建一个项目,复制项目的ID替换源码中的ID。》【【底部导航栏源码】】〖http://pan.baidu.com/s/1nvHT5l3〗《资源介绍:啥都不说,就是炫酷!€€使用说明:此源码需要使用ZArchiver解压,ZArchiver,别名是超级解压缩工具,百度能找到。》【【毒皇工具箱源码】】〖http://pan.baidu.com/s/1mi7LuiO〗《毒皇工具箱源码集成多种功能丶特效,需要的就下载吧!€€使用说明:此源码需要使用ZArchiver解压,ZArchiver,别名是超级解压缩工具,百度能找到,我就不发链接了。€€解压密码:DuHuang666€€需要的库:Google官方Maven运行库.zip€http://pan.baidu.com/s/1geXxI75》【【QQ强制聊天】】〖http://pan.baidu.com/share/link?shareid=2232764045&uk=1383490902〗《点击界面会有绚丽圈圈!》【【Android取标题和全屏】】〖http://pan.baidu.com/share/link?shareid=1344342784&uk=3431096586〗《暂无介绍》【【Android延时执行方法】】〖http://pan.baidu.com/share/link?shareid=717626294&uk=3431096586〗《暂无介绍》【【本地注册】】〖http://pan.baidu.com/share/link?shareid=865795361&uk=3431096586〗《暂无介绍》【【wifi密码查看】】〖http://pan.baidu.com/share/link?shareid=3281296219&uk=1300544377〗《仅供学习》【【虚拟定位】】〖http://pan.baidu.com/share/link?shareid=2427999502&uk=1300544377〗《仅供学习》
文章
Java  ·  Maven  ·  Android开发  ·  数据安全/隐私保护  ·  iOS开发
2023-02-12
...
跳转至:
开发与运维
5775 人关注 | 133431 讨论 | 319214 内容
+ 订阅
  • Yii2.0框架中的Active Record和数据访问对象(DAO)有什么区别?在实际开发中该如何选择?
  • Yii2.0框架的MVC架构是如何设计的?它的流程是什么样的?
  • Yii2.0框架是什么?它有哪些主要特点和优点?
查看更多 >
安全
1243 人关注 | 24148 讨论 | 85779 内容
+ 订阅
  • Yii2.0框架是什么?它有哪些主要特点和优点?
  • Python 异步: 检查网站状态示例(21)
  • Docker常用命令gr
查看更多 >
大数据
188710 人关注 | 30983 讨论 | 83892 内容
+ 订阅
  • Yii2.0框架的MVC架构是如何设计的?它的流程是什么样的?
  • 2023年A系列人工智能领域文章计划和简介
  • Docker常用命令gr
查看更多 >
云原生
234323 人关注 | 11606 讨论 | 47366 内容
+ 订阅
  • Docker常用命令gr
  • DOCKER安装及DOCKER可视化安装
  • 硬核测评!三款开发者常用的主机远程管理软件
查看更多 >
微服务
23046 人关注 | 11735 讨论 | 35097 内容
+ 订阅
  • GaoGaoWeb
  • Dubbo + ZooKeeper丨如何解决线上故障排查链路长的难题
  • 如何实现对 Oracle 的实时数据捕获和性能调优|Flink CDC 专题
查看更多 >