目录
一、 重要图详解: 2
(一) 图1:DispatcherServlet 2
(二) 图2、ssm框架简图 3
(三) 图3、spring容器 4
(四) 图4、客户端和服务端关系 5
(五) 图5、spring容器创建实例的两种方式 6
(六) 图6、系统架构分层 6
(七) 图7、IOC、DI和DL 7
(八) 图8、乱码问题 8
(九) 图9、Cache缓存 9
(十) 图10、Shiro-Aop实现权限认证流程 12
(十一) 图11、项目流程图 13
(十二) 图12、Tomcat工作图 14
(十三) 图13、JVM(1) 15
(十四) 图14、JVM(2) 15
(十五) 图15、高手班内容: 17
(十六) 单点登录业务实现 17
二、 框架基础知识 19
(一) 前端知识 19
(二) Map与POJO封装数据库数据 20
(三) AOP相关知识 20
(四) Java实体类(entity) 20
(五) js端解决问题方法: 21
(六) SpringBoot快捷键: 21
(七) CMD命令: 21
(八) Nginx命令: 22
(九) 重要网站 22
(十) Springboot注解整理 23
(十一) Springboot项目启动后,spring容器怎么启动。 24
(十二) 静态代码块: 25
(十三) Java中的变量分类: 25
(十四) JVM调优: 25
(十五) Java四大引用: 27
(十六) SpringBoot 29
(十七) Nginx基础命令 31
(十八) Redis基础命令 31
(十九) 几种常用AOP代码示例: 31
1、捕捉业务层异常的切面 32
2、 运行时间的切面 33
(二十) @Controller和@RestController的区别? 35
(二十一) 关Linux系统防火墙 35
三、 手残导致的单词错误问题: 35
四、 代码总结: 37
(一) 页面控制层的返回页面的优化方法: 38
(二) 用户名数据库校验: 38
(三) Insert时用到useGeneratedKeys="true" 40
(四) 方案1:数据层嵌套查询 --> 41
(五) 方案2:数据层多表查询 --> 42
(六) association 43
五、 动吧项目逻辑图分析: 44
(一) 菜单项CRUD 44
(二) 角色项CRUD 46
六、 Bug集 51
七、 Springboot项目搭环境怎么搭: 51
一、重要图详解:
(一)图1:DispatcherServlet
1、浏览器输入网址,发送url请求,经过过滤器处理后,发送到前端控制器(DispatcherServlet),收到请求后通过url地址调用处理器映射器(HandlerMapping)相当于注册中心,里面包括目标对象和目标方法。Url 、controller方法名。
2、处理器映射器找到具体的Controller(可以根据xml配置、注解进行查找),并将Controller名返回给前端控制器;
3、前端控制器把controller方法发送到处理器适配器,由它找到合适的handler处理器进行业务处理
4、handler处理器里包含控制层,业务层,dao层。Controller调用业务逻辑处理后返回ModelAndView到前端控制器,然后前端控制器调用视图解析器viewResolver进行解析,为View添加前缀后缀,并将结果再返回给前端控制器
5、前端控制器根据Model(数据)对View进行渲染(/templates/pages/goods.html)响应到客户端.
HandlerMapping里面的key相当于url,@RequestMapping(url);
HandlerAdaptor[əˈdæptər]找到合适的处理器(handler)处理业务逻辑,大概有9个处理器
(3)Mybatis:mybatis是对jdbc的封装,它让数据库底层操作变的透明。mybatis的操作都是围绕一个sqlSessionFactory实例展开的。mybatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再执行sql命令。
(二)图2、ssm框架简图
(三)图3、spring容器
用springboot工具创建springboot maven项目后,自动生成一个启动类Application.class,启动类上的@SpringBootApplication 注解修饰或描述启动类,用于告诉底层系统,
* 1)读取spring-boot-autoconfigure.jar包中的spring.factories
* 2)对此启动类以及所在包以及子包的类进行扫描,检测此类是否是spring管理的对象
ApplicationContext是一个接口,继承BeanFactory,给工厂创建的对象选择不同作用域的map进行储存
(四)图4、客户端和服务端关系
(五)图5、spring容器创建实例的两种方式
(六)图6、系统架构分层
(七)图7、IOC、DI和DL
(八)图8、乱码问题
Get请求乱码
数据库连接方式乱码
(九)图9、Cache缓存
readOnly=false;从缓存中拿数据是反序列化过去,创造一个新对象,地址变了,obj1!=obj2;如果改了obj1,缓存中不会变化,这样obje2不会改变,数据安全性得到保障。Mybtis系统默认是false,因为安全;
readOnly=true;从缓存中是引用对象,地址不变,obj1=obj2;这样效率高,但是如果改了obj1,缓存中会变化,这样obje2也会跟着改变,数据不安全。
一级缓存
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个sqlSession中,不同的sqlSession中的缓存是互相不能读取的。
一级缓存的工作原理:
用户发起查询请求,查找某条数据,sqlSession先去缓存中查找,是否有该数据,如果有,读取;
如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。
但sqlSession执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读。
如果commit不清空缓存,会有以下场景:A查询了某商品库存为10件,并将10件库存的数据存入缓存中,之后被客户买走了10件,数据被delete了,但是下次查询这件商品时,并不从数据库中查询,而是从缓存中查询,就会出现错误。
既然有了一级缓存,那么为什么要提供二级缓存呢?
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的作用范围更大。
还有一个原因,实际开发中,MyBatis通常和Spring进行整合开发。Spring将事务放到Service中管理,对于每一个service中的sqlsession是不同的,这是通过mybatis-spring中的org.mybatis.spring.mapper.MapperScannerConfigurer创建sqlsession自动注入到service中的。 每次查询之后都要进行关闭sqlSession,关闭之后数据被清空。所以spring整合之后,如果没有事务,一级缓存是没有意义的。
二级缓存
二级缓存原理:
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。
(十)图10、Shiro-Aop实现权限认证流程
AnnotationAwareAspectJAutoProxyCreator是springboot框架的代理对象,通过AopUtils来scan(扫描)Advisor,Advisor是spring框架里的接口AuthorizationAttributeSourceAdvisor是shiro框架里面顾问类,实现了Advisor接口;通过matches方法判断目标类和方法是否有注解,有注解返回true,通过Advice(通知)进行功能加强
(十一)图11、项目流程图
(十二)图12、Tomcat工作图
Protocol:网络数据交换规则,例如HTTP协议
Port:端口
Parse:分析
(十三)图13、JVM(1)
Java的三大虚拟机是HotSpot,JRockit,J9。HotSpot虚拟机包括Server版和Client版。虚拟机装在操作系统(OS)上,OS调用JVM交给CPU执行。
(十四)图14、JVM(2)
.class文件经过类加载器加载。
(十六)单点登录业务实现
业务说明:用户信息一定不能保存到session中,由于(多台服务器)Session不能共享,所以无法实现单点登录.
单点登录实现步骤
一、用户输入用户名和密码之后点击登录按钮发起url请求.
url: "/user/doLogin?r=" + Math.random(),
二、JT-WEB服务器接收用户的参数username/password(明文),将参数发给jt-sso(消费者)系统实现业务处理.
WEB项目的Controller层(Dubbo消费者)
@RequestMapping("/doLogin") @ResponseBody public SysResult doLogin(User user,HttpServletResponse response,HttpServletRequest request) { //获取userIP String userIP = IPUtil.getIpAddr(request); String ticket = userService.findUserByUP(user,userIP); //判断数据是否为null if(StringUtils.isEmpty(ticket)) { return SysResult.fail(); } //将ticket信息保存到cookie中 Cookie cookie = new Cookie("JT_TICKET", ticket); cookie.setMaxAge(7*24*3600); //7天有效 cookie.setPath("/"); //cookie数据读取的范围 cookie.setDomain("jt.com"); //设定cookie的共享 response.addCookie(cookie); //将username信息保存到cookie中 Cookie cookieUser = new Cookie("JT_USER",user.getUsername()); cookieUser.setMaxAge(7*24*3600);//7天有效 cookieUser.setPath("/"); //cookie数据读取的范围 cookieUser.setDomain("jt.com"); //设定cookie的共享 response.addCookie(cookieUser); return SysResult.success(); //正确返回 }
三、JT-SSO系统RPC接收数据之后,链接数据库,实现数据的校验
四、如果根据username和password查询到用户的数据.则开始单点登录的流程.
1)动态生成密钥,要求用户唯一.
2)将user对象转化为JSON数据,利用hash数据结构保存用户信息
3)保存数据时,控制数据的有效时间. 设定超时时间7天有效.
SSO项目的ServiceImpl层(Dubbo生产者)
/** * 1.根据username和password(明文~~密码)查询数据库. * 2.为null return null * 3.不为null, 准备ticket数据 UUID 准备userJSON数据 * 将数据保存到redis中. 7天有效. * 4.返回秘钥. */ @Override public String findUserByUP(User user,String userIP) { String password = DigestUtils.md5DigestAsHex(user.getPassword().getBytes()); user.setPassword(password); //密码加密 //根据对象中不为null的属性充当where条件 关系符=号 QueryWrapper<User> queryWrapper = new QueryWrapper<User>(user); //根据条件查询数据库记录 User userDB = userMapper.selectOne(queryWrapper); //判断userDB是否为null if(userDB == null) { //用户名和密码不正确 return null; } /** * 为了保证redis资源不浪费,则需要校验数据 * 如果检查发现当前用户已经登录过,则删除之前的数据 */ if(jedisCluster.exists("JT_USER_"+user.getUsername())) { //之前登录过,删除之前的ticket String oldTicket = jedisCluster.get("JT_USER_"+user.getUsername()); jedisCluster.del(oldTicket); } //程序执行到这里说明用户输入正确. //3.1获取uuid String ticket = UUID.randomUUID().toString(); //3.2准备userJSON数据 数据必须进行脱敏处理 userDB.setPassword("123456"); String userJSON = ObjectMapperUtil.toJSON(userDB); jedisCluster.hset(ticket, "JT_USER", userJSON); jedisCluster.hset(ticket, "JT_USER_IP", userIP); jedisCluster.expire(ticket, 7*24*3600); //将用户名与ticket信息绑定(防止用户重复登录) jedisCluster.setex("JT_USER_"+user.getUsername(),7*24*3600,ticket); return ticket; }
五、.JT-SSO.将请求处理之后,返回ticket数据给jt-web服务器.
六.将数据通过客户端Cookie保存起来,设定过期时间.设置Cookie数据共享
七.当用户再次访问时,jt-web服务器根据ticket信息查询redis服务器.如果数据存在则回显数据,如果数据不存在则要求用户再次登录.
重点:用户数据回显,需要网页有cookie数据
var TT = JT = { checkLogin : function(){ var _ticket = $.cookie("JT_TICKET"); //2.获取username信息 var _username = $.cookie("JT_USER"); if(!_ticket || !_username){ return ; } //当dataType类型为jsonp时,jQuery就会自动在请求链接上增加一个callback的参数 $.ajax({ url : "http://sso.jt.com/user/query/"+ _ticket +"/" + _username, dataType : "jsonp", type : "GET", success : function(data){ if(data.status == 200){ //把json串转化为js对象 var _data = JSON.parse(data.data); var html =_data.username+",欢迎来到京淘!<a href="http://www.jt.com/user/logout.html" class="link-logout">[退出]</a>"; $("#loginbar").html(html); } } }); } }
URL地址
http://sso.jt.com/user/query/15035732a39ae1dcfdcdbbd647089c3e?callback=jsonp1574039672842&_=1574039672914
SSO项目的控制层UserController(Dubbo生产者)
@RequestMapping("/query/{ticket}/{username}") public JSONPObject findUserByTicket(@PathVariable String ticket, @PathVariable String username, HttpServletRequest request, HttpServletResponse response, String callback) { JSONPObject object = null; //校验ticket是否有效,从redis中获取最终的ticket完成校验 String redisTicket = jedisCluster.get("JT_USER_"+username); if(StringUtils.isEmpty(redisTicket)){ object = new JSONPObject(callback,SysResult.fail()); CookieUtil.deleteCookie("JT_TICKET","/","jt.com", response); CookieUtil.deleteCookie("JT_USER","/","jt.com", response); return object; } //判断redisTicket与ticket是否相等 if(!redisTicket.equals(ticket)) { object = new JSONPObject(callback,SysResult.fail()); //删除cookie信息 CookieUtil.deleteCookie("JT_TICKET","/","jt.com", response); CookieUtil.deleteCookie("JT_USER","/","jt.com", response); return object; } String IP = IPUtil.getIpAddr(request); Map<String,String> map = jedisCluster.hgetAll(ticket); //1.校验IP是否有效. if(!IP.equals(map.get("JT_USER_IP"))) { //IP地址不正确. object = new JSONPObject(callback,SysResult.fail()); //删除cookie信息 CookieUtil.deleteCookie("JT_TICKET","/","jt.com", response); CookieUtil.deleteCookie("JT_USER","/","jt.com", response); return object; } //2.校验ticket数据信息. String userJSON = map.get("JT_USER"); if(StringUtils.isEmpty(userJSON)) { //IP地址不正确. object = new JSONPObject(callback,SysResult.fail()); CookieUtil.deleteCookie("JT_TICKET","/","jt.com", response); CookieUtil.deleteCookie("JT_USER","/","jt.com", response); return object; } //3.表示校验成功 object = new JSONPObject(callback, SysResult.success(userJSON)); return object; }
二、框架基础知识
(一)前端知识
blur()鼠标离开会触发事件(函数)
Click()鼠标点击会触发事件(函数)
Val()函数
val() 方法返回或设置被选元素的值。把this输入框的值返回并加入数组
array.push($(this).val());
prop函数
为jquery中获取对象属性值的一个函数 ,prop函数语法为prop(属性名,[属性值])->赋值
只有属性名时prop(属性名),意思是获取值
var cls=$(this).prop("class");
data函数
为jquery中数据绑定函数
data函数语法 data(key,[value]),给key赋值
$("#pageId").data("pageCurrent",pageObject.pageCurrent);
confirm(message)
message要在 window 上弹出的对话框中显示的纯文本(而非 HTML 文本)
如果用户点击确定按钮,则 confirm() 返回 true。如果点击取消按钮,则 confirm() 返回 false。
if (!confirm("确认删除吗?"))
return;
Checkbox
<input type="checkbox"> 每出现一次,Checkbox 对象就会被创建。
input[type='checkbox']
checked
checked 属性 与 <input type="checkbox"> 或 <input type="radio"> 配合使用。
<input type="checkbox" name="vehicle" value="Car" checked="checked" />
$("#tbodyId input[type='checkbox']").each(function() {
if ($(this).prop("checked")) {
array.push($(this).val());
}
})
(十七)Map与POJO封装数据库数据
数据库表一行记录映射为一个map对象,多行存储到list。
使用map存储数据,有什么优势劣势?
Map封装方便,简单,但可读性差
Pojo对象封装可读好。
(十八)AOP相关知识
Spring中Before通知的目标对象要实现的接口是 MethodBeforeAdvice
Spring中around通知的目标对象要实现的接口是MethodInterceptor
1.AOP作用
名称:面向切面编程
对原有方法进行扩展.在不影响原有的代码的基础之上进行扩展.实现了业务结构的松耦合.
2.AOP案例
问题:必然会造成代码的耦合,处理业务的代码和处理事务的代码耦合在一起.
try{
tx.begin(); //事务开始
itemMapper.insert(item);
tx.commit();//事务提交.
}cache(){
tx.rollback();
}
- 切面代码
try{
tx.begin(); //事务开始
业务执行代码
tx.commit();//事务提交.
}cache(){
tx.rollback();
}
- 业务代码
itemMapper.insert(item);
3.切面
切面 = 切入点(if判断) + 通知(切面中的方法)
4.切入点
- bean: com.jt.service.itemService 按类匹配1个
- within(com.jt.service.*) 按类匹配多个
粗粒度控制
- execution(返回值类型 包名.类名.方法名(参数列表))
execution(* com.jt.service..*.*(..)) 必须了解
execution(* com.jt.service..*(..))
4.@annotation(包名.注解名)
5.通知类型
1.环绕通知 目标方法执行前后都要执行. 控制方法是否执行. 缓存实现!!!
2.前置通知 目标方法执行之前执行
3.后置通知 目标方法执行之后执行
4.异常通知 目标方法执行时抛出异常时执行
5.返回后通知(最终通知) 不管什么时候最后执行的通知
6.全局异常处理
@RestControllerAdvice
public class SystemExeAOP {
/**
* 如果程序出错,应该在页面中返回什么???
* 应该返回SysResult.fail();将数据转化为JSON
* 在Controller中如果出现问题则执行业务操作
*/
@ExceptionHandler(RuntimeException.class)
public SysResult fail(RuntimeException e) {
e.printStackTrace();
return SysResult.fail();
}
}
(十九)Java实体类(entity)
Entity实体,和PO的功能类似,和数据表一一对应,一个实体一张表。
就是属性类,通常定义在model层里面 ,一般的实体类对应一个数据表,其中的属性对应数据表中的字段。
实体是就是Java中的O/R Mapping映射,即数据库中的一个表映射成对应的一个Java类,其中还有一个映射文件。给定一个较复杂的实体关系(如一对一,一对多,多对多),应该熟练地写出实体类!
O/R Mapping的重要部分是表与持久化类之间的映射,现在主要有两种方式:
1、单纯的持久化类映射:表与持久化类之间的映射是通过硬编码的方式写成类.
2、另外的一种是通过XML和持久化类一起来实现映射。
(二十)js端解决问题方法:
debugger,console.log,排除法
(二十一)SpringBoot快捷键:
Ctrl +Shift+T查类
Ctrl +T查接口实现类
Ctrl +O查类的方法,再Ctrl+O查父类方法
找项目的默认自动配置:
pring-boot-autoconfigure-2.2.0.RELEASE.jar->/META-INF/spring.factories
(二十二)CMD命令:
进入C盘-> cd/
进入C盘的文件夹->
cd C:Users 00maven epositoryorgprojectlomboklombok1.18.10
或cd Intel
进入D盘-> d:
进入D盘里的文件夹->cd D: omcat (java -jar 8081.war)
进入C盘-> c:
路径:把D:/tomcat这个路径改为cmd,然后回车
(二十三)Nginx命令:
前提:nginx的命令执行,必须在nginx的根目录中完成
命令:
1.启动nginx start nginx
2.重启nginx nginx -s reload
3.关闭nginx nginx -s stop
(二十四)重要网站
https://stackoverflow.com/ 问答网站
https://mvnrepository.com/ jar包下载
https://adminlte.io 动吧(开源的前端模板)
https://www.layui.com/前端框架,经典模块化前端框架
https://www.bazhuayu.com/八爪鱼,爬网页
Bootstrap最受欢迎的开源前端框架
adminLTE 后端模板,整合了bootstrap
https://www.echartsjs.com/报表库,工作中使用
网站:code.tarena.com.cn
用户名:tarenacode
密码:code_2017
(二十五)Springboot注解整理
@SpringBootApplication
注解一般用于修饰或描述springboot项目的启动类,用于告诉底层系统,
对此启动类以及所在包以及子包的类进行扫描,检测此类是否是spring管理的对象
@Component
注解对类进行描述时,表示此类的对象由spring框架创建并管理
@Scope
注解用于告诉spring框架此类型对象存储到什么作用域中(不同作用域对应不同的存储方式)对于非web项目,bean对象的作用域一般只有两个
1)singleton 表示此类实例在整个内存中只有一份(默认)
2)prototype 表示此类实例在每层请求都会创造一个实例
@SpringBootTest
注解描述的类是一个单元测试类,此类型的对象可以交给spring容器管理
@Mapper
注解用于告诉mybatis框架,此接口的实现类由mybatis框架创建,并且可以将实现类的对象交给spring容器管理,spring容器在存储此类对象时,默认会将类名(首字母小写)作为key存储
@param
ids 可变参数,其中...为JDK1.5新特性
当方法中可变参数在SQL进行引用是,默认可以使用array(xml映射文件)这个变量接收参数值,也可以在方法参数定义是使用@Param注解对方法参数进行修饰,然后在SQL中使用@Param注解指定的名字获取参数值
@return
删除的行数
@PostConstruct
注解用于描述对象初始化方法对象初始化时可以执行此方法
@PreDestroy
注解用于描述对象销毁方法,在对象销毁之前,可以让容器调用此方法,以完成资源释放
@Setter
注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
@Getter
使用方法同上,区别在于生成的是getter方法。
@ToString
注解在类,添加toString方法。
@EqualsAndHashCode
注解在类,生成hashCode和equals方法。
@NoArgsConstructor
注解在类,生成无参的构造方法。
@RequiredArgsConstructor
注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
@AllArgsConstructor
注解在类,生成包含类中所有字段的构造方法。相当于有参方法
@Data
注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
@Slf4j
注解在类,生成log变量,严格意义来说是常量。private static final Logger log = LoggerFactory.getLogger(UserController.class);
例如:private static Logger log = LoggerFactory.getLogger(GoodsTests.class);
(二十六)Springboot项目启动后,spring容器怎么启动。
当程序启动时,首先加载服务器,之后通过服务器启动Spring容器,Spring容器启动后,通过启动类@SpringBootApplication注解,该注解里面的@SpringBootConfiguration注解可以让springBoot中配置类生效,配置文件加载后,通过包扫描等方式创造实例化对象(范围是启动包下的类和子包下的类),程序启动完成。
Spring容器生命周期有tomcat决定,对象生命周期由容器决定.
单例对象的生命周期和容器一样。 启动一个tomcat服务器,就有一个JVM,它不共享。
(二十七)静态代码块:
类初始化时执行静态代码块
类加载可以初始化
(二十八)Java中的变量分类:
局部变量:方法内定义的变量
成员变量:把类内、方法体外定义的变量称为成员变量。
Java中的成员变量分为两种:
一是没有static修饰的,这些成员变量是对象中的成员,称为实例变量。
二是有static修饰的,称为类变量(静态变量)。
(二十九)JVM调优:
package com.cy.java.jvm; //-Xmx3m -Xms3m -XX:+PrintGCDetails //定义内存,和打印GC详情 public class TestObjectMemory01 { public static void main(String[] args) { Obj o=new Obj(); for(int i=0;i<1000000;i++) { o.doMethod();//连续调用doMethod方法,创造大量对象 } } /** * 类变量,实例变量,方法变量 * 打开逃逸分析后, 声明局部变量,方法内声明的变量,array对象是小对象,直接在栈上分配,GC次数少 如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁了,无须通过垃圾收集器回收,可以减小垃圾收集器的负载。 * 声明实例变量,array对象是大对象,在堆上分配,GC次数多 * 声明类变量,也在堆分配 成员变量包含类变量,实例变量。 * @author 000 *-server -XX:-DoEscapeAnalysis 关闭逃逸分析 *方法变量也会被放入堆中 */ static class Obj{//JDK1.8以后系统会默认打开逃逸分析 //byte[] array; //实例变量 public void doMethod() { //小对象,未逃逸,有可能直接在栈上分配 byte[] array=new byte[1]; //局部变量 } } } 开发中尽量声明局部变量,因为,如果能让对象在栈上分配,那大量的对象就会随着方法的结束而自动销毁了,无须通过垃圾收集器回收,可以减小垃圾收集器的负载。 逃逸分析的原理 //Java本身的限制(对象只能分配到堆中),我可以这么理解了,为了减少临时对象在堆内分配的数量,我会在一个方法体内定义一个局部变量, //并且该变量在方法执行过程中未发生逃逸,按照JVM调优机制,首先会在堆内存创建类的实例,然后将此对象的引用压入调用栈,继续执行, //这是JVM优化前的方式。然后,我采用逃逸分析对JVM进行优化。即针对栈的重新分配方式,首先找出未逃逸的变量,将该变量直接存到栈里, //无需进入堆,分配完成后,继续调用栈内执行,最后线程执行结束,栈空间被回收,局部变量也被回收了。如此操作,是优化前在堆中,优化后在栈中, //从而减少了堆中对象的分配和销毁,从而优化性能。
(三十)Java四大引用:
package com.cy.java.refs; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; class RefClass{ /**对象在回收之前会执行finalize()*/ @Override protected void finalize() throws Throwable { System.out.println("finalize()"); } } //JVM GC //-Xmx5m -Xms5m -XX:+PrintGCDetails public class TestRef01 { public static void main(String[] args) { //1.强引用 //RefClass r1=new RefClass();//r1强引用 //r1=null; //System.gc();//手动GC //2.软引用(在GC时,假如内存不足了,有可能会被回收) //SoftReference<RefClass> r2= //new SoftReference<RefClass>(new RefClass()); //System.out.println(r2.get()); //System.gc(); //3.弱引用(只要触发GC,此引用引用的对象就会被回收) WeakReference<RefClass> r3= new WeakReference<RefClass>(new RefClass()); System.out.println(r3.get()); //System.gc(); //4.虚引用(此引用主要用于记录引用的对象是否被销毁了) //PhantomReference List<byte[]> list=new ArrayList<>(); for(int i=0;i<5;i++) { list.add(new byte[1024*1024]); } }
(三十一)SpringBoot
1、parent作用.
SpringBoot旨在简化代码的配置.将公共的jar包进行统一的管理和维护.只要导入parent标签,相当于导入之前的全部公共的jar包程序.
同时内部定义了全部依赖包的版本信息.
maven的jar包依赖具有传递性 A-->B--àC
2、Build标签作用
3、主启动类作用
@Target(ElementType.TYPE) 注解的作用域 类上生效
@Retention(RetentionPolicy.RUNTIME) 标识什么时候有效 编译运行都有效
@Documented 自动关联文档
@Inherited @Inherited阐述了某个被标注的类型是被继承的。
@SpringBootConfiguration 让springBoot中配置类生效
@EnableAutoConfiguration(使能够自动配置) 当依赖某些jar包时,开启自动配置无需人为的干预.
@ComponentScan(组件扫描) 以后代码至少与主启动类同包/子包
(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
(三十二)Nginx基础命令
1.启动nginx start nginx
2.重启nginx nginx -s reload
3.关闭nginx nginx -s stop
启动redis
(三十三)Redis基础命令
1、启动redis
redis-server redis.conf
redis-server 6379.conf/6380.conf
2、进入客户端
redis-cli -p 6379
3、退出客户端: ctrl+c /exit/quit
4、关闭redis/客户端
redis-cli -p 6379 shutdown
说明:如果操作的redis是默认的端口号6379 则命令可以简化
redis-cli redis-cli shutdown
5、查进程
ps -ef |grep redis/java *
7.杀所有redis关键字的进程
ps -ef|grep 'redis'|grep -v 'ii'|cut -c 9-15|xargs kill -9
Kill pid 杀指定几个进程 kill 1 2 3 4
(三十四)几种常用AOP代码示例:
1、捕捉业务层异常的切面
@Component @Aspect public class RuntimeAOP { /** * execution(* com.jt.service..*.*(..)" * 拦截service 中的全部类的全部方法的任意参数 * @param joinPoint * @return */ //@Pointcut("execution(* com.jt.service..*.*(..))") //private void webPointcut() {} //@Around("webPointcut()") @Around("execution(* com.jt.service..*.*(..))") public Object around(ProceedingJoinPoint joinPoint) { Long startTime = System.currentTimeMillis(); Object obj = null; try { obj = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); throw new RuntimeException(e); } Long endTime = System.currentTimeMillis(); Class targetClass = joinPoint.getTarget().getClass(); String MethodName = joinPoint.getSignature().getName(); System.out.println("目标对象的类型:"+targetClass); System.out.println("目标方法的名字:"+MethodName); System.out.println("方法执行时间"+(endTime-startTime)+"毫秒"); return obj; } }
2、运行时间的切面
@Component @Aspect public class RuntimeAOP { /** * execution(* com.jt.service..*.*(..)" * 拦截service 中的全部类的全部方法的任意参数 * @param joinPoint * @return */ //@Pointcut("execution(* com.jt.service..*.*(..))") //private void webPointcut() {} //@Around("webPointcut()") @Around("execution(* com.jt.service..*.*(..))") public Object around(ProceedingJoinPoint joinPoint) { Long startTime = System.currentTimeMillis(); Object obj = null; try { obj = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); throw new RuntimeException(e); } Long endTime = System.currentTimeMillis(); Class targetClass = joinPoint.getTarget().getClass(); String MethodName = joinPoint.getSignature().getName(); System.out.println("目标对象的类型:"+targetClass); System.out.println("目标方法的名字:"+MethodName); System.out.println("方法执行时间"+(endTime-startTime)+"毫秒"); return obj; } }
(三十五)@Controller和@RestController的区别?
1、使用@Controller 注解,在对应的方法上,视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面
2、如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。
3、但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面
(三十六)关Linux系统防火墙
方案一
1、永久关闭防火墙
chkconfig iptables off 关闭
chkconfig iptables on 开启
2、临时关闭防火墙
如果当前虚拟机重启之后,则防火墙依然开启.
service iptables stop 关闭
service iptables start 开启
方案二:关闭防火墙
systemctl stop firewalld.service #关闭防火墙服务
systemctl disable firewalld.service #禁止防火墙开启启动
systemctl restart iptables.service #重启防火墙使配置生效
systemctl enable iptables.service #设置防火墙开机启动
[root@hadoop01 ~]# firewall-cmd --state #检查防火墙状态
not running #返回值,未运行
(三十七)Linux系统命令
改IP命令:vim /etc/sysconfig/network-scripts/ifcfg-ens33
(三十八)Docker常用命令
1、启动容器
docker run -it centos:7 bash -i:交互式 -t:终端
Docker run -dit centos:7 bash -d:后台运行
2、查看容器
docker ps -a
3、删除容器
Docker container rm -f id或name
4、进入容器
Docker exec -it id bash
四、代码总结:
(一)页面控制层的返回页面的优化方法:
@RequestMapping("menu/menu_list")
public String doMenuUI() {
return "sys/menu_list";
}
在PageController中优化返回UI页面的方法。找出共性进行提取,例如:
@RequestMapping("{module}/{moduleUI}")
public String doModuleUI(@PathVariable String moduleUI) {
return "sys/"+moduleUI;
}
(三十九)用户名数据库校验:
服务端代码
Dao层:
@Select("select count(*) from sys_users where ${columnName}=#{columnValue}")
int isExist(String columnName,String columnValue);
Service层:
boolean isExists(String columnName,String columnValue);
@Override
public boolean isExists(String columnName,String columnValue) {
int rows=sysUserDao.isExist(columnName,columnValue);
return rows>0;
}
Controller层:
@RequestMapping("isExists")
public JsonResult isExists(String columnName,String columnValue) {
boolean flag=sysUserService.isExists(columnName,columnValue);
return new JsonResult(flag);
}
客户端代码
<h3 class="msg"></h3>
<form>
<input type="text" name="username" class="form-control" id="usernameId">
<input type="text" name="email" class="form-control" id="emailId">
<input type="text" name="mobile" class="form-control" id="phoneId">
</form>
$(function(){
$("form")
.on("blur","#usernameId,#emailId,#phoneId",isExists)
});
function isExists(){
var columnName=$(this).prop("name");
var columnValue=$(this).val();
var params={"columnName":columnName,"columnValue":columnValue};
var url="user/isExists";
$.getJSON(url,params,function(result){
if(result.data){
$(".msg").html(columnValue+" exists");
}
})
}
(四十)Insert时用到useGeneratedKeys="true"
动吧项目的SysRoleMapper.xml
<insert id="insertObject"
parameterType="com.cy.pj.sys.entity.SysRole"
useGeneratedKeys="true" keyProperty="id">
<!-- useGeneratedKeys表示使用insert操作对应的自增主键值, keyProperty表示将自增主键值赋值给参数对象的
哪个属性(entity里面的id属性) -->
insert into sys_roles
(name,note,createdTime,modifiedTime,
createdUser,modifiedUser)
values
(#{name},#{note},now(),now(),
#{createdUser},#{modifiedUser})
</insert>
关联(嵌套)查询:
SysRoleMapper.xml one2many
<!-- resultMap表示结果映射,一般用于自定义映射或一些关联查询中 -->
<!-- 假如基于id做关联查询,又希望将id值存储到值对象, 可以对id进行专门映射 -->
property映射到列结果的字段或属性, column数据库中的列名
<!-- collection一般应用于one2many查询 -->
<!--
基于角色id查询角色以及角色对应的菜单id数据
1)resultMap表示结果映射,一般在使用自定义映射方式
时,会使用resultMap ;
2)collection 元素一般用于one2many查询
在当前应用中是基于角色id查询菜单id并将查询结果
封装到SysRoleMenuVo的menuIds属性上-->
(四十一)方案1:数据层嵌套查询 -->
<select id="findObjectById" resultMap="sysRoleMenuVo">
select id,name,note
from
sys_roles
where id=#{id}
</select>
<resultMap type="com.cy.pj.sys.vo.SysRoleMenuVo"
id="sysRoleMenuVo">
<id property="id" column="id" /><!-- property在对象类中的属性名 -->
<!-- collection一般应用于one2many查询 -->
<collection property="menuIds"
select="com.cy.pj.sys.dao.SysRoleMenuDao.findMenuIdsByRoleId"
column="id">
</collection>
</resultMap>
<!-- column数据库中的列名,把当前表(sys_roles)的哪个列的值(id)做为参数传递给另一个表(sys_role_menus)查询 -->
<!-- property映射到列结果的字段或属性 ,这里把查到menu_id值放入menuIds数组 -->
<!-- 基于角色id查询角色以及角色对应的菜单id数据
(四十二)方案2:数据层多表查询 -->
<resultMap type="com.cy.pj.sys.vo.SysRoleMenuVo"
id="sysRoleMenuVo">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="note" column="note"/>
<collection property="menuIds"
javaType="list"
ofType="int">
<id column="menu_id"/>
</collection>
</resultMap>
<select id="findObjectById"
resultMap="sysRoleMenuVo">
select r.id,r.name,r.note,rm.menu_id
from sys_roles r left join sys_role_menus rm
on r.id=rm.role_id
where r.id=#{id}
</select>
(四十三)association
SysUserMapper.xml many2one或one2one
association一般应用于many2one或one2one做关联查询
<select id="findPageObjects" resultMap="sysUserDeptVo">
select * from sys_users
<include refid="queryWhereId" />
order by createdTime desc
limit #{startIndex},#{pageSize}
</select>
<!-- association一般应用于many2one或one2one做关联查询 在当前应用是基于deptId查询部门信息并将其存储到SysUserDeptVo对象的sysDept属性中。 -->
<resultMap type="com.cy.pj.sys.vo.SysUserDeptVo"
id="sysUserDeptVo">
<association property="sysDept" column="deptId"
select="com.cy.pj.sys.dao.SysDeptDao.findById">
</association>
</resultMap>
(四十四)resultMap
resultType:Mybatis可以自动的实现对象封装,
前提:表的字段域对象的属性一一对应
resultMap:表示程序员自己手动封装数据
<!--案例
user表 字段: user_id,user_name
User对象 属性: userId, userName
resultType:Mybatis可以自动的实现对象封装,
前提:表的字段域对象的属性一一对应
resultMap:表示程序员自己手动封装数据
-->
<!-- <select id="findAll" resultMap="userRM">
select * from user
</select>
<resultMap type="User" id="userRM">
配置原则 1.写主键映射, 2.写其他字段映射
<id column="user_id" property="userId"/>
<result column="user_name" property="userName"/>
</resultMap>
五、动吧项目逻辑图分析:
(一)菜单项CRUD
查询
图略;
数据层方法:List<Map<String,Object>> findObjects();直接用Map封装数据
映射文件查询需要用到自关联查询,因为需要查到父菜单的名字。
数据层方法:List<Map<String,Object>> findObjects();
<select id="findObjects" resultType="map">
<!-- 方案1
select c.*,p.name parentName
from sys_menus c left join sys_menus p
on c.parentId=p.id
-->
<!-- 方案2 -->
select c.*,(
select p.name
from sys_menus p
where c.parentId=p.id
) parentName
from sys_menus c
</select>
删除
数据层方法:getChildCount(Integer id);deleteObjectsByMenuId(Integer menuId);
deleteObject(Integer id);
业务层:deleteObject(Integer id);
调数据层getChildCount(Integer id)方法判断是否有子菜单,若有子菜单,就抛出异常,请删除子菜单;调deleteObjectsByMenuld和deleteObject删除菜单
(四十五)角色项CRUD
角色管理业务后台API分层架构及调用关系图
查询:
用户发送请求,异步加载页面,前端控制器调doFindPageObjecs方法到控制层,控制层用findPageObjects( String name,Integer pageCurrent)方法调业务层;
业务层方法有findPageObjects(String name,Integer pageCurrent),并调数据层的getRowCount方法得到记录总数和数据层findPageObjects方法;
数据层接口方法有getRowCount(String name)和findPageObjects(String name,Integer startIndex,Integer pageSize);
数据层返回SysRole类封装的PO对象。业务层返回PageObject类的VO对象,包含PO封装的对象和页面信息。控制层返回http响应对象,也就是jsonResult对象。
删除:
用户发送请求,异步加载页面,前端控制器调doDeleteObject方法到控制层,控制层用deleteObject(id)方法调业务层;
业务层方法有deleteObject(Integer id),与控制层方法保持一致最好,并调数据层的方法;
数据层接口方法有deleteObjectsByRoleId(Integer roleId),deleteObjectsByRoleId(Integer roleId),deleteObject(Integer id);
数据层返回rows。业务层返回rows。控制层返回http响应对象,也就是jsonResult对象。
添加
业务层方法有:saveObject(SysRole entity,Integer[]menuIds);
数据层接口方法有:insertObject(SysRole entity);insertObjects(Integer roleId,
Integer[] menuIds);其中entity是PO对象,一个角色可以有多个菜单,多个菜单id用数组封装。菜单与角色联系表sys_role_menus中虽然也有单独id,插入时可忽略,只插入roleId与menuId。
修改
修改前需要先查出数据呈现到页面:
数据层方法:SysRoleMenuVo findObjectById(Integer id); SysRoleMenuVo封装了role表中的id,name,note属性和menu的id数组menuIds[]
映射文件运用了关联嵌套查询,所有只需要一个dao
业务层方法:SysRoleMenuVo findObjectById(Integer id) ;
数据呈现到页面后,用户修改完需要保存,更新页面:
数据层方法:updateObject(SysRole entity);
业务层方法:updateObject(SysRole entity,Integer[] menuIds);
实现类调用updateObject、deleteObjectsByRoleId和insertObjects方法。
六、Bug集
1、SpringBoot项目maven install(打包)
提示没有在jdk环境下运行。处理办法:window->Java->Installed JREs路径里移除jre,add上jdk。
2、String字符串转JSON格式:String本身;
Object格式(User对象)转JSON格式:key,value结构
@Override
public void updateCartNum(Cart cart) {
Cart cartTm = new Cart();
cartTm.setNum(cart.getNum());
cartTm.setUpdated(new Date());
UpdateWrapper<Cart> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("user_id", cart.getUserId())
.eq("item_id", cart.getItemId());
cartMapper.update(cartTm, updateWrapper);
//cartMapper.update(cart, updateWrapper);cart里面包含不更新的数据,所有不能用,自己新建一个对象封装数据
}
七、Springboot项目搭环境怎么搭:
导入jar包
Spring Web ->springMVC需要
JDBC API
MySQL Driver
MyBatis Framework
->SQL需要
Lombok 生成set get 方法
Spring Boot DevTools 热部署
Thymeleaf ->设置动态html
写配置文件application.properties/application.yml;