• 一文入门Spring


    生命无罪,健康万岁,我是laity。

    我曾七次鄙视自己的灵魂:
    第一次,当它本可进取时,却故作谦卑;
    第二次,当它在空虚时,用爱欲来填充;
    第三次,在困难和容易之间,它选择了容易;
    第四次,它犯了错,却借由别人也会犯错来宽慰自己;
    第五次,它自由软弱,却把它认为是生命的坚韧;
    第六次,当它鄙夷一张丑恶的嘴脸时,却不知那正是自己面具中的一副;
    第七次,它侧身于生活的污泥中,虽不甘心,却又畏首畏尾。

    一、Spring说明

    Spring框架是由于软件开发的复杂而创建的

    Spring对速度、简单性和成产率的关注使它称为世界上最流行的Java框架

    • 微服务开发 (Microsevices)
    • web开发(web apps)
    • 分布式开发(spring cloud)

    Spring 是一个大家族

    Spring全家桶

    • Spring Framwork
    • Spring Boot
    • Spring Cloud
    • Spring MVC

    官网地址

    https://spring.io/

    二、Spring Framework

    特征

    • 核心技术:依赖注入、事件、资源、i18n、验证、数据绑定、类型转换、SpEL、AOP。
    • 测试:模拟对象、TestContext 框架、Spring MVC 测试、WebTestClient.
    • 数据访问:事务、DAO 支持、JDBC、ORM、Marshalling XML。
    • Spring MVCSpring WebFlux Web 框架。
    • 集成:远程处理、JMS、JCA、JMX、电子邮件、任务、调度、缓存。
    • 语言:Kotlin、Groovy、动态语言。

    Spring 架构

    核心容器

    Core Container

    • Ioc控制反转
      • 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转换到外部,此思想称为控制反转。
    • Spring技术对IoC思想进行了实现
      • Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的外部
      • IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为 Bean
    • DI 依赖注入
      • 在容器中建立Bean与Bean之间的依赖关系的整个过程,称为依赖注入。

    数据访问

    Data Access/Integration

    面向切面编程

    AOP

    AOP思想的实现

    Aspects

    事务

    Transactions

    三、Spring IoC

    3.1 Spring框架部署(IoC)

    3.1.1 创建Maven项目

    • Java
    • Web

    3.1.2 添加SpringIoC依赖

    • core
    <!-- spring-core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.13</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • beans
    <!-- spring-beans -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.13.RELEASE</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • context
    <!-- spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.12.RELEASE</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • aop
      • 导入 context 直接携带导入
    • expression
      • 导入 context 直接携带导入

    3.1.3 创建Spring配置文件

    通过配置文件 “告诉” Spring 容器创建什么对象,给对象属性 赋什么值

    • 在resources目录下创建名为:applicationContext.xml 文件(文件名是可以自定义的,但是统一叫这个)

    • <?xml version="1.0" encoding="UTF-8"?>
      <beans
              xmlns="http://www.springframework.org/schema/beans"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:aop="http://www.springframework.org/schema/aop"
              xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/aop
              http://www.springframework.org/schema/aop/spring-aop.xsd">
      <!--对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则-->
      <!--通常一个框架为了开发者能够正确的配置,都会提供xml的规范文件(dtd\xsd)-->
      <!--xsd 可以根据配置引入多个,只要引入了相应的xsd,可以使用相应标签规则属性-->
      
      
      </beans>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    3.2 SpringIoc使用

    使用SpringIoC组件创建并管理对象

    3.2.1 创建一个实体类

    3.2.2 在Spring配置文件中配置实体类

    <!-- 通过bean 实体类配置给Spring进行管理 id表示实体类的唯一标识 -->
    <bean id="student" class="com.laity.ioc.bean.Student">
        <property name="stuNum" value="101"/>
        <property name="stuName" value="laity"/>
        <property name="stuAge" value="18"/>
    </bean>  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.2.3 初始化Spring工厂,获取对象

    public class Test1 {
        public static void main(String[] args) {
            // 通过Spring容器创建Student对象
            // 1.初始化spring容器,加载Spring配置文件。
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 2.通过Spring容器获取 Student对象
            Student student = (Student) context.getBean("student");
            System.out.println(student);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.3 IoC和DI

    • IoC控制反转,依赖Spring对象工厂完成对象的创建。
    • DI 依赖注入,在Spring完成对象创建的同时依赖Spring容器完成对象属性的赋值。

    3.3.1 IoC

    当我们需要通过Spring对象工厂,创建某个类的对象时,需要将这个交给spring容器来进行管理

    3.3.2 DI

    通过Spring容器 给创建的对象属性赋值

    3.4 DI依赖注入

    Spring容器加载配置文件后,通过反射创建类的对象,并给属性赋值

    四、Spring配置数据源

    4.1 数据源(连接池)的作用

    • 数据源(连接池)是提高程序性能出现的
    • 事先实例化数据源,初始化部分连接资源
    • 使用连接资源时从数据源中获取
    • 使用完毕后将连接资源归还给数据源

    常用的数据源(连接池):DBCP、C3P0、BoneCP、Druid等

    4.2 数据源开发步骤

    • 导入数据源坐标和数据源驱动坐标
    • 创建数据源对象
    • 配置文件,设置基本连接数据
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    
    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>
    </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4.3 抽取jdbc.properties文件

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql:///java?useSSL=false&useUnicode=true&characterEncoding=utf-8&useServerPrepStmts=true
    user=root
    password=wang9264
    initialSize=5
    maxActive=10
    # useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
    maxWait=3000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.4 Spring配置数据源

    <!-- Spring配置数据源 =====================================================-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///java?useSSL=false"></property>
        <property name="user" value="root"></property>
        <property name="password" value="wang9264"></property>
    </bean>
    
    name值 要与 方法名对应 例如:setDriverClass 写成 driverClass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.5 Spring加载properties

    <!-- 1. 先导入 context 规则-->
    <?xml version="1.0" encoding="UTF-8"?>
    <beans
            xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 2. 加载外部的properties文件 ${key} -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${driverClassName}"/>
            <property name="jdbcUrl" value="${url}"/>
            <property name="user" value="${user}"/>
            <property name="password" value="${password}"/>
        </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    五、Spring注解开发

    Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。

    5.1 Spring的原始注解

    主要代替 配置

    在这里插入图片描述

    • 在实现类上添加注解
    package com.laity.service.impl;
    
    import com.laity.dao.UserDao;
    import com.laity.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Service;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: com.laity.service.impl.UserServiceImpl
     * @Date: 2022年05月26日 19:59
     * @Description:
     */
    
    //<bean id="userService" class="com.laity.service.impl.UserServiceImpl">
    //<property name="userDao" ref="userDao"/>
    //</bean>
    //@Component("userService")  // 使用在类上用于实例化Bean
    @Service("userService")  // 使用在service层类上用于实例化Bean
    public class UserServiceImpl implements UserService {
    
        //<property name="userDao" ref="userDao"/>
        // @Autowired // 使用在字段上用于根据类型依赖注入
        // @Qualifier("userDao")  // 结合@Autowired一起使用用于根据名称进行依赖注入
        @Resource(name = "userDao") // 相当于 @Autowired + @Qualifier
        private UserDao userDao;
    	// xml 方式需要使用set方法 
        // 注解的方式 set方法可以不需要有
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void save() {
            userDao.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    package com.laity.dao.impl;
    
    import com.laity.dao.UserDao;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Repository;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: com.laity.dao.impl.UserDapImpl
     * @Date: 2022年05月26日 19:57
     * @Description:
     */
    //<bean id="userDao" class="com.laity.dao.impl.UserDapImpl"></bean>
    
    //@Component("userDao")  // 使用在类上用于实实例Bean
    @Repository("userDao") // 使用在dao层类上用于实例化Bean
    public class UserDapImpl implements UserDao {
        @Override
        public void save() {
            System.out.println("save running ...");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 在 applicationContext.xml 中 配置扫描器
    <!--    <bean id="userDao" class="com.laity.dao.impl.UserDapImpl"></bean>-->
    
    <!--    <bean id="userService" class="com.laity.service.impl.UserServiceImpl">-->
    <!--        <property name="userDao" ref="userDao"/>-->
    <!--    </bean>-->
    <!-- 配置组件扫描 : 扫注解 -->
    <!-- 扫描 com.laity 下的所有包 -->
    <context:component-scan base-package="com.laity"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5.2 Spring的新注解

    上面的注解还不能全部代替xml配置文件,所以出现了新注解 – 达到全注解开发

    注解说明
    @Configuration用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
    @ComponentScan用于指定Spring在初始化容器时要扫描的包。作用和在 Spring 的 xml 配置文件中的<context:component-scan base-package=“com.itheima”/>一样
    @Bean使用在方法上,标注将该方法的返回值存储到 Spring 容器中
    @PropertySource用于加载.properties 文件中的配置
    @Import用于导入其他配置类
    • Spring的核心配置文件:SpringConfiguration.class
    package com.laity.config;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.*;
    
    import javax.sql.DataSource;
    import java.beans.PropertyVetoException;
    import java.util.ResourceBundle;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: com.laity.config.SpringConfiguration
     * @Date: 2022年05月26日 22:08
     * @Description: Spring的核心配置文件
     */
    @Configuration // 标志 该类是Spring的核心配置文件
    //  <context:component-scan base-package="com.laity"/>
    @ComponentScan("com.laity") // 代替扫描注解的作用
    @Import({DataSourceConfiguration.class,})
    public class SpringConfiguration {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 数据源配置文件:DataSourceConfiguration.class
    package com.laity.config;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.PropertySource;
    
    import javax.sql.DataSource;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: com.laity.config.DataSourceConfigration
     * @Date: 2022年05月26日 22:28
     * @Description: 数据源 配置文件
     */
    
    // <context:property-placeholder location="classpath:jdbc.properties"/>
    @PropertySource("classpath:jdbc.properties")
    public class DataSourceConfiguration {
    
        @Value("${driverClassName}")
        private String driverClassName;
        @Value("${url}")
        private String url;
        @Value("${user}")
        private String user;
        @Value("${password}")
        private String password;
    
        // 使用在方法上,标注将该方法的返回值存储到 Spring 容器中
        @Bean("dataSource")  // Spring会将当前方法的返回值 以 指定名称存储到Spring容器中
        public DataSource getDataSource() throws Exception {
            /*
            <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${driverClassName}"/>
            <property name="jdbcUrl" value="${url}"/>
            <property name="user" value="${user}"/>
            <property name="password" value="${password}"/>
            </bean>
             */
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(driverClassName);
            dataSource.setJdbcUrl(url);
            // useSSL=false&useUnicode=true&characterEncoding=utf-8&useServerPrepStmts=true
            dataSource.setUser(user);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 修改测试文件:UserController.class
    package com.laity.web;
    
    import com.laity.config.SpringConfiguration;
    import com.laity.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: com.laity.web.UserController
     * @Date: 2022年05月26日 20:14
     * @Description: 假的web层
     */
    public class UserController {
        public static void main(String[] args) {
            // ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
            UserService userService = app.getBean(UserService.class); // 一个Bean用类,多个的时候用 id
            userService.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    六、Spring整合Junit

    让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉Junit
    将需要进行测试Bean直接在测试类中进行注入。

    5.3.1 导入Spring集成Junit的坐标

    <!-- spring-test -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.2.10.RELEASE</version>
        <scope>test</scope>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5.3.2 使用@Runwirh注解替换原来的运行期

    5.3.3 使用@ContextConfiguration指定配置文件或配置类

    5.3.4 使用@Autowired注入需要测试的对象

    5.3.5 创建测试方法进行测试

    • 2-5步骤都在一个文件中
    package com.laity.test;
    
    import com.laity.config.SpringConfiguration;
    import com.laity.service.UserService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: com.laity.test.SpringJunitTest
     * @Date: 2022年05月26日 22:50
     * @Description: SpringJunitTest
     */
    
    @RunWith(SpringJUnit4ClassRunner.class)
    //@ContextConfiguration("classpath:applicationContext.xml")
    @ContextConfiguration(classes = SpringConfiguration.class)
    public class SpringJunitTest {
        @Autowired
        private UserService userService;
    
        @Test
        public void test() {
            userService.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    七、Spring AOP

    7.1 什么是AOP

    AOPAspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

    AOP 是 OOP(面向对象编程) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    7.2 AOP的作用及其优势

    • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

    • 优势:减少重复代码,提高开发效率,并且便于维护

    面向切面编程 – 目标方法+增强方法 – 解耦合 – 运行时增强功能。

    7.3 AOP的底层实现

    实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

    7.4 AOP的动态代理技术

    常用的动态代理技术

    • JDK 代理 : 基于接口的动态代理技术

    • cglib 代理:基于父类的动态代理技术

    在这里插入图片描述

    7.4.1 JDK的动态代理

    Sping-aop/main/proxy/jdk – 自行查看

    7.4.2 cglib的动态代理

    spring-context中已经集成了 cglib的依赖

        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.0.5.RELEASE</version>
        </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Target实现类

    package proxy.cglib;
    
    import proxy.jdk.TargetInterface;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: proxy.jdk.Target
     * @Date: 2022年05月31日 12:51
     * @Description: 实现类
     */
    public class Target  {
        public void save() {
            System.out.println("save running ...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Advice增强方法

    package proxy.cglib;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: proxy.jdk.Advice
     * @Date: 2022年05月31日 12:52
     * @Description: 方法增强
     */
    public class Advice {
        public void before() {
            System.out.println("前置增强 ...");
        }
    
        public void afterReturning() {
            System.out.println("后置增强 ...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Java测试类

    package proxy.cglib;
    
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    import proxy.jdk.TargetInterface;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: proxy.jdk.ProxyTest
     * @Date: 2022年05月31日 12:54
     * @Description: 测试方法
     */
    public class ProxyTest {
        public static void main(String[] args) {
            // 创建目标对象
            final Target target = new Target();
            // 增强对象
            final Advice advice = new Advice();
    
            // 返回值 就是动态生成的代理对象 -- 基于cglib
            // 1.需要创建增强器
            Enhancer enhancer = new Enhancer();
            // 2.设置父类 -- 目标
            enhancer.setSuperclass(Target.class);
            // 3.设置回调
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    // 执行前置增强
                    advice.before();
                    // 执行目标
                    Object invoke = method.invoke(target, objects);
                    // 执行后置增强
                    advice.afterReturning();
                    return invoke;
                }
            });
            // 4.创建代理
            Target proxy = (Target) enhancer.create();
            // 测试
            proxy.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    7.5 AOP的相关概念

    Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

    在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:

    • Target(目标对象):代理的目标对象

    • Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类

    • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点

      • 可以被增强的方法就是连接点
    • Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义

      • 真正被增强的方法才是切入点
    • Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知

      • 告诉 切入点后续要执行的事情
    • Aspect(切面):是切入点和通知(引介)的结合

    • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

      • 目标对象通过增强(方法/逻辑)生成代理对象的过程

    7.6 AOP开发明确事项

    7.6.1 需要编写的内容

    • 编写核心业务代码(目标类的目标方法)

    • 编写切面类,切面类中有通知(增强功能方法)

    • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

    7.6.2 AOP 技术实现的内容

    Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

    7.6.3 AOP 底层使用哪种代理方式

    在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

    7.6 基于XML的AOP开发

    7.6.1 快速入门

    • 导入 AOP 相关坐标
            <!--导入spring的context坐标,context依赖aop-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.5.RELEASE</version>
            </dependency>
            <!-- aspectj的织入 -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.13</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 创建目标接口和目标类(内部有切点)
    package aop;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: proxy.jdk.TargetInterface
     * @Date: 2022年05月31日 12:51
     * @Description: 接口
     */
    public interface TargetInterface {
        void save();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    package aop;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: proxy.jdk.Target
     * @Date: 2022年05月31日 12:51
     * @Description: 实现类
     */
    public class Target implements TargetInterface {
        @Override
        public void save() {
            System.out.println("save running ...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 创建切面类(内部有增强方法)
    package aop;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: aop.MyAspect
     * @Date: 2022年05月31日 14:26
     * @Description: 切面类
     */
    public class MyAspect {
    
        public void before(){
            System.out.println("前置增强............");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 将目标类和切面类的对象创建权交给 spring
        <!-- 目标对象 -->
        <bean id="target" class="aop.Target">
    
        </bean>
        <!-- 切面对象 -->
        <bean id="myAspect" class="aop.MyAspect"></bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 在 applicationContext.xml 中配置织入关系
        <!-- 目标对象 -->
        <bean id="target" class="aop.Target">
    
        </bean>
        <!-- 切面对象 -->
        <bean id="myAspect" class="aop.MyAspect"></bean>
        <!--告诉Spring容器 哪些方法(切点)需要进行哪些增强(前置增强、后置增强)-->
        <!-- 配置织入 -->
        <aop:config>
            <!-- 声明切面 -->
            <aop:aspect ref="myAspect">
                <!-- 切面:切点+通知 -->
                <!-- 当你访问 aop.Target.save()方法时进行前置增强(method="before")-->
                <aop:before method="before" pointcut="execution(public void aop.Target.save())"></aop:before>
            </aop:aspect>
        </aop:config>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 测试代码
    package aop;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: aop.AopTest
     * @Date: 2022年05月31日 14:47
     * @Description: Aop方法测试
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AopTest {
        @Autowired
        private TargetInterface target;
    
        @Test
        public void testAop() {
            target.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    7.7 切点表达式的写法 *

    表达式语法:

    ​ execution([修饰符] 返回值类型 包名.类名.方法名(参数))

    <!-- 配置织入 -->
    \<aop:config>
        <!-- 声明切面 -->
        <aop:aspect ref="myAspect">
            <!-- 切面:切点+通知 -->
            <!-- 当你访问 aop.Target.save()方法时进行前置增强(method="before")-->
            <aop:before method="before" pointcut="execution(public void aop.Target.save())">\</aop:before>
        \</aop:aspect>
    \</aop:config>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    execution(public void aop.Target.method())
    execution(void aop.Target.*(..))
    execution(* aop.*.*(..))  -- aop下的包被增强 -- 常用
    execution(* aop..*.*(..)) -- aop下的子包也可以被增强
    execution(* *..*.*(..))  -- 都任意都增强
    * 就代表任意
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 访问修饰符可以省略

    • 返回值类型、包名、类名、方法名可以使用星号* 代表任意

    • 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类

    • 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表

    • XML 配置 AOP 详解

    通知的配置语法:

    <aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

    在这里插入图片描述

    7.8 基于注解的 AOP 开发

    基于注解的aop开发步骤:

    ① 创建目标接口和目标类(内部有切点)

    ② 创建切面类(内部有增强方法)

    ③ 将目标类和切面类的对象创建权交给 spring

    ④ 在切面类中使用注解配置织入关系

    ⑤ 在配置文件中开启组件扫描和 AOP 的自动代理

    ⑥ 测试

    7.8.1 创建目标接口和目标类(内部有切点)

    7.8.2 创建切面类(内部有增强方法)

    7.8.3 将目标类和切面类的对象创建权交给 spring

    package anno;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: aop.MyAspect
     * @Date: 2022年05月31日 14:26
     * @Description: 切面类
     */
    @Component("myAspect")  // 交给spring
    @Aspect  // 标注当前MyAspect是一个切面类
    public class MyAspect {
    
        // 配置前置增强
        @Before(value = "execution(* anno.*.*(..))")
        public void before() {
            System.out.println("前置增强............");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    package anno;
    
    import org.springframework.stereotype.Component;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: proxy.jdk.Target
     * @Date: 2022年05月31日 12:51
     * @Description: 实现类
     */
    @Component("target") // 交给spring容器
    public class Target implements TargetInterface {
        @Override
        public void save() {
            System.out.println("save running ...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7.8.4 在切面类中使用注解配置织入关系

        <!-- 组件扫描 -->
        <context:component-scan base-package="anno"/>
    
    • 1
    • 2

    7.8.5 在配置文件中开启组件扫描和 AOP 的自动代理

        <!-- 组件扫描 -->
        <context:component-scan base-package="anno"/>
    
        <!-- aop自动代理 -->
        <aop:aspectj-autoproxy/>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7.8.6 测试代码

    package anno;
    
    import anno.TargetInterface;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * @author: Laity
     * @Project: JavaLaity
     * @Package: aop.AopTest
     * @Date: 2022年05月31日 14:47
     * @Description: Aop方法测试
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext-anno.xml")
    public class AnnoTest {
        @Autowired
        private TargetInterface target;
    
        @Test
        public void testAop() {
            target.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    7.9 AOP注解

    在这里插入图片描述

    八、Spring事务控制

    8.1 编程式事务控制相关对象

    8.1.1 PlatformTransactionManager

    PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法

    在这里插入图片描述

    注意:

    PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:Dao 层技术是jdbc

    或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager

    Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager

    8.1.2 TransactionDefinition

    在这里插入图片描述

    设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。
    ISOLATION_DEFAULT
    ISOLATION_READ_UNCOMMITTED
    ISOLATION_READ_COMMITTED
    ISOLATION_REPEATABLE_READ
    ISOLATION_SERIALIZABLE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    事务传播行为

     REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
     SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
     MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
     REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
     NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
     NEVER:以非事务方式运行,如果当前存在事务,抛出异常
     NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
     超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
     是否只读:建议查询时设置为只读
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    8.1.3 TransactionStatus

    事务的状态信息

    在这里插入图片描述

    8.2 基于 XML 的声明式事务控制

    8.2.1 什么是声明式事务控制

    Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

    声明式事务处理的作用

    • 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可

    • 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便

    注意:Spring 声明式事务控制底层就是AOP。

    8.2.2 声明式事务控制的实现

    • 引入tx命名空间
    • 配置事务增强
    • 配置事务 AOP 织入
    • 测试事务控制转账业务代码

    8.3 基于注解的声明式事务控制

    ① 使用 @Transactional 在需要进行事务控制的类或是方法上修饰,注解可用的属性同 xml 配置方式,例如隔离
    级别、传播行为等。
    ② 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。
    ③ 使用在方法上,不同的方法可以采用不同的事务参数配置。
    ④ Xml配置文件中要开启事务的注解驱动<tx:annotation-driven />

    注解声明式事务控制的配置要点
     平台事务管理器配置(xml方式)
     事务通知的配置(@Transactional注解配置)
     事务注解驱动的配置 <tx:annotation-driven/>
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    (免费分享)基于springboot博客系统
    【Nginx】在Linux上如何安装Nginx教程+Nginx基本命令的使用
    一文读懂RFID射频识别技术
    SpringCloud Alibaba微服务第8章之Nacos
    静态ip详解
    MiniApp Dev 6
    以clion为例记录一次基于docker环境配置开发
    flutter windows 安装或者环境相关问题
    Docker的overlay2目录占用磁盘根目录的解决办法
    paddle学习赛——钢铁目标检测(yolov5、ppyoloe+,Faster-RCNN)
  • 原文地址:https://blog.csdn.net/duyun0/article/details/125597102