Spring6入门到高级-动力节点老杜

文章目录

  • OCP开闭原则
  • 依赖倒置原则
  • 控制反转
    • 依赖注入DI
      • Set方法注入
      • 构造注入
  • Sping特点
  • 代理模式
    • 代理模式中的角色
    • 动态代理
      • JDK动态代理
        • newProxyInstance() 的三个参数
      • JDK实现代理的步骤
        • 第一步:创建目标对象
        • 第二步:创建代理对象
        • 第三步:调用代理对象的代理方法
      • CGlib动态代理

OCP开闭原则

OCP(Open-Closed Principle):开放封闭原则
  OCP是面向对象设计中的重要原则之一,其核心思想是:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
  也就是说,如果在进行功能扩展的时候,添加额外的类是没有问题的,但是因为功能扩展而修改之前运行正常的程序,就是忌讳的,不被允许的。
  因为一旦修改之前运行正常的程序,就会导致项目整体要进行全方位的重新测试,这是相当麻烦的过程。导致这样问题的主要原因是:代码和代码之间的耦合度太高了。

依赖倒置原则

DIP(Dependence Inveersion Principle):依赖导致原则
  DIP倡导程序要面向抽象编程,面向接口编程,不要面向具体编程,不要依赖于具体事项。
  让上层不再依赖下层,下面改动了,上面的代码不会受到牵连。
  这样可以大大降低程序的耦合度,耦合度低了,扩展力就强了,同时代码复用性也会增强。
  (软件七大开发原则都是在为解耦合服务)

思考

1.如何做到依赖倒置?
2.依赖又倒置给谁,谁来实现和维护?

Spring框架就很好的做到了这一点,在Spring框架中,spring帮我们new对象,并且将new出来的对象赋值到属性上,并且帮助我们维护对象和对象之间的的关系。
这一点就称之为控制反转IOC

控制反转

IOC

IOC(Inversion of Control):控制反转
   上文提到的"依赖倒置"其实说的就是一种"控制反转"的思想。
   IOC是面向对象编程中的一种设计思想,可以用来降低代码之间的耦合度,符合依赖倒置原则。

控制反转的核心是:
   将对象的创建权交出去,将对象与对象之间关系的管理权交出去,由第三方容器来负责创建和维护。

而Spring框架就是一个实现了IoC思想的框架。

依赖注入DI

DI(Dependency Injection):依赖注入
   DISpring对控制反转的具体实现
   
   通常,依赖注入的实现又包括两种方式:
     ● set方法注入
     ● 构造方法注入

Set方法注入

set注入基于set方法实现:
  底层会通过反射机制调用属性对应的set方法然后给属性赋值。这种方式要求属性必须对外提供set方法。
public class UserService {

    private UserDao userDao;

    // 使用set方式注入,必须提供set方法。
    // 反射机制要调用这个方法给属性赋值的。
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

    <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
        <property name="userDao" ref="userDaoBean"/>
    </bean>

</beans>

构造注入

核心原理:通过调用构造方法来给属性赋值。

Sping特点

Spring是一个无侵入式的针对Bean的生命周期进行管理的轻量级的开源框架。是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。

1. 轻量
  a. 从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。
     并且Spring所需的处理开销也是微不足道的。
  b. Spring是非侵入式的:Spring应用中的对象不依赖于Spring的特定类。
  
2. 控制反转
  a. Spring通过一种称作控制反转(IoC)的技术促进了松耦合。
     当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。
     你可以认为IoCJNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
     
3. 面向切面
  a. Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。
     应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
     
4. 容器
  a. Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,
     你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),
     你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。
     然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
  
5. 框架
  a. Spring可以将简单的组件配置、组合成为复杂的应用。
     在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。
     Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

代理模式

应用背景
西游记中高老庄收猪八戒那一集中,猪八戒背着高小姐,背着背着怎么变成了背着孙猴子了。原来是孙悟空变成了高小姐,替高小姐和猪八戒成亲了。这其实就是一个代理模式:孙悟空代替了高小姐和猪八戒成亲,既保护了高小姐又完成了八戒非要和高小姐成亲的愿望。这就是非常典型的代理模式保护机制。

在成亲的过程中,八戒并不知道眼前的高小姐是孙悟空,而孙悟空却知道他是代替高小姐和猪八戒成亲的,孙悟空既知道高小姐,又知道猪八戒。

代理模式中有一个非常重要的特点:
   对于客户端程序来说,使用代理对象时就像在使用目标对象一样。

代理模式中的角色

代理模式中的角色:
  ● 代理类:(代理主题)孙悟空
  ● 目标类:(真实主题)高小姐
  ● 代理类和目标类的公共接口(抽象主题):客户端(八戒)在使用代理类时就像在使用目标类,
    不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。

代理模式的类图:
在这里插入图片描述

动态代理

动态代理:
  程序运行阶段,在内存中动态生成代理类的方式叫做动态代理,目的是为了减少代理类的数量,解决代码复用的问题。

JDK动态代理

由JDK的java.lang.reflect包下的Proxy类的newProxyInstance()来实现,只能代理接口。

newProxyInstance() 的三个参数

newProxyInstance源码

package java.lang.reflect;

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
     Objects.requireNonNull(h);

     @SuppressWarnings("removal")
     final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

     /*
      * Look up or generate the designated proxy class and its constructor.
      */
     Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

     return newProxyInstance(caller, cons, h);
 }
newProxyInstance()java.lang.reflect.Proxy中的方法
   这是JDK提供的一个类(所以称为JDK动态代理)。主要是通过这个类在内存中生成代理类的字节码。

   newProxyInstance 翻译为:新建代理对象
     也就是说,通过调用这个方法可以创建代理对象。
     本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
        第一件事:在内存中动态的生成了一个代理类的字节码class。
        第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
                
   其中newProxyInstance()方法有三个参数:
     ● 第一个参数:"ClassLoader loader" 类加载器。
                 在内存中生成的字节码class文件,加载类就需要类加载器,执行代码得先加载到内存当中,所以这里需要指定类加载器。
                 并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
                 
     ● 第二个参数:"Class<?>[] interfaces" 接口类型。
                 代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。
                 
     ● 第三个参数:"InvocationHandler h" 调用处理器。
                 InvocationHandler 被翻译为:调用处理器,是一个接口。
                 这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。
                 在调用处理器接口中编写的就是:增强代码。具体需要增强什么还是得自己写的,不可能JDK也能猜到你需要增强什么帮你自动生成。
                 显然这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。

JDK实现代理的步骤

第一步:创建目标对象
  // 创建目标对象:也就是具体的业务实现类
  OrderService target = new OrderServiceImpl();
第二步:创建代理对象

创建代理对象:TimerInvocationHandler(),该代理对象需要实现InvocationHandler接口

1. 为什么强行要求你必须实现InvocationHandler接口?
      因为一个类实现接口就必须实现接口中的方法。
      以下这个方法必须是invoke(),因为JDK在底层调用invoke()方法的程序已经提前写好了。
      注意:invoke方法不是我们程序员负责调用的,是JDK负责调用的。
            
2. invoke方法什么时候被调用呢?
      当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。

3. invoke方法的三个参数:
      invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数。
      我们可以在invoke方法的大括号中直接使用。
      第一个参数:Object proxy 代理对象的引用。这个参数使用较少。
      第二个参数:Method method 目标对象上的目标方法。(要执行的目标方法就是它。)
      第三个参数:Object[] args 目标方法上的实参。

      invoke方法执行过程中,使用method来调用目标对象的目标方法。
        
public class TimerInvocationHandler implements InvocationHandler {

    // 目标对象
    private Object target;

    public TimerInvocationHandler(Object target) {
        // 赋值给成员变量。
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 这个接口的目的就是为了让你有地方写增强代码。
        //System.out.println("增强1");
        long begin = System.currentTimeMillis();


        // 调用目标对象上的目标方法
        // 方法四要素:哪个对象,哪个方法,传什么参数,返回什么值。
        Object retValue = method.invoke(target, args);

        //System.out.println("增强2");
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

        // 注意这个invoke方法的返回值,如果代理对象调用代理方法之后,需要返回结果的话,invoke方法必须将目标对象的目标方法执行结果继续返回。
        return retValue;
    }
}
第三步:调用代理对象的代理方法

客户端程序

public class Client {
    //客户端程序
    public static void main(String[] args) {

        // 创建目标对象
        OrderService target = new OrderServiceImpl();
        // 创建代理对象
        OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                     target.getClass().getInterfaces(),
                                     new TimerInvocationHandler(target));

        // 上面代码通过一个工具类的封装,就简洁了。
        OrderService proxyObj = (OrderService) ProxyUtil.newProxyInstance(target);

        // 调用代理对象的代理方法
        // 注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行。
        proxyObj.generate();
        proxyObj.modify();
        proxyObj.detail();

        String name = proxyObj.getName();
        System.out.println(name);

    }
}

封装ProxyUtil工具类

public class ProxyUtil {

    /**
     * 封装一个工具方法,可以通过这个方法获取代理对象。
     * @param target
     * @return
     */
    public static Object newProxyInstance(Object target){
        // 底层是调用的还是JDK的动态代理。
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                      target.getClass().getInterfaces(),
                                      new TimerInvocationHandler(target));
    }

}

CGlib动态代理

CGLIB(Code Generation Library)是一个第三方的开源项目。
   是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
   它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。

CGLIB需要引入第三方依赖

      <!--cglib依赖-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

和JDK动态代理原理差不多,在CGLIB中需要提供的不是InvocationHandler,而是:net.sf.cglib.proxy.MethodInterceptor

代理类

public class TimerMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 前面增强
        long begin = System.currentTimeMillis();

        // 怎么调用目标对象的目标方法呢?
        Object retValue = methodProxy.invokeSuper(target, objects);

        // 后面增强
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

        return retValue;
    }
}

客户端程序

public class Client {
    public static void main(String[] args) {
        // 创建字节码增强器对象
        // 这个对象是CGLIB库当中的核心对象,就是依靠它来生成代理类。
        Enhancer enhancer = new Enhancer();

        // 告诉CGLIB父类是谁。告诉CGLIB目标类是谁。
        enhancer.setSuperclass(UserService.class);

        // 设置回调(等同于JDK动态代理当中的调用处理器。InvocationHandler)
        // 在CGLIB当中不是InvocationHandler接口,是方法拦截器接口:MethodInterceptor
        enhancer.setCallback(new TimerMethodInterceptor());

        // 创建代理对象
        // 这一步会做两件事:
        // 第一件事:在内存中生成UserService类的子类,其实就是代理类的字节码。
        // 第二件事:创建代理对象。
        // 父类是UserService,子类这个代理类一定是UserService
        UserService userServiceProxy = (UserService) enhancer.create();

        // 建议大家能够把CGLIB动态代理生成的代理对象的名字格式有点印象。
        // 根据这个名字可以推测框架底层是否使用了CGLIB动态代理
        System.out.println(userServiceProxy);

        // 调用代理对象的代理方法。
        boolean success = userServiceProxy.login("admin", "123");
        System.out.println(success ? "登录成功" : "登录失败");

        userServiceProxy.logout();
    }
}

在这里插入图片描述

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

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

相关文章

C语言学习--八种排序算法

目录 排序的概念 1.直接插入排序 基本思想 代码实现 算法分析 2.希尔排序 基本思想 代码实现 算法分析 3.冒泡排序 基本思想 代码实现 算法分析 4.快速排序 基本思想 代码实现 算法分析 5.简单选择排序 基本思想 代码实现 算法分析 6.堆排序 基本思想 代…

合合信息扫描全能王亮相静安区3·15活动,AI扫描带来绿色消费新体验

保护消费者的合法权益&#xff0c;是全社会的共同责任。为优化消费环境、促进品质消费高地建设&#xff0c;打造安全优质和谐的消费环境&#xff0c;上海静安区消保委于3月15日举办静安区2024年“315”国际消费者权益日活动。 “激发消费活力&#xff0c;绿色低碳同行”是本次3…

Vue+SpringBoot打造民宿预定管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色2.2.2 房主角色2.2.3 系统管理员角色 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿4.3 新增民宿评价4.4 查询留言4.5 新增民宿订单 五、免责说明 一、摘要 1.1 项目介绍 基于…

傻傻分不清目标检测、语义分割和实例分割,看这篇就够了

⭐️ 导言 随着深度学习技术的飞速发展&#xff0c;计算机视觉领域取得了巨大的进步。目标检测、语义分割和实例分割是计算机视觉中的重要任务&#xff0c;它们在图像理解和视频分析等方面发挥着关键作用。本文将深入探讨这三个任务的概念、原理、常用算法以及在实际应用中的案…

(css)vue 自定义背景 can‘t resolve

(css)vue 自定义背景 can’t resolve 旧写法&#xff1a; background-image: url(/assets/images/step-bg.jpg);background-size: 100% 100%; 新写法&#xff1a; background-image: url(~/assets/images/step-bg.jpg);background-size: 100% 100%; 解决参考&#xff1a;https…

shopee无货源出单了怎么发货?shopee怎么做无货源?

在Shopee的电商大舞台上&#xff0c;“无货源出单”就像是一场神奇的魔术表演。你的店铺是舞台&#xff0c;买家的订单是观众的掌声&#xff0c;而你&#xff0c;就是那位神秘的魔术师。订单来了&#xff0c;你却没有货&#xff1f;这可不是什么障碍&#xff0c;因为你有着更为…

算法详解——选择排序和冒泡排序

一、选择排序 选择排序算法的执行过程是这样的&#xff1a;首先&#xff0c;算法遍历整个列表以确定最小的元素&#xff0c;接着&#xff0c;这个最小的元素被置换到列表的开头&#xff0c;确保它被放置在其应有的有序位置上。接下来&#xff0c;从列表的第二个元素开始&#x…

[MySQL]数据库基础

文章目录 1.连接服务器2.理解mysql3.初见数据库4.主流数据库5.服务器&#xff0c;数据库&#xff0c;表关系6.数据逻辑存储7.MySQL架构8.SQL分类9.存储引擎 1.连接服务器 mysql -h 127.0.0.1 -P 3306 -u root -p -h&#xff1a;指明登录部署mysql服务的主机。没有写 -h 127.0.…

【链表】Leetcode 21. 合并两个有序链表【简单】

合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 解题思路 1、比较两个链表的头结点&#xff0c;选择其…

jumpserver管理集群

git地址&#xff1a;https://github.com/jumpserver/jumpserver.git 1、下载 jumpserver 需要docker来拉取镜像&#xff0c;没有的话会自动下载docker curl -sSL https://resource.fit2cloud.com/jumpserver/jumpserver/releases/latest/download/quick_start.sh | bash 拉取的…

Prometheus修改默认数据存储时间

Prometheus的默认数据存储时间可以通过修改启动脚本中的相关参数来调整。具体来说&#xff0c;可以通过修改--storage.tsdb.retention.time参数来改变数据保留的时长。该参数决定了何时删除旧数据&#xff0c;默认为15天。如果需要延长数据保留时间&#xff0c;可以将该参数的值…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Hyperlink)

超链接组件&#xff0c;组件宽高范围内点击实现跳转。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件仅支持与系统浏览器配合使用。 需要权限 使用网络时&#xff0c;需要申请权限ohos.per…

AI论文速读 | UniST:提示赋能通用模型用于城市时空预测

本文是时空领域的统一模型——UniST&#xff0c;无独有偶&#xff0c;时序有个统一模型新工作——UniTS&#xff0c;感兴趣的读者也可以阅读今天发布的另外一条。 论文标题&#xff1a;UniST: A Prompt-Empowered Universal Model for Urban Spatio-Temporal Prediction 作者&…

大势智慧与云世纪签署战略合作,实景三维赋能低空经济,泛测绘助力城市数据更新更高效

2024年《政府工作报告》提出“要大力推进现代化产业体系建设&#xff0c;加快发展新质生产力”、“积极打造商业航天、低空经济等新增长引擎”。 近日&#xff0c;武汉大势智慧科技有限公司&#xff08;以下简称“大势智慧”&#xff09;和青岛云世纪信息科技有限公司&#xf…

android 顺滑滑动嵌套布局

1. 背景 最近项目中用到了上面的布局&#xff0c;于是使用了scrollviewrecycleview&#xff0c;为了自适应高度&#xff0c;重写了recycleview&#xff0c;实现了高度自适应&#xff1a; public class CustomRecyclerView extends RecyclerView {public CustomRecyclerView(Non…

麒麟信安出品 | 无惧停服挑战!看C2K平台如何轻松拿捏CentOS迁移

2020年Redhat公司面向全球公布&#xff0c;于2021年底停止维护开源服务器操作系统CentOS 8&#xff0c;并将于2024年6月30日停止维护CentOS 7&#xff0c;届时CentOS全系列版本将停止维护。 在CentOS系统逐步停服的背景下&#xff0c;麒麟信安为满足各行各业现存的大量CentOS系…

基于树莓派实现 --- 智能家居

最效果展示 演示视频链接&#xff1a;基于树莓派实现的智能家居_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Tr421n7BM/?spm_id_from333.999.0.0 &#xff08;PS&#xff1a;房屋模型的搭建是靠纸板箱和淘宝买的家居模型&#xff0c;户型参考了留学时短租的公寓~&a…

el-tree 设置默认展开指定层级

el-tree默认关闭所有选项&#xff0c;但是有添加或者编辑删除的情况下&#xff0c;需要刷新接口&#xff0c;此时会又要关闭所有选项&#xff1b; 需求&#xff1a;在编辑时、添加、删除 需要将该内容默认展开 <el-tree :default-expanded-keys"expandedkeys":da…

【C语言】常见的字符串处理函数

目录 1、strlen&#xff08;&#xff09;函数 2、strcpy&#xff08;&#xff09;、strncpy&#xff08;&#xff09;函数 3、strstr&#xff08;&#xff09; 函数 4、strcmp&#xff08;&#xff09;、strncmp&#xff08;&#xff09;函数 5、strcat&#xff08;&#…

[数据结构]堆

一、堆是什么&#xff1f; 堆是一种完全二叉树 完全二叉树的定义&#xff1a;完全二叉树的形式是指除了最后一层之外&#xff0c;其他所有层的结点都是满的&#xff0c;而最后一层的所有结点都靠左边。​​​​​​&#xff0c;从左到右连续。 教材上定义如下: 若设二叉树的…