SpringBean容器:
bean就是java对象,容器负责java对象整个生命周期的管理,包括创建修改销毁等
IOC控制反转
IOC:是一种思想
IOC容器:IOC思想的实现容器
DI
实现IOc的方法之一,依赖注入,就是再IOC运行期间,动态的将依赖关系注入到对象之中
通过Ioc思想,引入Ioc容器,利用依赖关系注入实现对象之间的解耦.
设计上说明ioc,作用,用来降低代码之间的耦合性,实现方式:DI,DI有两种方式实现
注册Bean,在类上使用,一旦被类扫描到,就生成Bean对象注册到容器中,注解方式
Controller:web请求响应处理,控制器层 完成请求数据校验等操作
Service:业务逻辑的服务层 根据方法参数完成业务逻辑
Repository:数据访问层 关系型数据库的访问操作
Component:一般的组件
方式二
@Bean注释在方法上
更加灵活可以修改属性
方式三
@configuration
用来自定义配置某些资源
方式四:factorybean
工厂的bean对象,
工厂模式生产制定的对象
传统三层架构设计:Controller,Service.DAO
装配bean
@Autowired 自动装配
@Qualifier (“名字”)
@Resourse jdk spring也做了实现spring也做了实现
@Scope(prototype)的作用
Bean对象获取时,都会new一个新对象,singleTon,同一个对象
T通常使用在有状态Bean
Bean的生命周期
1.实例化Bean
2,依赖注入
3.注入 Aware 接口的方法参数对象,如顺序执行BeanNameAwar等e
4.Bean初始化之前,执行beanPostProcessor的before方法
5.Bean对象初始化:执行一些注解和接口方法
6.执行beanPostProcessor的after方法
7.容器关闭时,执行Bean对象的销毁方法
springboot项目准备
1.创建普通的maven项目
2.调整pom.xml文件:引入springBoot的相关依赖
3.src/main/resources
创建public static 存放web静态资源文件
创建application.properties,springboot默认的配置文件
4.随便创建启动类:必须写在某个包下,启动时自动扫描自动类所在包
5.修改debug日志:application.property文件里Debug
6.调整启动不报错
7.直接运行启动类
前段相对路径小坑儿
js是以在的html路径作为相对路径的起点
css是以文件自身的请求路径作为请求的起点
前端代码:Ajax详情解析:
异步请求,不会跳转页面,不会阻塞后续的代码,接收到响应数据执行回调方法:
(1)响应码200,执行success回调函数
(2)4XX,5xx执行失败的回调函数
浏览器提供了原生ajax技术实现
目前都使用jQuery框架,万变不离其宗
$(function(){ //html页面加载执行完毕后执行的方法
$("#login_form").submit(function(){//绑定表单的提交事件,提交后执行后边的方法
//执行ajax提交
$.ajax({
//从这里获得resp
//url:"../data/login.json",
url:"../data/login2.json",
type:"get",//请求方法
//dataType:"",//请求数据类型Content-type
data:$("#login_form").serialize(),//请求数据:使用表单的数据,键等于值
contentType:"json",//响应数据类型:Content-Type
//返回一个json的数据
success:function (resp) {//响应状态为2开头进入success
//返回数据success == true 跳转到maven页面
if (resp.success == true){
//把地址栏url该会指定的值
window.location.href = "main.html";
}else{
alert("错误码: " + resp.code+"\n错误信息: "+resp.message)
//alert(JSON.stringify(resp))//提示框:json对象转为字符串
}
},
error:function (req,textStatus,err) {//4大头响应状态码
alert(req.status)
}
})
//验证alert("submit")
return false;//返回值决定是否是使用表单默认提交
})
})
springMVC
Tomcat和servlet执行流程
资源:
(1)如果是静态资源,tomcat直接返回
(2)如果是servlet
----调用service()
----request:解析http协议的请求数据,封装为request
----response:调用service()还没有反会响应,但已经创建了响应对象
----tomcat根据response对象构造http协议的相应数据
mvc模型’’
RequestMapping
@Controller
public class Test2Controller {
@RequestMapping("/test2")
public String test(){
return "forward:/home.html";
}
}
String返回类型的作用
@controller:web前端控制器
@RequestMapping:类和方法上,value路径,method指定服务的方法,method没有,默认支持所有方法
@ResponseBody:返回值作为相应体
返回类型为String,字符串作为响应体的内容
返回类型为自定义的java类型,Object,序列化为JSON字符串
组合注释
1.RestController:controller + ResponseBody的注解
2.GetMapping:方法上使用了requestmapping+get
3.PostMapping:
Controller中请求映射方法中的参数
(1)@RequestParam:除了application/json,其他的都可以通过key获取value,默认是必填的,非必填指定
required=false
可以接受form-data,包括文件
@RequestMapping("login")
public Object login(@RequestParam String username,@RequestParam String password){
log.debug("获取到路径请求参数 username "+ username + "password" + password);
Map<String,Object> map = new HashMap<>();
map.put("ok",true);
return map;
}
@RequestMapping("/register")
public Object register(@RequestParam String username, @RequestParam String password, @RequestParam MultipartFile file) throws IOException {
log.debug("获取请求路径参数 : username={}, password={}",username,password);
log.debug("头像信息,名称={},内容={}",file.getOriginalFilename(),new String(file.getBytes()));
Map<String,Object> map = new HashMap<>();
map.put("ok",true);
return map;
}
public Object holiday(@PathVariable String day){
log.debug("获取到路径请求参数" + day);
Map<String,Object> map = new HashMap<>();
map.put("ok",true);
return map;
}
(2)POJO对象,和使用@RequestParam作用一样.
使用哪种方式获取请求数据:
(1)相对来说,pojo比@RequestParam更方便书写
(2)请求数据比较少,一般是不使用json,如果没有自定义类型对应,可以直接使用包装类型
(3)请求数据比较多,可能设计为请求数据类型可能设计为json,表单默认格式,formdata(pojo)
@RequestBody用json
RequstPart
二进制文件,默认required=true
@PostMapping("/file")
public Object file(@RequestPart MultipartFile file) throws IOException {
log.debug("头像信息,名称={},内容={}",file.getOriginalFilename(),new String(file.getBytes()));
Map<String,Object> map = new HashMap<>();
map.put("ok",true);
return map;
}
SevletApi
和原生servlet差不多,直接作为方法参数,springmvc会传入Servlet对象
一般是:reponse返回二进制,自定义输出,如二进制文件,session的处理
Springmvc请求和响应,后端使用
(1)返回试图,不带responsebody注解,返回类型为String
(2)返回json数据:方法上带Responsebody注解(类上也是作用在方法上,返回类型为Object)
(3)请求数据类型为json@RequestBody,如果报错,请求数据有某个key,检查访问数据格式,反序列化的java对象中没有该属性,报错反序列化的java对象中有某个属性,请求json数据没有该key,不会报错,java中的属性就是默认值;不建议使用基本数据类型
(4)请求数据类型不是json,或是get请求:
使用pojo,其他的能看懂
注意事项:如果发现pojo对象的属性为null,一定是请求没有这个key
(5)Servlet相关Api:session相关
Url变量绑定:
@pathVariable
HandlerInterceptor拦截器:作用和filter类似,由mvc提供
前后端交互注意事项
前段的调试手段:开发者工具
后端Debug
出现什么问题找前一个流程的逻辑
用户统一的会话管理实现:HandlerIntertceptor
服务端中要考虑的统一管理服务资源
开放的权限 登录后访问的权限(登录前不允许访问)
文章列表,新增修改html加入黑名单,禁止访问
(1)前端资源
(2)后端资源 用户登录注册(/user/**)
黑白名单:List<白名单>,List<黑名单>做一个业务,白名单允许操作,黑名单不允许操作
添加拦截器使用黑白名单的思想,白名单设置要拦截资源的路径,黑名单要排除要拦截的资源路径
实际配置资源路径,会考虑:配置最小集 + 灵活扩展(可维护性)
SpringMVC
HandlerInterceptor,Filter
(1)可以同时匹配多个
(2)方法返回boolean类型,表示是否调用链是否往下执行
(3)如果需要往下执行,return true
(4)如果不能继续访问,return false;if(条件)返回响应给客户端 return false;
ControllerAdvice
1.当前类为一个Controler的统一切面类
2.可以定义统一处理的方法
@controllerAdvice
public class XXX{
@ExceptionHandler(异常.class)
public 返回类型 方法名(传入参数)
}
@ExceptionHandler(AppException.class)
public Object handle1(AppException e){
JSONResponse json = new JSONResponse();
//自定义异常,保存我们的错误码和错误消息
json.setCode(e.getCode());
json.setMessage(e.getMessage());
log.debug(transfer(e));
//非自定义异常(英文的错误消息,堆栈信息,不能给用户看):
// 指定一个错误码,错误消息(未知错误,请联系管理员)
return json;
}
@ExceptionHandler(Exception.class)
public Object handle2(Exception e){
JSONResponse json = new JSONResponse();
//自定义异常,保存我们的错误码和错误消息
json.setCode("ERR000");
json.setMessage("未知错误,请联系管理员");
log.debug(transfer(e));
//非自定义异常(英文的错误消息,堆栈信息,不能给用户看):
// 指定一个错误码,错误消息(未知错误,请联系管理员)
return json;
}
异常需要掌握的
(1)出现异常时,代码执行的顺序(se)
(2)如何抛异常?手动抛异常(自定义异常),代码出bug抛异常
(3)如何处理异常?框架封装了我们的trycatch,需要使用统一异常处理的方案
(4)如何根据异常找到出现异常的代码行,调试代码
根据堆栈信息,查找哪个类,哪个方法抛出的,分析try catch,分析我们的代码行,具体是哪一行代码
(3)ControllerAdvice
类实现ResponseBodyAdvice接口,根据条件重写响应体内容
重写support: 返回boolean,表示执行Controller请求映射方法后,是否重写beforeBodyWrite:怎么样重写,使用response对象来重写
AOP
概念:面向切面编程,属于一种编程思想
(1)继承方式
静态代理设计模式:
继承原始类/被代理类,子类就是代理类
重写父类方法,方法中调用父类方法前后加入增强业务
(2)接口的方式
原始类.被代理类,代理类实现同一个接口
使用被代理对象来创建创建代理类-------public 代理类(被代理类){}
代理类接口方法重写,是调用被代理类对象的方法前后加入增强业务
静态织入技术
织入时机:编译期,类加载期
代表技术ASpectJ
动态代理
AOP
(1)静态代理设计模式
(2)静态织入
(3)动态代理
java说AOp,基本指SpringAop,基本上指动态代理
运行期间动态的织入
动态代理的实现方式:
Jdk实现:
要求被代理实现接口
通过JDK的InvocationHandler,Proxy.newP roxyInstance,来生成代理类
Spring提供aspectJ的注解语法支持,本质上还是给予jdk和CGLIB来实现-----方法增强
@Aspect//进行切面编程
@Component
//连接点
public class TestAOP {
//切面地点
//包下任意类.任意方法,任意参数
@Pointcut("execution(* com.example.mvclearn.controller.*.*(..))")
public void loginPointcut(){
}
}
AOP术语
@Aspect//进行切面编程,定义切面
@Component
//连接点
public class TestAOP {
//切面地点,定义切点
//包下任意类.任意方法,任意参数
@Pointcut("execution(* com.example.mvclearn.controller.*.*(..))")
public void loginPointcut(){
}
//通知,前置通知,传入切点方法名()
@Before("loginPointcut()")
public void beforeRequest(){
System.out.println("前置通知");
}
@After("loginPointcut()")
public void afterRequest(){
System.out.println("请求映射方法执行完毕");
}
@AfterReturning
public void afterRequestReturn(){
System.out.println("请求映射方法返回");
}
@AfterThrowing("loginPointcut()")
public void afterRequestThrow(){
System.out.println("请求映射方法抛出异常");
}
//环绕通知
}
对AOP的理解
(3)Spring动态代理
织入时机:运行期(启动就生成)
实现方式:
1.JDK:基于接口实现(被代理类实现接口,接口方法需要增强)
API:InvocationHandler,Proxy
原理:运行时,动态生成字节码代理类,进行类加载,容器中都是代理类对象
2.CGLIB:基于继承实现(被代理类可以不实现接口)
原理:cglib框架生成代理类,cglib又基于asm字节码来生成增强代码
Mybatis
建议xml配置方法
jdbc操作回顾
ORM框架:
Mybatis
实体类
配置mapper接口
Mybatis执行流程
Dao层单元测试方法
为什么可以使用UserMapper接口呢
@mapper注解
…
Mybatis框架基于扫描容器中mapper注解的接口,使用Aop动态的生成代理类
调用Mapper实现类的方法
练习略…
Mybatis关系映射
一对一映射
property 属性:指定 Article 中对应的属性,即用户。
resultMap 属性:指定关联的结果集映射,将基于该映射配置来组织用户数据。
columnPrefix 属性:绑定一对一对象时,是通过 columnPrefix+association.resultMap.column
来映射结果集字段。association.resultMap.column是指 标签中 resultMap属性,
对应的结果集映射中,column字段。
多对多映射
```xml
<resultMap>结果集映射中,一对多关系需要使用<collection>标签,使用方法和<association>类似。
以下为 UserMapper.xml 的结果集映射配置:
以下是单元测试代码:
<select id="selectAll" resultMap="BaseResultMap">
select
a.id a_id,
a.title a_title,
a.content a_content,
a.view_count a_view_count,
a.user_id a_user_id,
a.create_time a_create_time,
u.id,
u.username,
u.password,
u.nickname,
u.sex,
u.birthday,
u.head,
u.create_time
from article a
join user u
on u.id=a.user_id
</select>
<!-- 定义结果集映射关系:绑定结果集字段和转换的java对象之间的关系 -->
<resultMap id="BaseResultMap" type="org.example.model.User">
<!-- 结果集字段和java对象属性的映射 -->
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="nickname" property="nickname" />
<result column="sex" property="sex" />
<result column="birthday" property="birthday" />
<result column="head" property="head" />
<result column="create_time" property="createTime" />
<collection property="articles"
columnPrefix="a_"
resultMap="org.example.mapper.ArticleMapper.BaseResultMap" />
</resultMap>
```java
package org.example.mapper;
import org.example.model.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
//指定为Spring环境中的单元测试
@RunWith(SpringRunner.class)
//指定为SpringBoot环境的单元测试,Application为启动类
@SpringBootTest(classes = Application.class)
//使用事务,在SpringBoot的单元测试中会自动回滚
@Transactional
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void selectAll(){
List<User> users = userMapper.selectAll();
users.stream()
.forEach(System.out::println);
}
}
动态sql语句
标签
<insert id="insert" parameterType="org.example.model.User"
useGeneratedKeys="true" keyProperty="id">
insert into user(
username,
password,
nickname,
<if test="sex != null">
sex,
</if>
birthday,
head
) values (
#{username},
#{password},
#{nickname},
<if test="sex != null">
#{sex},
</if>
#{birthday},
#{head}
注意 test 中的 sex,是传入对象中的属性,不是数据库字段。
标签
之前的插入用户功能,只是有一个 sex 字段可能是选填项,如果有多个字段,一般考虑使用标签
结合标签,对多个字段都采取动态生成的方式。
标签中有如下属性:
prefix:表示整个语句块,以prefix的值作为前缀
suffix:表示整个语句块,以suffix的值作为后缀
prefixOverrides:表示整个语句块要去除掉的前缀
suffixOverrides:表示整个语句块要去除掉的后缀
<insert id="insert" parameterType="org.example.model.User"
useGeneratedKeys="true" keyProperty="id">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">
username,
</if>
<if test="password != null">
password,
</if>
<if test="nickname != null">
nickname,
</if>
<if test="sex != null">
sex,
</if>
<if test="birthday != null">
birthday,
</if>
<if test="head != null">
head,
</if>
<if test="createTime != null">
create_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="username != null">
#{username},
</if>
<if test="password != null">
#{password},
</if>
<if test="nickname != null">
#{nickname},
</if>
<if test="sex != null">
#{sex},
</if>
<if test="birthday != null">
#{birthday},
</if>
<if test="head != null">
#{head},
</if>
<if test="createTime != null">
#{createTime},
</if>
</trim>
</insert>
在以上 sql 动态解析时,会将第一个 部分做如下处理:
基于 prefix 配置,开始部分加上 (
基于 suffix 配置,结束部分加上 )
多个 组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于
suffixOverrides 配置去掉最后一个 ,
注意 <if test=“createTime != null”> 中的 createTime 是传入对象的属性
以上标签也可以使用 替换。
单元测试代码如下:
根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容。
UserMapper 接口中修改用户方法:根据传入的用户 id 属性,修改其他不为 null 的属性:
UserMapper.xml 中添加更新用户 sql:
<select id="selectByCondition" parameterType="org.example.model.User"
resultMap="BaseResultMap">
select id, username, password, nickname, sex, birthday, head, create_time
from user
<where>
<if test="username != null">
and username=#{username}
</if>
<if test="password != null">
and password=#{password}
</if>
<if test="nickname != null">
and nickname=#{nickname}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
<if test="birthday != null">
and birthday=#{birthday}
</if>
<if test="head != null">
and head=#{head}
</if>
<if test="createTime != null">
and create_time=#{createTime}
</if>
</where>
</select>
@Test
public void selectByCondition(){
User user = new User();
user.setUsername("e");
user.setPassword("5");
List<User> users = userMapper.selectByCondition(user);
System.out.println(users);
}
@Test
public void selectByCondition2(){
User user = new User();
user.setSex(false);
List<User> users = userMapper.selectByCondition(user);
System.out.println(users);
}
标签
根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容。
UserMapper 接口中修改用户方法:根据传入的用户 id 属性,修改其他不为 null 的属性:
UserMapper.xml 中添加更新用户 sql:
<update id="updateById" parameterType="org.example.model.User">
update user
<set>
<if test="username != null">
username=#{username},
</if>
<if test="password != null">
password=#{password},
</if>
<if test="nickname != null">
nickname=#{nickname},
</if>
<if test="sex != null">
sex=#{sex},
</if>
<if test="birthday != null">
birthday=#{birthday},
</if>
<if test="head != null">
head=#{head},
</if>
<if test="createTime != null">
create_time=#{createTime},
</if>
</set>
where id=#{id}
</update>
标签
对集合进行遍历时可以使用该标签。标签有如下属性:
collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
item:遍历时的每一个对象
open:语句块开头的字符串
close:语句块结束的字符串
separator:每次遍历之间间隔的字符串
示例:根据多个文章 id 来删除文章数据。
<delete id="deleteByIds">
delete from article
where id in
//变量:item
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
<insert id="batchIn">
insert into user(
id,
username,
password,
nickname,
sex,
birthday,
head
) values
<foreach collection="list" item="item" separator=",">
(
#{item.id},
#{item.username},
#{item.password},
#{item.nickname},
#{item.sex},
#{item.birthday},
#{item.head}
)
</foreach>
</insert>
mybatis生成工具…
略