美文网首页
Spring笔记

Spring笔记

作者: KyoDante | 来源:发表于2022-02-13 19:37 被阅读0次

    感谢狂神,讲解清晰,以下是原视频【狂神说Java】Spring5最新完整教程IDEA版通俗易懂的笔记

    简介

    • 2002年interface21是雏形
    • 2004年3月24日发布1.0正式版
    • 本身是大杂烩,方便JavaEE的开发
    • SSH :Struct2 + Spring + Hibernate
    • SSM : SpringMVC + Spring + Mybatis!

    官网:Spring | Why Spring?
    下载:repo.spring.io
    源代码:GitHub - spring-projects/spring-framework: Spring Framework

    • Spring web和Spring jdbc的maven依赖:
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.2.19.RELEASE</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.19.RELEASE</version>
    </dependency>
    

    优点

    • 轻量级、非入侵式、开源的框架(容器)
    • 控制反转(IOC, inversion of control)、面向切面编程(AOP, aspect oriented programming)
    • 支持事务的处理,对框架整合的支持

    总结:是一个轻量级的控制反转、面向切面编程的框架。

    组成及扩展

    组成

    扩展

    • Spring Boot

      • 一个脚手架
      • 可以快速开发单个微服务
      • 约定大于配置
    • Spring Cloud

      • 基于Spring Boot实现的

    学习SpringBoot的前提是Spring和SpringMVC,承上启下。

    弊端:发展久了,大杂烩导致配置繁琐,”配置地狱“。

    IOC理论推导

    1、UserDao 接口

    2、UserDaoImpl 实现类

    3、UserService 业务接口

    4、UserServiceImpl 业务实现类

    用户需求会影响原来的代码,代码量大,修改一次的成本代价大。

    private UserDao userDao;
    
    // 利用set进行动态实现值的注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    

    之前由程序员来指定,现在由使用者指定。(不需要修改原本的程序,即控制反转:主动创建对象--->被动的接受对象)

    IOC本质

    IOC是一种设计思想,DI(Dependency Injection,依赖注入)是实现IOC的一种方法。对象的创建由程序自己控制,控制反转之后将对象的创建转移给第三方。

    采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体。Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

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

    IOC创建对象

    无参构造

        <bean id = "user" class="com.pojo.User">
            <property name="name" value="KyoDante"/>
        </bean>
    

    有参构造的三种方式:下标、类型(不推荐)、参数名都可以实现。

        <!--index-->
        <bean id="user" class="com.pojo.User">
            <constructor-arg index="0" value="KyoDante"/>
        </bean>
    
        <!--type-->
        <bean id="user" class="com.pojo.User">
            <constructor-arg type="java.lang.String" value="KyoDante"/>
        </bean>
    
        <!--arg name-->
        <bean id="user" class="com.pojo.User">
            <constructor-arg name="name" value="KyoDante"/>
        </bean>
    

    Spring配置说明

    别名(Alias)

    <alias name="user" alias="aaaaa"/>
    

    Bean配置

    id是bean的唯一标识;
    class是bean的对应的类:包名+类名;
    name也是别名,而且可以同时取多个别名(用逗号或者空格分割)。

        <bean id="user" class="com.pojo.User" name="bbbbb,ccccc">
            <constructor-arg name="name" value="KyoDante"/>
        </bean>
    

    Import

    一般是多个beans.xml合并为一个。注意:重名的bean是后一个覆盖前一个。

    依赖注入(DI)

    构造器注入

    之前的部分就是构造器

    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="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="student" class="com.pojo.Student">
            <property name="name" value="KyoDante"/>
        </bean>
    </beans>
    
    1. 注入

    这样是空串,String(""),而不是null。

    <property name="wife" value=""/>
    

    想要null则是下面的方式。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="address" class="com.pojo.Address">
            <property name="address" value="潮汕"/>
        </bean>
    
        <bean id="student" class="com.pojo.Student">
            <property name="name" value="KyoDante"/>
            <property name="address" ref="address"/>
    
            <property name="books">
                <array>
                    <value>红楼梦</value>
                    <value>水浒传</value>
                    <value>三国演义</value>
                    <value>西游记</value>
                </array>
            </property>
    
            <property name="hobbies">
                <list>
                    <value>听歌</value>
                    <value>看电影</value>
                    <value>看书学习</value>
                </list>
            </property>
    
            <property name="card">
                <map>
                    <entry key="身份证" value="777777777"/>
                    <entry key="银行卡" value="888888888"/>
                </map>
            </property>
    
            <property name="games">
                <set>
                    <value>DMC</value>
                    <value>DNF</value>
                    <value>LOL</value>
                </set>
            </property>
    
            <property name="wife">
                <null/>
            </property>
    
            <property name="info">
                <props>
                    <prop key="性别">男</prop>
                    <prop key="姓名">KyoDante</prop>
                </props>
            </property>
        </bean>
    
    </beans>
    

    扩展方式注入

    p空间和c空间。

    <?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:c="http://www.springframework.org/schema/c"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="user" class="com.pojo.User" p:age="18" p:name="KyoDante">
        </bean>
    
        <bean id="user2" class="com.pojo.User" c:age="20" c:name="Dante">
        </bean>
    </beans>
    

    bean的默认scope(作用域)是singleton,可以调为prototye.

    <bean id="user2" class="com.pojo.User" c:age="20" c:name="Dante" scope="prototype">
    </bean>
    

    web开发分别有request、session等作用域,分别生命周期为单次HTTP request、HTTP session等。

    自动装配(autowire)

    • 可以节省一部分的xml语句,自动注入字段。
    • byName:要保证bean的id唯一,且属性和set方法的值一致!
    • byType:要保证所有bean的class唯一,且bean需要和自动注入的属性的类型一致!
    <bean id="dog" class="com.pojo.Dog"/>
        <bean id="cat" class="com.pojo.Cat"/>
        <bean id="people" class="com.pojo.People" autowire="byType">
            <property name="name" value="KyoDante"/>
    <!--        <property name="cat" ref="cat"/>-->
    <!--        <property name="dog" ref="dog"/>-->
        </bean>
    

    若使用注解,则需要配置beans.xml文件里面的context,打开annotation-config。

    xmlns:context="http://www.springframework.org/schema/context"
    
    xsi:schemaLocation="http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd"
    
    <context:annotation-config/>
    

    通常使用Autowired(如果设置了required=false,则可以为空对象)和Qualifier(当id和class找不到名字的时候,用value来指定名字)搭配使用。

    @Autowired(required = false)
    @Qualifier(value = "dog222")
    private Dog dog;
    
    • @Autowired 通过byType的方式实现。
    • 还有@Resource可以了解。(高版本JDK似乎没有了)先byName到byType。
    • 都可以放在属性字段上。

    注解

    @Nullable 学会这个注解,说明这个字段可以为null。

    注意:Spring4之后,要使用注解,需要确保aop的包导入了。同时配置context和annotation-config

    使用<context:component-scan/>来扫描应用注解的包。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:component-scan base-package="com.pojo"/>
        <context:annotation-config/>
    </beans>
    
    // 等价于 <bean id="user" class="com.pojo.User" />
    @Component
    public class User {
        // 等价于 <property name="name" value="KyoDante"/>
        @Value("KyoDante")
        public String name;
    }
    

    @Component在Web开发以MVC延申:

    • dao [@Repository]
    • service [@Service]
    • controller [@Controller]
    //等价于<bean id="user" class="com.pojo.User" scope="prototype"/>
    @Scope("prototype")
    public class User {
    

    总结

    • xml相对万能,维护相对简单。
    • 注解,不是自己的类用不了,注解维护困难。
    • 最佳实践:xml管理beans,注解管理属性注入。
    • 注意导入和配置。

    用JavaConfig来配置Spring

    • 直接@Bean!
    public class Config {
        @Bean
        public User getUser(){
            return new User();
        }
    }
    

    使用@Bean的话,最后获取使用getBean("getUser");

    • @Configuration+@Conponent+@ConponentScan
    @Configuration
    @ComponentScan("com")
    public class Config {
    }
    
    @Component
    public class User {
        @Value("KyoDante")
        private String name;
    
        public User() {
            System.out.println("no arg");
        }
    
        public User(String name) {
            System.out.println("a arg");
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    扫描包的话,最终获取使用getBean("user");

    • 联合两个配置(把Config2注入)
    @Import(Config2.class)
    public class Config {
    }
    

    同时使用时,如果用getBean("getUser");会出现两次构造函数,一次是因为Spring的单例,另一次是因为调用getUser()方法里面new了一个实例,这样会打破单例模式。

    • SpringBoot里随处可见注解!

    代理

    代理模式(SpringAOP底层)

    代理:比如房产中介、婚姻中介。

    • 静态代理
      • 抽象角色 使用抽象类或接口解决
    public interface Rent {
        void rent();
    }
    
    • 真实角色 被代理的角色
    public class Renter implements Rent{
        @Override
        public void rent() {
            System.out.println("房东要出租啦");
        }
    }
    
    • 代理角色 代理真实角色,代理真实角色后,一般会做附属操作
    public class Proxy {
        private Renter renter;
    
        public Proxy() {
        }
    
        public Proxy(Renter renter) {
            this.renter = renter;
        }
    
        public void seeHouse() {
            System.out.println("带你看房");
        }
    
        public void signContract() {
            System.out.println("签合同");
        }
    
        public void fee() {
            System.out.println("收中介费");
        }
    
        public void rent() {
            seeHouse();
            signContract();
            fee();
            renter.rent();
        }
    }
    
    • 客户 访问代理对象的人(需要隔着中介进行租房)
    public class Client {
        public static void main(String[] args) {
            Renter renter = new Renter();
            Proxy proxy = new Proxy(renter);
    //        renter.rent();
            proxy.rent();
        }
    }
    

    好处

    • 真实角色操作纯粹,不用管公共服务
    • 业务分工,公共服务给了代理完成
    • 公共服务扩展的时候方便

    缺点

    • 一个真实角色就会有一个代理,代码量增多

    尽量不动原本的业务代码,所以在原本的实现上面加一层代理,可以在代理之上添加新的功能。

    public interface UserService {
        void add();
        void delete();
        void update();
        void select();
    }
    
    public class UserServiceImp 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 select() {
            System.out.println("查找");
        }
    }
    
    public class UserServiceProxy {
        private UserService userService;
    
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    
        public void log(String msg) {
            System.out.println("[Debug]添加了:"+msg+"方法");
        }
    
        public void add() {
            log("add");
            userService.add();
        }
    
        public void delete() {
            log("delete");
            System.out.println("删除");
        }
    
        public void update() {
            log("update");
            System.out.println("改变");
        }
    
        public void select() {
            log("select");
            System.out.println("查找");
        }
    }
    
    public class Client {
        public static void main(String[] args) {
            UserServiceImp userServiceImp = new UserServiceImp();
            userServiceImp.add();
            // 用代理模式添加了一个日志功能。
            UserServiceProxy userServiceProxy = new UserServiceProxy();
            userServiceProxy.setUserService(userServiceImp);
            userServiceProxy.add();
        }
    }
    

    (AOP的原理:代理)
    (实现一些Service)
    | |
    ->Service->Controller->User

    • 动态代理

    • 动态代理和静态代理角色一样

    • 动态生成

    • 基于接口和类的动态代理

      • 基于接口-JDK动态代理【在这里使用】
      • 基于类-cglib
      • java字节码实现-javassist

    需要了解两个类:Proxy:代理、InvocationHandler:调用处理程序

    public class ProxyInvocationHandler implements InvocationHandler {
    
        // 目标被代理接口
        Object target;
    
        // 设置被代理接口
        public void setTarget(Object target) {
            this.target = target;
        }
    
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //        log(method.getName());
            Object result = method.invoke(target, args);
            return result;
        }
    
        // 日志功能
    //    public void log(String msg) {
    //        System.out.println(msg);
    //    }
    }
    
    public class TestCases {
        public static void main(String[] args) {
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
    
            Renter renter = new Renter();
            pih.setTarget(renter);
            Rent rentProxy = (Rent) pih.getProxy();
            rentProxy.rent();
    
            UserServiceImp userServiceImp = new UserServiceImp();
            pih.setTarget(userServiceImp);
            UserService userProxy = (UserService) pih.getProxy();
            userProxy.add();
            userProxy.delete();
        }
    }
    

    动态代理好处

    • 和静态代理一样的好处
    • 一个动态代理可以代理一个接口,一般对应一种业务
    • 一个动态代理可以代理多个类,只要实现了同一个接口即可。(动态性见上例即可有所体会。)

    建议:返回去学习注解和反射的相关课程

    AOP

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

    AOP在Spring中的作用

    • 提供声明式事务,允许用户自定义切面
    • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如:日志,安全,缓存,事务等等。
    • 切面(ASPECT):横切关注点 被模块化的 特殊对象。即,它是一个类。
    • 通知(Advice):却面必须要完成的工作。即,它是类中的一个方法。
    • 目标(Target):被通知对象。
    • 代理(Proxy):向目标对象应用通知之后创建的对象。
    • 切入点(PointCut):切面通知执行的“地点”的定义。
    • 连接点(JointPoint):与切入点匹配的执行点。

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

    即不改变原本代码的情况下,添加功能。

    实现方式一(SpringAOP的API)

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.7</version>
    </dependency>
    

    需要配置切入的店,以及通知的内容

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="userService" class="service.UserServiceImp"/>
        <bean id="log" class="log.Log"/>
        <bean id="afterLog" class="log.AfterLog"/>
    
        <aop:config>
            <aop:pointcut id="pointcut" expression="execution(* service.UserServiceImp.*(..))"/>
    
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
        </aop:config>
    
    </beans>
    
    public class Log implements MethodBeforeAdvice {
        // o:被代理的类
        // method:被执行方法
        // objects:参数
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
        }
    }
    
    public class AfterLog implements AfterReturningAdvice {
        // o:返回值
        // method:被执行方法
        @Override
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
            System.out.println("执行了"+method.getName()+"返回结果为"+o);
        }
    }
    
    public class TestCases {
        @Test
        public void test() {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            // 代理的是接口
            UserService userService = context.getBean("userService", UserService.class);
    
            userService.add();
        }
    }
    

    实现方式二(自定义)

    public class MyAspect {
        public void before() {
            System.out.println("before+++");
        }
    
        public void after() {
            System.out.println("after+++");
        }
    }
    
    <bean id="myAspect" class="aspects.MyAspect"/>
    
        <aop:config>
            <aop:aspect ref="myAspect">
                <aop:pointcut id="point" expression="execution(* service.UserServiceImp.*(..))"/>
    
                <aop:before method="before" pointcut-ref="point"/>
                <aop:after method="after" pointcut-ref="point"/>
            </aop:aspect>
        </aop:config>
    

    实现方式三(注解实现AOP)

    <bean id="annotationAspect" class="aspects.AnnotationAspect"/>
        <!-- proxy-target-class="false" jdk   -->
        <!-- proxy-target-class="true" cglib  -->
        <aop:aspectj-autoproxy/>
    
    @Aspect // 标注为切面
    public class AnnotationAspect {
    
        @Before("execution(* service.UserServiceImp.*(..))")
        public void before() {
            System.out.println("方法执行前");
        }
    
        @After("execution(* service.UserServiceImp.*(..))")
        public void after() {
            System.out.println("方法执行后");
        }
    
        @Around("execution(* service.UserServiceImp.*(..))")
        public void around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("环绕前");
            Object proceed = pjp.proceed();
            System.out.println("环绕后");
    
            System.out.println(pjp.getSignature());
            System.out.println(proceed);
        }
    }
    

    MyBatis

    整合MyBatis

    1. 导包(连接mysql,mybatis, spring-mybatis,spring-jdbc,aspectj等包)
    <dependencies>
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.28</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.7</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                  <groupId>org.mybatis</groupId>
                  <artifactId>mybatis</artifactId>
                  <version>3.5.6</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.7</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.19.RELEASE</version>
            </dependency>
        </dependencies>
    
    1. 使用mybatis:
    • 实体类
    • 核心配置
    • 接口
    • Mapper

    原本的mybatis-config-old.xml

    <?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.pojo"/>
        </typeAliases>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF8&amp;serverTimezone=UTC"/>
                    <property name="username" value="账号"/>
                    <property name="password" value="密码"/>
                </dataSource>
            </environment>
        </environments>
        <!--Mapper.XML-->
        <mappers>
            <mapper resource="com/mapper/UserMapper.xml"/>
        </mappers>
    </configuration>
    

    UserMapper.xml

    <?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.mapper.UserMapper">
        <!--select-->
        <select id="getUsers" resultType="User">
            select * from mybatis.user;
        </select>
    </mapper>
    

    转换成spring-mybatis,需要让spring接管mybatis的一些配置。具体如下:

    mybatis-config.xml

    <?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.pojo"/>
        </typeAliases>
    
    </configuration>
    

    spring-config.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 http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!--  DataSource:use spring's datasource to replace mybatis' settings (c3p0 dbcp druid)  -->
        <bean id = "dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF8&amp;serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="XIAOLIkyo54123414"/>
        </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/mapper/*.xml"/>
        </bean>
    
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
        <bean id="userMapper" class="com.mapper.UserMapperImp">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    </beans>
    

    具体的实现类:

    public class UserMapperImp implements UserMapper {
    
        SqlSession sqlSession;
    
        public void setSqlSession(SqlSession sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        @Override
        public List<User> getUsers() {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return mapper.getUsers();
        }
    }
    
    • 编写数据源
    • SqlSessionFactory
    • SqlSessionTemplate
    • 给接口加实现类(Imp)
    • 实现类注入到Spring中
    • 测试

    test测试了原本的mybatis,test2使用的则是spring-myabtis。

    public class TestCases {
        @Test
        public void test() {
            String resource = "mybatis-config-old.xml";
            SqlSessionFactory sqlSessionFactory;
            try (InputStream inputStream = Resources.getResourceAsStream(resource);){
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                SqlSession sqlSession = sqlSessionFactory.openSession();
                UserMapper mapper = sqlSession.getMapper(UserMapper.class);
                List<User> users = mapper.getUsers();
                for (User user : users) {
                    System.out.println(user);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Test
        public void test2() {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
            UserMapperImp userMapperImp = context.getBean("userMapper", UserMapperImp.class);
            List<User> users = userMapperImp.getUsers();
            for (User user : users) {
                System.out.println(user);
            }
        }
    }
    
    1. 测试(另一种实现方式,即官方帮我们实现了方式一,只需要继承SqlSessionDaoSupport,注入sqlSessionFactory即可)
    public class UserMapperImp2 extends SqlSessionDaoSupport implements UserMapper {
        @Override
        public List<User> getUsers() {
            return getSqlSession().getMapper(UserMapper.class).getUsers();
        }
    }
    
    <bean id="userMapper2" class="com.mapper.UserMapperImp2">
            <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    
    @Test
        public void test3() {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
            UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
            List<User> users = userMapper.getUsers();
            for (User user : users) {
                System.out.println(user);
            }
        }
    

    事务回顾

    1. 回顾事务
    • 一组业务当成一个业务来做
    • 重要,涉及到数据的一致性问题
    • 确保完整性和一致性

    事务ACID原则:

    • Atomic 原子性
    • Consistency 一致性
    • Isolation 隔离性
      • 多个业务可能操作同一个资源,防止数据损坏
    • durability 持久性
      • 事务一旦提交,无论系统发生什么问题,结果都不会再被影响

    Spring中的事务管理

    • 声明式事务:AOP
    • 编程式事务:在代码中进行事务管理

    UserMapper.xml(delete语句写错为deletes来测试事务)

    <?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.mapper.UserMapper">
        <!--select-->
        <select id="getUsers" resultType="User">
            select * from mybatis.user;
        </select>
    
        <insert id="addUser" parameterType="User">
            insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd})
        </insert>
    
        <delete id="deleteUser" parameterType="int">
            deletes from mybatis.user where id=#{id}
        </delete>
    </mapper>
    
    public class UserMapperImp extends SqlSessionDaoSupport implements UserMapper{
    
        @Override
        public List<User> getUsers() {
            UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
            mapper.addUser(new User(4, "KyoDante", "1111111"));
            mapper.deleteUser(5);
            return mapper.getUsers();
        }
    
        @Override
        public int addUser(User user) {
            UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
            return mapper.addUser(user);
        }
    
        @Override
        public int deleteUser(int id) {
            UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
            return mapper.deleteUser(id);
        }
    }
    

    spring-config.xml

    <!-- set up transactions -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!-- advice -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!-- which method -->
            <!-- propagation features -->
            <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="txPointCut" expression="execution(* com.mapper.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
        </aop:config>
    
    @Test
        public void test() {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
            UserMapper mapper = context.getBean("userMapper", UserMapper.class);
            List<User> users = mapper.getUsers();
            for (User user : users) {
                System.out.println(user);
            }
        }
    

    为什么需要事务?

    • 不配置事务,可能存在数据提交不一致的情况,如上例(增加成功,但是删除失败的时候,需要进行回滚)
    • 如果不用声明式AOP来配置事务,则需要通过代码的方式来进行。
    • 事务涉及到数据的一致性和完整性问题。

    总结回顾

    • IOC
    • 代理
    • AOP

    相关文章

      网友评论

          本文标题:Spring笔记

          本文链接:https://www.haomeiwen.com/subject/gqbkhrtx.html