Signin将以上代码放到模态框的body部分,但是其中的Remeberme记住我,和底下的button,Signin也不需要,所以把它们删掉,因为模态框里面有按钮,所以按钮也不需要,activity.html页面代码如下:那接下来,就可以根据活动表中的数据,去构建这个创建活动的模态框页面,另外,至于这个模态框到底拷贝到哪里,我们现在放到了table之上,那把模态框放到table之下也无所谓,甚至放到table所在div的外面也是可以的。下面我们把模态框的body部分也改一下,
后面这个,可能是一个类型,比如说有一个分类categoryId,下面的不想用input了啊,想改成下拉选项,它里面有一些选项
EmailaddressPasswordSignin将其添加到我们的activity.html页面中,只取前两个div,将示例中的form,改为div,即这个div里面设置了一个class="form-inline",代码效果如下:
发现前面还应该有一个label标签更好,代码修改及效果图如下:
发现效果并不好看,还不如写成两行的好,那复制第一个title的div进行相应的修改:
StartTimeEndTime最后还有一个textarea,可以选择Bootstrap中的样式,我们可以仿照title标签,修改为textarea,并设置其行数rows="5":
demo示例代码:
Remark最终效果如下:
那下一步,我们模态框点击提交时是一个按钮button,但是这个按钮没有写到表单里面,当我们点击这个Save按钮的时候,我们可能要提交表单,把表单里面的数据写到数据库里面,我们并没有用form表单里面的buttontype="submit"的形式的按钮,而是用的form外面的一个按钮button,那想把我们的表单的数据写到我们的数据库里面,应该如何提交,如何去实现呢,那首先得找到这个表单,可以通过表单的名字去找到这个表单,现在我们的表单还没有名字,那就给它一个名字或者是起一个id,然后给form外部的button上加一个单击事件onclick="doSaveObject()",把数据保存一下,我们写一个方法,那这时我们点击这个button的时候,就会有doSaveObject()的一个事件处理函数,那么在这个事件处理函数里面,想办法把表单提交给服务器端即可。另外,在这个form表单的属性上,我们可能还要写上一个action,要跳转到一个地址,可以用thymeleaf模板的写法,th:action,如果没有用thymeleaf标签的话,直接写那个地址也是可以的,那我们用thymeleaf写法,比如我们想提交的地址是/activity/doSaveActivity,然后表单提交方式method="post",即
那这时在服务器端要做一个save操作,在控制层ActivityController类中,添加这样一个方法doSaveActivity,并让它返回到activity.html页面,那这里先不考虑后台服务器保存刷新的问题,在这方法里首先做一个输出,测试在客户端提交的数据在服务器端是否可以收到,如果拿到数据,就可以把这些数据写入数据库里面了,那服务端的这个地址有了,接下来就可以去处理客户端,在activity.html页面里面如何提交表单中的数据,
@RequestMapping("/activity/doSaveActivity")publicStringdoSaveActivity(Activityactivity){System.out.println("activity="+activity);//检查客户端提交的数据//......return"activity";}在activity.html中模态框中的form表单数据,我没有用submit的表单控件,就是用了一个普通的button,这时如果想提交这个表单,就需要在这个button上定义一个单击事件函数,那现在就写下这个函数doSaveObject(),我们把函数写在body体里面的最底部,首先在函数里面想取到模态框中的那个form,我们可以在浏览器的F12控制台测试,输出一下如何获取到那个form,可以发现通过$(form)的方式,$(form)这个是jquery中基于标签获取对象的一种方式,是可以拿到的,确实是个对象,因为页面中现在只有一个form表单,拿到的对象里面也有submit这样的函数,因为button没有在form表单的内部,所以这里必须通过定义函数事件方式提交表单,否则如果只是一个普通的button是不会提交表单的:
客户端activity.html的doSaveObject函数:
functiondoSaveObject(){//表单校验(可考虑使用正则表达式)//提交表单//$(form)基于标签名(例如这里的标签名form)称获取表单对象//submit为jquery中的一个对象函数,通过此函数可以提交表单.$("form").submit();//提交表单}那下面我们试试,这样取到的对象能否提交表单给服务器,那首先在项目的ActivityController中的doSaveActivity方法的System.out.println("activity="+activity)这,加一个断点,然后(Re)debugCtrl+Alt+Shift+B,D启动,加断点的目的就想看看提交时,客户端的数据有没有提交到服务器端,服务器拿到没有:
可以看到断点在System.out.println("activity="+activity);这里,是接收到了客户端提交的数据的,那接下来,把它们写到数据库中就完成了。此时发现浏览器端返回了一个空页面,因为控制层里直接返回了页面,并没有刷新页面的数据,服务端还没有写那个把数据写到数据库,再查询,再回到这个页面的过程:
那如果想回到那个查询数据的操作,可以return"redirect:/activity/doFindActivitys";,然后把那个断点取消:
客户端页面,activity.html代码如下:
packagecom.cy.pj.activity.dao;importjava.util.List;importorg.apache.ibatis.annotations.Delete;importorg.apache.ibatis.annotations.Insert;importorg.apache.ibatis.annotations.Mapper;importorg.apache.ibatis.annotations.Select;importorg.apache.ibatis.annotations.Update;importcom.cy.pj.activity.pojo.Activity;@MapperpublicinterfaceActivityDao{intinsertObject(Activityactivity);/***查询所有活动信息,一行记录映射为内存中的一个Activity对象*1)谁负责与数据库交互(SqlSession,再具体一点就是JDBCAPI)*2)谁负责将ResultSet结果集合中的数据取出来存储到Activity(MyBatis,再具体一点就是ResultSetHandler)*/@Select("select*fromtb_activityorderbycreatedTimedesc")ListfindActivitys();}然后我们把这个Sql语句写到Mapper.xml映射文件里面去,我们从官网或其他的项目中拷贝一个Mapper.xml文件,放到src/main/resources/mapper/activity目录下,命名为ActivityMapper.xml,命名空间namespace="com.cy.pj.activity.dao.ActivityDao",insert标签上的id对应ActivityDao接口中的方法名,sql语句对应如下:
下面我们以Timer的方式为例,它是最简单的实现方案,虽然存在很多弊端,并不理想,但是至少我们写会了这种形式,是能解决问题的,先不去探究它的好与不好,至少我们是可以去解决的,比如类似的定时关机,定时任务取消等需求,都是可以用Timer去实现的,我们先new一个Timer出来,如下图所示:
publicvoidschedule(TimerTasktask,longdelay){if(delay<0)thrownewIllegalArgumentException("Negativedelay.");sched(task,System.currentTimeMillis()+delay,0);}publicvoidschedule(TimerTasktask,Datetime){sched(task,time.getTime(),0);}publicvoidschedule(TimerTasktask,longdelay,longperiod){if(delay<0)thrownewIllegalArgumentException("Negativedelay.");if(period<=0)thrownewIllegalArgumentException("Non-positiveperiod.");sched(task,System.currentTimeMillis()+delay,-period);}publicvoidschedule(TimerTasktask,DatefirstTime,longperiod){if(period<=0)thrownewIllegalArgumentException("Non-positiveperiod.");sched(task,firstTime.getTime(),-period);}我们这里写的还是比较简单的,
第一步:构建Timer对象;
@Update("updatetb_activitysetstate=0whereid=#{id}")intupdateState(Longid);当然,此方法也可以传两个值,一个id,一个状态值state,可以去修改基于此id的活动记录的状态为0-禁用或者1-启用。
数据层的Dao定义好方法以后,接下我们就可以在业务层的实现类ActivityServiceImpl.java中,直接调用这个ActivityDao中的updateState方法(这个方法没有定义在ActivityService接口中),并为之传入我们所要保存的对象的id,那这里有一个问题,既然是基于我们所正在创建的活动的id去修改状态,那首先我们需要拿到这个id,而页面上不可能提交id过来,因为这个活动是第一次创建的这么一条记录,如果想拿到这条id,首先是需要把此记录写到数据库以后,才能取到此id,那如何取到此id,这里有这样的一种机制:
打开ActivityMapper.xml映射文件,我们保存活动的insert的sql语句就写在这个文件中,我们想获取这条写入到数据库表中的这个记录的id值,因为这个id是自增长的,如果id是自增长的,那么它在mapper映射文件中的insert标签里,有一个useGeneratedKeys属性,将其设置为true,就表示我要获取写入到数据库表中的主键值;然后把这个主键值再赋值给此sql语句对应的数据层ActivityDao接口中,与之关联的方法参数对象中的另一个属性,具体是这个对象的哪一个属性,则由keyProperty来指定;那具体是方法中的哪个参数对象,可以由parameterType来指定,如果方法中只有一个参数对象,则parameterType通常是可以省略的,代码如下:
1)ActivityMapper.xml映射文件对应sql:
packagecom.cy.pj.activity.dao;importjava.util.List;importorg.apache.ibatis.annotations.Delete;importorg.apache.ibatis.annotations.Insert;importorg.apache.ibatis.annotations.Mapper;importorg.apache.ibatis.annotations.Select;importorg.apache.ibatis.annotations.Update;importcom.cy.pj.activity.pojo.Activity;@MapperpublicinterfaceActivityDao{//...intinsertObject(Activityactivity);//...}useGeneratedKeys:表示使用insert操作中生成的自增主键值(这里必须是自增主键);
keyProperty:表示将获得的自增主键值赋值给参数对象的指定属性(这里是id属性),至于具体赋值给哪一个属性,需要根据业务需求而定;
所以一般会用到线程池,也就是Java官方给出的第二种方式,ScheduledExecutorService,内置一个线程池。ScheduledExecutorService这个对象里面,它内置一个线程池,这个池中的线程可以重复应用,那我们可以通过这个池中的线程来处理多个并发请求,这样的话,性能上至少会好一些,可能对于资源的利用来讲,也会比较好,我们既要保证高效,又要实现低耗,还要保证线程安全,这是我们在程序开发中所要重点突破的点,虽然这个活动案例比较小,但很多地方都会有这样的应用场景,麻雀虽小,五脏俱全。
还有第三种写法,借助第三方的任务调度框架quartz,这个任务调度框架里面也使用了线程池技术,线程池是任务调度的基础。
那对于Timer对象的简单的用法就已经会用了,但是在我们这个程序中,如果在Timer的run方法中(内置的线程里),执行完了这个更新activityDao.updateState(entity.getId());,还要再加一句timer.cancel();,就退出这个任务调度,因为这个任务都已经执行完了,还让这个线程继续运行着做什么呢,没有必要了,执行结束以后,我们调用timer的cancel()方法,结束这个任务,此时线程就会退出,后续线程也会销毁。
但这样去写的话,代码量并没有减少,意义不大,所以还是不建议提取这工具类,但这种抽取工具类的方式,需要去理解:
packagecom.cy.pj.activity.scheduled;importjava.util.Date;importjava.util.Timer;importjava.util.TimerTask;publicclassTimerScheduledTask{publicstaticvoidschedule(TimerTasktask,Datedate){Timertimer=newTimer();timer.schedule(task,date);}}那这个Timer类中的schedule方法的第一个参数TimerTask能不能用lamda表达式的形式去写呢,并不能,TimerTask是一个抽象类,不是一个接口,必须是函数式接口才可以写成Lamda表达式形式,因为TimerTask里面,它还有其他的方法,要是只有一个方法就可以直接Lamda表达式。
那以上,我们把保存活动记录,并获得返回自动主键的sql语句写在了ActivityMapper.xml中,那还可以不写在xml里,比方说以注解的方式去做,但对于复杂的sql,我们还是建议写在xml中,这里我们注释掉ActivityMapper.xml中的这段sql,再以注解的方式去实现这个sql语句:
那现在我们来到ActivityDao.java,这个接口中,在dao中的intinsertObject(Activityactivity)方法上面,以注解的方式去实现保存活动记录的功能,那我们直接在方法之上写@insert,然后把之前在ActivityMapper.xml中写好的sql语句直接复制过来:
packagecom.cy.pj.activity.dao;importjava.util.List;importorg.apache.ibatis.annotations.Delete;importorg.apache.ibatis.annotations.Insert;importorg.apache.ibatis.annotations.Mapper;importorg.apache.ibatis.annotations.Options;importorg.apache.ibatis.annotations.Select;importorg.apache.ibatis.annotations.Update;importcom.cy.pj.activity.pojo.Activity;@MapperpublicinterfaceActivityDao{//...@Insert("insertintotb_activity(title,category,startTime,endTime,remark,state,createdUser,createdTime)values(#{title},#{category},#{startTime},#{endTime},#{remark},#{state},#{createdUser},now())")@Options(useGeneratedKeys=true,keyProperty="id")intinsertObject(Activityactivity);//...}重启服务,重新再创建一个活动,可以测试程序依然是可以正常运行的。
最后来到控制层ActivityController的doSaveActivity方法中,调用activityService.saveActivity(entity);方法,代码如下:
对于格式问题的解决方案,一般可以由前端去锁定,不允许用户自己输入,只能通过日期框进行选择:
那这个功能是如何实现的呢,用的就是基于Bootstrap前端框架扩展的第三方插件Bootstrapdatepicker,见后面有实现;那到这里,保存操作的基本逻辑流程就走通了,接下来再做一个删除功能:
点击delete按钮,我们可能是基于所选记录的id进行删除,如果基于id进行删除,那我们在服务器端就需要在ActivityController中添加这么一个方法doDeleteById,或者叫doDeleteObject,然后这个方法需要一个入参id,这个id的类型最好用Long,因为我们的pojo类中根据数据库字段定义的就是Long类型,与其保持一致。然后我们,在方法体内部把id打印一下,然后是返回值,删除完成后,还可以再查询一下,重定向到查询的请求,我们可以先简单一下,因为这里以重定向的方式并不理想,这里也可以直接在页面上更新,后面再说,最后映射请求路径为:"/activity/doDeleteObject"。代码如下:
@RequestMapping("/activity/doDeleteObject")publicStringdoDeleteObject(Longid){System.out.println("delete.id="+id);//...return"redirect:/activity/doFindActivitys";}控制层先写到这里,我们在客户端做一些修改,看看能否在控制层ActivityController中拿到活动记录的id,基本写法如下:
我们来到activity.html这个页面,找到删除按钮的位置,
delete | ,这里如果想用thymeleaf提供的方式触发点击事件函数,可以这样写,th:onclick="doDeleteObject([[${aty.id}]])",即delete | 其中,想把通过thymeleaf的取值方式,${aty.id},得到所需活动记录的参数,做为js函数方法的入参传给函数,则thymeleaf中规定,必须在其外层包裹两个方括号(比如这里是[[${aty.id}]])才可以。这是thymeleaf中规定的格式:点击确定后注意,我们没有真的删除这条记录,这时我们还没有做删除,看控制台执行结果,可以发现我们已经取到了这条记录ID:
拿到这个id以后,我们就可以继续下一步操作,基于这个id进行删除活动记录的业务。另外,我们这里请求路径传参的方式是以问号对请求路径与请求参数进行分割的,这是典型的java路径传参方式,那这里还可以使用更为普及的restful风格的方式进行传参,即根据斜杠的方式传参,实现方式如下:
@RequestMapping("/activity/doDeleteObject/{id}")publicStringdoDeleteObject(@PathVariableLongid){System.out.println("delete.id="+id);//...return"redirect:/activity/doFindActivitys";}相应的客户端,activity.html,请求路径url上就不是写问号了,直接是以斜杠连接:
@Delete("deletefromtb_activitywhereid=#{id}")intdeleteObject(Longid);那下一步,我们可能要去写业务层,业务层接口ActivityService中也是定义一样的方法:
@OverridepublicintdeleteObject(Longid){//校验活动状态(正在进行中的活动不允许进行删除)//.........//删除活动introws=activityDao.deleteObject(id);returnrows;}最后,我们在控制层ActivityController.java中,再调用一下业务层的这个实现类的deleteObject方法,控制层不做具体的业务,任何逻辑校验,它只是调用业务对象来执行业务,它做的是流程的这个控制,以及请求和响应数据的一个处理,即控制层就是拿客户端的数据,调用业务层方法,然后响应。
@RequestMapping("/activity/doDeleteObject/{id}")publicStringdoDeleteObject(@PathVariableLongid){System.out.println("delete.id="+id);activityService.deleteObject(id);return"redirect:/activity/doFindActivitys";}那重启服务,刷新,点击delete按钮,确定删除,发现数据就会真正的被删除了:
我们发现如果这样一个一个删除,太麻烦了,那将来能不能在这做一个全选,一个一个删除太麻烦了,当然是可以的;我们用模态框实现了创建活动的活动信息录入新增功能,很多场景都会有这种模态框的实现,虽然不是我们自己写一个模态框,但至少我们用了一下它,把它的内容改成自己的业务需求,以及删除和查询功能,我们都做了,将来随着活动数据越来越多,我们可能还要做分页,所以我们在此项目的基础上,可能还会写其他的一些业务,然后做一些业务方面的扩展,那我们如何基于我们的技术来实现我们的业务需求,那当我们还没有这种以业务为中心地去理解技术实现的思路时,那思路和代码哪个更为重要呢?比如,我们需要在创建活动时开启倒计时,在删除时需要对活动状态进行校验等等。那关于修改操作,最好是通过ajax去实现,因为修改这块数据的回显的写法,需要引入大量的js脚本,可能会有一定的难度,而保存和删除加一起也就5行代码左右,易于去分析和理解。
那对于一些前端技术的实现,比如日期选择框,工作后可能10几年也不会写这个东西,而是用现成的工具,那这个现有的工具怎么做,比如bootstrapdatepicker前端插件,那这个插件可以去网上下载它的静态资源,比如从github上下载的bootstrap-datetimepicker-master,这个就是datepicker官方里给出的内容:
这个文档里有Bootstrapv2,还有Bootstrapv3的,那我们这里用的是Bootstrapv3的版本,所以我们一定是看的sampleinbootstrapv3,这个里面的内容,从这个目录里去找index.html:
打开这个index.html,这个文件就是datepicker的demo案例,除了Bootstrap的css样式文件和js脚本外,首先发现在这个demo的head里引入了一个datetimepicker的css:
其次,它又在这个body体内的最下面的js脚本部分,引入了datepicker的js脚本,还引入了一个使用什么语言的js脚本,它这个语言用的是bootstrap-datetimepicker.fr.js,这个fr是法语的吧!
再往后,它又说你在初始化时,可以以以下3中形式进行初始化:
不管是什么,我们去应用一个技术,重点于会用,而不是把它的所有的技术细节都搞明白了,所以我们现在就去试验,如何使用这个datepiker框架:
那我们在这个activity的活动项目中就做了这样一件事,在项目的src/main/resources/static目录下添加了datepicker的这个静态资源文件datepicker,其中包括css和js两个子目录
我们发现css和js目录下分别有两个名称相同的,但后缀一个是css,一个是min.css的两个文件,有min标识的文件,是压缩版的css,一般项目上线都是使用压缩版的文件,在压缩版的文件中去除了文件中的缩进结构,所有的代码可能就在几行之内,而未压缩版的,即min标识的文件是我们开发时用的,它的可读性就会好很多,保留了开发过程中原始的缩进与换行的标识。
首先我们引入boostrap-datepicker到我们的页面里:
对于日期框,也可以使用js的默认日期框(这样就可以解决datepicker还必须选择小时和分钟的情况),直接把input的type属性改为date即可,而无需下载其他样式文件,但这种方式存在很多兼容性问题(经过试验,不同的浏览器对date的支持并不友好,所以此处应用了datepicker日期插件):
另外,对于日期框,浏览器还可能出现自动记忆填充功能,解决方案如下:
我们至少先写出来,如果说后面还有其他的一些需求,可以再去改,那么点击save保存后,我们发现保存成功了:
但是,我们发现在日期的位置,没有显示小时和分钟,只有年月日,那我们还想显示小时和分钟,那说明我们日期的格式化在我们客户端activity.html这里,还有点小问题,如果我们后面还想显示小时和分钟,就在客户端获取日期时,将其格式为'yyyy/MM/ddHH:mm'即可:
delete | 实现效果如下:到这里Activity活动模块的基本任务就算完成了,那基于这个小案例,我们继续扩展一些新的内容,什么是健康检查,什么是热部署,还有Lombok插件的应用等,首先我们先看一下健康检查的应用,那什么为健康检查,就是在SpringBoot当中,它还给我们提供了一个监控功能,比方说监控你的url和bean之间的这种映射,监控你的bean对象是否注入到我们的容器中,监控你的系统当中的一些环境的配置都可以,其实这个健康检查无非就是一个监控功能。那要在STS中去应用它,首先我们需要在项目中的pom.xml配置文件中添加健康检查的依赖:
假如希望查看更多的actuator的选项及信息,想监控更多的信息,可以在Springboot中的配置文件application.properties里添加如下语句(生成环境不加):management.endpoints.web.exposure.include=*,假如我们应用的是阿里云构建的SpringBoot项目,那在创建项目时,它的application.properties文件中默认这句话就有,假如是spring.io.starter构建的SpringBoot项目,那就需要手动去添加上这句话:
可以看到输出了一堆的beans,但这个看起来很不舒服,我们可以通过Ctrl+F,去找自己写的bean,比如activityService,默认情况下的输出结构确实不够爽,那想让这个结构呈现的时候更加清楚,可以对谷歌浏览器安装一个jsonView插件,就可以以更加结构化的方式去查看bean的信息了,但要安装这个插件,首先能够上Google网站才能安装,或者是在本地有下载离线版的,当然如果在线安装,则必须保证网络是畅通的,比如说打开Google设置中的更多工具,有一个扩展程序,
在打开的窗口中点击左上角的菜单图标,就可以找到打开Chrome网上应用店:
打开Chrome网上应用店,搜索JsonView,可能会出现多个JsonView,哪一个评价好,就用哪一个,我们就选第一个2832人的就可以了,添加至Chrome:
利用浏览器去查看健康检查的监控信息了解即可,我们重点要掌握的是健康检查,还可以直接在sts工具的BootDashboard(Boot面板)中选中项目,查看其属性(showproperties):
首先我们必须要确保项目已经启动了tomcat,然后打开BootDashBoard面板,选中启动项目,然后点击右上侧工具栏的一个小图标ShowProperties:
打开ShowProperties视图,首先看RequestMappings是映射,这映射里面就记录了我们写的添加:/activity/doSaveActivity,删除:/activity/doDeleteObject/{id},和查询:/activity/doFindActivitys,这里呈现了url对应的是哪一个方法,这就叫做监控;
然后,还有一些Beans,在Beans这个环境里面,我们可以找到我们自己写的bean,有activityController,activityDao和activityServiceImpl,其他的那些就不是我们自己写的,系统底层自动生成的。
打开activityController,我们可以发现它所在的是哪个类,它所依赖的是activityServiceImpl这个对象,以及这个activityController需要注入给谁么,它不需要注入给谁;
然后这个activityServiceImpl呢,它依赖于activityDao,activityServiceImpl会注入给谁,注入给activityController,这个结构是非常清楚的:
最后,看这个activityDao依赖于谁,activityDao依赖于一个是工厂sqlSessionFactory,一个是sqlSessionTemplate,依赖于这两个对象,那activityDao,它会注入给谁呢,注入给我们的activityServiceImpl:
那url和Beans的信息都看完了,最后还有一个就是环境Env,比如这个applicationConfig:[classpath:/application.properties],这是我们自己的那个配置文件,我们自己配置的那个环境,会在这个位置显示:
比如说,我们可以看到我们配置的,
健康检查:management.endpoints.web.exposure.include=*
映射文件的路径:mybatis.mapper-locations=classpath:/mapper/*/*.xml
连接池的配置:
spring.datasource.username=root
spring.datasource.url=jdbc:mysql:///dbactivityserverTimezone=Asia/Shanghai&characterEncoding=utf8spring.datasource.password=******
前端框架thymeleaf的配置:
spring.thymeleaf.prefix=classpath:/templates/modules/
spring.thymeleaf.cache=falsespring.thymeleaf.suffix=.html
日志的配置:logging.level.com.cy=debug
以上这是我们自己配置的内容,除了我们自己配置的内容,还可以看到这块还有一些系统的环境systemEnvironment:
比方说,默认情况下,我用的那个jdk在哪个位置:JAVA_HOME=C:\CGBSoft\First\jdk1.8.0_45,这里都属于一些监控信息。那这个ShowProperties窗口,在打开的时候可能没有显示,是有这种可能的,有的时候这个窗口它会卡顿,它反应慢,这时可以把这个窗口来回拖动一下,基本上都没问题:
那健康检查其实就是SpringBoot提供给我们的一个监控,那通过这个监控,我们可以在监控里面得到什么样的信息,解决什么样的问题呢?首先在RequestMappings里,一看就是url映射,那么通过这里,可以去检测什么问题呢,比方说可以通过此映射检测404问题,404问题无非就是你访问的url,在我们这个RequestMappings里面找不到对应的资源么,那就可以在这里看看你的url对应的资源是否存在;然后在Beans里面也有一部分信息,Beans这部分内容里面可以去解决这个问题,通过这里可以对Spring中的bean进行检查,例如可以发现NoSuchBeanDefinitionException,当在这个Beans里面都找不到bean,那启动时一定是没有的,就是当我们出现了NoSuchBean这样的异常,但又不知道是哪个bean,那首先要找到自己的那个类,在Beans这里就可以检查一下;而Env这部分里面定义的是什么呢,就是我们说的那个配置信息,这部分配置信息包含我们自己定义的,也包含系统帮我们去定义的一些配置信息:
我们点开这个Env,可以看到如下信息:
我们再点开applicationConfig,这个应用程序配置都是我们自己的配置:
比方说:我们配置的端口server.ports:
再比方这个commandLineArgs这个参数:
还有一些什么系统属性systemProperties,有很多,比如说我们现在用的java指定的版本,java.specification.version=1.8;比如我cpu的一些配置信息,sun.cpu.isalist=amd64;还有我们的编码方式sun.jnu.encoding=GBK;等等等等,这里都可以去看到:
以及我们自己配置的那部分内容:
这个就叫做监控,也就是说所谓的,对url,对bean,以及系统环境的一个监控,在这里面我们都能看到。
那下一个话题是热部署,对于热部署来讲,其实在我们的项目当中,假如我们改了java代码,我们还需要手动去启动我们的tomcat服务器,才能够去加载我们的这个改动,那么假如我们希望,当我们改了代码以后自动去加载这些配置信息,那就需要在项目的pom.xml配置文件中添加一个依赖:
org.springframework.bootspring-boot-devtoolsruntime这个依赖叫做自动部署依赖,如果我们没有配置热部署,没有引入热部署的包依赖,无论修改了项目中的任何代码,控制台都没有反应:
我们的tomcat服务器是不会自动重启的,我们必须手动重新部署项目。而一旦我们配置了热部署:
当修改一些文件时,tomcat服务器都会自动重启,但是对于硬件配置比较低的电脑,不建议配置热部署,而是手动去重启,因为我们每写一部分,它都要重启一下tomcat,其实是非常占用内存的,因为重启的过程,它需要涉及到资源的销毁,资源的重建,如果内存配置比较低,那就不要去开启热部署了。那加载了热部署,一旦我们修改了如下代码,tomcat就会自动重启:
(1)src/main/java目录下的源代码;
(2)src/main/resources目录下的application.properties配置文件;
(3)src/main/resources目录下的MyBatis的Mapper映射文件;
(4)项目目录下的包依赖的pom.xml配置文件;
比方说,我们在项目启动类CgbActivity01Application.java的位置,加一个换行或者加一个输出语句;
比如在application.properties配置文件中配置一个server.servlet.context-path,或者配置tomcat能支持的最多线程数:server.tomcat.threads.max=256(就类似于任务调度时底层Tomcat线程还会去启动另外一个线程Timer,启动那个线程和Tomcat线程是不一样的,Tomcat线程主要是负责处理客户端的请求);
比方说,我们在映射文件ActivityMapper.xml注释掉或解注释掉一段sql语句;
比方说,在项目的pom.xml文件中添加或去掉一个依赖,比如这个健康检查依赖:
以上资源的改动都需触发热部署,但是如果改动的是静态资源,比如src/main/resources/templates下的html页面,以及改动src/test/java目录下的测试类的时候,都不会重启,所以说,了解了项目结构以后,所谓的热部署就是我们修改了某些资源以后,tomcat会重启,来加载我们修改后的资源,而哪些资源修改后会触发热部署机制,自动重启tomcat加载资源,哪些资源的改动并不会进行热部署:
在目录结构中,有一个目录是src,这个src的内容是不需要我们去写的,我们在src/main/或者src/test等目录下面写的所有内容,都是存储在src的目录下;然后src目录下面生成的.class文件,还有我们的配置文件,都会存储到target目录下面,但是我们是无法点开这两个目录去查看的,只有进入工作区才可以看到,再下面的HELP.md,mvnw和mvnw.cmd,这是使用maven工具创建项目时自带的一些文件,即这个就是我们的model。
说明:当我们修改了src/main/java目录下的java文件或者修改了src/main/resources目录下的配置文件时,默认都会重启你的web服务器,但是修改了测试类或html文件不会自动重启和部署,但是假如希望修改了html,在重启tomcat的情况下也能看到页面模板内容的变化,需要配置spring.thymeleaf.cache=false,这样即便不重启服务器,只需在浏览器端刷新下页面也是可以进行实时的页面更新加载的。
那热部署到这里就告一段落了,下一个内容是Lombok插件的应用:那为什么要使用Lombok呢?首先在我们项目当中的会创建很多很多的pojo类,在这样的类里面,我们写好了属性以后,我们会通过sources,自动生成一些set/get等方法,假如我们写好了这个pojo类里面的属性以后,想让这些set/get/toString等方法由系统帮我们添加,而无需手动去添加,就可以通过Lombok帮我们实现,这样的话,至少我们源代码的代码量会减少,即.java代码不用我们自己去写,但编译后生成的.class文件里面会有,或者说在.java编译成.class的时候,我们希望在这个class里面植入一部分代码,那么就可以使用Lombok。
那么Lombok是什么,它就是一个第三方库,即由别人写好的,提供出来的一组API,我们可以基于Lombok中提供的API,在我们的.java程序编译成.class文件的时候,自动给它织入一些方法,我们的点java程序中没有这些方法,但是把.java程序编译成.class文件的时候,Lombok会自动添加这些方法;
那么如何使用Lombok呢,首先添加依赖:
另外,SpringBoot其实默认集成了这个插件,那有的时候,如果不想这么去拷贝依赖,那也可以在我们的工程当中,选中pom.xml,右键选择Spring,选择EditStarters,如果联网还可以的话,就可以在弹出的EditSpringBootStarters这里搜索,找到Lombok插件:
如果之前用过Lombok,就会显示Lombok默认已经选中了:
那我们已经在项目当中,把Lombok的依赖添加上了,可以在MavenDependencies目录下找到这个依赖:
可以在MavenDependencies下找到Lombok,复制它的CopyQualifiedName,然后在windows窗口直接输入此地址,直接回车:
发现回车不可以,说明了我这个jdk,它不是安装版的,它是配置了一个绿色版的,在这里面拷贝过来了,如果要是一个直接安装版的jdk,就可以直接把CopyQualifiedName,这个复制的.jar文件的地址粘贴到windows窗口上,一回车就会去自动运行它。
那如果不是安装版的jdk,可以去掉CopyQualifiedName后的文件名,进入到lombok.jar文件所在的目录中:
我们可以选中这个lombok的jar文件,鼠标右键打开命令行,如果这样找不到的命令行,还可以在这个目录的windows窗口上直接输入cmd,或者通过win+R+cmd进入命令行,然后输入目录地址切换到此目录下,通过java-jarlombok-1.18.12.jar命令,回车即可(说明:有的可能双击.jar文件就可以执行,有的可能双击不了,因为jdk是解压版的,不是直接安装的。):
然后,可能会弹出一个警告框Can'tfindIDE,不用去管它,直接点击确定即可:
然后,在Specifylocation..这个位置选择你的sdk要安装在什么位置:
这里要确保现在的Lombok装在正在使用的那一个STS的工具上面(已经添加有lombok依赖),然后点击Select:
然后点击install:
最后显示安装成功,叉掉这个窗口即可:
这里需要注意的是,STS所在的路径中不允许中文或特殊符号,比如$&等,否则我们的STS可能就启不来了,还得需要做额外的一些配置,假如按照成功以后,在我们的STS所在的目录下就会多出一个lombok.jar文件,有了这个文件以后,那接下来还得去修改一下STS所在目录下的SpringToolSuite4.ini文件:
在这个SpringToolSuite4.ini文件里面,它会对lombok做一个自动的配置,打开这个文件,它会多出这么一行:javaagent:D:\CGBIIICLASS\TOOLS\sts-4.7.0.RELEASE\lombok.jar:
即安装好lombok以后,就会在此文件里多出这么一行记录,那么多出这么一行记录,它指定了你安装的这个lombok.jar,这个文件所在的目录,即我们这个STS工具的目录下多出的lombok.jar所在的目录,那么它就会在这个ini配置文件有这样的一个记录,假如这个指定的目录存在中文或特殊符号,比如&符号,它最终在编译的时候,有可能会在&符号这里替换一个斜杠,那么lombok就找不到了,找不到以后,那这个位置可能就要报错了,STS工具启动都起不来了:
所有步骤安装完成以后,我们重启STS工具,看看STS是否能成功启动,如果可以正常启动,说明安装到这里是没有问题的,那启动成功以后,下一步就可以去验证我们的lombok是否安装成功或者说lombok有效了,我们可以在CGB-ACTIVITY-01这个项目里面,把这个pojo类Activity.java中的所有的set/get方法,以及toString方法全部去掉,只留有属性信息,这些属性会提示有警告标识,然后我们在Activity类之上去追加一个注解@Data,当我们加上这个@Data注解以后,就会发现这些属性上的警告没有了,这时就说明我们的lombok已经安装成功,而且可以正常使用,即系统当中会帮我们把这个类的属性,自动生成相应的get/set等方法,那么在我们这个Activity.java这个类上,就正确的使用了lombok里面的特性,在这个pojo类编译成.class文件的时候,lombok自动会帮我们添加set/get以及toString方法。
拦截到以后,有lombok.jar来进行我们的.java程序的编译,把编译好的一些内容,比如Activity.java这个类,编译好了以后,还会向这个类里面再添加一些set和get方法,那我们已经在Activity.java这个类里面添加了@Data注解,那lombok是否真的已经为我们添加了set和get方法,我们可以测试一下,比如我们在src/test/java/com/cy这个测试包下面,专门在这写一个测试类LombokTests,看一下有没有这个lombok,首先new一个Activityaty,然后aty.setId(100L);,aty.setTitle("互动答疑0");,发现是可以调用set方法的,那么这个set方法,我们并没有去写,但是生成了,我们在输出一下aty,System.out.println(aty);,执行此测试方法,单元测试成功:
这里我们输出了对象的内容,而不是对象的那种地址的表现形式,那么说明我们这里面除了添加了set和get方法,那么在输出对象的时候,也调用了对象的toString方法,而我们也没有重新toString,说明在我们Activity这个类的上面加了@Data以后,它帮我这个类生成了set/get,还有toString方法,还有hashcode,还有equals方法,它其实都帮我们生成了,都是由@Data注解描述此类时,但是如果只想要set和get方法,不想要其他的方法,那就可以直接加@Setter和@Getter,去掉@Data,这时就没有所谓的toString方法了,这时再对这个对象输出时,就只是我们的类全名和对象的hashcode值,比如com.cy.pj.activity.pojo.Activity@28a9494b,那如果我们再在这个类上加上@AllArgsConstructor,此时会发现这个Activity这个类就会报错,因为@AllArgsConstructor表示会在这个类里面添加一个全参的构造函数,我们在这个类当中添加了带参的构造函数,那么无参构造函数的默认就没有了,所以这是在LombokTests这个测试类中的测试方法testLombok,在newActivity()无参构造器的时候就会报错:
那我们在Activity.java这个pojo类中再添加一个无参的构造器,添加注解@NoArgsConstructor,如果还想添加toString方法,就添加注解@ToString:
这里的这些注解仅仅起到一个标识性作用,真正在这个类里面添加方法,不是说注解去添加,注解起到了这个标识以后,是底层会赋予这个类一些额外的特性,所以这里面要对这个注解要有一定的认识,这些这些可以被称为元数据,即描述数据的数据都叫做元数据,这里提到的都是最常用的,当然还有其他的注解,比如说,我们在我们的测试类LombokTests上,又加了一个注解@Slf4j,然后在testLombok方法内的最后,再做一个输出,log.info("titleis{}",aty.getTitle());,可以发现我们在这个类里可以直接使用log,但是我们在这个类里面并没有定义log,却依然可以使用它,只是因为我们加了一个@Slf4j注解,我们这个log就会自动生成;这个Slf4j之前有写过,是这样的,privatestaticfinalLoggerlog=LoggerFactory.getLogger(LombokTests.class);,都是利用这个Slf4j这个包下面的,这里还使用了一个设计模式,叫门面模式(阿里手册有说明)。假如在LombokTests类中加上这样一句话,那么lombok的@Slf4j这个注解就不起作用了,有一个警告:
单元测试成功,上面是toString方法,下面就是INFO就是我们自己输出的com.cy.LombokTests:titleis互动d答疑,Lombok具体的日志信息,这就是Lombok的应用,但是当我们引入了@Slf4j注解以后,发现这个log没有生成,说明这个STS这个软件有可能Lombok没有安装成功。
那么Lombok解决的问题就是,当我们写了一个.java程序,我在这个.java程序里面可能没有写LoggerFactory.getLogger(LombokTests.class);这样的语句,lombok可以帮我们自动生成这样的一条语句,也就是说我们的程序会变的,好像越来越智能了,有些代码不用我们写,也能够生成,这都是底层帮我们做的,比如说Lombok帮我们做的。Lombok仅仅起到的作用是让我们自己写程序的时候简单一点,其实它的.class文件一点都不简单,它的.class文件生成的还是和我们原先写的没有Lombok简化之前的代码的时候一样的内容,只是lombok帮我们在写java代码的时候
拷贝并粘贴下面给出的HTML代码,这就是一个最简单的Bootstrap页面了。
server.servlet.context-path不配置时,默认为/,如:localhost:8080/xxxxxx
当server.servlet.context-path有配置时,比如/demo,此时的访问方式为localhost:8080/demo/xxxxxx
1、springboot2.0之前,配置为server.context-path
2、springboot2.0之后,配置为server.servlet.context-path
原来的运营项目(已上线),配置文件添加server.servlet.context-path配置后,需要在thymleaf中进行action请求的追加吗?
答案:不需要。
栗子:
前端页面采取form请求
action拦截接受方式
@Controller@RequestMapping("/user")publicclassLoginController{@PostMapping("/userLogin")publicStringuserLogin(HttpServletRequestrequest,Modelmodel){
THE END
1.129方工装打印店一层快印店装修效果图酷家乐为设计师提供海量室内装修设计效果图作为灵感参考,您正在浏览的是129方工装打印店一层快印店https://www.kujiale.com/designpic/3FO4JUH0JFWI/141
2.图文快印店装修装修效果图图文快印店装修室内设计图片大全设计本为您提供图文快印店装修装修效果图,包括图文快印店装修装修图片、图文快印店装修室内设计图等,看图文快印店装修装修设计图片就到设计本。https://www.shejiben.com/works/t207983
3.用Word怎么打印门面出租用Word怎么打印门面出租浏览量:1528Word怎么把门面出租 用Word打印房屋出租怎么 Word打印出租怎么排版 怎么用Word 打出租俩字 用Word怎么打房屋出租 怎么在Word打出租 Word出租怎么打 Word怎么打出租 为您找到1500条“用Word怎么打印门面出租”相关问题这样的门面效果图是用什么软件做出来的? 共2条回答 > N&H _https://www.3d66.com/answers_relation/relation_606804_3.html
4.防水门头招牌效果图(托管招牌门头设计效果图)主文字超级字工艺/小字英文logo3D打印字案例十:优点造型材料结构:铝塑板/线条灯制作工艺:20X40镀锌方钢焊接/结构耐候胶粘板文字工艺:景观字厚度15厘米/小字英文logo3D打印字标题:防水门头招牌设计效果图店面设计包括以下五个方面设计:招牌设计,店门设计,橱窗设计,外部照明设计,壁面照明。店面设计是店面给人的整体感觉,http://51sole1115422.51sole.com/companyproductdetail_394363250.htm
5.正规图文快印打字复印学习机构另外每个人都会有快印方面的需求,如求职书、论文、复印材料等,企业就更不用说了,小量的材料都是自己设的文印室打印,但是大型的喷绘或者做的广告文案、标书之类的也是要求助于图文广告公司,建筑的图纸和设计方案、效果图等都是需要专业的图文广告公司来协助输出。这些需求可以说一直是比较庞大的,正所谓有需求就有https://www.pxto.com.cn/news-taotaoju/34557838.html
6.商业商户店铺装修手册.doc三、 竣工图要求装修施工完毕,租户应向业主提供竣工图打印版 2 套(每页图纸盖竣工出图章或租户公章)以及 光盘 1 张(须同时提供 PDF 及 CAD 格式),包括门面效果图、室内装修竣工图(平面、立面、施工 图),机电、空调、消防竣工图。图中应包括所有更改的隐藏管路等,以便于维护和存档备查。 5 附件 ( 2 ) XXXhttps://max.book118.com/html/2020/0302/8142016063002100.shtm
7.广州天琥园林效果图培训,广州天琥3D景观设计培全面系统学习CAD的绘制命令,科学规范地建立图层、设置文字样式和标注样式。通过多个实例制作,掌握CAD施工图的绘制流程,可熟练绘制平、立、剖面,以及局部大样图纸,懂得打印输出,终达到当前室外建筑施工图绘图行业的中高级水准。 + 《园林景观效果制作实战》学习目标 https://www.qinxue365.com/course/341.html
8.图文店特色服务方案但随着打印机和复印机的普及,许多单位,甚至个人都购置了这些设备,于是普通的打字复印店已是明日黄花,大多都是在惨淡经营,低端市场的打字复印店也不能满足客户的需求,这时的打印店的确需要开拓新的市场来实现一次新的飞跃。 在建筑工程设计、招标和审批的流程中,效果图和工程图的应用越来越普及,已经成为施工中不可或https://www.360wenmi.com/f/avmhltb3se63.html
9.彩色打印复印彩色打印复印厂家黄页彩色打印复印价格主营产品: 黑白激光打印复印,彩色激光打印复印,宽幅超精细喷绘,彩色效果图打印,CAD工程图打印复印,高速出图晒 地址: 重庆市渝北区泰山大道东段68号附8号注册资本:人民币100 万元(万元)成立时间:2012-10-31 上海丽其豪办公设备有限公司 主营产品: 黑白彩色打印复印; 名片制作; CAD出图; 文本装订; 写真喷绘https://product.11467.com/s-5f69827262535370590d5370.htm
10.服装店设计方案汇报8篇(全文)通过学习本课程,掌握服装效果图的历史、服装效果图的分类、艺术特征、人体结构、人体绘画方法、着装的表现、各种绘画工具的运用和技法、不同质感面料的表现、服装效果图的风格等。 通过学习本课程,达到可以绘制各种风格的服装效果图的能力,用于服装企业设计产品、服装公司展示效果以及社会上的各级服装效果图和服装设计比赛https://www.99xueshu.com/w/fileol8tz0w3.html
11.武汉图书打印装订,大幅面工程图打印,复印,扫描武汉永银数码图文制作_位于武昌_主营武汉图文快印,武昌打印复印,武汉装订,武汉彩打彩印,图文广告工程图纸输出复印,大幅面工程图打印,复印,扫描.价格咨询电话:027-87363689.是国内规模很大的专业数码图文处理公司之一http://www.yongyin888.com/
12.太原市万柏林区开元图文设计工作室打印,三维效果图,CAD制图,彩页设计制作,喷绘,雕刻,板面制作,刻字,室内布置、装饰,门面装饰,大字制作,条幅制作,排版,装订的制作与服务。(法律法规禁止的不得经营,需经审批未获批准前不得经营) ###打印,三维效果图,CAD制图,彩页设计制作,喷绘,雕刻,板面制作,刻字,室内布置、装饰,门面装饰,大字制作,条幅制作,排版https://www.qichacha.com/firm_729a5c35b442096db3d8e0393cb97656.html
13.标书制作工程图复印CAD出图高速蓝图输出效果图写真北京印之好数码快印有限公司是一家集彩色印刷、数码印刷、个性化印刷、标书制作、工程图复印、CAD出图、高速蓝图输出、效果图、写真喷绘、设计印刷、会议资料、广告宣传、展览展示等外宣业务的专业队伍等综合性大型图文快印公司。http://www.yinzhihao.com/
14.图文广告培训一对一教学Q: 如何开图文打印店 在建筑工程设计、招标和审批的流程中,效果图和工程图的应用越来越普及,已经成为施工中不可或缺的部分。设计项目,往往设计费就几十万甚至上百万,可是施工之前是无法让客户看到实物的,怎么能够说服客户接受,并且提供给他们对落成后建筑的整体感觉呢?鉴于此,所有的施工效果都要依靠效果图来展示,传https://zhuanlan.zhihu.com/p/691664472
15.24小时大型打印社图文店图文快印标书打印CAD出图公司现拥有海德宝对开机印刷机、柯美C6500等全球的彩色数码打印机、高精大幅面写真机、奥西工程机、晒图机、柯美黑白打印机及专业、齐全、先进的印后装订设备,为客户提供彩色和黑白数码快印、印刷、工程图、蓝图、效果图打印复印、高精度写真喷绘等服务。公司企业商务产品:企划部门: 画册、宣传册、说明书、CI手册、https://wap.kuyiso.com/changsha/yinshua/9c1ltcnge6891.htm
16.品牌策划包年(设计包年包月服务,泗阳企业再也不用为招聘设计师而一、杨树林文化传媒——是一家多元化经营的公司杨树林传媒是一家集图文广告制作、平面设计、产品包装、品牌策划、建筑效果图设计、视频制作、网站建设、展览展示、软件培训为一体的综合性顾问与设计机构,广泛的涉足政府机关、教育、房地产、卫生、工业、商业等领域企业的视觉识别设计,致力于为客户提供一站式的设计咨询整合https://www.niaogebiji.com/article-544520-1.html
17.打印机打印效果帮助中心 打印机打印效果 打印机打印效果打印效果: 一、标签打印 二、小票打印 针式打印机票据效果图 110mm票据打印效果图 80mm票据打印效果图 58mm票据打印效果图,电脑端打印吊牌效果图 合集图: 如需打印机请移步淘宝秦丝进销存旗舰店和秦丝进销存官方店 打印机快速上手:请点这里!http://qinsilk.com/mms/front/help/help-id732.html
18.SpringBoot2.0基础案例(02):配置Log4j2,实现不同环境日志打印2、日志打印之外观模式 每一种日志框架都有自己单独的API,要使用对应的框架就要使用其对应的API,增加应用程序代码和日志框架的耦合性。 《阿里巴巴Java开发手册》,其中有一条规范做了『强制』要求: SLF4J? Java简易日志门面(Simple Logging Facade for Java,缩写SLF4J),是一套包装Logging 框架的界面程式,以外观模https://blog.csdn.net/cicada_smile/article/details/91348920
19.重庆快印,重庆快印公司,重庆数码快印,重庆喷绘写真,重庆效果图百度 360 神马 搜狗 谷歌 收录 65 - - - - 反链 - - - - - 最近访问 lxjx.cnngbochuang.comwww.milegj2.comwww.hyper-v-mart.comwww.ahjjw.com.cnwww.youfumx.cnwww.biganzigongwen.comwww.tayun.netwww.southbound.cnwww.xjdz600.comservices.shen88.cnwww.hanyifawu.cnm.dh-ga.comwww.statesthttp://restart-ev.com/tools/seo/www.gojs.cn