Spring学习 Spring AOP

news2024/11/25 12:58:41

4.Spring AOP

4.1.为什么要学习AOP?

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

    @Service
    public class UserServiceImpl implements UserService {
    	
        @Autowired
        private 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 setUserDao(UserDao userDao){
        this.userDao=userDao;
    }
	
    @Override
    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不实现接口时的类型

    在这里插入图片描述

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

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

相关文章

环信IM Demo登录方式如何修改为自己项目的?

在环信即时通讯云IM 官网下载Demo&#xff0c;本地运行只有手机验证码的方式登录&#xff1f;怎么更改为自己项目的Appkey和用户去进行登录呢&#xff1f; &#x1f447;&#x1f447;&#x1f447;本文以Web端为例&#xff0c;教大家如何更改代码来实现 1、 VUE2 Demo vue2…

2023十大最具商业影响力量子公司 | 光子盒年度系列

量子技术以其广泛的应用范围和对多个领域的深远影响&#xff0c;是当之无愧的“通用底座”技术&#xff0c;其潜在的产业变革力正在展现&#xff0c;尽管当前量子技术与人工智能或虚拟现实等技术领域相比&#xff0c;量子对大多数人来说还有些“看不透”。 2023年&#xff0c;量…

C#实现个人账本管理系统

git地址&#xff1a;https://gitee.com/myshort-term/personal-ledger-management-system 1.系统简介 LedgerManagementSystem是一个小型的个人账本管理系统&#xff0c;可对收支项目进行增加、删除、修改、查询以及导入和导出。可对每日的各类收支项目进行汇总并查看和修改收…

作业--day42

界面设计 MyProWin::MyProWin(QWidget *parent): QMainWindow(parent) {/**********窗口主体**********///窗口大小this->setFixedSize(644, 493);this->setWindowTitle("QQ");this->setWindowIcon(QIcon("C:/Users/10988/Downloads/pictrue/pictrue/…

计算机毕业设计 | SpringBoot+vue移动端音乐网站 音乐播放器(附源码)

1&#xff0c;项目背景 随着计算机技术的发展&#xff0c;网络技术对我们生活和工作显得越来越重要&#xff0c;特别是现在信息高度发达的今天&#xff0c;人们对最新信息的需求和发布迫切的需要及时性。为了满足不同人们对网络需求&#xff0c;各种特色&#xff0c;各种主题的…

函数战争(栈帧)之创建与销毁(c语言)(vs2022)

首先&#xff0c;什么是函数栈帧&#xff1f; C语言中&#xff0c;每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。栈帧也叫过程活动记录&#xff0c;是编译器用来实现过程函数调用的一种数据结构。 以问答的方式解释编译器与解释器-CSDN博客htt…

【C++入门】函数模板类模板(泛型编程)

目录 前言 1. 泛型编程 2. 函数模板 2.1 概念 2.2 语法格式 2.3 原理 2.4 函数模板的实例化 隐式实例化 显示实例化 2.5 思考 2.6 模板参数的匹配原则 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 总结 前言 函数模板和类模板是C中的两种重要的模板形式&#xff0c;…

day11 有效的括号 删除字符串中的所有相邻重复项 逆波兰表达式求值

题目1&#xff1a;20 有效的括号 题目链接&#xff1a;20 有效的括号 题意 判断字符串是否有效&#xff0c;若有效&#xff1a; 1&#xff09;左括号必须用相应的右括号 2&#xff09;左括号的闭合顺序正确 ({)}顺序不正确&#xff0c;应该是&#xff08;{}&#xff09; …

深度解析Nginx负载均衡算法及配置实例

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

Python3从零基础到入门(1)

目录 一、环境搭建 1.检测Python环境 2.下载安装Python环境 3.VSCode中配置Python环境 二、第一个程序 1.编码 2.输出 3.标识符 4.import 5.保留字 6.注释 7.缩进 三、变量和赋值 1.Python 中的变量 2.变量的赋值 3.多个变量赋值 四、基础数据类型 1.类型查看…

智能音箱喇叭杂音问题

智能音箱喇叭杂音问题 智能音箱生厂或出货过程会遇到多种喇叭播放有杂音的问题&#xff0e; 螺丝不匹配 智能音箱设备在生产过程&#xff0c;会有SPL测试喇叭失真&#xff0c;发现不良率8%的杂音问题&#xff0e; 分析原因是来料导入了新螺丝&#xff0c; 使用过程进入异物…

harmonyOS 时间选择组件(TimePicker)

本文 我们来说 TimePicker 时间组件 首先 我们搭一个最基本的组件骨架 Entry Component struct Index {build() {Row() {Column() {}.width(100%)}.height(100%)} }然后 在 Column 组件内 放一个 TimePicker进去 这里 我们就可以看到 一个时间的选择器了 DatePicker 捕获当前…

EPQ艾森克人格测试 49题(免费版)

艾森克人格提出人格的三个基本因素&#xff1a;性格内外向E、神经质N&#xff08;也叫情绪性&#xff09;和精神质P。这三个维度的不同程度组合&#xff0c;形成了独立的个体人格特征。 其中性格内外向维度是目前评估性格内向和外向的成熟量表&#xff0c;神经质和精神质为人格…

机器人技能学习-robosuite-0-入门介绍

文章目录 前言模块介绍实战案例1&#xff1a;从 demo 中创建自己的 env案例2&#xff1a;更换属于自己的物体 前言 资料太少、资料太少、资料太少&#xff0c;重要的事说三边&#xff0c;想根据自己实际场景自定义下机器人&#xff0c;结果发现无路可走&#xff0c;鉴于缺少参…

Maven初学Day1

1.Maven是什么&#xff1f; 是一个构建工具&#xff0c;可以自动化构建过程、任务。是一个项目模型 2.作为构建工具有什么特点 1.跨平台&#xff0c;可以在多个操作系统上使用 2.对外提供一致的操作接口 3.Maven官网 https://maven.apache.org/download.cgi 4.安装步骤 …

卷积神经网络|迁移学习-猫狗分类完整代码实现

还记得这篇文章吗&#xff1f;迁移学习|代码实现 在这篇文章中&#xff0c;我们知道了在构建模型时&#xff0c;可以借助一些非常有名的模型&#xff0c;这些模型在ImageNet数据集上早已经得到了检验。 同时torchvision模块也提供了预训练好的模型。我们只需稍作修改&#xf…

外延炉及其相关的小知识

外延炉是一种用于生产半导体材料的设备&#xff0c;其工作原理是在高温高压环境下将半导体材料沉积在衬底上。 硅外延生长&#xff0c;是在具有一定晶向的硅单晶衬底上&#xff0c;生长一层具有和衬底相同晶向的电阻率且厚度不同的晶格结构完整性好的晶体。 外延生长的特点&…

Linux实验——页面置换算法模拟

页面置换算法模拟 【实验目的】 &#xff08;1&#xff09;理解虚拟内存管理的原理和技术。 &#xff08;2&#xff09;掌握请求分页存储管理的思想。 &#xff08;3&#xff09;理解常用页面置换算法的思想。 【实验原理/实验基础知识】 存储器是计算机系统的重要资源之…

腾讯面试总结

腾讯 一面 mysql索引结构&#xff1f;redis持久化策略&#xff1f;zookeeper节点类型说一下&#xff1b;zookeeper选举机制&#xff1f;zookeeper主节点故障&#xff0c;如何重新选举&#xff1f;syn机制&#xff1f;线程池的核心参数&#xff1b;threadlocal的实现&#xff…

揭开JavaScript数据类型的神秘面纱

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ ​ ✨ 前言 JavaScript作为一门动态类型语言,其数据类型一直是开发者们关注的话题。本文将深入探讨Jav…