导图社区 spring
spring的 基本功能,包含有 AOP、 AOP具体应用、 整合第三方bean等。
编辑于2024-04-19 19:41:29spring
使用流程
创建bean
aop通过切点创建
第三方bean通过方法创建
注入
第三方bean引用和形参传入自己手动setter注入
aop不用注入
使用
getbean获取
注解开发(基础注解)
配置
配置类SpringContext
表明配置类
@Configuration
指明扫描bean路径(包路径)
@ComponentScan("com.demo05")
PropertySource文件扫描
@PropertySource("e.properties")
xml表明注解扫描路径
<context:component-scan base-package="com.demo02"/>
Bean实例化加注解
Component组件
Repository数据库
Service服务层
Controller表现层
注入
简单注入value注解
字符串
${key}引入properties文件内容
引用类型自动注入autowired注解
Autowired按照bean类对象自动注入
qualifier有相同bean类型指明文件名
注意注解注入是暴力反射注入,不是setter注入
生命周期注解加在方法上
PostConstract
PreDestory
基本功能
IOC
概念(Inversion of Control)
控制反转作用
目的解耦合,对象不是由自己创建,变成spring创建,后续若有新功能,新对象不是由自己创建,仅仅从spring调用即可,省去了新对象的测试打包环节
创建spring对象(容器)
顶层接口创建
BeanFactory
顶级接口,创建对象是延迟加载,即用的时候才创建对象,最开始时候使用的对象
ApplicationContext创建spring对象是及时加载,但是若类型是多例模式,也是延迟加载,只有在用的时候才会创建一个新的对象
代码
Resource resources = new ClassPathResource("applicationContext.xml"); BeanFactory bf = new XmlBeanFactory(resources); BookDao bookDao = bf.getBean("bookDao", BookDao.class); bookDao.save();
多态形式用beanFactory接收
核心接口创建
如果是单例模式初始化时bean立即加载,多例模式使用一次创建一次
提供名字字符串
创建applicationContext.xml配置文件
指明注解扫描路径
<context:component-scan base-package="com.demo02"/>
new ClassPathXmlApplicationContext("applicationContext.xml");
提供配置类对象
创建SpringContext的类
表明配置类
@Configuration
指明扫描bean注解路径(包路径)
@ComponentScan("com.demo05")
PropertySource文件扫描
@PropertySource("e.properties")
new AnnotationConfigApplicationContext(SpringConfig03.class);
提供绝对路径
FileSystemXmlApplicationContext
bean实例化(创建bean)
不能在一个bean标签创建两个对象,如果是静态工厂,constructor-arg传进去的是生产方法的参数, 如果是动态工厂要先创建动态工厂对象,然后再使用生产方法,factory-method是实际这个标签创建对象的属性
xml配置bean实例化
构造方法实例化
无参构造实例化
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Object project = classPathXmlApplicationContext.getBean("project"); System.out.println(project);
有参构造实例化
配置文件
<bean id="project" class="factory"> <constructor-arg name="name" value="老王工厂"/> <constructor-arg name="age" value="122"/> </bean>
main
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Object project = classPathXmlApplicationContext.getBean("project"); System.out.println(project);
静态工厂实例化
main方法
//静态工厂实例化对象 ClassPathXmlApplicationContext classPathXmlApplicationContext1 = new ClassPathXmlApplicationContext("applicationContext.xml"); Object dog = classPathXmlApplicationContext1.getBean("dog"); System.out.println(dog);
配置文件
<bean id="dog" class="staticFactory" factory-method="getDog"/>
实例工厂实例化
main方法
//动态工厂实例化对象 ClassPathXmlApplicationContext classPathXmlApplicationContext2 = new ClassPathXmlApplicationContext("applicationContext.xml"); Object dongwu = classPathXmlApplicationContext2.getBean("dongwu"); System.out.println(dongwu);
applicatContext
<!--动态工厂实例化--> <bean id="factory" class="dynamicFactory"/> <bean id="dongwu" factory-bean="factory" factory-method="getDog"/>
FactoryBean实例化
main方法
//FactoryBean实例化对象 ClassPathXmlApplicationContext classPathXmlApplicationContext3 = new ClassPathXmlApplicationContext("applicationContext.xml"); Object model = classPathXmlApplicationContext3.getBean("model"); Object model1 = classPathXmlApplicationContext3.getBean("model"); System.out.println(model); System.out.println(model1);
applicationContext
<!--FactoryBean实例化对象--> <bean id="model" class="FactoryModel"/>
FactoryBean类
public class FactoryModel implements FactoryBean { @Override public Object getObject() throws Exception { //返回的对象 return new Dog("批量模式1号狗",10); } @Override public Class<?> getObjectType() { //返回对象的类型 return Dog.class; } @Override public boolean isSingleton() { //返回true单例模式,返回false多例模式 return false; } }
注解bean实例化
加注解
Component组件
Repository数据库
Service服务层
Controller表现层
包扫描
xml加标签扫描
<context:component-scan base-package="com.itheima.annotion"/>
配置类加注解扫描
@ComponentScan("com.itheima.annotion")
此注解只能添加一次,多个数据请用数组格式
bean的id
如果@Component注解没有使用参数指定Bean的名称,那么类名首字母小写就是Bean在IOC容器中的默认名称
管理bean
生命周期
配置方式(原来的类里加方法)
xml属性配置
init-method
destory-method
实现接口配置
InitializingBean
DisposableBean
注解配置
PostConstract
PreDestory
@PostConstruct和@PreDestroy注解是jdk中提供的注解,从jdk9开始,jdk中的javax.annotation包被移除了
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
具体重写方法
afterPropertiesSet
在成员变量注入后执行此方法
destroy
在bean销毁即spring容器关闭执行方法
关闭spring
直接关闭
close()
钩子关闭
registerShutdownHook();
用来在非web应用中关闭IoC容器的...如果你正在一个非web应用的环境下使用Spring的IoC容器,如dubbo服务,你想让容器优雅的关闭,并调用singleton的bean相应destory回调方法,你需要在JVM里注册一个“关闭钩子”(shutdown hook)
生命周期受到scope的影响,如果是多例模式PreDestory不会执行,因为多例的管理已经交给jvm管理,spring不会管理bean,销毁前spring不知道
bean创造模式scope(bean标签里的属性)
单例模式singleton
多例模式prototype
注意一旦设置多例模式,init将会每调用一次执行一次,destory方法将不会执行,spring每使用一次对象会创建一次对象,管理权限将会交给jvm
获取bean
getBean(id名或name名)需要结果进行强转
getBean(id名,类对象)
生成类对象类型,无需强转
getBean(类对象)
不能有相同类对象的实例
如果IOC容器中同类型的Bean对象有多个,此处获取会报错
xml模板
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"> <!--数组注入--> <property name="array"> <array> <value>100</value> <value>200</value> <value>300</value> </array> </property> <!--list集合注入--> <property name="list"> <list> <value>itcast</value> <value>itheima</value> <value>boxuegu</value> <value>chuanzhihui</value> </list> </property> <!--set集合注入--> <property name="set"> <set> <value>itcast</value> <value>itheima</value> <value>boxuegu</value> <value>boxuegu</value> </set> </property> <!--map集合注入--> <property name="map"> <map> <entry key="country" value="china"/> <entry key="province" value="henan"/> <entry key="city" value="kaifeng"/> </map> </property> <!--Properties注入--> <property name="properties"> <props> <prop key="country">china</prop> <prop key="province">henan</prop> <prop key="city">kaifeng</prop> </props> </property> </bean> </beans>
DI
概念
依赖注入
对属性默认值赋值,spring容器类似map容器
找spring容器里的对象可以根据class和id获取,class若出现两个会造成无法区分
加载配置文件用来注入成员变量简单类型
xml开发
开启命名空间
开启context约束文件,idea自动生成
先生成命名空间
xmlns:context="http://www.springframework.org/schema/context"
再指明约束文件地址
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
加载主标签
context:property-placeholder
加载文件属性
Loaction
加载内容Location内容
不加载系统标签
system-properties-mode="NEVER"
加载多个标签
location里的文件名按照逗号隔开
加载所有标签
*.properties
标准加载格式
classpath:*.properties
加载jar包中的文件格式classpath后面加*
classpath*:*.properties
禁止加载系统文件
system-properties-mode="NEVER"
注解开发
配置类中加注解,不能使用通配符
@PropertySource("e.properties")
@PropertySource()中加载多文件请使用数组格式配置,不允许使用通配符*==
注入(对成员变量赋值)
xml方式注入
注入内容
注入普通类型
<bean id="dog" class="com.itheima.dog"> <property name="name" value="小花"/> <property name="cat" ref="arr"/> </bean>
用value标签
用el表达式注入propert文件内容
注入引用类型
<bean id="dog" class="com.itheima.dog"> <property name="name" value="小花"/> <property name="cat" ref="arr"/> </bean>
用ref标签赋值成对象名字
注入集合
数组
<property name="array"> <array> <value>100</value> <value>200</value> <value>300</value> </array> </property>
list
<property name="list"> <list> <value>list100</value> <value>list200</value> </list> </property>
set
<property name="set"> <set> <value>set100</value> <value>set200</value> </set> </property>
map
<property name="map"> <map> <entry key="name" value="xiaoming"/> <entry key="age" value="12"/> </map> </property>
properties
<property name="properties"> <props> <prop key="name">小明</prop> <prop key="age">12</prop> </props> </property>
注入方式
setter注入
是通过set方法注入,不会通过反射自己注入,让自己决定如何注入,propety标签就是调用set方法注入
main
//setter注入成员变量 ClassPathXmlApplicationContext classPathXmlApplicationContext4 = new ClassPathXmlApplicationContext("applicationContext.xml"); Object xiaohau = classPathXmlApplicationContext4.getBean("xiaohau"); System.out.println(xiaohau);
applicationContext
<!--setter注入--> <bean id="xiaohau" class="Cat"> <property name="name" value="小花"/> <property name="age" value="12"/> </bean>
构造器注入
main
//构造器注入 ClassPathXmlApplicationContext classPathXmlApplicationContext5 = new ClassPathXmlApplicationContext("applicationContext.xml"); Object shushu = classPathXmlApplicationContext5.getBean("shushu"); System.out.println(shushu);
applicationContext
<!--构造器注入--> <bean id="shushu" class="mouse"> <constructor-arg type="java.lang.String" value="蜀黍我被抓了"/> <constructor-arg type="java.lang.Integer" value="10"/> </bean>
配置文件如何识别参数
name
参数名称耦合度高
type
参数类型,不能分辨相同类型
index
第几个参数
自动注入(一般在注解用)
<bean class="service.impl.UserServiceImpl" autowire="byType"/>
main
//自动注入 ClassPathXmlApplicationContext classPathXmlApplicationContext6 = new ClassPathXmlApplicationContext("applicationContext.xml"); Object rabbit = classPathXmlApplicationContext6.getBean("rabbit"); System.out.println(rabbit);
applicationContext
<bean class="Food"> <property name="name" value="胡萝卜"/> </bean> <bean id="rabbit" class="rabbit" autowire="byType"> <property name="name" value="小白红烧肉"/> </bean>
注解注入
简单注入value注解
字符串
${key}引入properties文件内容
引用类型自动注入autowired注解
Autowired按照bean类对象自动注入
qualifier有相同bean类型指明文件名
注意注解注入是暴力反射注入,不是setter注入
AOP
原理动态代理
CatImpl cat = new CatImpl(); animal result = (animal) Proxy.newProxyInstance(demo01.class.getClassLoader(), cat.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { method.invoke(cat, args); String name = method.getName(); System.out.println(name); System.out.println("完工"); return null; } }); result.eat(); }
AOP概念
AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
作用:在不惊动原始设计的基础上为其进行功能增强。简单的说就是在不改变方法源代码的基础上对方法进行功能增强。
Spring理念:无入侵式/无侵入式
相关概念
源对象(目标对象)Target
要被增强方法的目标对象
代理对象Proxy
对功能增强后的对象
连接点joinpoint
可能被增强的方法
指的是接口的方法,不是实现类的方法
一是因为用实现类方法形成的代理对象耦合度过高,为了解耦合
二是实现类有接口中没有的方法,这样不能形成代理
切入点pointcut
一定被增强的方法
切入点和切面仅有调用关系,仅仅是切面调用了切入点的方法引用,其他没有任何关系
切入点表达式
语法格式
动作关键字
execution
表示动作执行到指定切入点
访问修饰符
可以被省略如果是public
返回值
可以用*表示任意返回值类型
包名
接口名
方法名
参数
访问修饰符
异常名可以省略
通配符
*单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution(public * com.itheima..UserService.find(*))
.. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution(public User com..UserService.findById(..))
+:专用于匹配子类类型
execution(* *..Service+.(..))
技巧
所有代码按照标准规范开发,否则以下技巧全部失效 描述切入点通==常描述接口==,而不描述实现类 访问控制修饰符针对接口开发均采用public描述(==可省略访问控制修饰符描述==) 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述 ==包名==书写==尽量不使用..匹配==,效率过低,常用*做单个包描述匹配,或精准匹配 ==接口名/类名==书写名称与模块相关的==采用*匹配==,例如UserService书写成*Service,绑定业务层接口名 ==方法名==书写以==动词==进行==精准匹配==,名词采用匹配,例如getById书写成getBy,selectAll书写成selectAll 参数规则较为复杂,根据业务方法灵活调整 通常==不使用异常==作为==匹配==规则
如果指明的是实体类的方法aop会找到这个实体类的接口,创建这个接口的代理对象,实体类的方法和接口方法的方法对象相同,可以找到接口中要增强的方法
通知advice
具体增强的逻辑(指方法)
通知类型
图解
前置通知Before
获取参数
加形参JoinPoint,自动将参数传进里面
注意获取参数的形参要放在形参中第一位
#这里改变参数不会影响源对象执行时的参数,仅仅做获取用,仅会影响后续也用joinpoint获取参数的方法因为是数组joinpoint将形参获取后存进数组,这里改变的仅仅是数组地址存的内容
后置通知AfterReturning
获取参数
加形参JoinPoint,自动将参数传进里面
@Before("chongzhi()") public void beff(JoinPoint joinPoint){ joinPoint.getArgs()[0]=2000; System.out.println("这是前置通知修改成了" + joinPoint.getArgs()[0]); }
注意获取参数的形参要放在形参中第一位
获取返回值
在AfterReturning注解中加参数returning=参数名,然后在方法中加入同名的形参,aop自动将返回值传到同名的形参中
@AfterReturning( value = "chongzhi()",returning="aa") public void returns(JoinPoint joinPoint,Integer aa){ System.out.println("这是后置通知传进的参数" + joinPoint.getArgs()[0]); System.out.println("这是后置通知返回值"+aa); }
这里无法对源对象的返回值做修改,通过注解加returning仅仅是将返回值传进来,与原返回值无关
异常通知AfterThrowing
出异常才会执行,可以获取异常对象,与后置通知互斥
最终通知After
最后一定执行
环绕通知Around
可以对实际返回值做修改也可以对参数做修改,可以在前置后置异常最终内容做修改类似InvocationHandler()
代码
`@Around("chongzhi()") public Object show(ProceedingJoinPoint pjp){ Object proceed=null; try { System.*out*.println("这是环绕通知之前的"+pjp.getArgs()[0]); Object[] args = pjp.getArgs(); *// args[0]=1000; *proceed = pjp.proceed(args); System.*out*.println("这是环绕通知之后的"+args[0]); } catch (Throwable e) { throw new RuntimeException(e); } return proceed; }`
获取源对象要被增的方法加形参ProceedingJoinPoint调用proceed()方法
或取参数调用形参的getArgs()方法,如果没有改变原有参数,可以不获取参数传到proceed()参数中,aop会自动传入
通知类
含有通知的类,需要被实例化由动态代理生成时调用实例对象的方法
切面Aspect
切点+通知
工作流程
Spring容器启动
初始化spring依据配置类
依据扫描路径进行创建bean
读取所有切面配置中的切入点
读取切面配置的切入点,切面没有配置的切入点不会读取
初始化bean,判定bean对应的类中的方法师傅匹配到任意切入点
能匹配上创建源对象和代理对象,获取只能获取代理对象,不能获取源对象
匹配接口中是否存在这个切入点表达式描述的方法,匹配有没有这个接口的实现类,不存在不会创建代理对象
相当于坐标没有坐标就不知道到底要创建谁的代理对象,增强的是哪个方法
不能匹配,只创建源对象
在main方法中获取bean执行方法(执行源对象方法和增强方法)
获取的bean是原始对象时,调用方法并执行,完成操作
获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
注解开发步骤
导包
context中含有AOP相关的包不用单独导入
导入切点表达式,切面注解坐标
org.aspectj aspectjweaver 1.9.4
导入springconfig类
加入configration注解表示spring配置类
加入@ComponentScan("com.create")注解扫描需要被实例化的bean
开启AOP
@EnableAspectJAutoProxy
需要手动开始否则即使读取到切面标签也不会开启aop,和configration不同,configration读取到就能用,aop不开启功能读取到不会启用
创建通知类
类注解
加实例化标签
加aspect注解表明含有切面,让componentScan扫描时注意到这个类
类内容
内部创建切入点方法,加切点表达式指明要增强是哪个类的哪个方法
创建通知方法,方法上加通知类型注解,注解传入value="切点赋值方法()"
后置通知要想获取返回值要在注解改变属性值returning,然后将同名的形参加到方法里
要想获取参数仅仅是在形参中加对象参数JoinPoint,
代码
@Component @Aspect public class MyAdvice { @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))") private void pt(){} //JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数 // @Before("pt()") public void before(JoinPoint jp) { Object[] args = jp.getArgs(); System.out.println(Arrays.toString(args)); System.out.println("before advice ..." ); } // @After("pt()") public void after(JoinPoint jp) { Object[] args = jp.getArgs(); System.out.println(Arrays.toString(args)); System.out.println("after advice ..."); } //ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用 // @Around("pt()") public Object around(ProceedingJoinPoint pjp) { Object[] args = pjp.getArgs(); System.out.println(Arrays.toString(args)); args[0] = 666; Object ret = null; try { ret = pjp.proceed(args); } catch (Throwable t) { t.printStackTrace(); } return ret; } //设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同 @AfterReturning(value = "pt()",returning = "ret") public void afterReturning(JoinPoint jp,String ret) { System.out.println("afterReturning advice ..."+ret); } //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同 @AfterThrowing(value = "pt()",throwing = "t") public void afterThrowing(Throwable t) { System.out.println("afterThrowing advice ..."+t); } }
xml开发步骤
声明TargetBean创建源对象
声明AdviceBean创建通知类对象,方便动态代理调用方法
创建aop配置空间
创建切点标签,指明要被增强的接口的方法,切面要调用直接通过id调用,不用通过方法名调用(注解)
创建切面标签指定Advice类,通知都是用的这个类里的通知
绑定切点和通知,切点用切点的id调用,通知直接用绑定的通知类里的方法调用
具体代码
<?*xml version="1.0" encoding="UTF-8"*?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd "> <bean id="aops" class="com.create.aops"/> <bean id="yidong" class="com.create.yidong"/> <aop:config> <aop:pointcut id="chonghuafei" expression="execution(* com.create.yunyingshang.chonghuafei(..))"/> <aop:aspect ref="aops"> <aop:around method="chonghua" pointcut-ref="chonghuafei"/> </aop:aspect> </aop:config> </beans>
切换代理模式
默认是jdk动态代理模式,是创建了接口的实现类对象,只能通过接口获取代理对象,不能通过源对象获取代理对象,之间没有关系
CDLIB代理模式是创建了源对象的子类对象,可以通过源对象获取代理对象
注解
在配置类上开启aop注解里加参数proxyTargetclass=true
xml
<aop:config proxyTarget-class=true></aop:config>
易错误区
代理对象实际是创建的接口的实体类,要增强的方法也是针对的是接口方法,只不过是分别调用了通知对象的通知方法和实体类的方法
AOP具体应用
声明式事务
开启一个单独的功能需要导包,事务功能包集成在
原理
通过aop在方法内容前后加上开启事务和提交事务,形成一个总的事务,如果是设置单独一个dao开启单独事务,先判断原方法有没有事务,设置单独事务:(通过事务对象PlatformTransactionManager里的一个方法getTransaction传进去TransactionDefinition传出一个对象记录了原方法中是否有事务TransactionStatus)设置有事务是加入REQUIRES,设置有事务单独创建一个事务requires和,默认的区别是默认的原方法没有事务,会创建一个事务,requires没有事务不会创建事务
PlatformTransactionManager的功能
在一个service层定义了一个方法加上事务注解,这个方法再别的service类中被调用,可以设置被调用时事务是加入到事务管理员中,还是自己单独开一个事务,还是自己不开事务,通过注解对象的成员变量propagation设置
默认只会滚error和运行异常报错,不会滚系统异常(IO)通过rollbackfor可以设置
看官方文档关于xml设置事务
区别是事务要指定开始事务的方法,还要指定aop要增强的方法
官方网站
spring-jdbc包含了声明式事务的jar包
是通过代理模式进行增强,调用了datasorce添加了开启事务和提交事务,如果没有实现接口就不能用jvm的动态代理,会自动采用另一种代理模式,创建子类的模式
注解步骤
配置类上开启声明式事务,否则不会开启功能
@EnableTransactionManagement
在配置类中创建一个事务管理者,在其中传进datasocoure, 里面有对应的声明,能够在源对象方法中加前置增强开启事务,和关闭事务
代码
//配置事务管理器,mybatis使用的是jdbc事务 @Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager dtm = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; }
在需要增强的方法上或者类上(指的是实体类)加事务注解,可以在注解中个性化设置,设置什么时候回滚(默认error和运行异常回滚),设置事务传播行为,默认加入主事务中
设置
注解
@Transactional
RuntimeException类型异常或者Error错误,Spring事务能够进行回滚操作。但是对于编译器异常,Spring事务是不进行回滚的,所以需要使用rollbackFor来设置要回滚的异常。
声明式事务是针对的service层
异常处理(实际是用封装的对象操作)
创建异常类
创建自己的异常类,继承RuntimeException类,这样在后端中可以对不符合自己要求的参数/可能出现的问题进行抛出自己设定好的异常,可以针对这些异常进行个性化处理
开始异常处理的aop
原理
aop中需要切点和声明组成切面,才能对其进行增强,整体流程springmvc已经做出了优化,可以新建一个类加ControllerAdvice注解,表示这个类的方法是针对controller注解类的所有方法做的声明(切点是所有Controller的所有方法,通知是这个类的所有方法),方法加注解ExceptionHandler表示是对异常处理做的声明,注解参数可以按照严重程度依次加深,最后加一个execution注解,包含所有异常(前面异常都不匹配才走这里),
ControllerAdvice这一个注解就表示了切面
具体代码
注意
因为后端所有的运行都是开始于Controller跑起来的,所以只要有异常,都会抛到控制层,控制层如果没有处理,会抛给调用它的tomcat处理,tomcat无法处理,抛给调用它的jvm处理,jvm直接报错停止运行,如果有在控制层通过aop加上异常捕获,会执行到aop捕获的声明中处理,所以异常处理要写在控制层
异常处理Controlleradvice无需加开启功能开关,声明式事务需要加开启开关,事务spirng功能多,需要开启功能
整合第三方bean
管理第三方bean(第三方bean无法直接加注解)
创建
配置第三方bean的方法要放在配置类里
因为第三方bean和自己创建的bean不同,自己创建的bean可以直接在类里面加注解,第三方不能直接加注解,只能自己创建,自己通过第三方bean的set方法进行注入,相当于创建工作和注入工作spring做不了,自己给做了,spring只做管理(xml中是自己注入的,是调用了setter方法注入,但是注解不是自己注入,是spring通过暴力反射注入进去的)
导入配置类的两种方式
直接在另一个配置类加@Configration,由ComponentScan注解进行扫描,会自动识别到这个配置类加入到配置中,(aop的scope注解不会必须在配置类中加enable开启)
另一个配置类不加任何注解(也可以加Configration注解),在主配置类中加import表示导入配置类
注入
第三方bean自己调用第三方提供的set方法注入,注入来源获取
简单类型
在配置类上加成员变量,成员变量加value注解赋值,然后在第三方bean方法中直接引用
引用类型
直接在形参中加入,spring会根据形参类型自动注入(aop的通知方法获取参数ProceedingJoinPoint也是根据形参类型自动注入,返回值需要先在注解参数returning中定义一个变量,然后会根据名字自动传进去)
传引用类型通过方法形参传到方法内部
管理
在配置类里方法上加bean注解表示这个方法的返回值对象由spring接管
取用
按照id取用,方法名就是在bean容器的id名
按照类型取用
注解整合mybatis
导包
mysql
数据库驱动
mybatis
mybatis-spring
版本要和mybatis版本对应上
spring-context
spring-jdbc
spring-test
junit
用新版本
lombok
druid
单独定义配置类
可以不用单独创建,但是如果创建MapperScannerConfigurer对象,需要单独一个类创建,它实现了一个接口BeanDefinitionRegistryPostProcessor会导致没有进行增强就初始化了,因为没有增强成员变量还没有来得及赋值就调用方法创建第三方bean,获取的成员变量是null具体原理点我
注意下,在Spring整合MyBatis的时候 我们需要去配置我们的 SqlSessionFactoryBean MapperScannerConfigurer 这里MapperScannerConfigurer这个对象在初始化的时候会走spring bean的增强回调 public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { 这里的增强是BeanDefinitionRegistryPostProcessor这个接口 所以会导致加载bean的顺序出现问题,我们需要把mybatis和dataSource初始化的代码放到不同类中!!!!
或者使用mybatis-spring提供的mapperscan注解加在配置类上,原理不同就不会提前初始化了
创建JdbcConfig
核心配置类加@PropertySource("jdbc.properties")进行扫描,添加后可以用el表达式指明key来引用里面的value内容(表达式都是字符串类型)
将独立的配置类加入核心配置
导入配置类的两种方法
创建方法自己实例化bean,交给spring保管
配置druid连接池对象
简单类型注入:在配置类里创建方法传简单类型的参数
配置SqlSessionFactoryBean对象
实现了FactoryBean接口,是工厂模式创建对象,spring在获取到工厂对象后,发现实现了FactoryBean接口,调用里面的getObject方法获取真正要接管的对象即sqlsessionFactory对象,通过这种方式在创建时可以配置一些默认的参数值给sqlsessionFactory,如果自己创建工厂对象所有的参数都需要自己传进去所有逻辑需要自己加判断
通过形参传进datasorce对象,手动通过setter进行赋值
定义bean,返回MapperScannerConfigurer对象
可以在单独一个配置类中加这个方法,单独执行,否则提前实例化,成员变量不给赋值,获取的简单类型是null
可以直接在核心配置上加*@MapperScan("com.itheima.mapper")*指明要映射的路径,根据这个路径动态创建对象,方法加入sql语句
xml整合mybatis
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解扫描 --> <context:component-scan base-package="com.itheima.framework"></context:component-scan> <!--加载properties配置文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--初始化数据库连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${db.driver}"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> </bean> <!--初始化mybatis相关的对象--> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> </bean> <!--包扫描--> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.itheima.framework.mapper"/> </bean> </beans>
在xml配置文件中导入properties,<context:property-placeholder location="jdbc.properties"/>
导入包扫描<context:component-scan base-package="com.itheima.service"/>
依次创建对象
DruidDataSource
SqlSessionFactoryBean
MapperScannerConfigurer
整合junit
代码
`//【第二步】使用Spring整合Junit专用的类加载器 @RunWith(SpringJUnit4ClassRunner.class) //【第三步】加载配置文件或者配置类 @ContextConfiguration(classes = {SpringConfiguration.class}) //加载配置类 //@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载配置文件 public class AccountServiceTest { //支持自动装配注入bean @Autowired private AccountService accountService; @Test public void testFindById(){ System.out.println(accountService.findById(1)); } @Test public void testFindAll(){ System.out.println(accountService.findAll()); }`
导坐标
<!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--spring整合junit--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.9.RELEASE</version> </dependency>
注解开发总结
MVC
依赖
<dependencies> <!--mysql的驱动,连接mysql数据库--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> <!--mybatis功能能够实现的依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.11</version> </dependency> <!--连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <!--mybatis给spring做的适配能够实现快速开发, 有sqlsessionfactorybean快速创建数据源工厂和mapper注解快速配置扫描--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!--spring的容器--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!--提供关于jdbc的功能支持,例如事务管理--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version> 5.2.10.RELEASE</version> </dependency> <!--提供关于mvc功能支持, 提供一个调度servlet联合了父子容器,快速开发 比如调度jack对接收和发送数据的json转换,异常管理,拦截器管理--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!--实现对json转化--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.4.2</version> </dependency> <!--对测试功能的支持--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!--实现测试功能--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> </dependencies>
注解开发前提(配置类操作)
servlet控制器配置类
public class SpringDispatcherServletInitializer extends AbstractDispatcherServletInitializer { @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext(); annotationConfigWebApplicationContext.register(SpringMvcConfig.class); return annotationConfigWebApplicationContext; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } @Override protected WebApplicationContext createRootApplicationContext() { return null; } }
WebApplicationInitializer
功能
现在JavaConfig配置方式在逐步取代xml配置方式。而WebApplicationInitializer可以看做是Web.xml的替代,它是一个接口。通过实现WebApplicationInitializer,在其中可以添加servlet,listener等,在加载Web项目的时候会加载这个接口实现类,从而起到web.xml相同的作用。下面就看一下这个接口的详细内容
原理
重写方法传出自己创建的spring容器和子容器,子容器在内部传进servlet中,父容器传进监听器中
分别创建service包和controller包,分别对应了父容器和子容器存储,因为子容器与父容器进行了关联,在子容器bean中能自动注入父容器的bean
源码分析
总体分析
注意点:这个AbstractDispatcherServletInitializer只是负责将由spring-web提供的DispatcherServlet和registerContextLoaderListener对象创建出来,调用重写的容器方法创建出容器没有将其初始化,传到这两个对象中,然后将这两个对象直接添加到servletcontext容器中,让tomcat直接执行,将spring容器传进去registerContextLoaderListener,将springmvc容器传进DispatcherServlet,在这些,servlet的init方法和listener的init方法是由tomcat开始执行,在这些init方法中,先执行listener(监听到服务器启动就执行)的init方法,这里面将spring容器注册到servletcontext中,然后初始化spring容器,然后执行servlet的init方法,将传进来的springmvc容器注册到servletcontext中,同时将两个容器通过setParent关联起来(这样web里的bean可以自动匹配到service里的bean,否则这是两个容器的bean没有任何关联无法匹配)最后初始化springmvc容器
AbstractDispatcherServletInitializer功能
由spring-web提供的DispatcherServlet和registerContextLoaderListener对象创建出来,执行自己重写的获取spring父子容器的方法,获取出来,向这两个对象分别传进去,然后将这两个对象传到servletcontext中,让tomcat不用创建对象直接执行里面的init方法
流程分析
AbstractDispatcherServletInitializer的onStartup获取到servletContext目的是将创建的监听对象registerContextLoaderListener和servlet对象DispatcherServlet添加到servletcontext中让tomcat直接可以用,不用tomcat再创建对象了
执行super.onStartup(servletContext)方法,创建registerContextLoaderListener对象
先调用了createRootApplicationContext获取到自己创建的spring容器,然后创建了registerContextLoaderListener对象,将这个容器传到里面,然后将这个listener对象添加到tomcat的servletcontext容器中
tomcat执行这个registerContextLoaderListener对象的的init方法,注意tomcat先执行的是监听器的init方法,然后执行servlet的init方法,在init方法里执行一系列以init开头的方法,其中之一就是先将容器添加到servletcontext中,然后初始化spring容器
执行registerDispatcherServlet(servletContext)方法,创建DispatcherServlet对象
AbstractDispatcherServletInitializer先是调用了createServletApplicationContext方法获取到自己创建的springmvc容器,没有将其初始化(没有调用refresh()方法),然后创建了DispatcherServlet对象,将这个springmvc容器传到里面,将这个对象添加到servletcontext容器中
,由tomcat执行里面的init方法,init方法先从servletcontext获取到spring容器,通过setparent(spring容器)设置父子关系,然后初始化sprngmvc容器
具体代码
web项目开启先执行onstartup里面的方法,tomcat将最大的域对象传进这个方法依次执行,调用父类的onstartup方法,调度servlet方法
父类onstartup方法,操作关于监听器的方法
调用自己重写的createRootApplicationContext方法,传出自己创建的spring容器(父容器),创建一个监听器对象(监听器会在容器创建时执行contextInitialized方法,销毁时执行contextDestroyed方法),将这个spring容器传进去,在这里创建的spring容器
contextInitialized方法最后将这个spring容器当成一个属性value存进servletContext中
将创建的servlet对象传进servletContext,这样tomcat不用创建servlet直接调用
注意
最开始创建出来两个spring容器,但没有初始化,先执行的是监听器的init方法,后执行的servletinit方法,所以先初始化的是spring容器,后初始化的是springmvc容器,所以spring容器无法访问子容器,子容器可以直接访问父容器(创建时间不同),先将子容器和父容器绑定后再初始化,这样子容器有了父容器的bean
初始化容器和创建容器不是一个概念,初始化容器指的是根据配置类路径扫描需要创建的bean,将bean创建出来
AbstractAnnotationConfigDispatcherServletInitializer
区别就是不用自己创建容器,其他没有区别
spring容器配置类
@Configuration @ComponentScan("com.create.web") @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer { @Autowired private UserIntercrptor userIntercrptor; /** * 放行静态资源 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/user/**").addResourceLocations("/user/"); } /** * 拦截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { ArrayList<String> strings = new ArrayList<>(); strings.add("/js/**"); strings.add("/user/**"); strings.add("/users/login"); registry.addInterceptor(userIntercrptor).addPathPatterns("/**").excludePathPatterns(strings); } }
扫描路径
精确扫描,加具体的路径,可以传数组
都不用加configration注解,在创建容器对象的时候已经将传进去的类当做配置类了,还有impot默认导入的也是配置类,也不需要加configration注解
全局扫描,过滤掉web层文件
@ComponentScan(value = "com.example", excludeFilters=@ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Collector.class ))
全局扫描中,在springmvccontext中不能设置configration注解,否者父容器在初始化扫描时会读取到这个注解,把这个类当成配置类,加载里面的扫描注解
获取控制器,注册静态资源
获取控制器注册拦截器
声明式事务
原理
通过aop在方法内容前后加上开启事务和提交事务,形成一个总的事务,如果是设置单独一个dao开启单独事务,先判断原方法有没有事务,设置单独事务:(通过事务对象PlatformTransactionManager里的一个方法getTransaction传进去TransactionDefinition传出一个对象记录了原方法中是否有事务TransactionStatus)设置有事务是加入REQUIRES,设置有事务单独创建一个事务requires和,默认的区别是默认的原方法没有事务,会创建一个事务,requires没有事务不会创建事务
PlatformTransactionManager的功能
在一个service层定义了一个方法加上事务注解,这个方法再别的service类中被调用,可以设置被调用时事务是加入到事务管理员中,还是自己单独开一个事务,还是自己不开事务,通过注解对象的成员变量propagation设置
默认只会滚error和运行异常报错,不会滚系统异常(IO)通过rollbackfor可以设置
看官方文档关于xml设置事务
区别是事务要指定开始事务的方法,还要指定aop要增强的方法
官方网站
是通过代理模式进行增强,调用了datasorce添加了开启事务和提交事务,如果没有实现接口就不能用jvm的动态代理,会自动采用另一种代理模式,创建子类的模式
注解步骤
spring-jdbc包含了声明式事务的jar包
配置类上开启声明式事务,否则不会开启功能
@EnableTransactionManagement
在配置类中创建一个事务管理者,在其中传进datasocoure, 里面有对应的声明,能够在源对象方法中加前置增强开启事务,和关闭事务
@Bean public PlatformTransactionManager platformTransactionManager(DataSource dataSource){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; }
在需要增强的方法上或者类上(指的是实体类)加事务注解,可以在注解中个性化设置,设置什么时候回滚(默认error和运行异常回滚),设置事务传播行为,默认加入主事务中
设置·
子主题
注解
@Transactional
声明式事务是针对的service层
拦截器
概念
和过滤器的区别
过滤器是基于javaee三大体系中的过滤器,对tomcat所有请求都能拦截,拦截器是基于servlet实现的实际还是dispatcherservlet的内部功能,只能对dispatcherselet能够映射的路径做文章
作用
方法前后做事
阻止原生方法执行
执行流程
来:请求经过控制器先拦截
输送给通过后控制器判断资源类型
静态资源
动态资源
和异常的区别:加异常处理,是直接处理在方法上,不用注册,和控制器无关
改变控制器的控制是通过改变控制器的属性(注册)
来: 控制器加个拦截器
输送:控制器加个分辨资源方式
注册原因
是处理请求过来的逻辑,是控制器的功能, (而异常是通过aop加在方法上的,作用在动态资源上,无需控制器执行)
开发步骤
位置在controller下创建一个单独的包
,放在里面,因为他是对controller层做控制的
自己创建一个拦截器
@Component public class UserIntercrptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object username = request.getSession().getAttribute("username"); if(username==null){ HashMap<String, Object> stringObjectHashMap = new HashMap<>(); stringObjectHashMap.put("code",500); stringObjectHashMap.put("msg","登录后重试"); String value = new ObjectMapper().writeValueAsString(stringObjectHashMap); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(value); return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
代码
@Component public class UserIntercrptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object username = request.getSession().getAttribute("username"); if(username==null){ HashMap<String, Object> stringObjectHashMap = new HashMap<>(); stringObjectHashMap.put("code",500); stringObjectHashMap.put("msg","登录后重试"); String value = new ObjectMapper().writeValueAsString(stringObjectHashMap); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(value); return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
preHandle
方法执行前执行,做判断用
参数
request
resp
handler
被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
postHandle
方法执行后(返回时)执行,如果在拦截器链中没有通过,一个也不执行
参数
req
resp
modelAndView
如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转
afterCompletion
在posthandle之后执行,如果一个拦截链没有通过,这个链中的after不会执行
req
resp
handler
Exception
因为拦截器没有指明作用范围,配置类上继承WebMvcConfigurer需要手动配置上(是用在注册上)
@Configuration @ComponentScan("com.create.web") @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer { @Autowired private UserIntercrptor userIntercrptor; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/user/**").addResourceLocations("/user/"); } @Override public void addInterceptors(InterceptorRegistry registry) { ArrayList<String> strings = new ArrayList<>(); strings.add("/js/**"); strings.add("/user/**"); strings.add("/users/login"); registry.addInterceptor(userIntercrptor).addPathPatterns("/**").excludePathPatterns(strings); } }
拦截器链
当配置多个拦截器时,形成拦截器链
执行顺序是注册时的添加顺序
拦截器链的运行顺序参照拦截器添加顺序为准
当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作postHandle不工作
只要有一个拦截器没有通过,只执行这个拦截器之前的after方法,h不执行postHandle方法
请求和发送
@RestController @RequestMapping("/student") public class StudentController { @Autowired private StudentService studentService; /** * 查询 * @param student * @return */ @GetMapping("/currentpage") public Result getPage(Student student){ //健壮性判断 if (student==null) { return Result.Err(Code.SYSTEM_ERR,"没有接收到信息"); } Integer pageSize = student.getPageSize(); Integer newIndex = student.getNewIndex(); student.setNewIndex((newIndex-1)*pageSize); List<Student> students = studentService.selectPage(student); Integer integer = studentService.selectSum(student); //健壮性判断 if (students==null&&integer==null) { return Result.Err(Code.SYSTEM_ERR,"服务器繁忙"); } return Result.OkByCurrentPage(Code.SELECT_ALL_OK,students,integer); } /** * 删除 * @param ids * @return */ @PostMapping("/delete") public Result delete( @RequestBody List<Integer> ids){ //健壮性判断 if (ids==null) { throw new RuntimeException("没有发送数据"); } Integer integer = studentService.deleStudent(ids); if(integer==null){ throw new RuntimeException("数据库异常"); } return Result.OK(Code.DELETE_OK,"删除成功"); } @PostMapping("/add") public Result add(@RequestBody Student student){ //健壮性判断 if (student==null) { throw new RuntimeException("没有发送数据"); } Integer integer = studentService.addStudent(student); if(integer==null){ throw new RuntimeException("数据库异常"); } return Result.OK(Code.ADD_OK,"添加成功"); } @GetMapping("/getone/{id}") public Result getOne(@PathVariable Integer id){ //健壮性判断 if (id==null) { throw new RuntimeException("没有发送数据"); } Student student = studentService.selectById(id); if(student==null){ throw new RuntimeException("数据库异常"); } return Result.OK(Code.SELECT_ONE_OK,student); } @PutMapping private Result update(@RequestBody Student student){ //健壮性判断 if (student==null) { throw new RuntimeException("没有发送数据"); } Integer update = studentService.update(student); if(update==0){ throw new RuntimeException("数据库异常"); } return Result.OK(Code.UPDATE_OK,"修改成功"); } }
请求
设置请求访问路径
在springMvc容器有的路径
类上加路径
表明这个类中所有的方法访问需要访问的路径
方法上加路径
@RequestMapping("/twoParam")
这个注解可以设置method即rest风格的访问方式
@GetMapping("/{id}")直接用访问风格的访问方式注解,里面加上路径变量,注意路径变量在访问的时候无需加key值,原理将最后一个路径取出来当成一个变量使用
在springMvc容器中没有的路径(静态资源)
原因
和过滤器不同,过滤器需要在tomcat容器启动时就要执行放在servlet初始化中,访问静态资源是在访问时候要执行的,所以过滤器是springMvc容器的配置类里进行配置的
因为设置的Dispatcherervlet的访问路径是缺省路径,所有的浏览器都是访问的这个servlet,这个servlet里面有子容器且关联了父容器,能够访问控制层和service以及持久层的bean的方法,但是没有静态资源,所以访问静态资源直接报错,因为找不到,所以需要自己设置
设置方法
对springmvc容器进行配置
@Configuration @ComponentScan("com.create.controller") @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer { //访问静态资源 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/js/*").addResourceLocations("/js"); } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
添加springmvc容器的配置,实现webmvcconfigurer接口/webMvcconfiguretionsupport,重写addResourceHandlers方法,对方法传进来的ResourceHandlerRegistry对象添加访问规则
继承webMvcconfiguretionsupport的类
重写含有addresource的一个方法addResourceHandlers(添加资源处理器,registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");意思是注册器访问到pages的路径直接导向到第二个设置的路径,可以添加多个
创建一个webmvcconfigurer配置器的实现类
重写含有addresource的一个方法addResourceHandlers(添加资源处理,registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");意思是注册器访问到pages的路径直接导向到第二个设置的路径,可以添加多个
开启默认的servlet
实现方法configureDefaultServletHandling接收到传进来的DefaultServletHandlerConfigurer参数,执行开启方法enable()
意味着当有虚拟路径访问后端时,先DispatcherServlet分配给spring容器中有没有这个路径的映射,最终查到没有,开启默认的servlet,由默认的servlet搜后端所有的路径进行匹配
对DispatcherServlet的映射路径进行匹配
访问路径设加*.do,没有这个后缀的不有DispatcherServlet接管,由默认的servlet匹配
设置接收的请求方法(rest风格)
@GetMapping("/{id}")直接用访问风格的访问方式注解,里面加上路径变量,省去了RequstMapping注解
获取请求参数
处理中文乱码
在DispatcherServletInitializer类中重写getServletFilters方法自己重写方法,传出一个过滤器,可以自己在过滤器设置修改编码,可以创建CharacterEncodingFilter对象,使用springmvc提供的设置编码的过滤器
获取k=v形式的参数(在方法形参加上要获取的参数)(最不常用)
获取的实际是参数类型的数据,get请求的是参数类型,也把post的key=value形式加入到这里面了
访问的方式
post
get
场景
字符串类型接收
形参名字和key名字能匹配上
方法形参名字和发送的key名字相同直接传进来无需处理
形参名字和key名字不能匹配上
方法形参名字和发送的key的名字不同,需要在形参上加注解指明前端传进来的那个参数传到形参中去@RequestParam("username")
数组类型接收
前端设置了多个key相同的数据,后端可以用数组类型接收
集合类型接收
前端设置了多个key相同的数据,springMVC识别到,key相同的形参是对象类型,会先创建一个无参对象,然后将数据传到成员变量上,这时就会报错,应指明将数据当成参数传进去不是当成成员变量,加@RequestParam 注解 这个注解的作用一是指明发来的那个数据传到那个形参里面,二是将发来的数据当成参数传进形参中,如果是想kv传进对象里面,不要加这个注解,默认就是将kv参数传进来的,这个注解因为默认就是kv的参数,可以不用加这个注解,脱离了原本功能,在原本功能基础上添加了这些新功能,requestbody就没有参数传进形参的额外的功能
HttpServletrequest接收
springmvc会将request传进来
对象类型接收
单一对象接收
无需任何处理,发送数据无需按照类名.成员变量名,直接传成员变量名自动装进去自动将value按照key传进对象中,对象别忘了setget方法,原理是反射创建了无参对象,通过setter注入形式(spring通过注解的形式创建bean是暴力反射创建对象)
嵌套对象接收
发送的数据key应指明是哪个对象的,发送形式是类名.成员变量名=成员变量
接收时间类型
方法形参上加注解@DateTimeFormat(pattern="yyyy-MM-dd") 会将形参名字与key相同的字符串先进行转化,然后传进方法中
原理
其内部依赖Converter接口
接口就是实现数据接收过来自动转化的
请求参数年龄数据(String→Integer)
json数据转对象(json → POJO)
日期格式转换(String → Date)
注意
传递日期类型参数必须在配置类上使用@EnableWebMvc注解。其功能之一:根据类型匹配对应的类型转换器。
获取路径变量(有一个参数需要传)(最常用)
在形参上加@PathVariable 注解,表明将这个路径变量传进这个形参中,注意路径变量名字要和形参名字保持一致
注解上加el表达式表示环境变量@GetMapping("/{id}")
获取的是json格式的参数,是请求体中的数据(有多个参数需要传)
实际需要指明是获取的请求体的数据加注解RequestBody,具体指的是json格式的请求体数据,k=v格式的post请求把它归到Param类型了,也是通过jaskon自动将字符串类型的json格式数据转成对象了
POJO参数
直接对象接收
POJO集合参数,特指有[]引起来的数据
list<User>
List<Map<String',Object>>
这里的集合无需表明是注入参数不是注入成员变量,因为这里是请求体的数据,是jaskson来处理的,jaskson更智能,如果是kv形式,不属于请求体数据(kv的请求体也包括在内),是mvc解析的不智能
单个数据有[]引起来也能接收,但是单个数据没有[]引起来不能用json接收,json只能发送数组和对象,不能发送单个数据,对象数据可以对象名不对起来,但是成员变量名要对应,路径变量要自己定义的{id}值和形参对应,kv形式要k一致,get的可以改名
对于删除请求,可能有一个数据,可能有多个数据
对于明确只有一个参数可以用路径变量接收,不明确的要转成数组用json格式发送,原因是这是两种获取方式,获取的是不同的地方,解决方式是变成一种获取方式,都转成json格式相对简单#重点
原因
单个数据不是json格式,不能在请求体中接收,只能通过kv参数形式接收
形参不能加注解,表示获取的是RequestParam的参数
不能获取请求体的数据,没有加body注解,要是加上,不能获取param的数据
param和body的数据不能通过一个形参来接收,即使他们都能转成数组
传入格式转换
传入的是数组,可转换成java中的数组和集合
传入的是对象,可转换成java中的map集合和对象
匹配形参的注解
requestparam
获取参数值
用于接收url地址传参或表单传参,具体指的是kv形式
requestbody
形参的值来自请求体,不加默认是获取的参数值,请求体的值可以自动进行json字符串转换成对象
PathVariable
用于接收路径参数,使用(参数名称)描述路径参数
都是指的形参的值得来源
与aop声明获取参数和返回值的区别
获取参数
也是在形参中加注解JoinPoint指明来源,都是形参加上来源说明注解
发送
注解responseBody含义
将返回值写到response的响应体中(body)发给前端
流程
方法上没有responsebody注解且类上没有针对整个类的responsebody注解,返回的是跳转路径
转发
返回的spring数据前加forward:,可省略,默认的跳转格式
重定向
返回的spring数据前加redirect:表示跳转路径
有resposebody注解(表示这个方法返回的是到浏览器的数据不是跳转)
加注解的方法
**类上加restController注解,**自带responseBody注解和Controller注解
方法上加responsebody注解表示针对这个方法的配置
类上加responsebody注解,表示对这个类中所有方法的配置
判断返回的是字符串类型还是其他类型
字符串类型
因为是由responsebody注解,表示将这个字符串写到响应体中传给前端因为写到响应体中的是spring字符串,
其他类型
如果返回的是其他类型会自动转换成json格式,前提要开启这个功能,加入jackson依赖,在springMvc配置类上加注解@EnableWebMvc 表示开启mvc功能,(这是对springmvc进行的设置,不是对mvc容器的设置)
rest风格理解
路径不同
对于同一种请求类型,由于在路径上加了路径变量,url不同会走不同的方法,路径变量可以看成url的一部分,get参数由于加了?不能看成url的部分
数据存的地方不同
路径上可以存路径变量,可以直接获取url的最后路径当成参数用(路径变量)
请求体中存json数据
postmapting
省略了requestmapping
整体思路
访问到
路径+访问访问方式
获取数据
k=v
无需加注解指明文位置,默认获取位置参数形式,加注解特殊要求
直接自动填入,特殊要求加requestparam注解,(传进参数,指明对应)
json格式
requestbody指明获取的位置,自动转换
发出数据
response指明发出的位置,不加默认跳转
异常处理(实际是用封装的对象操作)
创建异常类
public class FormatExecution extends RuntimeException{ public FormatExecution(String message) { super(message); } }
创建自己的异常类,继承RuntimeException类,这样在后端中可以对不符合自己要求的参数/可能出现的问题进行抛出自己设定好的异常,可以针对这些异常进行个性化处理
开始异常处理的aop
@RestControllerAdvice public class ControllerAdvice { @ExceptionHandler(FormatExecution.class) public Result runErr(FormatExecution formatExecution){ String message = formatExecution.getMessage(); return Result.Err(Code.SYSTEM_ERR,message); } @ExceptionHandler(Exception.class) public Result systemErr(Exception exception){ return Result.Err(Code.SYSTEM_ERR,"系统繁忙,请稍后重试"); } }
原理
aop中需要切点和声明组成切面,才能对其进行增强,整体流程springmvc已经做出了优化,可以新建一个类加ControllerAdvice注解或者/restcontrolleradvice表示这个类的方法是针对controller注解类的所有方法做的声明(切点是所有Controller的所有方法,通知是这个类的所有方法),方法加注解ExceptionHandler表示是对异常处理做的声明,注解参数可以按照严重程度依次加深,最后加一个execution注解,包含所有异常(前面异常都不匹配才走这里),
ControllerAdvice这一个注解就表示了切面
具体代码
注意
因为后端所有的运行都是开始于Controller跑起来的,所以只要有异常,都会抛到控制层,控制层如果没有处理,会抛给调用它的tomcat处理,tomcat无法处理,抛给调用它的jvm处理,jvm直接报错停止运行,如果有在控制层通过aop加上异常捕获,会执行到aop捕获的声明中处理,所以异常处理要写在控制层
异常处理Controlleradvice无需加开启功能开关,声明式事务需要加开启开关,事务spirng功能多,需要开启功能
常见功能区分
service层功能
和声明事务的区别
生成
开启事务
事务已经封装好了,无需自己填东西,直接生成对象就行
连接
在需要加事务的类上加注解指明
controller层功能
和异常的区别
生成
异常是aop的,需要自己写里面的逻辑,自己创建一个类,生成对象,让spring扫描到就能用
连接
在处理异常类上加的restContollerAdvice注解就指明了作用范围
拦截器
生成
自己写拦截逻辑,生成bean让spring能够访问到
连接
在配置类里注册上,(改变控制器的参数值)
注册原因
是处理请求过来的逻辑,是控制器的功能,而异常是通过aop加在方法上的,作用在动态资源上,无需控制器执行
执行流程
来:请求经过控制器先拦截
输送给通过后控制器判断资源类型
具体的逻辑
静态资源
动态资源
加异常处理,是直接处理在方法上,不用注册,和控制器无关
改变控制器的控制是通过改变控制器的属性(注册)
来: 控制器加个拦截器
输送:控制器加个分辨资源方式
spring容器配置类加什么
第三方bean
mybatis
druid
添加功能的bean
声明式事务
已经写好的自己无需修改,采用第三方bean形式自己选择一个事务管理器创建出来,传出去让spring保管
@Bean public PlatformTransactionManager platformTransactionManager(DataSource dataSource){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; }
控制层声明
由于声明需要自己写具体的逻辑,spring无法给封装好,需要自己创建出来让spring保管,然后执行其中的逻辑
@Beanpublic PlatformTransactionManager platformTransactionManager(DataSource dataSource){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; }
配置操作
访问静态资源
主要是获取到ResourceHandlerRegistry对象,对里面的默认值做修改,不是创建bean
@Configuration@ComponentScan("com.create.controller") @EnableWebMvcpublic class SpringMvcConfig implements WebMvcConfigurer { //访问静态资源 *@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/js/*").addResourceLocations("/js"); } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
原则
添加功能创建bean
修改功能直接修改bean的默认值
xml开发
@Component public class UserIntercrptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object username = request.getSession().getAttribute("username"); if(username==null){ HashMap<String, Object> stringObjectHashMap = new HashMap<>(); stringObjectHashMap.put("code",500); stringObjectHashMap.put("msg","登录后重试"); String value = new ObjectMapper().writeValueAsString(stringObjectHashMap); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(value); return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
需要自己在web.xml创建出来DispatcherServlet,自己创建出来监听器,向里面分别传入容器配置类
springboot
基本开发步骤
main
@SpringBootApplication public class Springboot01QuickstartApplication { public static void main(String[] args) { SpringApplication.run(Springboot01QuickstartApplication.class, args); } }
pom文件
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--配置yml解析--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.5</version> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
切换服务器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--web起步依赖环境中,排除Tomcat起步依赖--> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!--添加Jetty起步依赖,版本由SpringBoot的starter控制--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
不能解析yml文件
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
依赖说明
依赖管理(提供版本号,无需自己写版本)
起步依赖(web的起步依赖和test的起步依赖,以及mybatis的起步依赖)
springboot插件打包jar包时提供入口程序
application.yml文件
server: port: 80 servlet: path: / spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///db1 username: root password: root mybatis: configuration: map-underscore-to-camel-case: true
mapper
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace=""> </mapper>
cmd快速启动
需要依赖maven插件支持
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
jar支持命令行启动需要依赖maven插件支持,请确认打包时是否具有SpringBoot对应的maven插件。
打包的程序是jar包,但是在jar包里面的项目是war包
启动
cmd指令
java -jar springboot_01_quickstart.jar # 项目的名称根据实际情况修改
停止ctrl c
切换环境
开头加--照着yml文件写,中间用点,最后用等于
配置
引用配置文件数据
yml代码
person: age: 18 name: 张三 sex: 男 hobbies: - 抽烟 - 喝酒 - 烫头
yml导入方式
value导入
${}注解的value值直接引用
environment导入
自动注入environment对象,里面存了所有属性
导入到bean
@Data @AllArgsConstructor @NoArgsConstructor @Component @ConfigurationProperties("data") public class User { String name; Integer age; }
controller代码
@RestController @RequestMapping("/users") public class UserController { @Value("${person.age}") Integer age; @Autowired Environment environment; @Autowired User user; @GetMapping public void show(){ System.out.println(age); System.out.println(environment.getProperty("person.name")); System.out.println(user); } }
配置不同环境(多环境)
yml配置
配置不同环境
子主题
使用
spring: profiles: active: dev
切换端口号和虚拟路径
server: port: 80 servlet: path: /
结合maven配置
开启静态资源替换占位符插件
<build> <plugins> <plugin> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>utf-8</encoding> <useDefaultDelimiters>true</useDefaultDelimiters> </configuration> </plugin> </plugins> </build>
pom文件定义替换元素
<profiles> <profile> <id>dev_env</id> <properties> <profile.active>dev</profile.active> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>pro_env</id> <properties> <profile.active>pro</profile.active> </properties> </profile> <profile> <id>test_env</id> <properties> <profile.active>test</profile.active> </properties> </profile> </profiles>
yml文件使用
设置日志显示优先级
logging: level: root: debug
日志级别
ALL < TRACE < DEBUG < INFO < WARN < ERROR < OFF
加载参数优先级
不同目录配置文件优先级
1级: file :config/application.yml 【最高】
jar包同级
2级: file :application.yml
3级:classpath:config/application.yml
jar包里面
4级:classpath:application.yml 【最低】
同目录配置文件优先级
1properties
2yml
3yaml
参数加载优先顺序
整合第三方bean
整合junit
创建与启动类同级的测试类,能够获取启动类的信息
测试类上加注解
@SpringBootTest
整合持久层
导入坐标
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
mysql的依赖
数据源的依赖
mybatis适配springboot的依赖
数据源需要自己配置
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///db1?useSSL=false username: root password: root type: com.alibaba.druid.pool.DruidDataSource
整合mybatis
mybatis工厂对象自动配好了,可以个性化设置
mybatis: configuration: map-underscore-to-camel-case: true
映射手动指明在启动类上加
@MapperScan("com.create.mapper")
也可直接在mapper类上加mapper注解,不用容易漏
SSM
静态资源放在resources里面
其他不变
流程理解
主程序注解
扫描
加上这个注解@SpringBootApplication里面有一个注解是ComponentScan是开始扫描包的注解,指明了扫描范围是从这个类的路径开始到子类路径
@ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} )
配置类
@SpringBootConfiguration带有这个注解
import导入了能够开启装配的配置类
@SpringBootApplication
@ComponentScan扫描同路径和子路径
@EnableAutoConfiguration开启自动装配
@Import(AutoConfigurationImportSelector.class)导入一个配置类
这个配置类会加载所有的META-INF/spring.factories目录,存在就会创建对象(自动注入)
mybatis启动依赖
mybatis-spring-boot-starter
存在一个子依赖mybatis-spring-boot-autoconfigure
依赖里就有符合boot规范的META-INF/spring.factories文件
文件表头加了限制条件,不满足条件不会加载这个类,就不会创建bean
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})//需要同时有这两个类 @ConditionalOnSingleCandidate(DataSource.class)//需要有数据源
文件内容读取key值符合boot要求的key值,加载value对应的类
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
MybatisAutoConfiguration这个类加了配置注解且里面有方法创建bean返出来交给spring接管
@Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); ----------------------------------------------省略线------------------------------------------------- return factory.getObject();这是factorybean工厂对象获取生产对象的方法 }
实际上springboot的文件spring.factories就有一大堆value值,里面都有对应的生成bean的类,但是不会执行,因为都加了条件,只有满足了条件才会执行
流程图

注意事项
可以转换名
yml文件没有提示可以写第一行后复制到下面
yml文件里空格和tab键不能混用
mybatisplus
配置
坐标
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--配置yml解析--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version> 3.4.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.5</version> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
mp单独坐标
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version> 3.4.1</version> </dependency>
yml综合配置
server: port: 80 spring: webservices: path: / datasource: driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql:///db1 username: root password: root mybatis-plus: global-config: db-config: table-prefix: tb_ logic-delete-field: deleted logic-not-delete-value: 0 logic-delete-value: 1 id-type: input configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
基础yml配置
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql:///db1 username: root password: root main: #取消spring图标工作台打印 banner-mode: off mybatis-plus: global-config: banner: false db-config: #设置要找的表名加上tb_,自动开启将pojo驼峰转换 table-prefix: tb_ configuration: #输出日志 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
要查询的表的别名
别名是相互转换的,由basemapper<bean名>可以装换成表类型,加上前缀,定位到数据表
开启日志
逻辑删除
id生成策略
图标关闭
解决日志打印过多问题
在resources下新建一个logback.xml文件,名称固定,内容如下:
<?xml version="1.0" encoding="UTF-8"?> <configuration> </configuration>
具体配置设置
pojo配置
@Data @TableName("tbl_user") public class User { /* id为Long类型,因为数据库中id为bigint类型, 并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型 */ private Long id; private String name; @TableField(value = "pwd",select = false)//查询时不返回这个数据 private String password; private Integer age; private String tel; @TableField(exist = false) //表示online字段不存在,所以不参与CRUD操作 private Boolean online; }
@TableField的value注解
exist属性排除不存在属性值
原因是在查询时生成的sql语句会将select后的名字按照brand添加上去
select设置查询权限
修改表名
TableName(添加单个)
@Data @TableName("tbl_user") public class User { /* id为Long类型,因为数据库中id为bigint类型, 并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型 */ private Long id; private String name; @TableField(value = "pwd",select = false) private String password; private Integer age; private String tel; @TableField(exist = false) //表示online字段不参与CRUD操作 private Boolean online; }
配置文件上(都添加)
mybatis-plus: global-config: banner: false db-config: #设置要找的表名加上tb_,自动开启将pojo驼峰转换 table-prefix: tb_
功能语句
crud
查询
封装器(条件)
封装器种类
LambdaQueryWrapper
QueryWrapper
QueryWrapper使用lambda方法开启lambda填写
条件查询
设置where后的条件值
能够设比较,模糊查询,分组,排序,分组后条件,
查询投影
select属性可以设置默认不显示
设置select后要显示的值,不设置默认填按照bean的属性,没有就报错
@Data @Builder public class Brand { Long id; //声明不投影这个属性 @TableField(select = false) String brandName; //声明表中不存在,在用select没有指定投影不拼这个属性 @TableField(exist = false) String companyName; }
分页
Page<Brand> brandPage = new Page<>(); brandPage.setCurrent(1); brandPage.setSize(10); QueryWrapper<Brand> brandQueryWrapper = new QueryWrapper<>(); Brand brand = new Brand(); brandQueryWrapper.like(brand.getBrandName()!=null && "".equals(brand.getBrandName()),"brand_name",brand.getBrandName()); brandQueryWrapper.like(brand.getCompanyName()!=null && "".equals(brand.getCompanyName()),"company_name",brand.getCompanyName()); brandQueryWrapper.eq(brand.getStatus()!=null,"status",brand.getStatus()); Page<Brand> brandPage1 = brandMapper.selectPage(brandPage, brandQueryWrapper); System.out.println(brandPage1); long total = brandPage.getTotal(); List<Brand> records = brandPage.getRecords();
新建page对象.构造方法中传当前页和页个数
实现分页功能需要在拦截器中添加拦截器插件
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ //1 创建MybatisPlusInterceptor拦截器对象 MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor(); //2 添加分页拦截器 mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mpInterceptor; } }
获取数据从page对象中获取
分组
QueryWrapper<Brand> brandQueryWrapper = new QueryWrapper<>(); brandQueryWrapper.groupBy("id"); List<Map<String, Object>> maps = brandMapper.selectMaps(brandQueryWrapper); System.out.println(maps);
新增
id生成策略
直接在配置文件设置id生成策略
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///db1?useSSL=false username: root password: root type: com.alibaba.druid.pool.DruidDataSource mybatis-plus: global-config: db-config: table-prefix: tb_ logic-delete-field: deleted logic-not-delete-value: 0 logic-delete-value: 1 #设置id生成是自动生成,(不拼sql) id-type: auto banner: false configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
assign-id
雪花算法,生成的id是连续的,在生成索引时方便快速
assign-uuid
auto
自动,生成sql不填id
input
手动填写,生成sql有id
是在代码层生成的
修改
乐观锁
在pojo中加version注解
实现乐观锁功能需要在拦截器中加拦截器插件
@Configuration public class MybatisConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); /*添加一个拦截器*/ mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; } }
原理
在修改操作时先比对version值,version值和取的时候一值可以修改,将version加一
删除
逻辑删除
设置一个列用作表示状态
注意不要将关键字delete当成这个状态列名
在pojo中加deleted注解
在配置文件中标明
server: port: 80 spring: webservices: path: / datasource: driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql:///db1 username: root password: root mybatis-plus: global-config: db-config: table-prefix: tb_ logic-delete-field: deleted logic-not-delete-value: 0 logic-delete-value: 1 id-type: input configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl