spring 笔记

news2024/10/3 0:28:40

一、spring概述

 

1.1 spring介绍

spring是一个轻量级的控制反转和面向切面的容器框架,用来解决企业项目开发的复杂度问题---解耦

  • 轻量级:体积小,对代码没有侵入性
  • 控制反转:IOC inverse of control, 把创建对象的工作交由spring完成,spring在创建对象的同时可以完成对象属性的赋值(DI)
  • 面向切面:AOP aspect oriented programming 面向切面编程,可以在不改变原有业务逻辑的情况下实现对业务的增强
  • 容器:实例的容器,管理创建的对象

1.2 spring架构简介

image-20220802084037659

1.2.1 core container

spring的容器组件,用于完成实例的创建和管理

  • core 容器组件的核心
  • beans 实例对象的管理
  • context 容器上下文

1.2.2 AOP Aspects

spring的AOP组件,实现面向切面编程

  • aop 
  • aspects

1.2.3 WEB

Spring web组件,实际指的是springMVC框架,实现web项目的MVC控制

  • web ---spring 对web项目的支持
  • webmvc --- springMVC

1.2.4 Data Access 

spring 数据访问组件,也是一个基于JDBC封装的持久层框架(即使没有mybatis,spring也可以完成持久化操作)

  • transactions  事务管理

1.2.5 test

spring的单元测试组件,提供了spring环境下的单元测试支持。

二、Spring IOC --- 基于XML

spring ioc 容器组件,可以完成对象的创建,对象属性赋值,对象的管理

2.1 spring框架部署(IOC相关)

  1. 创建maven工程
  2. 添加springIOC依赖
    1. core
    2. beans
    3. context --- 其实只导入这一个就行了,因为其他的依赖也会一起导入
  3. 创建spring配置文件
    1. 配置文件来“告诉”spring容器创建什么对象,给对象属性怎么赋值
    2. resources目录下创建名为applicationContext.xml文件(文件名可以自定义,需要定义一些规则,可以创建一个模板)
spring 依赖
<!--
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-core</artifactId>
	<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-beans</artifactId>
	<version>5.2.10.RELEASE</version>
</dependency>
-->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.2.10.RELEASE</version>
</dependency>

2.2 springIOC的使用 :创建并管理对象

  1. 创建do实体类,如Student类
  2. applicationContext.xml文件中配置bean标签
  3. 初始化spring容器,加载spring配置文件,获取实例对象
只完成实例的创建,但是属性没有值
<bean id="studentDo唯一标识" name="dao" class="Student的全限定名"/>

完成实例的创建,同时给name属性赋值
<bean id="studentDo唯一标识" name="dao" class="Student的全限定名">
	<property name="stuName" value="zhangsan" />
</bean>

public static void main(String[] args) {
	ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
	//此处根据bean标签的id属性和name属性的任意一个值来获取bean对象
    // 创建出来student对象,但是属性没有值
	Student Student1 = (Student) ctx.getBean("studentDo"); 
}

2.3 IOC和DI 

  • IOC inverse of control控制反转,当我们需要通过spring完成对象的创建时,需要将这个类交给spring进行管理 ---- 通过配置bean标签,spring反射
  • DI dependency injection 依赖注入,通过spring容器,给创建的对象属性赋值

2.4 DI 

2.4.1 依赖注入的方式 --- 反射

spring容器加载配置文件后,通过反射创建类的对象,并给属性赋值。通过反射实现属性注入有以下几种方式:

  • set方法注入
  • 构造器注入
  • 接口注入(不常用。。。)

2.4.2 set注入

在bean标签中通过配置property标签给属性赋值,实际就是通过set方法进行注入

  • 简单类型及字符串 ----> 直接通过property标签的value属性进行赋值
  • 引用类型   
    • ----> 使用property标签ref属性注入引用类型对象
    • ----> 在property标签里面创建bean标签
  • 集合类型
    • list 
      • list<简单类型封装类或者字符串>, 可以在property 的value里面直接赋值 逗号隔开
      • list<引用类型>  见下面
    • set ---和list一样,就是把list标签变成set
    • map
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
	<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.imipl.BookDaoImpl"/>

-------

<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
	<property name="bookDao">
		<bean id="bookDao" class="com.itheima.dao.imipl.BookDaoImpl"/>
	</property>
</bean>

--------
list 依赖注入1
<property name="hobbies" value="唱,跳,rap">

---------
list 依赖注入2
<property name="hobbies">
	<list>
		<value>唱</value>
		<value>跳</value>
		<value>rap</value>
	</list>
</property>

---------
list 依赖注入3

<property name="hobbies">
	<list>
		<bean id="book" class="com.itheima.Book"/>
		<bean id="book" class="com.itheima.Book"/>
		<bean id="book" class="com.itheima.Book"/>
	</list>
</property>

---------
list 依赖注入4

<property name="hobbies">
	<list>
		<ref id="book"/>
		<ref id="book"/>
	</list>
</property>

<bean id="book" class="com.itheima.Book"/>

---------
map

<property name="card">
	<map>
		<entry  key="身份证" value="123712739171293"></entry>
		<entry  key="银行卡" value="234234234324344"></entry>
	</map>
</property>

2.4.3 构造器注入

  • 简单类型和字符串
    • 1、定义有参构造器,2、使用constructor-arg标签,index值为第几个参数,value为要注入的值
  • 对象类型
  • 集合类型
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
	<constructor-arg name="databaseName" index="0" value="mysql"/>
	<constructor-arg name="connectionNum" index="1" value="666"/>
</bean>

<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
	<constructor-arg name="bookDao" ref="bookDao"/>
	<constructor-arg name="userDao" ref="userDao"/>
</bean>

2.5 ioc- bean的作用域

同一个bean_id ,通过配置scope 的值来指定对象的作用域

  • singleton ---单例模式  默认的值,默认饿汉模式,初始化阶段就会完成实例的创建
  • prototype ---多例模式
<bean id="book" class="book的全限定名" scope="" lazy-init=""/>
scope 有两个取值
singleton ---单例模式  默认的
prototype ---多例模式

lazy-init默认取值false,饿汉模式,设置为true可以改为懒汉模式

2.6 bean 的生命周期

bean标签中可以

  • 通过init-method属性指定当前bean的初始化方法,在构造器执行之后执行
  • 通过destroy-method指定当前bean的销毁方法,在对象销毁之前执行

2.7 自动装配

spring在实例化当前bean时,重spring自己的容器中找到匹配的实例赋值给当前bean的属性,通过bean标签中的autowire进行配置

  • byName:根据当前bean的属性名, 在spring容器中寻找名称匹配的对象(bean中的属性名称和spring容器中其他bean的id 进行比较,有可能导致类型不一致)
  • byType:根据当前bean的属性类型,在spring容器中寻找类型匹配的对象 (要是配置了两个不同id的bean,也会报错)

2.8 ioc的工作原理

三、Spring IOC --- 基于注解

3.1 spring框架部署

3.1.1 创建maven项目

3.1.2添加springIOC依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.13.RELEASE</version>
</dependency>

3.1.3 创建spring配置文件,声明spring的扫描范围

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

    <!-- 声明使用注解配置 -->
    <context:annotation-config/>

    <!-- 声明Spring工厂注解的扫描范围 -->
    <context:component-scan base-package="com.qfedu.beans"/>

</beans>

3.2 IOC的常见注解

3.2.1 @Component

  • 类注解,声明此类被spring容器进行管理,相当于bean标签
  • @Component(value=“stu”)value属性的值相当于bean标签中的id,可以省略,如果省略则当前类的id默认为类名首字母改小写
  • 除了@Component, @Service、@Controller、@Repository这三个注解也可以将类声明给Spring管理,他们主要是语义上的区别
    • @Controller 注解主要声明将控制器类配置给Spring管理,例如Servlet
    • @Service 注解主要声明业务处理类配置Spring管理,Service接口的实现类
    • @Repository 直接主要声明持久化类配置给Spring管理,DAO接口

3.2.2 @Scope

  • 类注解,用于声明当前类是否是单例模式,相当于bean标签的scope属性
  • @Scope("prototype") 表示声明当前类为非单例模式(默认单例模式)

3.2.3 @Lazy

  • 类注解,用于声明一个单例bean,是否为懒汉模式
  • 默认为饿汉模式,@Lazy(true) 表示声明为懒汉模式

3.2.4 @PostConstruct

  • 方法注解,声明一个方法为当前类的初始化方法(在构造器之后执行),相当于bean标签的init-method属性

3.2.5 @PreDestory

  • 方法注解,声明一个方法为当前类的销毁方法(在对象从容器中释放之前执行),相当于bean标签的destory-method属性

3.2.6 @Autowoired

  • 属性注解,声明当前属性自动装配,默认为byType
  • @Autowired(required = false) 通过requried属性设置当前自动装配是否为必须(默认必须——如果没有找到类型与属性类型匹配的bean则抛出异常)
// 也可以用在set方法上,ref引用,@Qualifier来byName寻找
@Autowired
public void setClazz(@Qualifier("c2") Clazz clazz) {
    this.clazz = clazz;
}

3.2.7 @Resource

  • 属性注解,也用于声明属性自动装配

  • 默认装配方式为byName,如果根据byName没有找到对应的bean,则继续根据byType寻找对应的bean,根据byType如果依然没有找到Bean或者找到不止一个类型匹配的bean,则抛出异常。

四,动态代理

代理设计模式的优点:将通用性的工作都交给代理对象完成,被代理对象只需专注自己的核心业务。

动态代理,几乎可以为所有的类产生代理对象

4.1 JDK动态代理类实现

JDK动态代理是通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类的对象。没有实现接口的类是不能用这种方式产生代理对象。

//使用代理对象调用方法,并不会执行调用的方法,
//而是进入到创建代理对象时指定的InvocationHandler类种的invoke方法,调用的方法作为一个Method参数,传递给了invoke方法

/***
 * JDK动态代理:是通过被代理对象实现的接口产生其代理对象的
 * 1.创建一个类,实现InvocationHandler接口,重写invoke方法
 * 2.在类种定义一个Object类型的变量,并提供这个变量的有参构造器,用于将被代理对象传递进来
 * 3.定义getProxy方法,用于创建并返回代理对象
 */
public class JDKDynamicProxy implements InvocationHandler {
    //被代理对象
    private Object obj;
    public JDKDynamicProxy(Object obj) {
        this.obj = obj;
    }
    //产生代理对象,返回代理对象
    public Object getProxy(){
        //1.获取被代理对象的类加载器
        ClassLoader classLoader = obj.getClass().getClassLoader();
        //2.获取被代理对象的类实现的接口
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        //3.产生代理对象(通过被代理对象的类加载器及实现的接口)
        //第一个参数:被代理对象的类加载器
        //第二个参数:被代理对象实现的接口
        //第三个参数:使用产生代理对象调用方法时,用于拦截方法执行的处理器
        Object proxy = Proxy.newProxyInstance(classLoader, interfaces,this);
        return proxy;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        begin();
        Object returnValue = method.invoke(obj,args);  //执行method方法(insert)
        commit();
        return returnValue;
    }

    public void begin(){
        System.out.println("----------开启事务");
    }

    public void commit(){
        System.out.println("----------提交事务");
    }
}

// --------------测试------------------

 //创建被代理对象
BookDAOImpl bookDAO = new BookDAOImpl();
StudentDAOImpl studentDAO = new StudentDAOImpl();

//创建动态代理类对象,并将被代理对象传递到代理类中赋值给obj
JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(studentDAO);

//proxy就是产生的代理对象:产生的代理对象可以强转成被代理对象实现的接口类型
GenaralDAO proxy = (GenaralDAO)jdkDynamicProxy.getProxy();

//使用代理对象调用方法,并不会执行调用的方法,
//而是进入到创建代理对象时指定的InvocationHandler类种的invoke方法
//调用的方法作为一个Method参数,传递给了invoke方法
proxy.insert(student);

4.2 CGLib动态代理实现

CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此即使没有实现任何接口的类也可以通过CGLib产生代理对象

因为是创建子类,因此CGLib动态代理不能为final类创建代理对象

添加CGLib的依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
/**
 * 1.添加cglib依赖
 * 2.创建一个类,实现MethodInterceptor接口,同时实现接口中的intercept方法
 * 3.在类中定义一个Object类型的变量,并提供这个变量的有参构造器,用于传递被代理对象
 * 4.定义getProxy方法创建并返回代理对象(代理对象是通过创建被代理类的子类来创建的)
 */
public class CGLibDynamicProxy implements MethodInterceptor {

    private Object obj;
    public CGLibDynamicProxy(Object obj) {
        this.obj = obj;
    }

    public Object getProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxy = enhancer.create();
        return proxy;
    }


    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        begin();
        Object returnValue = method.invoke(obj,objects); //通过反射调用被代理类的方法
        commit();
        return returnValue;
    }

    public void begin(){
        System.out.println("----------开启事务");
    }

    public void commit(){
        System.out.println("----------提交事务");
    }
}


// ------------测试 ------------

//创建被代理对象
BookDAOImpl bookDAO = new BookDAOImpl();
StudentDAOImpl studentDAO = new StudentDAOImpl();

//通过cglib动态代理类创建代理对象
CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(bookDAO);
//代理对象实际上是被代理对象子类,因此代理对象可直接强转为被代理类类型
BookDAOImpl proxy = (BookDAOImpl) cgLibDynamicProxy.getProxy();

//使用对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法,将当前调用的方法以及方法中的参数传递到intercept方法
proxy.update();

五、Spring AOP

5.1 AOP的概念

aspect oriented programming 面向切面编程,是一种利用横切的技术,对原有的业务逻辑进行拦截,并且可以在这个拦截的横切面上添加特点的业务逻辑,实现不改变代码对原有业务的增强。

底层的实现就是动态代理。

连接点 joinPoint:原始程序中的方法

切入点pointCut:被spring横切的方法

通知/增强:

切点:添加到切入点的 新增的业务方法

切面:定义切点方法的类

5.2 Spring AOP 框架部署

5.2.1 创建maven项目

5.2.2 添加依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.13.RELEASE</version>
</dependency>

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

5.3  AOP配置 --基于xml

例子:在DAO方法中添加开启事务和提交事务的逻辑

5.3.1 创建一个类,定义要添加的业务逻辑 --- 这个类就是切面,里面定义的方法就是切点

public class TxManager {
    public void begin(){
        System.out.println("-----------开启事务");
    }

    public void commit(){
        System.out.println("-----------提交事务");
    }
}

5.3.2 配置AOP

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

    <bean id="bookDAO" class="com.qfedu.dao.BookDAOImpl"></bean>
    <bean id="studentDAO" class="com.qfedu.dao.StudentDAOImpl"></bean>

    <!---->
    <bean id="txManager" class="com.qfedu.utils.TxManager"></bean>
    <aop:config>
        <!--声明切入点-->
        <aop:pointcut id="book_all" expression="execution(* com.qfedu.dao.*.*(..))"/>

        <!--声明txManager为切面类-->
        <aop:aspect ref="txManager">
            <!--通知-->
            <aop:before method="begin" pointcut-ref="book_all"/>
            <aop:after method="commit" pointcut-ref="book_all"/>
        </aop:aspect>
    </aop:config>

</beans>

总结AOP的开发步骤:

  1. 创建一个切面类,定义切点方法
  2. 将切面类配置给spring容器
  3. 声明切入点
  4. 配置AOP的通知策略

5.4 切入点的声明

5.4.1 各种切入点声明方式

<!--使用aop:pointcut标签声明切入点:切入点可以是一个方法-->
<aop:pointcut id="book_insert" expression="execution(* com.qfedu.dao.BookDAOImpl.insert())"/>

<!--BookDAOImpl类中所有无参数无返回值的方法-->
<aop:pointcut id="book_pc1" expression="execution(void com.qfedu.dao.BookDAOImpl.*())"/>

<!--BookDAOImpl类中所有无返回值的方法-->
<aop:pointcut id="book_pc2" expression="execution(void com.qfedu.dao.BookDAOImpl.*(..))"/>

<!--BookDAOImpl类中所有无参数的方法-->
<aop:pointcut id="book_pc3" expression="execution(* com.qfedu.dao.BookDAOImpl.*())"/>

<!--BookDAOImpl类中所有方法-->
<aop:pointcut id="book_pc4" expression="execution(* com.qfedu.dao.BookDAOImpl.*(..))"/>

<!--dao包中所有类中的所有方法-->
<aop:pointcut id="pc5" expression="execution(* com.qfedu.dao.*.*(..))"/>

<!--dao包中所有类中的insert方法-->
<aop:pointcut id="pc6" expression="execution(* com.qfedu.dao.*.insert(..))"/>

5.4.2 AOP使用的注意事项

如果要使用spring aop 面向切面编程,调用切入点方法的对象必须是通过spring容器获取的。

如果一个类中的方法被声明为切入点并织入了切点之后,通过spring容器获取该类对象,实际获取到的是一个代理对象

如果一个类中的方法没有被声明为切入点,通过spring容器获取的就是这个类真实的对象。

//BookServiceImpl bookService = new BookServiceImpl();
BookServiceImpl bookService = (BookServiceImpl) context.getBean("bookServiceImpl");
bookService.addBook();

5.5 AOP的通知策略

AOP的通知策略就是声明将切面类中的切点方法 如何织入到切入点

  • before -- 前置通知
  • after -- 后置 通知
  • after-throwing -- 异常之后的通知
  • after-returning -- 方法返回值之后,与 after的时机属于一个时间点,看那个先配置就先执行那个
  • around   -- 环绕通知,定义环绕通知的切点方法要遵守特定的规则
<bean id="myAspect" class="com.qfedu.utils.MyAspect"></bean>
<aop:config>
    <!--使用aop:pointcut标签声明切入点:切入点可以是一个方法-->
    <aop:pointcut id="book_insert" expression="execution(* com.qfedu.dao.BookDAOImpl.insert())"/>

    <aop:aspect ref="myAspect">
        <!--aop:before 前置通知,切入到指定切入点之前-->
        <aop:before method="method1" pointcut-ref="book_insert"/>
        <!--aop:after 后置通知,切入到指定切入点之后-->
        <aop:after method="method2" pointcut-ref="book_insert"/>
        <!--aop:after-throwing 异常通知,切入点抛出异常之后-->
        <aop:after-throwing method="method3" pointcut-ref="book_insert"/>
        <!--aop:after-returning 方法返回值返回之后,对于一个Java方法而言return返回值也是方法的一部分
             因此“方法返回值返回之后”和“方法执行结束”是同一个时间点,随意after 和 after-returning根据配置的顺序决定执行顺序-->
        <aop:after-returning method="method4" pointcut-ref="book_insert"/>
        
        <aop:around method="method5" pointcut-ref="book_insert"/>
    </aop:aspect>

</aop:config>
public class MyAspect {

    public void method1(){
        System.out.println("~~~~~~~method1");
    }
    public void method2(){
        System.out.println("~~~~~~~method2");
    }
    public void method3(){
        System.out.println("~~~~~~~method3");
    }
    public void method4(){
        System.out.println("~~~~~~~method4");
    }

    //环绕通知的切点方法,必须准守如下的定义规则:
    //1.必须带有一个ProceedingJoinPoint类型的参数
    //2.必须有Object类型的返回值
    //3.在前后增强的业务逻辑之间执行Object v = point.proceed();
    //4.方法最后返回v
    public Object method5(ProceedingJoinPoint point) throws Throwable {
        System.out.println("~~~~~~~method5---before");
        //此代码的执行,就表示切入点方法的执行
        Object v = point.proceed();
        System.out.println("~~~~~~~method5---after");
        return v;
    }

}

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

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

相关文章

JUC并发编程Ⅰ -- Java中的线程

文章目录线程与进程并行与并发进程与线程应用应用之异步调用应用之提高效率线程的创建方法一&#xff1a;通过继承Thread类创建方法二&#xff1a;使用Runnable配合Thread方法三&#xff1a;使用FutureTask与Thread结合创建查看进程和线程的方法线程运行的原理栈与栈帧线程上下…

MAC Boook打印长图

有时老师给留的作业是一张长图&#xff0c;直接打印或者通过把图放入word打印都不能实现把长页分成多页进行打印。通过网上找到思路可以通过EXCEL实现将长图分成多页打印。 测试版本 macos&#xff1a;ventura 13.1 office 365 注&#xff1a;同样适用windows版本的excel 第…

cass10.1+鸿业生成平纵横数据

cass10.1鸿业生成平纵横数据前言1 纵断面数据获取1.1 数据准备1.2 纵断面桩号设置&#xff08;1&#xff09;桩号设置&#xff08;2&#xff09;桩号标注&#xff08;3&#xff09;标注显示1.3 高程数据处理1.4 纵断面里程标高文件生成2. cass10.1生成横断面数据2.1 生成横断面…

区块链技术与应用2——BTC-数据结构

文章目录比特币中的数据结构1. 区块链&#xff08;block chain&#xff09;2. 默克尔树&#xff08;Merkle tree&#xff09;3.哈希指针的问题比特币中的数据结构 1. 区块链&#xff08;block chain&#xff09; 哈希指针&#xff1a; &#xff08;1&#xff09;保存数值的位置…

基于 Python 实时图像获取及处理软件图像获取;图像处理;人脸识别设计 计算机毕设 附完整代码+论文 +报告

界面结果:图像获取;图像处理;人脸识别 程序结构设计 图形用户界面设计与程序结构设计是互为表里的。或者说,程序结构设计是软件设计最本质、最核心的内容。徒有界面而内部逻辑结构混乱的软件一无是处。 Windows 操作系统是一款图形化的操作系统,相比于早期的计算机使用的命…

XXL-JOB 任务调度平台实践

XXL-JOB 任务调度平台实践一、调度中心(服务端)1、从gitbub 获取项目源码&#xff1a;[https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job)2、从源码中得到SQL脚本创建和初始化数据库3、Maven 编译打包 xxl-job-admin 并部署为调度中心4、启动运行 xxl-…

Linux 编译器 gcc/g++

本文已收录至《Linux知识与编程》专栏&#xff01; 作者&#xff1a;ARMCSKGT 演示环境&#xff1a;CentOS 7 目录 前言 正文 gcc/g常用命令 自定义可执行程序名命令-o 预处理指令-E 编译指令-S 汇编指令-c 链接指令gcc 命令巧记口诀 链接库 动态库-动态链接 静态库…

双11大型互动游戏“喵果总动员” 质量保障方案总结

推荐语&#xff1a;互动游戏是一个系统化工程&#xff0c;在笔者的“喵果总动员”质量方案中&#xff0c;可以看到为保障用户体验&#xff0c;我们在各个难点的解决方案&#xff0c; 例如&#xff1a;用线上压测能力支持业务及时调整各服务容量、通过强化学习覆盖游戏行业的测试…

设计师一定要知道这几个网站,解决你80%的设计素材。

本期推荐一波设计师必备的设计素材网站&#xff0c;设计党赶紧马住&#xff01;能解决你日常设计中80%的素材。 1、菜鸟图库 菜鸟图库-免费设计素材下载 这是一个为新手设计师提供免费素材的设计网站&#xff0c;站内有超多平面模板、海报、UI设计、电商设计等相关素材&#x…

数据与C(浮点数)

目录 一.基本概念 二.声明和初始化 三.浮点数上溢和下溢 浮点数在数据类型上一共就两个&#xff0c;一个是float&#xff0c;另一个是double。但两个唯一的区别就在于double精度方面是float的两倍 一.基本概念 首先介绍浮点数的三种书写形式 第一种 数字 &#xff1a;103…

K近邻算法和KD树详细介绍及其原理详解

相关文章 K近邻算法和KD树详细介绍及其原理详解 文章目录相关文章前言一、K近邻算法二、KD树总结前言 K近邻算法一般是我们学习机器学习的入门算法&#xff0c;本篇文章详细介绍了K近邻算法&#xff0c;并对其原理进行了说明。同时&#xff0c;为了优化K近邻算法查找最近K个邻…

Spring工厂模式

解决方案1:工厂模式 可以使用抽象工厂模式&#xff0c;让StudentDao、StudentService的实现在在工厂中生成&#xff0c;而工厂可以根据配置 文件的指定类型来创建不同的对象&#xff0c;而且工厂本身一般是不变的。从而降低了对可以变的业务逻辑类的 依赖&#xff0c;接近的软…

字节青训前端笔记 | 数据可视化基础

本课程主要内容可以分为三个章节&#xff1a; 数据可视化的基本概念可视化设计的基本原则面向前端的可视化工具 本手册可以作为学生学习数据可视化的“学习指南”&#xff0c;按照手册所列内容&#xff0c;结合扩展资料进行系统的学习和实践。本课程没有讨论更为前沿的可视化…

推荐5款干净又实用的软件

我们在使用电脑的时候&#xff0c;总是会用到一些好用的软件&#xff0c;今天分享这5款干净又实用的软件&#xff0c;实在是太好用了&#xff0c;我不允许你不知道。 1.桌面工具——火柴 火柴是一款集快速搜索&#xff0c;程序启动、本地文件查找、网站直达、网络搜索等多种功…

OSACN-Net:使用深度学习和Gabor心电图信号谱图进行睡眠呼吸暂停分类

这篇文章在之前读过一次&#xff0c;其主要的思路就是利用Gabor变换&#xff0c;将心电信号转变为光谱图进行识别研究&#xff0c;总体来讲&#xff0c;不同于其他的利用心电信号分类的算法&#xff0c;该论文将心电信号转换为光谱图&#xff0c;在此基础上&#xff0c;分类问题…

java面试题(二十)中间件redis

1.1 Redis可以用来做什么&#xff1f; 参考答案 Redis最常用来做缓存&#xff0c;是实现分布式缓存的首先中间件&#xff1b;Redis可以作为数据库&#xff0c;实现诸如点赞、关注、排行等对性能要求极高的互联网需求&#xff1b;Redis可以作为计算工具&#xff0c;能用很小的…

基于微信小程序 java高校后勤报修系统 Springboot校园报修管理系统

1绪论 5 1.1项目研究的背景 5 1.2开发意义 5 1.3项目研究内容 5 2开发技术介绍 6 2.1 B/S架构 6 2.2Java技术 6 2.3MySQL 介绍 7 2.4MySQL环境配置 7 2.5SpringBoot技术 8 3系统分析 9 3.1可行性分析 9 3.1.1技术可行性 9 3.1.2经济可行性 9 3.1.3操作可行性 9 3.2网站性能需求…

跟开发打了半个月后,我终于get报bug的正确姿势了

在测试人员提需求的时候&#xff0c;大家经常会看到&#xff0c;测试员和开发一言不合就上BUG。然后开发一下就炸了&#xff0c;屡试不爽&#xff0c;招招致命。 曾经看到有个段子这么写道&#xff1a; 不要对程序员说&#xff0c;你的代码有BUG。他的第一反应是&#xff1a;…

C语言学习笔记-强制类型转换

强制类型转换是通过类型转换运算来实现的。其一般形式为&#xff1a;&#xff08;类型说明符&#xff09;&#xff08;表达式&#xff09;其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。自动转换是在源类型和目标类型兼容以及目标类型广于源类型时发生一个类型…

《Redis实战篇》六、秒杀优化

6、秒杀优化 6.0 压力测试 目的&#xff1a;测试1000个用户抢购优惠券时秒杀功能的并发性能~ ①数据库中创建1000用户 这里推荐使用开源工具&#xff1a;https://www.sqlfather.com/ &#xff0c;导入以下配置即可一键生成模拟数据 {"dbName":"hmdp",…