Spring框架04(Spring框架中AOP)

news2024/11/26 16:45:57

一、spring中bean的生命周期

1.singleton 容器启动的时候创建对象,容器正常关闭时销毁对象

2.prototype 获取对象的时候创建对象,spring容器不负责对象的销毁

生命周期的过程:

1.调用无参创建对象

2.调用set方法初始化属性

3.调用初始化方法

4.对象创建完成,使用对象

5.关闭容器,调用销毁的方法

二、Spring Bean作用域

在配置文件中,除了可以定义 Bean 的属性值和相互之间的依赖关系,还可以声明 Bean 的作用域。例如,如果每次获取 Bean 时,都需要一个 Bean 实例,那么应该将 Bean 的 scope 属性定义为 prototype,如果 Spring 需要每次都返回一个相同的 Bean 实例,则应将 Bean 的 scope 属性定义为 singleton。

作用域的种类

Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。Spring 5 支持以下 6 种作用域。

1)singleton

默认值,单例模式,表示在 Spring 容器中只有一个 Bean 实例,Bean 以单例的方式存在。

<bean id="..." class="..." scope="singleton"/>

2)prototype

原型模式,表示每次通过 Spring 容器获取 Bean 时,容器都会创建一个 Bean 实例。

<bean id="..." class="..." scope="prototype"/>

3)request

每次 HTTP 请求,容器都会创建一个 Bean 实例。该作用域只在当前 HTTP Request 内有效。

4)session

同一个 HTTP Session 共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域仅在当前 HTTP Session 内有效。

5)application

同一个 Web 应用共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。

类似于 singleton,不同的是,singleton 表示每个 IoC 容器中仅有一个 Bean 实例,而同一个 Web 应用中可能会有多个 IoC 容器,但一个 Web 应用只会有一个 ServletContext,也可以说 application 才是 Web 应用中货真价实的单例模式。

6)websocket

websocket 的作用域是 WebSocket ,即在整个 WebSocket 中有效

三、spring注解开发

1.开启注解的包扫描

<!--开启注解的包扫描-->
<context:component-scan base-package="cn.kgc.spring"/>

 2.常用的注解

@Repository  dao层
@Component   组件通用
@Service     service层
@Controller  控制层

四个注解的功能是一样的,只是使用不同的注解可以看出层次结构

3.作用域注解

@scope("singleton")
@scope("prototype")

四、Spring框架中AOP

1.代理模式讲解

作用:

通过代理类为原始类增加额外的功能

代理分类:

1.1静态代理

静态代理优缺点

1)优点 :在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展 2)缺点 :因为代理对象需要与目标对象实现一样的接口,所以会很多代理类 ,一旦接口增加方法,目标对象与代理对象都要维护

开发代理对象的原则:

1.代理对象和目标对象实现相同的接口

2.代理对象依赖于目标对象

1.2动态代理(反射)

程序运行的过程中,通过jdk提供代理技术动态的为某个类产生动态代理对象的过程

开发代理对象的原则:

1)代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用JDK动态代理。 2)代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。 3)动态代理也叫做 :JDK代理、接口代理

@Test
    public void test4() { //jdk的动态代理

        // 被代理对象
        UserServiceImpl userService = new UserServiceImpl();
        InvocationHandler handler = new InvocationHandler() {
            /**
             * @param proxy  生成的代理对象  可忽略
             * @param method  原始对象中的方法
             * @param args     原始对象中的参数
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("------log---------");
                Object invoke = method.invoke(userService, args);
                return invoke;
            }
        };

        /**
         *参数1:类加载器,动态代理对象没有类加载器 需要借助别的类的加载器实现类的加载
         * 类加载器的作用:
         *  1.加载class文件到JVM虚拟机
         *  2.创建类的class对象,进而创建类的实例化对象
         *
         * 参数2:原始类实现的所有接口  基于jdk的动态代理要求代理对象和原始对象要实现相同的接口
         *
         * 参数3:代理对象额外增加的功能  InvocationHandler
         *
         */
  UserService o =(UserService) Proxy.newProxyInstance(TestStaticProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
        o.login();
        o.register();

    }

运行结果:

基于CGLib的动态代理

开发代理对象的原则:

1.代理对象无需和原始类对象实现相同的接口

2.代理对象和原始类对象要存在父子类关系

实现步骤

@Test
    public void test5(){ //基于cglib的动态代理

        UserServiceImpl userService = new UserServiceImpl();

        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(this.getClass().getClassLoader());
        enhancer.setSuperclass(userService.getClass());
        //设置额外的功能
        MethodInterceptor callback = new MethodInterceptor() {
            //等价于  jdk代理中 invocationHandler 中的 invoke 方法
            /**
             * @param o          代理对象
             * @param method    原始对象中的方法
             * @param objects   原始对象中的参数
             * @param methodProxy  代理方法
             * @return    原始方法的返回值
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("---------cglib log------------");
                return  method.invoke(userService, objects);
            }
        };
        enhancer.setCallback(callback);

        // 创建代理对象
        UserService usrService =(UserService) enhancer.create();
        usrService.login();
        usrService.register();
    }

 运行结果

 2.AOP概述

AOP为Aspect Oriented Programming的缩写,是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型

AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口

AOP 实现分类

AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能,按照 AOP 框架修改源代码的时机,可以将其分为两类:

  • 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。

  • 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP

AOP 术语

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等....

  • 切面(ASPECT) :横切关注点被模块化的特殊对象。即,它是一个类

  • 通知(Advice) :切面必须要完成的工作。即,它是类中的一个方法

    前置通知 后置通知 环绕通知 异常通知 最终通知

  • 目标(Target) ;被通知对象

  • 代理(Proxy) :向目标对象应用通知之后创建的对象

  • 切入点(PointCut) :切面通知执行的“地点"的定义

  • 连接点JointPoint) :与切入点匹配的执行点  

快速使用

1.基于接口形式实现

使用Spring aop接口方式实现aop, 可以通过自定义通知来供Spring AOP识别对应实现的接口是:

  1. 前置通知: MethodBeforeAdvice

  2. 返回通知:AfterReturningAdvice

  3. 异常通知:ThrowsAdvice

  4. 环绕通知:MethodInterceptor

通知的类型

Spring 方面可以使用下面提到的五种通知工作:

通知描述
前置通知在一个方法执行之前,执行通知。
后置通知在一个方法执行之后,不考虑其结果,执行通知。
返回后通知在一个方法执行之后,只有在方法成功完成时,才能执行通知。
抛出异常后通知在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。
环绕通知在一个方法调用之前和之后,执行通知。

案例

业务接口

/**
 * 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
 */

public interface IBaseCalculate {
 
    int add(int numA, int numB);
 
    int sub(int numA, int numB);
 
    int div(int numA, int numB);
 
    int multi(int numA, int numB);
 
}

实现类

public class BaseCalculate implements IBaseCalculate {
 
    @Override
    public int add(int numA, int numB) {
        System.out.println("执行目标方法: add");
        return numA + numB;
    }
 
    @Override
    public int sub(int numA, int numB) {
        System.out.println("执行目标方法: sub");
        return numA - numB;
    }
 
    @Override
    public int multi(int numA, int numB) {
        System.out.println("执行目标方法: multi");
        return numA * numB;
    }
 
    @Override
    public int div(int numA, int numB) {
        System.out.println("执行目标方法: div");
        return numA / numB;
    }
}

通知类

public class BaseAdvice implements MethodBeforeAdvice ,AfterReturningAdvice,ThrowsAdvice,MethodInterceptor{
    /**
     *
     * @param method 切入的方法
     * @param args 切入方法的参数
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("===========进入beforeAdvice()============");
        System.out.println("目标对象:" + target);
        System.out.println("方法名: "+method);
        System.out.println("即将进入切入点方法");
    }
    
    /**
     *
     * @param returnValue 切入点执行完方法的返回值,但不能修改
     * @param method 切入点方法
     * @param args 切入点方法的参数数组
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("==========进入afterReturning()=========== \n");
        System.out.println("切入点方法执行完成");
        System.out.println("后置通知--目标对象:" + target);
        System.out.println("后置通知--方法名: "+method);
        System.out.println("后置通知--方法入参: "+ args.toString());
        System.out.println("后置通知--方法返回值: "+ returnValue);
    }
    
    /**
    	异常通知   接口中没有抽象方法 利用反射实现 
    */
    
    public void afterThrowing(Exception e){
        System.out.println("异常为:"+e.getMessage());
    }
    
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
       System.out.println("=================环绕通知开始===================");
        String method = methodInvocation.getMethod().getName();
        System.out.println("方法名:"+method);
        Object[] arguments = methodInvocation.getArguments();
        System.out.println("方法参数:"+Arrays.toString(arguments));
        Object aThis = methodInvocation.getThis();
        System.out.println("目标对象:"+aThis);
        System.out.println("=================前置通知===================");
        //执行目标方法
        Object proceed = methodInvocation.proceed();
        System.out.println("=================后置通知===================");
        System.out.println("返回值:"+proceed);
        System.out.println("=================环绕通知结束===================");
        return proceed;
    }
    
}

基于Aspectj实现AOP操作

1.基于xml配置文件

2.基于注解的形式实现

切入点表达式

作用:声明对哪个类中的哪个方法进行增强

语法:

execution([访问权限修饰符] [ 返回值 ] [ 类的全路径名 ] [ 方法名 ] (参数列表))

方式1:

public class AfterLog  implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println(method.getName()+"方法被执行,返回值是:"+o);
    }
}

public class Log  implements MethodBeforeAdvice {
    /**
     *
     * @param method 目标对象的方法
     * @param objects  参数
     * @param o  目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行");
    }
}

方式2:  

public class PointCut {

     //前置通知
    public void beforeMethod(){
        System.out.println("开启事务");
    }
    //后置通知  切点方法正常执行之后执行
    public void afterMethod(){
        System.out.println("关闭事务");
    }

    //环绕通知
    public void around(ProceedingJoinPoint jp)  {


        try {
            System.out.println("------环绕前------");
            Object proceed = jp.proceed();
            System.out.println("------环绕后------");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知");
        }finally {
            System.out.println("最终通知");
        }

    }

    //最终通知  无论切点方法是否正常执行 都会在其后执行
    public void fy(){
        System.out.println("最终通知");
    }

    //异常通知   和后置通知永远只能执行一个
    public void exceptionMethod(){
        System.out.println("异常通知");
    }
    
}
 <aop:config>
<!--        自定义切面-->
        <aop:aspect ref="point">
<!--            切入点-->
            <aop:pointcut id="pt" expression="execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))"/>
<!--            通知-->
            <aop:before method="before" pointcut-ref="pt"/>
            <aop:after method="after" pointcut-ref="pt"/>
        </aop:aspect>

    </aop:config>

方式3:注解方式

<?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"
       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/aop
               http://www.springframework.org/schema/aop/spring-aop.xsd
                http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context.xsd">
	<!--开启注解扫描-->
	<context:component-scan base-package="cn.kgc.spring"/>
    <!--开启aop注解-->
    <aop:aspectj-autoproxy/>
</beans>    
    
@Component
@Aspect
public class PointCut2 {

    //前置通知
    @Before("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
    public void beforeMethod(){
        System.out.println("开启事务");
    }
    //后置通知  切点方法正常执行之后执行
    @AfterReturning("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
    public void afterMethod(){
        System.out.println("关闭事务");
    }

    //环绕通知
    @Around("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp){

        try {
            System.out.println("------环绕前------");
            Object proceed = jp.proceed();
            System.out.println("------环绕后------");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知");
        }finally {
            System.out.println("最终通知");
        }

    }

    //最终通知  无论切点方法是否正常执行 都会在其后执行
    @After("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
    public void fy(){
        System.out.println("最终通知");
    }

    //异常通知   和后置通知永远只能执行一个
    @AfterThrowing("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
    public void exceptionMethod(){
        System.out.println("异常通知");
    }

}

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

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

相关文章

知识付费系统源码,可直接打包成app、H5、小程序

知识付费&#xff0c;在近几年来&#xff0c;越来越受到大家的关注。知识付费系统源码是将知识通过互联网渠道变现的方式。以知识为载体&#xff0c;通过付费获得在线知识以及在线学习所带来的收益。知识付费平台主要以分享知识内容&#xff0c;内容分为直播、录播、图文等形式…

【从零开始学爬虫】采集收视率排行数据

l 采集网站 ​【场景描述】采集收视率排行数据。 【源网站介绍】收视率排行网提供收视率排行,收视率查询,电视剧收视率,综艺节目收视率和电视台收视率信息。 【使用工具】前嗅ForeSpider数据采集系统 【入口网址】http://www.tvtv.hk/archives/category/tv 【采集内容】 …

产线工控安全

场景描述 互联网飞速发展&#xff0c;工业4.0的大力推行&#xff0c;让工控产线更加智能化&#xff0c;生产网已经发展成一个组网的计算机环境。这些工控产线组网中的所有工控设备现在统称为主机。 信息化虽然提高各大企业的生产效率&#xff0c;但也会遭遇各类安全问题&…

Problem B: 算法10-15~10-17:基数排序

Problem Description 基数排序是一种并不基于关键字间比较和移动操作的排序算法。基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。 通过对每一个关键字分别依次进行排序&#xff0c;可以令整个关键字序列得到完整的排序。而采用静态链表存储记录&#xf…

FAST-LIO论文阅读

1. 摘要 本文提出一个开销较小且鲁棒的激光惯性里程计框架。使用迭代扩展卡尔曼滤波器来实现激光雷达特征点和IMU的紧耦合&#xff0c;可以在快速运动、有噪声或重复纹理等退化环境中鲁棒地定位。为了在测量数据量很大的情况下降低开销&#xff0c;提出了计算卡尔曼增益的新公…

如何做电商运营

电商是通过电子设备和网络技术进行的商业模式&#xff0c;通俗的来说也就是通过网络结识买家完成最终交易。电子商务凭借它便宜&#xff0c;丰富和方便的特性&#xff0c;迅速占领了中国一大半的经济市场&#xff0c;作为个人怎么才能做好电商呢&#xff1f;掌握这几个要点就不…

物联网开发笔记(63)- 使用Micropython开发ESP32开发板之控制ILI9341 3.2寸TFT-LCD触摸屏进行LVGL图形化编程:显示中文

一、目的 这一节我们学习如何使用我们的ESP32开发板来控制ILI9341 3.2寸TFT-LCD触摸屏进行LVGL图形化编程的第一步&#xff1a;显示中文。 二、环境 ESP32 3.2寸 ILI9341触摸屏 Thonny IDE 几根杜邦线 Win10 接线方法&#xff1a;请看上一篇文章。 三、流程介绍 …

Verilog刷题HDLBits——Conwaylife

Verilog刷题HDLBits——Conwaylife题目描述代码结果题目描述 Conway’s Game of Life is a two-dimensional cellular automaton. The “game” is played on a two-dimensional grid of cells, where each cell is either 1 (alive) or 0 (dead). At each time step, each c…

【图像融合】小波变换(加权平均法+局域能量+区域方差匹配)图像融合【含Matlab源码 1819期】

⛄一、小波变换彩色图像融合简介 1 前言 图像融合是将不同传感器所获得的多个图像根据某种算法进行融合处理,取长补短,使一幅图像能够更清楚、更准确地反映多幅图像的信息,多聚焦彩色图像融合是图像融合的一个分支。目前在各种图像采集与分析系统中已使用的CCD数码相机,对于聚…

分享7 个VUE项目用得上的JavaScript库

借助开源库加速VUE项目的开发进度是现代前端开发比较常见的方式&#xff0c;平常收集一些JavaScript库介绍&#xff0c;在遇到需要的时候可以信手拈来。 VUE 生态有很多不错的依赖库或者组件&#xff0c;是使用VUE开发前端的原因之一。 1. vueuse 这是 GitHub 上星最多的库之…

【coarse-to-fine:基于频谱和空间损失约束】

UPanGAN: Unsupervised pansharpening based on the spectral and spatial loss constrained Generative Adversarial Network &#xff08;UPanGAN&#xff1a;基于频谱和空间损失约束的生成式对抗网络的无监督全色锐化&#xff09; 研究发现&#xff0c;在大多数基于神经网…

扎根底层核心技术:OPPO发布旗舰蓝牙音频SoC芯片

OPPO自研芯片能力更进一步。 2022年12月14日&#xff0c;OPPO发布自研芯片马里亚纳MariSilicon Y&#xff0c;作为一颗旗舰蓝牙音频SoC&#xff0c;实现了三大核心技术突破&#xff0c;使OPPO具备了计算连接能力的蓝牙SoC平台的设计能力。 这是OPPO发布的第二款自研芯片。去年…

初学者数据分析——Python职位全链路分析

最近在做Python职位分析的项目&#xff0c;做这件事的背景是因为接触Python这么久&#xff0c;还没有对Python职位有一个全貌的了解。所以想通过本次分析了解Python相关的职位有哪些、在不同城市的需求量有何差异、薪资怎么样以及对工作经验有什么要求等等。分析的链路包括&…

用了那么久的Vue,你了解Vue的报错机制吗?

Vue的5种处理Vue异常的方法 相信大家对Vue都不陌生。在使用Vue的时候也会遇到报错&#xff0c;也会使用浏览器的F12 来查看报错信息。但是你知道Vue是如何进行异常抛出的吗&#xff1f;vue 是如何处理异常的呢&#xff1f;接下来和大家介绍介绍&#xff0c;Vue是如何处理这几种…

【数据结构】树以及二叉树的概念

作者&#xff1a;一个喜欢猫咪的的程序员 专栏&#xff1a;《数据结构》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 树的概念&#xff1a; 树的相关概念&#xff1a; 树如何表示&#xff…

Anaconda中Python虚拟环境的创建、使用与删除

本文介绍在Anaconda环境下&#xff0c;创建、使用与删除Python虚拟环境的方法。 在Python的使用过程中&#xff0c;我们常常由于不同Python版本以及不同第三方库版本的支持情况与相互之间的冲突情况&#xff0c;而需要创建不同的Python虚拟环境&#xff1b;在Anaconda的帮助下&…

如何使用Python构建Telegram机器人来生成随机引语

使用Python构建Telegram机器人以生成随机引语 聊天机器人是用于进行在线聊天对话的软件应用程序&#xff0c;通过文本或文本转语音的方式实现客户服务的自动化。[聊天机器人]可以用于提醒、预约等事情&#xff0c;也可以在社交媒体平台上使用。 在本教程中&#xff0c;我们将…

会自动化就能拿20K?不,你这顶多算会点皮毛···

前段时间公司要招2个自动化测试&#xff0c;同事面了几十个候选人&#xff0c;发现了一个很奇怪的现象&#xff0c;面试的时候&#xff0c;如果问的是框架api、脚本编写这些问题&#xff0c;基本上个个都能对答如流&#xff0c;等问到实际项目的时候&#xff0c;类似“怎么从0开…

如何实现一个基于WebRTC的音视频通信系统

文章有点长&#xff0c;推荐先收藏前言 目前市场上音视频技术方案大致分为以下几类&#xff0c;WebRTC因其超低延时、集成音视频采集传输等优点&#xff0c;是在线教育、远程会议等领域首选技术。 前言 目前市场上音视频技术方案大致分为以下几类&#xff0c;WebRTC因其超低延…

10年网安经验分享:一般人别瞎入网络安全行业

小白入门网络安全&#xff0c;如何选择方向&#xff1f; 如果你是一个新手小白&#xff0c;那么在最开始方向选择上面这一步是至关重要的&#xff0c;一旦你选错了那很可能就要和安全“saygoodbye”了。 很多小白刚开始的时候还没开始学会走就想着飞了&#xff0c;计算机功底…