Spring笔记

5 篇文章 0 订阅

1、Spring

spring是一个轻量级、非入侵式的控制反转(IOC)和面向切面(AOP)的容器框架.

  • SpringBoot

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

    • 基于SpringBoot实现的

控制反转 IoC

IoC是一种设计思想,而DI(依赖注入)只是实现 IOC的其中一种方法。

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

Hello,Spring!

  • 控制:谁来控制对象的创建!传统的应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
  • 反转:程序本身不创建对象,而变成被冻的接收对象
  • 依赖注入:就是利用set方法进行注入
  • IOC是一种编程思想,由主动的编程变为被动的接收。对象由Spring来创建、管理、装配!

Pojo:

package com.shen.pojo;

import lombok.Data;

@Data
public class Hello {
    private String str;
}

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
               https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--使用spring来创建对象,在spring这些都称为bean-->
    <bean id="hello" class="com.shen.pojo.Hello">
        <property name="str" value="Hello,spring!"></property>
    </bean>
</beans>

Test:

import com.shen.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    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());
    }
}

IoC例子2:

ServiceImpl:

package com.shen.service;

import com.shen.dao.UserDao;
import lombok.Data;

@Data
public class UserServiceImpl implements UserService{
    private UserDao userDao;

    @Override
    public void printDaoInfo() {
        userDao.printInfo();
    }
}

beans.xml:

<!--使用spring来创建对象,在spring这些都称为bean-->
    <bean id="mysqlDao" class="com.shen.dao.UserDaoMysqlImpl"></bean>
    <bean id="oracleDao" class="com.shen.dao.UserDaoOracleImpl"></bean>
    <bean id="service" class="com.shen.service.UserServiceImpl">
        <!--
            ref: 引用Spring容器中创建好的对象
            value:具体的值,基本数据类型!
        -->
        <property name="userDao" ref="oracleDao"></property>
    </bean>

test:

public class MyTest {
    public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserServiceImpl service = (UserServiceImpl)context.getBean("service");
        service.printDaoInfo();
    }
}

使用配置的好处在于,当需要修改set注入的内容的时候,不需要重启服务器,因为没有改动源代码,改的是xml配置!!而且解耦了,如果要增加新的实现,只需要写新的实现,并且替换配置。

IoC创建对象方式

  • 默认走的是无参构造

  • 可以使用有参构造

    • 1.按参数类型
    <bean id="service" class="com.shen.service.UserServiceImpl">
            <constructor-arg type="java.lang.String" value="有参构造的bean"></constructor-arg>
        </bean>
    

    缺点在于 如果有两个参数都是同类型,就不行了,所以不建议使用此法。(如果非要用的话,可以按顺序赋值)

    • 2.按照下标
    <constructor-arg index="0" value="有参构造的bean"></constructor-arg>
    
    • 3.匹配参数名
    <constructor-arg name="str" value="直接通过参数名赋值"></constructor-arg>
    

2、Spring配置

  1. 别名:alias,和bean是同一级别的标签。name,在bean标签内,也是别名,而且name可以取多个别名(用逗号、空格、分号进行分割)。

  2. import,和bean是同一级别的标签。一般用于团队开发使用,可以将多个配置文件导入合并为一个。

    <import resource="beans2.xml"/>
    

    注:使用import导入的时候,如果遇到同名的东西,后面的会覆盖前面的(因为按照从上到下的顺序进行导入合并)

  3. scope 在bean标签内,可以设置是否为单例模式(默认单例)

3、依赖注入

3.1 构造器注入

上面写过了

也可以用c命名空间

3.2 set方式注入(重点)

也可以用p命名空间

  • 依赖:bean对象的创建依赖于spring这个容器
  • 注入:bean对象中的所有属性,由容器注入
		<bean id="student" class="com.shen.pojo.Student" name="stu">
        <!--普通注入-->
        <property name="name" value="孟特"></property>
        <property name="id" value="4"></property>
        <!--对象注入(bean注入)-->
        <property name="address" ref="add"></property>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>《西游记》</value>
                <value>《三国演义》</value>
            </array>
        </property>
        <!--list注入-->
        <property name="hobbies">
            <list>
                <value>弹吉他</value>
                <value>写代码</value>
            </list>
        </property>
        <!--map注入-->
        <property name="scopes">
            <map>
                <entry key="语文" value="90"></entry>
                <entry key="数学" value="100"></entry>
            </map>
        </property>
        <!--properties注入,key-value格式-->
        <property name="info">
            <props>
                <prop key="学号">201920085211015</prop>
                <prop key="sex"></prop>
            </props>
        </property>
    </bean>
    <bean id="address" class="com.shen.pojo.Address" name="add">
        <constructor-arg name="addressName" value="梆子井4503"></constructor-arg>
        <!--set注入-->
        <!--测试注入一个空字符串,和一个null-->
        <property name="concludes">
            <set>
                <value>北京市</value>
                <value>朝阳区</value>
                <value></value>
                <value>null</value>
                <null></null>
                <value>三里屯</value>
            </set>
        </property>
    </bean>

3.3 拓展方式注入

p命名空间、c命名空间

需要引入第三方xml约束:

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

3.4 Bean scope (作用域)

  • Singleton 单例

    • 每次从容器中get的时候都会返回同一个对象
  • prototype 原型

    • 每次从容器中get的时候都会产生一个新的对象,其实这就是原型模式

    下面的这些只有在web中才能用

  • request

  • session

  • websocket

  • Application

4、Bean的自动装配(autowired)

spring不仅可以手动依赖,还可以实现自动依赖!

spring会在上下文中自动寻找,并且给bean装配属性

有三种装配的方式

  • 在xml中显示配置
  • 在java中显示配置
  • 隐式的自动配置

自动装配的实现方法:

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

    比如,当前bean对象有一个方法叫setCat,就会自动寻找一个id叫做的cat的bean对象,把它装配进去

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

    • 弊端:必须保证每个类型的实体全局唯一

5、使用注解进行自动装配

要使用注解须知:

  1. 导入约束
  2. 配置注解的支持
<?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>

例:

<bean id="dog" class="com.shen.pojo.Dog" >
        <property name="name" value="wang~"></property>
    </bean>
    <bean id="cat" class="com.shen.pojo.Cat" >
        <property name="name" value="miao~"></property>
    </bean>
    <bean id="people" class="com.shen.pojo.People" />
public class People {
    @Autowired
    private Dog dog;
    @Autowired
    private Cat cat;
    private String name;

    @Override
    public String toString() {
        return "People{" +
                "dog=" + dog +
                ", cat=" + cat +
                ", name='" + name + '\'' +
                '}';
    }
}

我们发现:没有set方法,仅使用注解,也可以成功注入!!这是为什么呢?

因为使用注解的时候,不是通过set注入,而是通过反射直接获取了变量名以后进行注入

小结

@Autowired既可以直接在属性上使用,也可以在set方式上使用。

@Autowired(required = false) 可以为null

科普

@Nullable 字段标记了这个注解,则这个字段可以为null而不报错!

如果自动装配环境比较复杂,无法通过一个注解完成的时候(比如一个类有多个bean实例),我们可以在@Autowired后面接一个@Qualifier(value = “dog111”) 来指定唯一的bean对象。

也可以不使用两个注解,而只使用@Resource(name = “xxx”),这个是java自带的注解,先通过名字匹配byName,不唯一的话再通过类型匹配byType。都找不到就报错了。

6、使用注解开发

在Spring4之后,要使用注解开发,必须保证aop的包已经导入

  1. bean

    package com.shen.pojo;
    
    import lombok.Data;
    import org.springframework.stereotype.Component;
    
    // @Component等价于 <bean id="user" class="com.shen.pojo.User"/>
    @Data
    @Component
    public class User {
        private String name;
    }
    
  2. 属性如何注入

    @Component
    public class User {
        // @Value 相当于bean里的property的value
        @Value("沈航冉")
        private String name;
    }
    
    
  3. 衍生的注解

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

    • dao【@Repository】 功能是一样的,但我们习惯用@Repository给dao层的东西加注解,也代表它是一个组件(component)

    • service【@Service】

    • controller【@Controller】

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

  4. 自动装配

  5. 作用域

    @Scope(“prototype”)

    采用原型模式(默认为singleton)

  6. 小结

    • xml更加万能,适用于任何场合,维护方便简单
    • 注解 不是自己的类不能用,维护相对复杂

    最佳实践:xml用来管理bean,注解只负责使用的注入。

    使用注解记得需要开启注解的支持。

7、使用Java的方式配置Spring

现在我们不使用Spring的xml配置了,全权交给java来做。

JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能。

@Data
@Component
public class User {
    @Value("沈阳的沈")
    private String name;
}

// 这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml一样的
@Configuration
public class ShenConfig {
  // 注册一个bean 就相当于我们之前写的一个bean标签 默认单例
    @Bean
    public User getUser(){
        return new User();
    }
}
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(ShenConfig.class);
        User getUser = context.getBean("getUser", User.class);
        System.out.println(getUser);
    }
}

8、代理模式

和装饰器模式的语法完全一样,只是语意不一样!

SpringAOP的底层就是代理模式.

8.1、静态代理

代码步骤:

  1. 公共接口
  2. 真实角色
  3. 代理角色
  4. 客户端访问代理角色

和装饰器模式完全相同。

  • 可以使真实角色更加纯粹
  • 公共的事情交给代理角色来做

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aW48FLto-1625239701232)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210629162314168.png)]

原来是纵向开发,在不改动原有DAO层代码的时候,想给DAO层加一个日志功能,就可以采取和Service层并列的新建一个代理模式的类来完成,这就是横向开发。

8.2、动态代理

动态代理的代理类是动态生成的,不是我们直接写好的。

动态代理分为两大类:

  • 基于接口的动态代理
    • JDK动态代理【接下来使用这个】
  • 基于类的动态代理
    • cglib
    • java字节码实现:javasist

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

例子:

用来生成代理类(对象)的类,详情看注释:

package shen;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 我们会用这个类来自动生成代理类----动态代理
public class ProxyInvocationHandler implements InvocationHandler {
    // 被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    // 生成得到代理类
    public Object getProxy(){
        // 三个参数:生成的代理类【放到哪】、【代理的那个类是什么类型的接口】、【如何生成(使用重写的invoke方法)】
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    // 处理代理实例,并返回结果(官方原话)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 动态代理的本质,就是使用反射机制!
        // 这里的rent是被代理的那个类
        Object invoke = method.invoke(rent, args);
        // 在这里使用中介独有的新增加的方法
        seeHouse();
        fare();
        return invoke;
    }

    // 中介独有方法:带客户看房子
    public void seeHouse(){
        System.out.println("中介带用户看房子");
    }
    public void fare(){
        System.out.println("中介收你中介费");
    }
}

代码步骤:

  1. 设置要代理的类是谁,给它写一个set方法
  2. 利用要代理的类,生成得到代理类
  3. 处理代理实例,返回结果(添砖加瓦)

客户这样使用:

public class Client {
    public static void main(String[] args) {
        Host host = new Host(); // 真实角色
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(); // 代理角色
        proxyInvocationHandler.setRent(host);
        Rent proxy = (Rent)proxyInvocationHandler.getProxy(); // 这里的proxy就是动态生成的,我们并没有写一个真实的代理类
        proxy.rrrrent();
    }
}

于是,我们发现了,上面写的ProxyInvocationHandler类==可以当做一个工具类了,因为它可以代理任何一个类呀!==比如要增加日志功能,只需要自顶一个log方法,并放到invoke方法里使用就可以了!

public void log(String msg){
        System.out.println("[Debug]执行了"+msg+"方法");
    }

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(rent, args);
        log(method.getName());
        return invoke;
    }

通过method.getName()反射可以获取方法名呀!

动态代理的好处:

  • 可以使真实角色的操作更加纯粹!不用关注一些公共的业务(比如log)
  • 公共业务可以交给代理角色,实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要实现了同一个接口就行,复用成本低。

使用spring实现aop,需要导入一个依赖包

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

8.3 使用spring的aop

方式1:使用原生API接口

写一个接口和一个要被代理的类,最终目的是给它增加日志功能。

public class UserServiceImpl implements UserService{

    @Override
    public void add() {
        System.out.println("使用了add方法");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void update() {
        System.out.println("使用了update方法");
    }

    @Override
    public void query() {
        System.out.println("使用了query方法");
    }
}

使用原生API接口,定义在某方法执行前,我们要做的事情

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
    // method: 要执行的目标对象的方法
    // objects:参数
    // o:目标对象
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行啦!");
    }
}

使用xml配置aop,定义切入点(在哪里执行log方法),并且定义执行环绕增加(log方法是谁)

<bean id="userService" class="com.shen.UserServiceImpl"/>
<bean id="log" class="com.shen.log.Log"/>

    <!--方式一:使用原生API接口-->
    <!--配置aop-->
    <aop:config>
        <!--切入点:在哪个地方执行方法 , expression:表达式,execution(要执行的位置!* * * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.shen.UserServiceImpl.*(..))"/>
        <!--执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    </aop:config>

测试

注意,我们动态代理代理的是接口,而不是具体实现类,所以如果写成具体实现类会报错!

import com.shen.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        // 动态代理代理的是接口,而不是实现类!
//        UserServiceImpl userservice = context.getBean("userService", UserServiceImpl.class);
        UserService userservice = context.getBean("userService", UserService.class);
        userservice.delete();

    }
}

方式2:使用自定义类实现AOP

package com.shen.diy;

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

将自定义类当做切面,配置到aop中

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

很明显,第一种方法功能要强大的多。因为第一种用到了反射。

方式3:使用注解实现

自定义切面类,在上面使用注解

补充:环绕增强,详见代码。

package com.shen.diy;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

// 方式三:使用注解方式实现AOP
@Aspect // 标注这个类是一个切面
public class AnnotationPointCut {
    @Before("execution(* com.shen.UserServiceImpl.*(..))") // 注解代表通知,其参数代表切入点
    public void before(){
        System.out.println("注解实现的方式执行之前");
    }
}
// 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点:
    @Around("execution(* com.shen.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("===环绕前===");
        // 获得签名
        Signature signature = proceedingJoinPoint.getSignature();
        System.out.println("签名:"+signature);
        // 执行方法
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("===环绕后===");
    }

然后将这个类配置到xml中,成为bean对象。最后,开启注解支持

<!--方式三:使用注解实现AOP-->
    <bean id="annotationPointCut" class="com.shen.diy.AnnotationPointCut"/>
    <!--开启注解支持!-->
    <aop:aspectj-autoproxy/>

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IUg3Pl4g-1625239701234)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210630231832789.png)]

8.4 名词总结

  • 横切关注点:与业务逻辑无关的,我们要实现的功能的概述。比如“日志、安全、缓存等等”
  • 切面(aspect):横切关注点被模块化的特殊对象。即,一个类
  • 通知(advice):切面要完成的工作。即,一个方法
  • 目标(target):被通知对象。即,被代理的类对象。
  • 代理(proxy):向目标对象应用通知后创建的对象。即,使用动态代理模式创建出的代理类对象。
  • 切入点(pointcut):切面通知执行的“地点”的定义。即,在哪里执行“通知”这个方法。
  • 连接点(jointpoint):与切入点匹配的执行点。

9、整合Mybatis

步骤:

  1. 导入相关jar包

    • junit
    • mybatis
    • mysql数据库
    • spring相关的
    • aop织入
    • mybatis-spring
    <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.25</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.3.8</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.3.7</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.7</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.2</version>
            </dependency>
        </dependencies>
    
  2. 编写配置文件

  3. 测试

整合步骤:

将spring的配置和mybatis的配置都整合到spring中,首先需要创建一个spring-dao.xml

其实本质就是把mybatis里的datasource、sqlSessionFactory和sqlSession交给spring托管

<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"
       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/context
               https://www.springframework.org/schema/context/spring-context.xsd
               http://www.springframework.org/schema/aop
               https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--DataSource: 使用spring的数据源替换Mybatis的配置   c3p0  dbcp
    在这里使用spring提供的jdbc
    -->
    <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/smbms?useSSL=TRUE&amp;useUnicode=TRUE&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="jiawensili1029"/>
    </bean>
    <!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource"/>
        <!--绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config2.xml"/>
        <property name="mapperLocations" value="classpath:com/shen/mapper/*.xml"/>
    </bean>

    <!--这个SqlSessionTemplate相当于原来的sqlSession,template就是模板的意思。这个类需要注入一个sqlSession工厂-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--只能使用构造器注入,因为它没有set方法-->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <bean id="userMapper" class="com.shen.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

</beans>

其中,userMapper是自定义的一个接口的实现类,它的作用就是,把sqlSession注入进来,然后使用它!而它本身又成为一个bean对象,方便我们spring来托管。

package com.shen.mapper;

import com.shen.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper{
    private SqlSessionTemplate sqlSession;

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

    @Override
    public List<User> getAllUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.getAllUser();
    }
}

在配置文件中,我们注意下面这段:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource"/>
        <!--绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config2.xml"/>
        <property name="mapperLocations" value="classpath:com/shen/mapper/*.xml"/>
    </bean>

其中,mapperLocations包含了所有mapper.xml,比如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "htto://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shen.mapper.UserMapper">

    <select id="getAllUser" resultType="user">
        select * from user_test
    </select>
</mapper>

我们的sql语句就是在这里写的!

而configLocation则是原来mybatis的配置文件。

由于原来mybatis的配置文件有很多内容已经集合到spring-dao.xml里了,所以现在的mybatis配置文件只剩下“别名”和“设置”这两部分内容!

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "htto://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心-->
<configuration>

    <!--设置要留着,可以添加其他设置-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <typeAliases>
        <typeAlias type="com.shen.pojo.User" alias="user"/>
    </typeAliases>

</configuration>

如果没有别名的话,写sql会比较麻烦,而设置里我们可以添加其他的设置,比如日志。

此外,由于除了userMapper这个bean对象以外,其他都是固定的,因此我们可以上面一部分内容单独拿出来固定在一个xml里,再新建一个总的application.xml,把上面那个xml通过resource的形式引入,这样就不会显得乱了!(解耦了)

<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"
       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/context
               https://www.springframework.org/schema/context/spring-context.xsd
               http://www.springframework.org/schema/aop
               https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--DataSource: 使用spring的数据源替换Mybatis的配置   c3p0  dbcp
    在这里使用spring提供的jdbc
    -->
    <import resource="spring-dao.xml"/>

    <bean id="userMapper" class="com.shen.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

</beans>

注:sqlSessionTemplate是线程安全

10、声明式事务

    @Override
    public List<User> mixFixed() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.addUser(new User("shr", "shrshrshr", "沈航冉"));
        mapper.deleteUser("shr");
        List<User> allUser = mapper.getAllUser();
        return allUser;
    }
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserMapperImpl userMapper = context.getBean("userMapper", UserMapperImpl.class);
        List<User> allUser = userMapper.mixFixed();
        for(User user:allUser){
            System.out.println(user);
        }
    }

经过测试,我们明明把好几个任务混合起来,当做了一个“新的任务”,它是一个新的原子性,应该当做一个事务来处理,但我们这么跑下来,虽然程序会报错(我们故意写错了delete的sql语句),但是还是成功插入了一组数据,说明这个方法并不是满足ACID原则的,说明它没有完成事务的功能!

为此,我们要使用声明式事务。

补充:

  • 声明式事务:AOP
  • 编程式事务:需要在代码中进行事务的管理(使用try、catch、rollback等)

声明式事务需要在spring的配置文件里做,本质就是spring帮我们写好了一个事务管理器。

    <!--配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>

切入点处的方法已经被tx实现了,所以我们导入tx依赖,就可以写成下面这样.

tx的作用就是实现了事务处理的一些方法,我们要把这些方法配置到使用的地方(切入点)

tx这一套东西相当于是一个切面(理解为一个bean对象,所以可以使用advice-ref来增加执行环绕)

		<!--结合AOP实现事务的织入-->
    <!--配置事务通知的类(需要在最上面导入tx)-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--给哪些方法配置事务-->
        <!--配置事务的传播特性:propagation-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" 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.shen.mapper.*.xml.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
    </aop:config>
  • 补充:配置事务的传播特性(七种):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-prPOjvAk-1625239701235)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210702205923362.png)]

经测试,发现执行mixFixed方法还是把东西插进去了,找到原因:

  • 因为mixFixed方法并没有在mapper中注册,而只是在UserMapperImpl里写明了它是把好几个事情混在一起的方法,我们的配置事务作用域并没有包含UserMapperImpl,而只包含了所有的mapper底下的所有的xml里的东西。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQLRfdyP-1625239701236)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210702213239187.png)]

  • 解决方案:

    1. 改为mapper底下所有文件,而不只是xml文件

      <!--配置事务切入-->
          <aop:config>
              <aop:pointcut id="txpointcut" expression="execution(* com.shen.mapper.*.*(..))"/>
              <aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
          </aop:config>
      
    2. 使用的地方,统一为UserMapper.class,而不是UserMapperImpl.class

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Elvyeevm-1625239701237)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210702214034558.png)]

      不然会报错:

      Bean named ‘xxx’ is expected to be of type ‘xxx’ but was actually of type 'com.sun.proxy.$Proxy14

    3. tx中配置的name必须与方法名相同

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UAZW157X-1625239701238)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210702214300647.png)]

经测试,此处注释掉mixFixed会导致失败.

当然,也可使直接使用*来匹配所有.

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
第一课:面向抽象编程 4 第二课:Jdom的基本使用 4 第三课:模拟Spring功能 5 第四课:搭建sping的运行环境 8 一、 建立一个新的项目 8 二、 建立spring的配置文件 8 三、 引入spring的jar包 8 四、 测试代码: 8 五、 注意接口的使用: 8 第五课:IOC(DI)配置及应用 9 一、 什么是IOC、DI 9 二、 编辑xml文件时,没有提示 9 三、 注入类型(Injecting dependencies) 9 (一) setter注入类型Setter Injection 9 (二) 构造方法Constructor Injection 10 四、 id、name 11 五、 简单属性的注入 11 六、 Bean的作用范围scope 12 七、 集合注入 12 八、 自动装配autowire 13 (一) byName 13 (二) byType 14 (三) 注意 14 九、 生命周期 15 (一) lazy-init/default-lazy-init 15 (二) init-method destroy-method 不要和prototype一起用(了解) 15 第六课:annotation方式Spring 16 一、 开始使用annotation配置Spring 16 二、 @Autowired、@Qualifier 16 (一) @Autowired 16 (二) @Qualifier 17 三、 @Resource(重要、推荐) 17 (一) JSR-250 17 (二) @Resource 17 四、 @Componet 18 五、 @Scope、@PostConstruct、@PreDestroy 19 六、 注解对应的jar包 19 第七课:AOP(面向切面编程) 19 一、 AOP概念 19 二、 利用动态代理实现面向切面编程 20 第八课:Spring AOP配置选项 21 一、 AOP配置annotation方式 21 (一) 搭建annotation开发环境 21 (二) aspectJ类库 22 (三) AOP的annotation实例 22 (四) AspectJ的专业术语 23 (五) 织入点语法 23 (六) Advice 24 (七) Pointcut 26 (八) annotatin方式的AOP实例 26 二、 AOP配置xml方式 27 三、 AOP实现动态代理注意 28 第九课:DataSource 28 一、 Sping配置数据源: 28 二、 注入使用 29 三、 dbcp.BasicDataSource 29 第十课 Spring整合Hiberante3 30 一、 Spring配置hibernate3的SessionFactory 30 (一) xml形式的SessionFactory 30 (二) annotation注解方式的SessionFactory 30 二、 引入hibernate所需要使用的jar 31 (一) 基本jar 31 (二) 加入annotation功能的jar包 31 (三) 搭建日志环境并配置显示DDL语句jar包 31 三、 Spring整合hibernate3事务 31 (一) Annotation注解方式配置事务管理 31 (二) Spring事务选项 35 (三) XML文件形式配置Spring事务管理 37 四、 HibernateTemplate 38 (一) HibernateTemplate 38 (二) HibernateDaoSupport 39 第十一课:Spring整合-SSH 40 一、 第一步:加入jar包(需要的jar包列表) 40 二、 第二步: 首先整合Spring + Hibernate 41 三、 第三步:再来整合Struts2 41 四、 struts的读常量: 43 第十二课:DTO、VO 43 一、 DTO 43 二、 VO 43 第十二课:SSH整合存在的问题 43 一、 Jsp中访问Session时,Session已经关闭 43 二、 如果不配置事务,openSessionView出现异常 44 三、 中文乱码问题: 44 第十三课:SSH整合的jar包 45 一、 Struts2 45 二、 Hibernate3.3.2 45 三、 Spring 46

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值