app engine_App Engine中的Google Services身份验证,第1部分

app engine

这篇文章将说明如何构建一个简单的Google App Engine(GAE)Java应用程序,该应用程序可以针对Google进行身份验证,并利用Google的OAuth授权访问Google的API服务(例如Google Docs)。 此外,在Google已经提供的一些示例的基础上,还将说明如何使用App Engine 数据存储区和Objectify持久化数据。

项目源代码

这篇文章背后的动机是,我很难找到以前将这些技术真正联系在一起的任何示例。 但是,这些技术确实代表了许多想要利用大量Google API服务的Web应用程序的基础。
为了简单起见,该演示仅允许用户通过Google域登录。 授权访问用户的Google文档服务; 并显示用户的Google Docs Word和电子表格文档的列表。 在本教程中,我确实对读者的专业知识做出了一些假设,例如对Java的非常熟悉。

流程概述

在我们直接进入教程/演示之前,让我们简要介绍一下导航流程。

尽管它看起来很复杂,但是主要流程可以总结为:

  1. 用户请求访问listFiles.jsp(实际上可以使用任何JSP页面)。
  2. 检查用户是否已登录Google。 如果不是,它们将被重定向到Google登录页面-登录后,它们将返回。
  3. 然后进行检查以确定用户是否存储在本地数据存储中。 如果没有,则将用户与用户的Google域电子邮件地址一起添加。
  4. 接下来,我们检查用户是否已将OAuth凭据授予了Google Docs API服务。 如果不是,则启动OAuth身份验证过程。 授予OAuth凭据后,它们将存储在本地用户表中(因此我们不必在每次用户尝试访问服务时都询问)。
  5. 最后,显示Google文档电子表格或Word文档的列表。

可以使用相同的方法来访问其他Google服务,例如YouTube(例如,您可以显示用户喜欢的视频的列表)。

环境设定

对于本教程,我使用以下内容:

  • Eclipse Indigo Service Release 2以及适用于Eclipse的Google插件(请参阅设置说明 )。
  • Google GData Java SDK Eclipse插件版本1.47.1(请参阅设置说明 )。
  • Google App Engine 1.6.5版。 早期版本存在一些问题,因此我建议确保您正在使用它。 它应该作为Eclipse的Google插件的一部分自动安装。
  • Objectify版本3.1。 所需的库已安装在项目的war / WEB-INF / lib目录中。

将项目导入Eclipse之后,您的构建路径应类似于:

App Engine设置应类似于:

您将需要设置自己的GAE应用程序以及指定自己的应用程序ID(请参阅Google GAE开发人员文档 )。
我见过的最好的教程介绍了如何使用OAuth访问Google API服务,可以在这里找到。 我发现最令人困惑的方面之一是如何获取放置OAuth请求时所需的必需的消费者密钥和消费者秘密值。 我完成此操作的方式是:

  1. 使用GAE管理控制台创建GAE应用程序。 您将需要创建自己的应用程序ID(只是您的Web应用程序的名称)。 有了它,您将在上面显示的Eclipse App Engine设置面板中更新您的应用程序ID。
  2. 为该应用程序创建一个新的域。 例如,由于我的应用程序ID在上面指定为“ tennis-coachrx”,因此我将目标URL路径前缀配置为:http://tennis-coachrx.appspot.com/authSub。 您将看到我们如何配置该Servlet以便很快接收凭证。
  3. 为了完成域名注册,Google将为您提供可以上传HTML文件。 将该文件的根路径包括在/ src / war目录下,然后将应用程序上载到GAE。 这样,当Google运行检查时,该文件将存在并生成必要的消费者凭证。 这是设置完成后的屏幕截图:

拥有OAuth使用者密钥OAuth使用者密钥之后 ,您将替换com.zazarie.shared.Constant文件中的以下值:
最后的静态字符串CONSUMER_KEY =''; 最后的静态字符串CONSUMER_SECRET ='';
哇,这似乎是很多工作! 但是,这是一次性交易,您不必再次大惊小怪。

代码演练

现在,我们已经摆脱了OAuth配置/设置的麻烦,现在我们可以深入研究代码了。 让我们从查看战争目录的结构开始,您的Web资产位于该目录中:

listFiles.jsp是您首次进入Web应用程序时显示的默认JSP页面。 现在让我们看一下web.xml文件,看看如何配置它,以及对所有内容都至关重要的servlet过滤器。

<?xml version='1.0' encoding='UTF-8'?><web-app xmlns:xsi='http:www.w3.org2001XMLSchema-instance'xsi:schemaLocation='http:java.sun.comxmlnsjavaee http:java.sun.comxmlnsjavaeeweb-app_2_5.xsd'version='2.5' xmlns='http:java.sun.comxmlnsjavaee'><!-- Filters --><filter><filter-name>AuthorizationFilter<filter-name><filter-class>com.zazarie.server.servlet.filter.AuthorizationFilter<filter-class><filter><filter-mapping><filter-name>AuthorizationFilter<filter-name><url-pattern>html*<url-pattern><filter-mapping><!-- Servlets --><servlet><servlet-name>Step2<servlet-name><servlet-class>com.zazarie.server.servlet.RequestTokenCallbackServlet<servlet-class><servlet><servlet-mapping><servlet-name>Step2<servlet-name><url-pattern>authSub<url-pattern><servlet-mapping><!-- Default page to serve --><welcome-file-list><welcome-file>htmllistFiles.jsp<welcome-file><welcome-file-list><web-app>

每当请求位于html目录中的JSP文件时,就会调用称为AuthorizationFilter的servlet过滤器。 我们稍后将介绍的过滤器负责确保用户已登录Google,如果是,则确保已为该用户授予OAuth凭据(即,它将启动OAuth认证过程(如果需要)。

步骤2的Servlet名称代表已授予OAuth凭据时Google调用的Servlet-将其视为回调。 我们将对此进行更详细的介绍。

让我们更详细地看一下AuthorizationFilter。

AuthorizationFilter深潜

doFilter方法是在servlet过滤器中进行工作的地方。 这是实现:

@Overridepublic void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;HttpSession session = request.getSession();LOGGER.info('Invoking Authorization Filter');LOGGER.info('Destination URL is: ' + request.getRequestURI());if (filterConfig == null)return;get the Google userAppUser appUser = LoginService.login(request, response);if (appUser != null) {session.setAttribute(Constant.AUTH_USER, appUser);}identify if user has an OAuth accessToken - it not, will set in motionoauth procedureif (appUser.getCredentials() == null) {need to save the target URI in session so we can forward to it whenoauth is completedsession.setAttribute(Constant.TARGET_URI, request.getRequestURI());OAuthRequestService.requestOAuth(request, response, session);return;} elsestore DocService in the session so it can be reusedsession.setAttribute(Constant.DOC_SESSION_ID,LoginService.docServiceFactory(appUser));chain.doFilter(request, response);}

除了通常的内务处理外,主要逻辑还从以下几行开始:

AppUser appUser = LoginService.login(request,response);

稍后我们将看到,LoginService负责将用户登录到Google,并且还将在本地BigTable数据存储区中创建用户。 通过将用户存储在本地,我们可以存储用户的OAuth凭据,从而消除了用户每次访问受限/过滤页面时都必须授予权限的需求。
在LoginService返回用户(AppUser对象)之后,我们将该用户对象存储到会话中(注意:要启用会话,必须在appengine-web.xml文件中设置session-enabled):
session.setAttribute(Constant.AUTH_USER,appUser);

然后,我们检查OAuth凭据是否与该用户相关联:

如果(appUser.getCredentials()== null){

session.setAttribute(Constant.TARGET_URI,request.getRequestURI());

OAuthRequestService.requestOAuth(请求,响应,会话);
返回;
}其他 session.setAttribute(Constant.DOC_SESSION_ID,LoginService.docServiceFactory(appUser));

如果getCredentials()返回null,则尚未为用户分配OAuth凭据。 这意味着OAuth流程需要启动。 由于此过程分为两步,即将请求发布到Google,然后通过回调(上面提到的Step2 servlet)取回结果,因此我们需要存储目标URL,以便在授权后可以将用户重定向到该URL。处理完成。 这是通过使用setAttribute方法将请求的URL存储到会话中来完成的。

然后,我们通过调用OAuthRequestService.requestOAuth()方法开始OAuth流程(详细信息将在下面讨论)。

如果getCredentials()返回一个非null值,则表明我们已经从数据存储区中的本地AppUser条目中获取了用户的OAuth凭据,我们只需将其添加到会话中,以便以后使用。

LoginService深入研究

LoginService类具有一个称为login的主要方法,其后是一堆JPA帮助器方法,用于保存或更新数据存储区中的本地用户。 我们将重点放在login()上,因为这是大多数业务逻辑所在的位置。

public static AppUser login(HttpServletRequest req, HttpServletResponse res) {LOGGER.setLevel(Constant.LOG_LEVEL);LOGGER.info('Initializing LoginService');String URI = req.getRequestURI();UserService userService = UserServiceFactory.getUserService();User user = userService.getCurrentUser();if (user != null) {LOGGER.info('User id is: '' + userService.getCurrentUser().getUserId()+ ''');String userEmail = userService.getCurrentUser().getEmail();AppUser appUser = (AppUser) req.getSession().getAttribute(Constant.AUTH_USER);if (appUser == null) {LOGGER.info('appUser not found in session');see if it is a new userappUser = findUser(userEmail);if (appUser == null) {LOGGER.info('User not found in datastore...creating');appUser = addUser(userEmail);} else {LOGGER.info('User found in datastore...updating');appUser = updateUserTimeStamp(appUser);}} else {appUser = updateUserTimeStamp(appUser);}return appUser;} else {LOGGER.info('Redirecting user to login page');try {res.sendRedirect(userService.createLoginURL(URI));} catch (IOException e) {e.printStackTrace();}}return null;}

我们要做的第一件事是使用Google UserService类来确定用户是否登录到Google:

UserService userService = UserServiceFactory.getUserService();

用户用户= userService.getCurrentUser();

如果Google的调用返回的User对象为null,则不会登录该用户,并使用以下命令将其重定向到登录页面:

res.sendRedirect(userService.createLoginURL(URI));

如果用户已登录(即不为null),则下一步是确定该用户是否存在于本地数据存储中。 这是通过使用appUser = findUser(userEmail)查找用户及其登录的Google电子邮件地址来完成的。 由于JPA / Objectify并不是本教程的主要讨论重点,因此我将不讨论该方法的工作原理。 但是, Objectify网站上有一些很棒的教程/文档。

如果用户在本地不存在,则使用Google的电子邮件地址填充该对象,并使用appUser = addUser(userEmail)创建该对象。 如果用户确实存在,我们仅出于登录目的而更新登录时间戳。

OAuthRequestService深入研究

您可能还记得过,一旦在本地设置了用户,然后AuthorizationFilter便会检查以查看用户是否已授予OAuth凭据。 如果不是,则调用OAuthRequestService.requestOAuth()方法。 如下图所示:

public static void requestOAuth(HttpServletRequest req,HttpServletResponse res, HttpSession session) {LOGGER.setLevel(Constant.LOG_LEVEL);LOGGER.info('Initializing OAuthRequestService');GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();oauthParameters.setOAuthConsumerKey(Constant.CONSUMER_KEY);oauthParameters.setOAuthConsumerSecret(Constant.CONSUMER_SECRET);Set the scope.oauthParameters.setScope(Constant.GOOGLE_RESOURCE);Sets the callback URL.oauthParameters.setOAuthCallback(Constant.OATH_CALLBACK);GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(new OAuthHmacSha1Signer());try {Request is still unauthorized at this pointoauthHelper.getUnauthorizedRequestToken(oauthParameters);Generate the authorization URLString approvalPageUrl = oauthHelper.createUserAuthorizationUrl(oauthParameters);session.setAttribute(Constant.SESSION_OAUTH_TOKEN,oauthParameters.getOAuthTokenSecret());LOGGER.info('Session attributes are: '+ session.getAttributeNames().hasMoreElements());res.getWriter().print('<a href='' + approvalPageUrl+ ''>Request token for the Google Documents Scope');} catch (OAuthException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}

为了简化OAuth的使用,Google提供了一组我们正在使用的Java帮助器类。 我们需要做的第一件事是设置使用者凭证(获取这些凭证已在前面讨论):

GoogleOAuthParameters oauthParameters =新的GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(Constant.CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(Constant.CONSUMER_SECRET);

然后,我们使用以下命令设置OAuth请求的范围:
oauthParameters.setScope(Constant.GOOGLE_RESOURCE);

Constant.GOOGLE_RESOURCE解析为https://docs.google.com/feeds/的位置。 发出OAuth请求时,请指定您试图获得访问权限的资源范围。 在这种情况下,我们尝试访问Google文档(每个服务的GData API都提供了作用域URL)。 接下来,我们确定希望Google返回答复的位置。

oauthParameters.setOAuthCallback(Constant.OATH_CALLBACK);

无论我们是以开发人员模式在本地运行还是部署到Google App Engine,此值都会更改。 在Constant接口中定义值的方法如下:

//用于在GAE上运行
//最终静态字符串OATH_CALLBACK ='http://tennis-coachrx.appspot.com/authSub';

//用于本地测试
最后的静态字符串OATH_CALLBACK ='http://127.0.0.1:8888/authSub';

然后,使用Google的帮助者签署请求时:

GoogleOAuthHelper oauthHelper =新的GoogleOAuthHelper(新的OAuthHmacSha1Signer());

然后,我们生成用户将导航到的URL,以授权对资源的访问。 这是使用以下方法动态生成的:

字符串rovalPageUrl = oauthHelper.createUserAuthorizationUrl(oauthParameters);

最后一步是向用户提供链接,以便他们可以导航到该URL来批准请求。 这是通过构造一些简单HTML来完成的,这些HTML使用res.getWriter()。print()输出。

用户授予访问权限后,Google便会调用由URL参数/ authSub标识的servlet,该参数对应于servlet类RequestTokenCallbackServlet。 接下来,我们将对此进行检查。

RequestTokenCallbackServlet深入研究

该servlet使用Google OAuth帮助程序类来生成所需的访问令牌和秘密访问令牌,这些访问令牌和秘密访问令牌在随后对Google API文档服务的调用中将是必需的。 这是从Google接收回叫响应的doGet方法:

public void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {Create an instance of GoogleOAuthParametersGoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();oauthParameters.setOAuthConsumerKey(Constant.CONSUMER_KEY);oauthParameters.setOAuthConsumerSecret(Constant.CONSUMER_SECRET);GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(new OAuthHmacSha1Signer());String oauthTokenSecret = (String) req.getSession().getAttribute(Constant.SESSION_OAUTH_TOKEN);AppUser appUser = (AppUser) req.getSession().getAttribute(Constant.AUTH_USER);oauthParameters.setOAuthTokenSecret(oauthTokenSecret);oauthHelper.getOAuthParametersFromCallback(req.getQueryString(),oauthParameters);try {String accessToken = oauthHelper.getAccessToken(oauthParameters);String accessTokenSecret = oauthParameters.getOAuthTokenSecret();appUser = LoginService.getById(appUser.getId());appUser = LoginService.updateUserCredentials(appUser,new OauthCredentials(accessToken, accessTokenSecret));req.getSession().setAttribute(Constant.DOC_SESSION_ID,LoginService.docServiceFactory(appUser));RequestDispatcher dispatcher = req.getRequestDispatcher((String) req.getSession().getAttribute(Constant.TARGET_URI));if (dispatcher != null)dispatcher.forward(req, resp);} catch (OAuthException e) {e.printStackTrace();}}

Google GoogleOAuthHelper用于执行填充我们感兴趣的两个值所需的整理任务:
字符串accessToken = oauthHelper.getAccessToken(oauthParameters);
字符串accessTokenSecret = oauthParameters.getOAuthTokenSecret();

获得这些值后,我们便从数据存储区中重新查询用户对象,并将这些值保存到AppUser.OauthCredentials子类中:
appUser = LoginService.getById(appUser.getId());
appUser = LoginService.updateUserCredentials(appUser, 新的OauthCredentials(accessToken,accessTokenSecret)); req.getSession()。setAttribute(Constant.DOC_SESSION_ID, LoginService.docServiceFactory(appUser));

此外,您还会看到它们也存储在会话中,因此在向Google Docs发出API请求时,我们可以随时使用它们。

现在我们已经拥有了所需的一切,我们只需将用户重定向回他们最初请求的资源即可:

RequestDispatcher调度程序= req.getRequestDispatcher((String)req
.getSession()。getAttribute(Constant.TARGET_URI));
dispatcher.forward(req,resp);

现在,当他们访问列出其文档的JSP页面时,一切都应该正常工作!

这是最终产品的截屏演示:

希望您喜欢本教程和演示-期待您的评论!

继续本教程的第二部分 。

参考:来自Jeff's SOA Ruminations博客的JCG合作伙伴 Jeff Davis 对Google App Engine中的Google Services进行身份验证 。


翻译自: https://www.javacodegeeks.com/2012/06/google-services-authentication-in-app.html

app engine

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354752.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Angular最新教程-第六节编写响应式导航栏

这节课我们讲解如何使用bootstrap 4 编写响应式布局。 参考图我们还是参照Angular中文社区http://www.angularjs.cn/ 图中标注红色的部分&#xff0c;我自己不是很喜欢&#xff0c;所以做了一点小改动。 他这里也没有做响应式布局&#xff0c;所以样式就不抄他的&#xff0c…

使用Spring-Retry重试处理

只要软件组件相互通信&#xff0c;就有可能出现临时的自我纠正错误。 此类故障包括服务的暂时不可用&#xff0c;网络连接的暂时丢失或服务繁忙时出现的超时。 在这种情况下&#xff0c;适当的重试处理可以减少这些故障可能引起的问题。 在这篇文章中&#xff0c;我们将看到如…

计算机在智慧交通的应用论文,智能交通的毕业论文

智能交通的毕业论文智能运输系统的研究许多国家都投入了巨大的人力和物力,并成为继航空航天、军事领域之后高新技术应用最集中的领域。下面为大家分享了有关智能交通的论文&#xff0c;欢迎欣赏&#xff01;摘 要&#xff1a;八十年代以来&#xff0c;世界一些发达国家纷纷投入…

MySQL5.5安装教程

1、 官网下载mysql5.5 下载地址&#xff1a; http://dev.mysql.com/downloads/mysql/5.5.html#downloads 2、 安装mysql5.5 注意&#xff0c;安装之前&#xff0c;请关闭杀毒软件。 &#xff08;1&#xff09; 打开下载的mysql-5.5.53-winx64.msi &#xff08;2&#xff09; 点…

计算机软件硬件基础知识,计算机硬件基础_计算机软硬件基础知识都包括什么具体的内容...

计算机硬件基础怎么学过去有许多微型计算机原理的书&#xff0c;讲或的原理现在肯定还有这方面的书&#xff0c;但讲的是什么我就不知道了还有学学单片机也可以对计算机硬件原理有所了解还可看有关杂志&#xff0c;如《微型计算机》&#xff0c;刚开始可能许多文章都看不懂&…

php里的抽象类和接口

//实例化类产生对象。//class fenbi//{// //普通成员&#xff0c;属于对象// public $length "10cm";// //静态成员&#xff0c;静态变量&#xff0c;属于类。// public static $color;//// //普通方法可以调用静态成员// function hua(){// echo $this->l…

新疆计算机二级慨库,2020新疆维吾尔自治区计算机二级易考套餐:二级MS Office高级应用全程班(网课+题库+教材)...

&nbsp&nbsp[导读]:2020新疆维吾尔自治区计算机二级易考套餐&#xff1a;二级MS Office高级应用全程班(网课题库教材)&#xff0c;更多新疆等级考试易考套餐&#xff0c;请访问易考吧新疆等级考试栏目2020新疆维吾尔自治区计算机二级易考套餐&#xff1a;二级MS Office高…

hotspot jvm_在Hotspot JVM中跟踪过多的垃圾收集

hotspot jvm由于内存泄漏或其他内存问题&#xff0c;经常导致应用程序冻结&#xff0c;仅使垃圾收集器&#xff08;GC&#xff09;进程运行失败&#xff0c;试图释放一些空间。 直到看门狗&#xff08;或沮丧的管理员&#xff09;重新启动应用程序并且问题从未解决之前&#xf…

计算机上平方米的单位,word怎么写平方米 word中平方米的单位怎么打

1、以Word2010版本为例&#xff0c;在文档中输入了平方米的单位&#xff0c;但是数字2不在上方&#xff1b;2、首先选中数字2&#xff0c;然后点击页面上方的上标图标&#xff0c;如下图所示&#xff1b;3、点击上标图标后&#xff0c;就会发现数字2在字母的右上方了&#xff0…

Xamarin 2017.10.9更新

Xamarin 2017.10.9更新本次更新主要解决了一些bug。Visual Studio 2017升级到15.4获得新功能。Visual Studio 2015需要工具-选项-Xamarin进行更新&#xff0c;更新Xamarin插件到4.7.10.22 版本。工具包分享链接&#xff1a;http://pan.baidu.com/s/1dE5hnCP 密码&#xff1a;3k…

通过入侵JVM打印阵列

总览 Java中最常见的陷阱之一就是知道如何打印数组。 如果有关如何打印阵列的答案获得了超过1000票赞成票&#xff0c;那么您必须怀疑是否有更简单的方法。 几乎所有其他流行语言都具有这种更简单的方法&#xff0c;因此我不清楚Java为什么仍会这样做。 与其他JDK类不同&#…

一种同于计算机键盘的测试装置,一种计算机键盘按键测试装置

主权项&#xff1a;1.一种计算机键盘按键测试装置&#xff0c;包括顶架以及固定安装在顶架正下方的测试机&#xff0c;其特征在于&#xff1a;顶架底面与测试机顶面之间固定连接有连接架&#xff0c;测试机内设有敲打装置和除尘装置&#xff0c;敲打装置包括设置在测试机左侧底…

SQL Server 索引重建手册

注意&#xff1a;索引重建前建议把数据库切换为完整模式&#xff0c;否则索引复制会在数据文件中进行&#xff0c;导致数据文件很大&#xff0c;而数据文件的收缩比日志文件的收缩要困难的多&#xff0c;且会对业务造成影响。步骤一:查询索引碎片&#xff0c;脚本如下&#xff…

三角形面积计算机方法,数学知识点:三角形面积的计算

原标题&#xff1a;数学知识点&#xff1a;三角形面积的计算复习&#xff11;、说说长方形、平行四边形的面积计算公式。长方形的面积长宽平行四边形的面积底高2、 计算下面长方形和平行四边形的面积&#xff0e;生活中常见的三角形思考 :怎样应用所学的方法探究三角形的面积计…

mysql学习第二天

alter tables转载于:https://www.cnblogs.com/hanxue112253/p/7657309.html

apache camel_令人印象深刻的第一个Apache Camel版本

apache camel在准备下周的CamelOne会议时&#xff0c;我花了一些时间回顾一下Apache Camel项目的历史。 因此&#xff0c;除其他外&#xff0c;我了解了Apache Camel的第一个正式1.0版本 。 Apache Camel 1.0 – 5年前 我看的越多&#xff0c;这个版本的事实给我留下了深刻的印…

notion自建服务器,【速报】Notion的特色「双向链接」方案来了,Synced Block 登场...

Notion 作为一款出色的「聚合型」(All-In-One)笔记 App&#xff0c;在这几年里逐渐获得了不少知识工作者的宠爱。在以前&#xff0c;大家一般会高度赞扬 Notion 的「模块化」编辑器&#xff0c;但同时也会好奇什么时候 Notion 可以带来像Roam Research 那样的 「双向链接」功能…

English 动词篇

动词后加to do 和 doing的记忆口诀 一、只能用动名词(ing)作宾语 [口诀] 考虑建议盼原谅&#xff0c;承认推迟没得想。 避免错过继续练&#xff0c;否定完成停欣赏。 禁止想象才冒险&#xff0c;不禁介意弃逃亡。 consider考虑suggest建议/advocate 提倡主张look forw…

本周Java技巧#7 – Maven慢吗?

本周Java本周技巧是有关Maven的上一期的后续内容。 第一个视频介绍了与Maven依赖关系有关的方面。 该视频将介绍一些加快Maven构建速度的技术。 Maven慢吗&#xff1f; 从Maven 3开始&#xff0c;您可以并行运行构建。 根据构建机器和项目结构的不同&#xff0c;您可能会获得6…

华北电力大学的计算机类专业排名,2018年华北电力大学王牌专业排行榜,考生和家长们都好好看看!...

华北电力大学有两个校区&#xff0c;一个是北京校区&#xff0c;一个是保定校区&#xff0c;在理科类的院校中&#xff0c;这所大学还是非常不错的&#xff0c;小编有幸去这个学校玩过几次&#xff0c;这个学校的占地面积非常大&#xff0c;环境相对比较优美&#xff0c;而且学…