Spring——AOP前言(写一个小demo为了好学习AOP)

news2024/11/14 1:22:10

1.AOP的概念

1.1 AOP简单样例

我们来先通过一个例子来对AOP进行理解,这个例子就是有关Spring的事务的一个样例,有关Spring是怎么实现事务的,这个事务其实本质上就是对于我们代码的一个增强。废话不多说,上程序,请各位同学自行感悟。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <!--有单元测试的环境,Spring5版本,Junit4.12版本-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    <scope>test</scope>
    </dependency>
    <!--连接池-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--mysql驱动包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>
    <!-- Spring整合Junit测试的jar包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>

        然后我们去看一眼我们的数据库,有没有一个Spring_db的表,(如果没有请参照上一篇博客第四小节应该有,创建数据库的sql语句)

        然后我们创建一个model的包,包中有创建一个Account的实体类

/**
 * JavaBean 实体类
 * 封装数据
 */
public class Account {

    private int id;

    private String name;

    private double money;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public Account() {
    }

    public Account(int id, String name, double money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

        然后创建Dao的包,写一个AccountDao的接口

/**
 * 持久层接口
 * 实现保存转账金额的方法
 */
public interface AccountDao {

    public void saveAll (Account account);
}

        再写一个AccountDaoImpl的实现类

/**
 * 持久层的实现类
 */
public class AccountDaoImpl implements AccountDao {
    @Override
    public void saveAll(Account account) {
        //jdbc的程序
        System.out.println("持久层保存成功");
    }
}

        创建service包,再service包下写AccountService的接口

/**
 * 业务层的接口
 */
public interface AccountService {

    public void saveAll(Account account, Account account1);
}

        然后写一个AccountServiceImpl的实现类

public class AccountServiceImpl implements AccountService{

    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void saveAll(Account account, Account account1) {
        System.out.println("业务层保存方法执行");
        accountDao.saveAll(account);
        accountDao.saveAll(account1);
    }
}

        写一个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: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">

    <bean id="accountService" class="com.qcby.service.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <bean id="accountDao" class="com.qcby.dao.AccountDaoImpl"/>
</beans>

        然后在test下创建com.qcby的包结构,写一个Test1的测试类

public class Test1 {

    @Test
    public void run1(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationcontext.xml");
        AccountService as=(AccountService) ac.getBean("accountService");
        Account account=new Account(1,"熊大",10000.00);
        Account account1=new Account(2, "熊二",9999.99);
        as.saveAll(account,account1);
    }
}

        然后我们去写持久层,在写持久层的时候,我们首先要有一个工具类

import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
public class TxUtils {
    //静态的类属性 连接池对象
    public static DruidDataSource ds=null;
    //使用ThreadLocal存储当前线程中的connection对象
    private static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();

    //在静态代码中创建数据库连接池
    static {
        try {
            //通过代码创建druid数据库连接池
            ds=new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql///spring_db");
            ds.setUsername("root");
            ds.setPassword("2020");

        }catch (Exception e){
            throw new ExceptionInInitializerError(e);
        }
    }

    public static Connection getConnection() throws Exception{
        Connection conn=threadLocal.get();
        if (conn==null){
            //从数据源获取数据连接conn
            conn= getDataSource().getConnection();
            //把conn对象存储到当前线程
            threadLocal.set(conn);
        }
        return conn;
    }

    /**
     * 开启事务
     */
    public static void startTransaction() {
        try{
            Connection conn=threadLocal.get();
            if (conn==null){
                conn=getConnection();
                threadLocal.set(conn);
            }
            //开启事务
            conn.setAutoCommit(false);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 回滚事务
     */
    public static void rollback(){
        try{
            Connection conn=threadLocal.get();
            if (conn!=null){
                conn.rollback();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 提交事务
     */
    public static void commit(){
        try{
            Connection conn=threadLocal.get();
            if (conn!=null){
                conn.commit();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 关闭数据库连接,这里的关闭指的是把数据库连接还给数据库连接池
     */
    public static void close(){
        try{
            Connection conn=threadLocal.get();
            if (conn!=null){
                conn.close();
                threadLocal.remove();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 获取数据
     */
    public static DruidDataSource getDataSource(){
        return ds;
    }
}

        这个工具类有几个值得注意的点我们来看一下,第一个,就是我们在类中设置了一个静态的类的属性 ,可以用threadLocal存储这对象,达到只用一个连接的目的,还有就是我们把close的方法写到这个工具类里了,因为我们不能在持久层关闭连接,我们可以从业务层进行关闭。

        然后我们再回到持久层,写连接

public class AccountDaoImpl implements AccountDao {
    @Override
    public void saveAll(Account account) throws Exception {
        //jdbc的连接数据库进行保存数据
        //获取连接加载驱动 保证事务

        System.out.println("持久层保存操作!!!");
        //把数据存到数据库之中
        //获取连接
        Connection conn = TxUtils.getConnection();
        //编写sql
        String sql="insert into account values(null,?,?)";
        //预编译SQL
        PreparedStatement statement= conn.prepareStatement(sql);
        //设置值
        statement.setString(1,account.getName());
        statement.setDouble(1,account.getMoney());
        //执行操作
        statement.executeUpdate();
        //关闭资源,(conn的关闭不能写在持久层)
        statement.close();
    }
}

        这时候我们的业务实现层肯定会有一个异常,跑出去就行了。

        然后运行一下test测试

        那么我们接下来模拟这么一个场景,就是插入第一条数据后,在插入第二条数据第二条数据时模拟一个异常。

try {
    accountDao.saveAll(account);
    int i=10/0;
    accountDao.saveAll(account1);
} catch (SQLException e) {
    e.printStackTrace();
}

        这时候就会有一个除零的异常

        按照测试代码,我们应该加入小明和小红两条数据,但是加入小明之后我们就出现异常就终止执行了,就没法插入小红的这条数据了。

@Test
public void run(){
    ApplicationContext ac=new ClassPathXmlApplicationContext("ApplicationContext.xml");

    AccountService accountService =(AccountService) ac.getBean("accountService");
    Account account=new Account(null,"小明",1000.0);
    Account account1=new Account(null,"小红",1000.0);
    accountService.saveAll(account,account1);
}

        然后我们去业务层中,加上事务的管理

public void saveAll(Account account, Account account1) {

    System.out.println("业务层方法执行了");
    //开启事务 conn是一个连接
    TxUtils.startTransaction();
    try {
        accountDao.saveAll(account);
        int i=10/0;
        accountDao.saveAll(account1);
        //提交事务
        TxUtils.commit();
    } catch (SQLException e) {

        e.printStackTrace();
        //回滚事务
        TxUtils.rollback();
    }

}

        然后我们把那个除零的异常语句给注释掉,我们执行以下看一下效果

        能加进来

        然后我们去把那个异常的注释给解掉,然后再来运行看一下效果。

        数据库中就没有添加进去小明。说明进行了事务的回滚。

        然后如果我们要在业务写一个转账的方法,也需要些开启事务,提交事务,还有回滚事务,就比较麻烦了。

        就是加事务这个代码,我们会发现与业务本身的逻辑没有关系,就是需要在业务逻辑执行前开启事务,执行完提交事务,要是出现问题就要回滚事务。这个事务就式对我们代码的一个增强。

        我们如果要做到不在改源代码的情况下实现事务,我们实现的思路有哪些?

                jdk动态代理

                静态代理

                父子类(父类做业务逻辑,子类做增强)

                装饰者模式

        AOP的底层就是动态代理

1.2 jdk动态代理类

上面说了,我们用jdk的动态代理的目的就是实现代码的增强。

我们首先就要有一个代理的类

代理类中有一个获取代理对象的方法,这个方法有三个参数,类的加载器,代理对象所实现的接口,回调函数,然后就在invoke方法中写我们对原代码的增强 method让目标对象执行方法(反射调用)。

public class JdkProxy {
    //获取代理对象的方法
    //增强目标对象的方法
    public static Object getProxy(AccountService accountService){
        /**
         * 使用jdk的动态代理生成代理对象
         * Proxy一个有关反射的一个类 三个参数 类的加载器 代理对象所实现的接口 回调函数 并把对象给返回
         * 通过Java反射的一个类,里面有一个生成代理对象的方法
         * 静态代理和动态代理的区别 一个是继承,一个是基于接口实现的让目标对象执行方法(反射调用),然后
         */

        Object proxy=Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
            //

            /**
             * 调用代理对象的方法,invoke方法就会执行
             * method就是对目标对象方法
             * @param proxy
             * @param method
             * @param args
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result=null;
                try{
                    //事务处理,开启事务
                    TxUtils.startTransaction();

                    //让目标对象执行方法(反射调用)
                    method.invoke(accountService,args);

                    //提交事务
                    TxUtils.commit();
                }catch (Exception e){
                    e.printStackTrace();
                    //回滚事务
                    TxUtils.rollback();
                }finally {
                    TxUtils.close();
                }

                return proxy;
            }
        });

        return null;
    }

        然后在测试方法中这么使用就可以了

@Test
    public void run(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("ApplicationContext.xml");

        AccountService accountService =(AccountService) ac.getBean("accountService");
        Account account=new Account(null,"小明",1000.0);
        Account account1=new Account(null,"小红",1000.0);
//        accountService.saveAll(account,account1);
        //增强的代理对象来执行
        Object proxyobj= JdkProxy.getProxy(accountService);
        //转型
        AccountService proxy=(AccountService) proxyobj;
        //调用逻辑
        proxy.saveAll(account,account1);
    }

        然后我们要是想看到这个方法的相互的调用的过程的话就打个断点debug走一下就明白了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2038851.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Datawhale AI夏令营第四期】魔搭-AIGC方向 Task02笔记 Scepter工具箱, 精读BaseLine代码

【Datawhale AI夏令营第四期】魔搭-AIGC方向 Task02笔记 Task02学习任务&#xff1a; https://linklearner.com/activity/14/10/32 传送门 我们继续看网课&#xff0c;并且在Kimi.AI的帮助下读一下BaseLine示例代码。 网课链接&#xff1a;https://space.bilibili.com/1069874…

如何创建一个SpringBoot项目呢?SpringBoot有什么优点呢?

目录 一、SpringBoot的优点 二、SpringBoot项目的创建 &#xff08;一&#xff09;通过Idea创建 &#xff08;二&#xff09;通过网页来创建Spring Boot项目 三、SpringBoot的目录 拓展知识 Spring的诞生是为了简化Java代码的开发&#xff0c;而Spring Boot的诞生是为了简…

剪映怎么剪辑视频?2024年剪辑软件精选!

在短视频风靡的时代&#xff0c;越来越多的人开始尝试自己制作短视频。而剪辑视频是短视频制作过程中至关重要的一环。很多小伙伴问剪映怎么剪辑视频&#xff1f;其实除了剪映&#xff0c;市面上还有不少剪辑软件操作简便&#xff0c;值得推荐&#xff01; 福昕视频剪辑 链接…

Dubbo源码深度解析(四)

接上篇博客《Dubbo源码深度解析(三)》&#xff0c;上篇博文&#xff0c;主要讲的是DubboBootstrap#start()方法中调用到的其他方法&#xff0c;以及讲到ServiceConfig#export()方法的调用链路。其中讲到最核心的方法为ServiceConfig#doExportUrlsFor1Protocol()&#xff0c;还没…

LVS详细解析及其NAT模式与DR模式部署(理论+实验全方位指导)

目录 1. 集群 2. 分布式系统 3. 集群与分布式的比较 4.通俗的解释 集群 分布式系统 总结 LVS&#xff08;Linux Virtual Server&#xff09;简介 LVS专业术语 工作原理 LVS调度算法 静态调度算法 动态调度算法 ipvsadm脑图 NAT模式集群 LVS的配置 在LVS主机内打开…

数据结构之顺序表的实现

主要参考&#xff1a; 【王道考研】王道数据结构与算法详细笔记&#xff08;全&#xff09;_王道数据结构笔记-CSDN博客 顺序表的概念 顺序表&#xff1a;用顺序存储的方式实现线性表顺序存储。把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中&#xff0c;元素之间的关系…

1Panel配置

1. 脚本安装 curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sh quick_start.sh2. 配置镜像加速 在容器-> 配置中设置&#xff0c;否则安装软件会失败。 https://docker.211678.top https://docker.1panel.live …

面对复杂订单统计,如何用这款工具化整为零?

最近制作报表时遇到了个问题&#xff0c;怎么把整合到一起的订单统计数据拆分成一个个单独的订单统计报表呢&#xff1f;经过一段时间的探索后&#xff0c;我找到了一款完全免费的报表软件&#xff0c;能够解决这个问题&#xff0c;这款软件叫作山海鲸可视化&#xff0c;我会在…

python 已知x+y=8 求x*y*(x-y)的最大值

先用导数求解 已知xy8 求xy(x-y)的最大值 令y8-x 则 f(x)x⋅(8−x)⋅(x−(8−x))x⋅(8−x)⋅(2x−8) 导数方程为 f(x)-3x^2 24x - 32 求方程 − 3 x 2 24 x − 32 0 -3x^2 24x - 32 0 −3x224x−320 的根。 首先&#xff0c;我们可以尝试对方程进行因式分解。观察…

Maven系列(一):Maven下载安装配置【Maven使Java构建事半功倍】

前言 ​ 作为Java开发工程师&#xff0c;那么Maven现已成为不可或缺的一部分&#xff0c;从最开始的依赖管理到编译运行及打包&#xff0c;可以说使伴随了Java项目的整个生命周期。那么这篇文章&#xff0c;将带你去认识Maven以及Maven的下载、安装、配置等等。 1. 什么是Mav…

RCE复现问题和研究

目录 先了解一些常见的知识点 PHP常见命令执行函数 call_user_func eval&#xff08;&#xff09; call_user_func_array array_filter 实战演练&#xff08;RCE&#xff09;PHP Eval函数参数限制在16个字符的情况下 &#xff0c;如何拿到Webshell&#xff1f; 1、长度…

【智能控制】第九,十章 一阶和二阶系统神经网络控制,输出受限系统和输入受限系统的神经网络控制(北京航天航空大学)

目录 一阶系统神经网络控制 1. 系统描述 2. 滑模控制器设计(f(x)已知) 3. 自适应神经网络控制(f(x)未知) 4. 仿真结果 二阶系统神经网络控制 1. 系统描述 2. 神经网络控制器​编辑​编辑​编辑​编辑​编辑 3. 仿真结果 输出受限系统的神经网络控制 1. 问题描述 2.…

认识一下测试策略与测试方案

目录 测试方案 测试策略 测试策略的内容主要包括 测试技术和工具 测试启动、停止和完成标准 风险分析和应对方案 测试范围 测试角色和职责 测试方法和类型 测试工具 测试层级 测试指标 测试可交付成果 测试方案的内容包括 测试目标 测试范围 测试环境 测试策略…

Linux云计算 |【第二阶段】OPERATION-DAY2

主要内容&#xff1a; 部署LNMP架构&#xff08;linux、Nginx、MariaDB、PHP&#xff09;、NginxFastCGI动静分离、Nginx地址重写 环境准备&#xff1a;还原配置文件 [rootproxy ~]# cd /usr/local/nginx/ [rootproxy nginx]# sbin/nginx [rootproxy nginx]# cp conf/nginx.c…

服务器CPU天梯图2024年8月,含EYPC/至强及E3/E5

原文地址&#xff08;高清无水印原图/持续更新/含榜单出处链接&#xff09;&#xff1a; >>>服务器CPU天梯图<<< 本文提供的服务器CPU天梯图数据均采集自各大专业网站&#xff0c;榜单图片末尾会标准其来源&#xff08;挂太多链接有概率会被ban&#xff0c;…

2024杭电多校3——1007单峰数列

补题链接 一道数据结构体&#xff0c;差分线段树&#xff0c;我从没有看见过的全新版本&#xff0c;不过据说挺常见的。线段树维护题目里询问的东西&#xff0c;是否一样&#xff0c;单调还有单峰&#xff0c;小细节挺多的。建线段树开始是从2开始的&#xff0c;因为差分的第一…

Java:实现杨辉三角

目录 思路 完整代码 思路 可以把杨辉三角看作是一个直角三角形&#xff0c;这样可以方便进行输出 以直角三角形中 i行j列 的3 为例&#xff0c;我们可以推出: [ i ] [ j ] [ i -1] [ j ] [ i - 1] [ j -1] 。 同时&#xff0c;杨辉三角也可以看作是一个二维数组&#x…

Tomcat服务器和Web项目的部署

目录 一、概述和作用 二、安装 1.进入官网 2.Download下面选择想要下载的版本 3.点击Which version查看版本所需要的JRE版本 4.返回上一页下载和电脑和操作系统匹配的Tomcat 5. 安装完成后&#xff0c;点击bin目录下的startup.bat&#xff08;linux系统下就运行startup.sh&…

Midjourney技巧-生成拟人化动物(做你的品牌形象代言人)

​ 前言/introduction 如何用Midjourney生成这种时尚潮流的动物拟人图片&#xff1f; 用来做你的品牌形象代言人也是不赖&#xff01; ​ 关键词/personify 生成这种动物拟人化的图片其实主要在关键词-拟人化personify 比如&#xff0c;一只拟人化的老鼠/An anthropomorphic…

惊!北京三害、上海四毒、广东五虎,谁才是互联网界的“拼命三郎”?

在互联网江湖中&#xff0c;流传着这样一份神秘的“工作强度排名”&#xff0c;它们以地域为划分&#xff0c;将那些被公认为加班狂魔的互联网大厂归为了“北京三害”、“上海四毒”和“广东五虎”。 这份名单一出&#xff0c;让人不禁哑然失笑&#xff0c;同时也让人深思&…