当前位置: 首页 > news >正文

设计模式(5)-使用设计模式实现简易版springIoc

自定义简易版springIoc

1 spring使用回顾

自定义spring框架前,先回顾一下spring框架的使用,从而分析spring的核心,并对核心功能进行模拟。

  • 数据访问层。定义UserDao接口及其子实现类

    public interface UserDao {public void add();
    }public class UserDaoImpl implements UserDao {public void add() {System.out.println("userDaoImpl ....");}
    }
    
  • 业务逻辑层。定义UserService接口及其子实现类

    public interface UserService {public void add();
    }public class UserServiceImpl implements UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void add() {System.out.println("userServiceImpl ...");userDao.add();}
    }
    
  • 定义UserController类,使用main方法模拟controller层

    public class UserController {public static void main(String[] args) {//创建spring容器对象ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");//从IOC容器中获取UserService对象UserService userService = applicationContext.getBean("userService", UserService.class);//调用UserService对象的add方法userService.add();}
    }
    
  • 编写配置文件。在类路径下编写一个名为ApplicationContext.xml的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"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"><bean id="userService" class="com.kuangzeng.service.impl.UserServiceImpl"><property name="userDao" ref="userDao"></property></bean><bean id="userDao" class="com.kuangzeng.dao.impl.UserDaoImpl"></bean></beans>
    

    代码运行结果如下:

在这里插入图片描述

通过上面代码及结果可以看出:

  • userService对象是从applicationContext容器对象获取到的,也就是userService对象交由spring进行管理。
  • 上面结果可以看到调用了UserDao对象中的add方法,也就是说UserDao子实现类对象也交由spring管理了。
  • UserService中的userDao变量我们并没有进行赋值,但是可以正常使用,说明spring已经将UserDao对象赋值给了userDao变量。

上面三点体现了Spring框架的IOC(Inversion of Control)和DI(Dependency Injection, DI)

2 spring核心功能结构

Spring大约有20个模块,由1300多个不同的文件构成。这些模块可以分为:

核心容器、AOP和设备支持、数据访问与集成、Web组件、通信报文和集成测试等,下面是 Spring 框架的总体架构图:

在这里插入图片描述

核心容器由 beans、core、context 和 expression(Spring Expression Language,SpEL)4个模块组成。

  • spring-beans和spring-core模块是Spring框架的核心模块,包含了控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)。BeanFactory使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离。BeanFactory属于延时加载,也就是说在实例化容器对象后并不会自动实例化Bean,只有当Bean被使用时,BeanFactory才会对该 Bean 进行实例化与依赖关系的装配。
  • spring-context模块构架于核心模块之上,扩展了BeanFactory,为它添加了Bean生命周期控制、框架事件体系及资源加载透明化等功能。此外,该模块还提供了许多企业级支持,如邮件访问、远程访问、任务调度等,ApplicationContext 是该模块的核心接口,它的超类是 BeanFactory。与BeanFactory不同,ApplicationContext实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配,使之处于待用状态。
  • spring-context-support模块是对Spring IoC容器及IoC子容器的扩展支持。
  • spring-context-indexer模块是Spring的类管理组件和Classpath扫描组件。
  • spring-expression 模块是统一表达式语言(EL)的扩展模块,可以查询、管理运行中的对象,同时也可以方便地调用对象方法,以及操作数组、集合等。它的语法类似于传统EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。EL的特性是基于Spring产品的需求而设计的,可以非常方便地同Spring IoC进行交互。

1.1 bean概述

Spring 就是面向 Bean 的编程(BOP,Bean Oriented Programming),Bean 在 Spring 中处于核心地位。Bean对于Spring的意义就像Object对于OOP的意义一样,Spring中没有Bean也就没有Spring存在的意义。Spring IoC容器通过配置文件或者注解的方式来管理bean对象之间的依赖关系。

spring中bean用于对一个类进行封装。如下面的配置:

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

为什么Bean如此重要呢?

  • spring 将bean对象交由一个叫IOC容器进行管理。
  • bean对象之间的依赖关系在配置文件中体现,并由spring完成。

3 Spring IOC相关接口分析

3.1 BeanFactory解析

Spring中Bean的创建是典型的工厂模式,这一系列的Bean工厂,即IoC容器,为开发者管理对象之间的依赖关系提供了很多便利和基础服务,在Spring中有许多IoC容器的实现供用户选择,其相互关系如下图所示。

在这里插入图片描述

其中,BeanFactory作为最顶层的一个接口,定义了IoC容器的基本功能规范,BeanFactory有三个重要的子接口:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是从类图中我们可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。

那么为何要定义这么多层次的接口呢?

每个接口都有它的使用场合,主要是为了区分在Spring内部操作过程中对象的传递和转化,对对象的数据访问所做的限制。例如,

  • ListableBeanFactory接口表示这些Bean可列表化。
  • HierarchicalBeanFactory表示这些Bean 是有继承关系的,也就是每个 Bean 可能有父 Bean
  • AutowireCapableBeanFactory 接口定义Bean的自动装配规则。

这三个接口共同定义了Bean的集合、Bean之间的关系及Bean行为。最基本的IoC容器接口是BeanFactory,来看一下它的源码:

public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";//根据bean的名称获取IOC容器中的的bean对象Object getBean(String name) throws BeansException;//根据bean的名称获取IOC容器中的的bean对象,并指定获取到的bean对象的类型,这样我们使用时就不需要进行类型强转了<T> T getBean(String name, Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);//判断容器中是否包含指定名称的bean对象boolean containsBean(String name);//根据bean的名称判断是否是单例boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String name) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
}

在BeanFactory里只对IoC容器的基本行为做了定义,根本不关心你的Bean是如何定义及怎样加载的。正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。

BeanFactory有一个很重要的子接口,就是ApplicationContext接口,该接口主要来规范容器中的bean对象是非延时加载,即在创建容器对象的时候就对象bean进行初始化,并存储到一个容器中。

在这里插入图片描述

要知道工厂是如何产生对象的,我们需要看具体的IoC容器实现,Spring提供了许多IoC容器实现,比如:

  • ClasspathXmlApplicationContext : 根据类路径加载xml配置文件,并创建IOC容器对象。
  • FileSystemXmlApplicationContext :根据系统路径加载xml配置文件,并创建IOC容器对象。
  • AnnotationConfigApplicationContext :加载注解类配置,并创建IOC容器。

3.2 BeanDefinition解析

Spring IoC容器管理我们定义的各种Bean对象及其相互关系,而Bean对象在Spring实现中是以BeanDefinition来描述的,如下面配置文件

<bean id="userDao" class="com.kuangzeng.dao.impl.UserDaoImpl"></bean>bean标签还有很多属性:scope、init-method、destory-method等。

其继承体系如下图所示。

在这里插入图片描述

3.3 BeanDefinitionReader解析

Bean的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过BeanDefinitionReader来完成,看看Spring中BeanDefinitionReader的类结构图,如下图所示。

在这里插入图片描述

看看BeanDefinitionReader接口定义的功能来理解它具体的作用:

public interface BeanDefinitionReader {//获取BeanDefinitionRegistry注册器对象BeanDefinitionRegistry getRegistry();@NullableResourceLoader getResourceLoader();@NullableClassLoader getBeanClassLoader();BeanNameGenerator getBeanNameGenerator();/*下面的loadBeanDefinitions都是加载bean定义,从指定的资源中*/int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

3.4 BeanDefinitionRegistry解析

BeanDefinitionReader用来解析bean定义,并封装BeanDefinition对象,而我们定义的配置文件中定义了很多bean标签,所以就有一个问题,解析的BeanDefinition对象存储到哪儿?答案就是BeanDefinition的注册中心,而该注册中心顶层接口就是BeanDefinitionRegistry。

public interface BeanDefinitionRegistry extends AliasRegistry {//往注册表中注册beanvoid registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException;//从注册表中删除指定名称的beanvoid removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;//获取注册表中指定名称的beanBeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;//判断注册表中是否已经注册了指定名称的beanboolean containsBeanDefinition(String beanName);//获取注册表中所有的bean的名称String[] getBeanDefinitionNames();int getBeanDefinitionCount();boolean isBeanNameInUse(String beanName);
}

继承结构图如下:

在这里插入图片描述

从上面类图可以看到BeanDefinitionRegistry接口的子实现类主要有以下几个:

  • DefaultListableBeanFactory

    在该类中定义了如下代码,就是用来注册bean

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    
  • SimpleBeanDefinitionRegistry

    在该类中定义了如下代码,就是用来注册bean

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
    

3.5 创建容器

ClassPathXmlApplicationContext对Bean配置资源的载入是从refresh()方法开始的。refresh()方法是一个模板方法,规定了 IoC 容器的启动流程,有些逻辑要交给其子类实现。它对 Bean 配置资源进行载入,ClassPathXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()方法启动整个IoC容器对Bean定义的载入过程。

4 自定义SpringIOC

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

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

4.1 定义bean相关的pojo类

4.1.1 PropertyValue类

用于封装bean的属性,体现到上面的配置文件就是封装bean标签的子标签property标签数据。

public class PropertyValue {private String name;private String ref;private String value;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;}
}
4.1.2 MutablePropertyValues类

一个bean标签可以有多个property子标签,所以再定义一个MutablePropertyValues类,用来存储并管理多个PropertyValue对象。

public class MutablePropertyValues implements Iterable<PropertyValue> {private final List<PropertyValue> propertyValueList;public MutablePropertyValues() {this.propertyValueList = new ArrayList<PropertyValue>();}public MutablePropertyValues(List<PropertyValue> propertyValueList) {this.propertyValueList = (propertyValueList != null ? propertyValueList : new ArrayList<PropertyValue>());}public PropertyValue[] getPropertyValues() {return this.propertyValueList.toArray(new PropertyValue[0]);}public PropertyValue getPropertyValue(String propertyName) {for (PropertyValue pv : this.propertyValueList) {if (pv.getName().equals(propertyName)) {return pv;}}return null;}@Overridepublic Iterator<PropertyValue> iterator() {return propertyValueList.iterator();}public boolean isEmpty() {return this.propertyValueList.isEmpty();}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())) {this.propertyValueList.set(i, new PropertyValue(pv.getName(),pv.getRef(), pv.getValue()));return this;}}this.propertyValueList.add(pv);return this;}public boolean contains(String propertyName) {return getPropertyValue(propertyName) != null;}
}
4.1.3 BeanDefinition类

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

public class BeanDefinition {private String id;private String className;private MutablePropertyValues propertyValues;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;}
}

4.2 定义注册表相关类

4.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();
}
4.2.2 SimpleBeanDefinitionRegistry类

该类实现了BeanDefinitionRegistry接口,定义了Map集合作为注册表容器。

public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {beanDefinitionMap.put(beanName,beanDefinition);}@Overridepublic void removeBeanDefinition(String beanName) throws Exception {beanDefinitionMap.remove(beanName);}@Overridepublic BeanDefinition getBeanDefinition(String beanName) throws Exception {return beanDefinitionMap.get(beanName);}@Overridepublic boolean containsBeanDefinition(String beanName) {return beanDefinitionMap.containsKey(beanName);}@Overridepublic int getBeanDefinitionCount() {return beanDefinitionMap.size();}@Overridepublic String[] getBeanDefinitionNames() {return beanDefinitionMap.keySet().toArray(new String[1]);}
}

4.3 定义解析器相关类

4.3.1 BeanDefinitionReader接口

BeanDefinitionReader是用来解析配置文件并在注册表中注册bean的信息。定义了两个规范:

  • 获取注册表的功能,让外界可以通过该对象获取注册表对象。
  • 加载配置文件,并注册bean数据。
public interface BeanDefinitionReader {//获取注册表对象BeanDefinitionRegistry getRegistry();//加载配置文件并在注册表中进行注册void loadBeanDefinitions(String configLocation) throws Exception;
}
4.3.2 XmlBeanDefinitionReader类

XmlBeanDefinitionReader类是专门用来解析xml配置文件的。该类实现BeanDefinitionReader接口并实现接口中的两个功能。

public class XmlBeanDefinitionReader implements BeanDefinitionReader {private BeanDefinitionRegistry registry;public XmlBeanDefinitionReader() {this.registry = new SimpleBeanDefinitionRegistry();}@Overridepublic BeanDefinitionRegistry getRegistry() {return registry;}@Overridepublic void loadBeanDefinitions(String configLocation) throws Exception {InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation);SAXReader reader = new SAXReader();Document document = reader.read(is);Element rootElement = document.getRootElement();//解析bean标签parseBean(rootElement);}private void parseBean(Element rootElement) {List<Element> elements = rootElement.elements();for (Element element : elements) {String id = element.attributeValue("id");String className = element.attributeValue("class");BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setId(id);beanDefinition.setClassName(className);List<Element> list = element.elements("property");MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();for (Element element1 : list) {String name = element1.attributeValue("name");String ref = element1.attributeValue("ref");String value = element1.attributeValue("value");PropertyValue propertyValue = new PropertyValue(name,ref,value);mutablePropertyValues.addPropertyValue(propertyValue);}beanDefinition.setPropertyValues(mutablePropertyValues);registry.registerBeanDefinition(id,beanDefinition);}}
}

4.4 IOC容器相关类

4.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.4.2 ApplicationContext接口

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

  • 加载配置文件。
  • 根据注册表中的BeanDefinition对象封装的数据进行bean对象的创建。
public interface ApplicationContext extends BeanFactory {//进行配置文件加载并进行对象创建void refresh() throws IllegalStateException, Exception;
}
4.4.3 AbstractApplicationContext类
  • 作为ApplicationContext接口的子类,所以该类也是非延时加载,所以需要在该类中定义一个Map集合,作为bean对象存储的容器。

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

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

public abstract class AbstractApplicationContext implements ApplicationContext {protected BeanDefinitionReader beanDefinitionReader;//用来存储bean对象的容器   key存储的是bean的id值,value存储的是bean对象protected Map<String, Object> singletonObjects = new HashMap<String, Object>();//存储配置文件的路径protected String configLocation;public void refresh() throws IllegalStateException, Exception {//加载BeanDefinitionbeanDefinitionReader.loadBeanDefinitions(configLocation);//初始化beanfinishBeanInitialization();}//bean的初始化private void finishBeanInitialization() throws Exception {BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();String[] beanNames = registry.getBeanDefinitionNames();for (String beanName : beanNames) {BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);getBean(beanName);}}
}

注意:该类finishBeanInitialization()方法中调用getBean()方法使用到了模板方法模式。

4.4.4 ClassPathXmlApplicationContext类

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

  • 在构造方法中,创建BeanDefinitionReader对象。
  • 在构造方法中,调用refresh()方法,用于进行配置文件加载、创建bean对象并存储到容器中。
  • 重写父接口中的getBean()方法,并实现依赖注入操作。
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{public ClassPathXmlApplicationContext(String configLocation) {this.configLocation = configLocation;//构建XmlBeanDefinitionReader对象beanDefinitionReader = new XmlBeanDefinitionReader();try {this.refresh();} catch (Exception e) {}}//根据bean的id属性值获取bean对象@Overridepublic Object getBean(String name) throws Exception {//return singletonObjects.get(name);Object obj = singletonObjects.get(name);if(obj != null) {return obj;}BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();BeanDefinition beanDefinition = registry.getBeanDefinition(name);if(beanDefinition == null) {return null;}String className = beanDefinition.getClassName();Class<?> clazz = Class.forName(className);Object beanObj = clazz.newInstance();MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();for (PropertyValue propertyValue : propertyValues) {String propertyName = propertyValue.getName();String value = propertyValue.getValue();String ref = propertyValue.getRef();if(ref != null && !"".equals(ref)) {Object bean = getBean(ref);String methodName = StringUtils.getSetterMethodNameByFieldName(propertyName);Method[] methods = clazz.getMethods();for (Method method : methods) {if(method.getName().equals(methodName)) {method.invoke(beanObj,bean);}}}if(value != null && !"".equals(value)) {String methodName = StringUtils.getSetterMethodNameByFieldName(propertyName);Method method = clazz.getMethod(methodName, String.class);method.invoke(beanObj,value);}}singletonObjects.put(name,beanObj);return beanObj;}@Overridepublic <T> T getBean(String name, Class<? extends T> clazz) throws Exception {Object bean = getBean(name);if(bean != null) {return clazz.cast(bean);}return null;}
}

4.5 自定义Spring IOC总结

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

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

4.5.2 符合大部分设计原则
4.5.3 整个设计和Spring的设计还是有一定的出入

spring框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性。而我们自定义SpringIOC有以下几个目的:

  • 了解Spring底层对对象的大体管理机制。
  • 了解设计模式在具体的开发中的使用。
  • 以后学习spring源码,通过该案例的实现,可以降低spring学习的入门成本。

相关文章:

设计模式(5)-使用设计模式实现简易版springIoc

自定义简易版springIoc 1 spring使用回顾 自定义spring框架前&#xff0c;先回顾一下spring框架的使用&#xff0c;从而分析spring的核心&#xff0c;并对核心功能进行模拟。 数据访问层。定义UserDao接口及其子实现类 public interface UserDao {public void add(); }public…...

数据结构与集合源码

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…...

nodejs+vue面向中小学课堂教学辅助软件系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

主要功能有&#xff0c;管理员通过后台会对此教学辅助进行审核&#xff0c;管理员在还可以进行首页、个人中心、学生管理、教师管理、班级信息管理、科目名称管理、课程信息管理、教学资料管理、作业信息管理、作业提交管理、作业成绩管理、在线考试管理、试题管理、考试管理、…...

智能配电系统解决方案

智能配电系统解决方案是一种集成了先进技术和智能化功能的配电系统&#xff0c;它能够提高电力系统的效率、可靠性和安全性。力安科技智能配电系统解决方案依托电易云-智慧电力物联网&#xff0c;具体实施的方案如下&#xff1a; 智能化设备和传感器&#xff1a;采用智能化的开…...

Python基础入门---conda 如何管理依赖包以及复制相同环境的

文章目录 创建虚拟环境:创建虚拟环境并指定Python版本:安装依赖包:从环境导出依赖包清单:从依赖包清单创建环境:复制环境:移植环境:在Conda中,你可以使用conda create命令来创建和管理虚拟环境,而使用conda install命令来安装和管理依赖包。以下是一些基本的命令和步骤…...

JVM jstat 查看内存新生代老年代回收情况,排查oom

jstat 命令 jstat - [-t] [-h] [ []] option&#xff1a;我们经常使用的选项有gc、gcutil vmid&#xff1a;java进程id interval&#xff1a;间隔时间&#xff0c;单位为毫秒 count&#xff1a;打印次数 每秒打印一次 jstat -gc 9162 1000S0C:年轻代第一个survivor的容量…...

Postman启动问题:Could not open Postman

Postman启动问题&#xff1a;Could not open Postman 状态&#xff0c;在单击Postman之后一直在转圈圈&#xff0c;无法正常启动。 细心的朋友会发现&#xff0c;右下角 会经常出现防火墙关闭等提示信息&#xff0c;表示该程序&#xff0c;在向外链接。 Error Could not open…...

Golang起步篇(Windows、Linux、mac三种系统安装配置go环境以及IDE推荐以及入门语法详细释义)

Golang起步篇 Golang起步篇一. 安装Go语言开发环境1. Wondows下搭建Go开发环境(1). 下载SDK工具包(2). 解压下载的压缩包&#xff0c;放到特定的目录下&#xff0c;我一般放在d:/programs下(路径不能有中文或者特殊符号如空格等)(3). 配置环境变量步骤1&#xff1a;先打开环境变…...

Error message “error:0308010C:digital envelope routines::unsupported“

1.降级到 Node.js v16。 您可以从 Node.js 的 website 重新安装当前的 LTS 版本。 您也可以使用 nvm。对于 Windows&#xff0c;请使用 nvm-windows。 2.启用传统 OpenSSL 提供程序。 在类 Unix 系统&#xff08;Linux、macOS、Git bash 等&#xff09;上&#xff1a; exp…...

解决java在idea运行正常,但是打成jar包后中文乱码问题

目录 比如&#xff1a; 打包命令使用utf-8编码&#xff1a; 1.当在idea中编写的程序,运行一切正常.但是当被打成jar包时,执行的程序会中文乱码.产生问题的原因和解决方案是什么呢? 一.问题分析 分别使用idea和jar包形式打印出System中所有的jvm参数---代码如下: public static…...

数据结构-插入排序+希尔排序+选择排序

目录 1.插入排序 插入排序的时间复杂度&#xff1a; 2.希尔排序 希尔排序的时间复杂度&#xff1a; 3.选择排序 选择排序的时间复杂度&#xff1a; 所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的…...

微信小程序数据传递的方式-页面数据的存取

我们在把数据显示到页面的时候&#xff0c;为了实现良好的互动&#xff0c;都希望在用户点击某个栏目后&#xff0c;获取这个栏目的捆绑数据&#xff0c;然后执行后续的操作。 例如&#xff0c;从数据库里取出对应的记录后&#xff0c;显示在页面上&#xff0c;是一条条的大横条…...

Flutter 应用启动从闪屏页短暂黑屏再到第一个页面

由于应用初始状态启动会有白屏现象&#xff0c;便使用 flutter_native_splash 2.3.5 插件生成了启动相关的配置&#xff0c;并且按照示例使用了 import package:flutter_native_splash/flutter_native_splash.dart;void main() {WidgetsBinding widgetsBinding WidgetsFlutte…...

Linux+qt:获取.so自身的路径(利用dladdr)

目录 1、QDir::currentPath() 2、QAppllication::appllicationDirPath() 3、获取.so自身的路径&#xff08;利用dladdr&#xff09; Qt中&#xff0c;也有相关的接口获取程序的相关路径的。 先了解下相关的接口&#xff1a; 1、QDir::currentPath() &#xff08;1&#x…...

CSS特效014:模仿钟摆效果

CSS常用示例100专栏目录 本专栏记录的是经常使用的CSS示例与技巧&#xff0c;主要包含CSS布局&#xff0c;CSS特效&#xff0c;CSS花边信息三部分内容。其中CSS布局主要是列出一些常用的CSS布局信息点&#xff0c;CSS特效主要是一些动画示例&#xff0c;CSS花边是描述了一些CSS…...

计算机毕业设计选题推荐-个人健康微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…...

【自然语言处理(NLP)实战】LSTM网络实现中文文本情感分析(手把手与教学超详细)

目录 引言&#xff1a; 1.所有文件展示&#xff1a; 1.中文停用词数据&#xff08;hit_stopwords.txt)来源于&#xff1a; 2.其中data数据集为chinese_text_cnn-master.zip提取出的文件。点击链接进入github&#xff0c;点击Code、Download ZIP即可下载。 2.安装依赖库&am…...

迭代新品 | 第四代可燃气体监测仪,守护燃气管网安全快人一步

城市地下市政基础设施是城市有序运行的生命线&#xff0c;事关城市安全、健康运行和高质量发展。近年来&#xff0c;我国燃气事故多发、频发。2020、2021、2022 年分别发生燃气事故668、1140 起、802 起&#xff0c;造成92、106、66 人死亡&#xff0c;560、763、487 人受伤。尤…...

【教3妹学编程-java基础6】详解父子类变量、代码块、构造函数执行顺序

-----------------第二天------------------------ 本文先论述父子类变量、代码块、构造函数执行顺序的结论&#xff0c; 然后通过举例论证&#xff0c;接着再扩展&#xff0c;彻底搞懂静态代码块、动态代码块、构造函数、父子类、类加载机制等知识体系。 温故而知新&#xff…...

深度学习中文汉字识别 计算机竞赛

文章目录 0 前言1 数据集合2 网络构建3 模型训练4 模型性能评估5 文字预测6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习中文汉字识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xf…...

从零开始 通义千问大模型本地化到阿里云通义千问API调用

从零开始 通义千问大模型本地化到阿里云通义千问API调用 一、通义千问大模型介绍 何为“通义千问”&#xff1f; “通义千问大模型”是阿里云推出的一个超大规模的语言模型&#xff0c;具有强大的归纳和理解能力&#xff0c;可以处理各种自然语言处理任务&#xff0c;包括但…...

Linux(3):Linux 的文件权限与目录配置

把具有相同的账户放入到一个组里面&#xff0c;这个组就是这两个账户的 群组 。在访问资源&#xff08;操作系统中计算机的资源&#xff09;时&#xff0c;可以让这个组里面的所有用户都具有访问权限。 每个账号都可以有多个群组的支持。 在我们Liux 系统当中&#xff0c;默认的…...

Linux进程——exec族函数、exec族函数与fork函数的配合

exec族函数解析 作用 我们用fork函数创建新进程后&#xff0c;经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时&#xff0c;该进程被完全替换为新程序。因为调用exec函数并不创建新进程&#xff0c;所以前后进程的ID并没有改变。 功能 在调用进程内部…...

客户端缓存技术

客户端缓存技术主要有以下几种&#xff1a; 内存缓存&#xff1a;客户端&#xff08;如浏览器&#xff09;会将请求到的资源&#xff08;如HTML页面、图片文件等&#xff09;存储在内存中&#xff0c;以便在再次访问相同资源时可以快速获取&#xff0c;减少向服务器的请求次数…...

Leetcode -2

Leetcode Leetcode -263.丑数Leetcode -268.丢失的数字 Leetcode -263.丑数 题目&#xff1a;丑数就是只包含质因数 2、3 和 5 的正整数。 给你一个整数 n &#xff0c;请你判断 n 是否为 丑数 。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例…...

使用 DFS 轻松求解数独难题(C++ 的一个简单实现)

起因 都说懒惰是第一生产力&#xff0c;最近在玩数独游戏的时候&#xff0c;总会遇到拆解数独比较复杂的情况&#xff0c;就想着自己写个代码解题&#xff0c;解放双手。所以很快就写了一个简单的代码求解经典数独。拿来跑了几个最高难度的数独发现确实很爽&#xff01;虽说是…...

【SQL server】 表结构的约束和维护

表结构的约束和维护 修改表结构 (1)添加列 (2)删除列 (3)修改列alter table 表名 add 新列名 数据类型给员工表添加一列邮箱 alter table People add PeopleMail varchar(200)删除列 alter table People drop column PeopleMain修改列 alter table 表名 alter column 列名 数据…...

竞赛 题目:基于大数据的用户画像分析系统 数据分析 开题

文章目录 1 前言2 用户画像分析概述2.1 用户画像构建的相关技术2.2 标签体系2.3 标签优先级 3 实站 - 百货商场用户画像描述与价值分析3.1 数据格式3.2 数据预处理3.3 会员年龄构成3.4 订单占比 消费画像3.5 季度偏好画像3.6 会员用户画像与特征3.6.1 构建会员用户业务特征标签…...

Vue3-ref、reactive函数的watch

Vue3-ref、reactive函数的watch ref函数的watch 原理&#xff1a;监视某个属性的变化。当被监视的属性一旦发生改变时&#xff0c;执行某段代码。watch 属性中的数据需要具有响应式watch 属性可以使用箭头函数watch 属性可以监视一个或者多个响应式数据&#xff0c;并且可以配…...

【智能家居项目】FreeRTOS版本——多任务系统中使用DHT11 | 获取SNTP服务器时间 | 重新设计功能框架

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f353;多任务系统中使用DHT11&#x1f345;关闭调度器&#x1f345;使用中断 &am…...

Power BI Desktop数据可视化图表

...

鸿蒙APP外包开发需要注意的问题

在进行鸿蒙&#xff08;HarmonyOS&#xff09;应用开发时&#xff0c;开发者需要注意一些重要的问题&#xff0c;以确保应用的质量、性能和用户体验。以下是一些鸿蒙APP开发中需要特别关注的问题&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软…...

Redis 19 事务

Redis通过MULTI、EXEC、WATCH等命令来实现事务&#xff08;transaction&#xff09;功能。事务提供了一种将多个命令请求打包&#xff0c;然后一次性、按顺序地执行多个命令的机制&#xff0c;并且在事务执行期间&#xff0c;服务器不会中断事务而改去执行其他客户端的命令请求…...

Fabric多机部署启动节点与合约部署

这是我搭建的fabric的网络拓扑 3 个 orderer 节点&#xff1b;组织 org1 , org1 下有两个 peer 节点&#xff0c; peer0 和 peer1; 组织 org2 , org2 下有两个 peer 节点&#xff0c; peer0 和 peer1; 以上是我的多机环境的网络拓扑&#xff0c;使用的是docker搭建的。我的网络…...

WordPress主题WoodMart v7.3.2 WooCommerce主题和谐汉化版下载

WordPress主题WoodMart v7.3.2 WooCommerce主题和谐汉化版下载 WoodMart是一款出色的WooCommerce商店主题&#xff0c;它不仅提供强大的电子商务功能&#xff0c;还与流行的Elementor页面编辑器插件完美兼容。 主题文件在WoodMart Theme/woodmart.7.3.2.zip&#xff0c;核心在P…...

Java 高等院校分析与推荐系统

1&#xff09;项目简介 随着我国高等教育的大众化&#xff0c;高校毕业生就业碰到了前所未有的压力&#xff0c;高校学生就业问题开始进入相关研究者们的视野。在高校学生供给忽然急剧增加的同时&#xff0c;我国高校大学生的就业机制也在发生着深刻的变化&#xff0c;作为就业…...

【JVM】Java虚拟机

本文主要介绍了JVM的内存区域划分,类加载机制以及垃圾回收机制. 其实JVM的初心,就是让java程序员不需要去了解JVM的细节,它把很多工作内部封装好了.但是学习JVM的内部原理有利于我们深入理解学习Java. 1.JVM的内存区域划分 JVM其实是一个java进程 ; 每个java进程,就是一个jvm…...

业务架构、技术架构、项目管理的有机结合

新入职的创业公司一年不行了。 这一年来没有上班&#xff0c;也因为大龄的问题找不到合适的工作。然后考了几个项目管理证书&#xff0c;又思考了一个技术兑现的问题。 技术本身是架构的执行层面&#xff0c;如果上面的公司战略、业务架构变小&#xff0c;缩水&#xff0c;或者…...

拜耳阵列(Bayer Pattern)以及常见彩色滤波矩阵(CFA)

一、拜耳阵列的来源 图像传感器将光线转化成电流&#xff0c;光线越亮&#xff0c;电流的数值就越大&#xff1b;光线越暗&#xff0c;电流的数值就越小。图像传感器只能感受光的强弱&#xff0c;无法感受光的波长。由于光的颜色由波长决定&#xff0c;所以图像传播器无法记录…...

【信息安全】浅谈IDOR越权漏洞的原理、危害和防范:直接对象引用导致的越权行为

前言 ┌──────────────────────────────────┐ │ 正在播放《越权访问》 - Hanser │ ●━━━━━━─────── 00:00 / 03:05 │ ↻ ◁ ❚❚ ▷ ⇆ └───────────────────────────────…...

uni-app 蓝牙打印, CPCL指令集使用

先上代码: GitHub - byc233518/uniapp-bluetooth-printer-demo: 使用uniApp 连接蓝牙打印机 Demo, CPCL 指令简单实用示例 (内含 芝珂,佳博,精臣 多个厂家指令集使用文档) 文件结构: ├── App.vue ├── CPCL 指令手册.pdf // 指令集参考手册 ├── LICENSE ├── R…...

vue-组件通信(二)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue-组件通信(二) 目录 组件通信(二) &#xff08;1&#xff09; props / $emit 1. 父组件向子组…...

2023年【危险化学品经营单位安全管理人员】考试题及危险化学品经营单位安全管理人员模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试题是安全生产模拟考试一点通总题库中生成的一套危险化学品经营单位安全管理人员模拟试题&#xff0c;安全生产模拟考试一点通上危险化学品经营单位安全管理人员作业手机同步练习。…...

Uni-App常用事件

Uni-App是一个跨平台的前端开发框架&#xff0c;支持多个平台的应用开发&#xff0c;包括H5、小程序、App等。在Uni-App中&#xff0c;有许多常用的事件可以用来处理用户交互、页面生命周期等方面的逻辑。以下是一些Uni-App中常用的事件&#xff1a; 点击事件&#xff08;click…...

【笔记 Pytorch】稀疏矩阵、scipy.sparse模块的使用

安装&#xff1a;pip install scipy 描述&#xff1a;就是专门为了解决稀疏矩阵而生。导入模块&#xff1a;from scipy import sparse 优缺点总结 七种矩阵类型描述coo_matrix ★【名称】coordinate format 【优点】    ① 不同稀疏格式间转换效率高(特别是CSR和CSC)  …...

C#学习相关系列之Linq常用方法---排序(一)

一、构建数据 public class Student_1{public int ID { get; set; }public string Name { get; set; }public int Chinese { get; set; }public int Math { get; set; }public int English { get; set; }public override string ToString(){return string.Format("ID:{0},…...

Android Proguard混淆

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、语法规则3.1 输入/输出选项3.2 保留选项3.3 缩…...

MySQL 1、初识数据库

一、什么是数据库&#xff1f; 以特定的格式保存好的文件&#xff0c;我们就叫做数据库。 提供较为便捷的数据的存取服务的软件集合、解决方案&#xff0c;我们就叫它数据库。 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库。 文件或数据库都可以存储数据&#…...

H5ke11--3介绍本地,会话存储

代码顺序: 1.设置input,捕获input如果有多个用属性选择符例如 input[typefile]点击事件.向我们的本地存储设置键值对 2.在点击事件外面设置本地存储表示初始化的值.点击上面的事件才能修改我们想修改的值 会话(session)浏览a数据可以写到本地硬盘,关闭页面数据就没了 本地(…...

技术分享 | 如何写好测试用例?

对于软件测试工程师来说&#xff0c;设计测试用例和提交缺陷报告是最基本的职业技能。是非常重要的部分。一个好的测试用例能够指示测试人员如何对软件进行测试。在这篇文章中&#xff0c;我们将介绍测试用例设计常用的几种方法&#xff0c;以及如何编写高效的测试用例。 ## 一…...