Spring5 框架一篇就够(IOC容器、AOP面向切面编程、JDBCTemplate、Spring事务等)

目录

文章目录

前言

Spring 是轻量级开源的JavaEE框架,它可以解决企业应用开发的复杂性。Spring 有两个核心部分分别是 控制反转(Inversion of Control 简称:IOC)、面向切面编程(Aspect Oriented Programming 简称:Aop),所谓的IOC则是把创建对象的过程交给 Spring 进行管理。而AOP则是对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

下载Spring 依赖

  1. 首先进入Spring官网找到Projects下的Spring Framework。

在这里插入图片描述

  1. 在Spring Framework中可以查看Spring GA 稳定版本;进入Github
    在这里插入图片描述

  2. Github中找到Access to Binaries里的 Spring Framework Artifacts点击。
    在这里插入图片描述

  3. 找到Downloading a Distribution点击 https://repo.spring.io地址。
    在这里插入图片描述

  4. 找到release/org/springframework/spring,复制的路径拼接到repo.spring.io后面:https://repo.spring.io/release/org/springframework/
    在这里插入图片描述

  5. 下载所需的spring版本
    在这里插入图片描述

导入Spring相关jar包

commons-logging.jar 可以在mvnrepository上下载

在这里插入图片描述

IOC 容器

所谓的IOC则是把创建对象的过程交给 Spring 进行管理,实现低耦合。

  • IOC底层原理:xml解析、工厂模式、反射。

  • Spring提供IOC容器实现两接口方式:

    • BeanFactory:是 Spring 内部的使用接口,不推荐开发人员使用 。加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象。

    • ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,推荐开发人 员使用,加载配置文件时就会把配置文件中的所有对象进行创建。

      • FileSystemXmlApplicationContext(String str) 实现类:某磁盘内的文件地址。

      • ClassPathXmlApplicationContext(String str)实现类:src下某个文件名。

        ApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("bean1.xml");
        

IOC 容器Bean的操作

Bean管理操作有两种方式

Bean管理有两个操作:Spring 创建对象、Spring 注入属性。

xml配置文件中<bean>标签介绍

属性描述
id是一个 Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成
nameSpring 容器同样可以通过此属性对容器中的 Bean 进行配置和管理,name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开
class该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,使用类的全限定名
scope用于设定 Bean 实例的作用域,其属性值有 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton
constructor-arg<bean>元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型
property<bean>元素的子元素,用于调用 Bean 实例中的 Set 方法完成属性赋值,从而完成依赖注入。该元素的 name 属性指定 Bean 实例中的相应属性名
ref<property><constructor-arg> 等元素的子元索,该元素中的 bean 属性用于指定对 Bean 工厂中某个 Bean 实例的引用
value<property><constractor-arg> 等元素的子元素,用于直接指定一个常量值
list用于封装 List 或数组类型的依赖注入
set用于封装 Set 类型属性的依赖注入
map用于封装 Map 类型属性的依赖注入
entry<map>元素的子元素,用于设置一个键值对。其 key 属性指定字符串类型的键值,ref 或 value 子元素指定其值
  • <property>标签属性介绍:(bean子标签)

    属性详情
    nameJava类中所对应的属性名称。
    value要赋值的属性。
    refxml中bean标签的id,也就是xml文件中的一个对象。
  • <constructor-arg>标签有参构造器介绍:(bean子标签)

    属性详情
    name有参构造器中对应的名称。
    value要赋值的属性。
    index有参构造器中第几个参数。

Spring DI 的实现方式:属性注入和构造注入Book类: (依赖注入)

public class Book {
    private String name;
    public Book(String name) {this.name = name;}
    public void setName(String name) {this.name = name;}
    public String getName() {return name;}
}

xml文件:

<bean id="user" class="com.tmsklst.spring5.Book"></bean>	
  • 使用setter方法注入属性。

    <bean id="book" class="com.tmsklst.spring5.Book">
        <property name="name" value="托马斯·克里斯特"></property>
    </bean>
    
  • 使用有参构造器注入属性。

    <bean id="book" class="com.tmsklst.spring5.Book">
        <constructor-arg name="name" value="托马斯·克里斯特"></constructor-arg>
    </bean>
    
  • 字面量设置null值

    <bean id="book" class="com.tmsklst.spring5.Book">
        <property name="name">
        	<null/>
        </property>
    </bean>
    
  • 属性值包含特殊符号

    <bean id="book" class="com.tmsklst.spring5.Book">
        <property name="name">
            <value><![CDATA[<<夏洛克·福尔摩斯>>]]></value>
        </property>
    </bean>
    

Spring 实例化:bean(service与dao为案例)

  1. 创建UserDaoImpl类:

    public class UserDaoImpl implements UserDao {
        @Override
        public void update() {
            System.out.println("userDaoImpl update.............");
        }
    }
    
  2. 创建UserServiceImpl类:(注意:需要个属性添加set方法)

    public class UserServiceImpl implements UserService {
        private UserDao userDao;
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        @Override
        public void add() {
            System.out.println("UserServiceImpl add..............");
            userDao.update();
        }
    }
    
  3. 创建bean.xml配置文件:

    <!-- service对象的创建 -->
    <bean id="UserService" class="com.tmsklst.service.impl.UserServiceImpl">
        <!-- 注入service对象 -->
        <property name="userDao" ref="UserDao"></property>
    </bean>
    <!-- dao对象的创建 -->
    <bean id="UserDao" class="com.tmsklst.dao.impl.UserDaoImpl"></bean>
    
  4. 测试:

    public void newInstanceTest() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //获取配置文件对象
        UserServiceImpl userService = context.getBean("UserService", UserServiceImpl.class);
        userService.add();
    }
    

Spring 实例化:嵌套bean与级联赋值创建对象(一对多为案例)

  1. 创建部门类Dept:

    public class Dept {
        private String dname;
        public void setDname(String dname) {
            this.dname = dname;
        }
        @Override
        public String toString() {
            return "Dept{" +"dname='" + dname + '\'' + '}';
        }
    }
    
  2. 创建员工类Emp:

    public class Emp {
        private String ename;
        private String gender;
        private Dept dept;//员工属于一个部门,使用对象形式表示
        public Dept getDept() { //级联赋值所需的get方法
            return dept;
        }
        public void setEname(String ename) {
            this.ename = ename;
        }
        public void setGender(String gender) {
            this.gender = gender;
        }
        public void setDept(Dept dept) {
            this.dept = dept;
        }
        public void add() {
            System.out.println(ename + "::" + gender + "::" + dept);
        }
    }
    
  3. 创建bean.xml配置文件:(嵌套)

    <bean id="emp" class="com.tmsklst.pojo.Emp">
        <!-- 普通属性 -->
        <property name="ename" value="托马斯·克里斯特"></property>
        <property name="gender" value=""></property>
        <!-- 对象属性 -->
        <property name="dept">
            <bean id="dept" class="com.tmsklst.pojo.Dept">
                <property name="dname" value="技术部"></property>
            </bean>
        </property>
    </bean>
    

    或 (级联赋值 注意:在emp类中一定要生成dept属性的get方法)

    <bean id="emp" class="com.tmsklst.pojo.Emp">
        <!-- 普通属性 -->
        <property name="ename" value="托马斯·克里斯特"></property>
        <property name="gender" value=""></property>
        <!-- 级联赋值 -->
        <property name="dept" ref="dept"></property>
        <property name="dept.dname" value="财务部"></property>
    </bean>
    <bean id="dept" class="com.tmsklst.pojo.Dept"></bean>
    
  4. 测试:

    public void newInstanceTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Emp emp = context.getBean("emp", Emp.class);
        emp.add();
    }
    

Spring 实例化:集合数据注入

  1. 创建Student类:

    public class Student {
        //数组属性
        private String[] array;
        //List属性
        private List<String> list;
        //set属性
        private Set<String> set;
        //Map属性
        private Map<String,String> map;
        //List对象属性
        private List<User> userList;
        
        public void setArray(String[] courses) {
            this.array = courses;
        }
        public void setList(List<String> list) {
            this.list = list;
        }
        public void setSet(Set<String> set) {
            this.set = set;
        }
        public void setMap(Map<String, String> map) {
            this.map = map;
        }
        public void setUserList(List<User> userList) {
            this.userList = userList;
        }
        public void collectionToString() {
            System.out.println("array:" + Arrays.asList(array));
            System.out.println("list:" + list);
            System.out.println("set:" + set);
            System.out.println("map:" + map);
            System.out.println("userList:" + userList);
        }
    }
    
  2. 创建xml配置文件:

    <!-- 集合属性的注入 -->
    <bean id="student" class="com.tmsklst.collection.Student">
        <!-- 数组注入 -->
        <property name="array">
            <array>
                <value>array01</value>
                <value>array02</value>
            </array>
        </property>
        <!-- List注入 -->
        <property name="list">
            <list>
                <value>list01</value>
                <value>list02</value>
            </list>
        </property>
        <!-- set注入 -->
        <property name="set">
            <set>
                <value>set01</value>
                <value>set02</value>
            </set>
        </property>
        <!-- map注入 -->
        <property name="map">
            <map>
                <entry key="key01" value="value01"></entry>
                <entry key="key02" value="value02"></entry>
            </map>
        </property>
        <!-- 对象list注入 -->
        <property name="userList">
            <list>
                <ref bean="user1"></ref>
                <ref bean="user2"></ref>
            </list>
        </property>
    </bean>
    <bean id="user1" class="com.tmsklst.pojo.User">
        <property name="name" value="托马斯·克里斯特"></property>
    </bean>
    <bean id="user2" class="com.tmsklst.pojo.User">
        <property name="name" value="汤姆"></property>
    </bean>
    

Spring 实例化:提取List集合、Map对象的数据注入

  1. 创建Book类:

    public class Book {
        private List<String> list;
        private Map<Integer,User> map;
    
        public void setList(List<String> list) {
            this.list = list;
        }
        public void setMap(Map<Integer, User> map) {
            this.map = map;
        }
        public void add() {
            System.out.println("list:" + list);
            System.out.println("map:" + map);
        }
    }
    
  2. 在配置文件beans头部标签中引入名称空间 util:

     <beans xmlns:util="http://www.springframework.org/schema/util"
            xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd>
    
  3. 使用 util 标签完成 list 集合与map集合对象的注入提取:

    <!-- 创建user对象 -->
    <bean id="user1" class="com.tmsklst.pojo.User">
        <property name="name" value="托马斯·克里斯特"></property>
    </bean>
    <bean id="user2" class="com.tmsklst.pojo.User">
        <property name="name" value="汤姆"></property>
    </bean>
    
    <!-- 1.提取list集合类型属性注入 -->
    <util:list id="bookList">
        <value>九阳神功</value>
        <value>九阴正经</value>
    </util:list>
    <util:map id="bookMap">
        <entry key="1">
            <ref bean="user1"></ref>
        </entry>
        <entry key="2">
            <ref bean="user2"></ref>
        </entry>
    </util:map>
    
    <!-- 2.提取list集合类型属性注入的使用 -->
    <bean id="book" class="com.tmsklst.pojo.Book">
        <property name="list" ref="bookList"></property>
        <property name="map" ref="bookMap"></property>
    </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:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                                http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    
        <!-- 创建user对象 -->
        <bean id="user1" class="com.tmsklst.pojo.User">
            <property name="name" value="托马斯·克里斯特"></property>
        </bean>
        <bean id="user2" class="com.tmsklst.pojo.User">
            <property name="name" value="汤姆"></property>
        </bean>
        
        <!-- 1.提取list集合类型属性注入 -->
        <util:list id="bookList">
            <value>九阳神功</value>
            <value>九阴正经</value>
        </util:list>
        <util:map id="bookMap">
            <entry key="1">
                <ref bean="user1"></ref>
            </entry>
            <entry key="2">
                <ref bean="user2"></ref>
            </entry>
        </util:map>
        
        <!-- 2.提取list集合类型属性注入的使用 -->
        <bean id="book" class="com.tmsklst.pojo.Book">
            <property name="list" ref="bookList"></property>
            <property name="map" ref="bookMap"></property>
        </bean>
    </beans>
    
  4. 测试

    public void collectionTest2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Book book = context.getBean("book", Book.class);
        book.add();
    }
    

Spring 实例化:工厂Bean(FactoryBean)

在配置文件定义bean类型可以和返回类型不一样。

  1. 创建MyBean类,让这个类作为工厂 bean,实现接口 FactoryBean

    public class MyBean implements FactoryBean<User> {
        //返回 Bean 的类型取决于 FactoryBean<T> 泛型值
        @Override
        public User getObject() throws Exception {
            User user = new User();
            return user;
        }
    }
    
  2. 第二步 xml配置

    <bean id="myBean" class="com.tmsklst.factorypojo.MyBean"></bean>
    
  3. 第三步 测试

    public void myBeanTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        User myBean = context.getBean("myBean", User.class);
        System.out.println(myBean);
    }
    

Spring Bean的作用域

  • 在 Spring 里面,设置创建 bean 实例是单实例还是多实例。

  • 在 Spring 里面,默认情况下,bean 是单实例对象。

  • 如何设置单实例还是多实例。

    • 在 spring 配置文件 bean 标签里面有属性 scope 用于设置单实例还是多实例。

    • scope 属性:

      • 默认值 singleton 表示是单实例对象。

      • prototype 表示是多实例对象。

        <bean scope="singleton"></bean>
        <bean scope="prototype"></bean>
        
      • request 单实例对象。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。(不常用)

      • session 单实例对象。而对不同的 Session会话,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。(不常用)

  • singleton 和 prototype 区别

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

      <bean id="user1" class="com.tmsklst.pojo.User" scope="singleton"></bean>
      <bean id="user2" class="com.tmsklst.pojo.User" scope="propertype"></bean>
      
      public void Test() {
          ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
          User singUser01 = context.getBean("user1", User.class);
          User singUser02 = context.getBean("user1", User.class);
          System.out.println(singUser01);//com.tmsklst.pojo.User@2f8f5f62
          System.out.println(singUser02);//com.tmsklst.pojo.User@2f8f5f62
          User proUser01 = context.getBean("user2", User.class);
          User proUser02 = context.getBean("user2", User.class);
          System.out.println(proUser01);//com.tmsklst.pojo.User@5f4754f5
          System.out.println(proUser02);//com.tmsklst.pojo.User@5f78f6f3
      }
      
    • 设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,在调用 getBean 方法时候创建多实例对象。

Spring Bean的生命周期

在这里插入图片描述

  1. 创建Order类 ,并在类中自定以两个方法initMethod()destroyMethod() 用于初始化和销毁。

    public class Order {
        private String name;
        public Order() {
            System.out.println("第一步 创建Bean的实例");
        }
        public void setName(String name) {
            this.name = name;
            System.out.println("第二步 设置属性值");
        }
        //初始化的方法
        public void initMethod() {
            System.out.println("第三步 执行初始化方法");
        }
        //销毁方法
        public void destroyMethod() {
            System.out.println("第五步 执行销毁方法");
        }
    }
    
  2. 创建MyBeanPost类 作为后置处理器并实现BeanPostProcessor接口。

    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;
        }
    }
    
  3. 创建xml配置文件,在bean标签中使用init-methoddestroy-method属性分别指定Order类中的初始化方法与销毁方法。

    <bean id="order" class="com.tmsklst.pojo.Order" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="iPhont"></property>
    </bean>
    <!-- 配置后置处理器 -->
    <bean id="myBeanPost" class="com.tmsklst.pojo.MyBeanPost"></bean>
    
  4. 测试

    public void test1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Object order = context.getBean("order", Order.class);
        System.out.println("第四步 获取创建bean实例对象");
        System.out.println(order);
        //销毁实例
        context.close();
    }
    

    在这里插入图片描述

Spring Bean自动注入

什么是自动注入?

根据指定装配规则属性名称或者属性类型,Spring自动将匹配的属性值进行注入。

Bean自动注入使用方法

给bean标签添加属性autowire:

  • byType:根据 xml 中 bean的类型自动注入。(对多种同源类型无效)

  • byName:根据 xml 中 bean的名称自动注入。

    <bean id="emp" class="com.tmsklst.autowire.Emp" autowire="byName">
        <!-- 或 -->
    <bean id="emp" class="com.tmsklst.autowire.Emp" autowire="byType">
    

案例:

Emp类:

public class Emp {
    private Dept dept;

    public void setDept(Dept dept) {
        this.dept = dept;
    }
    @Override
    public String toString() {
        return "Emp{" +
                "dept=" + dept +
                '}';
    }
    public void test() {
        System.out.println(dept);
    }
}

Dep类:

public class Dept {}

xml配置文件:

<bean id="emp" class="com.tmsklst.autowire.Emp" autowire="byName"></bean>
<bean id="dept" class="com.tmsklst.autowire.Dept"></bean>

测试:

public void test1() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    Emp emp = context.getBean("emp", Emp.class);
    System.out.println(emp);
}

Spring Bean自动注入druid连接池

  1. 在src下创建jdbc.properties配置文件

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc.mysql://localhost:3306/test
    username=root
    passowrd=root
    
  2. 在xml中新增名称空间context

    <beans 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">
    
  3. 名称空间引入配置文件

    <context:property-placeholder location="classpath:jdbc.properties" />
    
  4. bean配置

    <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    

Spring Annotation (注解)

在 Spring 中,尽管使用 XML 配置文件可以实现 Bean 的装配工作,但如果应用中 Bean 的数量较多,会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。使用注解大大简化了xml配置文件。

注解格式@注解名称(属性名称=属性值,属性名称=属性值。。。) 可包含多个属性。
注解的使用范围:类、方法、属性。
所需依赖:spring-aop.jar

注解名称描述
@Component可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
@Service通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Repository用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Autowired用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。
@Qualifier与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。
@Resource其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。
@Resource 中有两个重要属性:name 和 type。
Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。
如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
@Value给普通属性赋值。

注解注入Bean

配置注解扫描和注解扫描规则几种方式
方式一:指定包路径扫描

使用context命名空间,通知Spring扫描指定目录进行注解解析。(不同源的包用逗号隔开)

<context:component-scan base-package="com.tmsklst.annotation,
                                      com.tmsklst1.dao,
                                      com.tmsklst2.service" />
方式二:规则扫描定向注解

设置use-default-filters属性为false:不使用默认扫描规则,自定义配置规则。
通过include-filter标签指定注解

  • type:annotation(注解)。

  • expression:注解包全路径。

    <context:component-scan base-package="com.tmsklst" use-default-filters="false">
    	<context:include-filter type="annotation"
               expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
    
方式三:规则扫描反向注解(过滤注解)

通过exclude-filter标签指定需要无视的注解:

  • type:annotation(注解)。

  • expression:注解包全路径。(如果指定为Component,则该包下的Component注解不解析)

    <context:component-scan base-package="com.tmsklst">
    	<context:exclude-filter type="annotation"
            	expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
    
注解Bean测试
  1. 使用context命名空间,通知Spring扫描指定目录进行注解解析。

    <context:component-scan base-package="com.tmsklst" use-default-filters="false" >
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
    
  2. 创建UserService类,在类上方使用注解。注解里的value值默认为类的名称,且首字母小写。
    注解等同于:<bean id="userService" class="com.tmsklst.service.UserService" />

    @Component(value = "userService")
    public class UserService {
        public void add() {
            System.out.println("service add……");
        }
    }
    
  3. 测试

    public void test1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }
    

注解属性注入

@Autowired根据类型来注入属性值 (service和dao为案例)
  1. Service层:创建UserService类, 属性上的 @Autowired注解会更具类型(UserDao)去Dao层找到相应的类。

    @Service
    public class UserService {
        @Autowired
        private UserDao userDao;
        
        public void add() {
            System.out.println("service add……");
            userDao.add();
        }
    }
    
  2. Dao层:创建UserDao实现类,别忘了给实现类装配bean(@Repository)

    @Repository
    public class UserDaoImpl implements UserDao {
        @Override
        public void add() {
            System.out.println("UserDao 实现类一 add…………");
        }
    }
    
@Qualifier根据Bean名称注入属性,与@Autowired 注解配合使用(service和dao为案例)
  1. Service层:创建UserService类, 属性上的 @Autowired注解会更具类型(UserDao)去Dao层找到相应的类,当UserDao有多个实现类时,则加上@Qualifier注解,而value值是目标Bean名称

    @Service
    public class UserService {
    
        @Autowired
        @Qualifier(value = "userDao2")
        private UserDao userDao;
        
        public void add() {
            System.out.println("service add……");
            userDao.add();
        }
    }
    
  2. Dao层:创建UserDao实现类,多个实现类需要定义value值(@Repository)

    // 实现类一
    @Repository(value = "userDaol")
    public class UserDaoImpl implements UserDao {
        @Override
        public void add() {
            System.out.println("UserDao 实现类一 add…………");
        }
    }
    // 实现类二
    @Repository(value = "userDao2")
    public class UserDaoImpl2 implements UserDao {
        @Override
        public void add() {
            System.out.println("UserDao 实现类二 add()…………");
        }
    }
    
  3. 测试

    public void test1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }
    /**
    com.tmsklst.service.UserService@2cbb3d47
    service add……
    UserDao 实现类二 add()…………
    */
    
@Resource根据Bean名称注入属性,默认按照 Bean 实例名称进行装配
@Service
public class UserService {

    @Resource(name = "userDao")
    private UserDao userDao;

    public void add() {
        System.out.println("service add……");
        userDao.add();
    }
}

Spring 使用配置类代替配置文件

新建一个SpringConfig类,使用@Configuration标注当前类为配置类,@ComponentScan指向解析注解的包。

@Configuration
@ComponentScan(basePackages = {"com.tmsklst5"})
public class SpringConfig {
}

AOP 面向切面编程

  • 利用AOP(Aspect Oriented Programming)可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  • AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

AOP JDK动态代理

  • JDK 动态代理是通过 JDK 中的 java.lang.reflect.Proxy 类实现的。
  • static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h):返回指定接口的代理类实例,该接口将方法调用分派给指定的调用处理程序。
    • ClassLoader loader 参数:类的加载器。
    • Class<?>[] interfaces参数:增强方法所在类的实现接口,支持多个接口。
    • InvocationHandler h参数:实现InvocationHandler接口创建代理对象,增强代码部分。

实例:

  1. 创建接口UserDao,在UserDao添加两个方法,如下所示。

    public interface UserDao {
        int add(int num1,int num2);
        String update(String name);
    }
    
  2. 创建UserDao的实现类UserDaoimpl,如下所示。

    public class UserDaoimpl implements UserDao {
        @Override
        public int add(int num1, int num2) {
            System.out.println("第二:我是add方法原有逻辑……");
            return num1 + num2;
        }
        @Override
        public String update(String name) {
            System.out.println("第二:我是uodate方法原有逻辑……");
            return name;
        }
    }
    
  3. 创建JDKProxy类实现JDK动态代理。

    public class JDKProxy {
        public static void main(String[] args) {
            /* 1.获取需要扩展功能的接口 */
            Class[] interfaces = {UserDao.class};
            /* 2.创建接口实现类对象 */
            UserDao userDaoimpl = new UserDaoimpl();
            /*
                3.使用Proxy类中的newProxyInstance方法动态代理。
                参数:类的加载器、接口、实现了InvocationHandler接口的类对象,此处也可用匿名InvocationHandler接口。
                返回新的实例。
            */
            UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoimpl));
            /* 8.使用新的实例调用方法 add()与 update() */
            int add = userDaoProxy.add(10, 10);
            System.out.println("第四:addResult:" + add);
            String update = userDaoProxy.update("托马斯·克里斯特");
            System.out.println("第四:updateResult:" + update);
        }
    }
    
    /* 4.创建实现了InvocationHandler接口类 */
    class UserDaoProxy implements InvocationHandler {
        /* 5.接收目标类对象 */
        private Object obj;
        /* 6.创建有参构造器,用于接收目标类对象 */
        public UserDaoProxy(Object obj) {
            this.obj = obj;
        }
    
        /* 7.重写invoke方法用于增强逻辑 */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //方法之前处理
            if ("add".equals(method.getName())) {
                System.out.println("第一:我是add方法之前执行…… " + method.getName() + ":传递参数" + Arrays.asList(args));
            }
            if ("update".equals(method.getName())) {
                System.out.println("第一:我是update方法之前执行…… " + method.getName() + ":传递参数" + Arrays.asList(args));
            }
            /* 9.调用invoke方法被增强的方法执行,参数:目标类对象、参数 */
            Object invoke = method.invoke(obj, args);
    
            //方法之后处理
            System.out.println("第三:方法之后执行……" + obj);
            return invoke;
        }
    }
    

AspectJ开发AOP

AspectJ 是一个基于 Java 语言的 AOP 框架,它扩展了 Java 语言。Spring 2.0 以后,新增了对 AspectJ 方式的支持,新版本的 Spring 框架,建议使用 AspectJ 方式开发 AOP。

AspectJ 开发AOP的两种方式:

  • 基于XML的声明方式。

  • 基于Annotation的声明方式。

  • 除了Spring Aop的jar包以外,还要导入与 AspectJ 相关jar包:

    • spring-aspects-5.2.6.RELEASE:Spring 为 AspectJ 提供的实现。
    • com.springsource.net.sf.cglib-2.2.0
    • com.springsource.org.aopalliance-1.0.0
    • com.springsource.org.aspectj.weaver-1.6.8.RELEASE:AspectJ 提供的规范。

AspectJ 注解操作

AspectJ 框架为 AOP 开发提供了另一种开发方式——基于 Annotation 的声明式。AspectJ 允许使用注解定义切面、切入点和增强处理,而 Spring 框架则可以识别并根据这些注解生成 AOP 代理。

AspectJ 注解介绍
名称描述
@Aspect定义一个切面。
@Before定义前置通知。(在被增强的方法之前执行)
@AfterReturning定义后置通知。(在程序正常执行后执行)
@AfterThrowing定义异常通知。(有异常时执行)
@After定义最终通知。(不管是否异常,都执行)
@Around定义环绕通知。(在被增强的方法前后执行)被此注解所定义的方法必须携带ProceedingJoinPoint类型的参数,通过ProceedingJoinPoint调用proceed()方法,执行被增强的方法。
@DeclareParents用于定义引介通知。
@Order在有多个切面的情况下,设置优先级。数值越小,优先级越高。
@Pointcut对相同切入点作抽取。(对切入点表达式做封装)在其他切入点value值中调用被此注解标示的方法。
AspectJ 注解切入点表达式介绍
  • 语法结构:@Before(value = “execution(【权限修饰符】【返回值类型】【目标类全路径】【方法名(【参数列表】)】)”)。
    • *通配符:所有的。
    • 没有返回值类型用空格代替。
  • 实例1:对 aop.tmsklst.aspectJannotation.User 类的add方法增强。
    execution(* aop.tmsklst.aspectJannotation.User.add(..))
  • 实例2:对 aop.tmsklst.aspectJannotation.User 类的所有方法增强。
    execution(* aop.tmsklst.aspectJannotation.User.*(..))
  • 实例3:对 aop.tmsklst.aspectJannotation 包下的所有方法增强。
    execution(* aop.tmsklst.aspectJannotation.*.*(..))
AspectJ 注解的使用
  1. 在配置文件中使用context名称空间启用Annotation扫描,在使用aop名称空间开启AspectJ切面。

    <beans xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!-- Annotation扫描 -->
        <context:component-scan base-package="aop.tmsklst"></context:component-scan>
        <!-- 开启AspectJ生成切面 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

    或 使用配置类替换配置文件。

    @ComponentScan
    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class ConfigAop {
    }
    
  2. 被增强类

    @Component
    public class User {
        public void add() {
    //        int i = 1/0; //制造异常
            System.out.println("add......");
        }
    }
    
  3. 创建切面类,使用@Aspect注解表示当前类是切面类,用@Order设置优先级。

    @Aspect //定义一个切面
    @Order(1)//在有多个切面的情况下,设置优先级
    @Component
    public class UserProxy {
        //相同切入点抽取
        @Pointcut(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..) )")
        public void pointcut() {
    
        }
        //@Before 注解表示作为前置通知
        @Before(value = "pointcut())")
        public void defore() {
            System.out.println("defore....");
        }
        //最终通知:不管在扫描情况下都执行
        @After(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))")
        public void after() {
            System.out.println("after....");
        }
        //后置通知:在程序正常执行后执行
        @AfterReturning(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))")
        public void afterReturning() {
            System.out.println("afterReturning....");
        }
        //异常通知:有异常时执行
        @AfterThrowing(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))")
        public void afterThrowing() {
            System.out.println("afterThrowing....");
        }
        //环绕通知
        @Around(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕之前....");
            
            //被增强的方法执行
            proceedingJoinPoint.proceed();
    
            System.out.println("环绕之后....");
        }
    }
    
  4. 创建第二个切面类,使用@Aspect注解表示当前类是切面类,用@Order设置优先级。

    @Aspect //定义一个切面
    @Order(2)//在有多个切面的情况下,设置优先级
    @Component
    public class PersonProxy {
        @Before(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))")
        public void before() {
            System.out.println("PersonProx before....");
        }
    }
    
  5. 测试

    public void testAno1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        User user = context.getBean("user", User.class);
        user.add();
    }
    /* 执行结果                                  异常结果
    环绕之前....								 环绕之前....
    defore....							       defore....
    PersonProx before....					   PersonProx before....
    add......								   after....
    环绕之后....								afterThrowing....
    after....
    afterReturning....
    */
    

AspectJ 配置文件操作

  1. 被增强类

    public class Person {
        public void add() {
            System.out.println("add....");
        }
    }
    
  2. 切面类

    public class PersonProxy {
        public void before() {
            System.out.println("before....");
        }
    }
    
  3. 在配置文件中添加aop名称空间。

    <beans xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!-- 配置AOP增强 -->
        <aop:config>
            <!-- 切入点 -->
            <aop:pointcut id="p" expression="execution(* aop.tuoyingtao.aspectJxml.Person.add(..))"></aop:pointcut>
            <!-- 切面 -->
            <aop:aspect ref="personProxy">
                <!-- 增强方法 -->
                <aop:before method="before" pointcut-ref="p"></aop:before>
            </aop:aspect>
        </aop:config>
    

Spring JDBCTemplate操作数据库

什么是JDBCTemplate?

它是Spring框架对JDBC的封装,使用JDBCTemplate更方便实现对数据库操作。

导入相关jar包

  • druid-1.1.9:链接池
  • mysql-connector-java-5.1.7-bin:数据库连接
  • spring-tx-5.2.6.RELEASE:事务处理
  • spring-jdbc-5.2.6.RELEASE:spring对jdbc的封装

JDBCTemplate使用实例

  1. jdbc.properties 配置数据库连接基本信息。

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/user_db
    username=root
    passwprd=root
    
  2. Spring 配置文件添加context名称空间,引入jdbc.properties配置文件,启用注解解析,创建JdbcTemplate对象,并注入链接池。

    <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:property-placeholder location="jdbc.properties" />
        <context:component-scan base-package="aop2.jdbctemplate"/>
    
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${driverClassName}" />
            <property name="url" value="${url}" />
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </bean>
    
        <!-- JdbcTemplate对象 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <!-- 注入链接池 -->
            <property name="dataSource" ref="druidDataSource"/>
        </bean>
    </beans>
    
  3. BookDaoImpl类实现BookDao接口,并注入JdbcTemplate对象。

    @Repository
    public class BookDaoImpl implements BookDao {
        //注入JDBCTemplate
        @Autowired
        private JdbcTemplate jdbcTemplate;
    }
    
  4. 在BookService类中注入BookDaoImpl类。

    @Service
    public class BookService {
        //注入dao
        @Autowired
        private BookDao bookDao;
    }
    

JDBCTemplate 操作数据库(增、删、改)

  1. 创建Book类

    public class Book {
        private Integer id;
        private String userName;
        private String status;
    	…………
    }
    
  2. service层

    @Service
    public class BookService {
        //注入dao
        @Autowired
        private BookDao bookDao;
    	// 添加数据
        public void addBook(Book book) {
            bookDao.add(book);
        }
        //修改数据
        public void amendBook(Book book) {
            bookDao.amend(book);
        }
    	//删除数据
        public void deleteBook(Integer id) {
            bookDao.delete(id);
        }
    }
    
  3. dao层

    @Repository
    public class BookDaoImpl implements BookDao {
        //注入JDBCTemplate
        @Autowired
        private JdbcTemplate jdbcTemplate;
        //插入数据
        @Override
        public void add(Book book) {
            String sql = "insert into t_book(id,userName,status) values(?,?,?)";
            Object[] args = {book.getId(),book.getUserName(),book.getStatus()};
            int update = jdbcTemplate.update(sql, args);
            System.out.println(update);
        }
        //修改数据
        @Override
        public void amend(Book book) {
            String sql = "update t_book set userName = ?, status = ? where id = ?";
            Object[] args = {book.getUserName(),book.getStatus(),book.getId()};
            int update = jdbcTemplate.update(sql, args);
            System.out.println(update);
        }
    	//删除数据
        @Override
        public void delete(Integer id) {
            String sql = "delete from t_book where id = ?";
            int update = jdbcTemplate.update(sql, id);
            System.out.println(update);
        }
    }
    
  4. 测试

    //插入数据
    public void jdbcTemplateTest() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        bookService.addBook(new Book(null,"托马斯·克里斯特","true"));
    }
    //修改数据
    public void jdbcTemplateTest2() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        bookService.amendBook(new Book(1,"Tom","false"));
    }
    //删除数据
    public void jdbcTemplateTest3() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        bookService.deleteBook(1);
    }
    

JDBCTemplate 操作数据库(查询、批量操作)

  1. service层

    @Service
    public class BookService {
        //注入dao
        @Autowired
        private BookDao bookDao;
    
        public Integer findCount() {
           return bookDao.countTotal();
        }
    
        public Book findBook(Integer id) {
            return bookDao.findBookInfo(id);
        }
    
        public List<Book> findAll() {
            return bookDao.findAllList();
        }
    
        public void batcAdd(List<Object[]> batchArgs) {
            bookDao.batchAddBook(batchArgs);
        }
    }
    
  2. dao层

    @Repository
    public class BookDaoImpl implements BookDao {
        //注入JDBCTemplate
        @Autowired
        private JdbcTemplate jdbcTemplate;
    	//总共多少条记录
        @Override
        public Integer countTotal() {
            String sql = "select count(*) from t_book";
            Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
            return integer;
        }
    	//查询详情
        @Override
        public Book findBookInfo(Integer id) {
            String sql = "select id, userName, status from t_book where id = ?";
            Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
            return book;
        }
    	//查询列表
        @Override
        public List<Book> findAllList() {
            String sql = "select id,userName,status from t_book";
            List<Book> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
            return query;
        }
    	//批量添加(删除、修改类似)
        @Override
        public void batchAddBook(List<Object[]> batchArgs) {
            String sql = "insert into t_book(id,userName,status) values(?,?,?)";
            int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
            System.out.println(Arrays.toString(ints));
        }
    
    }
    
  3. 测试

    @Test
    public void jdbcTemplateTest4() {
        Integer count = bookService.findCount();
        System.out.println(count);
    }
    @Test
    public void jdbcTemplateTest6() {
        Book book = bookService.findBook(2);
        System.out.println(book);
    }
    @Test
    public void jdbcTemplateTest7() {
        List<Book> all = bookService.findAll();
        Iterator<Book> iterator = all.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
    @Test
    public void jdbcTemplateTest8() {
        List<Object[]> list = new ArrayList<>();
        Object[] o1 = {null,"name11","true"};
        Object[] o2 = {null,"name22","false"};
        Object[] o3 = {null,"name33","true"};
        list.add(o1);
        list.add(o2);
        list.add(o3);
        bookService.batcAdd(list);
    }
    

Spring 事务操作

事务是逻辑上的一组操作,要么都执行,要么都不执行。

事务的特性(ACID)

原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

Spring事务管理API

PlatformTransactionManager 接口介绍

PlatformTransactionManager接口表示事务管理器,为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,更具不同的框架提供不同的实现类。

PlatformTransactionManager 接口中定义的三个方法:

public interface PlatformTransactionManager extends TransactionManager {
    //根据指定的传播行为,返回当前活动的事务或创建一个新事务。
    TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
	//提交事务
    void commit(TransactionStatus var1) throws TransactionException;
	//对执行的事务进行回滚
    void rollback(TransactionStatus var1) throws TransactionException;
}

PlatformTransactionManager 接口对应不同框架的实现类

事务描述
org.springframework.jdbc.datasource.DataSourceTransactionManager使用Spring JDBC或者MyBatis进行持久化数据时使用。
org.springframework.orm.hibernate5.HibernateTransactionManager使用Hibernate进行数据持久化时使用。
org.springframework.orm.jpa.JpaTransactionManager使用JPA进行数据持久化时使用。
org.springframework.transaction.jta.JtaTransactionManager使用JTA实现管理事务,在一个事务跨越多个资源时使用。

在这里插入图片描述

当我们使用JDBC或Mybatis进行数据持久化操作时,xml配置操作如下:

<!-- 事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源 -->
    <property name="dataSource" ref="DruidDataSource"/>
</bean>
TransactionDefinition 接口介绍

事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition var1)方法来得到一个事务,这个方法里面的参数是 TransactionDefinition类 ,这个类就定义了一些基本的事务属性。

在这里插入图片描述

@Transactional 注解

@Transactional 注解定义在类上表示当前类中所有方法都添加事务。若在方法上,则表示为此方法添加事务。

propagation 事务传播行为:多事务方法直接进行调用,这个过程中事务是如何进行管理的。
在这里插入图片描述

ioslation 事务隔离级别:事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题(脏读、不可重复读、幻读)

隔离级别描述
READ UNCOMMITTED
(读未提交数据)
允许事务读取未被其它事务提交更改。脏读、不可重复读以及幻读的问题都会出现。
READ COMMITED
(读已提交数据)
只允许事务读取已经被其它事务提交的变更,可以避免脏读。但不可重复读和幻读问题仍然可能出现。
REPEATABLE READ
(可重复读)
确保事务可以多次从一个字段中读取相同的值。
在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读。但幻读的问题仍然存在。
SERIALIZABLE
(串行化)
确保事务可以从一个表中读取相同的值。
在这个事务持续期间。禁止其他事务对该表执行插入、更新、删除操作,所有并发问题都可以避免,但性能十分低下。

timeout 超时时间:事务需要在一定时间内进行提交,如果不提交进行回滚。默认值是 -1(时间以秒单位计算)
readOnly 是否只读:读:查询操作,写:添加修改删除操作。默认值 false。
rollbackFor 回滚:设置出现哪些异常进行事务回滚。
noRollbackFor 不回滚:设置出现哪些异常不进行事务回滚。

案例:
  1. 注解配置中添加tx命名空间

    <beans xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
        <!-- 事务管理器 -->
        <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 注入数据源 -->
            <property name="dataSource" ref="DruidDataSource"/>
        </bean>
        <!-- 开启事务注解 -->
        <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
    

    或 使用xml配置

    <!-- 事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="DruidDataSource"/>
    </bean>
    
    <!-- xml配置通知 -->
    <tx:advice id="txadvice">
        <!-- 配置事务参数 -->
        <tx:attributes>
            <tx:method name="account*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    
    <!-- 配置切入点和切面 -->
    <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut id="pt" expression="execution(* spring.tm.service.UserService.*(..))"/>
        <!-- 配置切面 -->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>
    
  2. service层(如果使用xml配置就需要添加@Transactional注解)

    @Service
    @Transactional(readOnly = false,timeout = 1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
    public class UserService {
    
        @Autowired
        private UserDao userDaoImpl;
    
        public void accountMoney(Integer addId,Integer reduceId, BigDecimal money) {
            userDaoImpl.addMoney(addId,money);
            //模拟异常
            int i = 10/0;
            userDaoImpl.reduceMoney(reduceId,money);
        }
    }
    

Spring使用配置类代替xml配置文件

@Configuration//配置类
@ComponentScan(basePackages = "spring.tm")//组件扫描
@EnableTransactionManagement //开启事务
public class AnnotationConfig {
    //创建数据库链接池
    @Bean
    public DataSource getDruidDataSource() {
        try {
            Properties properties = new Properties();
            InputStream resourceAsStream =
                    ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
            properties.load(resourceAsStream);
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
            return dataSource;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 创建JdbcTemplate对象
     * @param dataSource 在IOC容器中更具类型找到DataSource对象
     * @return
     */
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
    /**
     * 创建事务管理器
     * @param dataSource 在IOC容器中更具类型找到DataSource对象
     * @return
     */
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

Spring5 框架新功能

整个 Spring5 框架的代码基于 Java8,运行时兼容 JDK9,许多不建议使用的类和方法在代码库中已删除。

Spring5 框架日志

Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2。

1. 导入jar包
在这里插入图片描述

2. 创建log4j2.xml配置文件(名字是固定的)

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
    <!--先定义所有的appender-->
    <appenders>
        <!--输出日志信息到控制台-->
        <console name="Console" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

Spring5 框架核心容器支持@Nullable 注解

@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以 为空,参数值可以为空

注解用在方法上面,方法返回值可以为空。

@Nullable
String getId()

注解使用在方法参数里面,方法参数可以为空。

String getId(@Nullable Integer id)

注解使用在属性上面,属性值可以为空。

@Nullable
private String id;

Spring5 核心容器支持函数式风格 GenericApplicationContext

函数式风格创建对象,并交给Spring 进行管理。

public void genericApplicationContextTest() {
    //1.创建GenericApplicationContext对象
    GenericApplicationContext genericApplicationContext = new GenericApplicationContext();
    //2.调用context的方法对象注册
    genericApplicationContext.refresh();
    genericApplicationContext.registerBean(User.class,() -> new User());
    genericApplicationContext.registerBean("myUser",User.class,() -> new User());
    //3.获取在spring注册的对象
    User user = (User) genericApplicationContext.getBean("spring.tm.pojo.User");
    User user2 = (User) genericApplicationContext.getBean("myUser");
    System.out.println(user);
    System.out.println(user2);
}

Spring5 支持整合 JUnit5

整合 JUnit4

@RunWith(SpringJUnit4ClassRunner.class)//单元测试框架
@ContextConfiguration("classpath:ApplicationContext2.xml")//加载配置文件
public class JTest4 {
    @Autowired
    private UserService userService;
    @Test
    public void test() {
        userService.accountMoney(1001,1002,new BigDecimal(200));
    }
}

整合 JUnit5

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath*:ApplicationContext2.xml")
public class JTest5 {
    @Autowired
    private UserService userService;
    @Test
    public void test() {
        userService.accountMoney(1001,1002,new BigDecimal(300));
    }
}

使用复合注解替代上面两个注解完成整合。

@SpringJUnitConfig(locations = "classpath*:spring/tm/ApplicationContext2.xml")
public class JTest5 {
    @Autowired
    private UserService userService;
    @Test
    public void test() {
        userService.accountMoney(1001,1002,new BigDecimal(300));
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值