Spring学习笔记

Spring学习

关于Spring

简介

官网:https://spring.io/

介绍:Spring框架是一个开放源代码J2EE应用程序框架,由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson/1423612)发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。

特点:

  • 两个最大的特性:AOP(面向切面编程) 和 IOC(控制反转)—面试必问

  • Spring是一个轻量级的,非入侵式的框架(引入框架的时候不会对以前的代码产生影响)

  • 支持事务处理,对框架整合的支持,相当于一个大杂烩容器

  • 开源的,免费的

组成

在这里插入图片描述

  • Spring Boot
    • 一个快速开发的脚手架
    • 基于SpringBoot可以快速的开发单个微服务
    • 约定大于配置
  • Spring Cloud
    • SpringCloud是基于Stringboot开发的

IOC理论推导

推导

原先我们写MVC三层模式的时候是需要经历以下这些过程的:

  1. 编写UserDao接口

    public interface UserDao {
        void getUser();
    }
    
  2. 编写UserDaoImpl实现类

    public class UserDaoImpl implements UserDao {
        public void getUser() {
            System.out.println("我是数据访问层!");
        }
    }
    
  3. 编写UserService接口

    public interface UserService {
        void getUser();
    }
    
  4. 编写UserServiceImpl实现类

    public class UserServiceImpl implements UserService {
        private UserDao userDao = new UserDaoImpl();
    
        public void getUser() {
            userDao.getUser();
        }
    }
    
  5. 最后才能够调用到数据访问层

     public void Test01(){
            UserServiceImpl userService = new UserServiceImpl();
            userService.getUser();
        }
    

这样做的好处就是不用管数据访问层是什么,我们直接再表示层调用就可以了,我们根本不与数据库层打交道。

但是,这个模式也有很大的缺点,那就是我们如果后期添加很多需求的话我们必须去内部修改源码,这样的代价十分昂贵!牵一发而动全身!

改良以上方法,在服务层加一个set方法,这样一来用户的需求就不会改变源代码,这种思想与上面的思想有着天大的区别!

public class UserServiceImpl implements UserService {
    //本来是写死的,现在可以灵活调用了
    private UserDao userDao;

    //新加的set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void getUser() {
        userDao.getUser();
    }
}

测试:

public void Test01(){
    UserServiceImpl userService = new UserServiceImpl();
    userService.setUserDao(new UserDaoMysqlImpl());

    userService.getUser();
}
  • 之前,程序是主动创建对象的,因为在服务层里面是写死的,只能去new一个数据访问层,主动权在程序员的手里!而且耦合性很高,牵一发而动全身!
  • 而现在我们用了一个set方法注入后,程序new对象不再是死的了,可以很灵活,传入什么对象就new什么对象,被动的接收对象!
  • 这就是控制反转的思想!

这种思想从本质上解决了问题,程序猿不用再去管理对象的创建了。系统的耦合性大大地降低,可以更加专注的关注业务了。这是IOC的原型!
在这里插入图片描述
整体架构没有变,只是核心业务变了。

本质

在这里插入图片描述
如上图,一开始我们的程序之间都相互联系,耦合度很高,耦合度高的话不利于程序的拓展,这是一种强连接,于是乎演变出来了IOC容器,也就是第二个图,通过一个中间件来调用其他的模块,通过接口调用就不会是强连接了

控制反转是一种通过描述(XNL或注解)并通过第三方生产或获取特定对象的方式,在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)(set…)。

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用spring之后对象是由spring来创建的。

反转:程序本身不创建对象,而变成被动的接收对象。

核心是set方法,在配置文件中是利用实体类中的set方法对属性进行赋值

IOC是一种编程思想,由主动的编程变成被动的接收。

第一个spring程序

首先我们有一个pojo类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hello {
    private String name;
}

然后就是至关重要的配置文件了,在spring中new一个对象就是通过配置文件来的

<?xml version="1.0" encoding="GBK"?>
<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">

    <!--使用spring来创建对象 在spring中 对象称之为bean-->
    <!--
        id相当于变量名
        class相当于new的对象的目的地址
        property就相当于赋值
        变量名 id = new 变量名(class)
    -->
    <bean id="hello" class="pojo.Hello">
        <property name="name" value="Spring!"/>
    </bean>
</beans>

最后测试

public static void main(String[] args ){
    //获取spring的上下文对象 固定写法
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    //我们的对象现在都在spring中管理了 我们只需要取出来就可以了
    Hello hello = (Hello) context.getBean("hello");
    System.out.println(hello.toString());
}

只要在pojo类有这个标志,→
在这里插入图片描述
就代表已经连接成功了,已经被spring托管了。

到了现在,我们彻底不用再去程序里改动源码了,要实现不同的操作,只需要在xml文件中进行修改,所谓IOC总结为一句话就是:对象由spring来创建,管理,装配!

用spring改良mvc三层模式

在这里插入图片描述
由于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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="daoImpl.UserDaoImpl"/>
    <bean id="userDaoMysql" class="daoImpl.UserDaoMysqlImpl" />

    <bean id="userService" class="serviceImpl.UserServiceImpl">
        <property name="userDao" ref="userDaoMysql"/>
    </bean>

</beans>

测试:

@Test
public void test02(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    UserServiceImpl userService = (UserServiceImpl) context.getBean("userService");
    userService.getUser();
}

这样一来我们没有用关键字new去新建任何一个对象,只是用配置文件去调用(spring去实现创建)然后我们取出来就可以了,我们需要更改需求的时候也不需要更改源码,在改良的整个过程当中我们没有动过任何源码!!

接下来我们对这个配置文件进行讲解:

  • 每一个bean就是一个对象
  • 以前是在service的实现类中传不同的对象,现在直接在配置文件中传不同的对象,在第三个bean中我们可以传前两个对象中的任意一个,这个过程就相当于我们在service实现类传参
  • 在spring中是set方法很重要,在配置文件中我们也是通过调用set方法来设置它的值为某个对象的

IOC创建对象方式

  1. 使用无参构造方法,没有无参的构造函数就会报错(默认方法)在这里插入图片描述

  2. 使用有参构造方法

    1. 下标

      <bean id="user" class="pojo.User">
        	<!--  <property name="name" value="大姚"/>-->
          <constructor-arg index="0" value="火花"/>
      </bean>
      
    2. 参数的类型
      在这里插入图片描述
      通过这个我们可以得出一个结论,就是在配置文件中的bean都会被执行就像是new了多个对象,并不是你调用谁谁才被new,他已经new好了事先。

    3. 通过参数名来设置

      <bean id="user2" class="pojo.User">
          <constructor-arg name="name" value="hutu"/>
      </bean>
      

    spring配置

  • 别名 可以取多个别名 每个别名都可以用 <alias name="user2" alias="user3"/>
    *在这里插入图片描述
  • import
    • 将多个bean合并成一个总的bean
    • 场景:一个项目多个人开发,他们建的bean都不同,我们可以利用import将所有人的beans合并为一个总的,最后使用的时候直接用总的就可以了。
    • 在这里插入图片描述

依赖注入

构造器注入,如上

set注入(重点)

  • 依赖注入:set注入!核心
    • 依赖 : bean对象的创建依赖于容器!
    • 注入 : bean对象中的所有的属性,由容器来注入

环境搭建:

  1. 复杂类型
public class Address {
    private String address;

    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
  1. 真实测试对象
public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}
  1. beans.xml
<?xml version="1.0" encoding="GBK"?>
<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="student" class="pojo.Student">
        <property name="name" value="滴滴"/>
    </bean>
</beans>
  1. 测试类
public class MyTest {
    public static void main(String[] args ){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getName());
    }
}

基本di注入方式:

<?xml version="1.0" encoding="GBK"?>
<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="address" class="pojo.Address">
        <property name="address" value="南京"/>
    </bean>

    <bean id="student" class="pojo.Student">
        <!--第一种 普通值注入 value-->
        <property name="name" value="滴滴"/>

        <!--bean注入 ref-->
        <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>啪啪啪</value>
            </list>
        </property>

        <!--Map-->
        <property name="card">
            <map>
                <entry key="身份证" value="11123125415252"></entry>
                <entry key="银行卡" value="09876553565446"></entry>
            </map>
        </property>

        <!--Set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CF</value>
            </set>
        </property>

        <!--null-->
        <property name="wife">
            <null/>
        </property>

        <!--Properties-->
        <property name="info">
            <props>
                <prop key="drive">mysql.root.null</prop>
                <prop key="username">大疆</prop>
                <prop key="password">1243</prop>
            </props>
        </property>
    </bean>
</beans>

在这里插入图片描述

拓展方式注入

命名空间

官方使用
在这里插入图片描述
p and c

xmlns:p="http://www.springframework.org/schema/p"

xmlns:c="http://www.springframework.org/schema/c"

不能直接使用需要导入上面这两条约束

如果要用c命名的话必须要有无参和有参的构造函数

测试

<?xml version="1.0" encoding="GBK"?>
<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:c="http://www.springframework.org/schema/c"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="pojo.Address" p:address="南京"/>
    <bean id="user" class="pojo.User" p:name="大海" p:age="13" p:address-ref="address"/>
    <bean id="user2" class="pojo.User" c:name="王华" c:age="23" c:address-ref="address"/>
</beans>

注意点:在我们用上下文(context)调用beans.xml中的对象的时候可以用这种方式来规避每次的强转User user = context.getBean("user2", User.class);

bean作用域

在这里插入图片描述

  1. 单例模式(Spring默认机制),永远都是一个对象 单线程用

    • <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
  2. 原型模式:每次从容器get的时候都会产生一个新的对象 多线程用

    • <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
  3. 其余的在web中会用到

Bean的自动装配

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

装配的三种方式

在spring中有三种装配方式:

  1. 在xml中显示的配置
  2. 在java中显示的配置
  3. 隐式的自动装配bean <重要>

以前我们手动在beans.xml里面配置文件就是手动的。

使用xml自动装配

自动装配:
在这里插入图片描述

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

<bean id="person" class="pojo.Person" autowire="byName">
    <property name="name" value="幂幂"/>
</bean>

使用注解自动装配

导入约束,多了一句这个在这里插入图片描述

<?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">

    <context:annotation-config/>

</beans>

使用注解就可以不用set方法了

可以在属性上使用,也可以在set方法上使用,前提是你自动装配的属性在IOC(spring)容器中存在,且名字规范。
在这里插入图片描述
小结:

@Resource 和 @Autowired的区别:

  • 都可以自动装配 都可以放在属性字段上
  • @Autowired是通过ByType的方式实现的,而且这个对象必须存在
  • @Resource 默认是通过ByName实现的,如果找不到名字,则自动通过类型实现,如果名称和类型都不标准,那就报错!

使用注解开发

  1. bean

    • spring 4之后要使用注解AOP的包必须导入

    • 需要现在配置文件中声明注解生效的包

      • <!--指定要扫描的包 这个包下的注解才会生效-->
        <context:component-scan base-package="pojo"/>
        
    • @Component 组件 直接在类上面用就可以 说明这个类被spring管理了 这就是上面的bean标签

  2. 属性如何注入

    • @Value("大糊涂") 直接在属性上面写就可以
    @Component
    public class User {
        @Value("大糊涂")
        public String name;
    
        @Value("糊糊涂涂") //最后输出的是这个
        public void setName(String name) {
            this.name = name;
        }
    }
    
  3. 衍生的功能

    @Componet有几个衍生的注解 在web开发中 会按照mvc三层架构分层

    • dao 【@Repository】
    • service 【@Service】
    • control 【@org.springframework.stereotype.Controller】

    这四个注解的功能都是一样的,都是代表某个类注册到spring中,装配bean

  4. 自动装配
    在这里插入图片描述

  5. 作用域

    直接在类上面配置就可以
    在这里插入图片描述

  6. 小结

xml 和 注解:

  • xml更加全能 适用于各种场合 维护简单方便
  • 注解维护相对麻烦

xml用来管理bean

注解只负责属性的注入

我们必须要注意的一个问题就是在xml文件中开启注解,路径到某个包下。

使用Java的方式配置spring

我们现在完全不需要使用xml了,完全交给java去做

JavaConfig是一个核心功能!
在这里插入图片描述
在这里插入图片描述
这种纯Java的配置方法在springBoot中随处可见!

代理模式

为什么要学习代理模式?

because这是spring AOP的底层

面试必问【springAOP 和 springMVC】

代理模式的分类:

静态代理

在这里插入图片描述
好处:

  • 通过代理可以增加很多事情,使操作更加丰富,可以做很多真实对象做不了的事情
  • 使真实对象(房东)的操作更加纯粹,不用去关注一些公共的事务,实现了业务的分工
  • 业务扩展的时候方便集中管理
  • 不改变原有的代码 来增加新的需求

缺点:

  • 一个真实对象对应一个代理角色,这样的话会造成代码量翻倍,开发效率降低

实例

  1. 接口

    • public interface Rent {
          public void rent();
      }
      
  2. 真实角色

    • public class Host implements Rent{
          public void rent() {
              System.out.println("房东要出租房子!");
          }
      }
      
  3. 代理角色

    • public class Proxy implements Rent{
          public Host host;
      
          public Proxy(Host host) {
              this.host = host;
          }
      
          public Proxy() {
          }
      
          public void rent() {
              host.rent();
              fee();
              seeHouse();
          }
      
          public void fee(){
              System.out.println("收中介费");
          }
      
          public void seeHouse(){
              System.out.println("中介看房子");
          }
      }
      
  4. 客户访问代理角色

    • public class Cilent {
          public static void main(String[] args ){
              Proxy proxy = new Proxy(new Host());
              proxy.rent();
          }
      }
      

聊聊AOP
在这里插入图片描述

动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是自动生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理 和 基于类的动态代理
    • 基于接口—JDK 动态代理
    • 基于类 cglib
    • java字节码实现 : javasist

需要了解两个类 Proxy **代理 ** 和 InvocationHandler 调用处理程序
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
动态代理最大的好处就是:一个动态代理类可以代理多个类!!

实例:

接口

public interface Rent {
    public void rent();
}

真实对象(房东)

public class Host implements Rent {
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

代理对象(动态代理,不是写死的,可以代理很多类)

//自动生成代理类
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 {
        //动态代理的本质就是反射机制
        System.out.println("执行了"+method.getName()+"方法");
        seeHouse();
        Object result = method.invoke(target, args);
        return result;
    }

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

测试

public class Client {
    public static void main(String[] args ){
        Host host = new Host();
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(host);
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}

与静态代理最大的区别就是动态代理是基于反射实现的,而且在代理层面一个代理可以很灵活的代理多个类。

AOP

在这里插入图片描述
在这里插入图片描述
第一步导包:

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

我们也是先建一个userservice接口和一个userviceimpl实现类,实现了增删改查的方法,之前我们是用动态代理来做的,现在我们用spring来做。

方法一:使用spring的API接口

复制以前写的UserService类和UserServiceImpl

编写log类和afterlog类

public class Log implements MethodBeforeAdvice {
    /**
     * @param method  要执行的目标对象的方法
     * @param args  参数
     * @param target 目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}
public class AfterLog implements AfterReturningAdvice {
    /**
     * @param returnValue 返回值
     * @param method  调用的方法
     * @param args  参数列表
     * @param target  目标对象
     * @throws Throwable
     */
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
    }
}

创建applicationContext.xml配置文件

<?xml version="1.0" encoding="GBK"?>
<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-->
    <bean id="userservice" class="com.shi.serviceImpl.UserServiceImpl"/>
    <bean id="log" class="com.shi.log.Log"/>
    <bean id="afterlog" class="com.shi.log.AfterLog"/>

    <!--使用原生的方式一-->
    <!--配置aop 导入aop的约束-->
    <aop:config>
        <!--切入点 在那个地方去执行 expression 表达式  execution(要执行的位置! * * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.shi.serviceImpl..*(..))"/>

        <!--执行环绕增加!-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
    </aop:config>
    
</beans>

测试

public class MyTest {
    public static void main(String[] args ){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userservice = context.getBean("userservice", UserService.class);
        userservice.select();
    }
}

结果
在这里插入图片描述

方法二:实现自定义类实现

主要是切面定义

<bean id="diy" class="com.shi.diy.DiyPointCut"/>
<!--方法二:自定义类-->
<aop:config>
    <!--自定义切面 ref要引用的类-->
    <aop:aspect ref="diy">
        <!--切入点-->
        <aop:pointcut id="point" expression="execution(* com.shi.serviceImpl.UserServiceImpl..*(..))"/>
        <!--通知-->
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

自定义切面类:

public class DiyPointCut {
    public void before(){
        System.out.println("===========方法执行前============");
    }
    public void after(){
        System.out.println("===========方法执行后============");
    }
}

方法三:使用注解实现

@Aspect //标记这是一个切面
public class AnnotationPointCut {

    @Before("execution(* com.shi.serviceImpl.UserServiceImpl..*(..))")
    public void before(){
        System.out.println("============方法执行前==============");
    }
    @After("execution(* com.shi.serviceImpl.UserServiceImpl..*(..))")
    public void after(){
        System.out.println("============方法执行后==============");
    }
}

整合MyBatis

  1. 导入相关jar包

    • junit
    • mybatis
    • mysql数据库
    • spring相关的
    • aop
    • mybatis-spring(专门整合mybatis和spring的包)
  2. 编写配置文件

  3. 测试

回忆mybatis

  1. 编写实体类
  2. 核心配置文件
  3. 接口
  4. 实现类
  5. 测试

Mybatis-Spring

  1. 编写数据源配置
  2. sqlsessionfactory
  3. sqlsessionTemplate
  4. 需要给接口加实现类
  5. 想实现类注入到spring中
  6. 测试

代码示例:

配置文件:

spring-dao.xml配置文件

<?xml version="1.0" encoding="GBK"?>
<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">

    <!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--这句话就相当于让原来的mybatis配置找到接口的mapper.xml-->
        <property name="mapperLocations" value="classpath:com/shi/mapper/*.xml"/>
    </bean>

    <!--DataSource: 使用spring的数据源替换mybatis的配置 即替换用户名密码url等信息
        我们这里使用spring提供的JDBC
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.1:3306/test?serverTimezone=UTC&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123"/>
    </bean>

    <!--SqlSessionTemplate 就是我们使用的sqlsession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--只能使用构造器注入sqlsession 因为没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

</beans>

mybatis-config.xml配置文件 (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.shi.entity"/>
    </typeAliases>
    
</configuration>

applicationContext.xml整合配置文件

<?xml version="1.0" encoding="GBK"?>
<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="spring-dao.xml"/>

    <bean id="coursemapperimpl" class="com.shi.mapper.CourseMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSession"/>
    </bean>

</beans>

Course.xml配置文件(相当于以前的接口的dao)

<?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.shi.mapper.CourseMapper">
    <select id="list" resultType="course">
        select * from test.course 
    </select>
</mapper>

我们先在通过这三个配置文件就已经创建好了sqlsession和sqlsessionfactory了,在测试类中直接getBean就可以了。

因为是spring配置的所以要有一个接口的实现类

public class CourseMapperImpl implements CourseMapper {

    //我们的所有的操作都是用上去了session执行 在原来
    //现在我们都使用sqlsessionTemplate
    private SqlSession sqlSessionTemplate;

    public void setSqlSessionTemplate(SqlSession sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    public List<Course> list() {
        CourseMapper mapper = sqlSessionTemplate.getMapper(CourseMapper.class);
        return mapper.list();
    }
}

有了这个接口实现类我们就可以代理原接口,并且在这里面进行CourseMapper mapper = sqlSessionTemplate.getMapper(CourseMapper.class);,有了这个对象我们就可以返回我们的结果了,这样的话在客户端就不需要写很多的代码了。

测试

@Test
public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    CourseMapper cmi = context.getBean("coursemapperimpl", CourseMapper.class);
    for (Course course : cmi.list()) {
        System.out.println(course);
    }
}

注意:mybatis-config.xml是可以删掉的!!

这个可以简化,主要是简化sqlsession的创建,在实现类中我们可以一条语句就返回
在这里插入图片描述

public class CourseMapperImpl extends SqlSessionDaoSupport implements CourseMapper {

    //我们的所有的操作都是用上去了session执行 在原来
    //现在我们都使用sqlsessionTemplate
  /*  private SqlSession sqlSessionTemplate;

    public void setSqlSessionTemplate(SqlSession sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }*/

    public List<Course> list() {
        return  getSqlSession().getMapper(CourseMapper.class).list();
    }
}

在这里插入图片描述
相对应的上面这块内容也可以删掉。

声明式事务

回顾事务

  • 要么都成功,要么都失败
  • 确保完整性和一致性
  • 事务在项目开发中很重要 涉及到数据一致性的问题 不能马虎

事务ACID原则:

  • 原子性 : 确保要么都成功 要么都失败
  • 一致性 : 要么都提交 要么都失败
  • 隔离性 : 多个业务可能操作同一个资源,防止数据损坏
  • 持久性 : 事务一旦提交,无论系统发生什么问题,结果都不会再被影响 被持久化写到存储器中

spring事务

  • 声明式事务 : AOP
  • 编程式事务 : 需要在代码中进行事务管理
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="dataSource" />
</bean>

在这里插入图片描述
在这里插入图片描述
思考:

​ 为什么需要事务?

  • 如果不配置事务 可能存在数据提交不一致的情况,例如当两个插入语句,一个有错误,一个没有错误,按照事务的ACID原则,肯定都不能插入,但是不配置事务的话,那一条对的插入语句就会插入成功!违背了ACID原则。
  • 强调,事务很重要,关系到数据的完整性和一致性
<!--结合AOP实现事务的织入-->
<!--配置事务的类:-->

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--给那些方法配置事务-->
    <tx:attributes>
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="list" read-only="true"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<!--配置事务切入-->
<aop:config>
    <aop:pointcut id="txPoint" expression="execution(* mapper.*..*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值