0、运行全
第一步:Filter拦截图(Shiro用于认证/授权/缓存/记着我/Cookie)等拦截)
第二步:DispatcherServlet(1处理器映射器-2处理器适配器-3视图解析器-4视图渲染器)
第三步:HandlerInterceptor 拦截器
1、在DispatcherServilet与处理器适配器之间,dispatcher让controller方法运行之前/之后执行
2、比如,在规定时间访问,其他时间不能访问
第四步:Controller执行
第五步:Controller调用XxxService的代理对象($$CGLIB)(此对象由AOP切面对象自动生成)
第六步:执行AOP切面对象(@Around/@Before)、执行XxxServiceImpl、执行切面对象(@AfterRunning/@AfterThowing/@After)
@Around->@Before->XxxServiceImpl方法->@AfterReturning成功执行/@AfterThrowing异常执行->@After
第七步:执行DAO(由mybatis控制)
1、Spring
1.1、为什么要使用Spring框架
耦合/解耦
1、传统new对象,导致程序耦合太强
2、用Spring将new对象、管理对象、注销对象交给spring,spring本质就是管理软件对象
1.2、spring运行图
1.3、spring创建步骤
1)在src/main/resources下创建 beas.xml 文件;
2)把需要管理的类的全类名,放入<bean></bean>
3) <bean id="userName" class="com.tedu.pojo.UserName" scope="prototype"></bean>
id是业务层(ac.getBean("userName") )调用的标记,class指向的人全类名,scpore是定义单例/多例;
4)ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("beans.xml");
将beans文件读入配置,spring根据beans里的bean去找类,并自动创建对象,并存在对象池(map),把id值作为key值;
5) ac.getBean(“user") ;
getBean(User.class);
Class type = ac.getType("userName");
type.newInstance()
ac.isPrototype("userName")
ac.containsBean("userName”)
6、对象属性赋值,注入方法
<bean><property name="name" value="韩少云"/>
<property name="userInfo" rel="userInfo"/> 写入一个对象</bean>
1.4、什么时候使用单例和多例?
1)使用频率高、无线程安全问题用单例(创建放入对象池,程序关闭注销)
2)使用频率低、被多个线程共享(不放入对象池,GC自动管理生命周期)
1.5、全注解:通过扫描装配Bean
1)创建配置文件类AppConfig.java=beas.xml
@Configuration //此注解声明此类为配置文件类
@ComponentScan //此注解声明,扫描此文件所在包及子包;
public class AppConfig {}
2)声明此文件需要被装配到beas池里,
@Component() 此声明的类会被spring装配入bean池并管理
@Scope("prototype") 声明为多例或单例
@Lazy 延迟加载,当被调用时,才真正第一次去实例化
public class UserDao {
@Autowired
private Animal animal; //把Animal类(需加component)实例化并自动赋值给animal
}//调用名称是UserDao 头字母小写userDao
com.tedu.pojo Animal.java
@value("张三")
String name;
3)读取配置文件,并赋值给ctx变量
ApplicationContext ctx = new AnnotationConfigApplicationContext( AppConfig.class );
User u1 = (User)ctx.getBean(“user") ; //此文件在与UserDao在同一包中
u1.animal.getName();
2、SpringMVC
2.1、MVC模式设计(mvc调用过程图片)
2.2、MVC总结:
1) Controller(控制器):
1)获取请求中的数据
2)调用Model(模型)来处理数据(处理业务、访问数据库)
3)调用视图来展示请求处理的结果
2) Model(模型):处理请求
1)封装数据(POJO)
2)处理业务(service)
3)访问数据库(Dao)
3) View(视图):只负责展示请求处理的结果
软件分层:
表现层 – springmvc针对表现层提供了解决方案
业务层(业务逻辑层) – spring三层都有涉及,提供软件中所需要的对象
持久层(数据访问层) – mybatis针对持久层提供了解决方案,对JDBC进行了简化和封装
(POJO传递数据) (POJO传递数据)
Controller ---调用-----> Service ---调用——>Dao
2.3、创建简单的MVC
1、用maven创建项目war包;(在项目上JAVA EE-Generete…)生成web
2、配置pom文件,WEB-INF下配置web.xml文件,resources下创建springmvc-config.xml
3、com.tedu.controller.doit.java
1) 类上放@Controller 声明为controller层 ,创建对象交给spring管理
2) 方法上放@RequestMapping(“/t1”) /这是url请求的地址
3) public String t1(Emp emp,Model model) 用emp接收参数,用model传值到home.jsp
4) model.addAttribute(emp);传对象到home.jsp里,还可以传map,list,字符串
5) return “home”; //这是要转发的文件
6) 日期型参数Date必须为:2020/02/24 不能为2020-02-24
7) home.jsp拿数据
<%@page import=“com.tedu.pojo.Emp”%>导Emp包
<%Emp emp = (Emp)request.getAttribute(“emp”);%> 在request里用getAttribute拿emp对象
<%=emp.getName() %> 在对象里拿值;
2.4、SpringBoot工程下Spring MVC技术的应用?
1)工程创建
1、创建 2、添加依赖(Spring Web + Thymeleaf)
2)resourse 下的自动生成的目录
static 目录:可以存储html、css、js、image,这些资源可以直接访问;
templates 目录:thymeleaf依赖以后自动创建的目录,此目录中要存储一些html模板,这个模板页面不能直接通过浏览器url进行访问
注意配置文件里的前缀和后缀的设置,以及缓存的配置;
2.5、Spring MVC 2种响应数据处理方式
1、Model&view,2、JSON
第1种:Model & View
@Controller
public class ModelViewController {
@RequestMapping("/doModelAndView”)
public String doModelAndView(Model model) {
model.addAttribute(“username”, “jason”);
model.addAttribute(“state”, true);
return “default”; }}
执行用return 会转发到default.html, return "redirect:/t1" 会重定向到t1
ThymeLeaf会自动用default.html 里的[[${salary}]]去Map存的model,里key为salary的值;
然后返回数据封装好了的html;给default.html装载;
中小企业用户量不大时,可用此方案,数据量大时最好用前后端分离;
第2种:JSON
JSON响应数据有两种方案:POJO | Map
1、返回pojo对象
建controller类,用@RestController注解定义类,
@RestController =@Controller +@ResponseBody(定义类里的方法或该方法return值不是html,JSON需要用,否则会转发到html)
public class JsonObjectController {
@RequestMapping("/doConvertResponseToJson")
public ResponseResult doConvertResponseToJson(){
ResponseResult rs=new ResponseResult();
rs.setCode(200); 这里调用的是pojo的seCode方法
rs.setMessage("OK");
return rs ; }
@ResponseBody放类上则类里所有方法都不是转发到html,则放某个方法上表示这个方法不转发到html,只发送数据;
springBoot自动调用jackson把pojo转成字符串,发送给客户端
2、返回Map对象
@RequestMapping(“/doConvertMapToJson”)
public Map<String,Object> doConvertMapToJson(){
Map<String,Object> map=new HashMap<>();
map.put(“username”,“刘德华”);
map.put(“state”,true);
return map ; }
2.6、Spring MVC 3种请求参数处理方式
直接量 | POJO | Map
第一种:直接量
@GetMapping("/doParam01") //@GetMapping表示只接收Get方法的值;
public String doMethodParam(String name,String job,Double salary){
return "request params "+name; }
直接量的话,传过来的参数要与接收的参数名一致
如:a.com?name=bobo&job=保安&salary=55d
第二种:POJO
先建一个emp员工类POJO对象
@RequestMapping("/doemp")
public String doEmp(Emp emp){
return emp;}
//pojo对象接收请求参数,pojo对象中需提供与参数名相匹配的set方法
比如pojo对象 emp类有name,job,salary,那么就只接收这三个参数,这三个之外的不接收,
而map集合接收则参数所有值都接收;
第三种:Map
@GetMapping("/doParam03")
public String doEmp(@RequestParam Map<String,Object> map){
return "request params "+param.toString(); }
一定要在Map前加@RequestParam 注解,否则会认为Map是用来响应数据的,而不是接收数据的;
客户端传过来的所有值,map都会存,pojo只存对象里有setter方法的,而map都存
2.7、07-SpringBoot+MyBatis+Spring 技术整合实现商品模块的CRUD操作
1) 工程创建
1、创建 2、添加依赖(Spring Web + Thymeleaf + Spring JDBC + mybatis + mysql Driver)
2) Mode&view传List值;
控制层向html模板view层传 List<emp> 数据,
List<Brand> list = brandService.findBrand(name);
model.addAttribute("list", list);
ThymeLeaf拿到Map集合的model后,进行取值,
<tr th:each="b:${list}"> /each是对list集合的循环迭代,${list}是作为Key拿 model传过来的list集合的key为"list"的值,
并把当前循环出来的对象赋值给"b"
<td th:text="${b.id}">10</td> //b拿到值,其实是这次循环的Brand对象 .id,是拿Brand对象里的getId方法的返回值,代替默认值“10”
<td th:text="${b.name}">默认名字</td>
<td th:text="${b.remark}"></td>
th:text="${#dates.format(b.createdTime,'yyyy年MM月dd日')}”
3)thymeleaf
<button type="button" th:onclick="idDelete([[${b.id}]])">删除</button>
function idDelete(id){
if(!confirm("您确认删除吗"))return;
let url = "http://localhost:8080/t3/"+id;
location.href=url; }
2.8、rest风格url
@RequestMapping({"t1/{name}","t1/{name}/{job}","/t1"})//不带参,带name参,带name+job参
public String t1(@PathVariable(required = false) String name, @PathVariable(required = false) String job, Model model){
@PathVariable可以实现 sohu.com/t1/周波/总统 这样的参数形式;
required=false 代表这个参数可以没有
3、SpringBoot
3.1、用idea创建SpringBoot(maven)
1) new-module-Spring Initializr->
2) 创建后,右键pom.xml文件,选add maven(加入maven)->reload(手动开启导入依赖包)
3启动项目程序(运行含@SpringBootApplication注解的文件,component会从此文件所有包及子包自动扫描component注解并装配到bean pool
4) (理解spring启动流程图)
3.2、Git
1) git安装及配置
git config --global user.name "your-name"
git config --global user.email "your-email@youremail.com"
2) 添加git和Gitee进idea,(Gitee账号创建以及与idea绑定登陆)
3) 把项目导入到gitee:vcs-import into ->gitee
4) 项目提交:add->commit->push
(理解gif存储模型图)
3、vm options
1) edit configrations
-Xms512m 堆最小内存
-Xmx1024m 堆最大内存
2) GC参数: -XX:+PringGC (程序运行过程中输出GC相关信息)
3) 监控类加载顺序参数: -XX:+TraceClassLoading
3.3、SpringBoot 项目中的依赖注入过程分析
A、注入过程
1、创建A接口,再创建b1,b2类实现A接口并加@Component,再到C类中去创建@Autowired去调A类
2、@Autowired A a;
2.1、先根据类型去Bean pool对象池里去匹配,A类有且仅有一个b1实现类,就把已自动装配进Bean池的b1对象,赋值给 A a;
2.2、如果找到2个及以上子实现类对象时,就配置属性名,如:A b1 这样就是匹配的 b1这个子类对象的bean;
2.3、也可以在@Autowired处加 @Qualifier("b1")来显式指明调用b1这个子类的bean;
B、通过有参构造注入(给属性赋值)
private Cache cache;
@Autowired
public CacheService(@Qualifier("softCache") Cache cache) {this.cache = cache;}
3.4、数据库连接池(hikariCP)
SpringBoot工程下如何实现对HikariCP连接池的整合?
1) 创建项目后,导入两个依赖:mysql-connector-java,spring-boot-starter-jdbc
2) 配置application.properties文件
spring.datasource.url=jdbc:mysql:///dbgoods?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
3) 在jdbc调用时,直接是这样,
@Autowired
private DataSource dataSource; //DataSource 这个是从连接池自动生成;
conn = dataSource.getConnection();
sql = "select * from tb_goods where id>?";
ps = conn.prepareStatement(sql);
3.5、如何导入别人的工程:
1)本地导入:file->project Structure-> + -> import Module ->本包目录;
导入工程的话,先建一个工程;工程只是一个目录而已,一般都是建Module
2) 远程导入:VCS -> Get from Version Control -> url
3.6、日志纪录
* 1)项目中的日志记录用的什么API? SLF4J (Simple Logging Facade for Java)
* 2)为什么使用此API? (这组API定义了日志的规范-程序中建议耦合规范->其目的提高其可扩展性)
* 3)这组API的具体实现,你在项目中用的是谁?(ch.qos.logback.classic.Logger)
* 4)你为什么不用log4j?(异步情况下,logback性能要比log4j好,SpringBoot内置就是logback)
* 5)你的项目中的日志是同步写还是异步写?(大部分异步,提高其用户体验)
* 6)你了解日志的级别吗?(日志的输出级别-ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF)
在spring中默认日志纪录输出级别为Info
* 7) application.properties 中配置log
logging.file.path=/java/log/ //日志输出的文件
logging.level.com.tedu=debug //日志输出的级别
* 8) private static final Logger log=
LoggerFactory.getLogger(GoodsServiceImpl.class);
3.7、lombok
@Setter 用于为描述的类生成setter方法,不包含final修饰属性。
@Getter 用于为描述的类生成getter方法。
@ToString 用于为描述的类添加toString方法。
@Data 等于以上3个总和
@Slf4J 用于为描述的类添加一个日志属性对象。
步骤:
idea中的安装配置
第一步:打开idea的设置窗口,找到plugins菜单,搜索lombok进行安装,如图所示
第二步:Settings->Build->Annotation Processors->Enable annotation(打勾)
使用:
第一步:添加lombok依赖。
第二步:在类上应用lombok注解。
3.8、热布署
第一步:settings->Build->comiler-build project automatically(打勾)
第二步:IDEA工具中启动注册窗口(按ctrl+shift+alt+/),->registry
第三步:compiler.automake.allow.when.app.running 打勾
第四步:加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
3.9、项目健康监控
第一步:添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
第二步:http://localhost/actuator
3.10、springBoot异常处理
前端控制器流程
最优先:方法中try/catch
其次:在本类中找异常处理方法
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public String doHandleArithmeticException(ArithmeticException e){ //这里的异常要大于上面的
e.printStackTrace();
return "计算过程中出现了异常,异常信息为"+e.getMessage();}
@ExceptionHandler注解描述的方法为异常处理方法(注解中的异常类型为可处理的异常类型)
最后:全局异常处理类
@RestControllerAdvice
( = @ControllerAdvice+@ResponseBody )
public class GlobalExceptionHandler {
@ExceptionHandler(ArithmeticException.class)
public String doHandleArithmeticException(ArithmeticException e){
e.printStackTrace();
return "计算过程中出现了异常,异常信息为"+e.getMessage(); }}
@RestControllerAdvice 注解描述的类为全局异常处理类,当控制层方法中的异常没有自己捕获,
也没有定义其内部的异常处理方法,底层默认会查找全局异常处理类,调用对应的异常处理方法进行异常处理
尽量用条件判断处理,而不是用异常处理;
全局异常类要把遇到的所有异常列出来,还要写一个Runtime和Throwable来接,防止有的异常接不到;
3.11、ResponseResult响应标准设计及实现
不管什么项目,都要有一个“响应结果集”类,这是一个返回JSON数据的类;
第一步:创建pojo对象封装数据:ResponseResult.java
数据包含三部分:状态(state),消息(message),具体数据(data)
/**响应状态码(有的人用code)*/
private Integer state=1;//1表示ok,0表示error,.....
/**状态码对应的信息*/
private String message="ok";
/**正确的响应数据*/
private Object data;
还要设置上面属性的setter,getter
public void setData(Object data) {
this.data = data; }
public ResponseResult(Throwable e){
this.state = 0;
this.message = e.getMessage();}
第二步:controller类的方法中返回值用ResponseResult
@RequestMapping("/doCompute/{n1}/{n2}")
public ResponseResult doCompute(@PathVariable Integer n1, @PathVariable Integer n2){
Integer result=n1/n2;
ResponseResult r=new ResponseResult("计算结果:"+result);
r.setData(result);
return r;}
或者直接: return new ResponseResult( result );
第三步:全局异常处理类
@RestControllerAdvice
public class GlobalExceptionHandler {
// private static final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class);//2
@ExceptionHandler(ArithmeticException.class)
public ResponseResult doHandleArithmeticException(ArithmeticException e){
e.printStackTrace();
// log.info("exception {}",e.getMessage());
return new ResponseResult(e);//封装异常结果 }}
4、Mybatis进阶
4.1、springboot整合mybatis
第一步:创建Module(创建时如果官网默认连接打不开,用阿里云:https://start.aliyun.com)
第二步:添加依赖(mysql + spring jdbc + mybatis starter )
第三步:设置配置文件:
1、数据库连接池配置,
2、mybatis配置 mapper文件地址:mybatis.mapper-locations=classpath:/mapper//.xml
所有mapper 下的.xml 都放mapper下
第四步:代码实现
1、定义商品模块POJO对象类型,作数据封装
2、定义商品模块持久层DAO对象GoodsDao接口及方法映射
用@Mapper注解该类;
3、GoodsDao接口映射文件及SQL映射定义
<?xml version="1.0" encoding="UTF-8" ?>
//namespace指向mapper的全类名
<mapper namespace="com.cy.pj.goods.dao.GoodsDao”>
//id与mapper的方法名相同,resultType是返回值所需的类的数据封装类
<select id="findGoods" resultType="com.cy.pj.goods.pojo.Goods”>
select * from tb_goods </select> </mapper>
4、编写service业务层代码:获得DAO层数据;
4.2、@Mapper和@Repository
1)
@Mapper
此注解用于描述mybatis框架中的数据逻辑层接口,用于告诉mybatis框架此接口实现类,
由mybatis框架创建(底层基于jdb创建其代理),
mybatis会自动给该对象生成继承的代理子对象,并重写获得数据的方法(整合mapper.xml文件的sql语句)
此类型的对象会交给spring管理;
@Repository
描述数据层实现类,用于交给spring管理;
4.3、在方法中加入事务声明
@Transactional 这是一个AOP切面对象注解,此注解描述为事务切入点方法
这个注解会把描述的方法,执行前开启事务,方法结束后“提交或回滚事务”;
4.4、执行插入语句后将自增id存入POJO对象
<insert id="insertObject"
parameterType="com.cy.pj.sys.pojo.SysRole" //执行完sql后,把结果返回到此pojo对象中
useGeneratedKeys="true" //调用数据库的自增字段
keyProperty="id"> //调用pojo对象中的setId方法,把自增字段写入pojo对象;
4.5、写入一对多关联字段
比如一个用户,有超级管理员、仓库管理员、财务三个角色,
要把此用户id和角色id同时,用户及角色关联表(sys_user_roles)
<insert id="insertObjects">
insert into sys_user_roles
(user_id,role_id)
values
<foreach collection="roleIds" separator="," item="roleId">
(#{userId},#{roleId})
</foreach>
4.6、 基于多个角色查到菜单
-- where role_id in (1,2,3,4)
where role_id in
<foreach collection="roleIds" open="(" close=")" separator="," item="roleId">
#{roleId}
</foreach>
4.7、mybatis中多表查询返回到pojo对象有三种方法
第一种:分两次/多次分别查询,然后把结果分别写入pojo对象中;
第二种:多表联合查询,一次查询同时写入pojo(但只适合于多表在一个数据库中,分布式多个表在不同数据库中是不适合的)
第三种:嵌套查询,先查询一个表,基于第一个表的查询结果,再去查第二个表/第三个表;
第一种方案:
举例:我们要查id,name,dept_id,role这四个字段分别存在2个表中,我需要建一个gogo.java 这样的pojo对象这个对象里包括
id,name,dept_id,role四个属性和对应的getter和setter
我可以先在第一个表中,把id,name查出来,mybatis会自动把查出来的id,name,通过resultType指向的gogo这个pojo对象,
然后在这个pojo对象里的setId和setName方法把查出来的id和name存入对象;
然后再用一个mapper.xml写一个sql把dept_id,role用上述的方法,把dept_id,role也存入gogo这个pojo对象;
当这两个方法都执行了,gogo这个pojo对象就拥有了id,name,dept_id,role 的值;,然后直取值就可以了
但是:如果role角色是多个值,有管理员和部门经理两个角色,两个值,mybatis不会自动赋值给role,
需要在resultType声明为int,然后在DAO类里返回值用List<Integer>,这样就会返回Integer类型的list集合,
然后,再把这个返回值,在service层,调用gogo这个pojo对象的setRole方法,来存入;
第二种方案
<resultMap id="sysUserDept" type="com.cy.pj.sys.entity.SysUserDept">
<!--association 一般用于many2one(多对一)或one2one(一对一)的查询映射-->
<association property="sysDept"
column="deptId"
select="com.cy.pj.sys.dao.SysDeptDao.findById">
</association>
</resultMap>
<select id="findSysUsers" resultMap="sysUserDept">
select * from sys_users
<if test="userName!=null and userName!=''">
where userName like concat('%',#{userName},'%')
</if>
order by id desc
limit #{startIndex},#{pageSize}
</select>
//resultMap="sysUserDept"与上面的id="sysUserDept"一致,建立关联
下面语句执行完后,把deptId赋值到column,然后执行select="com.cy.pj.sys.dao.SysDeptDao.findById"这个
映射所代表的mapper.xml或dao文件里的方法,调用deptId,求出的结果放到property="sysDept"里,
再存到type="com.cy.pj.sys.entity.SysUserDept" 这个pojo对象里
4.8、一些其他技巧和注意事项
1)、多表关联删除时,先删除关系数据,再删除自身信息;
2)、数据校验一般放在service层,因为有时会用service作为接口对外暴露
3)、@param("username") 在dao类中,链接数据的接口,传递参数要用 @param("username")
List<SysLog> findPageObjects(@Param("username") String username,
4)、resources下给mapper新增目录时,一定要用 mapper/goods 这样的写,中间不是用 . 而是 /
4.9、mysql优化
1、建立索引(索引就像一本书的大纲)
2、select * from emp 这个 * 要改成具体的字段名,并且用多少拿多少;
3、去掉无用的sql语句;
5、AJXA异步
4.1、特点
4.2、js中Ajax四个编程步骤
基于图-4 的分析,Ajax 应用的编程步骤如下:
第一步:基于 dom 事件创建 XHR 对象(XMLHttpRequest 对象)
第二步:注册 XHR 对象状态监听,通过回调函数(callback)处理状态信息。
第三步:创建与服务端的连接
第四步:发送异步请求实现与服务端的通讯
function doAjaxGet(){//错误调试:debugger,console.log(),排除法
//1.创建 XHR 对象
var xhr=new XMLHttpRequest();
//2.设置状态监听(将服务端响应的结果更新到 ajaxResultId 位置)
xhr.onreadystatechange=function(){
if(xhr.readyState==4&&xhr.status==200){
document.getElementById("ajaxResultId").innerHTML=xhr.responseText; } };
//3.建立连接 xhr.open("GET","http://localhost/doAjaxGet",true);//true 表示异步(底层会启动线程与服务端通讯)
//4.发送请求
xhr.send(); }//get请求在xhr.send()里放数据传数据,都没用;
在url链接处加id=100这样传数据,到服务端其实变成了字符串,需要把比较双方都强传为"String"
特别是在进行id比较时,从浏览器用ajax传到服务器的id是字符串,而服务器的id往往是int,这时要把int强转为字符串;
4.3、jQuery中Ajax的应用步骤
1)原生使用
function doAjaxPost(){
let url="http://localhost/doajaxpost";
let params = "id=105&name=computer&remark=good";
$.ajax({
url:url,
data:params,
type:"POST", //默认为GET,get可以不写/ put改/delete删/get查/post增
async:true, //默认为true,表示异步,false是同步,异步可以不写此行
contentType:"application/x-www-form-urlencoded", //可以不写,声明post会自动加上,
dataType:"JSON", //这里的有Text/JSON表示服务端响应到客户端的数据类型,假如是json,客户端会将其换转为json格式的js对象;
success:function(result){
$("#result").html(result); } }); }
2)其他操作
$.getJSON(url,params,function(result){ //直接获取
$("#result").html(result); })
$.get(url,params,function(result){ $("#result").html(result); })
$.post(url,params,function(result){ $("#result").html(result),"json"; })
html,xml,json请求所返回的数据类型;
ajax请求方式:
增:post 接时(@PostMapping)
删:delete 接时(@deleteMapping)
改:put 接时(@PutMapping)
查:get 接时(@GetMapping)
Iterator it = dbList.iterator();
这个迭代器时,如果要比较id时,迭代器里的id不能为null
Ajax在服务端的处理
3、在服务端controller类的方法上加上@CrossOrigin
//修改操作
@PutMapping("/put")
public String update(@RequestParam Map<String,Object> updateMap){
Iterator it = list.iterator(); //用iterator来迭代,不要用foreach
while(it.hasNext()){
Map map = (Map<String,Object>)it.next();
if(map.get("id").equals(updateMap.get("id"))){
map.put("name",updateMap.get("name")); }}
return "修改成功"; }
3)于AJAX的一些重点问题:
1、谁将返回值转换为json格式字符串? jackson
在这个json字符串底层是如何获取key/value?通过pojo对象的getter方法;
从服务端传过来的字符串,到了客户端怎么就变成了Json对象呢?是客户端将服务端响应的字符串转换为json对象
是js里有一个函数叫json,里面的函数叫parse,
2、如何解析服务端返回的json字符串?服务端响应结果为字符串
"{"state":1,"message":"ok","data":"{pageCurrent: 3, records: [ { id: 47, name: "超级管理员",}] }"}"
第一步:将json字符串转换为json的js对象,let jsonObj2 = JSON.parse(jsonStr);
第二步:jsonObj2.state 结果是 1, jsonObj2.data.pageCurrent 结果是 3,jsonObj2.records
但是$.getJSON会自动将拿到的值从字符串转化成json对象;
$.get/$.post/$.ajax 如果dataType:"Text" 就会保持字符串,如果是json就会自动转成json对象;
所以就用result.data.records就能拿到list数组,再循环此数组就能拿具体的值了;
6、springBoot中注解
@SpringBootTest @Test-此注解描述的类为springboot工程中的单元测试类
@ComponentScan() 系统启动时会从此注解所在包及子包扫@Component()注解的类并装配入bean池
@Component() -将所在类交给spring来管理,自动创建对象
@Scope(“prototype”)声明为多例,单例不用声例是默认
@Lazy //spring框架提供的一个用于定义延迟加载特定的一个注解
Dao层(mybatis的注解)
@Mapper和@Repository是常用的两个注解,两者都是用在dao上
@Repository需要在Spring中配置扫描地址,然后生成Dao层的Bean才能被注入到Service层中。
@Mapper不需要配置扫描地址,通过xml里面的namespace里面的接口地址,生成了Bean后注入到Service层中。
@Param("userId") Integer userId
--@Select("select * from tb_brand where id=#{id}")
--@Update("update tb_brand set name=#{name},remark=#{remark} where id=#{id}")
--@Delete("delete from tb_brand where id=#{id}”)
Service层
@Service -指定这是一个service
@Autowired -把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作
@Override 重写
Controller层
@Controller -用于定义控制器,在spring项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层)
@ResponseBody -表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用
@RestController //==@Controller+ResponseBody
@RequestMapping -("/**")-可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@PathVariable -接收请求路径中占位符的值
@RequestParam -将请求参数绑定到你控制器的方法参数上,需要用map来接受参数时需要用到
@CrossOrigin -注解描述的方法支持跨域访问
与Ajax配置接收参数
--@GetMapping("/doAjaxGet") -处理get请求,传统的RequestMapping来编写应该是@RequestMapping(value = “/get/{id}”, method = RequestMethod.GET)
--@PostMapping("/doUpdateBrand") -处理post请求,传统的RequestMapping来编写应该是@RequestMapping(value = “/get/{id}”,method = RequestMethod.POST)
--@PutMapping("/doAjaxUpdate") -和PostMapping作用等同,都是用来向服务器提交信息。如果是添加信息,倾向于用@PostMapping,如果是更新信息,倾向于用@PutMapping。两者差别不是很明显。
--@DeleteMapping("/doAjaxDelete") -删除URL映射
全局异常处理
@ControllerAdvice -此注解描述的类为全局异常处理类
@RestControllerAdvice //==@ControllerAdvice + @ResponseBody
@ExceptionHandler(ArithmeticException.class)
Lombok
@Slf4j -实现日志输出,调用时用(log.info(“日志{},)
@Data =(@Setter + @Getter + @ToString)
Aop切面对象
@Transactional 此注解声明此方法为整个事务;
@Aspect 声明类为切面对象类,此类还要加@Component
@Pointcut 定义切入点 @Pointcut(“@annotation(reqire.class)”)
@Around / @Before / @ AfterReturning / @AfterThrowing / @After
7、面向切面AOP编程
7.1、面向切面AOP编程的逻辑原理
Controller控制器A类,调service业务层B接口,C类实现B接口,E类是切面对象,C类在执行“c1方法“时,E类可以控制
在c1方法执行前后,先执行D类的一些语句;
原理:底层给C类自动创建了一个实现类D代理对象,这个对象在控制先执行e类里的方法,再执行c类的方法
然后,系统调用变成controller–>XXXService->CGLIB(切面生成的service代理对象)->XXXServiceImpl(实现类)->dao
7.2、AOP编程步骤
第一步:AOP对象导入依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第二步:定义切面对象
2)@Aspect 声明一个类为切面对象 (记得加@Component,此类交给Spring来管理)
通知(Advice)、连接点(joinpoint)、切入点(pointcut)、各切面对象执行顺序(order(1) )
第三步:自定义引用注解
建一个注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredCache {}
第四步:定义切入点
3)@Pointcut 定义切入点
bean(重点)用于匹配指定bean对象的所有方法(粗粒度)
within(了解)用于匹配指定包下所有类内的所有方法
execution(了解) 用于按指定语法规则匹配到具体方法
@annotation(超重点)用于匹配指定注解修饰的方法(细粒度)
例:
@Pointcut("@annotation(com.tedu.pj.brand.common.RequiredCache)")
public void doCache(){}
第五步:定义通知和连接点
4)Advice通知
通知执行顺序:@Around->@Before->方法->@AfterReturning成功执行/@AfterThrowing异常执行->@After
5)JoinPoint连接点
@Around("doCache()")
public Object doCacheAround(ProceedingJoinPoint jp) throws Throwable{
System.out.println("首先执行");
result=jp.proceed(); //中间执行
System.out.println("最后执行");
return xy} 返回值必须目标执行方法的返回值;
第六步:在引用类引用
6)引用项目:调用切面对象为依赖,同时在需要被切面的方法上加注解;
<dependency>
<groupId>com.tedu.jt.emp.aop</groupId>
<artifactId>19aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
第七步:在方法上加注解
8、动吧项目
8.1、增删改查从controller>service(serviceImpl)>dao>mapper
8.2、向一个关联表插入一对多数据(详情看4.5)
8.3、插入数据后返回自增id(详情看4.4)
8.4、查询两张及以上的表,其中一张返回多条数据(详情看4.7)
8.5、返回数据上:从多表分别向关联pojo存数据,再把pojo返给page对象,再把page对象返给R对象。
8.6、日志写入
步骤:
第一步:创建RequiredLog注解
内容参数声明:String operation() default “";
第二步:在需日志纪录的目标执行方法加切面对象的注解:
@RequiredLog(operation = "禁用启用”)
第三步:创建切面对象,在切入点用
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)"
第四步:获得各项操作的信息(重点在获得username,operation,method,params这4个的值)
username:的获得:SysUser user = (SysUser)SecurityUtils.getSubject().getPrincipal();
这里有从数据库获得的该用户的所有信息,再:user.getUsername()就拿到username了;
(具体详情要看shiro获得用户值)
method方法名获得:
MethodSignature ms = (MethodSignature) jp.getSignature();//获得目标方法签名信息
String targetMethod = ms.getName(); //通过目标方法签名信息得方法名
String targetClassToString = ms.getDeclaringTypeName();//通过签名得全类名
opration注解上的操作
//获取目标方法上的requiredLog注解
String operation = jp.getTarget().getClass().getDeclaredMethod(targetMethod, ms.getParameterTypes()).getAnnotation(RequiredLog.class).operation();
以上操作:通过目标方法->获得类名->获得方法名(方法名,参数)->获得此方法名字为xx的注解->拿operation的值
params参数的获得:
//.jp.getArgs()会获得目标方法的执行时传入的实现参数并转化成对象,
// objectMapper().writeValueAsString()将任意对象转成字符串)
String params = new ObjectMapper().writeValueAsString(jp.getArgs());
第五步:写这些值写入pojo后,pojo传值到方法存入数据库;
8.7、公共类common的设计(本阶段其他环节纪录了)
R类,GlobalException类,Utils类,Pages类,
annotation,aspect,cache,pojo,vo,web
8.8、父模块引用,依赖模块导入
如何在模块中把另一个模块作为依赖?
1、project Structure -> module dependency(导入module依赖) / JARs包依赖
2、在pom.xml文件加依赖,
<dependency>
<groupId>com.cy.common</groupId>
<artifactId>20common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
3、删除15common的启动类
如何在模块中继承另一个模块?
1、15parent 是一个父模块,用来管理依赖,把src删掉,在创建父模块时声明父模块为pom包;
2、在子模块的pom文件中调用父模块
<parent>
<groupId>com.cy</groupId>
<artifactId>15-dbpms-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
这样子模块,就能像使用String一样使用15common的方法和类,也同时继承了15parent的包;
8.9、Web Mvc拦截器(实现在规定时间访问其他时间不能访问)
实现访问,可以用AOP拦截,这个一般处于在service层,也可以用MVC层做拦截器,会在controller执行前就被拦截
Spring MVC中的拦截器基于回调机制,可以在目标方法执行之前,先进行业务检测,满足条件则放行,
不满足条件则进行拦截,拦截器原理分析如下图所示
第一步:拦截器定义
/**
* 定义时间访问拦截器
* spring MVC模块中拦截器的标准为HandlerInterCeptor接口
* 这个接口中的方法可以实现对目标handler(@controller)方法的访问拦截,
* */
public class TimeAccessInterCeptor implements HandlerInterceptor {
// preHandle方法会在@Controller域@RestController描述的类中的请求响应处理方法之前执行。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=======执行一下");
LocalTime time = LocalTime.now();
int hour = time.getHour();
System.out.println("hour="+hour);
if(hour>=9&&hour<=13) return true;
throw new ServiceException("不在规定的访问时间:请于上午9点到下午18点之间"); }}
第二步:拦截器配置
@Configuration
public class SpringWebConfig implements WebMvcConfigurer{//web.xml
//配置spring mvc 拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TimeAccessInterceptor()).addPathPatterns("/user/doLogin"); }}
9、Shiro
9.1、Shiro基本知识,他有拦截器,并在前端控制器之前的(filter)
-
Subject :主体对象,负责提交用户认证和授权信息。
-
SecurityManager:安全管理器,负责认证,授权等业务实现。
-
Realm:领域对象,负责从数据层获取业务数据。
-
Shiro是apache旗下一个开源安全框架(http://shiro.apache.org/),它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架
shiro的主要功能:
- Authentication:身份认证/登录(账号密码验证)。
- Authorization:授权,即角色或者权限验证。
- Session Manager:会话管理,用户登录后的session相关管理。
4. Cryptography:加密,密码加密等。 - Web Support:Web支持,集成Web环境。
- Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
- Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
- Remember Me:记住我,登录后,下次再来的话不用登录了。
9.2、Shiro的基本配置
第一步、创建shiro配置文件
@Configuration public class SpringShiroConfig {} 第二步、在Shiro配置类中添加SecurityManager配置(这里一定要使用org.apache.shiro.mgt.SecurityManager这个接口对象)
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
return sManager; }
第三步: 在Shiro配置类中添加ShiroFilterFactoryBean对象的配置。通过此对象设置资源匿名访问、认证访问
@Bean
public ShiroFilterFactoryBean shiroFilterFactory (
SecurityManager securityManager) {
ShiroFilterFactoryBean sfBean=
new ShiroFilterFactoryBean();
sfBean.setSecurityManager(securityManager);
//定义map指定请求过滤规则(哪些资源允许匿名访问,哪些必须认证访问)
sfBean.setLoginUrl(“/doLoginUI”);//设置此路径为登陆url,controller要建一个这个方法
LinkedHashMap<String,String> map= new LinkedHashMap<>();
//静态资源允许匿名访问:“anon”
map.put(“/bower_components/“,“anon”);
map.put(”/build/”,“anon”);
map.put(”/user/doLogin",“anon”);//放行登陆页面
map.put(”/doLogout",“logout”); //退出地址
//除了匿名访问的资源,其它都要认证(“authc”)后访问
map.put(”/**",“authc”);
sfBean.setFilterChainDefinitionMap(map);
return sfBean;}
9.2、认证登陆
authentication 美[ɔːˌθentɪˈkeɪʃn] 身份验证
其中认证流程分析如下:
-
1)系统调用subject的login方法将用户信息提交给SecurityManager
-
2)SecurityManager将认证操作委托给认证器对象Authenticator
-
3)Authenticator将用户输入的身份信息传递给Realm。
-
4)Realm访问数据库获取用户信息然后对信息进行封装并返回。
-
5)Authenticator 对realm返回的信息进行身份认证。
-
第一步:创建dao和mapper.xml调用户、密码和盐值数据;
-
第二步:写Service(realm类)
-
通过token拿到用户名,通过用户名在数据库中拿出密码、盐值
-
并通过new SimpleAuthenticationInfo 在参数中传用户对象、密码、盐值、用户名)完成封装;
-
@Service
-
public class ShiroUserRealm extends AuthorizingRealm {
-
//设置凭证匹配器(与用户添加操作使用相同的加密算法)
-
// 通过此方法完成认证数据的获取及封装,系统底层会将认证数据传递认证管理器,由认证管理器完成认证操作。
-
@Override
-
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
-
throws AuthenticationException {
-
//1.获取用户名(用户页面输入)
-
UsernamePasswordToken upToken=(UsernamePasswordToken)token;
-
String username=upToken.getUsername();
-
//2.基于用户名在数据库查询用户信息
-
SysUser user= sysUserDao.findUserByUserName(username);
-
//3.判定用户是否存在
-
//4.判定用户是否已被禁用。
-
//5.封装用户信息
-
ByteSource credentialsSalt=
-
ByteSource.Util.bytes(user.getSalt()); //从数据库中得到盐值
-
//记住:构建什么对象要看方法的返回值
-
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(
-
user,//principal (身份)
-
user.getPassword(),//hashedCredentials
-
credentialsSalt, //credentialsSalt
-
getName());//realName
-
//6.返回封装结果
-
return info;//返回值会传递给认证管理器(后续
-
//认证管理器会通过此信息完成认证操作) }}
-
第三步:SpringShiroConfig配置类中添加realm
-
@Bean
-
public SecurityManager securityManager(Realm realm) {
-
DefaultWebSecurityManager sManager=
-
new DefaultWebSecurityManager();
-
sManager.setRealm(realm);
-
return sManager;}
-
第四步:在Controller中添加处理登陆的方法
@RequestMapping(“doLogin”)
public JsonResult doLogin(String username,String password,boolean isRememberMe){
//1、对用户进行封装存值
UsernamePasswordToken token = new UsernamePasswordToken(username, password, isRememberMe);
if(isRememberMe) token.setRememberMe(true);//2、对用户进行认证 SecurityUtils.getSubject().login(token); return new JsonResult("登陆成功");} //1)token会传给shiro的SecurityManager //2)SecurityManager将token传递给认证管理器 //3)认证管理器会将token传递给realm
第五步:在全局异常处理类中添加对应的异常回应
-
9.2、授权访问
第一步:在Shiro配置文件中加入advisor
* 此对象会在spring启动时加载,并且通过此对象,可以找到@RequiresPermissions注解描述的方法,
* 然后这些方法在运行时,由此Advisor对象,调用SecurityManager中的checkPermissions方法为目标切点方法
@Bean
public AuthorizationAttributeSourceAdvisor
authorizationAttributeSourceAdvisor (
SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor=
new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor; }
第二步:写DAO和Mapper,通过用户id得到角色,角色得到菜单,菜单得到授权组
第三步:ShiroRealm(service),从数据库中拿当前用户的授权标识并封装返回。
@Service
public class ShiroUserRealm extends AuthorizingRealm {
/**通过此方法完成授权信息的获取及封装*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//1.获取登录用户信息,例如用户id
SysUser user=(SysUser)principals.getPrimaryPrincipal();
Integer userId=user.getId();
//2.基于用户id获取用户拥有的角色(sys_user_roles)
List roleIds=sysUserRoleDao.findRoleIdsByUserId(userId);
if(roleIds==null||roleIds.size()0) throw new AuthorizationException();
//3.基于角色id获取菜单id(sys_role_menus)
List menuIds= sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);
if(menuIdsnull||menuIds.size()==0) throw new AuthorizationException();
//4.基于菜单id获取权限标识(sys_menus)
List permissions=sysMenuDao.findPermissions(menuIds);
//5.对权限标识信息进行封装并返回
Set set=new HashSet<>();
for(String per:permissions){
if(!StringUtils.isEmpty(per)){
set.add(per); } }
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.setStringPermissions(set);
return info;//返回给授权管理器 } }
第四步:向需要保护的方法上加注触
@RequiresPermissions("sys:user:update”)
这样advisor会扫描此注解,有人访问此注解描述的方法时,
会先从数据库查找此人是否拥有此sys:user:update标识,
如果有就执行目标方法,没有就返回错误,
9.3、加密(md5)
1、MD5加密
String pwd=“123456”;
String salt= UUID.randomUUID().toString(); //用随机产生盐值
SimpleHash sh = new SimpleHash(“MD5”,pwd,salt,10); //10是加密次数
String hashedPwd = sh.toHex();
9.4、 cacheManager
第一步:在配置文件中加入在SpringShiroConfig中配置缓存Bean对象(Shiro框架提供)。
@Bean
public CacheManager shiroCacheManager(){
return new MemoryConstrainedCacheManager(); }
第二步:修改securityManager的配置,将缓存对象注入给SecurityManager对象
@Bean
public SecurityManager securityManager(Realm realm,
CacheManager cacheManager) {
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
sManager.setRealm(realm);
sManager.setCacheManager(cacheManager);
return sManager;}
9.5、isRememberMe(记住我)(cookie)
第一步:在doLogin中加入代码
if(isRememberMe) token.setRememberMe(true);
第二步:在SpringShiroConfig配置类中添加记住我配置
@Bean
public RememberMeManager rememberMeManager() {
CookieRememberMeManager cManager=
new CookieRememberMeManager();
SimpleCookie cookie=new SimpleCookie(“rememberMe”);
cookie.setMaxAge(72460*60);
cManager.setCookie(cookie);
return cManager; }
第三步:在SpringShiroConfig中修改securityManager的配置,为securityManager注入rememberManager对象
@Bean
public SecurityManager securityManager(
Realm realm,CacheManager cacheManager
RememberMeManager rememberManager) {
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
sManager.setRealm(realm);
sManager.setCacheManager(cacheManager);
sManager.setRememberMeManager(rememberManager);
return sManager;}
第四步:修改shiro的过滤认证级别,将/**=author修改为/**=user,查看黄色背景部分。
从原来的:map.put("/**","authc”); 修改为:map.put("/**","user");//
user代表可以从用户浏览器cookie中读取账号信息进行身份认证
9.6、利用session 来配置Shiro会话时长(不配置就默认30分钟)
第一步:在SpringShiroConfig类中,添加会话管理器配置
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sManager=
new DefaultWebSessionManager();
sManager.setGlobalSessionTimeout(60601000);
return sManager;}
第二步:在SpringShiroConfig配置类中,对安全管理器 securityManager 增加 sessionManager值的注入
@Bean
public SecurityManager securityManager(
Realm realm,CacheManager cacheManager,
RememberMeManager rememberManager,
SessionManager sessionManager) {
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
sManager.setRealm(realm);
sManager.setCacheManager(cacheManager);
sManager.setRememberMeManager(rememberMeManager);
sManager.setSessionManager(sessionManager);
return sManager;}
9.7、介绍cookie和session
cookie是由服务端创建,发送给客户端,由客户端保存,下次客户端携带cookie访问服务端,
服务端读取cookie后,登陆信息在有效期,放行;
cookie是服务端生成一把密钥根据密钥生成密码形式的cookie(base64加密),发给客户端存储,客户端携cookie访问服务端,
服务端按之前的密钥去解密cookie,判断是不是合法身份,如果是就放行,。还有服务器重启此密钥会失效,生成新密钥,之前客户
端保存的cookie也会失效;因为新密钥无法解密之前密钥加密的信息;
1、HTTP协议是一种无状态协议,无法存储客户端与服务端的会话状态
2、客户端与服务端通讯过程中产生的会话状态信息存储到哪里(cookie和session)
3、cookie对象由服务端创建,但是是在客户端保存信息的一个对象;
4、cookie对象的类型有两种:会话cookie和持久cookie(我们手动设置了生命周期)
5、会话cookie在浏览器关闭时,生命周期自动结束;
6、session对象由服务端创建,并在服务端保存状态的一个对象;
7、session对象创建以后会将session 对象的id以会话cookie形式写到客户端,客户端可以再访问服务器时基于这个id找到服务端的session
8、浏览器关闭,会话cookie结束,写到客户端的jsessionid也就无效了,此时客户端再访问服务器无法基于jsessionid找到对应的session了(但服务端的session可能还是存在的)
* 这个对象一般是一个会话创建一个,并且会有一个会话ID(JSESSIONID),
* 可以通过这样的对象来记录登录用户信息;默认记30分钟,只要客户端操作界面一下,有活动,在活动结束后30分钟后失效;
* 需要重新登陆,除了登陆信息,还可以记录购物车信息;
* 在分布式架构中很少用,在单体架构中多用,,在分布式架构中用redis;
* session对象由服务端创建,但这个对象创建好以后,会将其JSESSIONID以会话cookie的形式写到客户端
* 这个JSESSIONID的生命周期是客户端关掉浏览器就没有了;
* 客户关在访问服务器时,会携带JSESSIONID到服务端,然后基于这个JSESSIONID找到session,再从session取数据
* 如果希望关了浏览器,还能存在,就把会话cookie存为持久cookie
10、前端
1、树结构treegrio,ztree
2、thymeleaf
11、其他经验总结
1)类的设计原则:单一职责(单一件事),不要类的功能大而全,而是单一,大而全,里面的东西就多,A功能出错或修改B功能也受影响;
2)is a (继承),has a(有一个,在A类中,调用B类(new)并赋值对一个变量)就是A类中有B类;,use a(a中使用了b的一个方法)
3)发现错误:先找caused by ,再向上查找;
4)连接协议(UDP广播协议,发完就完事),(TCP3次握手4次挥手,第1次:我要发东西,第2次:响应,行你发吧,第3次:发东西,第4次:关闭,再见)
5)接口定义的是can 能做啥,实现类定义的是 do 做啥
6)查数据库时,一条纪录可以存一个map,多行纪录存放到多个map里,多个map存List里;
7)程序应该耦合于规范,而不是耦合于实现,这就是门面模式,做一个A接口,做一个B类实现,调用是调A接口,这样将来B类实现要升级修改时
就不用改调用的代码了,直接修改实现类;
8)享元模式:池化思想,bean pool,这样的
9) 如果端口被占用可以netstat -ano | findstr “80"查到80端口的进程;
再用:taskkill /f /pid 1838(进程id)杀死进程
10)导入数据库前运行 set names utf8 以避免导入时就乱码了;
导入数据库:source D:/www/sql/back.sql;
11)package com.cy.pj.brand.service
com.cy域名倒写,pj项目名,brand模块名,service/dao/pojo/controller/是哪一个层;
12)用单例时必须考虑 线程安全+性能,要做到“原子操作”,就是执行中,不能被其他线程打断;
13) 网页400错误,一般是客户端向服务器端传递的请求参数与服务器端的默认设置不匹配,还有参数个数不匹配;或参数格式不匹配;
405,客户端提交的请求的方式如get,与服务端处理的方式如post,不一致导致的
14)在js里 数字非0为true,对象非undefined为true,对字符串非空为true;
15)比较两个数组是否相等可以用:Arrays.toString(a).equals(Arrays.toString(b)) 把两个数组转化字符串来比较
16)使用map充当pojo对象存数据的优劣势
1)优势:代码简单,适合工程量大时间短的外包项目;
2)劣势:可读性差,不知道里面存的是什么,类型不可控:Object可存任何数据,导致有安全风险;
17)CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
java,mysql,js,这些语言上,[]内表示可有可无,{}内表达二者必须选一
18、jQuery中点击事件,调用一个html来填充一个元素
$(“mainContentId”).click(function(){//事件处理函数
//load函数为一个jquery中的ajax函数,其作用是将url对应的资源,异步加载到指定位置
//此处表示在mainContentId对应的对象位置异步加载url指定资源
$(”#mainContentId").load(“menu/menu_list”); })
19、如果一个方法/函数,一屏显示不完,说明写得不合格,应该折开成两到三个方法/函数/
20、能用局部变量,就不用参数,能用参数就不用成员变量,能用成员变量,就不用静态变量;
21、前端和后端团队必须就前端向后端请求的链接和参数达成一致,后端向前端响应的链接和格式达成一致;
22、当你的数据来自于多张表,可以用嵌套查询、联表查询;
23、 js中直接加:debugger 单起一行;可以在客户端打断点
24、Serializable序列化
所有pojo存储数据的对象,都要实现 implements Serializable
把对象转化成字节,就要字节化,把字节转化为对象就要反序列化;
同时还要给此类加序列化码:用idea Serializable 搜索加的方法
加上后,鼠标放类上,会出提示,
26、软件设计图UML(统一建模语言)
包括 1用例图 2类图 3时序图 4协作图 5状态图 6活动图 7组件图 8配置图
27、软件设计六大原则
1.单一职责原则告诉我们实现类要职责单一;
2.里氏替换原则告诉我们不要破坏继承体系,用继承代替修改;
3.依赖倒置原则告诉我们要面向接口编程;
4.接口隔离原则告诉我们要在设计接口时要精简单一;
5.迪米特法则告诉我们要降低耦合;
6.开闭原则告诉我们要对扩展开放,对修改关闭;
设计模式的六大原则,你懂多少呢?
1.六大原则-单一职责原则
原则思想:一个方法只负责一件事情。
描述:单一职责原则很简单,一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序。 这是常识,几乎所有程序员都会遵循这个原则。
优点:降低类和类的耦合,提高可读性,增加可维护性和可拓展性,降低可变性的风险。
2.六大原则-里氏替换原则
原则思想:使用的基类可以在任何地方使用继承的子类,完美的替换基类。
描述:子类可以扩展父类的功能,但不能改变父类原有的功能。子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,子类中可以增加自己特有的方法。
优点:增加程序的健壮性,即使增加了子类,原有的子类还可以继续运行,互不影响。
3.六大原则-依赖倒置原则
原则思想:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象,抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
描述:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
优点:可以减少需求变化带来的工作量,做并行开发更加友好。
4.六大原则-接口隔离原则
原则思想:类和类之间应该建立在最小接口的上。
描述:类A通过接口依赖B,类C通过接口依赖D,如果接口类A和类B不是最小的接口,则依赖的类B和类D必须要实现他们不需要的方法。
优点:提高程序的灵活度,提高内聚,减少对外交互,使得最小的接口做最多的事情。
5.六大原则-迪米特法则
原则思想:一个对象应当对其他对象有尽可能少地了解,简称类间解耦
描述:一个类尽量减少自己对其他对象的依赖,原则是低耦合,高内聚,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。
优点:低耦合,高内聚。
6.六大原则-开放封闭原则
原则思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
描述:一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。
优点:单一原则告诉我们,每个类都有自己负责的职责,里氏替换原则不能破坏继承关系的体系。
0、运行全图
第一步:Filter拦截图(Shiro用于认证/授权/缓存/记着我/Cookie)等拦截)
第二步:DispatcherServlet(1处理器映射器-2处理器适配器-3视图解析器-4视图渲染器)
第三步:HandlerInterceptor 拦截器
1、在DispatcherServilet与处理器适配器之间,dispatcher让controller方法运行之前/之后执行
2、比如,在规定时间访问,其他时间不能访问
第四步:Controller执行
第五步:Controller调用XxxService的代理对象($$CGLIB)(此对象由AOP切面对象自动生成)
第六步:执行AOP切面对象(@Around/@Before)、执行XxxServiceImpl、执行切面对象(@AfterRunning/@AfterThowing/@After)
@Around->@Before->XxxServiceImpl方法->@AfterReturning成功执行/@AfterThrowing异常执行->@After
第七步:执行DAO(由mybatis控制)
1、Spring
1.1、为什么要使用Spring框架
耦合/解耦
1、传统new对象,导致程序耦合太强
2、用Spring将new对象、管理对象、注销对象交给spring,spring本质就是管理软件对象
1.2、spring运行图
1.3、spring创建步骤
1)在src/main/resources下创建 beas.xml 文件;
2)把需要管理的类的全类名,放入<bean></bean>
3) <bean id="userName" class="com.tedu.pojo.UserName" scope="prototype"></bean>
id是业务层(ac.getBean("userName") )调用的标记,class指向的人全类名,scpore是定义单例/多例;
4)ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("beans.xml");
将beans文件读入配置,spring根据beans里的bean去找类,并自动创建对象,并存在对象池(map),把id值作为key值;
5) ac.getBean(“user") ;
getBean(User.class);
Class type = ac.getType("userName");
type.newInstance()
ac.isPrototype("userName")
ac.containsBean("userName”)
6、对象属性赋值,注入方法
<bean><property name="name" value="韩少云"/>
<property name="userInfo" rel="userInfo"/> 写入一个对象</bean>
1.4、什么时候使用单例和多例?
1)使用频率高、无线程安全问题用单例(创建放入对象池,程序关闭注销)
2)使用频率低、被多个线程共享(不放入对象池,GC自动管理生命周期)
1.5、全注解:通过扫描装配Bean
1)创建配置文件类AppConfig.java=beas.xml
@Configuration //此注解声明此类为配置文件类
@ComponentScan //此注解声明,扫描此文件所在包及子包;
public class AppConfig {}
2)声明此文件需要被装配到beas池里,
@Component() 此声明的类会被spring装配入bean池并管理
@Scope("prototype") 声明为多例或单例
@Lazy 延迟加载,当被调用时,才真正第一次去实例化
public class UserDao {
@Autowired
private Animal animal; //把Animal类(需加component)实例化并自动赋值给animal
}//调用名称是UserDao 头字母小写userDao
com.tedu.pojo Animal.java
@value("张三")
String name;
3)读取配置文件,并赋值给ctx变量
ApplicationContext ctx = new AnnotationConfigApplicationContext( AppConfig.class );
User u1 = (User)ctx.getBean(“user") ; //此文件在与UserDao在同一包中
u1.animal.getName();
2、SpringMVC
2.1、MVC模式设计(mvc调用过程图片)
2.2、MVC总结:
1) Controller(控制器):
1)获取请求中的数据
2)调用Model(模型)来处理数据(处理业务、访问数据库)
3)调用视图来展示请求处理的结果
2) Model(模型):处理请求
1)封装数据(POJO)
2)处理业务(service)
3)访问数据库(Dao)
3) View(视图):只负责展示请求处理的结果
软件分层:
表现层 -- springmvc针对表现层提供了解决方案
业务层(业务逻辑层) -- spring三层都有涉及,提供软件中所需要的对象
持久层(数据访问层) -- mybatis针对持久层提供了解决方案,对JDBC进行了简化和封装
(POJO传递数据) (POJO传递数据)
Controller ---调用-----> Service ---调用——>Dao
2.3、创建简单的MVC
1、用maven创建项目war包;(在项目上JAVA EE-Generete…)生成web
2、配置pom文件,WEB-INF下配置web.xml文件,resources下创建springmvc-config.xml
3、com.tedu.controller.doit.java
1) 类上放@Controller 声明为controller层 ,创建对象交给spring管理
2) 方法上放@RequestMapping(“/t1”) /这是url请求的地址
3) public String t1(Emp emp,Model model) 用emp接收参数,用model传值到home.jsp
4) model.addAttribute(emp);传对象到home.jsp里,还可以传map,list,字符串
5) return “home”; //这是要转发的文件
6) 日期型参数Date必须为:2020/02/24 不能为2020-02-24
7) home.jsp拿数据
<%@page import=“com.tedu.pojo.Emp”%>导Emp包
<%Emp emp = (Emp)request.getAttribute(“emp”);%> 在request里用getAttribute拿emp对象
<%=emp.getName() %> 在对象里拿值;
2.4、SpringBoot工程下Spring MVC技术的应用?
1)工程创建
1、创建 2、添加依赖(Spring Web + Thymeleaf)
2)resourse 下的自动生成的目录
static 目录:可以存储html、css、js、image,这些资源可以直接访问;
templates 目录:thymeleaf依赖以后自动创建的目录,此目录中要存储一些html模板,这个模板页面不能直接通过浏览器url进行访问
注意配置文件里的前缀和后缀的设置,以及缓存的配置;
2.5、Spring MVC 2种响应数据处理方式
1、Model&view,2、JSON
第1种:Model & View
@Controller
public class ModelViewController {
@RequestMapping("/doModelAndView”)
public String doModelAndView(Model model) {
model.addAttribute(“username”, “jason”);
model.addAttribute(“state”, true);
return “default”; }}
执行用return 会转发到default.html, return "redirect:/t1" 会重定向到t1
ThymeLeaf会自动用default.html 里的[[${salary}]]去Map存的model,里key为salary的值;
然后返回数据封装好了的html;给default.html装载;
中小企业用户量不大时,可用此方案,数据量大时最好用前后端分离;
第2种:JSON
JSON响应数据有两种方案:POJO | Map
1、返回pojo对象
建controller类,用@RestController注解定义类,
@RestController =@Controller +@ResponseBody(定义类里的方法或该方法return值不是html,JSON需要用,否则会转发到html)
public class JsonObjectController {
@RequestMapping("/doConvertResponseToJson")
public ResponseResult doConvertResponseToJson(){
ResponseResult rs=new ResponseResult();
rs.setCode(200); 这里调用的是pojo的seCode方法
rs.setMessage("OK");
return rs ; }
@ResponseBody放类上则类里所有方法都不是转发到html,则放某个方法上表示这个方法不转发到html,只发送数据;
springBoot自动调用jackson把pojo转成字符串,发送给客户端
2、返回Map对象
@RequestMapping(“/doConvertMapToJson”)
public Map<String,Object> doConvertMapToJson(){
Map<String,Object> map=new HashMap<>();
map.put(“username”,“刘德华”);
map.put(“state”,true);
return map ; }
2.6、Spring MVC 3种请求参数处理方式
直接量 | POJO | Map
第一种:直接量
@GetMapping("/doParam01") //@GetMapping表示只接收Get方法的值;
public String doMethodParam(String name,String job,Double salary){
return "request params "+name; }
直接量的话,传过来的参数要与接收的参数名一致
如:a.com?name=bobo&job=保安&salary=55d
第二种:POJO
先建一个emp员工类POJO对象
@RequestMapping("/doemp")
public String doEmp(Emp emp){
return emp;}
//pojo对象接收请求参数,pojo对象中需提供与参数名相匹配的set方法
比如pojo对象 emp类有name,job,salary,那么就只接收这三个参数,这三个之外的不接收,
而map集合接收则参数所有值都接收;
第三种:Map
@GetMapping("/doParam03")
public String doEmp(@RequestParam Map<String,Object> map){
return "request params "+param.toString(); }
一定要在Map前加@RequestParam 注解,否则会认为Map是用来响应数据的,而不是接收数据的;
客户端传过来的所有值,map都会存,pojo只存对象里有setter方法的,而map都存
2.7、07-SpringBoot+MyBatis+Spring 技术整合实现商品模块的CRUD操作
1) 工程创建
1、创建 2、添加依赖(Spring Web + Thymeleaf + Spring JDBC + mybatis + mysql Driver)
2) Mode&view传List值;
控制层向html模板view层传 List<emp> 数据,
List<Brand> list = brandService.findBrand(name);
model.addAttribute("list", list);
ThymeLeaf拿到Map集合的model后,进行取值,
<tr th:each="b:${list}"> /each是对list集合的循环迭代,${list}是作为Key拿 model传过来的list集合的key为"list"的值,
并把当前循环出来的对象赋值给"b"
<td th:text="${b.id}">10</td> //b拿到值,其实是这次循环的Brand对象 .id,是拿Brand对象里的getId方法的返回值,代替默认值“10”
<td th:text="${b.name}">默认名字</td>
<td th:text="${b.remark}"></td>
th:text="${#dates.format(b.createdTime,'yyyy年MM月dd日')}”
3)thymeleaf
<button type="button" th:onclick="idDelete([[${b.id}]])">删除</button>
function idDelete(id){
if(!confirm("您确认删除吗"))return;
let url = "http://localhost:8080/t3/"+id;
location.href=url; }
2.8、rest风格url
@RequestMapping({"t1/{name}","t1/{name}/{job}","/t1"})//不带参,带name参,带name+job参
public String t1(@PathVariable(required = false) String name, @PathVariable(required = false) String job, Model model){
@PathVariable可以实现 sohu.com/t1/周波/总统 这样的参数形式;
required=false 代表这个参数可以没有
3、SpringBoot
3.1、用idea创建SpringBoot(maven)
1) new-module-Spring Initializr->
2) 创建后,右键pom.xml文件,选add maven(加入maven)->reload(手动开启导入依赖包)
3启动项目程序(运行含@SpringBootApplication注解的文件,component会从此文件所有包及子包自动扫描component注解并装配到bean pool
4) (理解spring启动流程图)
3.2、Git
1) git安装及配置
git config --global user.name "your-name"
git config --global user.email "your-email@youremail.com"
2) 添加git和Gitee进idea,(Gitee账号创建以及与idea绑定登陆)
3) 把项目导入到gitee:vcs-import into ->gitee
4) 项目提交:add->commit->push
(理解gif存储模型图)
3、vm options
1) edit configrations
-Xms512m 堆最小内存
-Xmx1024m 堆最大内存
2) GC参数: -XX:+PringGC (程序运行过程中输出GC相关信息)
3) 监控类加载顺序参数: -XX:+TraceClassLoading
3.3、SpringBoot 项目中的依赖注入过程分析
A、注入过程
1、创建A接口,再创建b1,b2类实现A接口并加@Component,再到C类中去创建@Autowired去调A类
2、@Autowired A a;
2.1、先根据类型去Bean pool对象池里去匹配,A类有且仅有一个b1实现类,就把已自动装配进Bean池的b1对象,赋值给 A a;
2.2、如果找到2个及以上子实现类对象时,就配置属性名,如:A b1 这样就是匹配的 b1这个子类对象的bean;
2.3、也可以在@Autowired处加 @Qualifier("b1")来显式指明调用b1这个子类的bean;
B、通过有参构造注入(给属性赋值)
private Cache cache;
@Autowired
public CacheService(@Qualifier("softCache") Cache cache) {this.cache = cache;}
3.4、数据库连接池(hikariCP)
SpringBoot工程下如何实现对HikariCP连接池的整合?
1) 创建项目后,导入两个依赖:mysql-connector-java,spring-boot-starter-jdbc
2) 配置application.properties文件
spring.datasource.url=jdbc:mysql:///dbgoods?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
3) 在jdbc调用时,直接是这样,
@Autowired
private DataSource dataSource; //DataSource 这个是从连接池自动生成;
conn = dataSource.getConnection();
sql = "select * from tb_goods where id>?";
ps = conn.prepareStatement(sql);
3.5、如何导入别人的工程:
1)本地导入:file->project Structure-> + -> import Module ->本包目录;
导入工程的话,先建一个工程;工程只是一个目录而已,一般都是建Module
2) 远程导入:VCS -> Get from Version Control -> url
3.6、日志纪录
* 1)项目中的日志记录用的什么API? SLF4J (Simple Logging Facade for Java)
* 2)为什么使用此API? (这组API定义了日志的规范-程序中建议耦合规范->其目的提高其可扩展性)
* 3)这组API的具体实现,你在项目中用的是谁?(ch.qos.logback.classic.Logger)
* 4)你为什么不用log4j?(异步情况下,logback性能要比log4j好,SpringBoot内置就是logback)
* 5)你的项目中的日志是同步写还是异步写?(大部分异步,提高其用户体验)
* 6)你了解日志的级别吗?(日志的输出级别-ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF)
在spring中默认日志纪录输出级别为Info
* 7) application.properties 中配置log
logging.file.path=/java/log/ //日志输出的文件
logging.level.com.tedu=debug //日志输出的级别
* 8) private static final Logger log=
LoggerFactory.getLogger(GoodsServiceImpl.class);
3.7、lombok
@Setter 用于为描述的类生成setter方法,不包含final修饰属性。
@Getter 用于为描述的类生成getter方法。
@ToString 用于为描述的类添加toString方法。
@Data 等于以上3个总和
@Slf4J 用于为描述的类添加一个日志属性对象。
步骤:
idea中的安装配置
第一步:打开idea的设置窗口,找到plugins菜单,搜索lombok进行安装,如图所示
第二步:Settings->Build->Annotation Processors->Enable annotation(打勾)
使用:
第一步:添加lombok依赖。
第二步:在类上应用lombok注解。
3.8、热布署
第一步:settings->Build->comiler-build project automatically(打勾)
第二步:IDEA工具中启动注册窗口(按ctrl+shift+alt+/),->registry
第三步:compiler.automake.allow.when.app.running 打勾
第四步:加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
3.9、项目健康监控
第一步:添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
第二步:http://localhost/actuator
3.10、springBoot异常处理
前端控制器流程
最优先:方法中try/catch
其次:在本类中找异常处理方法
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public String doHandleArithmeticException(ArithmeticException e){ //这里的异常要大于上面的
e.printStackTrace();
return "计算过程中出现了异常,异常信息为"+e.getMessage();}
@ExceptionHandler注解描述的方法为异常处理方法(注解中的异常类型为可处理的异常类型)
最后:全局异常处理类
@RestControllerAdvice
( = @ControllerAdvice+@ResponseBody )
public class GlobalExceptionHandler {
@ExceptionHandler(ArithmeticException.class)
public String doHandleArithmeticException(ArithmeticException e){
e.printStackTrace();
return "计算过程中出现了异常,异常信息为"+e.getMessage(); }}
@RestControllerAdvice 注解描述的类为全局异常处理类,当控制层方法中的异常没有自己捕获,
也没有定义其内部的异常处理方法,底层默认会查找全局异常处理类,调用对应的异常处理方法进行异常处理
尽量用条件判断处理,而不是用异常处理;
全局异常类要把遇到的所有异常列出来,还要写一个Runtime和Throwable来接,防止有的异常接不到;
3.11、ResponseResult响应标准设计及实现
不管什么项目,都要有一个“响应结果集”类,这是一个返回JSON数据的类;
第一步:创建pojo对象封装数据:ResponseResult.java
数据包含三部分:状态(state),消息(message),具体数据(data)
/**响应状态码(有的人用code)*/
private Integer state=1;//1表示ok,0表示error,.....
/**状态码对应的信息*/
private String message="ok";
/**正确的响应数据*/
private Object data;
还要设置上面属性的setter,getter
public void setData(Object data) {
this.data = data; }
public ResponseResult(Throwable e){
this.state = 0;
this.message = e.getMessage();}
第二步:controller类的方法中返回值用ResponseResult
@RequestMapping("/doCompute/{n1}/{n2}")
public ResponseResult doCompute(@PathVariable Integer n1, @PathVariable Integer n2){
Integer result=n1/n2;
ResponseResult r=new ResponseResult("计算结果:"+result);
r.setData(result);
return r;}
或者直接: return new ResponseResult( result );
第三步:全局异常处理类
@RestControllerAdvice
public class GlobalExceptionHandler {
// private static final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class);//2
@ExceptionHandler(ArithmeticException.class)
public ResponseResult doHandleArithmeticException(ArithmeticException e){
e.printStackTrace();
// log.info("exception {}",e.getMessage());
return new ResponseResult(e);//封装异常结果 }}
4、Mybatis进阶
4.1、springboot整合mybatis
第一步:创建Module(创建时如果官网默认连接打不开,用阿里云:https://start.aliyun.com)
第二步:添加依赖(mysql + spring jdbc + mybatis starter )
第三步:设置配置文件:
1、数据库连接池配置,
2、mybatis配置 mapper文件地址:mybatis.mapper-locations=classpath:/mapper/*/*.xml
所有mapper 下的.xml 都放mapper下
第四步:代码实现
1、定义商品模块POJO对象类型,作数据封装
2、定义商品模块持久层DAO对象GoodsDao接口及方法映射
用@Mapper注解该类;
3、GoodsDao接口映射文件及SQL映射定义
<?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">
//namespace指向mapper的全类名
<mapper namespace="com.cy.pj.goods.dao.GoodsDao”>
//id与mapper的方法名相同,resultType是返回值所需的类的数据封装类
<select id="findGoods" resultType="com.cy.pj.goods.pojo.Goods”>
select * from tb_goods </select> </mapper>
4、编写service业务层代码:获得DAO层数据;
4.2、@Mapper和@Repository
1)
@Mapper
此注解用于描述mybatis框架中的数据逻辑层接口,用于告诉mybatis框架此接口实现类,
由mybatis框架创建(底层基于jdb创建其代理),
mybatis会自动给该对象生成继承的代理子对象,并重写获得数据的方法(整合mapper.xml文件的sql语句)
此类型的对象会交给spring管理;
@Repository
描述数据层实现类,用于交给spring管理;
4.3、在方法中加入事务声明
@Transactional 这是一个AOP切面对象注解,此注解描述为事务切入点方法
这个注解会把描述的方法,执行前开启事务,方法结束后“提交或回滚事务”;
4.4、执行插入语句后将自增id存入POJO对象
<insert id="insertObject"
parameterType="com.cy.pj.sys.pojo.SysRole" //执行完sql后,把结果返回到此pojo对象中
useGeneratedKeys="true" //调用数据库的自增字段
keyProperty="id"> //调用pojo对象中的setId方法,把自增字段写入pojo对象;
4.5、写入一对多关联字段
比如一个用户,有超级管理员、仓库管理员、财务三个角色,
要把此用户id和角色id同时,用户及角色关联表(sys_user_roles)
<insert id="insertObjects">
insert into sys_user_roles
(user_id,role_id)
values
<foreach collection="roleIds" separator="," item="roleId">
(#{userId},#{roleId})
</foreach>
4.6、 基于多个角色查到菜单
-- where role_id in (1,2,3,4)
where role_id in
<foreach collection="roleIds" open="(" close=")" separator="," item="roleId">
#{roleId}
</foreach>
4.7、mybatis中多表查询返回到pojo对象有三种方法
第一种:分两次/多次分别查询,然后把结果分别写入pojo对象中;
第二种:多表联合查询,一次查询同时写入pojo(但只适合于多表在一个数据库中,分布式多个表在不同数据库中是不适合的)
第三种:嵌套查询,先查询一个表,基于第一个表的查询结果,再去查第二个表/第三个表;
第一种方案:
举例:我们要查id,name,dept_id,role这四个字段分别存在2个表中,我需要建一个gogo.java 这样的pojo对象这个对象里包括
id,name,dept_id,role四个属性和对应的getter和setter
我可以先在第一个表中,把id,name查出来,mybatis会自动把查出来的id,name,通过resultType指向的gogo这个pojo对象,
然后在这个pojo对象里的setId和setName方法把查出来的id和name存入对象;
然后再用一个mapper.xml写一个sql把dept_id,role用上述的方法,把dept_id,role也存入gogo这个pojo对象;
当这两个方法都执行了,gogo这个pojo对象就拥有了id,name,dept_id,role 的值;,然后直取值就可以了
但是:如果role角色是多个值,有管理员和部门经理两个角色,两个值,mybatis不会自动赋值给role,
需要在resultType声明为int,然后在DAO类里返回值用List<Integer>,这样就会返回Integer类型的list集合,
然后,再把这个返回值,在service层,调用gogo这个pojo对象的setRole方法,来存入;
第二种方案
<resultMap id="sysUserDept" type="com.cy.pj.sys.entity.SysUserDept">
<!--association 一般用于many2one(多对一)或one2one(一对一)的查询映射-->
<association property="sysDept"
column="deptId"
select="com.cy.pj.sys.dao.SysDeptDao.findById">
</association>
</resultMap>
<select id="findSysUsers" resultMap="sysUserDept">
select * from sys_users
<if test="userName!=null and userName!=''">
where userName like concat('%',#{userName},'%')
</if>
order by id desc
limit #{startIndex},#{pageSize}
</select>
//resultMap="sysUserDept"与上面的id="sysUserDept"一致,建立关联
下面语句执行完后,把deptId赋值到column,然后执行select="com.cy.pj.sys.dao.SysDeptDao.findById"这个
映射所代表的mapper.xml或dao文件里的方法,调用deptId,求出的结果放到property="sysDept"里,
再存到type="com.cy.pj.sys.entity.SysUserDept" 这个pojo对象里
4.8、一些其他技巧和注意事项
1)、多表关联删除时,先删除关系数据,再删除自身信息;
2)、数据校验一般放在service层,因为有时会用service作为接口对外暴露
3)、@param("username") 在dao类中,链接数据的接口,传递参数要用 @param("username")
List<SysLog> findPageObjects(@Param("username") String username,
4)、resources下给mapper新增目录时,一定要用 mapper/goods 这样的写,中间不是用 . 而是 /
4.9、mysql优化
1、建立索引(索引就像一本书的大纲)
2、select * from emp 这个 * 要改成具体的字段名,并且用多少拿多少;
3、去掉无用的sql语句;
5、AJXA异步
4.1、特点
4.2、js中Ajax四个编程步骤
基于图-4 的分析,Ajax 应用的编程步骤如下:
第一步:基于 dom 事件创建 XHR 对象(XMLHttpRequest 对象)
第二步:注册 XHR 对象状态监听,通过回调函数(callback)处理状态信息。
第三步:创建与服务端的连接
第四步:发送异步请求实现与服务端的通讯
function doAjaxGet(){//错误调试:debugger,console.log(),排除法
//1.创建 XHR 对象
var xhr=new XMLHttpRequest();
//2.设置状态监听(将服务端响应的结果更新到 ajaxResultId 位置)
xhr.onreadystatechange=function(){
if(xhr.readyState==4&&xhr.status==200){
document.getElementById("ajaxResultId").innerHTML=xhr.responseText; } };
//3.建立连接 xhr.open("GET","http://localhost/doAjaxGet",true);//true 表示异步(底层会启动线程与服务端通讯)
//4.发送请求
xhr.send(); }//get请求在xhr.send()里放数据传数据,都没用;
在url链接处加id=100这样传数据,到服务端其实变成了字符串,需要把比较双方都强传为"String"
特别是在进行id比较时,从浏览器用ajax传到服务器的id是字符串,而服务器的id往往是int,这时要把int强转为字符串;
4.3、jQuery中Ajax的应用步骤
1)原生使用
function doAjaxPost(){
let url="http://localhost/doajaxpost";
let params = "id=105&name=computer&remark=good";
$.ajax({
url:url,
data:params,
type:"POST", //默认为GET,get可以不写/ put改/delete删/get查/post增
async:true, //默认为true,表示异步,false是同步,异步可以不写此行
contentType:"application/x-www-form-urlencoded", //可以不写,声明post会自动加上,
dataType:"JSON", //这里的有Text/JSON表示服务端响应到客户端的数据类型,假如是json,客户端会将其换转为json格式的js对象;
success:function(result){
$("#result").html(result); } }); }
2)其他操作
$.getJSON(url,params,function(result){ //直接获取
$("#result").html(result); })
$.get(url,params,function(result){ $("#result").html(result); })
$.post(url,params,function(result){ $("#result").html(result),"json"; })
html,xml,json请求所返回的数据类型;
ajax请求方式:
增:post 接时(@PostMapping)
删:delete 接时(@deleteMapping)
改:put 接时(@PutMapping)
查:get 接时(@GetMapping)
Iterator it = dbList.iterator();
这个迭代器时,如果要比较id时,迭代器里的id不能为null
Ajax在服务端的处理
3、在服务端controller类的方法上加上@CrossOrigin
//修改操作
@PutMapping("/put")
public String update(@RequestParam Map<String,Object> updateMap){
Iterator it = list.iterator(); //用iterator来迭代,不要用foreach
while(it.hasNext()){
Map map = (Map<String,Object>)it.next();
if(map.get("id").equals(updateMap.get("id"))){
map.put("name",updateMap.get("name")); }}
return "修改成功"; }
3)于AJAX的一些重点问题:
1、谁将返回值转换为json格式字符串? jackson
在这个json字符串底层是如何获取key/value?通过pojo对象的getter方法;
从服务端传过来的字符串,到了客户端怎么就变成了Json对象呢?是客户端将服务端响应的字符串转换为json对象
是js里有一个函数叫json,里面的函数叫parse,
2、如何解析服务端返回的json字符串?服务端响应结果为字符串
"{"state":1,"message":"ok","data":"{pageCurrent: 3, records: [ { id: 47, name: "超级管理员",}] }"}"
第一步:将json字符串转换为json的js对象,let jsonObj2 = JSON.parse(jsonStr);
第二步:jsonObj2.state 结果是 1, jsonObj2.data.pageCurrent 结果是 3,jsonObj2.records
但是$.getJSON会自动将拿到的值从字符串转化成json对象;
$.get/$.post/$.ajax 如果dataType:"Text" 就会保持字符串,如果是json就会自动转成json对象;
所以就用result.data.records就能拿到list数组,再循环此数组就能拿具体的值了;
6、springBoot中注解
@SpringBootTest @Test-此注解描述的类为springboot工程中的单元测试类
@ComponentScan() 系统启动时会从此注解所在包及子包扫@Component()注解的类并装配入bean池
@Component() -将所在类交给spring来管理,自动创建对象
@Scope(“prototype”)声明为多例,单例不用声例是默认
@Lazy //spring框架提供的一个用于定义延迟加载特定的一个注解
Dao层(mybatis的注解)
@Mapper和@Repository是常用的两个注解,两者都是用在dao上
@Repository需要在Spring中配置扫描地址,然后生成Dao层的Bean才能被注入到Service层中。
@Mapper不需要配置扫描地址,通过xml里面的namespace里面的接口地址,生成了Bean后注入到Service层中。
@Param("userId") Integer userId
--@Select("select * from tb_brand where id=#{id}")
--@Update("update tb_brand set name=#{name},remark=#{remark} where id=#{id}")
--@Delete("delete from tb_brand where id=#{id}”)
Service层
@Service -指定这是一个service
@Autowired -把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作
@Override 重写
Controller层
@Controller -用于定义控制器,在spring项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层)
@ResponseBody -表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用
@RestController //==@Controller+ResponseBody
@RequestMapping -("/**")-可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@PathVariable -接收请求路径中占位符的值
@RequestParam -将请求参数绑定到你控制器的方法参数上,需要用map来接受参数时需要用到
@CrossOrigin -注解描述的方法支持跨域访问
与Ajax配置接收参数
--@GetMapping("/doAjaxGet") -处理get请求,传统的RequestMapping来编写应该是@RequestMapping(value = “/get/{id}”, method = RequestMethod.GET)
--@PostMapping("/doUpdateBrand") -处理post请求,传统的RequestMapping来编写应该是@RequestMapping(value = “/get/{id}”,method = RequestMethod.POST)
--@PutMapping("/doAjaxUpdate") -和PostMapping作用等同,都是用来向服务器提交信息。如果是添加信息,倾向于用@PostMapping,如果是更新信息,倾向于用@PutMapping。两者差别不是很明显。
--@DeleteMapping("/doAjaxDelete") -删除URL映射
全局异常处理
@ControllerAdvice -此注解描述的类为全局异常处理类
@RestControllerAdvice //==@ControllerAdvice + @ResponseBody
@ExceptionHandler(ArithmeticException.class)
Lombok
@Slf4j -实现日志输出,调用时用(log.info(“日志{},)
@Data =(@Setter + @Getter + @ToString)
Aop切面对象
@Transactional 此注解声明此方法为整个事务;
@Aspect 声明类为切面对象类,此类还要加@Component
@Pointcut 定义切入点 @Pointcut(“@annotation(reqire.class)”)
@Around / @Before / @ AfterReturning / @AfterThrowing / @After
7、面向切面AOP编程
7.1、面向切面AOP编程的逻辑原理
Controller控制器A类,调service业务层B接口,C类实现B接口,E类是切面对象,C类在执行“c1方法“时,E类可以控制
在c1方法执行前后,先执行D类的一些语句;
原理:底层给C类自动创建了一个实现类D代理对象,这个对象在控制先执行e类里的方法,再执行c类的方法
然后,系统调用变成controller–>XXXService->CGLIB(切面生成的service代理对象)->XXXServiceImpl(实现类)->dao
7.2、AOP编程步骤
第一步:AOP对象导入依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第二步:定义切面对象
2)@Aspect 声明一个类为切面对象 (记得加@Component,此类交给Spring来管理)
通知(Advice)、连接点(joinpoint)、切入点(pointcut)、各切面对象执行顺序(order(1) )
第三步:自定义引用注解
建一个注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredCache {}
第四步:定义切入点
3)@Pointcut 定义切入点
bean(重点)用于匹配指定bean对象的所有方法(粗粒度)
within(了解)用于匹配指定包下所有类内的所有方法
execution(了解) 用于按指定语法规则匹配到具体方法
@annotation(超重点)用于匹配指定注解修饰的方法(细粒度)
例:
@Pointcut("@annotation(com.tedu.pj.brand.common.RequiredCache)")
public void doCache(){}
第五步:定义通知和连接点
4)Advice通知
通知执行顺序:@Around->@Before->方法->@AfterReturning成功执行/@AfterThrowing异常执行->@After
5)JoinPoint连接点
@Around("doCache()")
public Object doCacheAround(ProceedingJoinPoint jp) throws Throwable{
System.out.println("首先执行");
result=jp.proceed(); //中间执行
System.out.println("最后执行");
return xy} 返回值必须目标执行方法的返回值;
第六步:在引用类引用
6)引用项目:调用切面对象为依赖,同时在需要被切面的方法上加注解;
<dependency>
<groupId>com.tedu.jt.emp.aop</groupId>
<artifactId>19aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
第七步:在方法上加注解
8、动吧项目
8.1、增删改查从controller>service(serviceImpl)>dao>mapper
8.2、向一个关联表插入一对多数据(详情看4.5)
8.3、插入数据后返回自增id(详情看4.4)
8.4、查询两张及以上的表,其中一张返回多条数据(详情看4.7)
8.5、返回数据上:从多表分别向关联pojo存数据,再把pojo返给page对象,再把page对象返给R对象。
8.6、日志写入
步骤:
第一步:创建RequiredLog注解
内容参数声明:String operation() default “";
第二步:在需日志纪录的目标执行方法加切面对象的注解:
@RequiredLog(operation = "禁用启用”)
第三步:创建切面对象,在切入点用
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)"
第四步:获得各项操作的信息(重点在获得username,operation,method,params这4个的值)
username:的获得:SysUser user = (SysUser)SecurityUtils.getSubject().getPrincipal();
这里有从数据库获得的该用户的所有信息,再:user.getUsername()就拿到username了;
(具体详情要看shiro获得用户值)
method方法名获得:
MethodSignature ms = (MethodSignature) jp.getSignature();//获得目标方法签名信息
String targetMethod = ms.getName(); //通过目标方法签名信息得方法名
String targetClassToString = ms.getDeclaringTypeName();//通过签名得全类名
opration注解上的操作
//获取目标方法上的requiredLog注解
String operation = jp.getTarget().getClass().getDeclaredMethod(targetMethod, ms.getParameterTypes()).getAnnotation(RequiredLog.class).operation();
以上操作:通过目标方法->获得类名->获得方法名(方法名,参数)->获得此方法名字为xx的注解->拿operation的值
params参数的获得:
//.jp.getArgs()会获得目标方法的执行时传入的实现参数并转化成对象,
// objectMapper().writeValueAsString()将任意对象转成字符串)
String params = new ObjectMapper().writeValueAsString(jp.getArgs());
第五步:写这些值写入pojo后,pojo传值到方法存入数据库;
8.7、公共类common的设计(本阶段其他环节纪录了)
R类,GlobalException类,Utils类,Pages类,
annotation,aspect,cache,pojo,vo,web
8.8、父模块引用,依赖模块导入
如何在模块中把另一个模块作为依赖?
1、project Structure -> module dependency(导入module依赖) / JARs包依赖
2、在pom.xml文件加依赖,
<dependency>
<groupId>com.cy.common</groupId>
<artifactId>20common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
3、删除15common的启动类
如何在模块中继承另一个模块?
1、15parent 是一个父模块,用来管理依赖,把src删掉,在创建父模块时声明父模块为pom包;
2、在子模块的pom文件中调用父模块
<parent>
<groupId>com.cy</groupId>
<artifactId>15-dbpms-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
这样子模块,就能像使用String一样使用15common的方法和类,也同时继承了15parent的包;
8.9、Web Mvc拦截器(实现在规定时间访问其他时间不能访问)
实现访问,可以用AOP拦截,这个一般处于在service层,也可以用MVC层做拦截器,会在controller执行前就被拦截
Spring MVC中的拦截器基于回调机制,可以在目标方法执行之前,先进行业务检测,满足条件则放行,
不满足条件则进行拦截,拦截器原理分析如下图所示
第一步:拦截器定义
/**
* 定义时间访问拦截器
* spring MVC模块中拦截器的标准为HandlerInterCeptor接口
* 这个接口中的方法可以实现对目标handler(@controller)方法的访问拦截,
* */
public class TimeAccessInterCeptor implements HandlerInterceptor {
// preHandle方法会在@Controller域@RestController描述的类中的请求响应处理方法之前执行。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=======执行一下");
LocalTime time = LocalTime.now();
int hour = time.getHour();
System.out.println("hour="+hour);
if(hour>=9&&hour<=13) return true;
throw new ServiceException("不在规定的访问时间:请于上午9点到下午18点之间"); }}
第二步:拦截器配置
@Configuration
public class SpringWebConfig implements WebMvcConfigurer{//web.xml
//配置spring mvc 拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TimeAccessInterceptor()).addPathPatterns("/user/doLogin"); }}
9、Shiro
9.1、Shiro基本知识,他有拦截器,并在前端控制器之前的(filter)
-
Subject :主体对象,负责提交用户认证和授权信息。
-
SecurityManager:安全管理器,负责认证,授权等业务实现。
-
Realm:领域对象,负责从数据层获取业务数据。
-
Shiro是apache旗下一个开源安全框架(http://shiro.apache.org/),它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架
-
shiro的主要功能:
-
1. Authentication:身份认证/登录(账号密码验证)。
-
2. Authorization:授权,即角色或者权限验证。
-
3. Session Manager:会话管理,用户登录后的session相关管理。
-
4. Cryptography:加密,密码加密等。
-
5. Web Support:Web支持,集成Web环境。
-
6. Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
-
7. Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
-
9. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
-
10. Remember Me:记住我,登录后,下次再来的话不用登录了。
-
9.2、Shiro的基本配置
第一步、创建shiro配置文件@Configuration
public class SpringShiroConfig {}
第二步、在Shiro配置类中添加SecurityManager配置(这里一定要使用org.apache.shiro.mgt.SecurityManager这个接口对象)
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
return sManager; }
第三步: 在Shiro配置类中添加ShiroFilterFactoryBean对象的配置。通过此对象设置资源匿名访问、认证访问
@Bean
public ShiroFilterFactoryBean shiroFilterFactory (
SecurityManager securityManager) {
ShiroFilterFactoryBean sfBean=
new ShiroFilterFactoryBean();
sfBean.setSecurityManager(securityManager);
//定义map指定请求过滤规则(哪些资源允许匿名访问,哪些必须认证访问)
sfBean.setLoginUrl(“/doLoginUI”);//设置此路径为登陆url,controller要建一个这个方法
LinkedHashMap<String,String> map= new LinkedHashMap<>();
//静态资源允许匿名访问:“anon”
map.put(“/bower_components/“,“anon”);
map.put(”/build/”,“anon”);
map.put(”/user/doLogin",“anon”);//放行登陆页面
map.put(”/doLogout",“logout”); //退出地址
//除了匿名访问的资源,其它都要认证(“authc”)后访问
map.put(”/**",“authc”);
sfBean.setFilterChainDefinitionMap(map);
return sfBean;}
9.2、认证登陆
authentication 美[ɔːˌθentɪˈkeɪʃn] 身份验证
其中认证流程分析如下:
-
1)系统调用subject的login方法将用户信息提交给SecurityManager
-
2)SecurityManager将认证操作委托给认证器对象Authenticator
-
3)Authenticator将用户输入的身份信息传递给Realm。
-
4)Realm访问数据库获取用户信息然后对信息进行封装并返回。
-
5)Authenticator 对realm返回的信息进行身份认证。
-
第一步:创建dao和mapper.xml调用户、密码和盐值数据;
-
第二步:写Service(realm类)
-
通过token拿到用户名,通过用户名在数据库中拿出密码、盐值
-
并通过new SimpleAuthenticationInfo 在参数中传用户对象、密码、盐值、用户名)完成封装;
-
@Service
-
public class ShiroUserRealm extends AuthorizingRealm {
-
//设置凭证匹配器(与用户添加操作使用相同的加密算法)
-
// 通过此方法完成认证数据的获取及封装,系统底层会将认证数据传递认证管理器,由认证管理器完成认证操作。
-
@Override
-
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
-
throws AuthenticationException {
-
//1.获取用户名(用户页面输入)
-
UsernamePasswordToken upToken=(UsernamePasswordToken)token;
-
String username=upToken.getUsername();
-
//2.基于用户名在数据库查询用户信息
-
SysUser user= sysUserDao.findUserByUserName(username);
-
//3.判定用户是否存在
-
//4.判定用户是否已被禁用。
-
//5.封装用户信息
-
ByteSource credentialsSalt=
-
ByteSource.Util.bytes(user.getSalt()); //从数据库中得到盐值
-
//记住:构建什么对象要看方法的返回值
-
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(
-
user,//principal (身份)
-
user.getPassword(),//hashedCredentials
-
credentialsSalt, //credentialsSalt
-
getName());//realName
-
//6.返回封装结果
-
return info;//返回值会传递给认证管理器(后续
-
//认证管理器会通过此信息完成认证操作) }}
-
第三步:SpringShiroConfig配置类中添加realm
-
@Bean
-
public SecurityManager securityManager(Realm realm) {
-
DefaultWebSecurityManager sManager=
-
new DefaultWebSecurityManager();
-
sManager.setRealm(realm);
-
return sManager;}
-
第四步:在Controller中添加处理登陆的方法
@RequestMapping(“doLogin”)
public JsonResult doLogin(String username,String password,boolean isRememberMe){
//1、对用户进行封装存值
UsernamePasswordToken token = new UsernamePasswordToken(username, password, isRememberMe);
if(isRememberMe) token.setRememberMe(true);//2、对用户进行认证 SecurityUtils.getSubject().login(token); return new JsonResult("登陆成功");} //1)token会传给shiro的SecurityManager //2)SecurityManager将token传递给认证管理器 //3)认证管理器会将token传递给realm
第五步:在全局异常处理类中添加对应的异常回应
-
9.2、授权访问
第一步:在Shiro配置文件中加入advisor
* 此对象会在spring启动时加载,并且通过此对象,可以找到@RequiresPermissions注解描述的方法,
* 然后这些方法在运行时,由此Advisor对象,调用SecurityManager中的checkPermissions方法为目标切点方法
@Bean
public AuthorizationAttributeSourceAdvisor
authorizationAttributeSourceAdvisor (
SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor=
new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor; }
第二步:写DAO和Mapper,通过用户id得到角色,角色得到菜单,菜单得到授权组
第三步:ShiroRealm(service),从数据库中拿当前用户的授权标识并封装返回。
@Service
public class ShiroUserRealm extends AuthorizingRealm {
/**通过此方法完成授权信息的获取及封装*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//1.获取登录用户信息,例如用户id
SysUser user=(SysUser)principals.getPrimaryPrincipal();
Integer userId=user.getId();
//2.基于用户id获取用户拥有的角色(sys_user_roles)
List roleIds=sysUserRoleDao.findRoleIdsByUserId(userId);
if(roleIds==null||roleIds.size()0) throw new AuthorizationException();
//3.基于角色id获取菜单id(sys_role_menus)
List menuIds= sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);
if(menuIdsnull||menuIds.size()==0) throw new AuthorizationException();
//4.基于菜单id获取权限标识(sys_menus)
List permissions=sysMenuDao.findPermissions(menuIds);
//5.对权限标识信息进行封装并返回
Set set=new HashSet<>();
for(String per:permissions){
if(!StringUtils.isEmpty(per)){
set.add(per); } }
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.setStringPermissions(set);
return info;//返回给授权管理器 } }
第四步:向需要保护的方法上加注触
@RequiresPermissions("sys:user:update”)
这样advisor会扫描此注解,有人访问此注解描述的方法时,
会先从数据库查找此人是否拥有此sys:user:update标识,
如果有就执行目标方法,没有就返回错误,
9.3、加密(md5)
1、MD5加密
String pwd=“123456”;
String salt= UUID.randomUUID().toString(); //用随机产生盐值
SimpleHash sh = new SimpleHash(“MD5”,pwd,salt,10); //10是加密次数
String hashedPwd = sh.toHex();
9.4、 cacheManager
第一步:在配置文件中加入在SpringShiroConfig中配置缓存Bean对象(Shiro框架提供)。
@Bean
public CacheManager shiroCacheManager(){
return new MemoryConstrainedCacheManager(); }
第二步:修改securityManager的配置,将缓存对象注入给SecurityManager对象
@Bean
public SecurityManager securityManager(Realm realm,
CacheManager cacheManager) {
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
sManager.setRealm(realm);
sManager.setCacheManager(cacheManager);
return sManager;}
9.5、isRememberMe(记住我)(cookie)
第一步:在doLogin中加入代码
if(isRememberMe) token.setRememberMe(true);
第二步:在SpringShiroConfig配置类中添加记住我配置
@Bean
public RememberMeManager rememberMeManager() {
CookieRememberMeManager cManager=
new CookieRememberMeManager();
SimpleCookie cookie=new SimpleCookie(“rememberMe”);
cookie.setMaxAge(72460*60);
cManager.setCookie(cookie);
return cManager; }
第三步:在SpringShiroConfig中修改securityManager的配置,为securityManager注入rememberManager对象
@Bean
public SecurityManager securityManager(
Realm realm,CacheManager cacheManager
RememberMeManager rememberManager) {
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
sManager.setRealm(realm);
sManager.setCacheManager(cacheManager);
sManager.setRememberMeManager(rememberManager);
return sManager;}
第四步:修改shiro的过滤认证级别,将/**=author修改为/**=user,查看黄色背景部分。
从原来的:map.put("/**","authc”); 修改为:map.put("/**","user");//
user代表可以从用户浏览器cookie中读取账号信息进行身份认证
9.6、利用session 来配置Shiro会话时长(不配置就默认30分钟)
第一步:在SpringShiroConfig类中,添加会话管理器配置
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sManager=
new DefaultWebSessionManager();
sManager.setGlobalSessionTimeout(60601000);
return sManager;}
第二步:在SpringShiroConfig配置类中,对安全管理器 securityManager 增加 sessionManager值的注入
@Bean
public SecurityManager securityManager(
Realm realm,CacheManager cacheManager,
RememberMeManager rememberManager,
SessionManager sessionManager) {
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
sManager.setRealm(realm);
sManager.setCacheManager(cacheManager);
sManager.setRememberMeManager(rememberMeManager);
sManager.setSessionManager(sessionManager);
return sManager;}
9.7、介绍cookie和session
cookie是由服务端创建,发送给客户端,由客户端保存,下次客户端携带cookie访问服务端,
服务端读取cookie后,登陆信息在有效期,放行;
cookie是服务端生成一把密钥根据密钥生成密码形式的cookie(base64加密),发给客户端存储,客户端携cookie访问服务端,
服务端按之前的密钥去解密cookie,判断是不是合法身份,如果是就放行,。还有服务器重启此密钥会失效,生成新密钥,之前客户
端保存的cookie也会失效;因为新密钥无法解密之前密钥加密的信息;
1、HTTP协议是一种无状态协议,无法存储客户端与服务端的会话状态
2、客户端与服务端通讯过程中产生的会话状态信息存储到哪里(cookie和session)
3、cookie对象由服务端创建,但是是在客户端保存信息的一个对象;
4、cookie对象的类型有两种:会话cookie和持久cookie(我们手动设置了生命周期)
5、会话cookie在浏览器关闭时,生命周期自动结束;
6、session对象由服务端创建,并在服务端保存状态的一个对象;
7、session对象创建以后会将session 对象的id以会话cookie形式写到客户端,客户端可以再访问服务器时基于这个id找到服务端的session
8、浏览器关闭,会话cookie结束,写到客户端的jsessionid也就无效了,此时客户端再访问服务器无法基于jsessionid找到对应的session了(但服务端的session可能还是存在的)
* 这个对象一般是一个会话创建一个,并且会有一个会话ID(JSESSIONID),
* 可以通过这样的对象来记录登录用户信息;默认记30分钟,只要客户端操作界面一下,有活动,在活动结束后30分钟后失效;
* 需要重新登陆,除了登陆信息,还可以记录购物车信息;
* 在分布式架构中很少用,在单体架构中多用,,在分布式架构中用redis;
* session对象由服务端创建,但这个对象创建好以后,会将其JSESSIONID以会话cookie的形式写到客户端
* 这个JSESSIONID的生命周期是客户端关掉浏览器就没有了;
* 客户关在访问服务器时,会携带JSESSIONID到服务端,然后基于这个JSESSIONID找到session,再从session取数据
* 如果希望关了浏览器,还能存在,就把会话cookie存为持久cookie
10、前端
1、树结构treegrio,ztree
2、thymeleaf
11、其他经验总结
1)类的设计原则:单一职责(单一件事),不要类的功能大而全,而是单一,大而全,里面的东西就多,A功能出错或修改B功能也受影响;
2)is a (继承),has a(有一个,在A类中,调用B类(new)并赋值对一个变量)就是A类中有B类;,use a(a中使用了b的一个方法)
3)发现错误:先找caused by ,再向上查找;
4)连接协议(UDP广播协议,发完就完事),(TCP3次握手4次挥手,第1次:我要发东西,第2次:响应,行你发吧,第3次:发东西,第4次:关闭,再见)
5)接口定义的是can 能做啥,实现类定义的是 do 做啥
6)查数据库时,一条纪录可以存一个map,多行纪录存放到多个map里,多个map存List里;
7)程序应该耦合于规范,而不是耦合于实现,这就是门面模式,做一个A接口,做一个B类实现,调用是调A接口,这样将来B类实现要升级修改时
就不用改调用的代码了,直接修改实现类;
8)享元模式:池化思想,bean pool,这样的
9) 如果端口被占用可以netstat -ano | findstr “80"查到80端口的进程;
再用:taskkill /f /pid 1838(进程id)杀死进程
10)导入数据库前运行 set names utf8 以避免导入时就乱码了;
导入数据库:source D:/www/sql/back.sql;
11)package com.cy.pj.brand.service
com.cy域名倒写,pj项目名,brand模块名,service/dao/pojo/controller/是哪一个层;
12)用单例时必须考虑 线程安全+性能,要做到“原子操作”,就是执行中,不能被其他线程打断;
13) 网页400错误,一般是客户端向服务器端传递的请求参数与服务器端的默认设置不匹配,还有参数个数不匹配;或参数格式不匹配;
405,客户端提交的请求的方式如get,与服务端处理的方式如post,不一致导致的
14)在js里 数字非0为true,对象非undefined为true,对字符串非空为true;
15)比较两个数组是否相等可以用:Arrays.toString(a).equals(Arrays.toString(b)) 把两个数组转化字符串来比较
16)使用map充当pojo对象存数据的优劣势
1)优势:代码简单,适合工程量大时间短的外包项目;
2)劣势:可读性差,不知道里面存的是什么,类型不可控:Object可存任何数据,导致有安全风险;
17)CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
java,mysql,js,这些语言上,[]内表示可有可无,{}内表达二者必须选一
18、jQuery中点击事件,调用一个html来填充一个元素
$(“mainContentId”).click(function(){//事件处理函数
//load函数为一个jquery中的ajax函数,其作用是将url对应的资源,异步加载到指定位置
//此处表示在mainContentId对应的对象位置异步加载url指定资源
$(”#mainContentId").load(“menu/menu_list”); })
19、如果一个方法/函数,一屏显示不完,说明写得不合格,应该折开成两到三个方法/函数/
20、能用局部变量,就不用参数,能用参数就不用成员变量,能用成员变量,就不用静态变量;
21、前端和后端团队必须就前端向后端请求的链接和参数达成一致,后端向前端响应的链接和格式达成一致;
22、当你的数据来自于多张表,可以用嵌套查询、联表查询;
23、 js中直接加:debugger 单起一行;可以在客户端打断点
24、Serializable序列化
所有pojo存储数据的对象,都要实现 implements Serializable
把对象转化成字节,就要字节化,把字节转化为对象就要反序列化;
同时还要给此类加序列化码:用idea Serializable 搜索加的方法
加上后,鼠标放类上,会出提示,
26、软件设计图UML(统一建模语言)
包括 1用例图 2类图 3时序图 4协作图 5状态图 6活动图 7组件图 8配置图
27、软件设计六大原则
1.单一职责原则告诉我们实现类要职责单一;
2.里氏替换原则告诉我们不要破坏继承体系,用继承代替修改;
3.依赖倒置原则告诉我们要面向接口编程;
4.接口隔离原则告诉我们要在设计接口时要精简单一;
5.迪米特法则告诉我们要降低耦合;
6.开闭原则告诉我们要对扩展开放,对修改关闭;
设计模式的六大原则,你懂多少呢?
1.六大原则-单一职责原则
原则思想:一个方法只负责一件事情。
描述:单一职责原则很简单,一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序。 这是常识,几乎所有程序员都会遵循这个原则。
优点:降低类和类的耦合,提高可读性,增加可维护性和可拓展性,降低可变性的风险。
2.六大原则-里氏替换原则
原则思想:使用的基类可以在任何地方使用继承的子类,完美的替换基类。
描述:子类可以扩展父类的功能,但不能改变父类原有的功能。子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,子类中可以增加自己特有的方法。
优点:增加程序的健壮性,即使增加了子类,原有的子类还可以继续运行,互不影响。
3.六大原则-依赖倒置原则
原则思想:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象,抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
描述:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
优点:可以减少需求变化带来的工作量,做并行开发更加友好。
4.六大原则-接口隔离原则
原则思想:类和类之间应该建立在最小接口的上。
描述:类A通过接口依赖B,类C通过接口依赖D,如果接口类A和类B不是最小的接口,则依赖的类B和类D必须要实现他们不需要的方法。
优点:提高程序的灵活度,提高内聚,减少对外交互,使得最小的接口做最多的事情。
5.六大原则-迪米特法则
原则思想:一个对象应当对其他对象有尽可能少地了解,简称类间解耦
描述:一个类尽量减少自己对其他对象的依赖,原则是低耦合,高内聚,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。
优点:低耦合,高内聚。
6.六大原则-开放封闭原则
原则思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
描述:一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。
优点:单一原则告诉我们,每个类都有自己负责的职责,里氏替换原则不能破坏继承关系的体系。
第八步:AOP切面方法异步执行
场景:a方法执行完后,执行日志切面对象,此日志记录会很慢,导致整个都很慢,可以
把a方法执行和日志切面分2个线程,分别执行;
在写日志的时存在高并发的情况,
1.在每一个方法中添加日志,代码重复,
2.优化:在切面中为每一个方法添加日志,会影响客户的体验,只有日志执行后才可以返回结果,
3.创建新的线程,电脑中的线程个数是有限的,所以当用户访问量剧增时,电脑会蹦,
4,springmvc底层提供了一个线程池,当所配置的线程池不够时会向用户抛出异常,影响用户体验
1、@EnableAsync //在标有@SpringBootApplication的启动类上,
做异步配置(底层会帮我们创建一个线程池),加此注解
2、@Async //此注解描述的方法为一个异步切入点方法,此方法会运行在异步池中的线程上。
//比如:存日志的方法上就可以加此注解,这样这个日志会被一个新线程拿来单独执行;
3、在application.yml配置文件中,
(task:
executio9n:
pool:
core-size: 16 # 池中线程数没有达到core-size这个值时,每次来一个新的任务都会创建一个新的线程,(两倍的cpu的个数x核数+1)
queue-capacity: 256 # 队列(先进先出)当核心线程数已经达到core-size设置的值,
#又来新的任务,假如所有的核心线程都在忙,则任务要存储到任务队列;
max-size: 256 #当所有核心线程都在忙,并且任务队例也满了,再来新的任务则会创建新的线程
#如果以上都满了,会抛出拒绝处理异常。
keep-alive: 60s #当并发高峰期过后,有些空闲线程可以被释放,
thread-name-prefix: dbpms-thread- #线程的前缀名字;)
在启动类中加@E
@Async//此注解描述的方法为一个异步池切入点方法,此方法会运行在异步池中的线程上
线程池中所需的配置:在yml中进行配置:核心线程数:可以自己设置