学习资源:
b站:遇见狂神说
链接:https://www.bilibili.com/video/BV1WE411d7Dv/?spm_id_from=333.999.0.0
1.IOC(控制反转)
- 在spring中实现控制反转的是IOC容器,其实现方式由DI依赖注入,控制反转是一种设计思想,DI依赖注入是实现IOC的一种方法
- 使用spring前,对象的创建和由程序员决定,使用spring后,对象由spring创建,管理和装配
HelloWorld
创建maven项目,引入相关依赖:
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
创建spring核心配置文件applicationContext.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">
<!--使用spring创建对象,这些对象称为bean-->
<!--将对象交给spring来床架和管理,我们只需要从IOC容器中取出即可-->
<bean id="hello" class="com.yang.pojo.HelloWorld">
<!--property给属性赋值-->
<property name="str" value="helloWorld" />
</bean>
</beans>
测试类:
package com.yang.pojo;
public class HelloWorld {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "HelloWorld{" +
"str='" + str + '\'' +
'}';
}
}
测试代码:
@Test
public void test(){
// 因为对象由spring创建在容器中,先获取spring配置文件(获取spring容器),读取xml配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过容器获取bean(对象),这里根据id获取
HelloWorld hello = (HelloWorld) applicationContext.getBean("hello");
System.out.println(hello);
}
理解:对象的创建在spring容器初始化的时候就创建了。对象的创建,管理和装配由spring来完成,我们可以把核心配置文件理解为spring容器,容器里面存放bean(对象),我们可以从容器中取出bean并使用
2.依赖注入
- 构造器注入
- spring创建对象,使用有参和无参构造的方式创建对象
- set方式注入(重点)
- 通过类的属性的set方法实现注入
2.1、构造器注入
- 无参构造方式注入(默认)
- 有参构造方式注入
- c命名空间和p命名空间注入
User类:
package com.yang.pojo;
public class User {
private String name;
public User() {
System.out.println("User无参构造执行...");
}
public User(String name) {
this.name = name;
System.out.println("User有参构造执行...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
核心配置文件(使用无参构造方式注入):
<!--无参构造方式注入-->
<bean id="user" class="com.yang.pojo.User">
<property name="name" value="tom"/>
</bean>
核心配置文件(使用有参构造方式注入):
<!--有参构造注入-->
<bean id="user" class="com.yang.pojo.User">
<constructor-arg name="name" value="jack" />
</bean>
测试:
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User)applicationContext.getBean("user");
System.out.println(user);
}
2.2、set注入
Student类:
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
'}';
}
}
Address类:
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
核心配置文件:
<!--id与下面的ref对应-->
<bean id="address" class="com.yang.pojo.Address">
<property name="address" value="广西玉林" />
</bean>
<bean id="student" class="com.yang.pojo.Student">
<!--普通属性注入-->
<property name="name" value="jack"/>
<!--对象类型属性注入,使用ref引用id为address的bean-->
<property name="address" ref="address" />
<!--数组类型属性注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--list集合类型属性注入-->
<property name="hobbys">
<list>
<value>唱</value>
<value>跳</value>
<value>rap</value>
<value>篮球</value>
</list>
</property>
<!--map集合类型属性注入-->
<property name="card">
<map>
<entry key="身份证" value="1414124141241241" />
<entry key="银行卡" value="1414122344124124" />
</map>
</property>
</bean>
测试:
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student)applicationContext.getBean("student");
System.out.println(student);
}
2.3、c命名空间和p命名空间注入
实体类:
package com.yang.pojo;
public class User {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
核心配置文件导入约束(beans标签内):
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
注入spring容器:
<!--p命名空间方式注入,该方式本质是通过set方式注入-->
<bean id="user" class="com.yang.pojo.User" p:name="jack" p:age="20" />
<!--c命名空间方式注入,该方式本质是通过构造器方式注入-->
<bean id="user02" class="com.yang.pojo.User" c:name="tom" c:age="30" />
测试:
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean("user", User.class);
User user02 = applicationContext.getBean("user02", User.class);
System.out.println(user);
System.out.println(user02);
System.out.println(user == user02);
}
总结:
- p命名空间方式注入本质是通过set方式注入
- c命名空间方式注入本质是通过构造器方式注入
3、bean的作用域
- singleton:单例模式(spring默认)
- prototype:原型模式
核心配置文件:
<!--单例模式singleton:每次get同一个bean时,为同一个对象-->
<bean id="user03" class="com.yang.pojo.User" p:name="smith" p:age="20" scope="singleton" />
<!--原型模式prototype:每次get同一个bean时,为不同的对象-->
<bean id="user04" class="com.yang.pojo.User" p:name="milan" p:age="20" scope="prototype" />
测试:
// 测试单例
@Test
public void test02(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean("user03", User.class);
User user02 = applicationContext.getBean("user03", User.class);
System.out.println(user==user02); //true
}
// 测试原型
@Test
public void test03(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean("user04", User.class);
User user02 = applicationContext.getBean("user04", User.class);
System.out.println(user==user02); //false
}
总结:单例模式下,每次从容器中获取bean时,都为同一个对象,原型模式下,每次从容器中获取bean时,获取不同对象
4、bean自动装配
spring三种装配bean的方式:
- 使用核心配置文件显示配置
- 使用java代码显示配置
- 隐式自动装配
- byName自动装配
- byType自动装配
为了方便测试,导入lombok依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
Dao类:
package com.yang.pojo;
public class Dog {
public void cry(){
System.out.println("狗叫");
}
}
Cat类:
package com.yang.pojo;
public class Cat {
public void cry(){
System.out.println("猫叫");
}
}
Person类:
package com.yang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private Cat cat;
private Dog dog;
private String name;
}
4.1、byName自动装配
核心配置文件:
<bean id="cat" class="com.yang.pojo.Cat" />
<bean id="dog" class="com.yang.pojo.Dog" />
<bean id="person" class="com.yang.pojo.Person" autowire="byName">
<property name="name" value="jack"/>
<!--<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>-->
</bean>
4.2、byType自动装配
核心配置文件:
<bean id="cat" class="com.yang.pojo.Cat" />
<bean id="dog" class="com.yang.pojo.Dog" />
<bean id="person" class="com.yang.pojo.Person" autowire="byType">
<property name="name" value="jack"/>
<!--<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>-->
</bean>
测试:
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = applicationContext.getBean("person", Person.class);
person.getCat().cry();
person.getDog().cry();
}
结论:
- byName自动装配:根据当前对象属性的set方法名字和spring容器中对应的bean的id匹配
- byType自动装配:根据当前对象属性的类型和spring容器中对应的bean的类型匹配
注意:
- 使用byName自动装配时,需要保证bean的id唯一,并且bean的id和被注入属性的bean的set方法值一致
- 使用byType自动装配时,需要保证bean的类型唯一,并且bean的类型和被注入属性的bean的属性类型一致
5、注解实现自动装配
使用注解开发的前提是在核心配置文件中配置:
- 导入约束
- 开启注解的支持
核心配置文件:
<?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:p="http://www.springframework.org/schema/p"
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
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解支持-->
<context:annotation-config />
<bean id="dog" class="com.yang.pojo.Dog" p:name="大黄"/>
<bean id="person" class="com.yang.pojo.Person" p:name="jack"/>
</beans>
使用@AutoWired注解实现自动装配:
@Data
public class Person {
private String name;
@Autowired
private Dog dog;
}
核心配置文件:
<bean id="dog" class="com.yang.pojo.Dog" p:name="大黄"/>
<bean id="person" class="com.yang.pojo.Person" p:name="jack"/>
测试:
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = applicationContext.getBean("person", Person.class);
System.out.println(person);
}
当同一个类型有多个bean时,想指定唯一的bean进行注入时,可以搭配@Qualifier注解进行使用
当同一类型有多个bean:
<bean id="dog" class="com.yang.pojo.Dog" p:name="大黄"/>
<bean id="dog02" class="com.yang.pojo.Dog" p:name="大黑"/>
指定唯一bean:
@Autowired
@Qualifier(value = "dog02") // value值与bean的id对应
private Dog dog;
6、Spring注解开发
- @Component:将类注入到spring容器中
- @Scope:配置bean的作用域
核心配置文件:
<!--开启注解支持-->
<context:annotation-config/>
<!--扫描指定包下支持spring注解开发-->
<context:component-scan base-package="com.yang"/>
User类:
package com.yang.pojo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
// 将该类注入到spring容器中
@Component
// 作用域
@Scope("prototype")
@Data
public class User {
// 给普通属性注入值
@Value("tom")
public String name;
}
测试:
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user.name);
}
在web开发中@Component注解还有几个类似的注解:
- @Repository
- @Controller
- @Service
这几个注解分别应用在MVC三层架构中,虽然是不同的注解,但是功能都是注入到spring容器中,由spring管理
7、使用java类实现Spring配置
使用java类和注解实现spring的配置,可以脱离核心配置文件,但是可以实现同样的效果
创建spring配置类:
package com.yang.config;
import com.yang.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration // 表示该类是spring的配置类
@ComponentScan("com.yang.pojo") // 开启组件扫描
// @Import(SpringConfig.class) // 引入多个配置类
public class SpringConfig {
// 注册bean到spring容器中
@Bean
public User getUser(){
return new User();
}
}
配置类2:
package com.yang.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig02 {
}
User类:
package com.yang.pojo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component
public class User {
@Value("jack")
private String name;
}
测试:
@Test
public void test(){
// 因为是通过配置类的形式让spring管理bean,所有使用AnnotationConfigApplicationContext形式获取spring容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
// 开启组件扫描后,可以通过方法名或者小写字母开头的类名两种方式获取
User user = applicationContext.getBean("user", User.class);
System.out.println(user.getName());
}
注意:
- 当spring配置类没有开启组件扫描时,获取bean的方式需要通过注册bean时的方法名来获取
- 开启组件扫描后,可以通过方法名或者小写字母开头的类名两种方式获取bean
8、AOP(面向切面编程)
代理模式是AOP的底层实现,所以需要学习代理模式,AOP底层使用的是动态代理(重点)
代理模式:
- 静态代理
- 动态代理
8.1、静态代理
例子:张三租房子
租房的接口:
package com.yang;
public interface RentingAHouse {
void rentingAHouse();
}
房东类:
package com.yang;
// 房东实现出租房子的接口,因为房东需要出租房子
public class Master implements RentingAHouse{
@Override
public void rentingAHouse() {
System.out.println("房东出租房子");
}
}
中介类:
package com.yang;
// 中介
public class Agent implements RentingAHouse{
private Master master;
public Agent() {
}
public Agent(Master master) {
this.master = master;
}
@Override
public void rentingAHouse() {
// 中介在帮房东出租房子时做的一些流程
System.out.println("中介带你看房");
master.rentingAHouse();
System.out.println("中介收取中介费");
System.out.println("租房完成");
}
}
张三找中介租房:
package com.yang;
// 张三
public class Person {
public static void main(String[] args) {
// 中介帮帮房东租房
Agent agent = new Agent(new Master());
agent.rentingAHouse();
}
}
总结:
- 房东只管出租房子
- 张三只管租房子
- 中介帮房东出租房子,期间走一些流程
好处:
- 真实角色(房东)的操作(租房子)更加明确
- 公共的任务(看房子、签合同、收中介费等)交给代理角色(中介)
- 分工明确,当期间需要发生业务的拓展时,方便管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量会增加,开发效率变低
8.2、动态代理
- 动态代理角色和静态代理角色一样
- 动态代理角色是动态生成的
- 动态代理分为两类:基于接口实现(java底层实现)和基于类实现
了解两个类:
- InvocationHandler(接口):代理对象关联的调用处理程序所实现的接口
- 每一个代理对象都有一个关联的调用处理程序,当代理对象调用方法时,该方法将被编码并分配到其关联的调用处理程序的invoke方法
- Proxy:用来生成代理对象的类
接口:
package com.yang.demo02;
public interface RentingAHouse {
void rentingAHouse();
}
房东类(房东需要实现接口完成出租房子的功能):
package com.yang.demo02;
// 房东
public class Master implements RentingAHouse {
@Override
public void rentingAHouse() {
System.out.println("房东出租房子");
}
}
代理对象所关联的调用处理程序:
package com.yang.demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 该类用于处理代理对象调用的方法(代理对象关联的调用处理程序)
public class ProxyInvocationHandler implements InvocationHandler {
// 保存被代理对象,需要通过改对象获取实现的接口,和执行被代理对象需要执行的方法(房东出租房子)
private RentingAHouse rentingAHouse;
public void setRentingAHouse(RentingAHouse rentingAHouse) {
this.rentingAHouse = rentingAHouse;
}
/**
* newProxyInstance(ClassLoader loader, // 代理对象关联的调用处理程序(实现了InvocationHandler接口的类)的类加载器
* Class<?>[] interfaces, // 代理对象需要实现的接口
* InvocationHandler h) // 代理对象关联的调用处理程序
*/
public Object getProxy(){ // 该方法用于获取代理对象,该代理对象是动态生成的
return Proxy.newProxyInstance(ProxyInvocationHandler.this.getClass().getClassLoader(),rentingAHouse.getClass().getInterfaces(),
ProxyInvocationHandler.this);
}
// 当代理对象调用方法时,会执行该invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("中介带你看房子");
// 通过反射调用被代理对象的方法(房东出租房子)
Object result = method.invoke(rentingAHouse, args);
System.out.println("中介收取中介费");
System.out.println("房子出租完成");
return result;
}
}
测试类:
package com.yang.demo02;
public class Test {
public static void main(String[] args) {
// 现在来动态获取代理对象
// 1 获取被代理对象(真实角色)
Master master = new Master();
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
// 2 设置代理对象需要实现的接口(代理对象需要实现被代理对象所实现的接口)
proxyInvocationHandler.setRentingAHouse(master);
// 3 生成代理对象
RentingAHouse proxy = (RentingAHouse)proxyInvocationHandler.getProxy();
// 4 执行代理对象所关联的调用处理程序的invoke方法
proxy.rentingAHouse();
}
}
8.3、Spring实现AOP
8.3.1、spring原生API实现AOP
添加依赖:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
UserService接口:
package com.yang.service;
public interface UserService {
void add();
void delete();
void update();
void query();
}
UserServiceImpl类:
package com.yang.service;
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("添加用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("修改用户信息");
}
@Override
public void query() {
System.out.println("查询用户信息");
}
}
前置通知(实现MethodBeforeAdvice接口):
package com.yang.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
// MethodBeforeAdvice:前置通知
public class BeforeLog implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(method.getName() + "方法开始执行...");
}
}
后置通知(实现AfterReturningAdvice接口):
package com.yang.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
// 后置通知
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(method.getName() + "方法执行后...");
}
}
核心配置文件加入aop约束:
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
注册bean和实现aop:
<bean id="userService" class="com.yang.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.yang.log.BeforeLog"/>
<bean id="afterLog" class="com.yang.log.AfterLog"/>
<!--配置aop:需要导入aop约束-->
<aop:config>
<!--设置切入点-->
<!--expression:设置切点表达式-->
<!--切点表达式:execution(* com.yang.service.UserServiceImpl.*(..))-->
<!--
*:表示方法修饰符和返回值类型任意
com.yang.service.UserServiceImpl:方法所在类的全类名
*(..):指定方法,以及方法的参数类型
-->
<aop:pointcut id="pointcut" expression="execution(* com.yang.service.UserServiceImpl.*(..))"/>
<!--切入日志功能-->
<!--ref:日志功能,pointcut-ref:选择切入点-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
测试:
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过接口的类型获取,因为动态代理对象,代理的是接口
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.add();
userService.delete();
}
结果:在执行add方法和delete方法的前后,会输出相应日志
8.3.2、自定义切面类实现AOP
自定义切面类:
package com.yang.log;
// 自定义切面类
public class Log {
public void after(){
System.out.println("方式执行后");
}
public void before(){
System.out.println("方式执行前");
}
}
核心配置文件:
<bean id="userService" class="com.yang.service.UserServiceImpl" />
<bean id="log" class="com.yang.log.Log"/>
<aop:config>
<!--自定义切面,ref要引用的切面类-->
<aop:aspect ref="log">
<!--设置切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.yang.service.UserServiceImpl.*(..))"/>
<!--before:方法执行前,method的值对应的是切面类的方法名-->
<aop:before method="before" pointcut-ref="pointcut" />
<!--after:方法执行后-->
<aop:after method="after" pointcut-ref="pointcut" />
</aop:aspect>
</aop:config>
8.3.3、使用注解实现AOP
自定义切面类,使用注解的方式实现:
package com.yang.diy;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect // 表示该类是一个切面类
public class AnnotationPointCut {
@Before("execution(* com.yang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("注解实现AOP,方法执行前...");
}
@After("execution(* com.yang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("注解实现AOP,方法执行后...");
}
}
核心配置文件:
<bean id="userService" class="com.yang.service.UserServiceImpl" />
<bean id="annotationPointCut" class="com.yang.diy.AnnotationPointCut" />
<!--开启注解支持aop-->
<aop:aspectj-autoproxy />
9、整合Mybatis
- 导入需要的依赖
- 准备实体类,接口,mapper
- spring核心配置文件中整合mybatis核心配置文件(重点)
- 创建接口的实现类
- 测试
需要的依赖:
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--spring操作数据库需要的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--spring整合mybatis需要的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
实体类:
package com.yang.pojo;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private String pwd;
}
接口:
package com.yang.mapper;
import com.yang.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> queryUserList();
}
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="com.yang.mapper.UserMapper">
<select id="queryUserList" resultType="user">
select * from user
</select>
</mapper>
mybatis核心配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--设置别名-->
<typeAliases>
<package name="com.yang.pojo"/>
</typeAliases>
</configuration>
spring核心配置文件:
<?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">
<!--DataSource:使用spring的数据源替换mybatis的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charsetEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--注册spring代替mybatis关联mapper-->
<property name="mapperLocations" value="classpath:com/yang/mapper/*.xml"/>
</bean>
<!--SqlSessionTemplate:在spring中,我们需要使用SqlSessionTemplate操作数据库-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!--通过有参构造的方式注入,name为构造器参数名,value为实际的sessionFactory实例-->
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapperImpl" class="com.yang.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
</beans>
接口对应的实现类:
package com.yang.mapper;
import com.yang.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
@Override
public List<User> queryUserList() {
UserMapper userMapper = sqlSessionTemplate.getMapper(UserMapper.class);
return userMapper.queryUserList();
}
}
测试:
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapperImpl userMapperImpl = applicationContext.getBean("userMapperImpl", UserMapperImpl.class);
List<User> userList = userMapperImpl.queryUserList();
for (User user : userList) {
System.out.println(user);
}
}
方式2整合mybatis
- 继承SqlSessionDaoSupport类使用getSqlSession()可以获取sqlSession
接口实现类继承SqlSessionDaoSupport类:
package com.yang.mapper;
import com.yang.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> queryUserList() {
// 使用父类SqlSessionDaoSupport的getSqlSession()可以获取sqlSession
return getSqlSession().getMapper(UserMapper.class).queryUserList();
}
}
在spring中为父类属性注入sqlSessionFactory:
<!--为父类属性注入sqlSessionFactory实例-->
<bean id="userMapperImpl" class="com.yang.mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
总结:
- 使用spring整合mybatis后,配置数据库的信息由spring来管理
- 操作数据库需要的对象以及接口实现类由spring来管理和创建
10、事务
实体类User:
package com.yang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String pwd;
}
接口:
package com.yang.mapper;
import com.yang.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper {
List<User> queryUserList();
int addUser(User user);
int deleteUser(@Param("uid") Integer id);
}
mapper:
<select id="queryUserList" resultType="user">
select * from user
</select>
<insert id="addUser" parameterType="user">
insert into user values (#{id},#{name},#{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{uid}
</delete>
spring核心配置文件:
<!--DataSource:使用spring的数据源替换mybatis的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charsetEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--注册spring代替mybatis关联mapper-->
<property name="mapperLocations" value="classpath:com/yang/mapper/*.xml"/>
</bean>
<bean id="userMapper" class="com.yang.mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!--配置声明式事务,需要传入一个数据源dataSource-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给指定方法配置事务管理-->
<tx:attributes>
<!--增删改操作设置事务-->
<!-- <tx:method name="add" propagation="REQUIRED"/>-->
<!-- <tx:method name="delete" propagation="REQUIRED"/>-->
<!-- <tx:method name="update" propagation="REQUIRED"/>-->
<!--查询方法设置为只读-->
<!-- <tx:method name="query" read-only="true"/>-->
<!--指定所有方法都配置事务-->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--设置事务切入,结合aop-->
<aop:config>
<!--切入点-->
<aop:pointcut id="txPointcut" expression="execution(* com.yang.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
接口实现类:
package com.yang.mapper;
import com.yang.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> queryUserList() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
addUser(new User(4,"milan","123"));
// 自定义错误
int num = 10 / 0;
deleteUser(3);
return mapper.queryUserList();
}
@Override
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
@Override
public int deleteUser(Integer id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
测试:
@Test
public void test02(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = applicationContext.getBean("userMapper", UserMapper.class);
userMapper.queryUserList();
}
结果:当调用测试中queryUserList方法时,调用addUser和deleteUser方法,当声明事务管理后,在出现错误时addUser方法执行的操作会回滚,当整个queryUserList方法的执行中没有出现错误,事务才会提交