• Review-Spring


    Spring

    1 Spring框架学习内容

    使用Spring、Spring MVC、MyBatis组成SSM结构
    IoC/DI、注解、AOP、动态代理
    
    • 1
    • 2

    2 Spring框架

    MyBatis框架是一个持久化的框架、负责数据访问
    Spring MVC是Spring框架中的一个模块,负责控制器的管理
    Spring是一个轻量级的容器框架
    轻量级:针对EJB。现在EJB版本进行了更新。3.x
    容器框架:Spring就是一个大工厂。程序中的工厂是用来生产对象的。容器就是一个生产、管理、维护对象的工厂
    Spring框架的核心技术: IoC   AOP
    IoC: 	控制反转
    AOP:	面向切面编程  (面向方面编程)
    Spring是一个基于Ioc和AOP的轻量级饿的容器框架
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3 Spring体系结构

    在这里插入图片描述

    Spring是由七个组件构成的。在使用Spring框架时,七个组件可以组合使用,也可以拆分使用
    1、 Spring Core:核心模块。整个Spring的功能都是基于这个模块的,他就是一个大工厂
    2、 Spring AOP: 是面向切面编程的模块,他是Spring核心模块之一
    3、 Spring DAO: Spring的数据访问模块,整合JDBC。对数据库进行访问时支持。声明式事务处理的组件
    4、 Spring ORM: 这个组件是用来整合使用像MyBatis这种的ORM框架的组件
    5、 Spring Context:上下文组件。整合环境用。ApplicationContext接口就在这个模块中
    6、 Spring Web: 用来整合WEB框架的,如Strust2
    7、 Spring MVC:是Spring对MVC模型的实现。功能上与Struts2一样

    4 IoC/DI是什么

    依赖注入 : DI (Dependency Injection)
    在运行期: 由外部容器动态地将依赖对象注入到组件中
    控制反转: IoC (Inversion of Control)
    所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护由外部容器来负责的
    这样控制就由应用本身转移到了外部容器,控制权的转移就是所谓控制反转
    依赖: 在程序开发中,对象之间存在依赖关系,比如:BIZ依赖于DAO,Controller依赖于BIZ
    程序开发是分层进行的。高内聚,低耦合。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (1) 控制层:Controller
    (2) 业务层:BIZ
    (3) 数据访问层: DAO
    第一版:依赖对象的主动获取

    public interface GoodsDAO {
    	public void save();
    public class GoodsDAOImpl implements GoodsDAO {
    	@Override
    	public void save() {
    		System.out.println("GoodsDAOImpl.save() is called....");
    	}
    public class GoodsBIZ {
    	
    	private GoodsDAO goodsDAO = new GoodsDAOImpl();
    	
    	public void save(){
    		System.out.println("GoodsBIZ.save() is called...");
    		this.goodsDAO.save();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在GoodsBIZ类中,依赖于GoodsDAOImpl对象
    在GoodsBIZ类中,是以主动获取的方式获得依赖对象。new GoodsDAOImpl();这样,GoodsBIZ使用哪个DAO是谁说了算
    BIZ与DAO耦合高

    第二版: 依赖对象的被动接收

    public interface GoodsDAO {
    	public void save();
    public class GoodsDAOImpl implements GoodsDAO {
    	@Override
    	public void save() {
    		System.out.println("GoodsDAOImpl.save() is called....");
    	}
    public class GoodsBIZ {
    	private GoodsDAO goodsDAO;
    	public GoodsDAO getGoodsDAO() {
    		return goodsDAO;
    	}
    	public void setGoodsDAO(GoodsDAO goodsDAO) {
    		this.goodsDAO = goodsDAO;
    	}
    	public void save(){
    		System.out.println("GoodsBIZ.save() is called...");
    		goodsDAO.save();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在GoodsBIZ类中,依赖于GoodsDAO接口,而接口只是一个规范
    在GoodsBIZ类中,是以被动接收的方式,获得依赖对象。依赖对象的创建由谁说了算?
    由外部容器指定
    BIZ与DAO耦合低

    public class SpringText {
    	public static void main(String[] args) {
    		GoodsBIZ goodsBIZ = new GoodsBIZ();
    		GoodsDAO goodsDAO = new GoodsDAOImpl();
    		goodsBIZ.setGoodsDAO(goodsDAO);
    		goodsBIZ.save();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    总结:
    GoodsBIZ由主动获取变成了被动接收,控制权发生了变化,这个变化就叫控制反转
    因为当前的控制权编程了被动接收,依赖对象由外部容器给GoodsBIZ注入。是通过
    setGoodsDAO(…)进行注入的
    goodsBIZ.setGoodsDAO(goodsDAO); 这句话就叫依赖注入

    5 使用Spring IoC工厂创建对象

    5.1 创建工程并导入Spring框架

    在src目录下有个applicationContext.xml文件,他是Spring框架的核心配置文件

    5.2 编写GoodsDAO并在Spring容器中创建对象

    在Spring容器中创建对象,在applicationContext.xml文件中配置标记

    5.3 使用Spring上下文对象获得GoodsDAO对象

    5.3.1 Spring上下文对象就是环境对象。读取Spring配置文件,生产对象
    ApplicationContext接口
    ClassPathXmlApplicationContext:基于类路径的方式加载Spring配置文件
    5.3.2 调用getBean方法获得对象

    ApplicationContext context = 
    				new ClassPathXmlApplicationContext("/applicationContext.xml");
    		GoodsDAO goodsDAO = (GoodsDAO) context.getBean("goodsDAO");
    		goodsDAO.save();
    
    • 1
    • 2
    • 3
    • 4

    5.4 编写GoodsBIZ并在Spring中创建对象

    <bean name="goodsBIZ" class="com.zpark.tea_mgr.biz.GoodsBIZ">
    </bean>
    
    • 1
    • 2

    5.5 在Spring容器中配置依赖注入

    <bean name="goodsBIZ" class="com.zpark.tea_mgr.biz.GoodsBIZ">
    	<property name="goodsDAO"> 	<!-- GoodsBIZA有个属性叫goodsDAO -->
    		<ref bean="goodsDAO"/>	<!—依赖。引用了上面那个bean(1) -->
    	</property>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.6 获得GoodsBIZ对象

    @SuppressWarnings("resource")
    	ApplicationContext context = 
    			new ClassPathXmlApplicationContext("/applicationContext.xml");
    		
    	/*GoodsDAO goodsDAO = (GoodsDAO) context.getBean("goodsDAO");
    	goodsDAO.save();*/
    		
    	GoodsBIZ goodsBIZ = (GoodsBIZ) context.getBean("goodsBIZ");
    	goodsBIZ.save();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    6 使用构造方法创建对象,并使用构造注入

    public class GoodsController {
    	private GoodsBIZ goodsBIZ;
    	public GoodsController(GoodsBIZ goodsBIZ){
    		System.out.println("GoodsController的有参构造被调用");
    		this.goodsBIZ = goodsBIZ;
    	}
    	
    	public void save(){
    		System.out.println("GoodsController.save() is called...");
    		goodsBIZ.save();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    <bean name="goodsController"
           class="com.zpark.tea_mgr.controller.GoodsController">
    	<constructor-arg name="goodsBIZ" ref="goodsBIZ"/>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    GoodsController goodsController = (GoodsController) context.getBean("goodsController");
    goodsController.save();
    
    • 1
    • 2

    7. 使用静态工厂创建对象

    public class DAOFactory {
    	public static GoodsDAO createDAO(){
    	return new GoodsDAOImpl();
    }
    
    • 1
    • 2
    • 3
    • 4
    <bean name="dao1" class="com.zpark.tea_mgr.factory.DAOFactory" 
    factory-method="createDAO"/>
    
    • 1
    • 2

    8 使用实例工厂创建对象

    public GoodsDAO getDAO(){
    	return new GoodsDAOImpl();
    }
    
    • 1
    • 2
    • 3
    <bean name="daoFactory" class="com.zpark.tea_mgr.factory.DAOFactory"/>
    <bean name="dao2" factory-bean="daoFactory" factory-method="getDAO"/>
    
    • 1
    • 2

    9 实例的作用域

    <bean name="dao1" class="com.zpark.tea_mgr.factory.DAOFactory" 
    factory-method="createDAO" scope="prototype"/>
    
    • 1
    • 2

    scope属性默认为”singleton”,表示单例;取prototype代表多例
    有些对象需要是多例的,比如Struts2中的Action就是多例的,因为他有状态(属性)

    10 Spring的注解

    10.1 导入注解使用的命名空间

    10.2 启动注解

    <context:annotation-config/>
    
    • 1

    10.3 配置注解的扫描路径

    <context:component-scan 
    base-package="com.zpark.tea_mgr.dao, com.zpark.tea_mgr.biz, com.zpark.tea_mgr.controller"/>
    
    • 1
    • 2

    10.4 使用在类上的注解

    @Controller		控制器层
    @Service		业务层
    @Repository		数据访问层
    @Component		通用(通常在你不知道他是什么身份时用)
    @Scope 		默认是单例,如果是单例不要设置
    				要设置成多例时写为: @Scope(value=”prototype”)
    @Lazy			默认是false, 如果不需要懒加载,不要设置
    				要设置成懒加载时写为: @Lazy(value=true)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    10.5 依赖关系上的注解

    @Autowired   by Type 
    @Qualifier(“value”)  by Name ,通常作为@Autowired的补充说明,配套用
    
    @Resource JSR-250规范,效果等价于上两者同时使用
    @PostConstruct  修饰的方法会在对象被初始化时调用
    @PreDestroy	 修饰的方法会在对象被销毁时调用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    10.6 Spring的基于Java配置类的使用

    @Configuration
    @ComponentScan(value = {"com.zpark.tea_mgr.dao", "com.zpark.tea_mgr.biz"})
    public class MyConfig {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    等价于使用applicationContext.xml文件中的

    <context:annotation-config/>
    <context:component-scan 
    base-package="com.zpark.tea_mgr.dao, com.zpark.tea_mgr.biz />
    
    • 1
    • 2
    • 3
    ApplicationContext context = 
    		new AnnotationConfigApplicationContext(MyConfig.class);
    GoodsBIZ goodsBIZ = context.getBean(GoodsBIZ.class);
    goodsBIZ.save();
    
    • 1
    • 2
    • 3
    • 4

    11 AOP

    Spring框架的两个核心概念:  IoC  和 AOP
    AOP: 面向方面(切面)编程  
    AOP是处理实际开发中复杂业务的开发难度的
    复杂的业务: 交叉的业务
    
    一旦交叉出现,提高程序开发难度
    最理想的开发状态是怎样的?分工。开发业务的人就只开发业务代码,开发事务的人仅开发事务就好
     
    现在这个状态是开发时最理想的状态
    开发业务的人只开发业务代码
    开发通用功能的人只编写通用代码
    这样,就达成了0耦合
    但是, 开发时不存在0耦合的情况
    
    现在,使用Spring容器工厂创建对象,创建的对象可以带有业务和通用功能
    
    将事务和日志这类的通用功能当成一个切面
    切到我们所做的业务中
    面向切面编程,就是在不影响你业务功能的基础上,附加其他的功能
    
    在Spring工厂中实现AOP主要使用的是代理模式
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    12 代理模式

    12.1 代理:

    有钱人买车: 1、交钱   2、提车
    中间有很多复杂的事情,可以交给代理人,由代理人帮你完成你不想做的事儿
    
    程序猿A:  保存业务  应该做事务
    程序猿A的代理: 帮A做事务,并代理保存业务
    代理模式: 就是在不影响你业务功能的基础上,附加其他的功能
    代理分为静态代理和动态代理
    静态代理: 被代理类实际存在,代理类也实际存在
    动态代理: 被代理对象和代理类都不清楚,使用反射动态生成
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    12.2 不使用代理

    public class GoodsBIZ {
    	public void save(){
    		System.out.println("事务开始");
    		System.out.println("保存业务");
    		System.out.println("事务结束");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    保存业务和事务功能代码都要自己写

    12.3 使用静态代理的基于父类的代理

    1 编写被代理的类

    public class GoodsBIZ {
    	public void save(){
    		System.out.println("保存业务");
    	}
    
    • 1
    • 2
    • 3
    • 4

    2 编写针对GoodsBIZ的代理类

    public class ProxyGoodsBIZ extends GoodsBIZ {
    	@Override
    	public void save(){
    		System.out.println("事务开始");
    		super.save();
    		System.out.println("事务结束");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3 测试

    public class ProxyTest {
    
    	public static void main(String[] args) {
    		GoodsBIZ proxyGoodsBIZ = new ProxyGoodsBIZ();
    		proxyGoodsBIZ.save();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    12.4 使用静态代理的基于接口的代理

    1 声明一个接口,通过这个接口说明被代理的方法是什么

    public interface BIZ {
    	public void save();
    
    • 1
    • 2

    2 编写被代理类,实现接口

    public class GoodsBIZ implements BIZ {
    	@Override
    	public void save() {
    		System.out.println("保存业务");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3 编写代理类,实现同一个接口

    public class ProxyBIZ implements BIZ {
    	private BIZ goodsBIZ;
    	public ProxyBIZ(BIZ goodsBIZ){
    		this.goodsBIZ = goodsBIZ;
    	}
    	@Override
    	public void save() {
    		System.out.println("事务开始");
    		goodsBIZ.save();
    		System.out.println("事务提交");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在代理类中有一个被代理接口的引用。这个引用将指向的是被代理对象
    4 测试

    public class ProxyTest {
    
    	public static void main(String[] args) {
    		BIZ goodsBIZ = new GoodsBIZ();
    		
    		BIZ proxyBIZ = new ProxyBIZ(goodsBIZ);
    		
    		proxyBIZ.save();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    12.5 找到切入点

    在上一版的代理类中观察,可以在被代理对象的方法调用的哪些位置附加代码?
    
    • 1
    @Override
    	public void save() {
    		try{
    			System.out.println("开始一刀");
    			goodsBIZ.save();
    			System.out.println("完成一刀");
    		}catch(Exception e){
    			e.printStackTrace();
    			System.out.println("异常一刀");
    			throw new RuntimeException(e);
    		}finally{
    			System.out.println("最终一刀");
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    12.6 抽取方面代码,编写切面代码的接口
    1 声明一个接口,说明被代理的方法是什么?

    public interface BIZ {
    	public void save();
    
    • 1
    • 2

    2 声明一个方面接口

    public interface AOP {
    	public void before();
    	public void after();
    	public void err();
    	public void end();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3 编写代理类

    public class ProxyBIZ implements BIZ{
    	
    	private BIZ biz;
    	private AOP aop;
    	
    	public ProxyBIZ(BIZ biz, AOP aop){
    		this.biz = biz;
    		this.aop = aop;
    	}
    
    	@Override
    	public void save(){
    		try{
    			aop.before();
    			biz.save();
    			aop.after();
    		}catch(Exception e){
    			e.printStackTrace();
    			aop.err();
    			throw new RuntimeException(e);
    		}finally{
    			aop.end();
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    4 编写被代理的实现类

    public class GoodsBIZ implements BIZ {
    
    	@Override
    	public void save() {
    		System.out.println("保存业务");
    		System.out.println(5/0);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5 编写方面代码: 事务切面代码

    public class TranAOP implements AOP {
    
    	@Override
    	public void before() {
    		System.out.println("事务开始");
    	}
    
    	@Override
    	public void after() {
    		System.out.println("事务提交");
    	}
    
    	@Override
    	public void err() {
    		System.out.println("事务回滚");
    	}
    
    	@Override
    	public void end() {
    		System.out.println("事务结束");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    6 编写测试类

    public class ProxyTest {
    
    	public static void main(String[] args) {
    		
    		BIZ goodsBIZ = new GoodsBIZ();
    		AOP tranAOP = new TranAOP();
    		
    		ProxyBIZ proxyBIZ = new ProxyBIZ(goodsBIZ, tranAOP);
    		
    		proxyBIZ.save();
    		
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    12.7 基于JDK的动态代理

    动态代理: 可以反射不针对具体的方法进行代理的代理类
    这个动态代理类是根据反射的代码动态生成的,这个类我们看不到
    
    • 1
    • 2

    12.7.1 Proxy类

    public static Object newProxyInstance(ClassLoader loader,
    Class[] interfaces, InvocationHandler h) newProxyInstance方法返回的就是一个代理类的实例。 代理类是在这个方法执行过程中动态创建的,我们看不到 ClassLoader: 类加载器,代理类是动态创建的,执行前没有这个类,他使用被代理对象的类加载器 Class[] interfaces: 被代理的接口数组
    基于JDK的动态代理只能针对接口
    InvocationHandler h: 回调函数
    回调函数就是动态生成的代理类要执行的源代码,它invoke方法里面的内容由我们编写

    12.7.2 InvocationHandler接口

    这个接口代表了动态代理类的行为
    在这个接口中只有一个方法: invoke
    
    • 1
    • 2
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    
    • 1
    • 2

    Object proxy: 动态代理的对象
    Method method: 当前代理的方法对象
    Object[] args: 当前代理的方法的参数
    在开发时,这个接口的实现类要程序员自己辨析,编写的内容就是动态代理的代理内容

    12.8 编写基于JDK的动态代理的工厂类

    1、 ProxyHandler类

    public class ProxyHandler implements InvocationHandler {
    
    	private Object obj;
    	private AOP aop;
    	
    	public ProxyHandler(Object obj, AOP aop){
    		this.obj = obj;
    		this.aop = aop;
    	}
    	
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		try{
    			aop.before();
    			Object proxyObj = method.invoke(obj, args);
    			aop.after();
    			return proxyObj;
    		}catch(Exception e){
    			e.printStackTrace();
    			aop.err();
    			throw new RuntimeException(e);
    		}finally{
    			aop.end();
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2、 工厂类 ProxyFactory

    public class ProxyFactory {
    	public static Object createProxyObjcet(Object obj, AOP aop){
    		return Proxy.newProxyInstance(
    				obj.getClass().getClassLoader(), 
    				obj.getClass().getInterfaces(), 
    				new ProxyHandler(obj, aop));
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3 测试

    BIZ goodsBIZ = new GoodsBIZ();
    AOP tranAOP = new TranAOP();
    BIZ proxyBIZ = (BIZ) ProxyFactory.createProxyObjcet(goodsBIZ, tranAOP);
    proxyBIZ.save();
    
    • 1
    • 2
    • 3
    • 4

    12.9 基于CGLIB的动态代理
    CGLIB是一个第三方实现的动态代理的组件
    12.9.1 导入CGLIB包

    12.9.2 Enhancer类
    通过Enhancer类的静态方法create方法来创建动态代理对象

    Class type:类对象,被代理类的类对象
    Callback callback:回调函数
    12.9.3 Callback接口
    这个接口代表了动态代理类的行为
    一般开发时不直接使用这个Callback接口
    而是使用MethodInterceptor接口

    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2,
    		MethodProxy arg3) throws Throwable {
    Object arg0: 当前被代理对象
    Method arg1: 当前被代理对象的被代理方法
    Object[] arg2:当前被代理方法对象的参数
    MethodProxy arg3:当前被代理方法对象的代理方法对象
    在这个接口中调用被代理对象的原方法
    Object proxyObj = arg3.invokeSuper(arg0, arg2);  这个方法用来返回代理对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    12.10 编写基于CGLIB动态代理类的工厂类

    public class ProxyInterceptor implements MethodInterceptor {
    	private AOP aop;
    	public ProxyInterceptor(AOP aop){
    		this.aop = aop;
    	}
    	@Override
    	public Object intercept(Object arg0, Method arg1, Object[] arg2,
    			MethodProxy arg3) throws Throwable {
    		try{
    			aop.before();
    			Object proxyObj = arg3.invokeSuper(arg0, arg2);
    			aop.after();
    			return proxyObj;
    		}catch(Exception e){
    			e.printStackTrace();
    			aop.err();
    			throw new RuntimeException(e);
    		}finally{
    			aop.end();
    		}
    	}
    }
    public class ProxyFactory {
    	@SuppressWarnings("unchecked")
    	public static <T>T createProxyObj(Class<T> c, AOP aop){
    		return (T)Enhancer.create(c, new ProxyInterceptor(aop));
    	}
    }
    GoodsBIZ proxyGoodsBIZ = ProxyFactory.createProxyObj(goodsBIZ.getClass(), tranAOP);
    proxyGoodsBIZ.delete();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • 相关阅读:
    华为笔记本原厂系统镜像恢复安装教程方法
    MTK手机平台充电原理
    【Linux】进程控制 —— 进程替换
    从面试官角度分析:面试功能测试工程师主要考察哪些能力?
    java基础---RandomAccessFile
    如何写http mjpeg server
    LVGL基础教程 – LVGL 简介
    STM32+USART+DMA+EC600N调试
    cereal:支持C++11的开源序列化库
    C语言----------#pragma预处理分析
  • 原文地址:https://blog.csdn.net/qq_41550190/article/details/126201685