SPRING-了解4-AOP1

news2024/11/24 8:52:28

主要理念:不用继承,而是用Java Reflect类中的Proxy来实现对方法的增强

面向切面编程   oop的延续,对业务进行隔离,降低耦合度,目标是不改变原来的代码在主干中增加新功能

比如已经写好登录功能,现在需要加入权限,那么应该额外写权限模块,之后配置即可

AOP底层原理:

    底层使用动态代理,以下两者差别在于一个接口实现类代理,一个子类实现代理

    (1)有接口情况:使用JDK动态代理

           比如有UserDao 接口  以及UserDaoImpl实现类

              JDK动态代理就是UserDao 接口实现类代理对象,不通过new的方法,以增强类的方法

     (2)无接口情况:使用CGLIB动态代理

           目标是不改变以下类的add实现方法

public class User{
public void add(){
}
}

原始方法:可以创建User类的子类

新方法:创建User类的子类代理对象,不用new方法

实例

  使用JDK动态代理

     使用Proxy里面的方法创建代理对象

    查阅文档:Java 8 中文版 - 在线API中文手册 - 码工具

java.lang.reflect --》Class Proxy

 其中有方法

newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)

 newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)

注意其返回指定接口代理了的实例,静态的方法,可以调用

第一个参数:类加载器  ClassLoader loader

第二个参数:数组,增强方法所在的类,这个类实现的接口,可为多个

第三个参数:实现这个接口InvocationHandler ,创建代理对象  写增强的方法

底层原理代码实现

1)创建接口与实现类

public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}
public class UserDaoImpl implements UserDao{
    @Override
    public int add(int a, int b) {
        System.out.println("add方法执行了");
        return a+b;
    }

    @Override
    public String update(String id) {
        System.out.println("update方法执行了");
        return  id;

    }
}

1)创建JDKProxy类,名字任意

package com.i7i8i9.spring5;

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

public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces={UserDao.class};//为了给Proxy.newProxyInstance提供第二个参数
        //创建接口实现类代理对象
        UserDaoImpl userDao=new UserDaoImpl(); //这个是为了Proxy.newProxyInstance第三个参数设计的类构造方法
        UserDao dao=(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao));
        int sum=dao.add(3,5);
        System.out.println("求和"+sum);
    }
}
//创建代理对象
class UserDaoProxy implements InvocationHandler {
    //创建谁的代理对象,就要把谁传进来,多种方式
    //有参构造  Object obj可以用UserDaoImpl,但是范围会缩小
    private Object obj;
    public UserDaoProxy(Object obj){
      this.obj=obj;

    }

    //增强的逻辑
    @Override
    //invoke方法意思是对象一创建,方法就会被调用
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println("方法之前执行"+method.getName()+"传递的参数"+ Arrays.toString(args));
        //被增强方法执行
        Object res=method.invoke(obj,args);
        //方法之后
        System.out.println("方法之后,"+obj);
        return res;
    }
}

main中  UserDao dao=(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao)); 相当于 new 对象

这里面分为两个类,第一个是JDKProxy,命名任意,里面包含main方法

主要是围绕Proxy.newProxyInstance三个参数进行

针对第二个参数定义了一个集合:Class[] interfaces={UserDao.class};

针对第三个参数创建了一个类 class UserDaoProxy implements InvocationHandler

2)class UserDaoProxy

   在这个类里面第一个要求是:创建谁的代理对象,就要把谁传进来,多种方式实现

这里使用了构造方法传值,为了增强通用性,使用了Object

  private Object obj;
    public UserDaoProxy(Object obj){
      this.obj=obj;

    }

第二部分写了增强逻辑

也就是在被增强方法执行前后加入了新逻辑

注意被增强方法执行处其实可以加一个判断

根据method.getName()进行判断

 //增强的逻辑
    @Override
    //invoke方法意思是对象一创建,方法就会被调用
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println("方法之前执行"+method.getName()+"传递的参数"+ Arrays.toString(args));
        //被增强方法执行
        Object res=method.invoke(obj,args);
        //方法之后
        System.out.println("方法之后,"+obj);
        return res;
    }

3)执行过程

先看执行结果

   

      a)第一步 准备阶段:   传入三个参数

    重点是    UserDao dao=(UserDao) Proxy.newProxyInstance(三个参数)

      因为Proxy类中创建了UserDaoImpl userDao并把它传给了第三个参数,即

new UserDaoProxy(userDao) 

  第三个参数返回了

因为是object,所以在UserDao dao=(UserDao)对其进行强转

b)第二步: 调用方法

int sum=dao.add(3,5);

这个触发了被增强方法执行,并返回了Object 对象 com.i7i8i9.spring5.UserDaoImpl@31cefde0

​
方法之前执行add传递的参数[3, 5]
add方法执行了
方法之后,com.i7i8i9.spring5.UserDaoImpl@31cefde0
求和8

​
//被增强方法执行
Object res=method.invoke(obj,args);

invoke执行完毕之后,再执行:

System.out.println("求和"+sum);

另外如果main中执行的是userdaoImpl另外一个方法,那么也会被增强,也就是说这个实现类中所有的方法都被增强了,当如不想增强,可以再invoke实现类中用if+method.getName获取当前是什么方法进入执行操作

AOP术语

1.连接点:

     从上面可以看出,假设一个实现类中有4个方法, 那么这四个方法都被增强了,他们就是连接点

2.切入点:实际被真正增强的方法

3.通知(增强):实际增强的逻辑部分,就是上文中的权限判断

   通知有多种类型

1)前置通知

2)后置通知

3)环绕通知:前后都执行

4)异常通知

5)最终通知:类似try catch 中的finally,无论有没有异常都会执行

切面:是个动作,把通知应用到切入点的过程

AOP操作(准备)

1.Spring框架一般是基于AspectJ实现AOP操作

   AspectJ不是Spring组成部分,是一个单独框架,不依赖Spring

一般把AspectJ和Spring结合起来做AOP操作

  两种实现方式

   1)基于xml配置文件

   2)基于注解方式实现

2.引入AOP操作相关依赖

  1) spring-framework中的spring-aspects-5.3.9.jar

还有其他三个

2) sf.cglib.jar

      https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib/2.2.0

点击files下载,出不来的话换一个网址

Download com.springsource.net.sf.cglib-2.2.0.jar : com.springsource.net « c « Jar File Download

3)aopalliance-1.0

第一个网址比较容易查,但有时候不是最新的

aopalliance-1.0.jar下载及Maven、Gradle引入代码,pom文件及包内class -时代Java

或者

https://mvnrepository.com/artifact/aopalliance/aopalliance/1.0

4)Aspectj

https://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.9.9.1

切入点表达式

作用:知道对哪个类的哪个方法进行增强

语法结构:execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))

举例1: 对 com.i7i8i9.dao.BookDao里面的add方法进行增强

          execution(* com.i7i8i9.dao.BookDao.add(..))注意*代表public private都可以   两个点代表方法参数

举例2: 对 com.i7i8i9.dao.BookDao里面的所有方法进行增强

          execution(* com.i7i8i9.dao.BookDao.*(..))注意第二个*代表所有方法

举例3: 对 com.i7i8i9.dao包里面的所有类的所有方法进行增强

          execution(* com.i7i8i9.dao.*.*(..))注意第二个*代表所有类

编写代码

1.建立类:User

2.创建增强类:UserProxy

3.通知配置5步

  1)开启注解扫描

      比如使用src下xml

 类似bean,引入conte和aop命名空间:各插入一行xmlns   增加两个http(修改其中3处名字)

<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"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">

<!--开启扫描-->
    <context:component-scan base-package="com.i7i8i9.spring5.aopanno"></context:component-scan>

  2)使用注解创建被增强和增强类的对象创建

@Component
public class User {
    public void add(){
        System.out.println("add.......");
    }
}
@Component
public class UserProxy {
    public void before(){
        System.out.println("before----");
    }
}

3)在增强类上增加注解 @Aspect,表示要生成代理对象

@Component
@Aspect
public class UserProxy {
    public void before(){
        System.out.println("before----");
    }
}

4)在Spring配置文件中开启生成代理对象,意思就是开启这个之后就去找@Aspect,找到就生成代理对象

<?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"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--开启扫描-->
    <context:component-scan base-package="com.i7i8i9.spring5.aopanno"></context:component-scan>
    <!--开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

5)配置通知

  在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式

通知类型注解有5种Before After  AfterReturning    AfterReturning    Around

package com.i7i8i9.spring5.aopanno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class UserProxy {
//@Before是Aspect注解
    @Before(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void before(){
        System.out.println("before----");
    }
    //方法执行之后,也是finally无论有没有异常都会执行
    @After(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void after(){
        System.out.println("after----");
    }
   //在返回值之后就执行 有异常就不执行
    @AfterReturning(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void afterReturn(){
        System.out.println("afterReturn----");
    }


    @AfterThrowing(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void afterThrow(){
        System.out.println("异常通知----");
    }


    @Around(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕之前----");
        proceedingJoinPoint.proceed();//用于区分执行前后,有异常抛出Throwable
        System.out.println("环绕之后----");//有异常就不执行
    }
}

测试

public class TestUserAop {

    @Test
    public void userAopTest(){
        ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
        User user=context.getBean("user", User.class);
        user.add();
        
    }
}

没有异常结果

环绕之前----
before----
add.......
afterReturn----
after----
环绕之后----

   制造一个异常

在User中增加:int i=10/0;

package com.i7i8i9.spring5.aopanno;

import org.springframework.stereotype.Component;

@Component
public class User {
    public void add(){
        int i=10/0;
        System.out.println("add.......");
    }
}

测试结果

环绕之前----
before----
异常通知----
after----

java.lang.ArithmeticException: / by zero

相同切入点提取

新建一个方法加@Pointcut

其他方法value=该方法

@Pointcut(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")

public void pointDemo(){ }



//@Before是Aspect注解 @Before(value = "pointDemo()")
public void before(){
        System.out.println("before----");
    }

增强优先级

多个类都对一个方法增强,可以在增强类上加@Order(数字类型值),越小优先级越高

似乎没有太大效果

@Order(13)
public class PersonProxy {
    @Before(value = "execution(* com.i7i8i9.spring5.aopanno.User.add(..))")
    public void afterReturn(){
        System.out.println("另外一个增强----");
    }
}



@Order(2)
public class UserProxy {

AspectJ 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"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--创建对象-->
    <bean id="book" class="com.i7i8i9.spring5.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.i7i8i9.spring5.aopxml.BookProxy"></bean>
<!--    切入点配置-->
    <aop:config>
<!--        切入点 p1名字随便起-->
        <aop:pointcut id="p1" expression="execution(* com.i7i8i9.spring5.aopxml.Book.buy(..))"/>
<!--        配置切面-->
    <aop:aspect ref="bookProxy">
<!--        配置增强在具体方法上-->
        <aop:before method="before" pointcut-ref="p1" />
    </aop:aspect>
    </aop:config>
</beans>

全注解方式

即不用xml

创建一个配置类

 

@Configuration
@ComponentScan(basePackages = {"com.i7i8i9"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}

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

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

相关文章

【大数据技术】流数据、流计算、Spark Streaming、DStream的讲解(图文解释 超详细)

流数据和流计算 在大数据时代&#xff0c;数据可以分为静态数据和流数据&#xff0c;静态数据是指在很长一段时间内不会变化&#xff0c;一般不随运行而变化的数据。流数据是一组顺序、大量、快速、连续到达的数据序列&#xff0c;一般情况下数据流可被视为一个随时间延续而无…

脑白质WM中的血流动力学响应函数HRF的特性

血流动力学响应函数HRF 估计BOLD信号的血流动力学响应函数(HRF)对于解释和分析与事件相关的fMRI数据至关重要广义线性模型(GLM)假设BOLD信号是真实反应与HRF进行卷积而得到的 一般来说&#xff0c;白质中功能激活的检测灵敏度远低于灰质&#xff0c;并且激活区域一般较小 原因…

[附源码]Nodejs计算机毕业设计健身房管理系统设计Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

J-003 Jetson电路设计之USB设计--NANO XAVIER NX

USB设计1 简介2 框图介绍3 USB HUB设计3.1电源部分3.2 芯片引脚3.3 晶振电路4 电源控制4.4 电源开关电路设计4 调试USB电路设计1 简介 NANO & XAVIER NX提供三路USB接口&#xff0c;其中包含一路调试USB&#xff08;用于镜像下载&#xff09;和一路USB3.2接口。引脚说明: …

编码踩坑——MySQL更新存放JSON的字段、\“ 转义成 “

本篇介绍在执行MySQL线上变更时遇到的问题&#xff0c;表现为"更新JSON字段时&#xff0c;实际更新的值与SQL语句中的值不一致&#xff0c;JSON格式错误"&#xff0c;如下&#xff1b; 问题描述 处理线上问题&#xff0c;需要新插入一条记录&#xff1b;将原JSON粘贴…

Jenkins(2)— 配置webhooks触发器

1、项目配置 项目配置 >> 构建触发器 >> 选择Gitee webhook触发器 生成Gitee WebHook密码&#xff1a;46d678257c0399b105635bcb9722ea09 2、Gitee配置 Gitee项目 >> 管理 >> 配置WebHooks 注意&#xff1a; 由于我的jenkins是部署在本地的&#xff…

element-ui+js+vue——实现图片的放大缩小拖动等功能——技能提升

elementuijsvue——实现图片组件的封装 1. 实现图片的放大缩小 2. 实现图片的拖动功能 3. 实现图片的预览最近同事在写一个关于 图片放大缩小拖动的功能&#xff0c;其实不止是图片&#xff0c;只要是在以下组件中的内容&#xff0c;都是可以支持放大缩小拖动的。 功能&#x…

Web前端项目的跨平台桌面客户端打包方案之——CEF框架

文章目录1、什么是CEF2、测测你电脑里的CEF3、从0开始的CEF入门实例1、什么是CEF CEF是什么 概念 Chromium Embedded Framework (CEF)是个基于Google Chromium项目的开源Web browser控件。 CEF的目标是为第三方应用提供可嵌入浏览器支持。CEF隔离底层Chromium和Blink的复杂代码…

靠着数套的Java刷题PDF,成功“混进”腾讯T3

昨晚半夜&#xff0c;一个学Java的老弟突然打电话告诉我他拿到了腾讯T3的offer&#xff0c;说实话&#xff0c;我有点诧异。。。 这老弟也是渣硕一枚&#xff0c;去投岗了腾讯的Java后台开发岗位。等了一个星期后简历被放回池子里了&#xff0c;眼看着提前批马上就要截止了&am…

jsp+ssm计算机毕业设计多媒体课程精品课程网站【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

海量数据小内存!从未出现过的数在哪里

文章目录题目要求1&#xff09;内存 1G2&#xff09;内存 3 KB3&#xff09;内存 有限变量举例题目 现在有 40 亿个无符号整数&#xff0c;无符号整数的范围是 0 ~ 232-1&#xff08;42亿&#xff09;&#xff0c;哪怕 40 亿个数完全不同&#xff0c;在该范围中也总有没有出现…

傻白探索Chiplet,关于EPYC Zen2 的一些理解记录(五)

目录 一、知识铺垫 &#xff08;1&#xff09;Chiplet &#xff08;2&#xff09;Zen架构 &#xff08;3&#xff09;EPYC和Ryzen &#xff08;4&#xff09;EPYC Zen2 二、关于EPYC Zen2里的部件 &#xff08;1&#xff09;内存控制器 &#xff08;2&#xff…

Python本地下载-实例的SQL审计日志

简介&#xff1a;使用阿里云的RDS数据库&#xff0c;开启DAS的数据库治理服务。会产生大量的审计日志。 我们有2T的审计日志数据&#xff0c;保留180天&#xff0c;每小时收费空间&#xff1a;0.008元/GB/小时 计算下来&#xff1a;2x1024x 24x 30 x 0.008 11796 元 解决&…

分类预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络分类预测(语音分类)

分类预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络分类预测(语音分类) 目录 分类预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络分类预测(语音分类)基本描述模型结构设计过程参考资料基本描述 传统的语音识别技术,主要在隐马尔可夫模型和高斯混合模型两大”神器“的加持…

[element plus] 分页组件使用 - vue

学习关键语句: 饿了么加组件分页组件 element分页组件 vue3 饿了么分页组件 写在前面 必须要说明的是 , 这篇文章使用的分页组件样式包含了饿了么官方给出警告以后会弃用的部分 但是问题是什么呢? 问题就是我还没找到这个用 vmodel 绑定的使用方法 , 再加上现在也算是有点…

C++代码简化之道

1. 善用emplace C11开始STL容器出现了emplace&#xff08;置入&#xff09;的语义。比如 vector、map、unordered_map&#xff0c;甚至 stack和 queue都有。 emplace方便之处在于&#xff0c;可以用函数参数自动构造对象&#xff0c;而不是向vector的push_back&#xff0c;ma…

论文投稿指南——中文核心期刊推荐(电工技术2)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

机器人开发--霍尔元件

机器人开发--霍尔元件1 霍尔效应2 霍尔元件特点3 霍尔传感器的典型应用电流传感器位移测量测转速或转数参考1 霍尔效应 霍尔效应是电磁效应的一种&#xff0c;这一现象是美国物理学家霍尔于1879年在研究金属的导电机制时发现的。当电流垂直于外磁场通过半导体时&#xff0c;载…

ORB-SLAM2 --- Tracking::SearchLocalPoints函数解析

1.函数作用 用局部地图点进行投影匹配&#xff0c;得到更多的匹配关系。 局部地图点中已经是当前帧地图点的不需要再投影&#xff0c;只需要将此外的并且在视野范围内的点和当前帧进行投影匹配。 2.函数流程 Step 1&#xff1a;遍历当前帧的地图点&#xff0c;标记这些地图点不…

Mycat(6):mycat简单配置

1 找到conf/schema.xml并备份 2 配置虚拟表table[在schema里面] 其中 sharding-by-intfile 为rule.xml中的规则 规则文件为conf文件夹中的partition-hash-int.txt 3 配置数据节点dataNode 现在数据库新建3个数据库&#xff0c;skywalking&#xff0c;skywalking1&#xff0c;s…