【Java设计模式】二十五、自定义Spring IoC

文章目录

  • 1、IoC类的定义
    • 1.1 定义bean相关的pojo类PropertyValue
    • 1.2 定义MutablePropertyValues类
    • 1.3 定义BeanDefinition类
  • 2、定义注册表相关类
    • 2.1 BeanDefinitionRegistry接口
    • 2.2 SimpleBeanDefinitionRegistry类
  • 3、定义解析器相关类
    • 3.1 BeanDefinitionReader接口
    • 3.2 XmlBeanDefinitionReader类
  • 4、IOC容器相关类
    • 4.1 BeanFactory接口
    • 4.2 ApplicationContext接口
    • 4.3 AbstractApplicationContext类
    • 4.4 ClassPathXmlApplicationContext类
  • 5、测试自定义的IoC
  • 6、自定义Spring IOC总结

自定义Spring框架的IOC,对下面的配置文件进行解析,并对涉及到的对象进行管理

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="userService" class="com.plat.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
    <bean id="userDao" class="com.plat.dao.impl.UserDaoImpl"></bean>
</beans>

1、IoC类的定义

1.1 定义bean相关的pojo类PropertyValue

根据上面xml的特点,定义PropertyValue类,用于封装bean标签下的property标签的各个属性。

  • property.name即对象的属性名
  • property.ref即引用参考对象
  • property.value即对象属性为基本类型或String时的赋的值
public class PropertyValue {

  private String name;  //对象的属性名
  private String ref;  //引用参考对象
  private String value;  //对象属性为基本类型或String时的赋的值


  public PropertyValue() {
  }

  public PropertyValue(String name, String ref,String value) {
    this.name = name;
    this.ref = ref;
    this.value = value;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getRef() {
    return ref;
  }

  public void setRef(String ref) {
    this.ref = ref;
  }

  public String getValue() {
    return value;
  }

  public void setValue(String value) {
    this.value = value;
  }
}

1.2 定义MutablePropertyValues类

一个bean标签可以有多个property子标签,一个property子标签对应一个PropertyValue对象,所以再定义一个MutablePropertyValues类,用来存储并管理多个PropertyValue对象。这里实现Iterable接口,用到了迭代器模式。

public class MutablePropertyValues implements Iterable<PropertyValue> {
	
	//里面存一个个PropertyValue对象(property子标签)。复习:这里用final修饰,list对象本身只能赋值一次,但其里面的元素(对象的属性)是可变的
    private final List<PropertyValue> propertyValueList;
	
	//被final修饰了,必须在其构造方法结束之前对其赋值,因此加有参和无参构造
	
	//无参构造
    public MutablePropertyValues() {
        this.propertyValueList = new ArrayList<PropertyValue>();
    }
	
	//有参构造
    public MutablePropertyValues(List<PropertyValue> propertyValueList) {
        this.propertyValueList = (propertyValueList != null ? propertyValueList : new ArrayList<PropertyValue>());
    }

	/**
	 * 获取所有的Property对象
	*/
    public PropertyValue[] getPropertyValues() {
    	//toArray返回的是Object数组,传个new PropertyValue[0]可让其返回PropertyValue数组,当然不这么写,强转也行
        return this.propertyValueList.toArray(new PropertyValue[0]);
    }

	/**
	 * 根据PropertyValue的name属性值获取PropertyValue对象
	*/
    public PropertyValue getPropertyValue(String propertyName) {
        for (PropertyValue pv : this.propertyValueList) {
            if (pv.getName().equals(propertyName)) {
                return pv;
            }
        }
        return null;
    }
	
	/**
	 * 接口方法重写
	 * 需要返回迭代器对象
	 * 直接把List对象的迭代器扔出去
	*/
    @Override
    public Iterator<PropertyValue> iterator() {
        return propertyValueList.iterator();
    }


	/**
	 * 判断集合是否为空
	*/
    public boolean isEmpty() {
        return this.propertyValueList.isEmpty();
    }

	/**
	 * 添加一个PropertyValue对象
	 * MutablePropertyValues 的返回值类型,用于链式编程
	*/
    public MutablePropertyValues addPropertyValue(PropertyValue pv) {
        for (int i = 0; i < this.propertyValueList.size(); i++) {
            PropertyValue currentPv = this.propertyValueList.get(i);
            if (currentPv.getName().equals(pv.getName())) {
            	//判断传入的PropertyValue对象是否已在集合中,若是,则覆盖(set方法替换指定索引的对象)
                this.propertyValueList.set(i, new PropertyValue(pv.getName(),pv.getRef(), pv.getValue()));
                //也可this.propertyValueList.set(i, pv);  
                return this;
            }
        }
        //无重复
        this.propertyValueList.add(pv);
        return this;
    }

	/**
	 * 判断集合是否包含某个name属性为形参值的PropertyValue对象
	*/
    public boolean contains(String propertyName) {
        return getPropertyValue(propertyName) != null;
    }
}

【补充】:上面那个list转数组的toArray返回的是一个Object[ ],想返回一个指定类型的数组,可强转,也可传个对应类型的数组,数组长度无所谓,0也行。

return this.propertyValueList.toArray(new PropertyValue[0]);
returnPropertyValue[]) this.propertyValueList.toArray();

以上两种等价,第一种的相关源码:也是做了一个转型和数组copy

在这里插入图片描述

1.3 定义BeanDefinition类

BeanDefinition类用来封装bean信息的,主要包含id(即bean对象的名称)、class(需要交由spring管理的类的全类名)及子标签property数据。

public class BeanDefinition {

    private String id;  //bean对象的名称
    
    private String className;   //全类名
    
    private MutablePropertyValues propertyValues;  //property标签

    public BeanDefinition() {
        propertyValues = new MutablePropertyValues();
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public void setPropertyValues(MutablePropertyValues propertyValues) {
        this.propertyValues = propertyValues;
    }

    public MutablePropertyValues getPropertyValues() {
        return propertyValues;
    }
}

2、定义注册表相关类

2.1 BeanDefinitionRegistry接口

BeanDefinitionRegistry接口定义了注册表的相关操作,定义如下功能:

  • 注册BeanDefinition对象到注册表中
  • 从注册表中删除指定名称的BeanDefinition对象
  • 根据名称从注册表中获取BeanDefinition对象
  • 判断注册表中是否包含指定名称的BeanDefinition对象
  • 获取注册表中BeanDefinition对象的个数
  • 获取注册表中所有的BeanDefinition的名称
public interface BeanDefinitionRegistry {

    //注册BeanDefinition对象到注册表中
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

    //从注册表中删除指定名称的BeanDefinition对象
    void removeBeanDefinition(String beanName) throws Exception;

    //根据名称从注册表中获取BeanDefinition对象
    BeanDefinition getBeanDefinition(String beanName) throws Exception;

    boolean containsBeanDefinition(String beanName);

    int getBeanDefinitionCount();

    String[] getBeanDefinitionNames();
    
}

2.2 SimpleBeanDefinitionRegistry类

写BeanDefinitionRegistry的子实现类,该类实现了BeanDefinitionRegistry接口,定义了Map集合作为注册表容器,用来存储BeanDefinition对象 ,选择双列集合,key为BeanDefinition的名称。

public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
	
	//存BeanDefinition
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName,beanDefinition);
    }

    @Override
    public void removeBeanDefinition(String beanName) throws Exception {
        beanDefinitionMap.remove(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) throws Exception {
        return beanDefinitionMap.get(beanName);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }

    @Override
    public int getBeanDefinitionCount() {
        return beanDefinitionMap.size();
    }

    @Override
    public String[] getBeanDefinitionNames() {
    	//toArray(new String[1])的形参是为了把toArray的Object[]返回类型转为String[],传的String[1],长度无所谓
    	//keySet拿到单列的key集合
        return beanDefinitionMap.keySet().toArray(new String[1]);
    }
}

3、定义解析器相关类

3.1 BeanDefinitionReader接口

BeanDefinitionReader是用来解析配置文件,封装成BeanDefinition对象,并在注册表BeanDefinitionRegistry中注册。接口只定义了两个规范,具体逻辑又子类去实现:

  • 获取注册表的功能,让外界可以通过该对象获取注册表对象。
  • 加载配置文件,并注册bean数据。
public interface BeanDefinitionReader {

	//获取注册表对象
    BeanDefinitionRegistry getRegistry();
	//加载配置文件并在注册表中进行注册
    void loadBeanDefinitions(String configLocation) throws Exception;
    
}

3.2 XmlBeanDefinitionReader类

根据不同的配置文件,写BeanDefinitionReader的实现类,如针对properties文件的PropertiesXmlBeanDefinitionReader类,这里写XmlBeanDefinitionReader类是专门用来解析xml配置文件的。该类实现BeanDefinitionReader接口并实现接口中的两个方法

public class XmlBeanDefinitionReader implements BeanDefinitionReader {
	
	//声明注册表对象
    private BeanDefinitionRegistry registry;

	//无参构造中对注册表对象进行赋值(赋子类)
    public XmlBeanDefinitionReader() {
        this.registry = new SimpleBeanDefinitionRegistry();
    }

    @Override
    public BeanDefinitionRegistry getRegistry() {
        return registry;
    }

	/**
	 * 形参为类路径下的xml配置文件的路径
	*/
    @Override
    public void loadBeanDefinitions(String configLocation) throws Exception {
		//通过当前类的类加载器获取输入流对象
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation);
        //dom4j的SAXReader对象
        SAXReader reader = new SAXReader();
        //SAXReader对象的read方法需要传入一个输入流,返回Document对象
        Document document = reader.read(is);
        //获取根标签对象,根据xml的写法,这里即beans标签
        Element rootElement = document.getRootElement();
        //解析beans标签
        parseBean(rootElement);
    }

	/**
	 * 解析beans标签
	*/
    private void parseBean(Element rootElement) {
		//获取根标签beans下的所有子标签对象(所有bean标签)
        List<Element> elements = rootElement.elements();
        //每个Element对象即一个个bean标签
        for (Element element : elements) {
            String id = element.attributeValue("id");   //获取bean标签的id属性
            String className = element.attributeValue("class"); //获取bean标签的class属性
            //将id属性和全类名属性封装到BeanDefinition对象中
            BeanDefinition beanDefinition = new BeanDefinition();  
            beanDefinition.setId(id); 
            beanDefinition.setClassName(className);
            //获取bean标签的所有property子标签
            List<Element> list = element.elements("property"); 
            //创建管理PropertyValues对象(一个property标签一个PropertyValues对象)的MutablePropertyValues对象
            MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
            //一个bean标签下可以有多个property标签
            for (Element element1 : list) {
            	//拿到bean标签下的一个个property标签的属性,封装到一个个PropertyValues对象中
                String name = element1.attributeValue("name");
                String ref = element1.attributeValue("ref");
                String value = element1.attributeValue("value");
                PropertyValue propertyValue = new PropertyValue(name,ref,value);
                //一个个PropertyValues对象最终交给mutablePropertyValues
                mutablePropertyValues.addPropertyValue(propertyValue);
            }
            //beanDefinition对象封装彻底完成
            beanDefinition.setPropertyValues(mutablePropertyValues);
			//将beanDefinition对象注册到注册表中存起来
            registry.registerBeanDefinition(id,beanDefinition);
        }
      
    }
}

到此,解析xml,封装成BeanDefiniton,注册到BeanDefinitonRegistry中存起来的事儿完成了。这就是BeanDefinitionReader干的事儿。注意,上面用dom4j来对xml文件进行解析,jar包坐标:

<dependency>
	<groupId>dom4j</groupId>
	<artifactId>dom4j</artifactId>
	<version>1.6.1</version>
</dependency>

以及通过当前类的类加载器获取输入流对象

InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation);

4、IOC容器相关类

4.1 BeanFactory接口

在该接口中,定义IOC容器的统一规范,即获取bean对象。

public interface BeanFactory {
	//根据bean对象的名称获取bean对象
    Object getBean(String name) throws Exception;
	//根据bean对象的名称获取bean对象,并进行类型转换
    <T> T getBean(String name, Class<? extends T> clazz) throws Exception;
}

4.2 ApplicationContext接口

BeanFactory的子接口,该接口所有的子实现类对bean对象的创建都是非延时的(立即加载),所以在该接口中定义 refresh() 方法,该方法主要完成以下两个功能:

  • 加载配置文件
  • 根据注册表中的BeanDefinition对象封装的数据进行bean对象的创建
/**
 * 非延时加载
 */
public interface ApplicationContext extends BeanFactory {
	//进行配置文件加载并进行对象创建
    void refresh() throws IllegalStateException, Exception;
}

4.3 AbstractApplicationContext类

  • 作为ApplicationContext接口的子类,所以该类也是非延时加载,所以需要在该类中定义一个Map集合,作为bean对象存储的容器。

  • 声明BeanDefinitionReader类型的变量,用来进行xml配置文件的解析,符合单一职责原则。

而BeanDefinitionReader类型属性的对象创建,则交由子类实现,因为只有子类明确到底创建BeanDefinitionReader哪儿子实现类对象。

public abstract class AbstractApplicationContext implements ApplicationContext {

	//声明解析器对象,protected修饰,更加方便子类访问
    protected BeanDefinitionReader beanDefinitionReader;
    
    //用来存储bean对象的容器(Map)   key存储的是bean的id值,value存储的是bean对象
    protected Map<String, Object> singletonObjects = new HashMap<String, Object>();

    //存储xml配置文件的路径
    protected String configLocation;
    
	/**
	 * 定义refresh方法
	 */
    public void refresh() throws IllegalStateException, Exception {

        //加载封装注册BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(configLocation);

        //初始化bean(创建Bean对象)
        finishBeanInitialization();
    }

    //bean的初始化
    private void finishBeanInitialization() throws Exception {
    	//铜鼓解析器获取注册表对象
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        //获取BeanDefinition的名称
        String[] beanNames = registry.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
            //进行Bean的初始化
            getBean(beanName);
        }
    }
}

注意:该类finishBeanInitialization()方法中调用getBean()目前是BeanFactory接口的一给抽象方法,以后子类去实现,这里使用到了模板方法模式。

4.4 ClassPathXmlApplicationContext类

该类主要是加载类路径下的配置文件,并进行bean对象的创建,主要完成以下功能:

  • 在构造方法中,创建BeanDefinitionReader对象(实现类)
  • 在构造方法中,调用refresh()方法,用于进行配置文件加载、创建bean对象并存储到容器中
  • 重写父接口中的getBean()方法,并实现依赖注入操作,注入这里依赖注入的实现思路!
//Ioc容器具体的子实现类,用于加载类路径下的xml的配置文件,创建对应的Bean
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{
	
	//有参构造方法,形参为xml路径
    public ClassPathXmlApplicationContext(String configLocation) {
        this.configLocation = configLocation;
        //构建XmlBeanDefinitionReader对象
        beanDefinitionReader = new XmlBeanDefinitionReader();
        try {
            this.refresh();
        } catch (Exception e) {
        }
    }

    /**
     * 实现getBean方法,根据bean的id属性值获取bean对象
     */
    @Override
    public Object getBean(String name) throws Exception {

        //父类中定义的Map集合,用来存储Bean对象
        //先看Map里有没,有则直接返回
        Object obj = singletonObjects.get(name);
        if(obj != null) {
            return obj;
        }
		//根据解析器获取注册表对象
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        //根据注册表中获取到封装Bean信息的BeanDefinition对象
        BeanDefinition beanDefinition = registry.getBeanDefinition(name);
        if(beanDefinition == null) {
            return null;
        }
        //获取全类名
        String className = beanDefinition.getClassName();
        Class<?> clazz = Class.forName(className);
        //反射创建对象
        Object beanObj = clazz.newInstance();
        //解析bean标签的property子标签,给反射得到的对象赋值(做依赖注入)
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        //遍历所有的property标签
        for (PropertyValue propertyValue : propertyValues) {
        	//获取属性的名称、值、引用参考
            String propertyName = propertyValue.getName();
            String value = propertyValue.getValue();
            String ref = propertyValue.getRef();
            //根据spring的xml语法,bean标签下的property子标签,value和ref属性肯定只有其中一个
            if(ref != null && !"".equals(ref)) {
				//获取依赖的Bean的对象,getBean方法递归
                Object bean = getBean(ref);
                //根据属性名获取set方法的名称
                String methodName = getSetterMethodNameByFieldName(propertyName);
                //反射获取Bean定义中全类名类的所有的方法对象
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if(method.getName().equals(methodName)) {
                    	//循环判断拿到set方法对象,执行这个set方法,给要返回的beanObj对象的引用属性,调用set方法赋值,赋依赖的bean对象
                    	//到此,完成了依赖注入
                        method.invoke(beanObj,bean);
                    }
                }
            }
			//如果属性是基本类型,即value有值
            if(value != null && !"".equals(value)) {
            	//拿到属性的set方法名称
                String methodName = getSetterMethodNameByFieldName(propertyName);
                //反射拿到set方法对象
                Method method = clazz.getMethod(methodName, String.class);
                //调用set方法,beanObj对象调用set方法,赋值为value
                method.invoke(beanObj,value);
            }
        }
        //将创建的对象,放到Map中,下次可直接获取,不用再反射(单例)
        singletonObjects.put(name,beanObj);
        return beanObj;
    }

    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {

        Object bean = getBean(name);
        //相比前面的getBean,多一个转型
        if(bean != null) {
            return clazz.cast(bean);
        }
        return null;
    }

	//根据属性名获取set方法的名称 name ==> setName,及set + 首字母截取并大写 + 拼装首字母以外的全部
	public static String getSetterMethodNameByFieldName(String fieldName) {
		return "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
	}
}



5、测试自定义的IoC

测试上面定义的IoC容器,能否完成xml的解析和读取,并创建出Bean。首先将上面自定义IoC的模块安装到本地仓库:

在这里插入图片描述

新建个干净的Maven工程,引入上面的自定义Ioc模块:

<dependency>
	<groupId>com.plat</groupId>
	<artifactId>myIoc_spring</artifactId>
	<version>1.0.0</version>
</dependency>

xml中定义Bean:

<bean id="userService" class="com.plat.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.plat.dao.impl.UserDaoImpl"></bean>

准备测试类:

@Setter
public class UserServiceImpl implements UserService {

	private UserDao ueserDao;   //声明个UserDao类型的对象

	public UserServiceImpl(){
		System.out.println("userService被创建了");
	}
	public void add(){
		System.out.println("userService ...");
		userDao.add();
	}
}

public class UserDaoImpl implements UserDao {
	
	public UserDaoImpl(){
		System.out.println("UserDao被创建了");
	}

	public void add(){
		System.out.println("userDao ...");
	}
}

测试:ApplicationContext和ClassPathXmlApplicationContext类均为上面自己定义的:

public class MyApplication {

	public static void main(String[] args){
		//创建Spring容器对象
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicaitonContext.xml");
		//从容器中获取Bean
		UserService bean = applicationContext.getBean("userService", UserService.class);
		bean.add();

	}
}

运行,看到Bean和依赖注入的Dao对象都成功了,且Debug可以看到依旧是非延时加载Bean。

在这里插入图片描述

6、自定义Spring IOC总结

自定义IoC使用到的设计模式:

  • 工厂模式:使用了工厂模式 + 配置文件(xml配置,
  • 单例模式:Spring IOC管理的bean对象都是单例的,此处的单例不是通过构造器进行单例的控制的,而是spring框架对每一个bean只创建了一个对象
  • 模板方法模式:AbstractApplicationContext类中的finishBeanInitialization()方法调用了子类的getBean()方法,因为getBean()的实现和环境息息相关
  • 迭代器模式:对于MutablePropertyValues类定义使用到了迭代器模式,因为此类存储并管理PropertyValue对象,也属于一个容器,所以给该容器提供一个遍历方式

spring框架其实使用到了很多设计模式,如AOP使用到了代理模式,选择JDK代理或者CGLIB代理使用到了策略模式,还有适配器模式,装饰者模式,观察者模式等。

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

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

相关文章

还是了解下吧,大语言模型调研汇总

大语言模型调研汇总 一. Basic Language ModelT5GPT-3LaMDAJurassic-1MT-NLGGopherChinchillaPaLMU-PaLMOPTLLaMABLOOMGLM-130BERNIE 3.0 Titan 二. Instruction-Finetuned Language ModelT0FLANFlan-LMBLOOMZ & mT0GPT-3.5ChatGPTGPT-4AlpacaChatGLMERNIE BotBard 自从Cha…

FFmpeg转码参数说明及视频转码示例

-b : 设置音频或者视频的转码码率 -b:v 只设置视频码率 -b:a 只设置音频码率 -ab: 只设置音频码率, 默认码率大小为: 128k bit/s -g: 设置视频GOP大小,表示I帧之间的间隔,默认为12 -ar: 设置音频采样率,默认0 -ac: 设置音频通道数量 默认0 -bf: 设置连…

[自研开源] MyData 数据集成之数据过滤 v0.7.2

开源地址&#xff1a;gitee | github 详细介绍&#xff1a;MyData 基于 Web API 的数据集成平台 部署文档&#xff1a;用 Docker 部署 MyData 使用手册&#xff1a;MyData 使用手册 试用体验&#xff1a;https://demo.mydata.work 交流Q群&#xff1a;430089673 概述 本篇基于…

spring boot nacos注册微服务示例demo_亲测成功

spring boot nacos注册微服务示例demo_亲测成功 先安装好Nacos Nacos安装使用 创建Maven项目 结构如图 例如项目名为: test-demo 下面有个子模块: test-demo-data-process 父模块pom.xml <?xml version"1.0" encoding"UTF-8"?> <project …

【Flink SQL】Flink SQL 基础概念(三):SQL 动态表 连续查询

《Flink SQL 基础概念》系列&#xff0c;共包含以下 5 篇文章&#xff1a; Flink SQL 基础概念&#xff08;一&#xff09;&#xff1a;SQL & Table 运行环境、基本概念及常用 APIFlink SQL 基础概念&#xff08;二&#xff09;&#xff1a;数据类型Flink SQL 基础概念&am…

数据有噪声?滤它!Python数据滤波详解

文章目录 维纳滤波巴特沃斯滤波器中值滤波排序滤波 Python科学计算&#xff1a;数组&#x1f4af;数据生成&#x1f4af;数据交互&#x1f4af;微积分&#x1f4af;插值&#x1f4af;拟合&#x1f4af;FFT&#x1f4af;卷积 维纳滤波 信号经过系统之后&#xff0c;相当于进行…

简单的arduino实验理解串口通信(uart为例)独立硬件的信息交互

前言 接触过单片机的人都知道串口通信&#xff0c;可以通过另一个短文了解,其中入门的应该就是串口通信了。UART全拼的个人理解为通用的异步接收和发送。常见两根短线作为通信线&#xff0c;一般使用TXD和RXD标记。对于两块通信的芯片来说&#xff0c;接收和发送是相对的&…

Stargo 管理部署 Starrocks 集群

配置主机间 ssh 互信 ssh-copy-id hadoop02 ssh-copy-id hadoop03配置系统参数 ############################ Swap检查 ############################ echo 0 | sudo tee /proc/sys/vm/swappiness########################### 内核参数检查 ########################## echo…

PHP+golang开源办公系统CRM管理系统

基于ThinkPHP6 Layui MySQL的企业办公系统。集成系统设置、人事管理、消息管理、审批管理、日常办公、客户管理、合同管理、项目管理、财务管理、电销接口集成、在线签章等模块。系统简约&#xff0c;易于功能扩展&#xff0c;方便二次开发。 服务器运行环境要求 PHP > 7.…

2.3 物理层设备

2.3 物理层设备 &#xff08;一&#xff09;中继器 产生原因 由于存在损耗&#xff0c;在线路上传输的信号功率会逐渐衰减&#xff0c;衰减到一定程度时将造成信号失真&#xff0c;因此会导致接收错误。 中继器的功能 对信号进行再生和还原&#xff0c;对衰减的信号进行放大…

ArkTs的资源Resource类型怎么转为string

使用ResourceManager同步转换 请参看&#xff1a;ResourceManager.getStringSync9 例子&#xff1a; try { let testStr: string this.context.resourceManager.getStringSync($r(app.string.test).id); } catch (error) { console.error(getStringSync failed, error code…

GEE数据集——全球( 30 弧秒)尺度地下水模型GLOBGM v1.0数据集

全球尺度地下水模型GLOBGM v1.0 GLOBGM v1.0 数据集是全球地下水建模的一个重要里程碑&#xff0c;提供了 30 弧秒 PCR-GLOBWB-MODFLOW 模型的并行实施。该数据集由 Jarno Verkaik 等人开发&#xff0c;以赤道约 1 公里的空间分辨率全面展示了全球地下水动态。该数据集利用两个…

VUE-组件间通信(一)props

props 1、单向绑定 props是父组件给子组件传输数据 当父组件的属性变化时&#xff0c;将传导给子组件&#xff0c;但是反过来不会 2、使用示例 子组件&#xff08;类似于方法&#xff09; <template> <div><h2>姓名:{{ name }}</h2><h2>性别:{{…

前端之CSS 创建css--行内引入、内联样式、外联样式

创建css有三种创建样式&#xff0c;行内引入、内联引入、外联引入。 行内引入 在行内标签引入 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>行内样式</title> </head> <body>…

Ubuntu 虚拟机安装

最小化安装后常用工具 sudo apt-get install vim# ifconfig apt install net-tools # nload apt install nload # 很多都要用到 apt install build-essential # 开发相关 apt install gcc gapt install iproute2 ntpdate tcpdump telnet traceroute \ nfs-kernel-server nfs…

mac打开exe文件的三大方法 mac怎么运行exe文件 mac打开exe游戏 macbookpro打开exe

exe文件是Windows系统的可执行文件&#xff0c;虽然Mac系统上无法直接打开exe文件&#xff0c;但是你可以在Mac电脑上安装双系统或者虚拟机来实现mac电脑上运行exe文件。除了这两种方法之外&#xff0c;你还可以在Mac电脑上使用类虚拟机软件打开exe文件&#xff0c;这三种方法各…

Java学习笔记------常用API(五)

爬虫 从网站中获取 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.regex.Matcher; import java.util.reg…

动态规划(算法竞赛、蓝桥杯)--单调队列优化烽火传递

1、B站视频链接&#xff1a;E43【模板】单调队列优化DP 烽火传递_哔哩哔哩_bilibili 题目链接&#xff1a;https://loj.ac/p/10180 #include <bits/stdc.h> using namespace std; const int N2e510; int n,m,w[N],f[N],q[N];int main(){cin>>n>>m;for(int …

生产线上的“变形金刚”:码垛机器人的崛起

在工业的森林里&#xff0c;有一种神奇的生物——码垛机器人。它们以精确无误的动作和不知疲倦的身躯&#xff0c;在生产线上演绎着一幕幕现代版的“变形金刚”。这些机械奇才不仅解放了人类的双手&#xff0c;更是以它们的“魔法”提升了生产效率&#xff0c;降低了成本&#…

[SAP ABAP] 使用事务码SU3改变日期与时间格式

当我们执行上述代码&#xff0c;返回结果如下所示 我们发现获取当前系统日期返回的日期格式并不是MM/DD/YYYY&#xff0c;而是YYYY.MM.DD的日期格式&#xff0c;那么我们怎样才能使得MM/DD/YYYY这种日期格式生效&#xff1f; 我们可以使用事务码SU3来改变日期或时间格式 配置完…