Spring详解

文章目录

      • 一、引言
        • 1.1 原生web开发中存在哪些问题?
      • 二、Spring框架
        • 2.1 概念
        • 2.2 访问与下载
      • 三、Spring架构组成
      • 四、自定义工厂
        • 4.1 配置文件
        • 4.2 工厂类
      • 五、构建Maven项目
        • 5.1 新建项目
        • 5.2 选择Maven目录
        • 5.3 GAV坐标
      • 六、Spring环境搭建
        • 6.1 pom.xml中引入Spring常用依赖
        • 6.2 创建Spring配置文件
      • 七、Spring工厂编码
      • 八、依赖与配置文件详解
        • 8.1 Spring依赖关系
        • 8.2 schema
      • 九、IoC(Inversion of Control )控制反转【`重点`】
        • 9.1 项目中强耦合问题
        • 9.2 解决方案
      • 十、DI(Dependency Injection)依赖注入【`重点`】
        • 10.1 概念
        • 10.2 Set注入
          • 10.2.1 定义目标Bean类型
          • 10.2.2 基本类型 + 字符串类型 + 日期类型
          • 10.2.3 容器类型
          • 10.2.4 自建类型
        • 10.3 构造注入【了解】
          • 10.3.1 定义目标Bean类型
          • 10.3.2 注入
        • 10.4 自动注入【了解】
      • 十一、Bean细节
        • 11.1 控·制简单对象的单例、多例模式
        • 11.2 FactoryBean创建复杂对象【了解】
          • 11.2.1 实现FactoryBean接口
          • 11.2.2 配置spring-context.xml
          • 11.2.3 特例
      • 十二、Spring工厂特性
        • 12.1 饿汉式创建优势
        • 12.2 生命周期方法
        • 12.3 生命周期注解
        • 12.4 生命周期阶段
      • 十三、代理设计模式
        • 13.1 概念
        • 13.2 静态代理设计模式
        • 13.3 动态代理设计模式
          • 13.3.1 JDK动态代理实现(基于接口)
          • 13.3.2 CGlib动态代理实现(基于继承)
      • 十四、面向切面编程【`重点`】
        • 14.1 概念
        • 14.2 AOP开发术语
        • 14.3 作用
        • 14.4 环境搭建
        • 14.5 开发流程
        • 14.6 AOP小结
        • 14.7 通知类【可选】
        • 14.8 通配切入点
        • 14.9 JDK和CGLIB选择
        • 14.10 后处理器
          • 14.10.1 后处理器定义
          • 14.10.2 配置后处理器
          • 14.10.3 bean生命周期
          • 14.10.4 动态代理源码(了解)
      • 十五、Spring + MyBatis【`重点`】
        • 15.1 配置数据源
          • 15.1.1 引入jdbc.properties配置文件
          • 15.1.2 整合Spring配置文件和properties配置文件
          • 15.1.3 Druid连接池可选参数
          • 15.1.4 Druid监控中心
          • 15.1.5 测试监控中心
        • 15.2 整合MyBatis
          • 15.2.1 导入依赖
          • 15.2.2 配置SqlSessionFactory
          • 15.2.3 配置MapperScannerConfigurer
          • 15.2.4 配置Service
      • 十六、事务【`重点`】
        • 16.1 配置DataSourceTransactionManager
        • 16.2 配置事务通知
        • 16.3 事务属性
          • 16.3.1 隔离级别
            • 16.3.1.1 概念
            • 16.3.1.2 特性
            • 16.3.1.3 并发问题
          • 16.3.2 传播行为
          • 16.3.3 读写性
          • 16.3.4 事务超时
          • 16.3.5 事务回滚
        • 16.4 编织
      • 十七、注解开发
        • 17.1 声明bean
        • 17.2 注入(DI)
        • 17.3 事务控制
        • 17.4 注解所需配置
        • 17.5 AOP开发
          • 17.5.1 注解使用
          • 17.5.2 配置
      • 十八、集成JUnit
        • 18.1 导入依赖
        • 18.2 编码

一、引言

EJB


1.1 原生web开发中存在哪些问题?

  • 传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。

  • 部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤)。

  • 侵入性强,移植性差(例如:DAO实现的更换,从Connection到SqlSession)。

二、Spring框架


2.1 概念

  • Spring是一个项目管理框架,同时也是一套Java EE解决方案。

  • Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。

  • Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。

2.2 访问与下载

官方网站:https://spring.io/

下载地址:http://repo.spring.io/release/org/springframework/spring/

三、Spring架构组成


Spring架构由诸多模块组成,可分类为

  • 核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,SpEL,AOP
  • 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
  • 数据访问:事务,DAO支持,JDBC,ORM,封送XML。
  • Spring MVC和 Spring WebFlux Web框架。
  • 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
  • 语言:Kotlin,Groovy,动态语言。
Spring架构组成
在这里插入图片描述
GroupIdArtifactId说明
org.springframeworkspring-beansBeans 支持,包含 Groovy
org.springframeworkspring-aop基于代理的AOP支持
org.springframeworkspring-aspects基于AspectJ 的切面
org.springframeworkspring-context应用上下文运行时,包括调度和远程抽象
org.springframeworkspring-context-support支持将常见的第三方类库集成到 Spring 应用上下文
org.springframeworkspring-core其他模块所依赖的核心模块
org.springframeworkspring-expressionSpring 表达式语言,SpEL
org.springframeworkspring-instrumentJVM 引导的仪表(监测器)代理
org.springframeworkspring-instrument-tomcatTomcat 的仪表(监测器)代理
org.springframeworkspring-jdbc支持包括数据源设置和 JDBC 访问支持
org.springframeworkspring-jms支持包括发送/接收JMS消息的助手类
org.springframeworkspring-messaging对消息架构和协议的支持
org.springframeworkspring-orm对象/关系映射,包括对 JPA 和 Hibernate 的支持
org.springframeworkspring-oxm对象/XML 映射(Object/XML Mapping,OXM)
org.springframeworkspring-test单元测试和集成测试支持组件
org.springframeworkspring-tx事务基础组件,包括对 DAO 的支持及 JCA 的集成
org.springframeworkspring-webweb支持包,包括客户端及web远程调用
org.springframeworkspring-webmvcREST web 服务及 web 应用的 MVC 实现
org.springframeworkspring-webmvc-portlet用于 Portlet 环境的MVC实现
org.springframeworkspring-websocketWebSocket 和 SockJS 实现,包括对 STOMP 的支持
org.springframeworkspring-jclJakarta Commons Logging 日志系统

四、自定义工厂


4.1 配置文件

userDAO=com.qf.dao.UserDAOImpl
userService=com.qf.service.UserServiceImpl

4.2 工厂类

/*** 自定义工厂*/
public class MyFactory {private Properties properties = new Properties();public MyFactory(){}public MyFactory(String config) throws IOException {// 加载配置文件properties.load(MyFactory.class.getResourceAsStream(config));}// 获取对象public Object getBean(String beanName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {// 获得类路径String classPath = properties.getProperty(beanName);if(classPath!=null){Class claz = null;// 反射:加载类对象claz = Class.forName(classPath);// 反射:获得对象return claz.newInstance();}return null;}
}

五、构建Maven项目


5.1 新建项目

使用IDEA打开已创建的文件夹目录
在这里插入图片描述

5.2 选择Maven目录

选择Maven项目
在这里插入图片描述

5.3 GAV坐标

GAV坐标
在这里插入图片描述

六、Spring环境搭建


6.1 pom.xml中引入Spring常用依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qf</groupId><artifactId>hello-spring</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- Spring常用依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency></dependencies>
</project>

6.2 创建Spring配置文件

命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.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"></beans>

七、Spring工厂编码


定义目标Bean类型

public class MyClass{public void show(){System.out.println("HelloWorld");}
}

spring-context.xml中的< beans >内部配置bean标签

<!-- 配置实例(id:“唯一标识”  class="需要被创建的目标对象全限定名") -->
<bean id="mc" class="com.qf.spring.part1.factory.MyClass" />

调用Spring工厂API(ApplicationContext接口)

public class TestFactory{/*** 程序中的对象都交由Spring的ApplicationContext工厂进行创建。*/public static void main(String[] args){//1. 读取配置文件中所需创建的bean对象,并获得工厂对象ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");//2. 通过id获取bean对象MyClass mc = (MyClass) ctx.getBean("mc");//3. 使用对象mc.show();}
}

八、依赖与配置文件详解


Spring框架包含多个模块,每个模块各司其职,可结合需求引入相关依赖Jar包实现功能。

8.1 Spring依赖关系

Spring常用功能的Jar包依赖关系
在这里插入图片描述
  • 注意:Jar包彼此存在依赖,只需引入最外层Jar即可由Maven自动将相关依赖Jar引入到项目中。

8.2 schema

配置文件中的顶级标签中包含了语义化标签的相关信息

  • xmlns:语义化标签所在的命名空间。
  • xmlns:xsi:XMLSchema-instance 标签遵循Schema标签标准。
  • xsi:schemaLocation:xsd文件位置,用以描述标签语义、属性、取值范围等。

九、IoC(Inversion of Control )控制反转【重点


Inverse Of Controll:控制反转

反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)

解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健

9.1 项目中强耦合问题

public class UserDAOImpl implements UserDAO{....}
public class UserServiceImpl implements UserService {// !!!强耦合了UserDAOImpl!!!,使得UserServiceImpl变得不稳健!!private UserDAO userDAO= new UserDAOImpl();@Overridepublic User queryUser() {return userDAO.queryUser();}....
}

9.2 解决方案

// 不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get)
public class UserServiceImpl implements UserService {// !!!不再耦合任何DAO实现!!!,消除不稳健因素!!private UserDAO userDAO;// 为userDAO定义set/get,允许userDAO属性接收spring赋值//Getters And Setters@Overridepublic User queryUser() {return userDAO.queryUser();}....
}
<bean id="userDAO" class="com.qf.spring.part1.injection.UserDaoImpl"></bean>
<!-- UserServiceImpl组件 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl"><!-- 由spring为userDAO属性赋值,值为id="userDAO"的bean --><property name="userDAO" ref="userDAO"/>
</bean>

此时,如果需要更换其他UserDAO实现类,则UserServiceImpl不用任何改动!

则此时的UserServiceImpl组件变得更加稳健!

十、DI(Dependency Injection)依赖注入【重点

三种属性注入方式:

  • 构造方法注入
  • set 方法注入(推荐)
  • p名称空间注入(本质上还是 set 方法注入)

各种注入属性:

  • 基本数据类型,直接使用标签的 value 属性注入
  • 对象
    • 外部定义好一个对象,然后通过 ref 引用对象
    • 直接在需要的地方通过 bean 标签定义一个对象(局限性,定义好的 bean 无法复用)
  • List 集合:list
  • 数组:array
  • Map:map
  • Properties :props

10.1 概念

在Spring创建对象的同时,为其属性赋值,称之为依赖注入。

10.2 Set注入

创建对象时,Spring工厂会通过Set方法为对象的属性赋值。

10.2.1 定义目标Bean类型
public class User {private Integer id;private String password;private String sex;private Integer age;private Date bornDate;private String[] hobbys;private Set<String> phones;private List<String> names;private Map<String,String> countries;private Properties files;//Getters And Setters
}
10.2.2 基本类型 + 字符串类型 + 日期类型
<bean id="u1" class="com.qf.spring.part1.injection.User"><!--base field--><property name="id" value="1001" /><property name="password" value="123456" /><property name="sex" value="male" /><property name="age" value="20" /><property name="bornDate" value="1990/1/1" /><!--注意格式"/"-->
</bean>
10.2.3 容器类型
<bean id="u1" class="com.qf.spring.part1.injection.User">	<!--Array--><property name="hobbys"><array><value>Run</value><value>Swim</value><value>Climb</value></array></property><!--Set--><property name="phones"><set><value>13777777777</value><value>13888888888</value><value>13999999999</value></set></property><!--List--><property name="names"><list><value>tom</value><value>jack</value><value>marry</value></list></property><!--Map--><property name="countries"><map><entry key="CN" value="China" /><entry key="US" value="America" /><entry key="KR" value="Korea" /></map></property><!--Properties--><property name="files"><props><prop key="first">One</prop><prop key="second">Two</prop><prop key="third">Three</prop></props></property>
</bean>
10.2.4 自建类型
<!--次要bean,被作为属性-->
<bean id="addr" class="com.qf.spring.part1.injection.Address"><property name="position" value="北京市海淀区" /><property name="zipCode" value="100001" />
</bean><!--主要bean,操作的主体-->
<bean id="u2" class="com.qf.spring.part1.injection.User"><property name="address" ref="addr" /><!--address属性引用addr对象-->
</bean>
<!--次要bean,被作为属性-->
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" /><!--主要bean,操作的主体-->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl"><property name="ud" ref="userDao" /><!--ud属性引用userDao对象-->
</bean>

10.3 构造注入【了解】

创建对象时,Spring工厂会通过构造方法为对象的属性赋值。

10.3.1 定义目标Bean类型
public class Student {private Integer id;private String name;private String sex;private Integer age;//Constructorspublic Student(Integer id , String name , String sex , Integer age){this.id = id;this.name = name;this.sex = sex;this.age = age;}
}
10.3.2 注入
 <!--构造注入-->
<bean id="u3" class="com.qf.zcg.spring.day1.t2.ioc.Student"><constructor-arg name="id" value="1234" /> <!-- 除标签名称有变化,其他均和Set注入一致 --><constructor-arg name="name" value="tom" /><constructor-arg name="age" value="20" /><constructor-arg name="sex" value="male" />
</bean>

10.4 自动注入【了解】

不用在配置中 指定为哪个属性赋值,及赋什么值.

由spring自动根据某个 “原则” ,在工厂中查找一个bean,为属性注入属性值

public class UserServiceImpl implements UserService {private UserDAO userDAO;//Getters And Setters....
}
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!-- 为UserServiceImpl中的属性基于类型自动注入值 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!-- 为UserServiceImpl中的属性基于类型自动注入值 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byName"></bean>

十一、Bean细节


11.1 控·制简单对象的单例、多例模式

配置< bean scope=“singleton | prototype” />

<!--singleton(默认):每次调用工厂,得到的都是同一个对象。prototype:每次调用工厂,都会创建新的对象。
-->
<bean id="mc" class="com.qf.zcg.spring.day1.t1.basic.MyClass" scope="singleton" /> 
  • 注意:需要根据场景决定对象的单例、多例模式。
  • 可以共用:Service、DAO、SqlSessionFactory(或者是所有的工厂)。
  • 不可共用:Connection、SqlSession、ShoppingCart。

11.2 FactoryBean创建复杂对象【了解】

作用:让Spring可以创建复杂对象、或者无法直接通过反射创建的对象。

FactoryBean解决复杂对象创建
在这里插入图片描述
11.2.1 实现FactoryBean接口
接口方法描述
在这里插入图片描述
  • 注意:isSingleton方法的返回值,需根据所创建对象的特点决定返回true/false。
  • 例如:Connection 不应该被多个用户共享,返回false。
  • 例如:SqlSessionFactory 重量级资源,不该过多创建,返回true。
11.2.2 配置spring-context.xml
配置与获取方式
在这里插入图片描述
11.2.3 特例
获取FactoryBean接口的实现类对象,而非getObject()所生产的对象。
在这里插入图片描述

十二、Spring工厂特性


12.1 饿汉式创建优势

工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式)。

提高程序运行效率。避免多次IO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)

12.2 生命周期方法

  • 自定义初始化方法:添加“init-method”属性,Spring则会在创建对象之后,调用此方法。

  • 自定义销毁方法:添加“destroy-method”属性,Spring则会在销毁对象之前,调用此方法。

  • 销毁:工厂的close()方法被调用之后,Spring会毁掉所有已创建的单例对象

  • 分类:Singleton对象由Spring容器销毁、Prototype对象由JVM销毁。

12.3 生命周期注解

初始化注解、销毁注解

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;@PostConstruct //初始化 
public void init(){System.out.println("init method executed");
}@PreDestroy //销毁
public void destroy(){System.out.println("destroy method executed");
}

12.4 生命周期阶段

**单例bean:**singleton

随工厂启动创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》随工厂关闭销毁

**多例bean:**prototype

被使用时创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》JVM垃圾回收销毁

十三、代理设计模式


13.1 概念

将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。

功能分离
在这里插入图片描述

13.2 静态代理设计模式

通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护。

静态代理
在这里插入图片描述
  • 代理类 = 实现原始类相同接口 + 添加辅助功能 + 调用原始类的业务方法。
  • 静态代理的问题
    • 代理类数量过多,不利于项目的管理。
    • 多个代理类的辅助功能代码冗余,修改时,维护性差。

13.3 动态代理设计模式

动态创建代理类的对象,为原始类的对象添加辅助功能。

有两种实现方式:

  • 基于 JDK(不需要额外引入jar):被代理的对象存在接口。
  • 基于 cglib(需要引入外部jar):被代理的对象可以没有接口。
13.3.1 JDK动态代理实现(基于接口)
//目标
final OrderService os = new OrderServiceImpl();
//额外功能
InvocationHandler handler = new InvocationHandler(){//1.设置回调函数(额外功能代码)@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("start...");method.invoke(os, args);System.out.println("end...");return null;}
};
//2.创建动态代理类
Object proxyObj = Proxy.newProxyInstance(ClassLoader , Interfaces , InvocationHandler);
13.3.2 CGlib动态代理实现(基于继承)
final OrderService os = new OrderServiceImpl();
Enhancer cnh = new Enhancer();//1.创建字节码曾强对象
enh.setSuperclass(os.getClass());//2.设置父类(等价于实现原始类接口)
enh.setCallback(new InvocationHandler(){//3.设置回调函数(额外功能代码)@Overridepublic Object invoke(Object proxy , Method method, Object[] args) throws Throwable{System.out.println("start...");Object ret = method.invoke(os,args);System.out.println("end...");return ret;}
});
OrderService proxy = (OrderService)enh.create();//4.创建动态代理类
proxy,createOrder();

十四、面向切面编程【重点

AOP 的实现:

选项spring-aspectsaspectj(首选)
实现类spring-aspectsaspectjweaver,aspectjrt
实现方式通过实现接口来定义通知通过方法+注解来实现通知

14.1 概念

AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

14.2 AOP开发术语

  • 连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。

  • 切点(Pointcut):被Spring切入连接点。

  • 通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知、返回通知等。

  • 目标对象(Target):代理的目标对象

  • 引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。

  • 织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。

  • 代理(Proxy):被AOP织入通知后,产生的结果类。

  • 切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。

14.3 作用

Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能。

14.4 环境搭建

引入AOP相关依赖

<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.1.6.RELEASE</version>
</dependency>

spring-context.xml引入AOP命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

14.5 开发流程

定义原始类

package com.qf.aaron.aop.basic;public interface UserService {public void save();
}
package com.qf.aaron.aop.basic;public class UserServiceImpl implements UserService {public void save() {System.out.println("save method executed...");}
}

定义通知类(添加额外功能)

package com.qf.aaron.aop.basic;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;public class MyAdvice implements MethodBeforeAdvice { //实现前置通知接口public void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("before advice executed...");}
}

定义bean标签

<!--原始对象-->
<bean id="us" class="com.qf.aaron.aop.basic.UserServiceImpl" /><!--辅助对象-->
<bean id="myAdvice" class="com.qf.aaron.aop.basic.MyAdvice" />

定义切入点(PointCut)

形成切面(Aspect)

<aop:config><!--切点--><aop:pointcut id="myPointCut" expression="execution(* save())" />
</aop:config>
<aop:config><!--组装切面 --><aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut" />
</aop:config>

14.6 AOP小结

  • 通过AOP提供的编码流程,更便利的定制切面,更方便的定制了动态代理。

  • 进而彻底解决了辅助功能冗余的问题;

  • 业务类中职责单一性得到更好保障;

  • 辅助功能也有很好的复用性。

14.7 通知类【可选】

定义通知类,达到通知效果

前置通知:MethodBeforeAdvice后置通知:AfterAdvice返回通知:AfterReturningAdvice //有异常不执行,方法会因异常而结束,无返回值异常通知:ThrowsAdvice环绕通知:MethodInterceptor

14.8 通配切入点

根据表达式通配切入点

<!--匹配参数-->
<aop:pointcut id="myPointCut" expression="execution(* *(com.qf.aaron.aop.basic.User))" />
<!--匹配方法名(无参)-->
<aop:pointcut id="myPointCut" expression="execution(* save())" />
<!--匹配方法名(任意参数)-->
<aop:pointcut id="myPointCut" expression="execution(* save(..))" />
<!--匹配返回值类型-->
<aop:pointcut id="myPointCut" expression="execution(com.qf.aaron.aop.basic.User *(..))" />
<!--匹配类名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.UserServiceImpl.*(..))" />
<!--匹配包名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.*.*(..))" />
<!--匹配包名、以及子包名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop..*.*(..))" />

14.9 JDK和CGLIB选择

  • spring底层,包含了jdk代理和cglib代理两种动态代理生成机制

  • 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理

class DefaultAopProxyFactory{// 该方法中明确定义了 JDK代理和CGLib代理的选取规则// 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理public AopProxy createAopProxy(){...}
}

14.10 后处理器

  • spring中定义了很多后处理器;

  • 每个bean在创建完成之前 ,都会有一个后处理过程,即再加工,对bean做出相关改变和调整;

  • spring-AOP中,就有一个专门的后处理器,负责通过原始业务组件(Service),再加工得到一个代理组件。

常用后处理器
在这里插入图片描述
14.10.1 后处理器定义
/*** 定义bean后处理器* 作用:在bean的创建之后,进行再加工*/
public class MyBeanPostProcessor implements BeanPostProcessor{/*** 在bean的init方法之前执行* @param bean  原始的bean对象* @param beanName* @return* @throws BeansException*/public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("后处理器 在init之前执行~~~"+bean.getClass());return bean;}/*** 在bean的init方法之后执行* @param bean  postProcessBeforeInitialization返回的bean* @param beanName* @return* @throws BeansException*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("后处理器 在init之后执行~~~"+bean.getClass());return bean;// 此处的返回是 getBean() 最终的返回值}
}
14.10.2 配置后处理器
<!-- 配置后处理器,将对工厂中所有的bean声明周期进行干预 -->
<bean class="com.qianfeng.beanpostprocessor.MyBeanPostProcessor"></bean>
14.10.3 bean生命周期

构造 》 注入属性 满足依赖 》 后处理器前置过程 》 初始化 》后处理器后置过程 》 返回 》 销毁

14.10.4 动态代理源码(了解)
// AbstractAutoProxyCreator是 AspectJAwareAdvisorAutoProxyCreator的父类
// 该后处理器类中的 wrapIfNecessary方法即动态代理生成过程
AbstractAutoProxyCreator#postProcessAfterInitialization(Object bean, String beanName){if (!this.earlyProxyReferences.contains(cacheKey)) {// 开始动态定制代理return wrapIfNecessary(bean, beanName, cacheKey);}
}

十五、Spring + MyBatis【重点


15.1 配置数据源

将数据源配置到项目中

15.1.1 引入jdbc.properties配置文件
#jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
15.1.2 整合Spring配置文件和properties配置文件
<!--spring-context.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"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.xsd"><!--配置文件参数化(参数占位符)--><context:property-placeholder location="classpath:jdbc.properties" /><!--与PooledDataSource集成(二选一)--><bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource"><property name="driver" value="${driverClass}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></bean><!--与DruidDataSource集成(二选一)--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><!--基本配置--><property name="driverClassName" value="${jdbc.driverClass}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean>
</bean>
15.1.3 Druid连接池可选参数
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><!--基本配置--><property name="driverClassName" value="${jdbc.driverClass}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/><!-- 配置初始化大小、最小、最大 --><property name="initialSize" value="${jdbc.init}"/><property name="minIdle" value="${jdbc.minIdle}"/><property name="maxActive" value="${jdbc.maxActive}"/><!-- 配置获取连接等待超时的时间 --><property name="maxWait" value="60000"/><!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --><property name="timeBetweenEvictionRunsMillis" value="60000"/><!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --><property name="minEvictableIdleTimeMillis" value="300000"/>
</bean>
15.1.4 Druid监控中心
<!--web.xml-->
<servlet><servlet-name>DruidStatView</servlet-name><servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>DruidStatView</servlet-name><url-pattern>/druid/*</url-pattern>
</servlet-mapping>
15.1.5 测试监控中心

配置tomcat,并访问protocol://ip:port/project/druid/index.html

15.2 整合MyBatis

将 SqlSessionFactory、DAO、Service 配置到项目中

15.2.1 导入依赖
<!-- spring-jdbc -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.6.RELEASE</version>
</dependency><!-- spring+mybatis集成依赖 -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.1</version>
</dependency>
15.2.2 配置SqlSessionFactory
<!-- 工厂bean:生成SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 注入连接池 --><property name="dataSource" ref="dataSource"></property><!-- 注入dao-mapper文件信息 ,如果映射文件和dao接口 同包且同名,则此配置可省略--><property name="mapperLocations"><list><value>classpath:com/qf/spring/dao/*.xml</value></list></property><!-- 为 dao-mapper文件中的实体 定义缺省包路径 如:<select id="queryAll" resultType="User"> 中 User类可以不定义包--><property name="typeAliasesPackage" value="com.qf.entity"></property>
</bean>
15.2.3 配置MapperScannerConfigurer

管理DAO实现类的创建,并创建DAO对象,存入工厂管理

  • 扫描所有DAO接口,去构建DAO实现

  • 将DAO实现存入工厂管理

  • DAO实现对象在工厂中的id是:“首字母小写的-接口的类名”,

    例如:UserDAO==>userDAO , OrderDAO==>orderDAO

<!-- mapperScannerConfigurer -->
<bean id="mapperScannerConfigurer9" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- dao接口所在的包  如果有多个包,可以用逗号或分号分隔 <property name="basePackage" value="com.a.dao,com.b.dao"></property>--><property name="basePackage" value="com.qf.spring.dao"></property><!-- 如果工厂中只有一个SqlSessionFactory的bean,此配置可省略 --><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
15.2.4 配置Service
<bean id="userService" class="com.qf.spring.service.UserServiceImpl"><!-- 注意ref中的值是对应DAO接口的首字母小写的接口名 --><property name="userDAO" ref="userDAO"></property>
</bean>

十六、事务【重点


16.1 配置DataSourceTransactionManager

事务管理器,其中持有DataSource,可以控制事务功能(commit,rollback等)。

<!-- 1. 引入一个事务管理器,其中依赖DataSource,借以获得连接,进而控制事务逻辑 -->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property>
</bean>

注意:DataSourceTransactionManager 和 SqlSessionFactoryBean 要注入同一个DataSource的Bean,否则事务控制失败!!!

16.2 配置事务通知

基于事务管理器,进一步定制,生成一个额外功能:Advice。

此Advice可以切入任何需要事务的方法,通过事务管理器为方法控制事务。

<tx:advice id="txManager" transaction-manager="tx"><tx:attributes><!--<tx:method name="insertUser" rollback-for="Exception" isolation="DEFAULT"    propagation="REQUIRED" read-only="false"/>--><!-- 以User结尾的方法,切入此方法时,采用对应事务实行--><tx:method name="*User" rollback-for="Exception"/><!-- 以query开头的方法,切入此方法时,采用对应事务实行 --><tx:method name="query*" propagation="SUPPORTS"/><!-- 剩余所有方法 --><tx:method name="*"/></tx:attributes>
</tx:advice>

16.3 事务属性

16.3.1 隔离级别
16.3.1.1 概念

isolation 隔离级别

名称描述
default(默认值)(采用数据库的默认的设置) (建议)
read-uncommited读未提交
read-commited读提交 (Oracle数据库默认的隔离级别)
repeatable-read可重复读 (MySQL数据库默认的隔离级别)
serialized-read序列化读

隔离级别由低到高为:read-uncommited < read-commited < repeatable-read < serialized-read

16.3.1.2 特性
  • 安全性:级别越高,多事务并发时,越安全。因为共享的数据越来越少,事务间彼此干扰减少。

  • 并发性:级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。

16.3.1.3 并发问题

事务并发时的安全问题

问题描述
脏读一个事务读取到另一个事务还未提交的数据。大于等于 read-commited 可防止
不可重复读一个事务内多次读取一行数据的相同内容,其结果不一致。大于等于 repeatable-read 可防止
幻读一个事务内多次读取一张表中的相同内容,其结果不一致。serialized-read 可防止
16.3.2 传播行为

propagation传播行为

当涉及到事务嵌套(Service调用Service)时,可以设置:

  • SUPPORTS = 不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中。(适合查询)

  • REQUIRED = 不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中。 (默认值)(适合增删改)

16.3.3 读写性

readonly 读写性

  • true:只读,可提高查询效率。(适合查询)

  • false:可读可写。 (默认值)(适合增删改)

16.3.4 事务超时

timeout事务超时时间

当前事务所需操作的数据被其他事务占用,则等待。

  • 100:自定义等待时间100(秒)。
  • -1:由数据库指定等待时间,默认值。(建议)
16.3.5 事务回滚

rollback-for 回滚属性

  • 如果事务中抛出 RuntimeException,则自动回滚

  • 如果事务中抛出 CheckException(非运行时异常 Exception),不会自动回滚,而是默认提交事务

  • 处理方案 : 将CheckException转换成RuntimException上抛,或 设置 rollback-for=“Exception”

16.4 编织

将事务管理的Advice 切入需要事务的业务方法中

<aop:config><aop:pointcut expression="execution(* com.qf.spring.service.UserServiceImpl.*(..))" id="pc"/><!-- 组织切面 --><aop:advisor advice-ref="txManager" pointcut-ref="pc"/>
</aop:config>

十七、注解开发


17.1 声明bean

用于替换自建类型组件的 <bean…>标签;可以更快速的声明bean

  • @Service 业务类专用
    @Repository dao实现类专用
    @Controller web层专用

  • @Component 通用

  • @Scope 用户控制bean的创建模式

// @Service说明 此类是一个业务类,需要将此类纳入工厂  等价替换掉 <bean class="xxx.UserServiceImpl">
// @Service默认beanId == 首字母小写的类名"userServiceImpl"
// @Service("userService") 自定义beanId为"userService"
@Service //声明bean,且id="userServiceImpl"
@Scope("singleton") //声明创建模式,默认为单例模式 ;@Scope("prototype")即可设置为多例模式
public class UserServiceImpl implements UserService {...   
}

17.2 注入(DI)

用于完成bean中属性值的注入

  • @Autowired 基于类型自动注入
  • @Resource 基于名称自动注入
  • @Qualifier(“userDAO”) 限定要自动注入的bean的id,一般和@Autowired联用
  • @Value 注入简单类型数据 (jdk8种+String)
@Service
public class UserServiceImpl implements UserService {@Autowired //注入类型为UserDAO的bean@Qualifier("userDAO2") //如果有多个类型为UserDAO的bean,可以用此注解从中挑选一个private UserDAO userDAO;
}
@Service
public class UserServiceImpl implements UserService {@Resource("userDAO3") //注入id=“userDAO3”的beanprivate UserDAO userDAO;/*@Resource //注入id=“userDAO”的beanprivate UserDAO userDAO;*/
}
public class XX{@Value("100") //注入数字private Integer id;@Value("shine") //注入Stringprivate String name;
}

17.3 事务控制

用于控制事务切入

  • @Transactional

  • 工厂配置中的 <tx:advice… 和 <aop:config… 可以省略 !!

//类中的每个方法都切入事务(有自己的事务控制的方法除外)
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class,timeout = -1)
public class UserServiceImpl implements UserService {...//该方法自己的事务控制,仅对此方法有效@Transactional(propagation=Propagation.SUPPORTS)public List<User> queryAll() {return userDao.queryAll();}public void save(User user){userDao.save(user);}
}

17.4 注解所需配置

<!-- 告知spring,哪些包中 有被注解的类、方法、属性 -->
<!-- <context:component-scan base-package="com.qf.a,com.xx.b"></context:component-scan> -->
<context:component-scan base-package="com.qf"></context:component-scan><!-- 告知spring,@Transactional在定制事务时,基于txManager=DataSourceTransactionManager -->
<tx:annotation-driven transaction-manager="txManager"/>

17.5 AOP开发

17.5.1 注解使用
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect // 声明此类是一个切面类:会包含切入点(pointcut)和通知(advice)
@Component //声明组件,进入工厂
public class MyAspect {// 定义切入点@Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))")public void pc(){}@Before("pc()") // 前置通知public void mybefore(JoinPoint a) {System.out.println("target:"+a.getTarget());System.out.println("args:"+a.getArgs());System.out.println("method's name:"+a.getSignature().getName());System.out.println("before~~~~");}@AfterReturning(value="pc()",returning="ret") // 后置通知public void myAfterReturning(JoinPoint a,Object ret){System.out.println("after~~~~:"+ret);}@Around("pc()") // 环绕通知public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {System.out.println("interceptor1~~~~");Object ret = p.proceed();System.out.println("interceptor2~~~~");return ret;}@AfterThrowing(value="pc()",throwing="ex") // 异常通知public void myThrows(JoinPoint jp,Exception ex){System.out.println("throws");System.out.println("===="+ex.getMessage());}
}
17.5.2 配置
<!-- 添加如下配置,启用aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

十八、集成JUnit


18.1 导入依赖

<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>4.3.6.RELEASE</version>
</dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>
</dependency>

18.2 编码

可以免去工厂的创建过程;

可以直接将要测试的组件注入到测试类。

@RunWith(SpringJUnit4ClassRunner.class) //由SpringJUnit4ClassRunner启动测试
@ContextConfiguration("classpath:applicationContext.xml") //spring的配置文件位置
public class SpringTest{//当前测试类也会被纳入工厂中,所以其中属性可以注入@Autowired // 注入要测试的组件@Qualifier("userDAO")private UserDAO userDAO;@Testpublic void test(){// 测试使用userDAOuserDAO.queryUser();....}
}

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

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

相关文章

【sql】MongoDB 增删改查 高级用法

【sql】MongoDB 增删改查 高级用法 相关使用文档 MongoDB Query API — MongoDB Manual https://www.mongodb.com/docs/manual/reference/sql-comparison //增 //新增数据2种方式 db.msg.save({"name":"springboot&#x1f600;"}); db.msg.insert({&qu…

prompt工程(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 我准备想办法把这些东西整合到我的ScholarEase项目里。到时候按照分类、按照prompt生成方法列一堆选项&#xff0c;用户自己生成prompt后可以选择在ScholarEase里面聊天&#xff0c;也可以复制到别的地方&#xff08;比如ChatGPT网页版之类的&a…

Ansible 使用 RHEL 系统角色

安装 RHEL 系统角色软件包&#xff0c;并创建符合以下条件的 playbook /home/greg/ansible/timesync.yml 在所有受管节点上运行 使用 timesync 角色 配置该角色&#xff0c;以使用当前有效的 NTP 提供商 配置该角色&#xff0c;以使用时间服务器 172.25.254.254 配置该角色&am…

Linux虚拟机安装(Ubuntu 20)

最近这段时间使用VMWare安装了一下Ubuntu版本的Linux虚拟机&#xff0c;在这里记录一下安装时参考的文章以及需要注意的细节 参考链接&#xff1a; VMware虚拟机下安装Ubuntu20.04&#xff08;保姆级教程&#xff09; 一、安装VMWare 下载链接&#xff1a;VMware Workstatio…

GB28181国标平台测试软件NTV-GBC(包含服务器和模拟客户端)

GB28181国标平台测试软件NTV-GBC用于对GB28181国标平台进行测试(测试用例需要服务器软件&#xff0c;服务器软件可以是任何标准的国标平台&#xff0c;我们测试使用的是NTV-GBS&#xff09;&#xff0c;软件实现了设备注册、注销、目录查询&#xff0c;消息订阅、INVITE&#x…

概率密度函数 累积分布函数

概率密度函数&#xff1a;是指想要求得面积的图形表达式&#xff0c;注意只是表达式&#xff0c;要乘上区间才是概率&#xff0c;所以概率密度并不是概率&#xff0c;而是概率的分布程度。 为什么要引入概率密度&#xff0c;可能是因为连续变量&#xff0c;无法求出某个变量的…

软件测试及数据分析处理实训室建设方案

一 、系统概述 软件测试及数据分析处理是软件开发过程中的一项重要测试活动&#xff0c;旨在验证不同软件模块或组件之间的集成与交互是否正常。综合测试确保各个模块按照设计要求正确地协同工作&#xff0c;以实现整个软件系统的功能和性能。以下是软件测试及数据分析处理的一…

【BUG】解决安装oracle11g或12C中无法访问临时位置的问题

项目场景&#xff1a; 安装oracle时&#xff0c;到第二步出现oracle11g或12C中无法访问临时位置的问题。 解决方案&#xff1a; 针对客户端安装&#xff0c;在cmd中执行命令&#xff1a;前面加实际路径setup.exe -ignorePrereq -J"-Doracle.install.client.validate.cli…

汽车电子笔记之:AUTOSA架构下的多核OS操作系统

目录 1、AUTOSAR多核操作系统 1.1、OS Application 1.2、多核OS的软件分区 1.3、任务调度 1.4、核间任务同步 1.5、计数器、报警器、调度表 1.6、自旋锁与共享资源 1.7、核间通信IOC 1.8、OS Object中元素交互 1.9、多核OS的启动与关闭 2、多核OS注意事项 2.1、最小…

【QT5-自我学习-线程qThread练习-两种使用方式-2:通过继承Qobject类-自己实现功能函数方式-基础样例】

【QT5-自我学习-线程qThread练习-两种使用方式-2&#xff1a;通过继承Qobject类-自己实现功能函数方式-基础样例】 1、前言2、实验环境3-1、学习链接-参考文章3-2、先前了解-自我总结&#xff08;1&#xff09;线程处理逻辑事件&#xff0c;不能带有主窗口的事件&#xff08;2&…

4G模组EC20 网卡udhcpc获取IP但是没有设置IP

使能网卡&#xff1a; ifconfig usb0 up dhcp获取ip&#xff0c;虽然没有报error&#xff0c;但是很显然没有设置进配置 获取ip命令&#xff1a;udhcpc -i usb0 非正常现象&#xff1a; 正常现象&#xff1a; 解决方法&#xff1a; &#xff08;1&#xff09;rootfs 创建文件夹…

lvs-DR模式:

lvs-DR数据包流向分析 客户端发送请求到 Director Server&#xff08;负载均衡器&#xff09;&#xff0c;请求的数据报文&#xff08;源 IP 是 CIP,目标 IP 是 VIP&#xff09;到达内核空间。 Director Server 和 Real Server 在同一个网络中&#xff0c;数据通过二层数据链路…

安卓主板定制_电磁屏/电容屏安卓平板基于MTK联发科方案定制

定制化行业平板 在各行各业中的地位越来越重要&#xff0c;甚至在行业转型和发展中发挥着不可替代的作用。随着工业化社会的快速发展&#xff0c;工业生产对智控设备要求越来越高&#xff0c;运用的范畴也越来越普遍广泛&#xff0c;工业级平板就是其中一种应用广泛的设备。 新…

字节一面:post为什么会发送两次请求?

前言 最近博主在字节面试中遇到这样一个面试题&#xff0c;这个问题也是前端面试的高频问题&#xff0c;因为在前端开发的日常开发中我们总是会与post请求打交道&#xff0c;一个小小的post请求也是牵扯到很多知识点的&#xff0c;博主在这给大家细细道来。 &#x1f680; 作者…

python爬虫实战(3)--爬取某乎热搜

1. 分析爬取地址 打开某乎首页&#xff0c;点击热榜 这个就是我们需要爬取的地址&#xff0c;取到地址某乎/api/v3/feed/topstory/hot-lists/total?limit50&desktoptrue 定义好请求头&#xff0c;从Accept往下的请求头全部复制&#xff0c;转换成json headers {Accep…

Levels - 场景参考:山脉景观(Landscape Mountains)

一些从前的笔记的归档&#xff0c;记录了一些UE4资产的相关信息&#xff1b; 山脉景观&#xff08;Landscape Mountains&#xff09;&#xff1a; 项目的地形材质比较复杂&#xff0c;有几个比较重要的大效果功能&#xff0c;一个是沉积岩的效果&#xff1a; 沉积岩效果的功能…

保研面试题复习

信源/信道编码的目的和种类&#xff1f; 这个图是每个人在学习通信原理的时候&#xff0c;都会遇到的图。包含了三要素&#xff1a;信源、信道和信宿。这个图直接可以回答最开始的问题&#xff0c;所谓信源编码就是针对信源编码&#xff0c;所谓信道编码就是针对信道编码。 有…

python 使用 pdf2image 库将PDF转换为图片

python 使用 pdf2image 库将PDF转换为图片 初环境步骤一&#xff1a;安装pdf2image库步骤二&#xff1a;导入必要的库步骤三&#xff1a;指定PDF文件路径步骤四&#xff1a;将PDF转换为图片步骤五&#xff1a;保存图像为图片文件完整代码运行结果 在数字化时代&#xff0c;PDF&…

如何在 Ubuntu 中安装最新的 Python 版本

动动发财的小手&#xff0c;点个赞吧&#xff01; Python 是增长最快的主要通用编程语言。其原因有很多&#xff0c;例如其可读性和灵活性、易于学习和使用、可靠性和效率。 目前使用的 Python 有两个主要版本 – 2 和 3&#xff08;Python 的现在和未来&#xff09;&#xff1…

Linux下的系统编程——makefile入门(四)

前言&#xff1a; 或许很多Winodws的程序员都不知道这个东西&#xff0c;因为那些Windows的IDE都为你做了这个工作&#xff0c;但我觉得要作一个好的和professional的程序员&#xff0c;makefile还是要懂。这就好像现在有这么多的HTML的编辑器&#xff0c;但如果你想成为一个专…