一、概述 1.是什么 简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。 2.feature 1.Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。 2.Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。 3. Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。 3.文档 官方教程:http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#what-is-thymeleaf 推荐教程:http://blog.didispace.com/springbootweb/ http://blog.csdn.net/u012706811/article/details/52185345 二、HelloWorld 1.引入依赖 springboot直接引入:
|
1 2 3 4 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> |
非springboot项目使用如下依赖:
|
1 2 3 4 5 |
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>2.1.4</version> </dependency> |
默认的模板映射路径是:src/main/resources/templates,springboot1.4之后,可以使用thymeleaf3来提高效率,并且解决标签闭合问题,配置方式:
|
1 2 3 4 5 6 7 8 |
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- set thymeleaf version --> <thymeleaf.version>3.0.0.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.0.0</thymeleaf-layout-dialect.version> <!--set java version--> <java.version>1.8</java.version> </properties> |
之前的model/modelMap/modelAndView等页面数据传递参考之前随笔:点击查看 快速回顾:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
package com.learndemo.controller; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping(value = "/return") public class LearnReturnType { /** * Model本身不能设置页面跳转的url地址别名或者物理跳转地址,那么我们可以通过控制器方法的返回值来设置跳转url * 地址别名或者物理跳转地址 * * @param model * 一个接口, 其实现类为ExtendedModelMap,继承了ModelMap类 * @return 跳转url地址别名或者物理跳转地址 */ @RequestMapping(value = "/index1") public String index1(Model model) { model.addAttribute("result", "后台返回index1"); return "result"; } /** * ModelMap对象主要用于传递控制方法处理数据到结果页面,类似于request对象的setAttribute方法的作用。 用法等同于Model * * @param model * @return 跳转url地址别名或者物理跳转地址 */ @RequestMapping(value = "/index2") public String index2(ModelMap model) { model.addAttribute("result", "后台返回index2"); return "result"; } /** * ModelAndView主要有两个作用 设置转向地址和传递控制方法处理结果数据到结果页面 * @return 返回一个模板视图对象 */ @RequestMapping(value = "/index3") public ModelAndView index3() { ModelAndView mv = new ModelAndView("result"); // ModelAndView mv = new ModelAndView(); // mv.setViewName("result"); mv.addObject("result", "后台返回index3"); return mv; } /** * map用来存储递控制方法处理结果数据,通过ModelAndView方法返回。 * 当然Model,ModelMap也可以通过这种方法返回 * @return 返回一个模板视图对象 */ @RequestMapping(value = "/index4") public ModelAndView index4() { Map<String, String> map = new HashMap<String, String>(); map.put("result", "后台返回index4"); return new ModelAndView("result", map); } } |
2.配置thymeleaf视图解析器 这点与springMVC是相类似的:
|
1 2 3 4 5 6 7 |
#thymeleaf start spring.thymeleaf.mode=HTML5 spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.content-type=text/html #开发时关闭缓存,不然没法看到实时页面 spring.thymeleaf.cache=false #thymeleaf end |
实际项目中可能会有不太严格的HTML格式,此时设置mode=HTML5将会对非严格的报错,可以参考以下配置:
|
1 |
spring.thymeleaf.mode=LEGACYHTML5 |
|
1 2 3 4 5 6 7 8 9 10 |
你可能会发现在默认配置下,thymeleaf对.html的内容要求很严格,比如<meta charset="UTF-8" />, 如果少最后的标签封闭符号/,就会报错而转到错误页。也比如你在使用Vue.js这样的库,然后有<div v-cloak></div>这样的html代码, 也会被thymeleaf认为不符合要求而抛出错误。 因此,建议增加下面这段: spring.thymeleaf.mode = LEGACYHTML5 spring.thymeleaf.mode的默认值是HTML5,其实是一个很严格的检查,改为LEGACYHTML5可以得到一个可能更友好亲切的格式要求。 需要注意的是,LEGACYHTML5需要搭配一个额外的库NekoHTML才可用。 |
<dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.22</version> </dependency>
|
1 |
最后重启项目就可以感受到不那么严格的thymeleaf了。 |
这样,需要的配置项如下:
|
1 2 3 4 |
# 一项是非严格的HTML检查,一项是禁用缓存来获取实时页面数据,其他采用默认项即可 thymeleaf: mode: LEGACYHTML5 cache: false |
// 完整配置项参考类ThymeleafProperties 3.编写控制器
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * 测试demo的controller * * @author zcc ON 2018/2/8 **/ @Controller public class HelloController { private static final Logger log = LoggerFactory.getLogger(HelloController.class); @GetMapping(value = "/hello") public String hello(Model model) { String name = "jiangbei"; model.addAttribute("name", name); return "hello"; } } |
4.编写模板html
|
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>hello</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> </head> <body> <!--/*@thymesVar id="name" type="java.lang.String"*/--> <p th:text="'Hello!, ' + ${name} + '!'">3333</p> </body> </html> |
其中,注释是通过alt+enter进行自动生成的,便于IDEA补全,如果不加,IDEA将会报错cannot reslove。 当然也可以通过如下方式解决,解决之前推荐在maven项目中reimport一下!(据说新版本的IDEA中已经修复此问题,待更新至2017.3以后) 5.测试 三、基础语法 1.创建HTML 由上文也可以知道需要在html中添加:
|
1 |
<html xmlns:th="http://www.thymeleaf.org"> |
这样,下文才能正确使用th:*形式的标签! 2.获取变量值${…} 通过${…}进行取值,这点和ONGL表达式语法一致!
|
1 2 |
<!--/*@thymesVar id="name" type="java.lang.String"*/--> <p th:text="'Hello!, ' + ${name} + '!'">3333</p> |
选择变量表达式*{…}
|
1 2 3 4 5 6 7 8 9 10 11 |
<div th:object="${session.user}"> <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p> <p>Nationality: <span th:text={nationality}">Saturn</span>.</p> </div> 等价于 <div> <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p> </div> |
至于p里面的原有的值只是为了给前端开发时做展示用的.这样的话很好的做到了前后端分离。 这也是Thymeleaf非常好的一个特性:在无网络的情况下也能运行,也就是完全可以前端先写出页面,模拟数据展现效果,后端人员再拿此模板修改即可! 3.链接表达式: […]
View Details1. Druid是什么? Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。 2. 在哪里下载druid 正式版本下载: maven中央仓库: http://central.maven.org/maven2/com/alibaba/druid/ 3. 怎么获取Druid的源码 Druid是一个开源项目,源码托管在github上,源代码仓库地址是 https://github.com/alibaba/druid。同时每次Druid发布正式版本和快照的时候,都会把源码打包,你可以从上面的下载地址中找到相关版本的源码 4. 怎么配置maven Druid 0.1.18 之后版本都发布到maven中央仓库中,所以你只需要在项目的pom.xml中加上dependency就可以了。例如:
|
1 2 3 4 5 |
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid-version}</version> </dependency> |
也可以选择 Maven仓库查找公共的仓库地址:http://www.mvnrepository.com/artifact/com.alibaba/druid 5. 怎么打开Druid的监控统计功能 Druid的监控统计功能是通过filter-chain扩展实现,如果你要打开监控统计功能,配置StatFilter,具体看这里:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter 6. 怎样使用Druid的内置监控页面 内置监控页面是一个Servlet,具体配置看这里:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE 7. 内置监控中的Web和Spring关联监控怎么配置? Web关联监控配置 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter Spring关联监控配置 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_Druid%E5%92%8CSpring%E5%85%B3%E8%81%94%E7%9B%91%E6%8E%A7%E9%85%8D%E7%BD%AE 8. 怎么配置防御SQL注入攻击 Druid提供了WallFilter,它是基于SQL语义分析来实现防御SQL注入攻击的。具体配置看这里:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE-wallfilter 9. Druid有没有参考配置 不同的业务场景需求不同,你可以使用我们的参考配置,但建议你仔细阅读相关文档,了解清楚之后做定制配置。https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_DruidDataSource%E5%8F%82%E8%80%83%E9%85%8D%E7%BD%AE 10. 我想日志记录JDBC执行的SQL,如何配置 Druid提供了Log4jFilter、CommonsLogFilter和Slf4jFilter,具体配置看这里https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_LogFilter 11. 我的程序可能产生连接泄漏了,有什么办法? Druid提供了多种监测连接泄漏的手段,具体看这里:https://github.com/alibaba/druid/wiki/%E8%BF%9E%E6%8E%A5%E6%B3%84%E6%BC%8F%E7%9B%91%E6%B5%8B 12. 在Druid中使用PSCache会有内存占用过大问题么? 连接Oracle数据库,打开PSCache,在其他的数据库连接池都会存在内存占用过多的问题,Druid是唯一解决这个问题的连接池。具体看这里:https://github.com/alibaba/druid/wiki/Oracle%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8BPreparedStatementCache%E5%86%85%E5%AD%98%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88 13. 有没有和其他数据库连接池的对比? 各种数据库连接池对比https://github.com/alibaba/druid/wiki/%E5%90%84%E7%A7%8D%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0%E5%AF%B9%E6%AF%94 14. 从其他连接池迁移要注意什么? 不同连接池的参数参照对比: http://code.alibabatech.com/wiki/pages/viewpage.action?pageId=6947005 DBCP迁移 https://github.com/alibaba/druid/wiki/DBCP%E8%BF%81%E7%A7%BB 15. Druid中有没有类似Jboss DataSource中的ExceptionSorter ExceptionSorter是JBoss DataSource中的优秀特性,Druid也有一样功能的ExceptionSorter,但不用手动配置,自动识别生效的。具体看这里:https://github.com/alibaba/druid/wiki/ExceptionSorter_cn 16. Druid中的maxIdle为什么是没用的? maxIdle是Druid为了方便DBCP用户迁移而增加的,maxIdle是一个混乱的概念。连接池只应该有maxPoolSize和minPoolSize,druid只保留了maxActive和minIdle,分别相当于maxPoolSize和minPoolSize。 17. 我的应用配置的是JNDI数据源,可以用DruidDataSource么? DruidDataSource支持JNDI配置,具体看这里:https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_JNDI_Tomcat 具体实现的类是这个:com.alibaba.druid.pool.DruidDataSourceFactory,你可以阅读代码加深理解。 18. 我的应用已使用DBCP,是代码中写死的,怎样更换为Druid? 可以的,Druid提供了一个中完全平滑迁移DBCP的办法。 1) 从http://repo1.maven.org/maven2/com/alibaba/druid/druid-wrapper/ 下载druid-wrapper-xxx.jar 2) 加入druid-xxx.jar 3) 从你的WEB-INF/lib/中删除dbcp-xxx.jar 4) 按需要加上配置,比如JVM启动参数加上-Ddruid.filters=stat,动态配置druid的filters […]
View Detailsthymeleaf的模板默认路径是resources的templates,我想把模板放到webapp下,然后就弄了半天,最后终于发现问题了(想饿自己三天不吃饭~),记录一下这次惨痛的教训😂 springboot使用thymeleaf在pom文件中引包,然后修改项目结构,修改yml配置就ok了 pom文件中引包 org.springframework.boot spring-boot-starter-thymeleaf 修改项目结构 最后点击ok然后去设置yml文件 from:https://blog.csdn.net/zhaocongc/article/details/87183872
View Details首先堆栈和堆(托管堆)都在进程的虚拟内存中。(在32位处理器上每个进程的虚拟内存为4GB) 堆栈stack 堆栈中存储值类型。 堆栈实际上是向下填充,即由高内存地址指向低内存地址填充。 堆栈的工作方式是先分配内存的变量后释放(先进后出原则)。 堆栈中的变量是从下向上释放,这样就保证了堆栈中先进后出的规则不与变量的生命周期起冲突! 堆栈的性能非常高,但是对于所有的变量来说还不太灵活,而且变量的生命周期必须嵌套。 通常我们希望使用一种方法分配内存来存储数据,并且方法退出后很长一段时间内数据仍然可以使用。此时就要用到堆(托管堆)! 堆(托管堆)heap 堆(托管堆)存储引用类型。 此堆非彼堆,.NET中的堆由垃圾收集器自动管理。 与堆栈不同,堆是从下往上分配,所以自由的空间都在已用空间的上面。 比如创建一个对象: Customer cus; cus = new Customer(); 申明一个Customer的引用cus,在堆栈上给这个引用分配存储空间。这仅仅只是一个引用,不是实际的Customer对象! cus占4个字节的空间,包含了存储Customer的引用地址。 接着分配堆上的内存以存储Customer对象的实例,假定Customer对象的实例是32字节,为了在堆上找到一个存储Customer对象的存储位置。 .NET运行库在堆中搜索第一个从未使用的,32字节的连续块存储Customer对象的实例! 然后把分配给Customer对象实例的地址赋给cus变量! 从这个例子中可以看出,建立对象引用的过程比建立值变量的过程复杂,且不能避免性能的降低! 实际上就是.NET运行库保存对的状态信息,在堆中添加新数据时,堆栈中的引用变量也要更新。性能上损失很多! 有种机制在分配变量内存的时候,不会受到堆栈的限制:把一个引用变量的值赋给一个相同类型的变量,那么这两个变量就引用同一个堆中的对象。 当一个应用变量出作用域时,它会从堆栈中删除。但引用对象的数据仍然保留在堆中,一直到程序结束 或者 该数据不被任何变量应用时,垃圾收集器会删除它。 装箱转化 using System; class Boxing { public static void Main() { int i=110; object obj=i; i=220; Console.WriteLine("i={0},obj={1}",i,obj); obj=330; Console.WriteLine("i={0},obj={1}",i,obj); } } 定义整数类型变量I的时候,这个变量占用的内存是内存栈中分配的,第二句是装箱操作将变量 110存放到了内存堆中,而定义object对象类型的变量obj则在内存栈中,并指向int类型的数值110,而该数值是付给变量i的数值副本。 所以运行结果是 i=220,obj=110 i=220,obj=330 内存格局通常分为四个区 全局数据区:存放全局变量,静态数据,常量 代码区:存放所有的程序代码 栈区:存放为运行而分配的局部变量,参数,返回数据,返回地址等, 堆区:即自由存储区 值类型变量与引用类型变量的内存分配模型也不一样。为了理解清楚这个问题,读者首 先必须区分两种不同类型的内存区域:线程堆栈(Thread Stack)和托管堆(Managed Heap)。 每个正在运行的程序都对应着一个进程(process),在一个进程内部,可以有一个或多 个线程(thread),每个线程都拥有一块“自留地”,称为“线程堆栈”,大小为1M,用于保 存自身的一些数据,比如函数中定义的局部变量、函数调用时传送的参数值等,这部分内存 区域的分配与回收不需要程序员干涉。 所有值类型的变量都是在线程堆栈中分配的。 另一块内存区域称为“堆(heap)”,在.NET 这种托管环境下,堆由CLR 进行管理,所 以又称为“托管堆(managed heap)”。 用new 关键字创建的类的对象时,分配给对象的内存单元就位于托管堆中。 在程序中我们可以随意地使用new 关键字创建多个对象,因此,托管堆中的内存资源 是可以动态申请并使用的,当然用完了必须归还。 打个比方更易理解:托管堆相当于一个旅馆,其中的房间相当于托管堆中所拥有的内存 单元。当程序员用new 方法创建对象时,相当于游客向旅馆预订房间,旅馆管理员会先看 一下有没有合适的空房间,有的话,就可以将此房间提供给游客住宿。当游客旅途结束,要 办理退房手续,房间又可以为其他旅客提供服务了。 […]
View Details解决方案 今天像往常一样,编写文章,并使用gulp bulid压缩代码,但是一运行:gulp build 就出现了这个错误:AssertionError: Task function must be specified。 gulp项目需要全局安装gulp和项目内安装gulp,通过 gulp -v 查看全局gulp 和本地项目的gulp版本: image.png 这俩显然,不一致。 下面,我们可以看到项目里记录的gulp的版本号: image.png 可以卸载修改json文件里面的版本号:为gulp 3 版本。如 3.9.1 重新运行 npm install gulp 3 和 gulp 4运行方式是不一样的,所以会出现错误。具体细节,可以看这里 gup3 VS gulp4 区别 Gulp 4最大的变化就是你不能像以前那样传递一个依赖任务列表。 Gulp3,如果有一个任务A,B和C的列表,你想在一个序列中运行(确保A在B开始之前完成,而B在C开始之前完成),代码如下:
|
1 2 3 4 5 6 7 8 9 |
gulp.task('a', function () { // Do something. }); gulp.task('b', ['a'], function () { // Do some stuff. }); gulp.task('c', ['b'], function () { // Do some more stuff. }); |
在Gulp 4中,你不能再这样做了。你会得到以下错误:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
assert.js:85 throw new assert.AssertionError({ ^ AssertionError: Task function must be specified at Gulp.set [as _setTask] (/home/hope/web/node_modules/undertaker/lib/set-task.js:10:3) at Gulp.task (/home/hope/web/node_modules/undertaker/lib/task.js:13:8) at Object.<anonymous> (/home/hope/web/gulpfile.js:31:6) at Module._compile (module.js:570:32) at Object.Module._extensions..js (module.js:579:10) at Module.load (module.js:487:32) at tryModuleLoad (module.js:446:12) at Function.Module._load (module.js:438:3) at Module.require (module.js:497:17) at require (internal/module.js:20:19) |
不要用Gulp3的方式指定依赖任务,你需要使用gulp.series和gulp.parallel,因为gulp任务现在只有两个参数。 gulp.series:按照顺序执行 gulp.paralle:可以并行计算
|
1 2 3 |
gulp.task('my-tasks', gulp.series('a', 'b', 'c', function() { // Do something after a, b, and c are finished. })); |
|
1 2 3 |
gulp.task('build', gulp.parallel('styles', 'scripts', 'images', function () { // Build the website. })); |
或者这样
|
1 2 3 |
gulp.task('my-tasks', gulp.series('a', gulp.parallel('styles','scripts', 'images'), 'b', 'c', function() { // Do something after a, b, and c are finished. })); |
相关任务必须在被调用之前发生。 参考: https://github.com/gulpjs/gulp/blob/master/docs/API.md https://www.fastless.com/gulp-4 https://www.jianshu.com/p/40b99bed3127 作者:cuteximi_1995 链接:https://www.jianshu.com/p/c30ff8592421 来源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
View Detailsgulpfile.js
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var gulp = require('gulp'); var webserver = require('gulp-webserver'); gulp.task('webserver', function () { gulp.src('./') .pipe(webserver({ host: 'localhost', port: 3000, livereload: true, open: './pages/myPages/login.html', directoryListing: { enable: true, path: './' }, proxies: [ { source: '/api', target: 'http://jsonplaceholder.typicode.com' } ] })) }); gulp.task('default', ['webserver'], function () { console.log('成功'); }); |
1、在本地起一个服务,端口号为3000。 2、open:默认打开的文件的路径 3、directoryListing:是否显示文件列表 4、proxies: 设置代理 接口调用: 接口调用 url http://localhost:3000/api 作者:动次动次forever 链接:https://www.jianshu.com/p/237bc5c04fa1 来源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
View Details为什么需要使用代理? 目前,前端发展飞速,前后端分离式开发已经不是新鲜事儿。 1 如果前端只是静态的页面(不需要调用后端的api加载动态数据),那么前端直接在本地开发即可。 即使是前后端分开部署,只需要单独为前端启动一个服务,使用gulp-connect即可。不会?(请戳gulp之用gulp-connect开启一个本地webServer) 2 如果前端需要调用后端提供的接口呢?除非前后端部署在同一个服务器上,否则由于浏览器的限制是没办法跨域请求数据的。真实情况是,我们在本地开发时就需要访问接口,这时后端的程序猿给你的往往是这样一个地址 http://192.168.1.123/controller/action,有人说上传代码到http://192.168.1.123/放在一个目录下不就行了,是啊!可以的。但是好麻烦有没有,调试接口需要传代码上去!!!! 3 如果我的数据是从其他的服务器来的呢?例如我想访问豆瓣的开放接口
|
1 2 3 4 |
$.post(<span class="hljs-string">'https://api.douban.com/v2/book/1220562'</span>,{},<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span>{ <span class="hljs-built_in">console</span>.log(data); }) |
豆瓣告诉你,不行!你跨域了! 所以,还是代理来的方便。 代理是个什么鬼? 代理请求通俗点讲,就是把发往 a.com的请求,通过代理服务器发送到b.com。 怎么开启一个代理? 代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<span class="hljs-comment">//服务端口</span> <span class="hljs-keyword">const</span> PORT=<span class="hljs-number">8000</span>; <span class="hljs-comment">//启动服务的根目录</span> <span class="hljs-keyword">const</span> server_root=<span class="hljs-string">'src'</span> <span class="hljs-comment">//引用gulp</span> <span class="hljs-keyword">var</span> gulp = <span class="hljs-built_in">require</span>(<span class="hljs-string">'gulp'</span>); <span class="hljs-comment">//引用gulp开启服务的插件</span> <span class="hljs-keyword">var</span> connect = <span class="hljs-built_in">require</span>(<span class="hljs-string">'gulp-connect'</span>); <span class="hljs-comment">//引用插件 gulp代理中间件插件</span> <span class="hljs-keyword">var</span> proxy = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http-proxy-middleware'</span>); <span class="hljs-comment">//开启服务</span> gulp.task(<span class="hljs-string">'proxyServer'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>() </span>{ connect.server({ <span class="hljs-attr">root</span>: [server_root], <span class="hljs-attr">port</span>: PORT, <span class="hljs-attr">middleware</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">connect, opt</span>) </span>{ <span class="hljs-keyword">return</span> [ proxy(<span class="hljs-string">'/api'</span>, { <span class="hljs-attr">target</span>: <span class="hljs-string">'https://api.douban.com/v2'</span>,<span class="hljs-comment">//代理的目标地址</span> changeOrigin:<span class="hljs-literal">true</span>,<span class="hljs-comment">//</span> pathRewrite:{<span class="hljs-comment">//路径重写规则 </span> <span class="hljs-string">'^/api'</span>:<span class="hljs-string">''</span> } }) ] } }); }); |
说明 '/api' =>匹配你要代理的请求地址前缀 target=》你要把请求代理到哪 pathRewrite =》是把你再页面中写的请求地址 某部分 重写为后面的字符串 (例子中讲 请求中写的 /api 重写为 空字符串) 也就是发往 /api/book/1220562的请求最终会被发往 https://api.douban.com/v2/book/1220562 页面写请求时的写法
|
1 2 3 4 |
$.post(<span class="hljs-string">'/api/book/1220562'</span>,{},<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span>{ <span class="hljs-built_in">console</span>.log(data); }) |
目录结构 image.png 运行效果 image.png 结语 当然还有其他的方式,比如 node、cros、jsonp、phpheader、nginx设置代理 作者:coder_turbo 链接:https://www.jianshu.com/p/cc543fd0d7c5 来源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
View Details由于我们的接口是通过springboot一键式打包成jar包发布到服务器的,因此在通过接口上传文件之后,客户端如果还需要再访问该文件,那就需要用到静态资源访问,spring配置如下:
|
1 2 3 |
spring: resources: static-locations: file:E:\workspace\serverWorkspace\upload\images\photo\20180820,file:/opt/www |
一个是widows下做测试使用,另一个是linux下测试服务器使用。 有时候你会发现可能配置了static-locations但无效,原因可能就是你的目录不对,请仔细检查一下,别问我怎么知道的 如此之后验证即可,如我的: 浏览器输入: localhost:9999/filename ——————— 作者:weixin_34387284 来源:CSDN 原文:https://blog.csdn.net/weixin_34387284/article/details/87066649 版权声明:本文为博主原创文章,转载请附上博文链接!
View Details|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<insert id="saveOrUpdate" > <selectKey keyProperty="count" resultType="int" order="BEFORE"> select count(*) from country where id = #{id} </selectKey> <if test="count > 0"> update country set countryname = #{countryname},countrycode = #{countrycode} where id = #{id} </if> <if test="count==0"> insert into country values(#{id},#{countryname},#{countrycode}) </if> </insert> |
注意如果insert的parameterType是自定义对象的话,对象里需要有count属性,否则会报count属性找不到的错误 from:https://ydlmlh.iteye.com/blog/2236671
View Details在这篇文章里面主要讲解如何在mybatis里面使用一对一、一对多、多表联合查询(类似视图)操作的例子。 注:阅读本文前请先大概看一下之前两篇文章。 一、表结构 班级表class,学生表student,班级学生关系表ClassStudent。 这里一个学生只会在一个班级里面,也就是一对一的关系;一个班级有多个学生,也就是一对多的关系。 结构如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
CREATE TABLE [dbo].[Class]( [class_id] [int] NOT NULL, [class_name] [varchar](50) NOT NULL, CONSTRAINT [PK_Class] PRIMARY KEY CLUSTERED ( [class_id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] CREATE TABLE [dbo].[ClassStudent]( [class_id] [int] NOT NULL, [student_id] [int] NOT NULL ) ON [PRIMARY] CREATE TABLE [dbo].[Student]( [s_id] [int] NOT NULL, [s_name] [varchar](50) NOT NULL, CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED ( [s_id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] |
3张表的数据如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
insert into Class values(1,'三(1)班') insert into Class values(2,'三(2)班') insert into Class values(3,'三(3)班') insert into Class values(4,'三(4)班') insert into Student values(1001,'张三') insert into Student values(1002,'李四') insert into Student values(1003,'赵五') insert into Student values(1004,'王二麻子') insert into ClassStudent values(1,1001) insert into ClassStudent values(1,1003) insert into ClassStudent values(4,1002) insert into ClassStudent values(3,1004) |
二、在原项目下新建一个包com.mybatis.sqljoinrelation,在包里面新建一个sqlMapper.xml的映射文件,在项目的mybatis的配置文件conf.xml中对这个sqlMapper.xml进行注册,注册片段如下:
|
1 2 3 4 |
<!-- 注册映射文件 --> <mappers> <mapper resource="com/mybatis/sqljoinrelation/sqlMapper.xml" /> </mappers> |
紧接着在原项目下再新建一个包com.mybatis.bean,在里面新建实体类 学生:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.mybatis.bean; /** * 学生(临时) */ public class StudentTemp { private int studentid; private String studentname; private Class tempClass; public Class getTempClass() { return tempClass; } public void setTempClass(Class tempClass) { this.tempClass = tempClass; } public int getStudentid() { return studentid; } public void setStudentid(int studentid) { this.studentid = studentid; } public String getStudentname() { return studentname; } public void setStudentname(String studentname) { this.studentname = studentname; } @Override public String toString() { return "StudentTemp [studentid=" + studentid + ", studentname=" + studentname + ", tempClass=" + tempClass + "]"; } } |
班级:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package com.mybatis.bean; /** * 班级 */ public class Class { private int classid; private String classname; public int getClassid() { return classid; } public void setClassid(int classid) { this.classid = classid; } public String getClassname() { return classname; } public void setClassname(String classname) { this.classname = classname; } @Override public String toString() { return "Class [classid=" + classid + ", classname=" + classname + "]"; } } |
学生班级视图类:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.mybatis.bean; public class studentclass { private int s_id; private String s_name; private int class_id; private String class_name; public int getS_id() { return s_id; } public void setS_id(int s_id) { this.s_id = s_id; } public String getS_name() { return s_name; } public void setS_name(String s_name) { this.s_name = s_name; } public int getClass_id() { return class_id; } public void setClass_id(int class_id) { this.class_id = class_id; } public String getClass_name() { return class_name; } public void setClass_name(String class_name) { this.class_name = class_name; } @Override public String toString() { return "studentclass [s_id=" + s_id + ", s_name=" + s_name + ", class_id=" + class_id + ", class_name=" + class_name + "]"; } } |
三、查询操作 1、 一对一,查询学生编号等于1001且他所在班级信息 2、一对多,查询班级编号等于1且包含所有学生信息 3、类似视图查询学生编号等于1001且他所在班级信息 sqlMapper.xml如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.mybatis.sqljoinrelation.sqlMapper"> <!-- 1、 获取某学生以及该学生所在班级的信息 --> <!-- 可以显示指出c.class_id,c.class_name,s.s_id,s.s_name列明, 如果用"*"代替,则mybatis会自动匹配resultMap中提供的列名 --> <select id="getStudentAndClass" parameterType="int" resultMap="studentClassMap"> select c.class_id,c.class_name,s.s_id,s.s_name from Class c left join ClassStudent cs on c.class_id = cs.class_id left join Student s on cs.student_id = s.s_id where s.s_id = #{id} </select> <!-- resultMap中的type表示返回什么类型的对象 --> <resultMap type="com.mybatis.bean.StudentTemp" id="studentClassMap"> <!-- property 表示com.mybatis.bean.StudentTemp中的属性; column 表示表中的列名 --> <id property="studentid" column="s_id" /> <result property="studentname" column="s_name" /> <!-- association字面意思关联,这里只专门做一对一关联; property表示是com.mybatis.bean.StudentTemp中的属性名称; javaType表示该属性是什么类型对象 --> <association property="tempClass" javaType="com.mybatis.bean.Class"> <!-- property 表示com.mybatis.bean.Class中的属性; column 表示表中的列名 --> <id property="classid" column="class_id" /> <result property="classname" column="class_name" /> </association> </resultMap> <!-- 2、获取某学生以及该学生所在班级的信息,类似视图实现方式 --> <!-- resultType 对应简单类型,也就是实体中的属性名称与数据库表字段名称一模一样; resultMap 对应复杂类型,属性名称与字段名称不一样可以通过resultMap中property,column进行映射 --> <select id="getStudentAndClassView" parameterType="int" resultType="com.mybatis.bean.studentclass"> select c.class_id,c.class_name,s.s_id,s.s_name from Class c left join ClassStudent cs on c.class_id = cs.class_id left join Student s on cs.student_id = s.s_id where s.s_id = #{id} </select> <!-- 3、获取某班级以及班级里面所有学生的信息 --> <select id="getClassStudents" parameterType="int" resultMap="classStudentsMap"> select * from Class c left join ClassStudent cs on c.class_id = cs.class_id left join Student s on cs.student_id = s.s_id where c.class_id = #{id} </select> <resultMap type="com.mybatis.bean.ClassTemp" id="classStudentsMap"> <id property="classid" column="class_id"/> <result property="classname" column="class_name"/> <!-- property表示集合类型属性名称,ofType表示集合中的对象是什么类型 --> <collection property="students" ofType="com.mybatis.bean.Student"> <id property="studentid" column="s_id"/> <result property="studentname" column="s_name"/> </collection> </resultMap> </mapper> |
测试代码如下: 一对一与类似视图查询的代码在一起,如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.mybatis.sqljoinrelation; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import com.mybatis.util.MybatisUtils; /** * 一对一 */ public class testonetoone { public static void main(String[] args) { SqlSessionFactory factory = MybatisUtils.getFactory(); SqlSession session = null; try { session = factory.openSession(true); //1、StudentTemp String statement1 = "com.mybatis.sqljoinrelation.sqlMapper.getStudentAndClass"; System.out.println(session.selectOne(statement1, 1001)); //2、用model类型接收数据 String statement2 = "com.mybatis.sqljoinrelation.sqlMapper.getStudentAndClassView"; System.out.println(session.selectOne(statement2, 1001)); } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } } } |
一对多测试代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.mybatis.sqljoinrelation; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import com.mybatis.util.MybatisUtils; /** * 一对多 */ public class testonetomany { public static void main(String[] args) { SqlSessionFactory factory = MybatisUtils.getFactory(); SqlSession session = null; try { session = factory.openSession(true); //1、StudentTemp String statement = "com.mybatis.sqljoinrelation.sqlMapper.getClassStudents"; System.out.println(session.selectOne(statement, 1)); } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } } } |
四、sqlMapper.xml需要说明的问题 1、一对一:<association property="tempClass" javaType="com.mybatis.bean.Class"> association字面意思关联,这里只专门做一对一关联; property表示是com.mybatis.bean.StudentTemp中的属性名称; javaType表示该属性是什么类型对象 2、类似视图查询: <select id="getStudentAndClassView" parameterType="int" resultType="com.mybatis.bean.studentclass"> resultType 对应简单类型,也就是实体中的属性名称与数据库表字段名称一模一样; resultMap 对应复杂类型,属性名称与字段名称不一样可以通过resultMap中property,column进行映射,其中一对一与一对多都是用resultMap来映射 3、一对多:<collection property="students" ofType="com.mybatis.bean.Student"> collection字面意思是集合,这里专门做一对多关联 ,property表示集合类型属性名称,ofType表示集合中的对象是什么类型 4、<select>select * from …</student>与<select>select 字段1,字段2,字段3… from …</student> 可以显示指出c.class_id,c.class_name,s.s_id,s.s_name列明,如果用"*"代替,则mybatis会自动匹配resultMap或者resultType中提供的列名, 如果某对象存在列映射的属性,但是在select 后面找不到这个列,则在返回结果中,该列多映射的值为null。 至此,对于一对一、一对多、视图查询都能很好的解决,主要还是要重点关注第四点说明。 from:https://www.cnblogs.com/wucj/p/5148813.html
View Details