【Spring框架精讲】进阶指南:企业级Java应用的核心框架(Spring5)

文章目录

  • 【Spring框架精讲】进阶指南:企业级Java应用的核心框架(Spring5)
    • 1.Spring框架快速入门
      • 1.1七大核心模块
        • 1.1.1 Spring Core
        • 1.1.2 Spring-Beans
        • 1.1.3 Spring Context
        • 1.1.4 Spring-Expression
        • 1.1.5 Spring AOP
        • 1.1.6 JDBC和DAO模块(Spring DAO)
        • 1.1.7 spring-transaction
        • 1.1.8 Spring ORM
        • 1.1.9 Spring Web MVC
      • 1.2 项目构建
    • 2.SpringIOC
      • 2.1反射创建对象
      • 2.2 IOC核心的接口
      • 2.3 ApplicationContext主要实现类
    • 3.SpringBean的注入方式
      • 3.1 创建对象和set方法、有参构造函数注入属性
      • 3.2注入空值和特殊符号
      • 3.3注入属性外部bean
      • 3.4注入内部bean(应用较少)
      • 3.5注入级联赋值
      • 3.6注入集合类型属性
      • 3.7注入集合类型为对象属性
      • 3.8集合注入部分提取公共
      • 3.9 IOC操作Bean的管理
    • 4.Spring的工厂Bean
      • 4.1 SpringBean的作用域
      • 4.2 SpringBean的生命周期
      • 4.3 SpringBean的自动装配
    • 5.SpringBean的AOP
      • 5.1代理模式
        • 5.1.1 代理模式创建方式
        • 5.1.2 静态代理
        • 5.1.3 动态代理
      • 5.2 AOP详解
        • 5.2.1 Aop常用术语
        • 5.2.2 Aop环境准备

【Spring框架精讲】进阶指南:企业级Java应用的核心框架(Spring5)

JDK最低版本要求1.8

Spring概念
Spring是一个JavaEE开源的轻量级别的框架,可以解决我们企业开发中遇到的难题,它提供了依赖注入(DI)和面向切面编程(AOP)等功能。依赖注入可以简化对象之间的依赖关系管理,面向切面编程可以将一些横切关注点(例如事务管理、日志记录等)从核心业务逻辑中解耦出来。能够让编码变的更加简单,核心组件 IOC容器和Aop面向切面编程。

  1. IOC 控制反转:把整个对象创建的过程,统一交给我们SpringIOC容器来实现管理,底层使用反射+工厂模式实现。
  2. Aop面向切面编程:对我们功能(方法)前后实现增强,比如打印日志、事务原理、权限管理,底层是基于动态代理模式实现的。
    减少到我们的代码的冗余性问题。

Spring优势

  1. 方法的解耦,简化开发;
  2. Aop技术的支持;
  3. 提供声明事务支持
  4. Junit单元测试
  5. 方便整合其他框架(MybatisSpringMVCSpringBootSpringCloudRedis等)
  6. 降低我们的JavaEEapi开发使用的难度(Spring对很多复杂的api接口实现了封装)

Spring与SpringBoot关系
SpringBoot直接采用注解化的方式启动,底层会依赖于Spring/SpringMVC注解方式启动。
总结:SpringBoot底层基于Spring/SpringMVC注解化方式实现包装。

比如

  1. @RestController
  2. @ComponentScan("com.mayikt.aop")
  3. @Configuration
  4. @Component
  5. @Scheduled
  6. @Value
  7. @Bean

1.Spring框架快速入门

Spring的官网
Spring官方下载依赖jar包\

1.1七大核心模块

在这里插入图片描述

test
对应spring-test.jar. Spring提供的测试工具, 可以整合JUnit测试, 简化测试环节.

Core Container
Spring的核心组件, 包含了Spring框架最基本的支撑.
Beans, 对应spring-beans.jar. Spring进行对象管理时依赖的jar包.
Core, 对应spring-core.jar, Spring核心jar包.
Context, 对应spring-context.jar, Spring容器上下文对象.
SpEL, 对应spring-expression.jar, Spring表达式语言.

AOP
面向切面编程, 对应spring-aop.jar.

Data Access
Spring对数据访问层的封装
JDBC, 对应spring-jdbc.jar. Spring对jdbc的封装, 当需要使用spring连接数据库时使用. spring-jdbc.jar需要依赖spring-tx.jar.
Transactions, 对应spring-tx.jar. 事务管理
ORM, 对应spring-orm.jar. spring整合第三方orm框架需要使用的jar包, 例如Hibernate框架.

Web
Spring对javax下的接口或类做的扩展功能.
spring-web.jar, 对Servlet, filter, Listener等做的增强.
spring-webmvc.jar, 实际上就是SpringMVC框架. 需要依赖spring环境和spring-web.jar.

1.1.1 Spring Core

核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。

Maven依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.2.1.RELEASE</version>
</dependency>
1.1.2 Spring-Beans

这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。
外部依赖spring-core,(CGLIB)

<dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.1.RELEASE</version>
</dependency>
1.1.3 Spring Context
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.1.RELEASE</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>5.2.1.RELEASE</version>
</dependency>

Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。

1.1.4 Spring-Expression

模块提供了一个强大的表达式语言,用于在运行时查询和处理对象图。该语言支持设置和获取属性值;属性赋值,方法调用,访问数组的内容,收集和索引器,逻辑和算术运算,命名变量,并从Spring的IOC容器的名字对象检索,它也支持列表选择和投影以及常见的列表聚合。

<dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.2.1.RELEASE</version>
</dependency>
1.1.5 Spring AOP

通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.1.RELEASE</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.1.RELEASE</version>
</dependency>
1.1.6 JDBC和DAO模块(Spring DAO)

JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。

<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.2.1.RELEASE</version>
</dependency>
1.1.7 spring-transaction

以前是在这里org.springframework.transaction
为JDBC、Hibernate、JDO、JPA、Beans等提供的一致的声明式和编程式事务管理支持。

<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.2.1.RELEASE</version>
</dependency>
1.1.8 Spring ORM

Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。

<dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.2.1.RELEASE</version>
</dependency>
1.1.9 Spring Web MVC

MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。

<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.2.1.RELEASE</version>
</dependency>

1.2 项目构建

Maven依赖

<dependencies><!--这个jar 文件包含Spring 框架基本的核心工具类。Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己的应用系统中使用这些工具类。
外部依赖Commons Logging, (Log4J)。--><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.2.1.RELEASE</version></dependency><!--这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。
外部依赖spring-core,(CGLIB)。--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.1.RELEASE</version></dependency><!--
这个jar 文件为Spring 核心提供了大量扩展。可以找到使用Spring ApplicationContext特性时所需的全部类,JDNI 所需的全部类,instrumentation组件以及校验Validation 方面的相关类。
外部依赖spring-beans, (spring-aop)。--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.1.RELEASE</version></dependency></dependencies>

创建spring.xml文件
在 resources 目录下创建

<?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"><!--配置SpringBean对象--><bean id="userEntity" class="com.zhaoli.entity.UserEntity"></bean></beans>

获取Bean对象
在test\java 目录下创建 Test01类

//new UserEntity()
// 1.读取xml配置文件
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
// 2.根据bean的id获取bean对象
UserEntity userEntity = classPathXmlApplicationContext.getBean("userEntity", UserEntity.class);
System.out.println(userEntity);
userEntity.addUser();

2.SpringIOC

IOC容器底层实现原理;

  1. IOC容器中非常核心的接口 BeanFactory
    BeanFactory
    Bean对象 Factory工厂
  2. IOC容器基本的概念:控制反转
    把对象的创建过程与使用统一都交给我们的Spring来进行原理。
    不需要开发者自己去new对象
  3. IOC容器底层实现技术:反射技术、解析xml、工厂模式
  4. IOC作用 降低我们代码的耦合度。

创建对象的方式有那些:

  1. 单独new方式—耦合度太高了
    每次单独new对象,没有实现统一管理对象,如果后期userDao的名称信息发生变化的情况下,需要改变的引用地方比较多,耦合度太高。
  2. 工厂模式—降低我们耦合度
    概念:统一的管理和维护我们每个对象创建与使用的过程。
    不需要自己new对象。
  3. 反射的方式
    降低代码的-耦合度

com.zhaoli.dao—数据库访问层;
com.zhaoli.service—业务逻辑层;
业务逻辑层调用到数据库访问层

2.1反射创建对象

SpringIOC容器底层实现原理:
反射+工厂模式+解析xml技术实现

  1. 使用解析xml技术 解析spring.xml配置文件;
  2. 获取 类的完整路径地址
  3. 使用到反射技术初始化对象
  4. 需要使用工厂模式封装初始化对象

UserDaoFactory

public class UserDaoFactory {public static UserDao getUserDao(){return new UserDao();}
}
UserFactort
public class UserFactort {public static UserEntity getUserEntity() throws ClassNotFoundException, InstantiationException, IllegalAccessException {//使用到反射技术初始化对象Class<?> aClass = Class.forName("com.zhaoli.entity.UserEntity");//默认执行的是无参构造函数UserEntity userEntity = (com.zhaoli.entity.UserEntity) aClass.newInstance();return userEntity;}
}

使用解析 xml 技术解析 spring.xml 配置文件 应用 dom4j 技术
UserFactort

public class UserFactort {public static UserEntity getUserEntity() throws ClassNotFoundException, InstantiationException, IllegalAccessException, DocumentException {//使用解析 xml 技术解析 spring.xml 配置文件 应用 dom4j 技术String userClass = new Dom4jClass().getUserClass();//使用到反射技术初始化对象Class<?> aClass = Class.forName(userClass);//默认执行的是无参构造函数UserEntity userEntity = (com.zhaoli.entity.UserEntity) aClass.newInstance();return userEntity;}
}

com/zhaoli/utils/Dom4jClass.java

public class Dom4jClass {public String getUserClass() throws DocumentException {//this.getClass().getResource() 动态获取到 resources 的绝对路径File xmlFile = new File(this.getClass().getResource("/") + "spring.xml");SAXReader saxReader = new SAXReader();Document document = saxReader.read(xmlFile);//获取到根节点Element rootElement = document.getRootElement();//获取到 beans 中的 bean 标签Element bean = rootElement.element("bean");String aClass = bean.attributeValue("class");return aClass;}
}

Test02

UserEntity userEntity = UserFactort.getUserEntity();
System.out.println(userEntity);
userEntity.addUser();

2.2 IOC核心的接口

  1. IOC的核心思想底层基于反射+工厂模式实现
  2. Spring提供IOC容器实现两种方式:
    • BeanFactory IOC容器基本的实现,是spring内部自己使用的接口,不提供给开发者使用。
      加载配置文件过程的时候,不会创建对象,当我们在获取对象的时候才会获取创建对象。
    • ApplicationContext 是 BeanFactory 接口的子接口,提供更多的强大功能,适合于开发者使用。当我们在加载配置文件的过程中,就会将配置文件中的对象创建。

在做服务器端开发的时候,使用ApplicationContext 比较多,因为所有bean初始化操作在项目启动完成之前都已经初始化了。

2.3 ApplicationContext主要实现类

ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
ConfigurableApplicationContextApplicationContext的子接口,包含一些扩展方法
refresh()close()ApplicationContext具有启动、关闭和刷新上下文的能力。所以要关闭ApplicationContext需要new此接口的对象调用close()方法
WebApplicationContext 专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作

3.SpringBean的注入方式

3.1 创建对象和set方法、有参构造函数注入属性

  1. 什么是Bean管理
    使用spring创建对象、使用spring注入属性
  2. Bean的管理有两种方式
  • 基于XML方式配置
<bean id="userEntity" class="com.zhaoli.entity.UserEntity"></bean>

在spring的配置文件中,会配置一个bean标签,注入bean的信息,创建bean对象
Id:获取bean对象,唯一bean对象的名称; bean的名称不允许重复
Class属性:类的完整路径地址(类名称+包名称)
默认底层使用反射技术执行无参数构造函数

  • 基于XML方式注入属性
    DI 依赖注入: 对象的属性注入值; (spring实现)

(1)第一种实现方式:基于对象属性set方法实现

<!-- set方法注入属性值 -->    
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity"><property name="bookName" value="面试宝典" ></property><property name="bookPrice" value="88.8" ></property>
</bean>

在Bean 标签下再定义一个属性标签
name:类中的属性名称
value:需要注入的属性值

(2)第二种实现方式:有参构造函数注入属性
实例类 OrderEntity

public class OrderEntity {private String orderId;private String orderName;public OrderEntity() {}public OrderEntity(String orderId, String orderName) {this.orderId = orderId;this.orderName = orderName;System.out.println("反射机制执行到有参构造函数:"+orderId+";"+orderName);}@Overridepublic String toString() {return "OrderEntity{" +"orderId='" + orderId + '\'' +", orderName='" + orderName + '\'' +'}';}
}

Xml配置文件 order.xml

<!-- 第一种方式 指定参数列表名称注入参数 -->
<bean id="orderEntity" class="com.zhaoli.entity.OrderEntity"><constructor-arg name="orderId" value="123456"></constructor-arg><constructor-arg name="orderName" value="赵立"></constructor-arg>
</bean>
<!-- 第二种方式 指定参数列表索引注入参数 -->
<bean id="orderEntity" class="com.zhaoli.entity.OrderEntity"><constructor-arg index="0" value="123456"></constructor-arg><constructor-arg index="1" value="赵立"></constructor-arg>
</bean>

(3)第三种实现方式:p名称空间注入(应用较少)
xml头部引入P标签
xmlns:p="http://www.springframework.org/schema/p"
使用p标签注入属性:

<!-- p标签方式注入属性值 实际上最终还是走set方法   -->
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity" 
p:bookName="面试宝典aaa" p:bookPrice="99.9"></bean>

3.2注入空值和特殊符号

1.注入空值属性

<bean id="bookEntity" class="com.zhaoli.entity.BookEntity"><property name="bookName"><null></null></property><property name="bookPrice" value="88.8" ></property>
</bean>

给bookName赋值为空

2.注入特殊符号
(1)转义注入方式
<< 转移为:&lt;&lt;
>>转移为:&gt;&gt;

<!-- 注入特殊符号 例如 < > -->
<!-- (1)转义注入方式 -->
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity">
<!--    <property name="bookName" value="<<面试宝典>>" ></property>--><property name="bookName" value="&lt;&lt;面试宝典&gt;&gt;" ></property><property name="bookPrice" value="88.8" ></property>
</bean>

(2)cdata注入方式

<![CDATA[ 需要注入的内容  ]]>
<!-- (2)cdata注入方式 -->
<bean id="bookEntity" class="com.zhaoli.entity.BookEntity"><property name="bookName"><value><![CDATA[<<面试宝典aaa>>]]></value></property><property name="bookPrice" value="88.8" ></property>
</bean>

3.3注入属性外部bean

com.zhaoli.dao.MemberDao(接口)

public interface MemberDao {void addMember();
}
com.zhaoli.dao.MemberDaoImpl(实现类)
public class MemberDaoImpl implements MemberDao{@Overridepublic void addMember() {System.out.println(">>MemberDao.addMember()<<");}
}
com.zhaoli.service.MemberService
public class MemberService {/*** 使用到属性注入的方式*/private MemberDao memberDao;public void setMemberDao(MemberDao memberDao) {this.memberDao = memberDao;}public void addMember() {System.out.println(">MemberService.addMember()<");memberDao.addMember();}
}

Resources\member.xml

<!-- 将memberService注入到ioc容器中 -->
<bean id="memberService" class="com.zhaoli.service.MemberService"><!--name="memberDao" MemberService类中属性的名称ref="memberDao" 在ioc容器中注入的 beanid--><property name="memberDao" ref="memberDao"></property>
</bean>
<!-- 将memberDao注入到ioc容器中 -->
<bean id="memberDao" class="com.zhaoli.dao.MemberDaoImpl"></bean>

Test

public static void main(String[] args) {ClassPathXmlApplicationContext app = newClassPathXmlApplicationContext("member.xml");MemberService memberService = (MemberService) app.getBean("memberService");memberService.addMember();}

3.4注入内部bean(应用较少)

  1. 数据库表 一对多或者一对一的关系
  2. 部门–n多个员工 一对多
  3. 站在员工角度考虑 员工属于那个部门
  4. 站在部门的角度考虑 部门下n多个员工
    员工对象 EmpEntity
public class EmpEntity {private String name;private String addres;//住址/*** 员工属于那个部门*/
private DeptEntity deptEntity;
}

部门对象 DeptEntity

public class DeptEntity {private String name;//部门名称
}

Resources\spring_02.xml

<!-- 注入内部bean对象 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity"><property name="name" value="赵立"></property><property name="addres" value="陕西省西安市"></property>
<!-- 注入内部bean对象 --><property name="deptEntity"><bean id="deptEntity" class="com.zhaoli.entity.DeptEntity"><property name="name" value="开发部门"></property></bean></property>
</bean>

Test

public static void main(String[] args) {ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_02.xml");EmpEntity empEntity = (EmpEntity) app.getBean("empEntity");System.out.println(empEntity);
}

注意:两个实体类都要生成 set()方法。

3.5注入级联赋值

实体类 员工对象和部门对象同3.4
写法一
Resources\spring_03.xml

<!-- 级联赋值的形式 --><bean id="empEntity" class="com.zhaoli.entity.EmpEntity"><property name="name" value="赵立"></property><property name="addres" value="陕西省西安市"></property><!-- 级联赋值的形式 --><property name="deptEntity" ref="deptEntity"></property></bean><!-- 注入部门对象 -->
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity">
<property name="name" value="教育部门"></property>
</bean>

写法二
Resources\spring_03.xml

<!-- 级联赋值的形式 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity"><property name="name" value="赵立"></property><property name="addres" value="陕西省西安市"></property><!-- 级联赋值的形式 --><property name="deptEntity" ref="deptEntity"></property><property name="deptEntity.name" value="IT部门"></property>
</bean>
<!-- 注入部门对象 -->
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity"></bean>

注意:需要在员工实体类新增:deptEntityGet()方法。

3.6注入集合类型属性

实体类 StuEntity

public class StuEntity {private String[] arrays;private List<String> list;private Map<String, String> map;private Set<String> set;
}

Resources\spring_04.xml

<bean id="stuEntity" class="com.zhaoli.entity.StuEntity"><!-- 对 arrays 属性赋值 --><property name="arrays"><array><value>arrays000</value><value>arrays111</value></array></property><!-- 对 list 属性赋值 --><property name="list"><list><value>list000</value><value>list111</value></list></property><!-- 对 map属性赋值 --><property name="map"><map><entry key="000" value="赵立"></entry><entry key="111" value="樊靖"></entry></map></property><!-- 对 set 属性赋值 --><property name="set"><set><value>set000</value><value>set111</value></set></property>
</bean>

Test

public static void main(String[] args) {ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_04.xml");StuEntity stuEntity = (StuEntity) app.getBean("stuEntity");System.out.println(stuEntity);}

3.7注入集合类型为对象属性

实体类 StuEntity

public class StuEntity {private String[] arrays;private List<String> list;private Map<String, String> map;
private Set<String> set;
/**
* 一名学生可以上 n 多门课程
*/
private List<CourseEntity> courses;
}
实体类  CourseEntity
public class CourseEntity {
private String name;
}

Resources\spring_04.xml

<bean id="stuEntity" class="com.zhaoli.entity.StuEntity"><!-- 对 arrays 属性赋值 --><property name="arrays"><array><value>arrays000</value><value>arrays111</value></array></property><!-- 对 list 属性赋值 --><property name="list"><list><value>list000</value><value>list111</value></list></property><!-- 对 map属性赋值 --><property name="map"><map><entry key="000" value="赵立"></entry><entry key="111" value="樊靖"></entry></map></property><!-- 对 set 属性赋值 --><property name="set"><set><value>set000</value><value>set111</value></set></property><!-- 对 courses 属性赋值(集合的类型为对象) --><property name="courses"><list><ref bean="courseEntity_java"></ref><ref bean="courseEntity_dsj"></ref></list></property>
</bean>
<!-- java课程 -->
<bean id="courseEntity_java" class="com.zhaoli.entity.CourseEntity"><property name="name" value="java课程"></property>
</bean>
<!-- 大数据 -->
<bean id="courseEntity_dsj" class="com.zhaoli.entity.CourseEntity"><property name="name" value="大数据"></property>
</bean>

Test

public static void main(String[] args) {ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_04.xml");StuEntity stuEntity = (StuEntity) app.getBean("stuEntity");System.out.println(stuEntity);
}

3.8集合注入部分提取公共

xml头部引入util标签

<?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:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsd">
使用util标签提取公共部分:
<!-- 提取公共部分 -->
<util:list id="list"><value>zhaoli001</value><value>zhaoli002</value>
</util:list><bean id="stuEntity" class="com.zhaoli.entity.StuEntity"><!-- 对 list 属性赋值 --><property name="list" ref="list"></property>
</bean>

3.9 IOC操作Bean的管理

  1. Spring中两种类型bean,一种是为普通的bean,另外一种是工厂bean FactoryBean
  2. 普通Bean:在配置文件中定义什么类型与返回的类型需一致;
  3. 工厂Bean:在配置文件中定义Bean类型与返回类型可以不一致;

创建一个类,这个类是为工厂Bean,实现FactoryBean接口

4.Spring的工厂Bean

4.1 SpringBean的作用域

什么是作用域?设定bean作用域是为单例还是多例
作用域单例与多例有什么区别呢?

  1. 单例的作用域:每次在调用getbean方法获取对象都是为同一个对象。
  2. 多例的作用域:每次在调用getbean方法获取对象都是一个新的对象。

注意:在spring默认的情况下,bean的作用域就是为单例,节约服务器内存。
单例:在同一个jvm中,该bean对象只会创建一次。
多例:在同一个jvm中,该bean对象可以被创建多次。

设定对象单例还是多例
在spring的默认的情况下,springbean的作用域为单例。

  1. 单例就是每次获取bean都是同一个对象;
  2. 多例就是每次获取bean都是新的一个对象;
    单例:在同一个jvm中该bean只能存在一个实例;
    多例:在同一个jvm中该bean存在多个实例;
    证明:如果是为单例,则两个对象地址都是一样的,多例子对象则两个对象地址不一样。

单例配置:(默认就是为单例子)
<bean id="userEntity" class="com.mayikt.entity.UserEntity" scope="singleton"></bean>
多例配置:
<bean id="userEntity" class="com.mayikt.entity.UserEntity" scope="prototype"></bean>

4.2 SpringBean的生命周期

简单分为:实例化→属性赋值→初始化→销毁

生命周期概念:对象的创建与销毁的过程,类似之前学习servlet生命的周期过程。

生命周期的原理:

  1. 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
  2. 为bean的属性设置(使用反射调用set方法)
  3. 调用bean的初始化的方法(需要单独在类中配置初始化的方法)
  4. 正常使用bean对象
  5. Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)

com.zhaoli.entity.MemberEntity

public class MemberEntity {private String name;public MemberEntity(){System.out.println("[第一步流程:]执行MemberEntity无参构造函数");}public void setName(String name) {System.out.println("[第二步流程:]执行setName方法");this.name = name;}public void init(){System.out.println("[第三步流程:]调用init方法");}public void destroy(){System.out.println("[第五步流程:]调用destroy方法");}
}

Resources\spring_06.xml

<!--初始化的方法  init-method="init"销毁回调的方法  destroy-method="destroy"-->
<bean id="memberEntity" class="com.zhaoli.entity.MemberEntity" init-method="init" destroy-method="destroy"><property name="name" value="赵立"></property>
</bean>

test

public static void main(String[] args) {ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring_06.xml");MemberEntity memberEntity = app.getBean("memberEntity", MemberEntity.class);System.out.println("[第四步流程:]获取使用到的memberEntity");app.close();//手动让 bean 容器销毁
}

在这里插入图片描述

Bean的后置处理器 作用提供更多的扩展功能 BeanPostProcessor

  1. 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
  2. 为bean的属性设置 (使用反射调用set方法)
  3. 将bean传递给后置处理器 调用初始化方法之前执行
  4. 调用bean的初始化的方法(需要单独在类中配置初始化的方法)
  5. 将bean传递给后置处理器 调用初始化方法之后执行
  6. 正常使用bean对象
  7. Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)

com.zhaoli.entity.MemberEntity 同上
com.zhaoli.bean.MyPostProcessor

public class MyPostProcessor implements BeanPostProcessor {/*** 调用 init 方法之前处理*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("[后置处理器调用init方法之前执行操作..]");return bean;}/*** 调用 init 方法之后处理*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("[后置处理器调用init方法之后执行操作..]");return bean;}
}

Resources\spring_06.xml (加上下面内容)

<!-- 注册 bean 对象的后置处理器 MyPostProcessor  -->
<bean id="myPostProcessor" class="com.zhaoli.bean.MyPostProcessor"></bean>

Test 同上
在这里插入图片描述

配置多个 BeanPostProcessor

实现Ordered接口重写 getOrder()方法 返回的值越小越优先加载
com.zhaoli.bean.MyPostProcessor

public class MyPostProcessor implements BeanPostProcessor, Ordered {/*** 调用 init 方法之前处理*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("[后置处理器调用init方法之前执行操作..]");return bean;}/*** 调用 init 方法之后处理*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("[后置处理器调用init方法之后执行操作..]");return bean;}@Overridepublic int getOrder() {return 1;}
}

com.zhaoli.bean.MyPostProcessor2

public class MyPostProcessor2 implements BeanPostProcessor, Ordered {/*** 调用 init 方法之前处理*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("[MyPostProcessor2:][后置处理器调用init方法之前执行操作..]");return bean;}/*** 调用 init 方法之后处理*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("[MyPostProcessor2:][后置处理器调用init方法之后执行操作..]");return bean;}@Overridepublic int getOrder() {return 0;}
}

Resources\spring_06.xml

<!--初始化的方法  init-method="init"销毁回调的方法  destroy-method="destroy"
-->
<bean id="memberEntity" class="com.zhaoli.entity.MemberEntity" init-method="init" destroy-method="destroy"><property name="name" value="赵立"></property>
</bean>
<!-- 注册 bean 对象的后置处理器 MyPostProcessor  -->
<bean id="myPostProcessor" class="com.zhaoli.bean.MyPostProcessor"></bean>
<!-- 注册 bean 对象的后置处理器 MyPostProcessor2  -->
<bean id="myPostProcessor2" class="com.zhaoli.bean.MyPostProcessor2"></bean>

Test 同上
在这里插入图片描述

4.3 SpringBean的自动装配

bean 标签中有一个属性 autowire

  1. byName 根据属性的名称自动装配 bean的id名称与属性的名称一致
  2. byType 根据属性的类型自动装配 bean的类型与属性类型一致(不能配置多个部门对象)

员工对象 EmpEntity

public class EmpEntity {private String name;private String addres;//住址/*** 员工属于那个部门*/
private DeptEntity deptEntity;
}

部门对象 DeptEntity

public class DeptEntity {
private String name;//部门名称
}

根据属性的名称自动装配 bean的id名称与属性的名称一致

<!-- 注入内部bean对象 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity" autowire="byName"><property name="name" value="赵立"></property><property name="addres" value="陕西省西安市"></property>
</bean><!-- 注入部门对象  -->
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity"><property name="name" value="开发部门"></property>
</bean>

根据属性的类型自动装配 bean的类型与属性类型一致

<!-- 注入内部bean对象 -->
<bean id="empEntity" class="com.zhaoli.entity.EmpEntity" autowire="byType"><property name="name" value="赵立"></property><property name="addres" value="陕西省西安市"></property>
</bean><!-- 注入部门对象  -->
<bean id="deptEntity" class="com.zhaoli.entity.DeptEntity"><property name="name" value="开发部门"></property>
</bean>

5.SpringBean的AOP

AOP基本的概念
AOP(Aspect Oriented Programming)是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,即解剖对象的内部,将那些影响了多个类的公共行为抽取到一个可重用模块里,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性。

AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理、增强处理。

简单理解:
Aop面向切面编程,在方法之前和之后实现处理 应用场景在于:日志打印、事务实现、安全、权限控制、自定义注解等。 因为AOP可以解决我们程序上的代码冗余问题。

AOP 底层基于代理设计模式封装
代理设计模式 静态代理与动态代理
动态代理 jdk动态代理与 cglib动态代理

通俗易懂 aop 在我们的目标方法之前和之后处理的操作
开启事务
目标方法
提交或者回滚事务

5.1代理模式

代理模式应用场景

  1. 日志的采集
  2. 权限控制
  3. 实现aop
  4. Mybatis mapper
  5. Spring的事务
  6. 全局捕获异常
  7. Rpc远程调用接口
  8. 代理数据源

代理模式实现的原理
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy)
抽象主题角色:可以是接口,也可以是抽象类;
委托类角色:真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。

5.1.1 代理模式创建方式

com.mayikt.service.OrderService(接口)

public interface OrderService {/*** 添加订单数据*/String addOrder(String orderName);
}
com.mayikt.service.impl.OrderServiceImpl
import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;@Slf4j
public class OrderServiceImpl implements OrderService {@Overridepublic String addOrder(String orderName) {log.info("<orderName:{}>", orderName);return "ok";}
}
5.1.2 静态代理

基于接口实现方式
com.mayikt.proxy1.OrderServiceProxy

import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderServiceProxy implements OrderService {private OrderService orderService;public OrderServiceProxy(OrderService orderService) {this.orderService = orderService;}@Overridepublic String addOrder(String orderName) {// 目标方法前后处理操作log.info("<目标方法之前执行...>");String result = orderService.addOrder(orderName);log.info("<目标方法之后执行...>");return result;}
}

Tses

public static void main(String[] args) {OrderServiceProxy orderServiceProxy = new OrderServiceProxy(new OrderServiceImpl());String result = orderServiceProxy.addOrder("mayikt");System.out.println(result);}

基于继承实现方式
com.mayikt.proxy2.OrderServiceProxy

import com.mayikt.service.impl.OrderServiceImpl;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderServiceProxy  extends OrderServiceImpl {@Overridepublic String addOrder(String orderName) {// 目标方法前后处理操作log.info("<目标方法之前执行...>");String result = super.addOrder(orderName);log.info("<目标方法之后执行...>");return result;}
}

Tses

 public static void main(String[] args) {OrderServiceProxy orderServiceProxy = new OrderServiceProxy();String result = orderServiceProxy.addOrder("mayikt");System.out.println(result);
}
5.1.3 动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。

JDK动态代理的一般步骤如下:

  1. 创建被代理的接口和类;
  2. 实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;
  3. 调用Proxy的静态方法,创建代理类并生成相应的代理对象;

实现原理:利用拦截器机制必须实现InvocationHandler接口中的invoke方法实现对我们的目标方法增强。

5.2 AOP详解

5.2.1 Aop常用术语
  1. 连接点(Join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。类中的哪些方法可以被增强,这些方法就被称作为连接点。
  2. 切点(PointCut): 可以插入增强处理的连接点,实际被增强的方法就称作为切入点
  3. 通知(Advice): AOP 框架中的增强处理,通知描述了切面何时执行以及如何执行增强处理, 实际增强的业务逻辑,该过程就可以称作为通知 前置、后置、环绕通知
  4. 切面(Aspect): 切面是通知和切点的结合。 把通知应用到的过程 就是为切面
  5. 引入(Introduction):允许我们向现有的类添加新的方法或者属性。
  6. 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的代理对象

  1. 连接点 该类中哪些方法需要被增强,这些方法就可以称作连接点
  2. 切点 实际被增强的方法
    2 .通知 在方法前后执行代码
    前置通知 调用方法之前处理…
    后置通知 调用完该方法之后处理
    环绕通知 在我们被代理方法前后执行
    异常通知
    最终通知
    4 .切面 把通知应用到的过程 就是为切面
5.2.2 Aop环境准备
  1. Spring框架一般都是基于AspectJ实现AOP操作

(1)什么是AspectJ
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件,AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作.

  1. 基于AspectJ实现AOP

(1)基于xml配置文件实现
(2)基于注解方式(偏多的)

  1. 在项目工程目录引入AOP依赖

maven依赖

<!-- aspectj支持(切面依赖) -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.9</version>
</dependency>
<dependency><groupId>org.apache.geronimo.bundles</groupId><artifactId>aspectjweaver</artifactId><version>1.6.8_2</version>
</dependency>
<!-- SpringAOP依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.1.RELEASE</version>
</dependency>

切入点表达式
具体那个类中的那个方法来实现增强
需要描述 该类中哪些方法是需要被增强-----切入点规则

在这里插入图片描述

语法规范execution( [权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]));
例如:

  1. public.String.com.zhaoli.service.MayiktService.addUser(..) --拦截的是MayiktService类中addUser方法名称 所有参数 返回值String
    2.* com.zhaoli.service.MayiktService.*(..)拦截我们的MayiktService类中的所有方法
    3.* com.zhaoli.service.*.*(..)拦截就是我们 com.mayikt.service.包下的所有的类所有的方法。

//举例1:对com.zhaoli.service.MayiktService类里面的 add() 进行增强 execution(*com.zhaoli.service.MayiktService.add(..)); // * 表示所有, … 表示参数列表
//举例2:对com.zhaoli.service.MayiktService类里面的 所有方法 进行增强 execution(*com.zhaoli.service.MayiktService.*(..));
//举例3:对com.zhaoli.service.MayiktService所有类里面的 所有方法 进行增强 execution(*com.zhaoli.service.MayiktService.*.*(..));

开启springaop
这是配置 spring.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">

注意粘贴时将这一段代码替换掉

测试代码(前置通知\后置通知)
com.zhaoli01.service.MayiktService

import org.springframework.stereotype.Component;@Component //等同于 <bean id="userProxy" class="com.zhaoli01.prox.UserProxy">
public class MayiktService {public String addUser() {System.out.println("addUser...");return "ok";}
}

com.zhaoli01.proxy.UserProxy

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Component //等同于 <bean id="userProxy" class="com.zhaoli01.prox.UserProxy">
@Aspect //定义切面
public class UserProxy {/*** //@Before(value="定义切入点") 拦截我们 MayiktService 类中的所有方法* 前置通知*/@Before(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")public void before() {System.out.println("在目标方法之前执行...");}/*** 后置通知*/@After(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")public void after() {System.out.println("在目标方法之后执行...");}
}

Resources\spring_08.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 定义扫包范围 --><context:component-scan base-package="com.zhaoli01"></context:component-scan><!-- 开启切面 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

test`

public static void main(String[] args) {ClassPathXmlApplicationContext classPathXmlApplicationContext= new ClassPathXmlApplicationContext("spring_08.xml");MayiktService mayiktService = classPathXmlApplicationContext.getBean("mayiktService", MayiktService.class);mayiktService.addUser();
}

测试代码(环绕通知)
com.zhaoli01.service.MayiktServiceResources\spring_08.xml 同上
com.zhaoli01.proxy.UserProxy

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Component //等同于 <bean id="userProxy" class="com.zhaoli01.prox.UserProxy">
@Aspect //定义切面
public class UserProxy {/*** //@Before(value="定义切入点") 拦截我们 MayiktService 类中的所有方法* 前置通知*/@Before(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")public void before() {System.out.println("在目标方法之前执行...");}/*** 后置通知*/@After(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")public void after() {System.out.println("在目标方法之后执行...");}/*** 环绕通知 前置+后置组合*/@Around(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕通知调用目标方法之前...");Object result = proceedingJoinPoint.proceed();//调用我们目标方法System.out.println("环绕通知调用目标方法之后...");return result;}
}

返回通知、表达异常通知

//@AfterReturning表达后置通知/返回通知,表达方法返回结果之后执行@AfterReturning(value = "execution(* com.mayikt.service.MayiktService.addMayikt(..));")public void afterReturning() {System.out.println("afterReturning");}//@AfterThrowing表达异常通知@AfterThrowing(value = "execution(* com.mayikt.service.MayiktService.addMayikt(..));")public void afterThrowing() {System.out.println("afterThrowing");}

代码优化

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component //等同于 <bean id="userProxy" class="com.zhaoli01.prox.UserProxy">
@Aspect //定义切面
public class UserProxy {/*** 定义切入点 用的时候直接调用 减少代码冗余*/@Pointcut(value = "execution(* com.zhaoli01.service.MayiktService.*(..));")private void pointcut() {}/*** //@Before(value="定义切入点") 拦截我们 MayiktService 类中的所有方法* 前置通知*/@Before(value = "pointcut()")public void before() {System.out.println("在目标方法之前执行...");}/*** 后置通知*/@After(value = "pointcut()")public void after() {System.out.println("在目标方法之后执行...");}/*** 环绕通知 前置+后置组合*/@Around(value = "pointcut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕通知调用目标方法之前...");Object result = proceedingJoinPoint.proceed();//调用我们目标方法System.out.println("环绕通知调用目标方法之后...");return result;}
}

spring框架中使用 cglib?jdk动态代理?
spring aop 底层基于代理模式封装
如果我们被代理类 有实现接口的情况下 则使用 jdk动态代理

如果我们 被代理类 没有实现接口的情况下 则使用 cglib动态代理

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

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

相关文章

C语言 | Leetcode C语言题解之第412题Fizz Buzz

题目&#xff1a; 题解&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/ char ** fizzBuzz(int n, int* returnSize) {/*定义字符串数组*/char **answer (char**)malloc(sizeof(char*)*n);for(int i 1;i<n;i){/*分配单个字符串…

visual prompt tuning和visual instruction tuning

visual prompt tuning&#xff1a;作为一种微调手段&#xff0c;其目的是节省参数量&#xff0c;训练时需要优化的参数量小。 输入&#xff1a;视觉信息image token可学习的prompt token 处理任务&#xff1a;比如常见的分类任务 visual prompt tuning visual instruction tu…

Microsoft 365 Copilot: Wave 2

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Python3将Excel数据转换为文本文件

文章目录 python3安装使用Python将Excel数据转换为文本文件&#xff1a;逐步指南openpyxl库简介前提条件脚本解析代码详细解析实际应用场景使用示例 结论 python3安装 centos安装python3 Python3基础知识 使用Python将Excel数据转换为文本文件&#xff1a;逐步指南 在数据处理…

闯关leetcode——27. Remove Element

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/remove-element/description/ 内容 Given an integer array nums and an integer val, remove all occurrences of val in nums in-place. The order of the elements may be changed. Then retur…

Docker 消息队列RabbitMQ 安装延迟消息插件

介绍 RabbitMQ的官方推出了一个插件&#xff0c;原生支持延迟消息功能。该插件的原理是设计了一种支持延迟消息功能的交换机。当消息投递到交换机后可以暂存一定时间&#xff0c;到期后再投递到队列。 查看版本号 docker exec rabbit名字 rabbitmqctl version根据版本下载 插…

Java | Leetcode Java题解之第412题Fizz Buzz

题目&#xff1a; 题解&#xff1a; class Solution {public List<String> fizzBuzz(int n) {List<String> answer new ArrayList<String>();for (int i 1; i < n; i) {StringBuffer sb new StringBuffer();if (i % 3 0) {sb.append("Fizz"…

启动windows更新/停止windows更新,在配置更新中关闭自动更新的方法

在Windows操作系统中&#xff0c;启动或停止Windows更新&#xff0c;以及调整“配置更新”的关闭方法&#xff0c;涉及多种途径&#xff0c;这里将详细阐述几种常用的专业方法。 启动Windows更新 1.通过Windows服务管理器&#xff1a; -打开“运行”对话框&#xff08;…

《小迪安全》学习笔记04

这一块主要讲信息收集——渗透测试第一步&#xff01;&#xff01; 1.首先看有无网站&#xff1a; 存在CDN就用上次说的方法找到真实IP&#xff0c;然后转上↑ 收集四类信息&#xff1a;程序源码&#xff08;CMS&#xff09;等等 2.看有无APP&#xff0c;如涉及到WEB&#xf…

opencv学习:图像掩码处理和直方图分析及完整代码

图像掩码是一种二值图像&#xff0c;用于控制图像处理操作的应用区域。通过将掩码与原图像进行按位与操作&#xff0c;可以提取或屏蔽图像的特定部分。直方图是图像处理中的一个重要工具&#xff0c;用于分析图像的灰度分布。 实验步骤 使用OpenCV读取图像文件“phone.png”为…

JS基础之【对象详解 -- 对象的属性与方法、遍历对象与内置对象】

&#x1f680; 个人简介&#xff1a;某大型国企高级前端开发工程师&#xff0c;7年研发经验&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码…

后台数据管理系统 - 项目架构设计-Vue3+axios+Element-plus(0916)

接口文档: https://apifox.com/apidoc/shared-26c67aee-0233-4d23-aab7-08448fdf95ff/api-93850835 接口根路径&#xff1a; http://big-event-vue-api-t.itheima.net 本项目的技术栈 本项目技术栈基于 ES6、vue3、pinia、vue-router 、vite 、axios 和 element-plus http:/…

Linux 基本使用和 web 程序部署 ( 8000 字 Linux 入门 )

一&#xff1a;Linux 背景知识 1.1. Linux 是什么 Linux 是一个操作系统. 和 Windows 是 “并列” 的关系&#xff0c;经过这么多年的发展, Linux 已经成为世界第一大操作系统&#xff0c;安卓系统本质上就是 Linux. 1.2 Linux 发行版 Linux 严格意义来说只是一个 “操作系…

Parallels Desktop 20 搭载内置 AI 包正式发布

近期 Parallels 官方发布 Parallels Desktop 20 For Mac&#xff0c;这是一款流行的Mac虚拟化软件。 该版本引入了 Parallels AI 工具包&#xff0c;这将主要惠及需要新工具在 Mac 上使用人工智能的开发人员。 配合即将发布的 macOs sequoia 和 Windows 11 24H2&#xff0c;P…

Android源码集成 Google Play

1、 The Open GApps Project 下载 对应系统和平台的gms压缩包&#xff0c;压缩包文件如下&#xff1a; Core和Optional目录下为谷歌服务核心文件和应用&#xff0c;需要预置到源码中。解压Core 和 Optional 目录所有文件&#xff0c;得到如下文件夹&#xff1a; 其中 etc 和 li…

C++ | Leetcode C++题解之第412题Fizz Buzz

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<string> fizzBuzz(int n) {vector<string> answer;for (int i 1; i < n; i) {string curr;if (i % 3 0) {curr  "Fizz";}if (i % 5 0) {curr "Buzz";}if (curr.si…

Django学习实战篇五(适合略有基础的新手小白学习)(从0开发项目)

前言&#xff1a; 本章中&#xff0c;我们开始引入前端框架Bootstrap 来美化界面。在前面的章节中&#xff0c;我们通过编写后端代码来处理数据。数据之于网站&#xff0c;就相当于灵魂之于人类。而网站的前端就相当于人的形体外貌。其中HTML是骨架&#xff0c;而CSS是皮肤&…

创造增强叙事的互动:Allison Crank的沉浸式体验设计理念

在沉浸式技术日新月异的今天,如何通过用户交互增强叙事,而非分散注意力,成为了设计师们共同面临的挑战。作为用户体验设计师和研究员,Allison Crank以其独特的视角和丰富的经验,为我们揭示了这一领域的核心原则与实践方法。 叙事与互动的和谐共生 Allison Crank强调,互…

STM32使用ESP-01S连接WiFi通过云平台与手机APP联动

ESP-01S测试 我们买回来ESP-01S模块之后&#xff0c;首先要对模块的好坏进行测试&#xff0c;是否能正常通过串口通信&#xff0c;是否能正常连接手机热点或WiFi&#xff0c;一般模块买回来默认出厂烧录了AT固件&#xff0c;如果我们需要使用一些特殊的协议&#xff08;如MQTT协…

【云岚到家-即刻体检】-day07-2-项目介绍及准备

【云岚到家-即刻体检】-day07-2-项目介绍及准备 1 项目介绍1&#xff09;项目简介2&#xff09;界面原型3&#xff09;实战目标 2 搭建实战环境1&#xff09;服务端2&#xff09;管理端前端工程3&#xff09;用户端前端工程4&#xff09;测试 3 熟悉项目代码1&#xff09;接口文…