Spring框架学习---Spring Framework下篇

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: Spring框架学习---Spring Framework下篇

关于上篇学习链接:Spring框架学习---Spring Framework上篇

3.6 静态代理和动态代理

3.6.1 静态代理

  • 生活中的代理(租客通过中介租到房东的房子)(例1)

在这里插入图片描述

角色分析:
在这里插入图片描述
项目框架:
在这里插入图片描述
Host.Java 相当于现实生活中的房东(准备出租房子的人)
在这里插入图片描述
上面的Rent为接口,表示出租操作
在这里插入图片描述
Proxy.java 相当于现实生活中的中介(房东将房子交给中介进行出租)
在这里插入图片描述

Client.Java相当于现实生活中的租客(准备租房子的人,租客通过中介租到房东的房子)
在这里插入图片描述
运行效果:
在这里插入图片描述
上面提到的四个角色,抽象角色对应Rent接口,真实角色对应房东,代理角色对应中介,客户角色对应租客。

代理模式的优缺点:
在这里插入图片描述

再来一个例子(例2):
项目结构:
在这里插入图片描述

UserService.Java 抽象角色
在这里插入图片描述
UserServiceImpl.Java 真实角色
在这里插入图片描述
Client.Java 客户角色
在这里插入图片描述
运行效果:
在这里插入图片描述

如果要给UserServiceImp里面每一个操作(增删改查)增加一个日志
在这里插入图片描述
像上面这种操作非常的麻烦,我们可以通过上面学到的代理来解决这个问题
首先先编写一个代理类(UserServiceProxy.Java),在代理类中添加日志业务,并在各个方法(增删改查)中实现日志

public class UserServiceProxy implements UserService {
    private UserService userService;

    public UserServiceProxy(UserService userService) {
        // TODO Auto-generated constructor stub
        this.userService = userService;
    }

    @Override
    public void add() {
        // TODO Auto-generated method stub
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        // TODO Auto-generated method stub
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        // TODO Auto-generated method stub
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        // TODO Auto-generated method stub
        log("query");
        userService.query();
    }
    
    //打印日志方法
    public void log(String msg) {
        System.out.println("[Debug]使用了"+msg+"方法");
    }

}

客户角色通过代理对象实现业务逻辑
在这里插入图片描述
运行效果:
在这里插入图片描述
上面的操作其实是一种横向开发(保持原有方法不变的情况下增加相应业务)
在这里插入图片描述

3.6.2 动态代理

在这里插入图片描述
通过动态代理实现例1:
项目结构:(Host.Java和Rent.Java和实例1无差别)
在这里插入图片描述
ProxyInvocationHandler.Java代理处理程序,用户生成代理,代理的相关操作

public class ProxyInvocationHandler implements InvocationHandler{
    private Rent rent;
    
    public void setRent(Rent rent) {
        this.rent = rent;
    }
    
    //生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces()    , this);
    }
    
    // 处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        // 中介带租客看房子
        seeHouse();
        // 中介将房子出租给租客
        Object result=method.invoke(rent, args);
        //中介收取中介费
        fare();
        return result;
    }
    
    public void seeHouse() {
        System.out.println("中介看房子");
    }

    
    public void fare() {
        System.out.println("中介收费");
    }
}

client.Java 租客通过中介租房子
在这里插入图片描述
运行效果:
在这里插入图片描述

通过动态代理实现例2:
项目框架:
在这里插入图片描述
ProxyInvocationHandler.Java

public class ProxyInvocationHandler implements InvocationHandler{
    private UserService     userService;
    
    public void setRent(UserService userService) {
        this.userService = userService;
    }
    
    //生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), userService.getClass().getInterfaces()    , this);
    }
    
    // 处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        //打印日志
        log(method.getName());
        //执行相应业务
        Object result=method.invoke(userService, args);
        return result;
    }
    
       //打印日志方法
        public void log(String msg) {
            System.out.println("[Debug]使用了"+msg+"方法");
        }
    
}

Client.Java
在这里插入图片描述
运行效果:
在这里插入图片描述
从上面的实例2我们可以发现其实我们只是稍微修改了一下ProxyInvocationHandler.Java中接口对象,其实我们可以整理出一个模板如下图(将接口对象设置为Object对象):
在这里插入图片描述

具体代码如下:

public class ProxyInvocationHandler implements InvocationHandler{
    private Object     target;
    
    public void setTarget(Object target) {
        this.target = target;
    }
    
    //生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }
    
    // 处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        //打印日志
        log(method.getName());
        //执行相应业务
        Object result=method.invoke(target, args);
        return result;
    }
    
       //打印日志方法
        public void log(String msg) {
            System.out.println("[Debug]使用了"+msg+"方法");
        }
    
}

Client.Java中(需要使用的代理的类只需要给setTarget("接口“)传递一个接口对象即可)
在这里插入图片描述
运行效果一致:
在这里插入图片描述

动态代理的好处:
在这里插入图片描述

3.7 AOP

  • 什么是AOP?
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。[1]

简单解释:中文叫做面向切面编程,在不改变源码的情况下给程序动态添加功能的一种技术。
在这里插入图片描述

  • 相关概念

在这里插入图片描述

  • 实例1

环境搭建
项目结构:
在这里插入图片描述

maven:

<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.9</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

在这里插入图片描述
service包:
在这里插入图片描述
在这里插入图片描述
log包:
在这里插入图片描述
在这里插入图片描述
resources包:
先导入aop的头文件

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

配置aop
在这里插入图片描述

测试类:
在这里插入图片描述
运行效果:
在这里插入图片描述

  • 另一种实现方式

新增一个diy包:
在这里插入图片描述
在包中增加一个DiyPointCut.Java类
在这里插入图片描述
修该配置(把原来胡aop配置注释了):
在这里插入图片描述
再次运行测试类:
在这里插入图片描述

  • 通过注解实现

在DiyPointCut.Java类中,使用注解:
在这里插入图片描述
开启注解支持(把前面的aop配置注释掉):
在这里插入图片描述
再次运行测试类:
在这里插入图片描述

  • 环绕切入

在DiyPointCut.Java类中添加一个around方法
在这里插入图片描述
运行测试类(签名就是具体执行的哪一个方法):
在这里插入图片描述

3.8 Spring整合Mybatis

  • 整合前

项目架构:
在这里插入图片描述

Maven导入相关依赖:
在项目pom.xml中导入以下依赖

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

Mybatis中资源导出失败解决之build配置

 <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

创建dao包:
创建UserMapper接口
在这里插入图片描述
创建UserMapper接口对应的mapper映射文件

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

    <select id="selectUser" resultType="User">
        select * from user_test
    </select>

</mapper>

创建pojo包:
在包中创建User的实体类
在这里插入图片描述
在resources包中创建mybatis的配置文件(mybatis-config.xml)

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


    <typeAliases>
        <package name="pojo"/>
    </typeAliases>
        <!-- 对事务的管理和连接池的配置 -->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC" />
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8" />
                    <property name="username" value="root" />
                    <property name="password" value="root" />
                </dataSource>
            </environment>
        </environments>


        <mappers>
            <mapper class="dao.UserMapper"></mapper>
        </mappers>
</configuration>

创建测试类MyTest:

public class MyTest {
    @Test
    public void selectUser() throws IOException {

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> userList = mapper.selectUser();
        for (User user: userList){
            System.out.println(user);
        }

        sqlSession.close();
    }
}

运行selectUser:
在这里插入图片描述

  • 整合后方式一(使用MyBatis-Spring)

什么是MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

相应版本对应:
在这里插入图片描述
导入MyBatis-Spring(在上面的项目中已经导入了该配置)
在这里插入图片描述
配置数据源替换mybaits的数据源:
创建spring-dao.xml
在这里插入图片描述

在spring-dao.xml中添加mybaits的数据源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>

在spring-dao.xml中配置SqlSessionFactory,关联MyBatis

    <!--配置SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--关联Mybatis-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:dao/*.xml"/>
    </bean>

在配置文件中((mybatis-config.xml)只留下别名和相关配置即可(配置信息都移到上面去了)
在这里插入图片描述

在spring-dao.xml中注册sqlSessionTemplate,关联sqlSessionFactory;

   <!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--利用构造器注入-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

增加Dao接口的实现类;私有化sqlSessionTemplate
在这里插入图片描述

public class UserDaoImpl implements UserMapper {

    //sqlSession不用我们自己创建了,Spring来管理
    private SqlSessionTemplate sqlSession;

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

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

}

在spring-dao.xml中注册bean实现(初始化sqlSession)

  <bean id="userDao" class="dao.UserDaoImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

测试类中新增测试方法test2

    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        UserMapper mapper = (UserMapper) context.getBean("userDao");
        List<User> user = mapper.selectUser();
        System.out.println(user);
    }

运行test2
在这里插入图片描述

  • 整合方式二(通过继承SqlSessionDaoSupport,就无需再配置注册sqlSessionTemplate,因为SqlSessionDaoSupport提供了getSqlSession()可以直接获取SqlSession())

(备注:mybatis-spring1.2.3版才可以使用以下这种方式)

在spring-dao中把sqlSessionTemplate注释掉
在这里插入图片描述
创建一个UserDaorImpl2类,继承SqlSessionDaoSupport并且实现UserMapper接口
在这里插入图片描述
在applicationContext.xml配置中将bean进行修改(初始化sqlSessionFactory)
在这里插入图片描述
再次运行测试2:
在这里插入图片描述

3.9 事物

根据ACID原则一个操作执行要么成功,要么不成功,不存在一部分成功,一部分不成功,为了做到这一点,我们需要使用事务,事务分为编程式事务和声明式事务。

  • 编程式事务

将事务管理代码嵌到业务方法中来控制事务的提交和回滚,这种方式有一个明显的缺点就是必须在每个事务操作业务逻辑中包含额外的事务管理代码。

  • 声明式事务

将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,但是这种方式需要Aop的支持,将事务管理作为横切关注点,通过aop方法模块化。

在spring-dao.xml配置文件中:
先导入事务(tx)和aop的头文件
在这里插入图片描述

       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd

配置声明式事务

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

结合Aop实现事务的置入,先配置事务通知

    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="search*" propagation="REQUIRED"/>
            <tx:method name="get" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

7种事务传播,默认REQUIRED
在这里插入图片描述

然后再配置事务的切入点

    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* dao.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

在UserMapper接口中新增addUser(User user)和delUser(int id)二个方法
在这里插入图片描述
配置这二个方法的映射
在这里插入图片描述
在UserDaorImpl2类中
在这里插入图片描述
在映射文件中,我们故意把delUser的语句设置错误
在这里插入图片描述
测试类中,新增一个test3
在这里插入图片描述
运行情况
在这里插入图片描述
我们查看一下数据库中user_test表的数据,并没有增加(如果不使用事务的话,执行addUser是会成功的,就会增加数据)
在这里插入图片描述

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4天前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
13天前
|
缓存 NoSQL Java
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
Spring Cache 是 Spring 提供的简易缓存方案,支持本地与 Redis 缓存。通过添加 `spring-boot-starter-data-redis` 和 `spring-boot-starter-cache` 依赖,并使用 `@EnableCaching` 开启缓存功能。JetCache 由阿里开源,功能更丰富,支持多级缓存和异步 API,通过引入 `jetcache-starter-redis` 依赖并配置 YAML 文件启用。Layering Cache 则提供分层缓存机制,需引入 `layering-cache-starter` 依赖并使用特定注解实现缓存逻辑。
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
|
1天前
|
安全 前端开发 Java
随着企业应用复杂度提升,Java Spring框架以其强大与灵活特性简化开发流程,成为构建高效、可维护应用的理想选择
随着企业应用复杂度提升,Java Spring框架以其强大与灵活特性简化开发流程,成为构建高效、可维护应用的理想选择。依赖注入使对象管理交由Spring容器处理,实现低耦合高内聚;AOP则分离横切关注点如事务管理,增强代码模块化。Spring还提供MVC、Data、Security等模块满足多样需求,并通过Spring Boot简化配置与部署,加速微服务架构构建。掌握这些核心概念与工具,开发者能更从容应对挑战,打造卓越应用。
6 1
|
10天前
|
XML Java 应用服务中间件
深入探索Spring Boot框架的核心特性
Spring Boot 是一款基于Spring框架的开源框架,旨在简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式(默认配置)来简化整个构建过程。
29 11
|
3天前
|
运维 Java Nacos
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
|
4天前
|
XML Java Maven
Spring5入门到实战------16、Spring5新功能 --整合日志框架(Log4j2)
这篇文章是Spring5框架的入门到实战教程,介绍了Spring5的新功能——整合日志框架Log4j2,包括Spring5对日志框架的通用封装、如何在项目中引入Log4j2、编写Log4j2的XML配置文件,并通过测试类展示了如何使用Log4j2进行日志记录。
Spring5入门到实战------16、Spring5新功能 --整合日志框架(Log4j2)
|
4天前
|
Java API Spring
Spring5入门到实战------1、Spring5框架概述、入门案例
这篇文章是Spring5框架的入门教程,概述了Spring框架的核心概念和特点,并通过一个创建普通Java类的案例,详细演示了从下载Spring核心Jar包、创建配置文件、编写测试代码到运行测试结果的完整流程,涵盖了Spring IOC容器的使用和依赖注入的基本用法。
Spring5入门到实战------1、Spring5框架概述、入门案例
|
7天前
|
前端开发 Java Spring
springboot 整合 netty框架, 实现 心跳检测,自动重连
springboot 整合 netty框架, 实现 心跳检测,自动重连
|
6天前
|
安全 前端开发 Java
Web端系统开发解决跨域问题——以Java SpringBoot框架配置Cors为例
在Web安全上下文中,源(Origin)是指一个URL的协议、域名和端口号的组合。这三个部分共同定义了资源的来源,浏览器会根据这些信息来判断两个资源是否属于同一源。例如,https://www.example.com:443和http://www.example.com虽然域名相同,但由于协议和端口号不同,它们被视为不同的源。同源(Same-Origin)是指两个URL的协议、域名和端口号完全相同。只有当这些条件都满足时,浏览器才认为这两个资源来自同一源,从而允许它们之间的交互操作。
Web端系统开发解决跨域问题——以Java SpringBoot框架配置Cors为例
|
27天前
|
缓存 Java Spring
Spring框架
【7月更文挑战第24天】
35 16