Java框架之spring 的 AOP 和 IOC

news2024/11/18 2:34:38

写在前面

本文一起看下spring aop 和 IOC相关的内容。

1:spring bean核心原理

1.1:spring bean的生命周期

在这里插入图片描述

spring bean生命周期,参考下图:
在这里插入图片描述

我们来一步步的看下。

  • 1
    其中1构造函数就是执行类的构造函数完成对象的创建,如下:
public User() {
    System.out.println("User 被实例化");
}
  • 2
    2是通过xml配置或者是@Resource等相关注入bean的注解,从容器中获取相关的spring bean进行赋值,如下:

在这里插入图片描述

在这里插入图片描述

  • 3
    3BeanNameAware,其中BeanNameAware是一个接口,只定义了一个唯一的方法void setBeanName(String name);,如果bean实现了该接口,则会回调setBeanName方法并将当前bean的beanName作为参数传递进去,这样,bean就能够获取自己的名称了,如下:
@Component
public class TestAware implements BeanNameAware {
    private String name;
    
    @Override
    public void setBeanName(String name) {
        this.name = name;
        System.out.println("TestAware.setBeanName, name is: " + this.name);
    }
}
  • 4,5
    4,5类似于3,也都分别定义了相关的xxxAware接口,以及对应的setXxx抽象方法,如下:
public class TestAware implements BeanFactoryAware, ApplicationContextAware {
    private BeanFactory beanFactory;
    private ApplicationContext applicationContext;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println("TestAware.setBeanFactory");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        System.out.println("TestAware.setApplicationContext");
    }
}
  • 6
    6是spring留给我们的一个扩展的口,对应的接口是BeanPostProcessor,定义了初始化前执行的方法postProcessBeforeInitailization和初始化后执行的方法postProcessAfterInitialization如下:
public interface BeanPostProcessor {
    // 1:在InitailizingBean的afterProperties方法执行之前执行
    // 2:在自定义的init方法执行之前执行
    // 3:接收的参数是bean本身以及bean的名称
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	// 1:在InitailizingBean的afterProperties方法执行之后执行
    // 2:在自定义的init方法执行之后执行
    // 3:接收的参数是bean本身以及bean的名称
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

使用BeanPostProcessor的方式也比较简单,只需要实现该接口,对前置bean处理和后置bean处理两个方法提供具体实现后,将实现类注册为spring bean就可以了,如下可能实现:

public class MyBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("before--实例化的bean对象:"+bean+"\t"+beanName);
		// 可以根据beanName不同执行不同的处理操作
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("after...实例化的bean对象:"+bean+"\t"+beanName);
		// 可以根据beanName不同执行不同的处理操作
		return bean;
	}
}
  • 7
    InitializingBean也是一个扩展接口,定义了一个方法afterProperties,是在bean的属性都设置完毕后调用的方法,可以在该接口检测不可为空属性是否有值等操作,接口定义如下:
package org.springframework.beans.factory;

// 1:在所有属性设置完毕后执行
// 2:可以检测强制属性是否设置完成
// 3:可以使用自定义的init方法作为该方式的替代品
public interface InitializingBean {
	void afterPropertiesSet() throws Exception;

}

使用实例如下:

public class MyInitializingBeanTest implements InitializingBean {
    private String name;
    private int age;

    @Override
    public void afterPropertiesSet() throws Exception {
        String newName = "李四的歌";
        System.out.println("修改name属性值从 " + this.name + " -> " + newName);
        // 修改name属性的值
        this.name = newName;
    }
    ...
}
  • 8
    自定义的初始化方法,在bean标签中通过init-method设置,如下:
public class MyBeanWithInitMethod {
    private String name;

    public void changeNameProp() {
        String newName = "李四的歌";
        System.out.println("通过init-method修改name属性值从 "
                + this.name + " -> " + newName);
        this.name = newName;
    }
    ...
}

<bean class="yudaosourcecode.studyhelp.MyBeanWithInitMethod"
        id="myBeanWithInitMethod" init-method="changeNameProp">
    <property name="name" value="张三的歌"/>
</bean>
  • 9
    参考6,只不过是在InitialiBean和自定义init初始化方法执行完毕之后执行。
  • 10
    使用期。
  • 11
    当容器关闭时执行操作的接口规范,一般用来执行一些释放资源,收尾等操作,接口定义如下
package org.springframework.beans.factory;

// 1:当bean销毁时想要释放资源可以让bean实现该接口
// 2:可以使用自定义销毁方法作为另一个选择
public interface DisposableBean {
	void destroy() throws Exception;
}

实例如下:

public class MyLifeCycleBean implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean#destroy被调用了。。。");
    }
}
  • 12
    类似于11,自定义销毁方法,如下:
public class MyLifeCycleBean {

    public void destroyMethdo(){
        System.out.println("destroy-method 被调用...");
    }
}

<bean id="lifeCycle" 
      class="yudaosourcecode.studyhelp.MyLifeCycleBean"
      destroy-method="destroyMethdo"/>

1.2:spring bean生命周期完整实例

  • BeanPostProcessor
public class BeanPostProcessor123 implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("6:BeanPostProcessor#postProcessBeforeInitialization被调用了。。。");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("9:BeanPostProcessor#postProcessAfterInitialization被调用了。。。");
        return bean;
    }

}
  • bean
public class MyLifeCycleBean implements BeanNameAware,
        BeanFactoryAware,
        ApplicationContextAware,
//        BeanClassLoaderAware,
//        BeanPostProcessor,
        InitializingBean,
        DisposableBean {
    private String test;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("5:ApplicationContextAware 被调用。。。");
    }

    // 普通方法
    public void display(){
//        System.out.println("普通类实例方法调用...");
        System.out.println("10:使用中。。。");
    }

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        System.out.println("2:依赖注入");
        this.test = test;
    }

    public MyLifeCycleBean() {
        System.out.println("1:构造函数");
    }
//    @Override
//    public void setBeanClassLoader(ClassLoader classLoader) {
//        System.out.println("BeanClassLoaderAware 被调用了。。。");
//    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4:BeanFactoryAware 被调用了。。。");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("3:BeanNameAware 被调用了。。。");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("11:DisposableBean#destroy被调用了。。。");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("7:InitializingBean#afterPropertiesSet被调用了。。。");
    }
//
//    @Override
//    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//        System.out.println("BeanPostProcessor#postProcessBeforeInitialization被调用了。。。");
//        return bean;
//    }
//
//    @Override
//    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//        System.out.println("BeanPostProcessor#postProcessAfterInitialization被调用了。。。");
//        return bean;
//    }

    public void initMethod(){
        System.out.println("8: init-method 被调用...");
    }

    public void destroyMethdo(){
        System.out.println("12:destroy-method 被调用...");
    }
}
  • 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="lifeCycle" class="yudaosourcecode.studyhelp.MyLifeCycleBean"
          init-method="initMethod" destroy-method="destroyMethdo">
        <property name="test" value="test"/>
    </bean>

    <bean class="yudaosourcecode.studyhelp.BeanPostProcessor123"/>
</beans>
  • 测试
@Test
public void mylifecyclebeantest111() {
//        ClassPathResource resource
//                = new ClassPathResource("mylifecyclebeantest.xml");
//        XmlBeanFactory xbf = new XmlBeanFactory(resource);
    AbstractApplicationContext ac = new ClassPathXmlApplicationContext("mylifecyclebeantest.xml");
//        Closeable
    // 手动添加后置bean处理器
//        xbf.addBeanPostProcessor(new MyLifeCycleBean());
//        ac.addBeanPostProcessor(new BeanPostProcessor123());
//        ac.
//        MyLifeCycleBean lifeCycle
//                =
    ac.getBean("lifeCycle", MyLifeCycleBean.class).display();
//        lifeCycle.display();
    // 销毁容器
    ac.close();
}

运行:

1:构造函数
2:依赖注入
3:BeanNameAware 被调用了。。。
4:BeanFactoryAware 被调用了。。。
5:ApplicationContextAware 被调用。。。
6:BeanPostProcessor#postProcessBeforeInitialization被调用了。。。
7:InitializingBean#afterPropertiesSet被调用了。。。
8: init-method 被调用...
9:BeanPostProcessor#postProcessAfterInitialization被调用了。。。
10:使用中。。。
11:DisposableBean#destroy被调用了。。。
12:destroy-method 被调用...

对比如下图:

在这里插入图片描述

1.3:spring xml配置原理

这部分来看看下我们在xml配置的<bean>标签是如何解析为spring bean的,我们会定义如下的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="lifeCycle" class="yudaosourcecode.studyhelp.MyLifeCycleBean"
          init-method="initMethod" destroy-method="destroyMethdo">
        <property name="test" value="test"/>
    </bean>

    <bean class="yudaosourcecode.studyhelp.BeanPostProcessor123"/>
</beans>

其中在<beans中声明的我们叫做是命名空间,然后在命名空间中就可以定义各种各样的标签,并定义了校验xml的xsd,为了解决通过互联网直接加载xsd慢,或者是加载不到的问题,定义了配置文件spring.schemas,配置了命名空间和对应的本地xsd文件的对应关系:

在这里插入图片描述

这样就可以直接从jar包中获取xsd文件了,那么标签如何解析呢?需要使用到spring.handlers,如下:

在这里插入图片描述

当然我们也可以自定义标签,同样也需要在spring.schemas中配置我们自己的xsd,以及并实现并配置解析对应标签的handler到spring.handles配置文件中,具体可以参考spring的自定义标签 一文。

1.4:spring bean配置方式演进

1.0/2.0 XML配置bean,@Autowire注解注入bean
2.5 @Service xml配置和注解配置bean方式混用的半自动注解配置
3.0 @Bean,@Condition Javaconfig方式配置bean
4.0 @Condition,@AutoConfigureX 全自动注解配置

在这里插入图片描述

2:spring AOP

通过前面分析的spring bean的功能,我们已经能够灵活地定义各种对象实例,并设置其各种依赖关系了,此时假定我们现在有类似于下面这样的需求:

1:怀疑某个方法执行的慢,影响性能,但又无法直接修改源代码看其耗时
2:想要记录某一批方法的调用日志,因为方法很多,一个个改的话非常麻烦
3:程序有bug,但是程序年代久远,源程序早已经遗失,但又必须解决

该怎么做呢?在计算机领域有这样一个至理名言,计算机领域所有的问题都可以通过加一层来解决,这里的问题可以吗?可以的,我们只要在方法执行的前和后都加一层就行了,如下:

在这里插入图片描述

spring为了实现这个加一层的功能就提供了对应的功能实现,首先我们需要定义要对那些方法加一层,通过如下红框方式来表示:

在这里插入图片描述

spring给其起个一个专门的名称切点,即要切的点,要拦截的方法,这样还不够,知道要对哪些方法加一层了,但是需要做什么呢,这个时候就可以这样定义:

在这里插入图片描述

spring也给其起了一个专门的名称通知,其中在方法执行前执行的叫做前置通知,在方法执行后执行的叫做后置通知,所有的同志加在一起,spring也起了一个专门的名称切面

以上这个实现加一层的功能,spring也提供了一个专门的名称,aspect oriented programming,即面向切面编程,即AOP。

写在后面

参考文章列表

Java 操作 XML(11)–XMLBeans 使用 。

spring的自定义标签 。

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

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

相关文章

【Java】Java核心 79:Git 教程(2)Git 安装

文章目录 目标内容总结 上一篇我们讲到&#xff1a;Git是一个分布式版本控制系统&#xff0c;常用于协同开发和版本管理的工具。它可以跟踪文件的修改、记录历史版本&#xff0c;并支持多人协同工作。通过Git&#xff0c;你可以轻松地创建和切换分支、合并代码、回滚修改等操作…

Kicad编译

Windows 1.安装visual studio Pro 2019 以上版本&#xff0c;建议2022&#xff0c;自行破解&#xff0c;安装时一定要勾选上cmake工具&#xff1b;安装Git bash 2.去Gitlab上fork kicad的master分支到自己的gitlab仓库上&#xff0c;在本地创建kicad-source路径&#xff0c;初…

【debug】:安装mmcv-full==1.2.4包过程报错

【debug】:安装mmcv-full1.2.4包过程报错This error originates from a subprocess, and is likely not a problem with pip WARNING: Ignoring invalid distribution -illow (d:\anaconda3\envs\pytorch\lib\site-packages) 这是由于安装包过程中曾经出现问题&#xff0c;会影…

生成特定相关系数的变量

本文转载自根据相关性生成变量 已知一组变量a&#xff0c;想要生成另一组变量b&#xff0c;要求a与b之间相关性为c。 实现思路如下&#xff1a; 设固定变量为x1&#xff0c;随机变量x2&#xff0c;相关系数为rho。x1与x2之间的相关性可以转化为向量之间的夹角问题&#xff0c;…

Linux与Windows:操作系统之争及个人体验比较

在当今数码化的世界中&#xff0c;操作系统扮演着关键的角色。Linux和Windows作为最受欢迎和广泛使用的操作系统之一&#xff0c;具有不同的特点和优势。作为一个AI模型&#xff0c;我虽然没有真正的使用经验&#xff0c;但我可以就这两个操作系统进行比较&#xff0c;并提供一…

pointclouds 点云 demo 中的资源文件如pcd找不到的问题

一、问题描述 点云官网提供了许多学习点云类库的例子和demo &#xff0c;但是在github中找到tutorials后执行 mkdir build cd build cmake .. make 之后却发现没有 资源文件&#xff0c;怎么办 如&#xff1a;pcl-pcl-1.7.2\doc\tutorials\content\sources\normal_estimation…

QVHZO-A-06-3/U0/WG直动式比例流量阀控制器

QVHZO-A-06-3/U0/WG、QVHZO-A-06-12/I/PE、QVHZO-A-06-18/I、QVHZO-A-06-36/U0、QVHZO-A-06-45/I、QVKZOR-A-10-65/I/PE、QVKZOR-A-10-90/I直动式比例流量阀特点&#xff1a; 具有恒压差流量补偿功能 通过比例线圈起动 用于底板安装: 油口安装面符合ISO4401(规格尺寸6和10)…

基于单片机智能温室大棚控制系统

功能介绍 以51单片机作为主控系统&#xff1b; DS18B20温度采集模块检测温度&#xff1b; 光敏电阻和ADC0832组成的光照检测模块&#xff1b; 土壤湿度检测模块检测土壤湿度&#xff1b; CO2检测模块检测CO2浓度&#xff1b; LCD1602显示模块显示测量值、 若温度小于温度最…

Middleware ❀ Zookeeper功能与使用详解

文章目录 1、功能简述1.1 服务目标1.2 文件系统 - 树状结构1.3 数据节点Znode类型1.4 变更通知 - Watcher1.4.1 工作机制1.4.1.1 Client注册1.4.1.2 Server处理1.4.1.3 Client回调 1.4.2 监听特性 1.5 权限控制 - ACL1.5.1 模式 - scheme1.5.2 权限 - Permission 1.6 选举机制1…

Springboot如何手动连接库并获取指定表结构

一、前言&#x1f525; 通过&#xff0c;在使用springboot框架之后&#xff0c;就很少涉及到手动连接数据库的方式了&#xff0c;但bug菌有遇到这么一个需求场景&#xff0c;给到你的是无上限的数据库连接信息&#xff0c;要求你能按连接信息指定获取表数据&#xff0c;突然我就…

玩机搞机---另类操作 修改原生卡刷包转换为线刷包方式刷机

偶然给安卓机型刷写原生安卓的系统。可能其第三方twrp原因或者底包原因导致卡刷一直报错。虽然最终写入开机&#xff0c;但浪费时间&#xff0c;究其原因还在于分区切换和挂载分区导致的。写这篇博文的意义不是在于让玩家按步骤转换线刷&#xff0c;只是明白其分区写入的原理 索…

【JavaEE初阶】TCP/IP协议(二)

文章目录 网络层重点协议IP协议地址管理路由选择 数据链路层重点协议以太网协议MTU 应用层重要协议DNS&#xff08;域名解析系统&#xff09; 网络层重点协议 IP协议 协议头格式如下&#xff1a; 4位版本号&#xff08;version&#xff09;&#xff1a;指定IP协议的版本&am…

2023-6-28-第十一式代理模式

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

解读 RocketMQ 5.0 全新的高可用设计

作者&#xff1a;斜阳 高可用架构演进背景 在分布式系统中不可避免的会遇到网络故障&#xff0c;机器宕机&#xff0c;磁盘损坏等问题&#xff0c;为了向用户不中断且正确的提供服务&#xff0c;要求系统有一定的冗余与容错能力。RocketMQ 在日志&#xff0c;统计分析&#x…

Selenium系列(二) - 详细解读针对浏览器的操作

控制浏览器有哪些操作&#xff1f; 最大化、最小化浏览器控制、获取浏览器大小获取当前标签页title、url前进、后退、刷新执行js语句打开、关闭新标签页滚动页面 点击右边目录即可跳转哦&#xff01; -------------->>>>>>>>>> 最大化、最小化…

从零开始 Spring Boot 51:JPA 中的默认列值

从零开始 Spring Boot 51&#xff1a;JPA 中的默认列值 图源&#xff1a;简书 (jianshu.com) JPA 是一个 ORM 框架&#xff0c;因此&#xff0c;通常我们需要在实体类中定义表结构&#xff0c;这其中就包含可能的字段默认值。 本文介绍如何在 Hibernate&#xff08;JPA&#…

HBase(6):计数操作

1 需求 查看HBase中的ORDER_INFO表&#xff0c;一共有多少条记录。 2 count命令 count命令专门用来统计一个表中有多少条数据。语法&#xff1a; count 表名 注意&#xff1a;这个操作是比较耗时的。在数据量大的这个命令可能会运行很久&#xff0c;真实环境不要使用该命令。…

【从零开始学习JAVA | 第二十五篇】泛型

目录 前言&#xff1a; 泛型&#xff1a; 额外拓展&#xff1a; 总结&#xff1a; 前言&#xff1a; 本文将详细介绍之前我们在JAVA 中一直在讲的泛型&#xff0c;各位感兴趣的同学可以点击进来观看。 泛型&#xff1a; 泛型是一种编程概念&#xff0c;它允许在定义类、接…

文章测试

Markdown示例 本文件的源码是一个markdown文件,也就是说在本工程中直接添加markdown即可嵌入到sphinx文档中。 关于使sphinx支持markdown的详细配置说明&#xff0c;请参考文档markdown-sphinx。 markdown的公式语法在sphinx可能不支持。 以下是markdown的语法使用示例 文…

PDF如何转换成Word?PDF转Word方法分享!​

PDF大家都不陌生了吧&#xff1f;作为打工人&#xff0c;学生党的大家都知道&#xff0c;PDF是现在不可或缺的文件传输工具之一&#xff0c;不仅可将文档转为Word&#xff0c;还可以转成excel,ppt等各种形式&#xff0c;其重要性不言而喻&#xff0c;那么今天小编就跟大家具体说…