Spring基础学习

news2024/9/20 7:07:59

一 Spring 框架概述

1.1 Spring 概述

轻量级开源的 JavaEE 框架。解决企业开发应用的复杂性。
Spring的核心部分:

  • IOC:控制反转(Inversion of Controll),将创建对象的过程交由 Spring 管理
  • AOP:面向切面编程(Aspect Orient Programming),不修改源代码的情况进行功能增强

特点:

  1. 解耦、简化开发
  2. Aop 编程支持
  3. 方便程序测试
  4. 方便集成框架
  5. 方便进行事物的操作
  6. 降低 API 开发难度

1.2 入门案例

版本:5.2.6
依赖:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>5.2.8.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.8.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>5.2.8.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>5.2.8.RELEASE</version>
</dependency>
<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>

demo01:创建普通类

public class User {

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

}

resources 目录下创建bean1.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"
>
  <bean id="user" class="com.pyd.User"> </bean>
</beans>

进行测试代码编写:

    @Test
    public void testAdd() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        // 获取id为user的类
        User user = context.getBean("user", User.class);
        System.out.println(user);
        // 调用方法
        user.add();
    }

getBean方法的Bean是什么?

官方的解释是 Spring Bean是事物处理组件类和实体类(POJO)对象的总称,是能够被实例化、能够被spring容器管理的java对象。可以把bean看做是一个组件,这个组件用来具体实现某个业务功能。总结性的讲,Bean就是由IOC容器初始化、装配及管理的对象,除此之外,和程序中的其他对象没有区别。简单来说,就是Java对象,但是能够被Spring容器管理。

二 IOC

2.1 IOC 概述

控制反转:交给Spring管理

控制反转:

  • 面向对象的设计原则,降低耦合度
  • 将对象创建和之间的调用过程,交给 Spring 管理

底层原理

底层原理:

  • xml 解析、工厂模式、反射
  • IOC 的思想基于 IOC 容器完成,IOC 的容器底层就是对象工厂

IOC 的过程

IOC 的过程:

  1. XML 配置文件,配置创建的对象
  2. service 和 dao 类,创建工厂类
    通过 xml 文件获取类的路径
    通过反射创建对象

Spring提供的IOC接口

  1. Spring 提供 IOC 容器的两种方式:
  • BeanFactory:
    • IOC 容器的基本实现,是 Spring 内部的使用接口,不允许开发人员进行实现
    • 加载配置文件时,不创建对象,使用时创建对象
  • ApplicationContext:
    • BeanFactory 的子接口,面向开发人员使用
    • 加载配置文件时,创建对象

二者区别:是否在加载配置文件时,创建对象。

ApplicationContext的实现类

ApplicationContext 的实现类
绝对路径和类路径的区别

2.2 IOC 操作 Bean 管理

Bean 管理:

  • Spring 创建
  • Spring 注入属性

Bean 管理的两种方式:

  • 基于 xml 配置文件
  • 基于注解的实现

创建对象
使用 bean 标签,在标签中添加属性,创建对象,常用属性:

  • id:唯一标识
  • class 属性:类全路径
  • name:可以加特殊符号

创建对象时,默认执行无参方法

注入属性
DI:依赖注入(Dependency Injection),是IOC的另一种表述方式,即组件以一些预先定义好的方式(如:getter方法)来接收来自容器的资源注入,即注入属性

2.2.1 方法一:使用 set 方法实现对Bean的属性注入

  1. 创建属性,创建属性对应的 set 方法。
public class Book {

    String author;
    String title;

    public void setAuthor(String author) {
        this.author = author;
    }

    public void setTitle(String title) {
        this.title = title;
    }

}

  1. 在 spring 配置文件中,配置属性的注入
<bean id="book" class="com.pyd.Book">
  <!--   完成属性的注入     -->
  <property name="author" value="张浩瀚"></property>
  <property name="title" value="武汉理工优秀学生"></property>
</bean>

使用 p 名称空间注入(需要有 set 方法):

<?xml version="1.0" encoding="UTF-8"?>
<beans
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:p="http://www.springframework.org/schema/p"
  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"
>
  <!--   p名称空间注入,需要有set方法     -->
  <bean
    id="book"
    class="com.pyd.Book"
    p:author="张浩瀚"
    p:title="武汉理工优秀学生"
  >
  </bean
></beans>

2.2.2 方法二:有参数的构造注入

public class Order {
    String name;
    String id;

    public Order(String name, String id) {
        this.name = name;
        this.id = id;
    }
}

在 xml 中配置:

<bean id="order" class="com.pyd.Order">
  <constructor-arg index="0" value="电脑" />
  <constructor-arg name="id" value="1" />
</bean>

2.3 IOC 设置属性其他类型

2.3.1 字面量

  1. null 值
<property name="address">
  <null/>
</property>
  1. 属性值包含特殊符号

转义:

<property name="address" value="&lt;&gt;">
</property>

CDATA:

<property>
  <value> <![CDATA[<<>>]]> </value>
</property>

2.3.2 注入属性:外部 bean

  1. 创建 service 和 dao
public interface UserDao {
    public void update();
}
public class UserDaoImp implements UserDao {

    @Override
    public void update() {
        System.out.println("userDaoImp update() called");
    }
}
  1. serivce 调用 dao 的方法
public class User {
    private UserDao userDao;

    public void setUserDao(UserDaoImp userDao) {
        this.userDao = userDao;
    }

    public void test() {
        this.userDao.update();
    }

}
  1. 在 xml 中实现注入外部 bean
<bean id="userSvc" class="com.pyd.service.User">
  <property name="userDao" ref="userDaoImp" />
</bean>

<bean id="userDaoImp" class="com.pyd.dao.UserDaoImp" />

2.3.3 注入属性:内部 bean 和级联赋值

  1. 一对多的关系:部门和员工
    一个部门有多个员工,一个员工属于一个部门。

  2. 创建 emp 和 dept 类

public class Dept {

    String name;

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

    @Override
    public String toString() {
        return "dept{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Emp {
    String name;
    String gender;
    Dept dept;

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

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }
}
  1. 编写 xml

方式一:

<bean id="emp" class="com.pyd.bean.Emp">
  <property name="name" value="张浩瀚" />
  <property name="gender" value="" />
  <property name="dept">
    <bean id="dept" class="com.pyd.bean.Dept">
      <property name="name" value="帅哥部" />
    </bean>
  </property>
</bean>

方式二:级联赋值

<bean id="emp" class="com.pyd.bean.Emp">
  <property name="name" value="张浩瀚" />
  <property name="gender" value="" />
  <property name="dept" ref="dept"> </property>
</bean>

<bean id="dept" class="com.pyd.bean.Dept">
  <property name="name" value="帅哥部" />
</bean>

需要 get 方法的写法:

<bean id="emp" class="com.pyd.bean.Emp">
  <property name="name" value="张浩瀚" />
  <property name="gender" value="" />
  <property name="dept" ref="dept" />
  <!--必须有getDept()方法,否则得不到该对象-->
  <property name="dept.name" value="大帅哥部" /> 
</bean>

<bean id="dept" class="com.pyd.bean.Dept"/>

2.4 xml 注入集合属性

集合类型属性的创建:

public class Stu {

    private String[] courses;
    private List<String> list;
    private Map<String, String> map;

    private Set<String> set;

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setCourses(String[] courses) {
        this.courses = courses;
    }
}

2.4.1 数组类型属性

<property name="courses">
  <array>
    <value>1</value>
    <value>2</value>
    <value>3</value>
  </array>
</property>

2.4.2 List 集合类型属性

<property name="list">
  <list>
    <value>1</value>
    <value>2</value>
    <value>3</value>
  </list>
</property>

2.4.3 Map 集合类型属性

<property name="map">
  <map>
    <entry key="zhh" value="天下第一帅" />
  </map>
</property>

2.4.4 在集合中设置对象类型的值

<bean id="course1" class="com.pyd.bean.Course">
  <property name="name" value="张浩瀚帅哥入门课上" />
  <property name="id" value="1" />
</bean>

<bean id="course2" class="com.pyd.bean.Course">
  <property name="name" value="张浩瀚帅哥入门课下" />
  <property name="id" value="2" />
</bean>
<property name="courseList">
  <list>
    <ref bean="course1" />
    <ref bean="course2" />
  </list>
</property>

2.4.5 将集合注入部分提取出来

  1. 在 spring 配置文件中引入名称空间 util
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       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
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
</beans>
  1. 提取 list 集合属性注入
<util:list id="bookList">
  <value>book1</value>
  <value>book2</value>
</util:list>

<bean name="book" class="com.pyd.bean.Book">
  <property name="books" ref="bookList" />
</bean>

2.5 IOC 操作 Bean 管理(FactoryBean)

  • 普通 bean:定义的类型和返回类型相同
  • 工厂 bean:定义的类型和返回类型可以不相同
  1. 创建类,作为工厂 bean,实现接口 FactoryBean
public class MyBean implements FactoryBean {

}
  1. 实现接口的方法,在方法中定义返回的 Bean 类型
    @Override
    public Object getObject() throws Exception {
        Course course = new Course();
        course.setName("张浩瀚");
        return course;
    }
  1. 配置文件 xml
<bean id="myBean" class="com.pyd.factoryBean.MyBean"></bean>

2.6 IOC 操作 Bean 管理(bean 的作用域)

2.6.1 单实例和多实例

创建的对象,默认是单实例。(比较地址是否相同)

设置多实例对象:

scope 属性:

  • singleton:默认值,加载配置文件时就会创建,在每个 Spring ioc 容器中只有一个实例。
  • prototype:多实例对象,在调用 getBean 方法时创建多实例对象
  • request:将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期
  • session:将单个 bean 定义的作用域限定为 HTTP 会话的生命周期。
  • application:将单个 bean 定义的作用域限定为 ServletContext 的生命周期。
  • websocket:将单个 bean 定义的作用域限定为 WebSocket 的生命周期。
<bean id="myBean" class="com.pyd.factoryBean.MyBean" scope="prototype"></bean>

2.7 IOC 操作 Bean 管理(bean 生命周期)

从对象创建到销毁的过程

2.7.1 bean 声明周期

  1. 通过构造器创建 bean 的实例(无参数构造)
  2. 为 bean 的属性设置值和其他 bean 引用(调用 set 方法)
  3. 调用 bean 的初始化方法(需要配置初始化方法)
  4. 可以使用 bean,获取到了对象
  5. 容器关闭时,调用 bean 的销毁方法(需要配置销毁的方法)
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        context.close();
1. 无参构造
2. 设置name
3. 执行初始化方法
4. 获取到了bean对象
5. 销毁对象方法执行

后置处理器:

  1. 通过构造器创建 bean 的实例(无参数构造)
  2. 为 bean 的属性设置值和其他 bean 引用(调用 set 方法)
  3. 把 bean 实例传递给 bean 后置处理器 (postProcessBeforeInitialization)
  4. 调用 bean 的初始化方法(需要配置初始化方法)
  5. 把 bean 实例传递给 bean 后置处理器 (postProcessAfterInitialization)
  6. 可以使用 bean,获取到了对象
  7. 容器关闭时,调用 bean 的销毁方法(需要配置销毁的方法)
<bean name="myPostBean" class="com.pyd.factoryBean.MyBeanPost"></bean>
无参构造
设置name
bean后置处理器 postProcessBeforeInitialization
执行初始化方法
bean后置处理器 postProcessAfterInitialization
获取到了bean对象
销毁对象方法执行

2.7 IOC 操作 Bean 管理(xml 自动装配)

实际工作中,用得很少

自动装配:根据装配规则(属性名称或类型),Spring 自动将匹配的属性值注入。

autowire 属性:

  • byName: 根据属性名称注入,bean 的 id 和属性值相同
  • byType: 根据属性类型注入,相同类型的不能定义多个

2.8 IOC 操作 Bean 管理(外部属性文件)

2.8.1 直接配置数据库信息

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/fruitdb" />
  <property name="username" value="root" />
  <property name="password" value="zhanghaohanzuishuai" />
</bean>

2.8.2 引入外部文件配置数据库连接池

数据库文件jdbc.property

username=root
password=zhanghaohanzuishuai
url=jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
driverClass=com.mysql.cj.jdbc.Driver

引入 context 名称空间:

<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/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"
>
</beans>

引入外部属性文件:

<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <property name="driverClassName" value="${driverClass}" />
  <property name="url" value="${url}" />
  <property name="username" value="${username}" />
  <property name="password" value="${password}" />
</bean>

2.9 IOC 操作 Bean 管理(基于注解方式)

依赖:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.2.8.RELEASE</version>
</dependency>

2.9.1 注解

  1. 特殊的代码标记格式
  2. 可以作用于类、方法、属性
  3. 简化 XML 配置

2.9.2 Spring 针对 Bean 管理中创建对象提供的注解

  1. Component
  2. Service
  3. Controller
  4. Repositary

功能相同

2.9.3 实现对象的创建

  1. 开启组件扫描

扫描多个包:用逗号分隔
扫描包上层目录

<context:component-scan base-package="com.pyd.aop"></context:component-scan>
  1. 创建类,并添加注解

注解的 value 默认是类的名称,首字母小写。

@Component("userService")
public class UserService {
    public void add() {
        System.out.println("user add");
    }
}
  1. 组件扫描的细节
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-default- filters="false">
  <context:include-filter
    type="annotation"
    expression="org.springframework.stereotype.Controller"
  />
</context:component-scan>
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
  <context:exclude-filter
    type="annotation"
    expression="org.springframework.stereotype.Controller"
  />
</context:component-scan>

Spring 针对 Bean 中属性的注入提供的注解

  1. Autowried:根据属性类型自动装配
  2. Qualifier:根据属性名称自动注入
  3. Value: 注入普通类型

2.9.3 根据类型实现属性注入

  1. 创建 service 和 dao 对象,并添加创建对象的注解

接口对象:

public interface UserDao {
    public void print();
}

接口实现类:

@Repository
public class UserDaoImp implements UserDao {

    @Override
    public void print() {
        System.out.println("userDaoImp print");
    }
}
  1. 在 service 定义 dao 类型属性,并加上注解(不需要添加 set 方法)
@Component("userService")
public class UserService {

    @Autowired
    private UserDao userDao;

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

2.9.4 根据属性名称进行注入

场景:一个接口有多个实现类

需要和 Autowired 一起使用

@Repository("userDaoImp1")
public class UserDaoImp1 implements UserDao {

    @Override
    public void print() {
        System.out.println("userDaoImp1 print");
    }
}

根据名称进行注入:

    @Autowired
    @Qualifier("userDaoImp1")
    private UserDao userDao;

2.9.5 根据类型注入或根据名称注入

Resource:根据类型注入或根据名称注入(javax 包中)

根据类型注入:

@Resource

根据名称注入:

@Resource("userService")

2.9.6 注入普通属性

    @Value(value = "zhh")
    public String name;

2.9.7 完全注解开发

不使用配置文件,完全使用注解的方式。

  1. 创建配置类,代替配置文件

@Configuration:作为配置类
@ComponentScan: 需要扫描的包路径

@Configuration
@ComponentScan(basePackages = {"com.pyd.aop"})
public class SpringConfig {
}
  1. 加载配置类

AnnotationConfigApplicationContext

      ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

三、AOP

3.1 概念

  1. 面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  2. 通过不修改源码的方式,增加新的功能

基于 Java 的主要 AOP 实现有:AspectJ Spring AOP JBoss AOP

3.2 基本原理

3.2.1 动态代理

有两种情况的动态代理:

(1)有接口:使用 JDK 动态代理

创建 UserDao 接口实现类代理对象,增强类的方法

(2)无接口:使用 CGLIB 动态代理

创建子类。
CGLIB:创建当前类子类的代理对象

3.2.2 JDK 动态代理

使用 Proxy 类的方法创建类的代理对象。

调用 newInstance 方法:

  • 参数 1:类加载器
  • 参数 2:增强方法锁在类实现的接口(支持多个)
  • 参数 3:实现接口 InvcationHandler,创建代理对象,写增强方法。
  1. 创建接口,定义方法
public interface UserDao {
    public int add(int a, int b);

    public String update(int id);
}
  1. 创建实现类,实现方法
public class UserDaoImp implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println(a + b);
        return 0;
    }

    @Override
    public String update(int id) {
        return null;
    }
}
  1. 使用 proxy 类实现增强
public class JDKProxy {
    public static void main(String[] args) {
        UserDaoImp userDaoImp = new UserDaoImp();
        Class[] interfaces = {UserDao.class};
        new UserDaoImp();
        UserDao proxy = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoImp));
        proxy.add(10, 20);
    }
}

class UserDaoProxy implements InvocationHandler {
    public Object obj;

    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行之前");

        System.out.println("method.getName() = " + method.getName());
        // 增强方法执行
        Object res = method.invoke(obj, args);

        System.out.println("方法执行之后");

        return res;
    }
}

3.3 术语

  1. 连接点:类中可以被增强的方法

  2. 切入点:实际真正被增强的方法

  3. 通知(增强):实际增强的逻辑部分。
    通知有多种类型:

    • 前置
    • 后置
    • 环绕
    • 异常
    • 最终
  4. 切面:将通知应用到切入点的过程。

3.4 准备工作

导入相关 Jar 包:

<!-- AOP相关Jar -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>5.2.0.RELEASE</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.6.8</version>
</dependency>

<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
  <groupId>aopalliance</groupId>
  <artifactId>aopalliance</artifactId>
  <version>1.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib -->
<dependency>
  <groupId>net.sourceforge.cglib</groupId>
  <artifactId>com.springsource.net.sf.cglib</artifactId>
  <version>2.2.0</version>
</dependency>

3.5 基于 AspectJ 实现 AOP 操作

不是 Spring 的组成部分,独立的 AOP 框架,一般将 AspectJ 和 Spring 框架一起使用。

  • 基于 xml 配置文件实现
  • 基于注解方式实现

3.5.1 切入点表达式

作用:知道对哪个类和哪个方法来增强。

语法结构:

execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))

例:

  • 举例 1:对 com.micah.dao.BookDao 类里面的 add 进行增强

    execution(\* com.atguigu.dao.BookDao.add(..))
    
  • 举例 2:对 com.micah.dao.BookDao 类里面的所有的方法进行增强

    execution(_ com.atguigu.dao.BookDao._ (..))
    
  • 举例 3:对 com.atguigu.dao 包里面所有类,类里面的所有的方法进行增强

    execution(_ com.micah.dao._.\* (..))
    

3.5.2 AspectJ 注解

  1. 创建类
public class User {
    public void add() {
        System.out.println("user add");
    }
}
  1. 创建增强类,编写增强逻辑

创建不同方法,代表不同的通知类型

public class UserProxy {
    public void before() {
        System.out.println("前置通知");
    }
}
  1. 进行通知的配置
    (1)在 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"
      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/context http://www.springframework.org/schema/context/spring-context.xsd
                         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
    >
      <!-- 开启注解扫描 -->
      <context:component-scan
        base-package="com.pyd.aop"
      ></context:component-scan>
    
      <!-- 开启Aspect生成代理对象-->
      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    

    (2)使用注解创建 User 和 UserProxy 对象

    @Component
    

    (3)在增强类上添加注解 Aspect

    @Aspect
    

    (4)开启 ApsectJ 生成代理对象

  2. 配置不同类型的通知

添加通知的注解,填写切入点表达式:

@Component
@Aspect
public class UserProxy {
    @Before(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void before() {
        System.out.println("before 前置通知");
    }


    @AfterReturning(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning 后置通知(返回通知)");
    }

    @After(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void after() {
        System.out.println("after 最终通知");
    }

    @AfterThrowing(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing 异常通知");
    }

    @Around(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知前");
        proceedingJoinPoint.proceed();
        System.out.println("环绕通知后");
    }
}
  1. 相同切入点抽取

Pointcut

    @Pointcut(value = "execution(* com.pyd.aop.annotation.User.add(..))")
    public void pointDemo() {
    }

    @Before(value = "pointDemo()")
    public void before() {
        System.out.println("before 前置通知");
    }
  1. 设置增强类的优先级

在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高:

@Component
@Aspect
@Order(1)
public class PersonProxy

在 spring 配置文件中配置切入点:

<aop:config>
  <!--切入点-->
  <aop:pointcut
    id="p"
    expression="execution(*
com.atguigu.spring5.aopxml.Book.buy(..))"
  />
  <!--配置切面-->
  <aop:aspect ref="bookProxy">
    <!--增强作用在具体的方法上-->
    <aop:before method="before" pointcut-ref="p" />
  </aop:aspect>
</aop:config>

3.5.3 AspectJ 完全注解开发

  1. 创建配置类
@Configuration
@ComponentScan(basePackages = {"com.pyd.annotation"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
class SpringAspectConfig {
}

四、JdbcTemplate

4.1 概述

Spring 框架中提供的一个对象,是对原始 JDBC API 对象的简单封装。

其他操作模板类:

  • 操作关系型数据的:JdbcTemplate 和 HibernateTemplate。
  • 操作 nosql 数据库的:RedisTemplate。
  • 操作消息队列的:JmsTemplate。

4.2 需要的依赖

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.22</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.4</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.3.2</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.3.2</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-orm</artifactId>
  <version>5.3.2</version>
</dependency>

4.3 准备工具

4.3.1 Spring 配置文件

<?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/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"
>
</beans>

创建数据库连接池:

<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <property name="driverClassName" value="${driverClass}" />
  <property name="url" value="${url}" />
  <property name="username" value="${username}" />
  <property name="password" value="${password}" />
</bean>

创建 JdbcTemplate 对象:

<bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <!-- 注入datasource-->
  <property name="dataSource" ref="dataSource"></property>
</bean>

开启组件扫描:

<context:component-scan base-package="com.atguigu"></context:component-scan>

4.3.2 创建 service 和 dao 类,在 dao 注入 jdbcTmeplate 对象

public interface BookDao {}

注入配置文件中的 JdbcTemplate 对象

@Repository
public class BookDaoImp implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
}

4.4 操作数据库

数据库:

SELECT * FROM  t_fruit;

CREATE DATABASE dbtest;

USE dbtest;

CREATE TABLE book(
    id int,
    name VARCHAR(128),
    status BOOLEAN
);

4.4.1 添加

  1. 对应数据库创建实体类
public class Book {

    private int id;
    private String name;
    private boolean status;

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

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

    public void setStatus(boolean status) {
        this.status = status;
    }
}
  1. 在 dao 进行数据库添加操作
@Repository
public class BookDaoImp implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void add(Book book) {
        String sql = "insert into book values(?,?,?);";
        Object[] args = {book.getId(), book.getName(), book.isStatus()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);

    }
}
@Service
public class BookService {
    @Autowired
    private BookDaoImp bookDao;

    public void add(Book book) {
        bookDao.add(book);
    }

}

4.4.2 查询—返回某个值

查询表有多少条记录

    @Override
    public int count() {
        String sql = "select count(*) from book";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        return count;
    }

4.4.3 查询—返回对象

public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args)

参数 2:RowMapper,接口,返回不同类型的数据

查询图书的详细信息

    @Override
    public Book getBook(int id) {
        String sql = "select * from book where id = ?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
    }

4.4.4 查询—返回集合

查询图书列表:

    @Override
    public List<Book> getBooksList() {
        String sql = "select * from book";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
    }

4.4.5 批量添加操作

betchUpdate

    @Override
    public void batchAdd(List<Object[]> books) {
        String sql = "insert into book values (?,?,?);";
        jdbcTemplate.batchUpdate(sql, books);
    }
        List<Object[]> books = new ArrayList<>();
        Object[] book1 = {3, "zhanghh", false};
        Object[] book2 = {4, "zhangh", false};
        Object[] book3 = {5, "zhanhh", false};
        books.add(book1);
        books.add(book2);
        books.add(book3);
        bookService.batchAdd(books);

4.5 事务

4.5.1 基本概念

数据库操作的基本单元,逻辑上一组的操作,要么都成功,要么都失败。

四个特性(ACID):

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

4.5.2 环境搭建

  1. 数据库建表
CREATE TABLE user
(
    id    INT,
    name  VARCHAR(128),
    money INT
);
INSERT INTO user VALUES (1,'张浩瀚',200),(2,'章帅',300);
  1. 创建 service,搭建 dao,完成对象创建和注入关系

dao:

public interface UserDao {
    public void add(int money);

    public void reduce(int money);

}
@Repository
public class UserDaoImp implements UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;


    @Override
    public void add(int money) {
        System.out.println("id=1 : -" + money);
        String sql = "update user set money-? where id=?";
        jdbcTemplate.update(sql, money, 1);
    }

    @Override
    public void reduce(int money) {
        System.out.println("id=2 : +" + money);
        String sql = "update user set money+? where id=?";
        jdbcTemplate.update(sql, money, 2);

    }
}

service:

@Service
public class UserService {

    @Autowired
    private UserDaoImp userDao;

    public void transfer() {
        userDao.add(100);
        userDao.reduce(100);
    }
}
  1. 模拟异常
int i = 10/0;

4.5.3 事务操作过程

事务操作过程:

  1. 开启事务操作

  2. 捕获异常

  3. 出现异常,回滚

4.5.4 Spring 事务

  1. 将事务加到 Service 层上

  2. 在 spring 进行事务管理操作:编程式事务管理和声明式事务管理

  3. 在 Spring 进行声明式事务管理,底层使用 AOP 原理

  4. 相关 API
    接口:事务管理器,该接口针对不同框架提供不同实现类

4.5.5 注解方式实现声明式事务管理

  1. 在 Spring 配置文件中配置事务管理器
<bean
  id="dataSourceTransactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
  <!--注入数据源-->
  <property name="dataSource" ref="dataSource" />
</bean>
  1. 开启事务注解

引入名称空间:tx

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx.xsd"
>

开启事务注解:

 <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
  1. 在 service 类上添加事务注解

Transactional:

  • 加在类上:当前类的所有方法添加事务
  • 加在方法:当前方法添加事务

4.5.6 声明式事务管理参数配置

当一个事务的方法被另一个事务方法调用时,该事务方法如何进行

事务传播行为:

传播行为描述
PROPAGATION_REQUIREDSpring 默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行
PROPAGATION_REQUES_NEW该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可
PROPAGATION_SUPPORT如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
PROPAGATION_NEVER如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
PROPAGATION_NOT_SUPPORT该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
PROPAGATION_MANDATORY与 NEVER 相反,如果外层没有事务,则抛出异常
PROPAGATION_NESTED该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。

传播规则回答了这样一个问题:一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行。

事务隔离级别 isolation

多事务之间不会产生影响。
不考虑隔离性会出现三个读问题:

  • 脏读:一个未提交的事务读取到了另一个未提交事务的数据
  • 不可重复读: 一个未提交事务读到了另一个已提交事务的修改数据
  • 虚读(幻读):一个未提交的事务读到了新提交的数据

通过设置隔离级别来解决读问题。

脏读不可重复读虚读
READ UNCOMMIT 读未提交
READ COMMITTED 读已提交
REPEATABLE READ 可重复读 (MySQL 默认)
SERIALIZABLE 串行化
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)

超时时间 timeout

设置事务需要在一定时间内进行提交,超时则回滚。
默认值为-1,单位为秒。

@Transactional(timeout = 5)

readOnly 是否只读
读:查询操作
写:添加修改删除操作
默认值为 false,可读写
设置为 true,只能查询

rollbackFor 回滚
设置出现哪些异常需要回滚

noRollbackFor 是否只读
设置出现哪些异常不需要回滚

4.5.6 XML 实现声明式事务管理

  1. 配置事务管理器
<!--    创建事务管理器-->
<bean
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
  id="dataSourceTransactionManager"
>
  <!--        注入数据源-->
  <property name="dataSource" ref="dataSource"></property>
</bean>
  1. 配置通知(事务)
<!--    创建通知-->
<tx:advice id="txadvice">
  <tx:attributes>
    <!-- 指定在哪种规则的方法上添加事务-->
    <tx:method name="transfer*" propagation="REQUIRES_NEW" />
  </tx:attributes>
</tx:advice>
  1. 配置切入点和切面
<aop:config>
  <!--切入点配置-->
  <aop:pointcut
    id="px"
    expression="execution(* com.pyd.jdbctemplate.serivce.UserService.*(..))"
  />
  <!--切面配置-->
  <aop:advisor advice-ref="txadvice" pointcut-ref="px"></aop:advisor>
</aop:config>

4.5.7 完全注解实现声明式事务管理

  1. 创建配置类,使用配置类替代 xml
@Configuration
@ComponentScan(basePackages = {"com.pyd.jdbctemplate"})
@EnableTransactionManagement  // 开启事务
public class TxConfig {

    // 创建数据库的连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUsername("root");
        druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/dbtest");
        druidDataSource.setPassword("");
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        return druidDataSource;
    }

    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        // 注入datasource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }

}

Spring 5 框架新功能

5.1 概述

  1. 常规升级:

    • 对 JDK 9 运行时兼容性
    • 在 Spring Framework 代码中使用 JDK 8 特性
    • 响应式编程支持
    • 函数式 Web 框架
    • Jigsaw 的 Java 模块化
    • 对 Kotlin 支持
    • 舍弃的特性
  2. 基于 Java8 兼容 Java9

    • 核心 Spring 接口中的 Java 8 static 方法
    • 基于 Java 8 反射增强的内部代码改进
    • 在框架代码中使用函数式编程——lambdas 表达式和 stream 流
  3. 支持响应式编程,需要使用框架

    • Reactive Streams:尝试定义与语言无关的响应性 API。
    • Reactor:Spring Pivotal 团队提供的响应式编程的 Java 实现。
    • Spring WebFlux:启用基于响应式编程的 Web 应用程序的开发。 提供类似于 Spring MVC 的编程模型。

5.2 日志框架

自带日志封装

(1) Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2
(2) Spring5 框架整合 Log4j2

  1. 导入 jar 包
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.11.2</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.11.2</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.30</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
  <version>2.11.2</version>
</dependency>
  1. 创建 log4j2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--日志级别以及优先级排序: OFF> FATAL> ERROR> WARN> INFO> DEBUG> TRACE> ALL-->
<!--Configuration后面的 status用于设置 log4j2自身内部的信息输出,可以不设置, 当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="DEBUG">
  <!--先定义所有的 appender-->
  <appenders>
    <!--输出日志信息到控制台-->
    <console name="Console" target="SYSTEM_OUT">
      <!--控制日志输出的格式-->
      <PatternLayout
        pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
      />
    </console>
  </appenders>
  <!--然后定义 logger,只有定义 logger并引入的 appender,appender才会生效-->
  <!--root:用于指定项目的根日志,如果没有单独指定 Logger,则会使用 root作为默认的日志输出-->
  <loggers>
    <root level="info">
      <appender-ref ref="Console" />
    </root>
  </loggers>
</configuration>
  1. 手动输出
public class Log {

    private static final Logger log = LoggerFactory.getLogger(Log.class);

    public static void main(String[] args) {
        log.info("hello log4j");
        log.warn("hello log4j");
    }

}

5.3 核心容器

5.3.1 支持 Nullable 注解

Nullable:

  • 用于方法:方法的返回值可以为空
  • 用于方法的参数:参数可以为空
  • 用于属性:属性值可以为空

5.3.2 支持函数式风格 GenericApplication Context

        GenericApplicationContext context = new GenericApplicationContext();
        // 调用context对象进行注册,先清空
        context.refresh();
        context.registerBean(User.class, () -> new User());
        context.registerBean("user1", User.class, () -> new User());
        // 获取对象 全路径
        User user = (User) context.getBean("com.pyd.User");
        // 获取对象:指定名称
        User user1 = (User) context.getBean("user1");

5.4 支持 JUnit5

  1. 整合 JUnit4
@RunWith(SpringJUnit4ClassRunner.class)
//单元测试框架
@ContextConfiguration("classpath:bean1.xml")
//加载配置文件
public class JTest4 {
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    }
}
  1. 整合 JUnit5

需要引入 JUnit5 的 Jar 包

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
public class JTest5 {
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    }
}
  1. 复合注解
@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class JTest5{
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    }
}

5.4 SpringWebFlux

5.4.1 概述

是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似的,Webflux 使用当前一种比较流程响应式编程出现的框架。
使用传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,WebFlux 是一种异步非阻塞的框架,异步非阻塞的框架在 Servlet 3.1 以后才支持,核心是基于 Reactor 的相关 API 实现的。

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

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

相关文章

云服务器-Docker容器-系统搭建部署

一、引言 最近公司在海外上云服务器&#xff0c;作者自己也搞了云服务器去搭建部署系统&#xff0c;方便了解整体架构和系统的生命周期&#xff0c;排查解决问题可以从原理侧进行分析实验。虽然用的云不是同一个&#xff0c;但是原理都是相通的。 二、选型 作者选用的是腾讯云…

进入嵌入式之后究竟会干些什么?

嵌入式被称为互联网、计算机行业的万金油&#xff0c;未来的就业方向多种多样&#xff0c;工作内容也不一而足&#xff0c;但可以分为如下几个角度&#xff1a; 架构师 在大型企业中&#xff0c;一个人很难承担过多的任务&#xff0c;因为这会带来很大的风险。大企业更需要在…

Find My资讯|苹果Vision Pro开发者需将设备配对 AirTag

最近苹果Vision Pro获开发者申请&#xff0c;苹果要求获批的申请者使用 Measure and Fit 应用确认合适的佩戴尺寸&#xff0c;并会根据申请者提交的信息&#xff0c;定制不同的 Vision Pro 开发者套件&#xff0c;以便于契合申请者的面部特征&#xff0c;提供更好的佩戴体验。 …

N4010A|安捷伦Agilent N4010A蓝牙测试仪

描述 N4010A是一款多功能多格式无线连接测试解决方案&#xff0c;您可以针对R&D、集成和验证或制造中的特定蓝牙、无线局域网(WLAN) 802.11a、b和g以及ZigBee应用进行配置。 特征 测试多种技术的灵活性通过快速、准确的测量提高生产量从开发到生产的可重复测量结果适应新…

MySQL索引下推讲解

文章目录 一、什么是索引下推二、MySQL架构图三、DEMO演示过程 一、什么是索引下推 索引条件下推&#xff08;Index Condition Pushdown&#xff0c;ICP&#xff09;是MySQL 5.6版本后引入的一项新特性。它通过减少回表的次数来提高数据库的查询效率。 在不使用ICP的情况下&am…

Optional的基础运用

Optional的基础运用 简介代码示例 简介 代码示例 package org.example;import org.junit.Test;import java.util.Optional;public class OptionalTest {Testpublic void advance() {String str "hello";str null;// of(T t):封装数据t生成Optional对象&#xff0c…

贝叶斯推理问题、MCMC和变分推理

一、介绍 贝叶斯推理是统计学中的一个主要问题&#xff0c;在许多机器学习方法中也会遇到。例如&#xff0c;用于分类的高斯混合模型或用于主题建模的潜在狄利克雷分配都是在拟合数据时需要解决此类问题的图形模型。 同时&#xff0c;可以注意到&#xff0c;贝叶斯推理问题有时…

2018年9月全国计算机等级考试真题(C语言二级)

2018年9月全国计算机等级考试真题&#xff08;C语言二级&#xff09; 第1题 若有以下程序 main() { int a6, b0, c0; for(;a;) { ba; a-c; } printf("%d,%d,%d\n",a,b,c); } 则程序的输出结果是 A. 1,14,3 B. 0,18,3 C. …

【C语言】指针的进阶

目录 一、字符指针 二、指针数组 三、数组指针 1.数组指针的定义 2.&数组名和数组名区别 3.数组指针的使用 四、数组参数与指针参数 1.一维数组传参 2.二维数组传参 3.一级指针传参 4.二级指针传参 五、函数指针 六、函数指针数组 七、指向函数指针数组的指针…

【Matter】设备入网流程

官方信息 认证、配网过程 https://developers.home.google.com/matter/primer/commissioning Commissioning 是一个动作&#xff0c;待入网设备Commissionee 加入到Commissioner 过程 1.第一步&#xff0c;设备发现&#xff0c;比如通过ble 蓝牙方式 2.第二步&#xff0c;建…

科技资讯|苹果Vision Pro新专利曝光:可调节液态透镜

苹果公司近日申请了名为“带液态镜头的电子设备”&#xff0c;概述了未来可能的头显设计。头显设备中的透镜采用可调节的液态透镜&#xff0c;每个透镜可以具有填充有液体的透镜腔&#xff0c;透镜室可以具有形成光学透镜表面的刚性和 / 或柔性壁。 包括苹果自家的 Vision Pr…

嵌入式入门教学——C51(中)

嵌入式入门教学汇总&#xff1a; 嵌入式入门教学——C51&#xff08;上&#xff09;嵌入式入门教学——C51&#xff08;中&#xff09;嵌入式入门教学——C51&#xff08;下&#xff09; 目录 七、矩阵键盘 八、定时器和中断 九、串口通信 十、LED点阵屏 十一、DS1302实…

谷歌云 | 最大限度地提高可靠性降低成本:调整 Kubernetes 工作负载的大小

【Cloud Ace 是谷歌云全球战略合作伙伴&#xff0c;拥有 300 多名工程师&#xff0c;也是谷歌最高级别合作伙伴&#xff0c;多次获得 Google Cloud 合作伙伴奖。作为谷歌托管服务商&#xff0c;我们提供谷歌云、谷歌地图、谷歌办公套件、谷歌云认证培训服务。】 您知道通过调整…

计蒜客T1266——出勤记录

水题&#xff0c;唯一考验操作水平的只有同级连续字符串最大值这一操作&#xff0c;解决方式如下&#xff1a; int late-1; //连续缺勤的次数 int max0;//最长连续的L //缺勤检验 for(int k0;k<temp.size()-1;k){if(temp[k]L&&late-1){late1;//当前是连续的第一个…

国产之光:讯飞星火最新大模型V2.0

大家好&#xff0c;我是herosunly。985院校硕士毕业&#xff0c;现担任算法研究员一职&#xff0c;热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名&#xff0c;CCF比赛第二名&#xff0c;科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…

Java实现CAS的原理

文章目录 1、 什么是CAS2、CAS的原理3、CAS的应用场景4、Java中的CAS实现5、使用AtomicInteger实现线程安全的计数器6、CAS实现原子操作的三大问题6.1、ABA问题6.2、循环时间长6.3、只能保证一个共享变量的原子性 7、总结 1、 什么是CAS CAS&#xff08;Compare and Swap&…

【GeoDa实用技巧100例】017:制作平行坐标图

文章目录 一、平行坐标图介绍二、平行坐标图制作1. 加载数据2. 平行坐标图三、平行坐标图链接一、平行坐标图介绍 在一般坐标系中,3D是可视化探索不同点相关性的极限。为了探索高维空间(大于3维)的可视化问题,1885年法国数学家奥克南提出了平行坐标的解决方案。 平行坐标图…

跑步用什么耳机听歌才不会掉,精选的几款跑步掉的运动耳机

跑步是一项受欢迎的运动&#xff0c;而好的音乐能够让跑步变得更加有趣。但是&#xff0c;很多人在跑步过程中都会遇到耳机脱落的问题&#xff0c;这不仅会打扰运动的节奏&#xff0c;也会让人感到非常恼火。因此&#xff0c;选择一款适合跑步的耳机非常重要。在本文中&#xf…

220、仿真-基于51单片机ULN2003A步进电机正反转加减速LCD12864显示Proteus仿真设计(程序+Proteus仿真+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图 ​编辑 四、程序源码 资料包括&#xff1a; 需要完整的资料可以点击下面的名片加下我&#xff0c;找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方…

开发环境搭建

Anaconda安装搭建Python环境 官网下载Anaconda anaconda官网安装Anaconda设置系统环境变量 按照实际安装路径新建填写红框环境变量 验证环境是否正常运行 WINR输入cmd conda --version python --version pip --version 显示版本信息即为正常 VSCODE Python ShiftCtrlP顶部…