创始人:Rod Johnson
spring理念使现有技术更加容易使用,像是一个粘合剂
SSM: Spring+SpringMVC+MyBatis
官网: Spring | Home 中文网: Spring 中文网 (springref.com)
官方下载地址: https://repo.spring.io/ui/native/libs-release-local/org/springframework/spring/
GitHub上的Spring项目: https://github.com/spring-projects/spring-framework
官方API:https://spring.io/projects/spring-framework#learn
官方文档阅读**:https://docs.spring.io/spring-framework/docs/current/reference/html/**
核心技术文档: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core
maven:spring-webmvc,spring-jdbc
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.14version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.13version>
dependency>
优点
轻量级,非入侵式(不会改变原本代码)
控制反转(IOC),面向切面编程(AOP)
支持事务和框架整合
七大组件
现代化的java开发,就是基于spring的开发
Spring七大组件Spring七大组件_四舍五入不如六的博客-CSDN博客(https://blog.csdn.net/xuemin__/article/details/103325402)
IOC控制权反转
- IOC是一种设计思想,DL(依赖注入)是实现IOC的一种方法.
- 是一种通过描述与第三方,生产或获取,特定对象的方式.
- 在Spring中由IOC容器实现,实现方法是DL
原先的对象,由我们(程序员)来决定是什么样的
UserDao userDao = new UserDaoImpl();
现在的对象,传进来,不再直接由我们来决定了.控制权交给别人了
public interface UserDao {}
public class UserDaoImpl implements UserDao{}
public class UserService {
//这是以前的方式,控制权在我们
private UserDao oldUserDao = new UserDaoImpl();
UserDao userDao;
//现在控制权不在我们手里了,控制层传进来什么就是什么
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
}
这几个类全部同包
public class Servlet {
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
UserService userService = new UserService();
userService.setUserDao(userDao);
}
}
Hello Spring
包结构:com.changGe.li.pojo
User实体类
import lombok.Data;
@Data
public class User{
private String names;
private Student student;
}
Student
public class Student { }
Spring配置文件:beans.xml
要放在resource包下
官方模板
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
bean>
<bean id="..." class="...">
bean>
beans>
我们自己写的
<bean id="student" class="com.changGe.li.pojo.Student"/>
<bean id="user" class="com.changGe.li.pojo.User">
<property name="names" value="李长歌"/>
<property name="student" ref="student"/>
bean>
测试使用
import com.changGe.li.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void sprintTest(){
//读取配置文件,获取Spring容器
ApplicationContext application = new ClassPathXmlApplicationContext("beans.xml");
//从容器中,得到名叫user的对象,类型是User
User user = application.getBean("user", User.class);
String name = user.getNames();//李长歌
System.out.println(name);
}
}
ClassPathXmlApplicationContext一直向上找,可以看到途中实现了一个DefaultResourceConifg类,这个类就是用来通过流,读取配置文件的.
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
IOC创建方式 Spring的IOC:对象的一切交由Spring *** 作,如创建,管理,赋值等.
默认调用无参构造器创建
上面的配置,给User类加上全参构造器后,就报错:找不到这样的类
@Data
@AllArgsConstructor
public class User{
private String names;
private Student student;
}
<bean id="user" class="com.changGe.li.pojo.User">
<constructor-arg index="0" value="李长歌"/>
<constructor-arg type="java.lang.String" value="李长歌"/>
<constructor-arg name="names" value="李长歌"/>
bean>
把spring容器理解成一个堆内存
User user = application.getBean("user",User.class);
User user1 = application.getBean("user",User.class);
System.out.println(user == user1);//true
Spring配置
别名
<alias name="user" alias="user1"/>
原先的和别名都能使用
User user = application.getBean("user",User.class);
User user1 = application.getBean("user1",User.class);
System.out.println(user == user1);//true
name可以有多个别名
<alias name="user" alias="user1"/>
<bean id="user" class="com.changGe.li.pojo.User" name="user1,user2 user3; use4">
<constructor-arg index="0" value="李长歌"/>
bean>
User user = application.getBean("use4",User.class);
User user1 = application.getBean("user3",User.class);
System.out.println(user == user1);//true
import(导入)多人开发时,最终将所有的配置文件,导入联合在一起
beans.xml只有一个user的bean
<bean id="user" class="com.changGe.li.pojo.User" name="user1,user2 user3; use4">
<constructor-arg index="0" value="李长歌"/>
bean>
aliasTest.xml导入beans.xml后,alias标签就不会报红了
<import resource="beans.xml"/>
<alias name="user" alias="user1"/>
依赖注入
构造器注入,就是前面的**set(UserDao userDao)**的方式
Set注入
@Data
@AllArgsConstructor
@ToString
public class User{
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
}
<bean id="user" class="com.changGe.li.pojo.User">
<constructor-arg name="card">
<map>
<entry key="name" value="李长歌"/>
<entry key="age" value="18"/>
<entry key="sex" value="女"/>
map>
constructor-arg>
<constructor-arg name="games">
<set>
<value>LOLvalue>
<value>CFvalue>
<value>CSvalue>
set>
constructor-arg>
<constructor-arg name="wife">
<null/>
constructor-arg>
<constructor-arg name="info">
<props>
<prop key="driver">com.jdbc.cj.mysql.Driverprop>
<prop key="url">jdbc:mysql:///smbmsprop>
<prop key="username">rootprop>
<prop key="password">rootprop>
props>
constructor-arg>
bean>
User user = application.getBean("user",User.class);
System.out.println(user);
User(card={name=李长歌, age=18, sex=女}, games=[LOL, CF, CS], wife=null, info={password=root, url=jdbc:mysql:///smbms, driver=com.jdbc.cj.mysql.Driver, username=root})
命名空间注入
xmlns:p=“http://www.springformework.org/schema/p”
xmlns:c=“http://www.springformework.org/schema/c”
xmlns:util=“http://www.springformework.org/schema/util”
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd">
先用c,p好像没有-ref这种注入模式,直接p:wife也不能用
c-命名空间带有"-ref",而p-命名空间是不带有的,以此可以区分是字面量注入还是构造器注
spring中p命名空间和c命名空间的最大区别是什么呢?-问答-阿里云开发者社区-阿里云 (aliyun.com)
<bean id="user" class="com.changGe.li.pojo.User" c:card-ref="card" c:games-ref="games"
c:wife="李长歌" c:info-ref="info"/>
<util:map id="card" key-type="java.lang.String" value-type="java.lang.String">
<entry key="1" value="2"/>
util:map>
<util:set id="games">
<value>LOLvalue>
util:set>
<util:properties id="info">
<prop key="driver">Driverprop>
util:properties>
User(card={1=2}, games=[LOL], wife=李长歌, info={driver=Driver})
Bean的作用域
<bean scope="singleton"/>
<bean scope="prototype"/>
//测试
User user = application.getBean("user",User.class);
User user1 = application.getBean("user",User.class);
System.out.println(user==user1);
其他的作用域:request,session,application和websocket
自动装配autowire
@Data
public class User{
private Student student;
}
public class Student { }
byName:自动在容器上下文中找,和自己对象set方法后面的值对应的bean的id
如name匹配setName()
<bean id="student" class="com.changGe.li.pojo.Student"/>
<bean id="user" class="com.changGe.li.pojo.User" autowire="byName"/>
byType自动在容器上下文中找,和自己对象属性类型相同的bean
被自动装配的bean,可以不写id
<bean class="com.changGe.li.pojo.Student"/>
<bean id="user" class="com.changGe.li.pojo.User" autowire="byType"/>
测试类都是一样的
ApplicationContext application = new ClassPathXmlApplicationContext("beans.xml");
User user = application.getBean("user",User.class);
System.out.println(user.getStudent());
byName需要保证bean的id唯一性
byTyep要保证类型的唯一性
注解式自动装配@Autowired(byType式)
context约束
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
@Data
public class User{
//自动装配,可以不用写set方法了
//前提是这个student已经存在ioc容器中了,且符合byName的规则
@Autowired
private Student student;
}
<bean id="student" class="com.changGe.li.pojo.Student"/>
<bean id="user" class="com.changGe.li.pojo.User"/>
//注解配置应用上下文:用纯注解的方式获取IOC容器
ApplicationContext application = new AnnotationConfigApplicationContext(com.changGe.li.pojo.User.class);
User user = application.getBean("user",User.class);
System.out.println(user.getStudent());
ApplicationContext application = new ClassPathXmlApplicationContext("beans.xml");
User user = application.getBean("user",User.class);
System.out.println(user.getStudent());
@Nullable规定字段可以为空
@Autowired
@Nullable
private Student student;
//@Nullable同时存在,没有影响
public void setStudent(@Nullable Student student){
this.student = student;
}
User user = application.getBean("user",User.class);
user.setStudent(null);
//正常运行
System.out.println(user.getStudent());//null
@Autowired有个required属性,默认为true,不允许为空
public @interface Autowired {
boolean required() default true;
}
显式定义required,使字段可以为空
//三者可以同时存在
@Autowired(required = false)
@Nullable
private Student student;
public void setStudent(@Nullable Student student){
this.student = student;
}
@Qualifier规定使用哪个bean进行自动注入
定义了相同类型的两个bean,但是只有一个有id
<bean class="com.changGe.li.pojo.Student"/>
<bean id="student1" class="com.changGe.li.pojo.Student"/>
@Qualifier指定使用哪个bean
@Autowired
@Qualifier("student1")
private Student student;
@Resource:byName式自动装配
@Data
public class Student {
private String name;
}
两个student对象,分别给name属性赋值
<bean id="student" class="com.changGe.li.pojo.Student">
<property name="name" value="student"/>
bean>
<bean id="student1" class="com.changGe.li.pojo.Student">
<property name="name" value="student1"/>
bean>
@Data
public class User{
//两者可以同时存在,没有
@Resource(name = "student")
private Student student;
@Resource(name = "student1")
public void setStudent(Student student){
this.student = student;
}
}
测试结果最后是student1
ApplicationContext application = new ClassPathXmlApplicationContext("beans.xml");
User user = application.getBean("user",User.class);
System.out.println(user.getStudent().getName());//student1
@Autowrite要求对象必须存在
@Resource默认先通过byName来实现,找不到名字,通过byType实现.
@Resource
private Student student;
@Resource
public void setStudent(Student student){
this.student = student;
}
byType模式匹配时,只能有一个bean,多了就报错
<bean class="com.changGe.li.pojo.Student">
<property name="name" value="student1"/>
bean>
注解开发
注解必须要有aop包支持,spring-webmvc默认就有了
首行需要配置包扫描
<context:component-scan base-package="com.changGe.li.pojo"/>
<context:annotation-config/>
@Data
//相当于
@Component//确定这个类是一个组件
public class User{
//相当于
@Value("李长歌")
private String name;
@Value("李世民")
public void setName(String name){
this.name = name;
}
}
ApplicationContext application = new ClassPathXmlApplicationContext("beans.xml");
User user = application.getBean("user",User.class);
System.out.println(user.getName());//李世民
compoent延伸注解
@Repositroy == dao层
@Data
@Repository
public class User{
@Value("李长歌")
private String name;
}
User user = application.getBean("user",User.class);
System.out.println(user.getName());//李长歌
service服务层
@Data
@Service
public class User{
@Value("李长歌")
private String name;
}
controller控制层
@Data
@Controller
public class User{
@Value("李长歌")
private String name;
}
注解配置单例模式
SpringBoot:@Scope注解学习 - 寒烟濡雨 - 博客园 (cnblogs.com)
@Data
@Controller
@Scope("singleton")
public class User{
@Value("李长歌")
private String name;
}
User user = application.getBean("user",User.class);
User user1 = application.getBean("user",User.class);
System.out.println(user == user1);//true
@Scope("prototype")//false
最理想的状态是:xml管理bean,注解赋值
JavaConfig:用注解的方式配置spring
JavaConfig是Spring的一个子项目,Spring4之后,成为核心功能
public class User{}
public class Test1 {}
@Configuration//这个注解的底层源码,也是被component注解了的
@ComponentScan("com.changGe.li")//相当于扫描包
@Import(Test1.class)//导入其他的javaConfig类
public class Test{
@Bean//相当于语句,方法名是id,返回值是个User的对象
public User getUser(){
return new User();
}
}
@Test
public void sprintTest(){
ApplicationContext application = new AnnotationConfigApplicationContext(com.changGe.li.pojo.Test.class);
User user = application.getBean("getUser",User.class);
System.out.println(user);
}
代理模式
能组合,就不继承
面向对象的七大原则: https://blog.csdn.net/qq_34760445/article/details/82931002
改动别人的代码,在公司,是大忌
能加层,就不要改代码
动态代理
思想本质:以前的静态代理:房东(接口)要出租房子(接口),需要找到中介(代理类).然后中介通过传进来的对象,调用传进来的对象的方法.这个就是静态代理
//接口有一个方法,需要实现
public interface UserService {
void add();
void delete();
void update();
void query();
}
动态代理本质就是静态代理中的代理类,动态生成了.
还是房东,要出租房子.本来应该需要一个代理类的.
package com.changGe.dynamicProxy;
//实现了接口的方法
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("add执行中");
}
@Override
public void delete() {
System.out.println("delete执行中");
}
@Override
public void update() {
System.out.println("update执行中");
}
@Override
public void query() {
System.out.println("query执行中");
}
}
但是现在有个中间的工具类,实现了Invacationhanderl接口下的invoke方法.
这个方法中有三个参数:object不用管,method(就是接口中的方法),还有args(参数列表).
动态代理对象调用方法时,就自动调用的这个方法.method.invoke(这个方法要执行)(object(bject(执行哪个对象的),args(要执行的参数));
最后method.invoke返回我们一个object对象,这个不就是方法执行完后的返回值吗?
而Proxy.newProxyIntanfice(this.getClass.getClassloader(房东),target.getintanfices(接口),this(invaicationhander)),最后 的返回值就是我们要的 动态生成的 代理类.
package com.changGe.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//这是一个代理工具类:用来动态生成代理对象
public class Dynamic implements InvocationHandler {
//要实现的接口
private Object target;
public void setTarget(Object target){
this.target = target;
}
//传入真实角色,要动态代理的接口,怎么代理(就是我们自己的动态代理工具类)
//返回一个动态代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),
this);
}
//ScriptKill被动态代理后,实际是调用这个方法,来动态代理他本来要实现的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//对代理方法进行增强
System.out.println(method.getName()+"方法被执行了");
//这个方法执行后,会有一个object类型的返回值
Object invoke = method.invoke(target, args);
System.out.println(method.getName()+"方法执行完了");
return invoke;
}
}
最后动态生成的代理类调用方法,就是和静态代理一样了.
package com.changGe.dynamicProxy;
public class Test {
public static void main(String[] args) {
//真实角色
UserServiceImpl user = new UserServiceImpl();
//动态代理工具类
Dynamic dynamic = new Dynamic();
dynamic.setTarget(user);
//获取动态代理对象
UserService proxy = (UserService)dynamic.getProxy();
//动态代理对象调用方法
/**
* add方法被执行了
* add执行中
* add方法执行完了
*/
proxy.add();
}
}
如果我们改变了其中的target,就是修改了接口,而这个接口一般是一类业务的方法总和.
所以我们修改其中的target,就是修改了一类业务
同时,因为最后其实调用的invoke方法,我们就可以在这个方法执行的前后,增加 *** 作了,这就是方法增强
这是Spring AOP(面向切面编程)的原理
AOP面向切面编程
实际作用就是:不改变原有代码的情况下,对原有代码进行增强
通过预编译方式,和运行期动态代理,实现程序功能的 统一维护
提供声明式事务,允许用户自定义切面
AOP织入依赖
org.aspectj.aspectjweaver1.9.4
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.8.RC1version>
dependency>
实现方式
Spring的原生api接口实现
UserService实现了接口User
public interface User {
void add();
void delete();
void query();
}
package com.changGe.li.pojo;
import com.changGe.li.pojo.User;
public class UserImpl implements User{
@Override
public void add() {
System.out.println("add实现了");
}
@Override
public void delete() {
System.out.println("delete实现了");
}
@Override
public void query() {
System.out.println("query实现了");
}
}
自定义类实现方法增强的接口,如MethodBeforeAdvice(切入点之前),AfterRetruningActive(切入点返回值后)
package com.changGe.li.pojo;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//建议之前的方法:切入点之前的方法
public class LogBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("切入点之前的方法");
}
public void beforeTest(){
System.out.println("切入点之前的方法的测试方法");
}
}
package com.changGe.li.pojo;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
//afterReturningAdvice返回值后的建议:切入点返回值之后的方法
public class LogAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("切入点返回值之后的方法");
}
public void beforeTest(){
System.out.println("切入点返回值之后的方法的测试方法");
}
}
beans.xml中导入aop依赖:可以直接
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
配置包扫描
<context:component-scan base-package="com.changGe.li.pojo"/>
<context:annotation-config/>
配置bean和aop织入
<bean id="userService" class="com.changGe.li.pojo.UserImpl"/>
<bean id="before" class="com.changGe.li.pojo.LogBefore"/>
<bean id="after" class="com.changGe.li.pojo.LogAfter"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.changGe.li.pojo.User.*(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
aop:config>
测试的时候,spring动态代理的接口,所以application.getBean时,传参与返回值必须是接口类型的
ApplicationContext application = new ClassPathXmlApplicationContext("beans.xml");
User user = application.getBean("userService",User.class);
user.add();
aop切面定义
自定义切面类
package com.changGe.li.pojo;
public class Diy {
public void before(){
System.out.println("这是before方法");
}
public void after(){
System.out.println("这是after方法");
}
}
引用切面类的增强方法
<bean id="userService" class="com.changGe.li.pojo.UserImpl"/>
<bean id="diy" class="com.changGe.li.pojo.Diy"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="pointcut" expression="execution(* com.changGe.li.pojo.User.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
aop:aspect>
aop:config>
User user = application.getBean("userService",User.class);
user.add();
//运行结果
这是before方法
add实现了
这是after方法
注解aop
package com.changGe.li.pojo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class Diy {
@Before("execution(* com.changGe.li.pojo.User.*(..))")
public void before(){
System.out.println("这是before方法");
}
@After("execution(* com.changGe.li.pojo.User.*(..))")
public void after(){
System.out.println("这是after方法");
}
@After("execution(* com.changGe.li.pojo.User.*(..))")
public void after1(){
System.out.println("这是after方法1");
}
//环绕增强:代理对象执行方法的时候,把所有的增强方法都执行了一遍
@Around("execution(* com.changGe.li.pojo.User.*(..))")
//point代表获取被切入的点
public void around(ProceedingJoinPoint point){
System.out.println("环绕前");
//获取签名,也就是方法名()
Signature signature = point.getSignature();
System.out.println("签名是:"+signature);
Object proceed = null;
try {
//执行方法
proceed = point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("环绕后");
System.out.println("返回值是:"+proceed);
}
}
开启注解增强,就不用配置其他的aop织入了
<aop:aspectj-autoproxy/>
//运行结果:可以看出:
//就是在代理对象执行方法的时候,把所有的增强方法都执行了一遍
环绕前
签名是:void com.changGe.li.pojo.User.add()
这是before方法
add实现了
这是after方法1
这是after方法
环绕后
返回值是:null
整合MyBatis
Access denied for user ‘’@‘localhost’ (using password: YES)错误解决方法
(7条消息) MySQL Access denied for user ‘root‘@‘localhost‘ (using password: YES/NO) 的原因以及解决方案_花花花菜的博客-CSDN博客
加上mybatis-spring和spring-jdbc的依赖
基本的mybatis和mysql的依赖,可能会用到的都可以导入一遍
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.13version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.27version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
package com.changGe.li.pojo;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class Student {
private String id;
private String name;
private int tid;
}
package com.changGe.li.pojo;
import java.util.List;
public interface StudentMapper {
List<Student> getStudentList();
}
jdbc.properties
driver=com.mysql.cj.jdbc.Driver
username=root
password=root
url=jdbc:mysql:///mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC
mybatis-config.xml
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.changGe.li.pojo"/>
typeAliases>
<mappers>
<package name="com/changGe/li/pojo/StudentMapper.xml"/>
mappers>
configuration>
StudentMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.changGe.li.pojo.StudentMapper">
<select id="getStudentList" resultType="student">
select * from student;
select>
mapper>
beans.xml整合mybatis配置
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${password}"/>
<property name="password" value="${password}"/>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/changGe/li/pojo/StudentMapper.xml"/>
bean>
beans>
测试类
import com.changGe.li.pojo.Student;
import com.changGe.li.pojo.StudentMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class SpringTest {
@Test
public void sprintTest(){
try {
ApplicationContext application = new ClassPathXmlApplicationContext("beans.xml");
SqlSessionFactory sqlSessionFactory = application.getBean("sqlSessionFactory", SqlSessionFactory.class);
/*//这里跟随主配置文件而加载spring的配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("beans.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);*/
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudentList();
for (Student student : studentList) {
System.out.println(student.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
SqlSessionTemplete
SqlSessonTemplete(SqlSession模板),可以通过SqlSessionFactroy直接构建SqlSession对象
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/changGe/li/pojo/StudentMapper.xml"/>
bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
bean>
//sqlSessionTemplete就是一个qlSession
SqlSession sqlSession = application.getBean("sqlSessionTemplate", SqlSession.class);
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudentList();
for (Student student : studentList) {
System.out.println(student.toString());
}
SqlSessionTemplete是线程安全的,他的构造方法的参数可以用sqlsessionfactroy
//SqlSessionTemplete构造器源码
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
在这里我们可以把spring.xml再次拆分,变成一个专注配置,一个专注管理bean.
bases.xml专注于配置
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${password}"/>
<property name="password" value="${password}"/>
bean>
beans>
beans.xml专注于bean管理,通过导入bases.xml实现整合mybatis
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="bases.xml"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/changGe/li/pojo/StudentMapper.xml"/>
bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
bean>
beans>
SqlSessionDaosupport:SqlSessionDao支持
package com.changGe.li.pojo;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class StudentMapperImpl extends SqlSessionDaoSupport implements StudentMapper{
//SqlSessionDaoSupport的源码是:return this.sqlSessionTemplate;
private SqlSession sqlSession = this.getSqlSession();
private StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
@Override
public List<Student> getStudentList() {
return mapper.getStudentList();
}
}
直接测试会报错:NullPointException
@Test
public void sprintTest(){
StudentMapper studentMapper = new StudentMapperImpl();
List<Student> studentList = studentMapper.getStudentList();
for (Student student : studentList) {
System.out.println(student.toString());
}
}
因为SqlSessionDaoSupport,底层需要注入一个SqlSessionFactroy
getSqlSession时会调用他自己的sqlSessionTemplate,但是这个变量初始并没有赋值
public final SqlSessionFactory getSqlSessionFactory() {
return (this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null);
}
private SqlSessionTemplate sqlSessionTemplate;
他希望子类是通过传入SqlSessionFactroy的方式,创建一个SqlSessionTemplate
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
正确的用法应该是这样
package com.changGe.li.pojo;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class StudentMapperImpl extends SqlSessionDaoSupport implements StudentMapper{
@Override
public List<Student> getStudentList() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = applicationContext.getBean("sqlSessionFactory", SqlSessionFactory.class);
SqlSessionTemplate sqlSessionTemplate = this.createSqlSessionTemplate(sqlSessionFactory);
//获取mapper
StudentMapper mapper = sqlSessionTemplate.getMapper(StudentMapper.class);
return mapper.getStudentList();
}
}
或者直接在beans.xml中配置sqlSessionFactroy对象
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/changGe/li/pojo/StudentMapper.xml"/>
bean>
<bean id="studentMapperImpl" class="com.changGe.li.pojo.StudentMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
bean>
Spring会自动注入SqlSessionDaoSupport需要的SqlSessionFactroy
我们也可以在实现接口的时候,加入我们的代码了
public class StudentMapperImpl extends SqlSessionDaoSupport implements StudentMapper{
@Override
public List<Student> getStudentList() {
StudentMapper mapper = this.getSqlSession().getMapper(StudentMapper.class);
//这里可以变成其他业务代码
System.out.println("Hello Spring");
return mapper.getStudentList();
}
}
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
StudentMapper studentMapper = applicationContext.getBean("studentMapperImpl", StudentMapperImpl.class);
List<Student> studentList = studentMapper.getStudentList();
for (Student student : studentList) {
System.out.println(student.toString());
}
//运行结果
Hello Spring
Student(id=1, name=天才第二步, tid=1)
声明式事务
Spring配置的dataSource,默认关闭事务(MyBatis在配置环境时,必须配置transactionManager type=“jdbc”/>,默认是开启事务的)
所以下面的这些代码,即使有明显的异常,最终inster也会成功.
Student实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private String id;
private String name;
private int tid;
}
StudentMapper
public interface StudentMapper {
int insertStudent(Student student);
int deleteStudentById(@Param("id")String id);
void query();
}
StudentMapper实现类,运行insertStudent方法时,必定产生一个异常
public class StudentMapperImpl extends SqlSessionDaoSupport implements StudentMapper{
@Override
public int insertStudent(Student student) {
StudentMapper mapper = this.getSqlSession().getMapper(StudentMapper.class);
int i = mapper.insertStudent(student);
//java.lang.ArithmeticException: / by zero
i = 1 / 0;
return i;
}
@Override
public int deleteStudentById(String id) {
StudentMapper mapper = this.getSqlSession().getMapper(StudentMapper.class);
return mapper.deleteStudentById(id);
}
@Override
public void query() {}
}
StudentMapper.xml
<insert id="insertStudent">
insert into student values(#{id},#{name},#{tid})
</insert>
<delete id="deleteStudentById">
delete from student where id = #{id};
</delete>
<select id="query"></select>
测试
StudentMapper studentMapper = applicationContext.getBean("studentMapperImpl", StudentMapper.class);
Student student = new Student("14","路飞",1);
studentMapper.insertStudent(student);
studentMapper.deleteStudentById("1");
最后insert还是被执行了
这时我们就需要配置事务是为了:保证ACID原则
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
bean>
导入aop和tx约束
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.ArithmeticException"/>
<tx:method name="insertStudent" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="deleteStudentById" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="query" read-only="true"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="point" expression="execution(* com.changGe.li.pojo.*.*(..))"/>
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="point"/>
aop:config>
spring事务传播属性
Spring事务的传播:PROPAGATION_REQUIRED - 神只吃苹果 - 博客园 (cnblogs.com)
在 spring的 TransactionDefinition接口中一共定义了六种事务传播属性:
PROPAGATION_REQUIRED – 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS – 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY – 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW – 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED – 以非事务方式执行 *** 作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER – 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED – 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的 *** 作。
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)
事务的隔离级别
(7条消息) spring 中 isolation 和 propagation 详解_饥饿小猪的博客-CSDN博客
- ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应
- ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
- ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
- ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)