Spring5学习教程

Spring5

Spring 是于2003年兴起的一款轻量级的,非侵入式的IOC和AOP的一站式的java开发框架,为简化企业级应用开发而生


Spring有两个核心部分:IOCAOP

  • IOC:控制反转,把创建对象过程交给 Spring 进行管理
  • AOP:面向切面,不修改源代码进行功能增强

Spring特点:

  1. 方便解耦,简化开发
  2. Aop 编程支持
  3. 方便程序测试
  4. 方便和其他框架进行整合
  5. 方便进行事务操作
  6. 降低 API 开发难度

image-20230909200228552

IOC容器

定义

控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理

目的:降低耦合度

IOC基于IOC容器完成,IOC容器底层就是对象工厂

Spring提供IOC容器实现两种方式(两个接口):

BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用,其加载配置文件时不会创建对象,在获取对象(使用)才去创建对象

ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用,其加载配置文件时会把配置文件对象进行创建

Bean

概念

Bean是Spring装配的组件模型,一切实体类都可以配置成一个Bean,进而就可以在任何其他的Bean中使用,一个Bean也可以不是指定的实体类,这就是抽象Bean

作用
  1. 用于配置对象让Spring来创建
  2. 默认情况下调用类中的无参构造函数,如果没有无参构造函数则无法创建
属性
  1. id: 给对象在容器中提供一个唯一标识,用于获取对象
  2. class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
  3. scope:指定对象的作用范围
  4. singleton:默认值,单例的
  5. prototype:多例的
  6. request:WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中
  7. session:WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中
  8. global session:WEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession相当于session
Bean管理

IOC操作Bean管理就是两个操作:

  1. Spring创建对象
  2. Spring注入属性

基于XML方式注入属性(DI:依赖注入(注入属性)):

  • set方法注入

    @Test
    public void testAdd(){
        //1.加载spring配置文件
        BeanFactory context=
                new ClassPathXmlApplicationContext("bean2.xml");
    
        //2.获取配置创建的对象
        Book book = context.getBean("book", Book.class);
        System.out.println(book);
        book.add();
    
    }
    
    <!--set方法注入-->
    <bean id="book" class="com.lanest.Book">
        <property name="name" value="java教程"/>
        <property name="author" value="lanest"/>
    </bean>
    
  • 有参构造注入

    <!--有参构造注入-->
    <bean id="user" class="com.lanest.User">
        <constructor-arg name="id" value="10"/>
        <constructor-arg name="name" value="lanest"/>
    </bean>
    
  • p名称空间注入

    <!--p名称空间注入-->
    <bean id="book2" class="com.lanest.Book" p:name="English" p:author="jack"/>
    
  • 注入空值和特殊符号

    <!--注入空值和特殊符号-->
    <bean id="book3" class="com.lanest.Book">
        <property name="author">
            <null/>
        </property>
    
        <!--特殊符号赋值-->
        <!--属性值包含特殊符号
          a 把<>进行转义 &lt; &gt;
          b 把带特殊符号内容写到CDATA
         -->
        <property name="name">
            <value><![CDATA[<<广州>>]]></value>
        </property>
    </bean>
    
  • 注入属性-外部bean

    <!--注入属性-外部bean-->
    <bean id="userService" class="com.lanest.UserService">
        <property name="userDao" ref="userDaoImpl"/>
    </bean>
    
    <bean id="userDaoImpl" class="com.lanest.UserDaoImpl">
    </bean>
    
  • 注入属性-内部bean

    <!--注入属性-内部bean-->
    <bean id="emp" class="com.lanest.Emp">
        <!--设置两个普通属性-->
        <property name="ename" value="Andy"></property>
        <property name="gender" value="女"></property>
        <property name="dept">
            <bean id="dept" class="com.lanest.Dept">
                <property name="dname" value="研发部门"></property>
            </bean>
        </property>
    </bean>
    
  • 级联赋值

    <!--级联赋值 方式一:-->
    <bean id="emp3" class="com.lanest.Emp">
        <!--设置两个普通属性-->
        <property name="ename" value="Andy"/>
        <property name="gender" value="女"/>
        <!--级联赋值-->
        <property name="dept" ref="dept4"/>
    </bean>
    
    <bean id="dept4" class="com.lanest.Dept">
        <property name="dname" value="公关部门"/>
    </bean>
    
    <!--级联赋值 方式二:-->
        <bean id="emp2" class="com.lanest.Emp">
            <!--设置两个普通属性-->
            <property name="ename" value="jams"></property>
            <property name="gender" value="男"></property>
            <!--级联赋值-->
            <property name="dept" ref="dept2"></property>
            <property name="dept.dname" value="技术部门"></property>
        </bean>
        <bean id="dept2" class="com.lanest.Dept">
        </bean>
    
  • 注入集合类型属性

    <!--注入集合类型属性-->
    <bean id="stu" class="com.lanest.Stu">
        <!--数组类型属性注入-->
        <property name="courses">
            <array>
                <value>java课程</value>
                <value>数据库课程</value>
            </array>
        </property>
        <!--list类型属性注入-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
            </list>
        </property>
        <!--map类型属性注入-->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"/>
                <entry key="PHP" value="php"/>
            </map>
        </property>
        <!--set类型属性注入-->
        <property name="sets">
            <set>
                <value>MySQL</value>
                <value>Redis</value>
            </set>
        </property>
        <!--注入list集合类型,值是对象-->
        <property name="courseList">
            <list>
                <ref bean="course1"/>
                <ref bean="course2"/>
            </list>
        </property>
    </bean>
    
  • 自动装配:根据指定装配规则(属性名称或属性类型),Spring自动将匹配的属性值进行装配

bean标签属性autowire,配置自动装配

常用两个值:

​ byName 根据属性名称注入,注入值bean的id值和类的属性名称一样

​ byType 根据属性类型注入

<!--自动装配-->
<bean id="emp" class="com.lanest.entity.Emp" autowire="byType">
</bean>

基于注解方式:

注解:是代码的特殊标记

  • 格式:@注解名称(属性名称=属性值,属性名称=属性值…)
  • 使用注解,注解作用在上面,方法上面,属性上面
  • 作用:简化xml配置

Spring针对Bean管理中创建对象提供注解:

  • @Componet:普通组件
  • @Service:业务逻辑层组件
  • @Controller:表述层控制器组件
  • @Repository:持久化层组件

上面四个注解功能是一样的,都可以用来创建bean实例,只是仅仅为了让开发人员自己明确当前组件扮演的角色


基于注解方式实现对象创建:

  1. 引入依赖

  2. 开启组件扫描

    <context:componet-scan base-package="com.lanest"></context>
    

    相当于springboot中的@SpringbootApplication注解中的@ComponentScan注解

  3. 创建类,添加注解

    //value属性值可以省略不写,默认值时类名称,首字母小写
    @Component(value = "userService2") //此注解等于xml配置文件 <bean id="userService" class=".."/>
    public class UserService2 {
        public void add(){
            System.out.println("service add方法...");
        }
    }
    

开启组件扫描配置:

<!--开启组件扫描配置-->
<!--示例 1
    use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
    context:include-filter ,设置扫描哪些内容
 -->
<context:component-scan base-package="com.lanest" use-default-filters="false">
    <context:include-filter type="annotation"
                            expression="org.springframework.stereotype.Controller"/><!--代表只扫描Controller注解的类-->
</context:component-scan>

<!--示例 2
    下面配置扫描包所有内容
    context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.lanest">
    <context:exclude-filter type="annotation"
                            expression="org.springframework.stereotype.Controller"/><!--表示Controller注解的类之外一切都进行扫描-->
</context:component-scan>

基于注解方式实现属性注入:

  • @AutoWired:根据属性类型进行自动装配

    @Service
    public class UserService3{
        //定义dao类型属性
        //不需要添加set方法
        //添加注入属性注解
        @Autowired  //根据属性类型进行自动装配
        private UserDao userDao;
    }
    
    //@Repository(value = "userDaoImpl") 默认值
    @Repository
    public class UserDaoImpl implements UserDao {...}
    
  • @Qualifier:根据属性名称进行注入,需要和@Autowired一起使用,如果有多个实现类,@AutoWired则不知道装配哪个,此时可以用@Qualifier

     @Autowired  //根据属性类型进行自动装配
     @Qualifier(value = "userDaoImpl")  //区别同一接口下的多个实现类
     private UserDao userDao;
    
  • @Resource:可以根据类型,也可以根据名称注入(属于javax包里面的,不是spring包,不推荐使用)

    //@Resource 根据类型进行注入
    @Resource(name = "userDaoImpl") //根据名称进行注入
    private UserDao userDao;
    
  • @Value:注入普通类型属性

    @Value(value = "算法设计")
    private String cname;
    

完全注解开发

@Configuration 作为配置类,替代xml文件

@Configuration  //此注解作为配置类,替代xml文件
@ComponentScan(basePackages = "com.lanest")  //组件扫描
public class SpringConfig {
}
工厂Bean

Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)

  • 普通 bean:在配置文件中定义 bean 类型就是返回类型

  • 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样

    实现步骤:

  • 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean

  • 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型

public class FactoryBeanTest implements FactoryBean<Course> {

    //定义返回bean
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("aaa");
        return course;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

}
<bean id="factoryBeanTest" class="com.lanest.test.FactoryBeanTest">
</bean>
@Test
public void testFactoryBean(){
    BeanFactory context= new ClassPathXmlApplicationContext("bean1.xml");
    Course course = context.getBean("factoryBeanTest", Course.class);
    //返回值类型可以不是定义的bean类型
    System.out.println(course);
}
作用域

spring默认情况下,bean是单实例对象

作用域设置

  • xml中bean标签里面有属性scope用于设置单实例和多实例

  • scope属性值默认值为singleton,表示单实例对象

    • singleton 单实例,prototype 多实例

    • 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象 ;

      设置 scope 值是 prototype 时候,并不是在加载 spring 配置文件时候创建对象,而是在调用 getBean 方法的时候创建多实例对象

<bean id="book6" class="com.lanest.entity.Book" scope="prototype"><!--设置为多实例-->
    <property name="list" ref="bookList"/>
</bean>
生命周期

生命周期是指从对象创建到对象销毁的过程

bean的生命周期

  1. 通过构造器创建bean实例(无参构造)
  2. 为bean的属性设置值和对其他bean引用(调用set方法)
  3. 调用bean的初始化的方法(需要进行配置初始化的方法)
  4. bean可以使用了(对象获取到了)
  5. 当容器关闭时,调用bean的销毁的方法(需要进行配置销毁的方法)

bean 的后置处理器,bean 生命周期有七步: (正常生命周期为五步,而配置后置处理器后为七步)

  1. 通过构造器创建 bean 实例(无参数构造)

  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

  3. 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

  4. 调用 bean 的初始化的方法(需要进行配置初始化的方法)

  5. 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

  6. bean 可以使用了(对象获取到了)

  7. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

<!--配置文件的bean参数配置-->
<bean id="orders" class="com.lanest.entity.Orders" init-method="initMethod" destroy-method="destroyMethod">    <!--配置初始化方法和销毁方法-->
    <property name="oname" value="平板"/><!--这里就是通过set方式(注入属性)赋值-->
</bean>
public class Orders {
    private String oname;

    //无参数构造
    public Orders() {
        System.out.println("第一步:执行无参数构造创建 bean 实例");
    }

    //调用set方法
    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二步:调用 set 方法设置属性值");
    }

    //创建执行的初始化的方法
    public void initMethod() {
        System.out.println("第三步:执行初始化的方法");
    }

    //创建执行的销毁的方法
    public void destroyMethod() {
        System.out.println("第五步:执行销毁的方法");
    }
}

后置处理器实现:

<!--配置后置处理器-->
<bean id="myBeanPost" class="com.lanest.test.MyBeanPost"></bean>
/**
 * @Author: Lanest
 * @Date: 2023-09-08
 * @Description: 创建后置处理器实现类
 */
public class MyBeanPost implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化 之前 执行的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化 之后 执行的方法");
        return bean;
    }
}

AOP

概念

面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

简单来说,就是不通过修改源代码方式,在干功能里面添加新功能

比如在登录功能上添加权限判断功能

底层原理

AOP底层使用动态代理,动态代理有两种情况:

  • 有接口情况,即基于接口的动态代理,使用 JDK 动态代理 ;创建接口实现类代理对象,增强类的方法

    image-20230908222916172

  • 没有接口情况,即基于类的动态代理,使用 CGLIB 动态代理;创建子类的代理对象,增强类的方法

    image-20230908222848559

JDK动态代理

使用JDK动态代理,可以调用Proxy类里面的newProxyInstance方法实现:

  • 参数一:类加载器
  • 参数二:增强方法所在的类,这个类实现的接口(支持多个接口)
  • 参数三:实现接口InvocationHandler,创建代理对象,写增强的部分
public static Object newProxyInstance(ClassLoader loader, 
                                      Class[] interfaces, 
                                      InvocationHandler h) {...}

具体实现:

public interface UserDao {
    public int add(int a,int b);
}
public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("add方法执行...");
        return a+b;
    }
}
public class JDKProxy {
    public static void main(String[] args) {

        //创建接口实现类代理对象
        Class[] interfaces = {UserDao.class};
        UserDaoImpl userDao = new UserDaoImpl();

        /**
         *  参数一:类加载器
         *  参数二:增强方法所在的类,这个类实现的接口(支持多个接口)
         *  参数三:实现接口InvocationHandler,创建代理对象,写增强的部分
         */
        UserDao dao =(UserDao)Proxy.newProxyInstance
                (JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));

        int result = dao.add(1, 2);
        System.out.println("result:" + result);

    }
}

    //创建代理对象代码
    class UserDaoProxy implements InvocationHandler {
        //1 把创建的是谁的代理对象,把谁传递过来
        //有参数构造传递
        private Object obj;
        public UserDaoProxy(Object obj) {
            this.obj = obj;
        }
        //增强的逻辑
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            //方法之前
            System.out.println("方法之前执行...."+method.getName()+" :传递的参数..."+ Arrays.toString(args));

            //被增强的方法执行
            Object res = method.invoke(obj, args);

            //方法之后
            System.out.println("方法之后执行...."+obj);
            return res;
        }
    }

操作术语

  • 连接点:类里面可以被增强的方法

  • 切入点:实际被真正增强的方法

  • 通知(增强):实际增强的逻辑部分称为通知(增强)

    有五种类型:

    • 前置通知:增强方法之前执行的通知
    • 后置通知:增强方法之后执行的通知。
    • 环绕通知:增强方法之后执行的通知。
    • 异常通知:增强方法之后执行的通知。
    • 最终通知:类似try-catch-finally中的finally,无论是否出异常,最终都会执行的通知
  • 切面:把通知应用到切入点的过程,是个动作

AOP操作

Spring框架一般都是基于AspectJ实现AOP操作

AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring一起使用进行AOP操作

切入点表达式execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表])

1.com.atguigu.dao.BookDao 类里面的 add 进行增强:
execution(* com.lanest.dao.BookDao.add(...))
    
2.com.atguigu.dao.BookDao 类里面的所有的方法进行增强:
execution(* com.lanest.dao.BookDao.* (...))
    
3.对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强:
execution(* com.lanest.dao.*.* (...))

基于AspectJ实现AOP操作:

  1. 基于xml配置文件实现

    <bean id="book" class="com.lanest.aop.xml.Book"/>
    <bean id="bookProxy" class="com.lanest.aop.xml.BookProxy"/>
        <!--配置 aop 增强-->
        <aop:config>
            <!--切入点-->
            <aop:pointcut id="p" expression="execution(* com.lanest.aop.xml.Book.buy(..))"/>
            <!--配置切面-->
            <aop:aspect ref="bookProxy">
                <!--增强作用在具体的方法上-->
                <aop:before method="before" pointcut-ref="p"/>
            </aop:aspect>
        </aop:config>
    
  2. 基于注解方式实现

    @Component
    public class User {
        public void add() {
            System.out.println("add.......");
        }
    }
    
    @Component
    @Aspect  //生成代理对象
    public class UserProxy {
    
    
        //前置通知
        @Before(value = "execution(* com.lanest.aop.anno.User.add(..))")
        public void before() {
            System.out.println("before......");
        }
    
        //最终通知
        @After(value = "execution(* com.lanest.aop.anno.User.add(..))")
        public void after() {
            System.out.println("after......");
        }
    
        //后置通知
        @AfterReturning(value = "execution(* com.lanest.aop.anno.User.add(..))")
        public void afterReturning() {
            System.out.println("afterReturning......");
        }
    
        //异常通知
        @AfterThrowing(value = "execution(* com.lanest.aop.anno.User.add(..))")
        public void afterThrowing() {
            System.out.println("afterThrowing......");
        }
    
        //环绕通知
        @Around(value = "execution(* com.lanest.aop.anno.User.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
            System.out.println("around之前......");
            proceedingJoinPoint.proceed();  //切入点执行
            System.out.println("around之后......");
        }
    }
    
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.lanest.aop.anno"/>
    
    <!-- 开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy/>
    

@AspectJ注解:

@Before:前置通知

@AfterReturning:后置通知,在返回结果后执行,有异常不执行

@Around:环绕通知,在方法之前和之后都执行

@AfterThrowing:异常通知,方法有异常才执行

@After:最终通知,方法之后就执行


设置增强类的优先级:

在增强类上面添加注解@Order(数字类型值),值越小优先级越高

@Component
@Aspect
@Order(1)
public class UserProxy{...}

事务

概念

事务是数据库操作最基本单元,事务由单独单元的一个或多个SQL语句组成,在这个单元中,每个SQL语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中某条SQL语句一旦执行失败或产生错误,整个单元将会回滚。所有受到影响的数据将返回到事物开始以前的状态;如果单元中的所有SQL语句均执行成功,则事物被顺利执行


ACID

事务的ACID属性:

  1. 原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
  2. 一致性(Consistency)
    事务必须使数据库从一个一致性状态变换到另外一个一致性状态
  3. 隔离性(Isolation)
    事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
    如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个事务请求同一数据。不同的事务之间彼此没有任何干扰。
  4. 持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

声明式事务管理

事务添加到JavaEE三层结构里的Service层

在Spring进行事务管理操作有两种方式:

  • 编程式事务管理

  • 声明式事务管理(使用这个):底层使用AOP原理,对方法前后进行拦截,然后在目标方法开始之前创建或加入一个事务,在执行完目标方法之后根据执行情况提交或回滚事务

    • 基于注解方式

    • 基于xml方式


Spring事务管理API

提供一个接口PlatformTransactionManager,代表事务管理器,此接口针对不同的框架提供不同的实现类

基于注解方式
  • 开启事务注解

    <!--开启注解扫描-->
    <context:component-scan base-package="com.lanest"/>
    
    <!--引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${prop.url}" />
        <property name="username" value="${prop.username}" />
        <property name="password" value="${prop.password}" />
        <property name="driverClassName" value="${prop.driverClass}" />
    </bean>
    
    <!--JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入上面的dataSource-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--注入数据源-->
            <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
  • 类上面添加事务注解@Transactional

    @Service
    @Transactional(propagation = Propagation.REQUIRED,
                   isolation = Isolation.REPEATABLE_READ,
                   timeout = 2,
                   readOnly = true,
                   rollbackFor = ArithmeticException.class)
    public class UserService {...}
    

参数配置

注解@Transactional参数配置:

  • propagation:事务传播行为

    在spring中有7种 :

    • **REQUIRED:**如果有事务在运行,当前的方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行

      如:如果add方法本身有事务,调用update方法后,update使用add方法里面的事务;如果add方法本身没有事务,调用update方法后,创建新事物

    • **REQUIRED_NEW:**当前的方法必须启动新事物,并在它自己的事务内运行,如果有事务内运行,如果有事务正在运行,应该将它挂起

      如:使用add方法调用update方法,无论add方法是否有事务,都创建新事务

    • **SUPPORTS:**如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务种

    • NOT_SUPPORTS:当前的方法不应该运行在事务中,如果有运行的事务,将它挂起

    • MANDATORT:当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常

    • NEVER:当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常

    • NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行

  • isolation:事务隔离级别

    事务的隔离性,多事务操作之间不会产生影响

    如果不考虑隔离性,有三个读问题:

    • 脏读:一个未提交事务读取到另一个未提交事务的数据

    • 不可重复读:一个未提交事务读取到另一个提交事务修改数据

    • 虚(幻)读:一个未提交事务读取到另一个提交事务添加数据

    通过设置事务隔离级别,解决读问题:

    image-20230909213842958

  • timeout:超时时间

    事务超过超时时间还没提交则进行回滚,默认值是-1,以秒为单位

  • readOnly:是否只读

    默认false

  • rollbackFor:回滚

    设置出现哪些异常进行事务回滚

  • rollbackForClassName:回滚

    设置出现哪些异常名称进行事务回滚

  • noRollbackFor:不回滚

    设置出现哪些异常不进行事务回滚

  • noRollbackForClassName:不回滚

    设置出现哪些异常名称不进行事务回滚


完全注解方式

@Bean注解:(等同于xml中的bean标签)创建一个bean对象,并交给Spring管理,Spring将这个bean对象放入IOC容器,这些bean都要在**@Configuration**注解下的类创建

用配置类代替xml配置文件:

@Configuration
@ComponentScan(basePackages = {"com.lanest"})//开启注解扫描
@EnableTransactionManagement //开启事务
public class MyConfig {
    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() throws IOException {
        
        //用类加载器读取配置文件中的4个基本信息
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");    
        
		//创建属性集合类,用于载入配置文件的属性数据
        Properties p = new Properties();
        p.load(is);

        //获取配置文件中的值
        String username = p.getProperty("prop.username");
        String password = p.getProperty("prop.password");
        String url = p.getProperty("prop.url");
        String driverClass = p.getProperty("prop.driverClass");

        //创建德鲁伊数据库连接池
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
	}

    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {  //到IOC容器中根据类型找到dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource); //注入dataSource
        return jdbcTemplate;
    }

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {  //到IOC容器中根据类型找到dataSource
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

基于xml方式
  1. 配置事务管理器
  2. 配置通知
  3. 配置切入点和切面
<!--开启注解扫描-->
<context:component-scan base-package="com.lanest"/>

<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
    <property name="url" value="${prop.url}" />
    <property name="username" value="${prop.username}" />
    <property name="password" value="${prop.password}" />
    <property name="driverClassName" value="${prop.driverClass}" />
</bean>

<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入上面的dataSource-->
    <property name="dataSource" ref="dataSource"/>
</bean>

    
<!--声明式事务-->
<!--1、创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>
    
<!--2、配置通知-->
<tx:advice id="txadvice">
    <!--配置事务参数-->
    <tx:attributes>
        <!--name指定在哪个方法上面添加事务-->
        <tx:method name="transfer" propagation="REQUIRED" isolation="REPEATABLE_READ"/>
        <!--也可以通过这种规则决定哪个方法上面添加事务<tx:method name="trans*"/>-->
    </tx:attributes>
</tx:advice>

<!--3、配置切入点和切面-->
<aop:config>
    <!--配置切入点-->
    <aop:pointcut id="pt" expression="execution(* com.lanest.transaction.UserService.*(..))"/>
    <!--配置切面-->
    <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>

Spring5新特性

  • 基于java8,运行时兼容JDK9,删除了很多不建议使用的方法和类

  • 移除了Log4jConfigListener,而使用Log4j2

  • 支持**@Nullable**注解:用在方法、属性、参数上面,表示方法或属性值或参数值可以为空

  • 引入了Junit5@Test注解

  • Webflux:类似于SpringMVC,针对响应式编程,但SpringMVC是命令式编程,Webflux是异步响应式编程

    **响应式编程:**一种面向数据流和变化传播的编程范式,这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播

    异步: 调用者发送请求,如果发送请求之后不等着对方回应就去做其他事情就是异步

    非阻塞: 被调用者受到请求之后,受到请求之后马上给出反馈然后再去做事情就是非阻塞

Spring6新特性

  • 整个框架的代码基于Java 17源码级别。
  • Servlet、JPA等从"javax"迁移到"jakata"命名空间。
  • 兼容最新一代的web容器:Tomcat 10,Jetty 11.
  • 升级到ASM 9.4和Kotlin 1.7
  • 移除过时的Tiles和FreeMarker JSP的支持
  • JDK HttpClient与WebClient 集成
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java之弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值