Spring见解3 AOP

4.Spring AOP

4.1.为什么要学习AOP?

  • 案例:有一个接口Service有一个insert方法,在insert被调用时打印调用前的毫秒数与调用后的毫秒数,其实现为:

    public class UserServiceImpl implements UserService {
    
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void addUser(){
            System.out.println("方法开始时间:"+new Date());
            userDao.addUser();
            System.out.println("方法结束时间:"+new Date());
        }
    }
    
  • 问题:输出日志的逻辑还是无法复用

4.2.AOP概述

AOP:全称是Aspect Oriented Programming即:面向切面编程。

简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对程序进行增强:权限校验,日志记录,性能监控,事务控制.

4.3.代理(Proxy)模式

4.3.1.创建工程

4.3.2.代理(Proxy)模式介绍

  • 作用:通过代理可以控制访问某个对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。(即: AOP的微观实现!)

  • 在这里插入图片描述

  • 核心角色

    • 抽象角色(接口):定义公共对外方法

    • 真实角色(周杰伦):实现抽象角色,定义真实角色所要实现的业务逻辑

    • 代理角色(代理人):实现抽象角色,是真实角色的代理,通过调用真实角色的方法来完成业务逻辑,并可以附加自己的操作

4.3.3.静态代理

4.3.3.1.抽象角色
package com.by.proxy.StaticProxy;

public interface Star {
	/**
	 * 面谈
	 */
	void confer();
	/**
	 * 签合同
	 */
	void signContract();
	/**
	 * 订票
	 */
	void bookTicket();
	/**
	 * 唱歌
	 */
	void sing();
	/**
	 * 收钱
	 */
	void collectMoney();
}
4.3.3.2.真正角色(周杰伦)
package com.by.proxy.StaticProxy;

public class RealStar implements Star {

	public void bookTicket() {

	}

	public void collectMoney() {

	}

	public void confer() {

	}

	public void signContract() {

	}

	public void sing() {
		System.out.println("RealStar(周杰伦本人).sing()");
	}
}
4.3.3.3.代理角色(经纪人)
package com.by.proxy.StaticProxy;

public class ProxyStar implements Star {
	
	private Star star;
	
	public ProxyStar(Star star) {
		super();
		this.star = star;
	}

	public void bookTicket() {
		System.out.println("ProxyStar.bookTicket()");
	}

	public void collectMoney() {
		System.out.println("ProxyStar.collectMoney()");
	}

	public void confer() {
		System.out.println("ProxyStar.confer()");
	}

	public void signContract() {
		System.out.println("ProxyStar.signContract()");
	}

	public void sing() {
		star.sing();
	}
}
4.3.3.4.测试
package com.by.proxy.StaticProxy;

public class Client {
	public static void main(String[] args) {
		Star proxy = new ProxyStar(new RealStar());
		
		proxy.confer();
		proxy.signContract();
		proxy.bookTicket();
		proxy.sing();
		
		proxy.collectMoney();
		
	}
}
4.3.3.5.静态代理的缺点
  1. 代理类和实现类实现了相同的接口,这样就出现了大量的代码重复。
  2. 代理对象只服务于一种类型的对象。如果要服务多类型的对象,例如代码是只为UserService类的访问提供了代理,但是还要为其他类如DeptService类提供代理的话,就需要我们再次添加代理DeptService的代理类。

4.3.4.jdk动态代理

4.3.4.1.抽象角色
public interface Star {
    /**
     * 唱歌
     */
    void sing();
}
4.3.4.2.真正角色(周杰伦)
package com.by.JdkProxy;

//真实角色(周杰伦)
public class RealStar implements Star {
    //优点:此时代码不再重复

    public void sing() {
        System.out.println("周杰伦:快使用双截棍,哼哼哈嘿....");

    }
}

4.3.4.3.代理角色(经纪人)
package com.by.JdkProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//代理类工厂
public class ProxyFactory {

    //优点:此时可以代理任意类型的对象
    //真实角色(周杰伦)
    private Object realObj;

    public ProxyFactory(Object realObj) {
        this.realObj = realObj;
    }

    //获得代理对象
    public Object getProxyObject(){

        /**
         * Proxy:作用创建代理对象
         *      ClassLoader loader:类加载器
         *      Class<?>[] interfaces:真实角色实现的接口,根据接口生成代理类
         *      InvocationHandler h:增强的逻辑,即如何代理(宋吉吉要做的事)
         */
        return Proxy.newProxyInstance(
                realObj.getClass().getClassLoader(),
                realObj.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     *
                     * @param proxy:代理类,一般不用
                     * @param method:要调用的方法
                     * @param args:调用方法时的参数
                     * @return
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args)
                        											throws Throwable {

                        System.out.println("真正的方法执行前!");
                        System.out.println("面谈,签合同,预付款,订机票");

                        Object result = method.invoke(realObj, args);

                        System.out.println("真正的方法执行后!");
                        System.out.println("收尾款");

                        return result;
                    }
                }
        );
    }
}

4.3.4.4.测试
public class Client {
    public static void main(String[] args) {
        //获得代理对象
        Star proxyObject = (Star) new ProxyFactory(new RealStar()).getProxyObject();
        System.out.println(proxyObject.getClass());//class com.sun.proxy.$Proxy0
        proxyObject.sing();
    }
}

4.3.5.Cglib动态代理

cglib与动态代理最大的区别就是:

  • 使用jdk动态代理的对象必须实现一个接口
  • 使用cglib代理的对象则无需实现接口

CGLIB是第三方提供的包,所以需要引入jar包的坐标:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。

4.3.5.1.真正角色
package com.by.proxy.CglibProxy;

public class RealStar{

	public void sing() {
		System.out.println("RealStar(周杰伦本人).sing()");
	}
}
4.3.5.2.代理角色(经纪人)
package com.by.proxy.CglibProxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//代理工厂
public class ProxyFactory implements MethodInterceptor {

    //真实角色
    private Object realObj;

    public ProxyFactory(Object realObj) {
        this.realObj = realObj;
    }

    /**'
     * 获得子类代理对象
     * @return
     */
    public Object getProxyObject() {
        //工具类
        Enhancer en = new Enhancer();
        //设置父类
        en.setSuperclass(realObj.getClass());
        //设置回调函数
        en.setCallback(this);
        //创建子类代理对象
        return en.create();
    }
    /*
        在子类中调用父类的方法
            intercept方法参数说明:
                obj : 代理对象
                method : 真实对象中的方法的Method实例
                args : 实际参数
                methodProxy :代理对象中的方法的method实例
     */
    public Object intercept(Object obj, Method method, Object[] args, 
                            	MethodProxy methodProxy)throws Throwable {
        System.out.println("真正的方法执行前!");
        System.out.println("面谈,签合同,预付款,订机票");

        Object result = method.invoke(realObj, args);

        System.out.println("真正的方法执行后!");
        System.out.println("收尾款");
        return object;
    }
}
4.3.5.3.测试
package com.by.proxy.CglibProxy;

//测试类
public class Client {
    public static void main(String[] args) {
        //获取代理对象
        RealStar proxyObject = 
            (RealStar) new ProxyFactory(new RealStar()).getProxyObject();
        proxyObject.sing();
    }
}

4.4.AOP相关术语

  1. 连接点(joinpoint)

    被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法。

  2. 切入点(pointcut)

    切入点是指我们要对哪些连接点进行拦截的定义

  3. 通知(advice)

    所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

  4. 切面(aspect)

    是切入点和通知的结合

  5. 引介(introduction)

    是一种特殊的通知,在不修改代码的前提下,引介可以在运行期为类动态地添加一些方法或字段

  6. 目标对象(Target)

    要代理的目标对象(要增强的类)

  7. 织入(weave)

    将增强应用到目标的过程将advice应用到target的过程

  8. 代理(Proxy)

    一个类被AOP织入增强之后,就产生一个代理类

在这里插入图片描述

4.5.Spring的AOP配置

4.5.1.创建工程

在这里插入图片描述

4.5.1.1.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.by</groupId>
    <artifactId>Spring_AOP_Xml</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- Spring常用依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
         <!--支持切点表达式 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
    </dependencies>
</project>
4.5.1.2.dao
/**
 * 持久层实现类
 */
public class UserDaoImpl implements UserDao {

    @Override
    public void addUser(){
        System.out.println("insert into tb_user......");
    }
}
4.5.1.3.service
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void addUser(){
        userDao.addUser();
    }
}
4.5.1.4.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">
    
    <bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.by.service.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>
4.5.1.5.web
/**
 * 模拟表现层
 */
public class Client {
    public static void main(String[] args) {
        ApplicationContext ac = 
            new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用对象
        UserService userService = ac.getBean("userService",UserService.class);
        System.out.println(userService.getClass());
        userService.addUser();
    }
}

4.5.2.增强

  1. 创建增强类

    package com.by.advice;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    import java.util.Date;
    
    public class MyLogAdvice {
    
        //前置通知
        public void before(){
            System.out.println("前置通知");
        }
    
        //后置通知【try】
        public void afterReturning(){
            System.out.println("后置通知");
        }
    
        //异常通知【catch】
        public void afterThrowing(){
            System.out.println("异常通知");
        }
    
        //最终通知【finally】
        public void after(){
            System.out.println("最终通知");
        }
    
        //环绕通知
        public void around(ProceedingJoinPoint joinPoint){
            try {
                System.out.println("方法执行前的环绕通知");
                joinPoint.proceed();
                System.out.println("方法执行后的环绕通知");
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }
    
  2. 配置增强类

    <!--增强-->
    <bean id="myLogger" class="com.by.advice.MyLogger"></bean>
    

4.5.3.切点

  1. 切点表达式

    表达式语法:

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

    例如:

    execution(* com.by.service.UserService.add(..))

    execution(* com.by.service.UserService.*(..))

    execution(* com.by.service.*.*(..))

  2. 配置切点

    <aop:config>
        <!--切点-->
        <aop:pointcut id="pointcut" expression="execution(* com.by.service.*.*(..))"/>
    </aop:config>
    

4.5.4.切面

  1. 增强的类型

    • aop:before:用于配置前置通知
    • aop:after-returning:用于配置后置【try】通知,它和异常通知只能有一个执行
    • aop:after-throwing:用于配置异常【catch】通知,它和后置通知只能执行一个
    • aop:after:用于配置最终【finally】通知
    • aop:around:用于配置环绕通知
  2. 配置切面

    <!--切面-->
    <aop:aspect ref="myLogger">
        <!-- 用于配置前置通知:指定增强的方法在切入点方法之前执行 
    		method:用于指定通知类中的增强方法名称
    		ponitcut-ref:用于指定切入点
    	-->
    	<aop:before method="before" pointcut-ref="pointcut"/>
        <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/>
        <aop:after method="after" pointcut-ref="pointcut"/>
        <aop:around method="around" pointcut-ref="pointcut"/>
    </aop:aspect>
    

4.5.5.测试

  1. 测试service实现接口时的类型

  2. 测试service不实现接口时的类型

    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

书生·浦语大模型全链路开源开放体系

书生浦语大模型全链路开源体系_哔哩哔哩_bilibili 大模型全链路开源开放体系等你来探索~ https://github.com/internLM/tutorial 书生浦语全链条开源开放体系 1&#xff09;数据: 书生万卷 2TB数据&#xff0c;并行训练&#xff0c;极致优化涵盖多种模态与任务 预训练: I…

vmware虚拟机安装esxi7.0步骤

一、安装准备 1、下载镜像文件 下载链接&#xff1a;https://pan.baidu.com/s/12XmWBCI1zgbpN4lewqYw6g 提取码&#xff1a;mdtx 2、vmware新建一个虚拟机 2.1 选择自定义 2.2 选择ESXi对应版本 2.3 选择稍后安装操作系统 2.4 默认选择 2.5 自定义虚拟机名称及存储位置 2…

android 分享文件

1.在AndroidManifest.xml 中配置 FileProvider <providerandroid:name"android.support.v4.content.FileProvider"android:authorities"com.example.caliv.ffyy.fileProvider"android:exported"false"android:grantUriPermissions"true…

将Django项目从本地上传至宝塔服务器(踩坑记录)

文章目录 写在前面配置本地文件配置宝塔面板解决遇到问题展示运行结果热门文章 自我介绍 ⭐2022年度CSDN 社区之星 Top6 ⭐2023年度CSDN 博客之星 Top16 ⭐2023年度CSDN 城市之星 Top2&#xff08;苏州&#xff09; ⭐CSDN Python领域 优质创作者 ⭐CSDN 内容合伙人 推荐热门…

spring-boot-maven插件repackage(goal)的那些事

前言&#xff1a;在打包Springboot项目成jar包时需要在pom.xml使用spring-boot-maven-plugin来增加Maven功能&#xff0c;在我的上一篇博客<<Maven生命周期和插件的那些事&#xff08;2021版&#xff09;>>中已经介绍过Maven和插件的关系&#xff0c;在此不再赘述&…

基于哈里斯鹰算法优化的Elman神经网络数据预测 - 附代码

基于哈里斯鹰算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于哈里斯鹰算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于哈里斯鹰优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…

凝聚层次聚类及DBscan算法详解与Python实例

凝聚层次聚类及DBscan算法详解与Python实例 凝聚层次聚类DBscan算法实例演示 在本篇博客中&#xff0c;我们将深入探讨凝聚层次聚类&#xff08;Agglomerative Hierarchical Clustering&#xff09;和DBscan算法&#xff0c;并通过Python实例演示它们的应用。这两种算法都属于聚…

12 位多通道,支持 MPU 存储保护功能,应用于工业控制,智能家居等产品中的国产芯片ACM32F403/F433

ACM32F403/F433 芯片的内核基于 ARMv8-M 架构&#xff0c;支持 Cortex-M33 和 Cortex-M4F 指令集。芯片内核 支持一整套DSP指令用于数字信号处理&#xff0c;支持单精度FPU处理浮点数据&#xff0c;同时还支持Memory Protection Unit &#xff08;MPU&#xff09;用于提升应用的…

Vue中break关键字

Change() {//每次触发该事件&#xff0c;都要讲data重新赋值一次this.data JSON.parse(JSON.stringify(this.data1));// 根据选中的等级更新数据switch (this.selectedlevel) {case 1:// 更新数据为一级数据this.data this.data.filter(item > item.level "1"…

VINS-MONO拓展2----更快地makeHessian矩阵

1. 目标 完成大作业T2 作业提示&#xff1a; 多线程方法主要包括以下几种(参考博客)&#xff1a; MPI(多主机多线程开发),OpenMP(为单主机多线程开发而设计)SSE(主要增强CPU浮点运算的能力)CUDAStream processing, 之前已经了解过std::thread和pthread&#xff0c;拓展1…

Unity 打包AB 场景烘培信息丢失

场景打包成 AB 资源的时候&#xff0c;Unity 不会打包一些自带相关的资源 解决办法&#xff1a;在 Project settings > Graphics下设置&#xff08;Automatic 修改成 Custom&#xff09;

WPF 入门教程DispatcherTimer计时器

https://www.zhihu.com/tardis/bd/art/430630047?source_id1001 在 WinForms 中&#xff0c;有一个名为 Timer 的控件&#xff0c;它可以在给定的时间间隔内重复执行一个操作。WPF 也有这种可能性&#xff0c;但我们有DispatcherTimer控件&#xff0c;而不是不可见的控件。它几…

【响应式编程-03】Lambda表达式底层实现原理

一、简要描述 Lambda的底层实现原理Lambda表达式编译和运行过程 二、Lambda的底层实现原理 Lambda表达式的本质 函数式接口的匿名子类的匿名对象 反编译&#xff1a;cfr-0.145.jar 反编译&#xff1a;LambdaMetafactory.metafactory() 跟踪调试&#xff0c;转储Lambda类&#x…

产品分析 | 数据资产目录竞品分析

一、分析背景和目的 分析市场上主流的包含数据资产目录的产品&#xff0c;重新整理一篇竞品分析以供参考和学习。 二、版本信息 三、名词解释 四、需求背景 1. 产品现状 建设了数据资产目录&#xff0c;但是偏技术向&#xff0c;比较难用&#xff0c;细节流程上欠考虑。元数…

Typescript 中的namespace

命名空间&#xff1a; 类似 vuex 的 namespace 相当于一个容器。 namespace 是一种将相关代码组织在一起的方式&#xff0c;中文译为“命名空间”。 它出现在 ES 模块诞生之前&#xff0c;作为 TypeScript 自己的模块格式而发明的。但是&#xff0c;自从有了 ES 模块&#x…

护眼台灯哪个牌子好?2024年专业护眼台灯品牌排行榜!

近些年来&#xff0c;护眼台灯作为视力健康照明工具愈发受到欢迎&#xff0c;越来越多的人使用护眼台灯取代传统白炽灯&#xff0c;做护眼台灯的产品也是层出不穷。 不过&#xff0c;也有很多人对护眼台灯的效果保持怀疑的台灯&#xff0c;一是对护眼效果的疑问&#xff0c;二…

Springcloud alibab和dubbo有什么区别?

Spring Cloud Alibaba 和 Dubbo 都是为了简化企业级应用开发而生的框架&#xff0c;尤其是在分布式系统和微服务架构的背景下。 虽然他们在某些功能上有重叠&#xff0c;但各有侧重点和使用场景。 微服务架构图 首先介绍一下 Spring Cloud Alibaba&#xff1a; Spring Cloud …

Java 开发环境搭建

什么是 JDK 和 JRE&#xff1f; JDK &#xff08;Java Development Kit&#xff09;&#xff1a;是 Java 程序开发工具包&#xff0c;包含 JRE 和开发人员使用的工具JRE&#xff08;Java Runtime Environment&#xff09;&#xff1a;是 Java 程序的运行时环境&#xff0c;包含…

LLM 中的长文本问题

近期,随着大模型技术的发展,长文本问题逐渐成为热门且关键的问题,不妨简单梳理一下近期出现的典型的长文本模型: 10 月上旬,Moonshot AI 的 Kimi Chat 问世,这是首个支持 20 万汉字输入的智能助手产品; 10 月下旬,百川智能发布 Baichuan2-192K 长窗口大模型,相当于一次…

软件测试——自动化测试框架有哪些?

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;软件测试面试题分享&#xff1a; 1000道软件测试面试题及答案&#x1f4e2;软件测试实战项目分享&#xff1a; 纯接口项目-完…