天天看点

Spring学习笔记Spring

Spring

1、简介

  • 历史:在2002年,首次推出了Spring框架的雏形:interface21框架。而Spring框架就是以interface21框架为基础,经过重新设计并不断丰富其内涵,在2004年3月24日发布了1.0正式版
  • 创始人:Rod Johnson,著名作者,专业是音乐学而非计算机
  • 理念:整合了现有的技术框架,使现有的技术更加容易使用,本身是一个大杂烩
  • 优点:
    • 开源的免费的框架(容器)
    • 轻量级的、非入侵式的框架
    • 最大特点是控制反转(IOC)、面向切面编程(AOP)
    • 支持事务处理和框架整合
  • 弊端:发展了太久之后,违背了原来的理念,配置十分繁琐
  • 组成:
    Spring学习笔记Spring
  • 拓展
    • Spring官网的介绍:现代的Java开发,其实就是基于Spring的开发
Spring学习笔记Spring
  • Spring Boot
    • 一个快速开发的脚手架,基于SpringBoot可以快速的开发单个微服务
    • 约定大于配置
    • 大多数公司使用,学习前提是完全掌握Spring及SpringMVC
  • Spring Cloud
    • 基于SpringBoot实现

官网:https://spring.io/projects/spring-framework#overview

官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html

官方下载地址:https://repo.spring.io/release/org/springframework/spring/

Github:https://github.com/spring-projects/spring-framework

2、IOC

2.1 理论推导

问题:在之前的业务中,用户的需求可能会影响原代码,需要根据用户的需求去修改原代码,如果程序代码量非常大,修改一次的成本代价非常昂贵

改进:使用set接口

private UserDao userDao;

public void setUserDao(UserDao userDao){
    this.userDao = userDao;
}
           
  • 改进之前,程序主动创建对象,控制权在程序员手上
  • 改进之后,程序不再具有主动性,变成了被动地接受对象
  • 这种思想从本质上解决了问题,程序员不用再管理对象的创建,可以更加专注于业务实现,系统耦合性大大降低。这是IOC原型

2.2 本质

控制反转(IoC)是一种设计思想,DI(依赖注入)是实现IoC的一种方法。在没有IoC的程序中使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制;IoC是将对象的创建转移给第三方。通俗来说,控制反转是一种通过描述(XML或者注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方式是依赖注入(Dependency Injection,DI)

Spring学习笔记Spring
  • 配置:IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC
    • 两种配置方式的区别:采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的
  • 原理:Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IoC容器中取出需要的对象

3、HelloSpring

直接给类成员变量赋值测试:

  1. 导入Spring所需依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
           
  1. 建立实体类,编写get、set方法
public class Hello {
    private String str;

    public void setStr(String str){
        this.str=str;
    }

    public String getStr(){
        return str;
    }

    @Override
    public String toString() {
        return "Hello{" +
                "str='" + str + '\'' +
                '}';
    }
}
           
  1. 编写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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="hello" class="com.Nana.pojo.Hello">
        <property name="str" value="hello"></property>
    </bean>

</beans>
           
  1. 测试
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
        Hello hello = (Hello)classPathXmlApplicationContext.getBean("hello");
        System.out.println(hello);
    }
}
           

先注册再引用核心代码:

<bean id="mysqlImpl" class="com.Nana.dao.UserDaoMysql"></bean>
<bean id="oracleImpl" class="com.Nana.dao.UserDaoOracle"></bean>

<bean id="UserServiceImpl" class="com.Nana.service.UserServiceImpl">
    <property name="userDao" ref="mysqlImpl"></property>
</bean>
           
  • ref:引用Spring容器中创建好的对象
  • value:适用于基本数据类型,给成员变量赋值

ClassPathXmlApplicationContext和ApplicationContext的继承关系:

Spring学习笔记Spring

4、IOC创建对象的方式

  1. 无参构造(默认)
<bean id="user" class="com.Nana.pojo.User">
    <property name="name" value="海燕酱"></property>
</bean>
           
  1. 有参构造
    • 使用下标赋值
    <bean id="user" class="com.Nana.pojo.User">
        <constructor-arg index="0" value="海燕酱"></constructor-arg>
    </bean>
               
    • 使用类型赋值(不建议使用)
    <bean id="user" class="com.Nana.pojo.User">
        <constructor-arg type="java.lang.String" value="海燕酱"></constructor-arg>
    </bean>
               
    • 通过参数名赋值(推荐)
    <bean id="user" class="com.Nana.pojo.User">
        <constructor-arg name="name" value="海燕酱"></constructor-arg>
    </bean>
               

注意:在配置文件加载的时候,容器中管理的对象就已经初始化了

5、Spring配置

5.1 别名

作用:添加了别名,可以使用别名获取到对象

5.2 Bean的配置

属性:

  • id:唯一标识符,相当于对象名
  • class:bean对象所对应的全限定名:包名+类名
  • name:别名,比alias更高级,可以同时取多个别名
<bean id="user" class="com.Nana.pojo.User" name="u2,u3,u4">
    <property name="name" value="海燕酱"></property>
</bean>
           

5.3 import

作用:将多个配置文件倒入合并为一个,一般用于团队开发使用,可直接使用总的配置

6、依赖注入

  • 本质:Set注入
    • 依赖:bean对象的创建依赖于容器
    • 注入:bean对象中的所有属性由容器来注入

6.1 普通方式

  • 构造器注入

    详见4

  • 普通值注入
  • Bean注入
<bean id="address" class="com.Nana.pojo.Address"></bean>

<bean id="student" class="com.Nana.pojo.Student">
    <property name="address" ref="address"></property>
</bean>
           
  • 数组注入
<property name="books">
    <array>
        <value>红楼梦</value>
        <value>西游记</value>
        <value>水浒传</value>
        <value>三国演义</value>
    </array>
</property>
           
  • List注入
<property name="hobbys">
    <list>
        <value>听歌</value>
        <value>敲代码</value>
        <value>看电影</value>
    </list>
</property>
           
  • Map注入
<property name="card">
    <map>
        <entry key="身份证" value="111111111111111"></entry>
        <entry key="银行卡" value="222222222222222"></entry>
    </map>
</property>
           
  • Null注入
<property name="wife">
    <null/>
</property>
           
  • Properties注入
<property name="info">
    <props>
        <prop key="username">root</prop>
        <prop key="password">123456</prop>
    </props>
</property>
           
  • Set注入
<property name="games">
    <set>
        <value>LOL</value>
        <value>COC</value>
        <value>BOB</value>
    </set>
</property>
           

6.2 扩展方式

使用p命名空间和c命名空间注入:

  • p标签:property缩写,作用是给成员变量赋值
  • c标签:constructor-arg缩写,作用是给有参构造函数传参

使用:

  1. 在Mapper中导入xml约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
           
  1. 使用
<bean id="user" class="com.Nana.pojo.User" p:name="海燕酱" p:age="21"></bean>
<bean id="user2" class="com.Nana.pojo.User" c:name="海燕酱" c:age="21"></bean>
           

6.3 Bean的作用域

Spring学习笔记Spring
  1. 单例模式(Spring的默认模式),在通过相同name多次取bean的时候取到的是同一个
  1. 原型模式,每次从容器中get时都会产生一个新对象
  1. 其余的四个只能在web开发中使用到

7、Bean的自动装配

解释:

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

Spring三种装配方式:

  • 在xml中显示地配置
  • 在java中显示地配置
  • 隐式地自动装配bean

7.1 ByName和ByType自动装配

byName:自动在容器的上下文中查找和自己对象set方法后面值对应的beanid

<bean id="cat" class="com.Nana.pojo.Cat"></bean>
<bean id="dog" class="com.Nana.pojo.Dog"></bean>

<bean id="people" class="com.Nana.pojo.People" autowire="byName">
    <property name="name" value="海燕酱"></property>
</bean>
           

byType:自动在容器上下文中查找和自己对象属性类型相同的bean

<bean class="com.Nana.pojo.Cat"></bean>
<bean class="com.Nana.pojo.Dog"></bean>

<bean id="people" class="com.Nana.pojo.People" autowire="byType">
    <property name="name" value="海燕酱"></property>
</bean>
           
  • byName的使用需要保证所有bean的id唯一,并且这个bean需要和自动注入属性的set方法的值一致
  • byType的使用需要保证所有bean的class唯一,并且这个bean需要和自动注入属性的类型一致

7.2 使用注解进行自动装配

  • jdk1.5开始支持注解,Spring2.5开始支持注解

注解使用:

  1. 导入context约束
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
</beans>
           
  1. 配置注解支持(重要,不能遗忘这一步)

@Autowired和**@Qualifier**

  • 可以直接在属性上使用,也可以在set方法上使用
  • 使用此注解可以不用编写set方法,前提是这个自动装配的属性在IoC容器中存在,且符合名字byName
//如果显示地定义了required属性为false,说明这个对象可以为空,否则不能为空
@Autowired(required = false)
String name;
@Autowired
Cat cat;
Dog dog;
           

注意:如果@AUtowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成的时候,可以使用@Qualifier(value = “xxx”)去配置Autowired的使用,指定一个唯一的bean对象注入

@Autowired
@Qualifier(value = "cat111");
Cat cat;
           

@Resource

@Resource(name = "cat111")
Cat cat;
@Resource
Dog dog;
           

@Autowired和@Resource相同点和区别:

  • 相同点:都是用来自动装配的,都可以放在属性字段上
  • 不同点:
    • @Autowired通过byType的方式实现,而且要求这个对象存在
    • @Resource通过byName的方式实现,如果根据名字无法找到,就通过byType实现,如果两种方式都找不到就报错
    • 执行顺序不同:@Autowired通过byType方式实现,@Resource默认通过byName的方式实现

8、使用注解开发

注意:

  • 在Spring4之后,要使用注解开发,必须保证aop包的导入
Spring学习笔记Spring
  • 使用注解需要导入context约束,增加注解支持
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/beans/spring-context.xsd">
    
    <context:annotation-config/>
</beans>
           

@Component和@Value

@Component
public class User {
    public String name;

    @Value("海燕酱")
    public void setName(String name){
        this.name=name;
    }
}
           

@Repository、@Service、@Controller

@Component衍生注解,在web开发中,会按照mvc三层架构分层

  • dao【@Repository】
  • service【@Service】
  • controller【@Controller】

这四个注解功能一样,都是代表将某个类注册到Spring中,装配Bean

@Scope

@Component
@Scope("prototype")
public class User {
    public String name;

    @Value("海燕酱")
    public void setName(String name){
        this.name=name;
    }
}
           

xml和注解:

  • xml更加万能,适用于任何场合,维护简单方便;注解不是自己的类使用不了,维护相对复杂
  • xml是用来管理bean的,注解只负责完成属性的注入
  • 在使用注解过程中需要注意的是:必须让注解生效,需要开启注解的支持

9、使用Java的方式配置Spring

  1. 写实体类,给实体类成员变量注入值
//@Component注解表明这个类被Spring接管了,注册到了容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }

    @Value("海燕酱")
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
           
  1. 编写配置类
//@Configuration注解表明当前类是配置类,作用类似于之前写过的beans.xml
@Configuration
//@ComponentScan注解自动扫描指定包下所有使用@Service,@Component,@Controller,@Repository的类并注册
@ComponentScan
//@Import注解可导入单个组件或者多个组件,id默认为全类名
@Import(NanaConfig.class)
public class NanaConfig {
    
    //@Bean注解作用是注册一个bean,相当于之前写的一个bean标签
    //方法名相当于bean标签中的id属性
    //方法的返回值相当于bean标签中的class属性
    @Bean
    public User user(){
        return new User();
    }
}
           
  1. 编写测试类
//如果完全使用了配置类方式去做,就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(NanaConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user.getName());
           

这种纯Java配置方式,在SpringBoot中随处可见

10、代理模式

  • 使用:SpringAOP的底层
  • 分类:静态代理和动态代理
Spring学习笔记Spring

10.1 静态代理

角色分析:

  • 抽象角色:一般会使用角色或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色后,一般会做一些附属操作
  • 客户:访问代理对象的人

优点:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

租客、房东、中介案例:

//租房接口
public interface Rent {
    public void rent();
}
           
//房东要出租房子
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}
           
//中介在租房子同时作恶一些附加操作
public class Proxy implements Rent{
    private Host host;

    public Proxy(){}
    public Proxy(Host host){
        this.host = host;
    }

    public void rent(){
        seeHouse();
        host.rent();
        hetong();
        fare();
    }

    public void seeHouse(){
        System.out.println("中介带你看房");
    }

    public void hetong(){
        System.out.println("签租赁合同");
    }

    public void fare(){
        System.out.println("收中介费");
    }
}
           
//租客租房子
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}
           

10.2 加深理解

Spring学习笔记Spring

实践代码:

//Service接口层
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
           
//Service接口实现层
public class UserserviceImpl implements UserService{
    public void add() {
        System.out.println("增加了一个用户");
    }

    public void delete() {
        System.out.println("删除了一个用户");
    }

    public void update() {
        System.out.println("修改了一个用户");
    }

    public void query() {
        System.out.println("查询了一个用户");
    }
}
           
//代理层,主要为了增加输出日志功能
public class UserServiceProxy {
    private UserserviceImpl userservice;

    public void setUserService(UserserviceImpl userService){
        this.userservice = userService;
    }

    public void add(){
        log("add");
        userservice.add();
    }

    public void delete(){
        log("delete");
        userservice.delete();
    }

    public void update(){
        log("update");
        userservice.update();
    }

    public void query(){
        log("query");
        userservice.query();
    }

    public void log(String msg){
        System.out.println("[Debug]使用了"+msg+"方法");
    }
}
           
//访问代理层
public class Client {
    public static void main(String[] args) {
        UserserviceImpl userService = new UserserviceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.query();
    }
}
           

10.3 动态代理

  • 和静态代理相同及不同点:
    • 相同点:和静态代理角色一样
    • 代理类是动态生成的,不是直接写好的
  • 分类:基于接口的动态代理和基于类的动态代理
    • 基于接口:JDK
    • 基于类:cglib
    • java字节码实现:javasist
  • 使用到的类:
    • Proxy:代理
    • InvocationHandler:调用处理程序
  • 优点:
    • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
    • 公共业务交给代理角色,实现了业务的分工
    • 公共业务发生扩展的时候,方便集中管理
    • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
    • 一个动态代理类可以代理多个类,只要是实现了同一个接口

使用:

public class ProxyInvocationHandler 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);
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws  Throwable{
        log(method.getName());
        //执行target接口中的args方法,返回执行结果
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String msg){
        System.out.println("执行了"+msg+"方法");
    }
}
           
public class Client {
    public static void main(String[] args) {
        //新建一个真实角色
        UserserviceImpl userService = new UserserviceImpl();

        //新建一个不存在的动态代理
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        //设置要代理的对象
        pih.setTarget(userService);

        //生成一个真实的代理类
        UserService proxy = (UserService)pih.getProxy();
        proxy.query();
    }
}
           

11、AOP

11.1 什么是AOP

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Spring学习笔记Spring

11.2 AOP在Spring中的作用

1. 提供声明式事务

2. 允许用户自定义切面

3. 可以在不改变原有代码的情况下,去增加新的功能

主要组成部分:

  • 横切关注点:跨越应用程序多个模块的方法或功能,即与业务逻辑无关,但是需要关注的部分。如:日志、安全、缓存、事务等等
  • 切面(Aspect):横切关注点被模块化的特殊对象,是一个类
  • 通知(Advice):切面必须要完成的工作,是类中的一个方法
  • 目标(Target):被通知的对象
  • 代理(Proxy):向目标对象应用通知之后创建的对象
  • 切入点(PointCut):切面通知执行的"地点"
  • 连接点(JointPoint):与切入点匹配的执行点
Spring学习笔记Spring

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

Spring学习笔记Spring

11.3 使用Spring实现AOP

导入AOP所需依赖:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
           

测试环境搭建:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}
           
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加了一个用户");
    }

    public void delete() {
        System.out.println("删除了一个用户");
    }

    public void update() {
        System.out.println("修改了一个用户");
    }

    public void select() {
        System.out.println("查询了一个用户");
    }
}
           
public class BeforeLog implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}
           
public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
    }
}
           
<?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
        https://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="userService" class="com.Nana.service.UserServiceImpl"></bean>
    <bean id="beforelog" class="com.Nana.Beforelog.Log"></bean>
    <bean id="afterLog" class="com.Nana.log.AfterLog"></bean>
</beans>
           
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applictaionContext.xml");
        //注意点:动态代理代理的是接口
        UserService userService = (UserService)context.getBean("userService");
        userService.select();
    }
}
           

实现方式一:使用Spring的API接口

<aop:config>
    <aop:pointcut id="pointcut" expression="execution(* com.Nana.service.UserServiceImpl.*(..))"/>
    <aop:advisor advice-ref="beforelog" pointcut-ref="pointcut"></aop:advisor>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
           

实现方式二:使用自定义类

public class DiyPointCut {
    public void before(){
        System.out.println("========方法执行前========");
    }
    public void after(){
        System.out.println("========方法执行后========");
    }
}
           
<bean id="diy" class="com.Nana.diy.DiyPointCut"></bean>

<aop:config>
    <aop:aspect ref="diy">
        <aop:pointcut id="point" expression="execution(* com.Nana.service..UserServiceImpl.*(..))"></aop:pointcut>
        <aop:before method="before" pointcut-ref="point"></aop:before>
        <aop:after method="after" pointcut-ref="point"></aop:after>
    </aop:aspect>
</aop:config>
           

实现方式三:使用注解

@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.Nana.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("========方法执行前========");
    }

    @After("execution(* com.Nana.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("========方法执行后========");
    }

    @Around("execution(* com.Nana.service.UserServiceImpl.*(..))")
    public void arround(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("'环绕前");
        Signature signature = jp.getSignature();
        System.out.println("signature:"+signature);

        Object proceed = jp.proceed();
        System.out.println("环绕后");
        System.out.println(proceed);
    }
}
           
<bean id="annotationPointCut" class="com.Nana.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
           

execution表达式:

execution(* com.loongshawn.method.ces….(…))
Spring学习笔记Spring

12、整合Mybatis

12.1 测试环境搭建

  1. 导入相关jar包:junit、mybatis、mysql、spring、aop、mybatis-spring
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.2</version>
</dependency>
           
  1. 导入静态资源过滤
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>
           
  1. 编写核心配置文件
<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper class="com.Nana.mapper.UserMapper"/><!--注意这里是/做分隔符-->
    </mappers>
</configuration>
           
  1. 编写pojo实体类
public class User {
    private int id;
    private String name;
    private String pwd;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
           
  1. 编写Mapper查询语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.Nana.mapper.UserMapper">
    <select id="selectUser" resultType="com.Nana.pojo.User">
        select * from user
    </select>
</mapper>
           
  1. 测试
public void test() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = sessionFactory.openSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.selectUser();
    for (User user : userList) {
        System.out.println(user);
    }
}
           

12.2 整合方式一

  1. 编写数据源配置dao.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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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="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&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </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/Nana/mapper/*.xml"/>
    </bean>

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
</beans>
           
  1. 编写总配置applictaionContext.xml,专注于注册Mapper和引用其他配置
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <import resource="spring-dao.xml"></import>

    <bean id="userMapper" class="com.Nana.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"></property>
    </bean>
</beans>
           
  1. 编写接口实现类,专注于业务实现
public class UserMapperImpl implements UserMapper{
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
           
  1. 测试
public void test() throws IOException {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
    for (User user : userMapper.selectUser()) {
        System.out.println(user);
    }
}
           

注意:在mybatis核心配置文件中可以只进行取别名typealiases和设置settings

12.3 整合方式二

在整合方式一基础上做了一定简化,可以直接注入SqlSessionFactory

  1. 在12.2步骤2代码基础上添加
<bean id="userMapper2" class="com.Nana.mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
           
  1. 在12.2步骤3代码基础上改进
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> selectUser() {
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}
           

13、Spring中的事务管理

事务重要性:

  • 如果不配置事务,可能存在数据提交不一致的情况
  • 如果不在Spirng中配置声明式事务,就需要在代码中手动配置事务
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性

事务管理分类:

  • 声明式事务:使用aop进行事务管理,无需修改业务代码
  • 编程式事务:需要在代码中进行事务的管理

声明式事务的使用:

<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">

    <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&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    
	<!--先根据数据源获得一个事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </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:config>
        <aop:pointcut id="txPoint" expression="execution(* com.Nana.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
    </aop:config>
</beans>
           

Spring中七种事务传播行为:

Spring学习笔记Spring