框架概述

所谓的框架其实就是程序的架子,在这个程序的架子中,搭建起程序的基本的骨架,针对程序的通用问题给出了便捷的解决方案,可以使开发人员 基于框架快速开发具体的应用程序。

常见的框架

  • SSH: Struts2 (Web层) / Spring (Service层) / Hibernate (DAO层)

  • SSM: SpringMVC (Web层) / Spring (Service层) / MyBatis (DAO层)

Spring框架概述

  • Spring是一个Service层的框架,可以整合许多其它框架进行工作。

  • Spring的主要技术是 IoC (DI) & AoP

    • IoC (DI) — 控制反转 (依赖注入)
    • AoP — 面向切面编程

Spring IoC

IoC (DI) — 控制反转 (依赖注入)

  • IoC称之为控制反转,简单来说就是将对象的创建的权利及对象的生命周期的管理过程交由Spring框架来处理

  • 开发过程中不再需要关注对象的创建和生命周期的管理,而是在需要时由Spring框架提供,这个由spring框架管理对象创建和生命周期的机制称之为控制反转

  • 通过IoC + 接口,可以在软件分层实践中,将耦合性提取到Spring容器中进行管理。这样就可以实现软件分层中“高内聚,低耦合”目标中的低耦合

在这里插入图片描述

  • 创建对象的过程中Spring可以依据配置对对象的属性进行设置,这个过称之为依赖注入,即DI。

入门案例演示:

  1. 创建项目:创建基本的java项目

  2. 导入相关的jar包 (6+1)
    在这里插入图片描述

  3. 创建配置文件 — applicationContext.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">
    </beans>
    

    在Idea中: 在src目录下右键 — >新建 — > XML Configuration File —> Spring Config

  4. 开发代码

    创建一个类:

    public class Person01 {public void eat(){System.out.println("eat ... ");}public  void say(){System.out.println("say ... ");}
    }
    

    配置bean:

    <?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--><bean id="person01" class="cn.zss.domain.Person01"></bean>
    </beans>
    

    在程序中通过Spring容器获取对象并使用:

    public class Test01 {/** 普通方法创建对象* */@Testpublic void test01(){Person01 person01 = new Person01();person01.say();person01.eat();}/** Spring框架创建对象* */@Testpublic void test02(){// 1. 初始化Spring容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 通过Spring容器获取对象Person01 p = (Person01) context.getBean("person01");// 3. 适用对象p.eat();p.say();// 4.关闭容器((ClassPathXmlApplicationContext) context).close();}
    }
    

IoC的实现原理

  • 初始化Spring容器,加载指定的配置文件,基于配置文件初始化Spring容器
  • 当解析到配置文件中的<bean>标签时,Spring容器根据该标签中的class属性指定的类的全路径名,通过反射创建该类的对象,并将该对象存入内置的Map中管理。其中键就是该标签的id值,值就是该对象。
  • 当通过getBean方法来从容器中获取对象时,其实就是根据传入的条件在内置的Map中寻找是否有匹配的键值,如果有则将该键值对中保存的对象返回,如果没有匹配到则抛出异常。

在这里插入图片描述

推论:

  • 默认情况下,多次获取同一个id的bean,得到的将是同一个对象。

  • 不可以配置多个id相同的bean

  • 可以配置多个id不同但class相同的bean

    /** SpringIOC推论1:*   默认情况下,从Spring容器中多次获取同一个id的bean得到的同一个对象*   原因在于:从Spring容器中获取bean的过程本质上是从容器内部map中找到指定键的值返回*   无论获取多少次,得到的都是map中key对应的对象,自然是同一个对象* *//** SpringIOC推论2:*   不可以在spring容器中配置多个id相同的bean*   原因在于:向spring容器中注册的bean,本质上要以指定的id和反射创建的对象形成键值对存储到map中*   而map的键是不允许重复的,所以配置多个id相同的bean无法同时存入内部map,因此会抛出异常* */@Testpublic  void test03(){// 初始化容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取 beanPerson01 p1 = (Person01) context.getBean("person01");// 第二次获取person01Person01 p2 = (Person01) context.getBean("person01");System.out.println(p1 == p2);  // true}/** SpringIOC推论3:*   可以在容器中配置多个id不同但是class相同的bean*   原因在于:多个class相同的bean,分别反射创建对象后,使用自己的id存储到spring容器内的map中,因此可以在spring中配置多个class相同但是id不同的bean* */@Testpublic  void test04(){// 初始化容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取 beanPerson01 p1 = (Person01) context.getBean("person01");// 获取第二个beanPerson01 p2 = (Person01) context.getBean("person01x");System.out.println(p1 == p2);  // false}

IoC获取对象的方式

通过context.getBean()方法获取bean时,可以通过如下两种方式获取:

  1. 传入id值

    /** Spring容器获取对象的方法1:通过id获取*   如果找不到:抛出异常[NoSuchBeanDefinitionException]*   如果找到唯一的一个:返回找到的对象*   如果找到多个 (推论2):容器初始化时,抛出异常[BeanDefinitionParsingException]
    * */
    @Test
    public void  test01(){// 1. 初始化Spring容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 通过容器获取beanPerson01 p1 = (Person01) context.getBean("person01");// 3. 使用beanp1.eat();p1.say();// 4. 关闭spring容器((ClassPathXmlApplicationContext) context).close();
    }
    
  2. 传入class类型

    <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"><!-- 配置多个id不同但是class相同的bean--><bean id="person01" class="cn.zss.domain.Person01"></bean><bean id="person02" class="cn.zss.domain.Person01"></bean>
    </beans>
    
    /** Spring容器获取对象的方法2:通过class获取*   如果找不到:抛出异常[NoSuchBeanDefinitionException]*   如果找到唯一的一个:返回找到的对象*   如果找到多个:在获取bean时,抛出异常[NoUniqueBeanDefinitionException]* */
    @Test
    public void  test02(){// 1. 初始化Spring容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 通过容器获取beanPerson01 p1 = context.getBean(Person01.class);// 3. 使用beanp1.say();p1.eat();// 4. 关闭spring容器((ClassPathXmlApplicationContext) context).close();
    }
    
  • 通过class方式获取bean时,如果同一个类配置过多个bean,则在获取时因为无法确定到底要获取哪个bean会抛出异常。

  • SpringIOC在通过class获取bean时,如果找不到该类型的bean还会去检查是否存在该类型的子孙类型的bean,如果有则返回,如果找不到或找到多个则抛出异常。这符合java面向对象思想中的多态的特性。

别名标签

在 Spring中提供了别名标签<alias>可以为配置的<bean>起一个别名,要注意的是这仅仅是对指定的<bean>起的一个额外的名字,并不会额外的创建对象存入map

<?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="person01" class="cn.zss.domain.Person01"></bean><!-- 起别名--><alias name="person01" alias="per01"></alias>
</beans>
public class Test01 {@Testpublic void  test01(){// 1. 初始化Spring容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 通过容器获取beanPerson01 p1 = (Person01) context.getBean("person01");// 通过别名获取beanPerson01 p2 = (Person01) context.getBean("per01");// 3. 使用beanSystem.out.println(p1 == p2);  // true// 4. 关闭spring容器((ClassPathXmlApplicationContext) context).close();}
}

Spring创建对象的方式

通过类的无参构造方法创建对象

  • 当用最普通方式配置一个<bean>时,默认就是采用类的无参构造创建对象。

  • 在Spring容器初始化时,通过<bean>上配置的class属性反射得到字节码对象,通过newInstance()创建对象

  • 这种方式下spring创建对象,要求类必须有无参的构造,否则无法通过反射创建对象,会抛出异常。

public class Person01 {private int age;Person01(int age){this.age = age;}public void eat(){System.out.println("eat ... ");}
}
public class Test01 {@Test/** 普通方法创建对象* */public void  test01(){Person01 person01 = new Person01(5);person01.eat();}/** 通过反射创建对象*   newInstance()方法本质上是在调用当前类的无参构造器创建对象*   如果该类没有无参构造器,则此方法无法正常执行,会抛出异常* */@Testpublic void  test02() throws ClassNotFoundException, IllegalAccessException, InstantiationException {Class clz = Class.forName("cn.zss.domain.Person01");// 前提:类必须有无参构造函数Person01 p = (Person01) clz.newInstance();p.eat();}/** 如果bean没有无参构造,spring无法通过默认的机制管理此bean* */@Testpublic void test03() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Person01 p = (Person01)context.getBean("person01");p.eat();((ClassPathXmlApplicationContext)context).close();}
}

通过静态工厂创建对象

工厂设计模式 (Factory Pattern)

  • 通过工厂隐藏创建对象的细节,使用者不需要关注创建对象的细节,只要找到工厂调用对应的生产方法,就可以直接得到可用的对象

很多的时候,我们面对的类是无法通过无参构造去创建的,例如该类没有无参构造、是一抽象类等等情况,此时无法要求spring通过无参构造创建对象,此时可以使用静态工厂方式创建对象。

  1. 定义Person类

    public class Person01 {private String name;private int age;private char gender;public Person01(String name, int age, char gender) {this.name = name;this.age = age;this.gender = gender;}
    }
    
  2. 定义Person类的静态工厂

    /*
    * 静态工厂
    * */
    public class Person01StaticFactory {// 私有化构造方法,保证静态工厂类无法实例化private  Person01StaticFactory(){}// 提供公有静态方法用来生产目标对象public static Person01 getInstance(){return new Person01("lili",20,'女');}
    }
    
  3. 编写配置文件

    <!-- 静态工厂配置-->
    <bean id="person01" class="cn.zss.domain.Person01StaticFactory" factory-method="getInstance"></bean>
    
  4. 创建对象

    public class Test01 {@Testpublic void test01(){// 1. 初始化容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 获取beanPerson01 p = (Person01) context.getBean("person01");// 3. 使用beanSystem.out.println(p);// 4. 关闭容器((ClassPathXmlApplicationContext)context).close();}}
    

实例工厂创建对象

实例工厂也可以解决类是无法通过无参构造创建的问题,解决的思路和静态工厂类似,只不过实例工厂提供的方法不是静态的。

spring需要先创建出实例工厂的对象,再调用实例工厂对象上指定的普通方法来创建对象。所以实例工厂也需要配置到Spring中管理。

  1. 定义Person类的实例工厂

    public class Person01InstanceFactory {public Person01 getInstance(){return new Person01("Tom",20,'男');}
    }
    
  2. 编写配置文件

    <!--实例工厂-->
    <bean id = "pif" class="cn.zss.domain.Person01InstanceFactory"></bean>
    <bean id = "person01" factory-bean="pif" factory-method="getInstance"></bean>
    
  3. 创建对象

    @Test
    public void test02(){// 1. 初始化容器ApplicationContext context = new ClassPathXmlApplicationContext("application2Context.xml");// 2. 获取beanPerson01 p = (Person01) context.getBean("person01");// 3. 使用beanSystem.out.println(p);// 4. 关闭容器((ClassPathXmlApplicationContext)context).close();
    }
    

Spring工厂创建对象

  1. 定义Person类的Spring工厂

    /*Spring工厂*/
    public class Person01SpringFactory implements FactoryBean<Person01> {/** 生产对象的方法* */@Overridepublic Person01 getObject() throws Exception {return new Person01("Potter",20,'男');}/** 获取当前工厂生产的对象类型的方法* */@Overridepublic Class<?> getObjectType() {return Person01.class;}/** 当前对象是否是单例对象* */@Overridepublic boolean isSingleton() {return true;}
    }
    
  2. 编写配置文件

    <!--Spring工厂-->
    <bean id = "person01" class="cn.zss.domain.Person01SpringFactory"></bean>
    
  3. 创建对象

    @Test
    public void test02(){// 1. 初始化容器ApplicationContext context = new ClassPathXmlApplicationContext("application3Context.xml");// 2. 获取beanPerson01 p = (Person01) context.getBean("person01");// 3. 使用beanSystem.out.println(p);// 4. 关闭容器((ClassPathXmlApplicationContext)context).close();
    }
    

单例和多例

  • Spring容器管理的bean在默认情况下是单例的 (常用),即一个bean只会创建一个对象,存在内置 map中,之后无论获取多少次该bean,都返回同一个对象。

  • Spring默认采用单例方式,减少了对象的创建,从而减少了内存的消耗。

  • 在实际开发中是存在多例的需求的,Spring也提供了选项可以将bean设置为多例模式。

配置单例模式 / 多例模式:

<?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--><!-- scope属性控制当前bean的创建模式:singleton:则当前bean处在单例模式中,默认就是此模式prototype:则当前bean处在多例模式中--><bean id="cart" class="cn.zss.domain.Cart" scope="prototype"></bean>
</beans>

bean在单例模式下的生命周期

  • bean在单例模式下,spring容器启动时解析xml发现该bean标签后,直接创建该bean的对象存入内部map中保存
  • 之后无论获取多少次返回的都是同一个bean
  • 直到容器销毁,spring关闭,内部的bean跟着被销毁

bean在多例模式下的生命周期

  • bean在多例模式下,spring容器启动时解析xml发现该bean标签后,只是将该bean进行管理,并不会创建对象
  • 此后每次使用 getBean()获取该bean时,spring都会重新创建该对象返回,每次都是一个新的对象
  • 这个对象spring容器并不会持有,什么销毁取决于使用该对象的用户自己什么时候销毁该对象 (即使关闭容器,对象也可以继续使用)。

懒加载机制

  • Spring默认会在容器初始化的过程中,解析xml,并将单例的bean创建并保存到map中

  • 这样的机制在bean比较少时问题不大,但一旦bean非常多时,spring需要在启动的过程中花费大量的时间来创建bean,花费大量的空间存储bean,但这些bean可能很久都用不上,在时间和空间上的浪费显得非常的不值得。

  • 所以Spring提供了懒加载机制。所谓的懒加载机制就是可以规定指定的bean不在启动时立即创建,而是在后续第一次用到时才创建,从而减轻在启动过程中对时间和内存的消耗。

  • 懒加载机制只对单例bean有作用,对于多例bean设置懒加载没有意义。

懒加载的配置方式:

<?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"default-lazy-init="true"><!--配置父标签默认值 (为全局配置懒加载)--><!--配置bean--><!-- 配置懒加载lazy-init = "default" 使用父标签中配置的默认值lazy-init = "true" 开启懒加载lazy-init = "false" 关闭懒加载** 多例模式里设置懒加载毫无意义--><!--为指定bean配置懒加载--> <bean id="person" class="cn.zss.domain.Person" lazy-init="true"></bean>
</beans>

如果同时设定全局和指定bean的懒加载机制,且配置不相同,则对于该bean局部配置覆盖全局配置。

配置初始化和销毁的方法

在Spring中如果某个bean在初始化之后或销毁之前需要做一些额外操作可以为该bean配置初始化和销毁的方法,在这些方法中完成要功能。

配置方式 (示例):

  1. 定义类

    public class JDBCUtils {/** 初始化方法* */public void init(){System.out.println("连接数据库");}/** 销毁方法* */public void destroy(){System.out.println("销毁数据库连接");}/** 普通方法* */public void getConn(){System.out.println("获取连接");}
    }
    
  2. 编写配置文件

    <?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="jdbcUtils" class="cn.zss.domain.JDBCUtils" init-method="init" destroy-method="destroy"></bean>
    </beans>
    
  3. 测试

    public class Test01 {/** 初始化,销毁方法测试* */@Testpublic void test01(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");JDBCUtils jdbcUtils = (JDBCUtils) context.getBean("jdbcUtils");jdbcUtils.getConn();/** 打印结果:*   连接数据库  获取连接  销毁数据库连接* */((ClassPathXmlApplicationContext)context).close();}
    }
    

Spring中关键方法的执行顺序

  • 在Spring创建bean对象时,先创建对象 (通过无参构造或工厂)
  • 接着调用init方法来执行初始化操作
  • 之后此bean就可以调用其它普通方法
  • 而在对象销毁之前,spring容器调用其destory方法来执行销毁操作。

Spring DI

创建对象的过程中Spring可以依据配置对对象的属性进行设置,这个过称之为依赖注入,即DI

基于Set方法的注入

通常的javabean属性都会私有化,而对外暴露Getter和Setter方法,此时spring可以通过Setter方法将属性的值注入对象。

  • 普通属性注入:

    a. 定义类

    public class Hero {private String name;private int age;private List<String> job;private Set<String> set;private Map<String, String> map;private Properties props;public Hero() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public List<String> getJob() {return job;}public void setJob(List<String> job) {this.job = job;}public Set<String> getSet() {return set;}public void setSet(Set<String> set) {this.set = set;}public Map<String, String> getMap() {return map;}public void setMap(Map<String, String> map) {this.map = map;}public Properties getProps() {return props;}public void setProps(Properties props) {this.props = props;}@Overridepublic String toString() {return "Hero{" +"name='" + name + '\'' +", age=" + age +", job=" + job +", set=" + set +", map=" + map +", props=" + props +'}';}
    }
    

    b. 编写配置文件

    <?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--><bean id="hero" class="cn.zss.domain.Hero"><!--配置的是bean属性的名称(由Set方法决定),而不是类属性--><!--基本属性配置--><property name="name" value="狄仁杰"> </property><property name="age" value="18"></property><!--集合属性配置--><!--job属性配置--><property name="job"><list><value>神探</value><value>官员</value></list></property><!--set属性配置--><property name="set"><set><value>s1</value><value>s2</value></set></property><!--map属性配置--><property name="map"><map><entry key="k1" value="v1"></entry><entry key="k2" value="v2"></entry><!--重复的键,保存最后的键值对--><entry key="k2" value="v4"></entry></map></property><!--properties属性配置--><property name="props"><props><prop key="p1">v1</prop><prop key="p2">v2</prop></props></property></bean>
    </beans>
    
  • 自定义bean的注入:

    a. 自定义类

    public class Enemy {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Enemy{" +"name='" + name + '\'' +", age=" + age +'}';}
    }
    

    b. 在Hero类中定义属性并提供get和set方法

    public class Hero {private Enemy enemy;// ... 其他属性public Enemy getEnemy() {return enemy;}public void setEnemy(Enemy enemy) {this.enemy = enemy;}// ... 其他get和set方法@Overridepublic String toString() {return "enemy=" + enemy;}
    }
    

    c. 编写配置文件

    <?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--><bean id="hero" class="cn.zss.domain.Hero"><!--自定义bean注入--><!--通过ref指定配置的bean的id--><property name="enemy" ref="enemy"></property></bean><bean id = "enemy" class="cn.zss.domain.Enemy"><property name="name" value="袁天罡"></property><property name="age" value="20"></property></bean>
    </beans>
    
  • 自动装配

    为指定<bean>开启自动装配:

    ?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--><!--自动装配:autowire="byName"根据当前要注入的自定义bean属性名称找到对应id的bean,找到就注入,找不到不注入autowire="byType":根据当前要注入的自定义bean的Class类型找到对应Class的bean,找到唯一的就注入,找不到不注入,找到多个抛出异常--><bean id="hero" class="cn.zss.domain.Hero" autowire="byName"><bean id = "enemy" class="cn.zss.domain.Enemy"><property name="name" value="袁天罡"></property><property name="age" value="20"></property></bean>
    </beans>
    

基于构造方法的注入

对象属性设置的另一种方式是在对象创建的过程中通过构造方法传入并设置对象的属性;Spring也可以通过这样的构造方法实现属性的注入

a. 定义类

public class Hero {String name;private int age;private Enemy enemy;public Hero() {}public Hero(String name, int age, Enemy enemy) {this.name = name;this.age = age;this.enemy = enemy;}@Overridepublic String toString() {return "Hero{" +"name='" + name + '\'' +", age=" + age +", enemy=" + enemy +'}';}
}
public class Enemy {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Enemy{" +"name='" + name + '\'' +", age=" + age +'}';}
}

b. 编写配置文件

<?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--><bean id="hero" class="cn.zss.domain.Hero" ><!--index: 对第几个参数设置name: 对哪个名称的参数设置type: 对哪个类型的参数设置前三个参数必选一个value: 要设置什么值(直接赋值)ref: 要设置什么值 (引用赋值)后两个参数根据类型必选一个--><constructor-arg index="0" value ="狄仁杰" type="java.lang.String"></constructor-arg><constructor-arg index="1" value ="45" type="int"></constructor-arg><constructor-arg index="2" type="cn.zss.domain.Enemy" ref="enemy"></constructor-arg></bean><bean id ="enemy" class="cn.zss.domain.Enemy"><property name="name" value="袁天罡"></property><property name="age" value="66"></property></bean>
</beans>

c. 编写测试类

ublic class Test01 {@Testpublic void test01(){// 初始化容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取beanHero hero = (Hero)context.getBean("hero");// 使用beanSystem.out.println(hero);// 销毁bean((ClassPathXmlApplicationContext)context).close();}
}

基于注解方式配置Spring IoC

注解

  • 注释:java中注释的格式://, /**/, /** */

  • 注解:

    • sun在jdk5.0开始提供的新特性
    • 给程序看的提示信息,程序看后可以根据有无注解及注解上属性的不同配置执行不同的逻辑。
    • java中的注解的格式:@AnnoName(key=value,…)
  • 注解在开发中,可以作为轻量化配置来使用,比起使用xml作为配置文件,更加的轻便易用,在java开发中大量的使用。

java 内置注解

  • @Override — 声明重写父类方法的注解,要求编译器帮我们检查是否成功的覆盖,如果没有成功覆盖父类方法,编译器将会进行报错提示

  • @Deprecated — 声明方法被过时,不再建议使用,要求编译器在编译的过程中对于这样的方法的调用提出警告,提示方法过时。

  • @SuppressWarnings — 压制警告,提示编译器,在编译的过程中对指定类型的警告不再提示

注解可以基于有注解 / 没注解或者注解属性的不同来控制程序按照不同方式运行

注解常用作轻量化配置,替代配置文件的部分功能

注解方便开发阶段的配置和更改,配置文件在发布后仍可以方便修改,因此实际开发中注解和配置文件需要结合使用

自定义注解

开发一个注解类

  • 开发一个注解类的过程,非常类似于开发一个接口,只不过需要通过@interface关键字来声明

使用元注解修饰注解的声明

  • 所谓的元注解是用来修饰注解声明的注解,可以控制被修饰的注解的特性。
  1. @Target:用来声明被修饰的注解可以用在什么位置。

    可以在@Target的属性中设置ElementType类型的数组来指定可以使用的位置。如果不用此元注解修饰,默认注解可以用在任意位置。

    @Target({ElementType.FIELD, ElementType.METHOD})
    public @interface FirstAnno {  // 自定义注解可以应用于属性和方法上
    }
    
  2. @Retention:用来声明被修饰的注解会被保留到什么阶段。

    java —> 编译 —> .class —> 类加载器 —> 字节码

    可以在该注解的属性中通过RetentionPolicy类型的值来指定注解被保留到何时。

    • RetentionPolicy.SOURCE:此注解将会被保留到源码阶段,.java中;在编译过程中被删除。这种类型的注解通常是给编译器看的。

    • RetentionPolicy.CLASS:此注解将会被保留在源码阶段和编译阶段,.java和.class中;在类加载的过程中被删除。这种类型的注解通常是给类加载器看的。

    • RetentionPolicy.RUNTIME:此注解将会被保留在源码阶段,编译阶段和运行阶段,.java .class和内存中的字节码中都会存在。这种类型的注解通常用来在运行阶段进行反射,控制程序运行过程。

    • 只有RUNTIME级别的注解才可以通过反射技术进行反射。

  3. @Documented :用来声明被修饰注解是否要被文档提取工具提取到文档中;javadoc命令可以用于生成文档

    默认不提取。

  4. @Inherited:用来声明被修饰的注解是否具有继承性

    默认没有继承性

为注解增加属性

  • 注解类中还可以声明属性。
  • 为注解类声明属性的过程非常类似于为接口定义方法
  • 但要求,注解中的所有的属性必须是public的,可以显式声明,也可以不声明,不声明默认就是public的。
  • 注解中的属性只能是八种基本数据类型,String类型,Class类型,枚举类型,其他注解类型,及以上类型的一维数组。
  • 一旦声明了属性,在注解使用的过程中就必须为属性赋值,为其赋值的方式就是使用注解时,在注解后通过(属性名=属性值)的方式 指定属性的值
  • 也可以在声明注解时 在注解的属性后 通过default关键字,声明属性的默认值,声明过默认值的属性默认采用默认值,也可以手动赋值,覆盖默认值
  • 如果属性是 一维数组类型而在传入的数组中只有一个值,则包括数组的大括号可以省略
  • 如果注解的属性只有一个需要赋值,且该属性的名称叫做value,则在使用注解时,value=“xxx” 中的 ‘value=’ 可以不写
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FirstAnno {public String name();public int age() default 0;String[] addrs();
}
public class Person {String name;int age;@FirstAnno(name="lili",age=18, addrs={"china","beijing"})public void eat(String food1, String food2){System.out.println("eat...");}
}

反射注解

  • 只有RUNNTIME级别的注解能够被反射
  • Class Method Field Package… 都提供了获取注解信息的方法
返回值方法
Annotation[]getAnnotations ():返回此元素上存在的所有注释。
AgetAnnotation (Class annotationClass): 如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
booleanisAnnotationPresent (Class<? extends Annotation> annotationClass) : 如果指定类型的注释存在于此元素上,则返回 true,否则返回 false

Spring注解方式实现IoC & DI

基本实现流程

a. 导入开发包

在这里插入图片描述

b. 编写配置文件,并导入context约束

<?xml version="1.0" encoding="UTF-8"?>
<!--约束技术: 通过约束技术可以进行代码提示xmlns: 用于声明名称空间的缩写,以缩写开头的标签受名称空间的管理名称空间必须是唯一的http://www.springframework.org/schema/beans/spring-beans.xsd: 为名称空间真正文件的地址
-->
<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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"></beans>

Idea可以将该配置文件设置为模板,方便以后使用

文件 —> 设置 —> 编辑器 —> 文件和代码模板 —> 点击左侧加号新建模板

c. 开启包扫描

在配置文件中,开启包扫描,指定Spring自动扫描哪些个包下的类;只有在指定的扫描包下的类上的IOC注解才会生效。

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 指定扫描哪些包--><!--Spring容器会在启动时自动扫描指定包极其子孙包--><context:component-scan base-package="cn.zss.domain"></context:component-scan>
</beans>

d. 使用注解注册bean

  • 在配置的包中的类上使用@Component注解,则这个类会自动被注册为bean

  • 使用当前类的class为的class,通常情况下使用类名首字母小写为的id

  • 如果类名的第二个字母为大写则首字母保留原样,如类名: PErson —> id名: PErson

  • 也可以通过在@Component中配置value属性,明确的指定bean的id

package cn.zss.domain;import org.springframework.stereotype.Component;@Component  // <bean id="person" class="cn.zss.domain.Person"></bean>
/*@Component(value="per") 等价于 @Component("per")<bean id="per" class="cn.zss.domain.Person"></bean>*/
public class Person {
}

e. 测试类

public class Test01 {@Testpublic void test01(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Person p = (Person)context.getBean("person");System.out.println(p);    ((ClassPathXmlApplicationContext)context).close();}
}

注解方式实现工厂注册bean

  • Spring默认通过反射创建bean,如果某些bean没有无参构造器或创建过程非常复杂,则无法通过简单的反射创建bean
  • 此时可以通过创建bean的工厂,令SpringIoC通过工厂来创建bean,从而进行注册。
  • 可以通过配置文件方式配置bean工厂,同样也可以通过注解配置bean工厂。

配置工厂类

  • 工厂类必须放在包扫描目录下,且被@Component注解修饰

配置工厂类中生产bean的方法

  • 工厂中生产bean的方法要被@Bean修饰

  • 则此方法会被SpringIOC调用,并将返回的对象注册为Spring的bean

  • 默认自动推断id (根据方法名,不常用),在老版本中通过@Bean(name=“xxx”)指定id;在新版本中,通过value属性指定id。

案例演示:

a. 定义Dog类

package cn.zss.domain;public class Dog {public Dog (String name){}
}

b. 定义Dog类的工厂类

package cn.zss.domain;import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;/*
* 实例工厂
* */
@Component
public class DogFactory {// <bean id = "fogFactory" class="cn.zss.domain.DogFactory"></bean>@Bean(name = "dog")// <bean id = "dog" class="cn.zss.domain.Dog"></bean>public Dog getInstance(){return new Dog("Tom");}
}

c. 测试

@Test
public void test02(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Dog dog = (Dog)context.getBean("dog");System.out.println(dog);((ClassPathXmlApplicationContext)context).close();
}

Spring注解方式实现DI

  • 通过底层的反射机制实现 (可以突破私有的防控权限),既不依靠构造函数,也不依靠Set方法

a. 导入开发包

在这里插入图片描述

b. 编写配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--约束技术: 通过约束技术可以进行代码提示xmlns: 用于声明名称空间的缩写,以缩写开头的标签受名称空间的管理名称空间必须是唯一的http://www.springframework.org/schema/beans/spring-beans.xsd: 为名称空间真正文件的地址
-->
<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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 指定扫描哪些包--><!--Spring容器会在启动时自动扫描指定包极其子孙包--><context:component-scan base-package="cn.zss.domain"/><!--开启注解方式的DI--><context:annotation-config/>
</beans>

c. 注解方式注入spring内置支持的类型数据 - 非集合类型

  • spring中可以通过@Value注解来实现spring内置支持的类型的属性的注入。
package cn.zss.domain;@Component
public class Hero {@Value("美国队长")private String name;@Value("40")private int age;private List<String> list;private Set<String> set;private Map<String, String> map;private Properties props;@Overridepublic String toString() {return "Hero{" +"name='" + name + '\'' +", age=" + age +", list=" + list +", set=" + set +", map=" + map +", props=" + props +'}';}
}
  • 这种方式可以实现Spring内置支持类型的注入,但是将注入的值写死在了代码中,后续如果希望改变注入的值,必须来修改源代码
  • 可以将这些值配置到一个properties配置文件中,再在Spring中进行引入。

d. 编写properties配置文件(my.properties)并加载

myname = 美国队长
myage = 40
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 指定扫描哪些包--><!--Spring容器会在启动时自动扫描指定包极其子孙包--><context:component-scan base-package="cn.zss.domain"></context:component-scan><!--开启注解方式的DI--><context:annotation-config></context:annotation-config><!--加载属性文件--><context:property-placeholder location="my.properties" />
</beans>

e. 添加@Value注解

@Component
public class Hero {@Value("${myname}")private String name;@Value("${myage}")private int age;private List<String> list;private Set<String> set;private Map<String, String> map;private Properties props;@Overridepublic String toString() {return "Hero{" +"name='" + name + '\'' +", age=" + age +", list=" + list +", set=" + set +", map=" + map +", props=" + props +'}';}
}

f. 注解方式注入spring内置支持的类型数据

​ 引入util名称空间,通过适当的util标签注册数据

<?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:util="http://www.springframework.org/schema/util"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.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsd"><!-- 指定扫描哪些包--><!--Spring容器会在启动时自动扫描指定包极其子孙包--><context:component-scan base-package="cn.zss.domain"></context:component-scan><!--开启注解方式的DI--><context:annotation-config></context:annotation-config><!--加载属性文件--><context:property-placeholder location="my.properties" /><!--定义集合数据--><util:list id="l1"><value>l1</value><value>l2</value></util:list><util:set id="s1"><value>s1</value><value>s2</value></util:set><util:map id="m1"><entry key="k1" value="v1"></entry><entry key="k2" value="v2"></entry></util:map><util:properties id ="p1"><prop key="p1">v1</prop><prop key="p2">v2</prop></util:properties>
</beans>

​ 再在类的属性中通过@Value注入赋值

@Component
public class Hero {@Value("${myname}")private String name;@Value("${myage}")private int age;@Value("#{@l1}")private List<String> list;@Value("#{@s1}")private Set<String> set;@Value("#{@m1}")private Map<String, String> map;@Value("#{@p1}")private Properties props;@Overridepublic String toString() {return "Hero{" +"name='" + name + '\'' +", age=" + age +", list=" + list +", set=" + set +", map=" + map +", props=" + props +'}';}
}

g. 使用注解注入自定义bean类型数据

​ 在bean中的属性上通过@Autowired实现自定义bean类型的属性注入

  • 当Spring容器解析到@Component注解时,创建当前类的bean在Spring容器中进行管理,在创建bean的过程中发现了@Autowired注解,会根据当前bean类型,寻找在Spring中是否存在该类型的bean,找到直接注入,如果找不到还会检查是否有子孙类、实现类存在,如果存在唯一的则自动注入,如果还是没有找到或找到多个无法注入,则还会按照属性名对应id去查找对应的bean,如果存在则注入,如果还是没有找到则抛出异常。

  • 也可以额外配置@Qualifier(value=“xxx”)注解强制要求按照id寻找bean,则此时会直接使用给定的id寻找bean,而不会进行基于类型的匹配。

在这里插入图片描述

package cn.zss.domain;
@Component
public class Hero {@Value("美国队长")private String name;@Value("100")private int age;/*首先按类型注入,* 如果找到唯一就注入,* 如果找不到或者找到多个,则开始按照id注入* 寻找是否存在bean的id和当前属性的属性名相同,如果存在就注入,不存在就抛出异常*/@Autowired  // 根据类型找到Enemy和newEnemy,然后根据id(enemy) 找到Enemy// @Qualifier("newEnemy")  --- > Hero{name='美国队长', age=100, enemy=Enemy{name='灭霸', age=150}}// 一旦配置了@Qualifier,@Autowired的工作机制就会发生变化,直接按照指定的id进行注入,// 如果找到唯一的就注入,找不到,或者找到多个直接抛出异常private Enemy enemy;@Overridepublic String toString() {return "Hero{" +"name='" + name + '\'' +", age=" + age +", enemy=" + enemy +'}';}
}
package cn.zss.domain;
@Component
public class Enemy {@Value("九头蛇")private String name;@Value("100")private int age;@Overridepublic String toString() {return "Enemy{" +"name='" + name + '\'' +", age=" + age +'}';}
}
package cn.zss.domain;
@Component
public class NewEnemy extends Enemy{@Value("灭霸")private String name;@Value("150")private int age;@Overridepublic String toString() {return "Enemy{" +"name='" + name + '\'' +", age=" + age +'}';}
}

其他注解

a. @Scope(value=“prototype”):配置修饰的类的bean是单例还是多例,如果不配置默认为单例

@Component
@Scope("prototype")
public class Person {}

b. @Lazy: 配置修饰的类的bean采用懒加载机制

@Component
@Lazy
public class Person02 {public Person02(){System.out.println("init...");}
}

c. @PostConstruct: 在bean对应的类中,修饰某个方法,将该方法声明为初始化方法,对象创建之后立即执行

d. @PreDestroy:在bean对应的类中,修饰某个方法,将该方法声明为销毁的方法,对象销毁之前调用的方法

@Component
public class JDBCUtils {/** 初始化方法* */@PostConstructpublic void initConn(){System.out.println("连接数据库");}/** 销毁方法* */@PreDestroypublic void destroyConn(){System.out.println("销毁数据库连接");}/** 普通方法* */public void insert(){System.out.println("插入");}/** 普通方法* */public void delete(){System.out.println("删除");}}

e. @Controller @Service @Repository @Component:

  • 这四个注解的功能是完全相同的,都是用来修饰类,将类声明为Spring管理的bean的。
  • 其中@Component一般认为是通用的注解
  • 而@Controller用在软件分层中的控制层,一般用在Web层
  • 而@Service用在软件分层中的业务访问层,一般用在Service层
  • 而@Repository用在软件分层中的数据访问层,一般用在DAO层

利用Spring IOC&DI 实现软件分层解耦

在层与层之间设计接口 (面向接口编程) 并通过Spring注册对象

在这里插入图片描述

Web层

package cn.zss.web;
@Controller
public class UserServlet {@Autowiredprivate UserService userService = null;public void regist(){System.out.println("userServlet ... regist ...");userService.regist();}
}

Service层

a. 接口UserService

package cn.zss.service;public interface UserService {public void regist();
}

b. 接口的实现类UserServiceImpl

package cn.zss.service;
@Service
public class UserServiceImpl implements UserService {@Autowired@Qualifier("userDao")private UserDao userDao = null;@Overridepublic void regist() {System.out.println("userService ... regist");userDao.insert();}
}

DAO层

a. 接口

package cn.zss.dao;public interface UserDao {void insert();
}

b. 接口的第一个实现类

package cn.zss.dao;import org.springframework.stereotype.Repository;@Repository("userDao")
public class MySqlUserDao implements UserDao{@Overridepublic void insert() {System.out.println("Mysql ... insert ...");}
}

c. 接口的第二个实现类

package cn.zss.dao;import org.springframework.stereotype.Repository;@Repository
public class OracleUserDao implements UserDao {@Overridepublic void insert() {System.out.println("Oracle ... insert ...");}
}
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Cocos2d-x 3,androidapp开发教程

    CatmullRomBy类 &#xff1a;这是一个按照笛卡尔曲线移动目标点的动作. ** ** – ActionCatmullRom – 笛卡尔曲线运动 local function ActionCatmullRom() local layer cc.Layer:create() initWithLayer(layer) centerSprites(2) – 设置精灵位置 tamara:setPosition…...

    2024/4/7 20:15:09
  2. 数据分析课程笔记(一)简介、jupyter和conda、matplotlib

    数据分析课程笔记数据分析简介数据分析流程jupyter和conda的使用condajupyter notebookmatplotlib什么是matplotlib基本要点设置图片大小调整X或者Y轴上的刻度设置中文显示折线图实例双折线图实例常用统计图数据分析简介 什么是数据分析 数据分析是用适当的方法对收集来的大量…...

    2024/4/13 18:02:08
  3. Canvas加动画,实现火柴人跳绳效果,移动端混合开发框架

    50); canvas.drawPath(path, paint); 3.quadTo 用于绘制二阶贝塞尔曲线&#xff0c;从上一个点开始&#xff0c;绘制二阶Bezier曲线(x1,y1)为控制点&#xff0c; (x2,y2)为终点如果之前没有调用过 moveTo()&#xff0c;则默认从 (0,0)作为起点绘制。 /** 从上一个点开始&…...

    2024/4/20 16:51:10
  4. 命令 模式

    命令模式 命令模式基本介绍 命令模式&#xff08;Command Pattern&#xff09;&#xff1a;在软件设计中&#xff0c;我们经常需要向某些对象发送请求&#xff0c;但是并不知道请求的接收者是谁&#xff0c;也不知道被请求的操作是哪个&#xff0c; 我们只需在程序运行时指定…...

    2024/4/7 20:15:03
  5. http 报文格式、状态码

    HTTP 报文格式 HTTP 报文由请求行、首部、实体组成&#xff0c;首部和实体之间由 CRLF&#xff08;回车换行符&#xff09; 分隔开。 请求报文格式&#xff1a; <method> <request-URL> <version> <headers> <entity-body>请求方法、请求 URL、…...

    2024/4/14 20:33:04
  6. 不可变集合和Stream流介绍,获取流、收集流

    不可变集合(Set、List、Map) 什么是不可变集合&#xff1f; 不可变集合&#xff0c;就是不可被修改的集合 集合的数据项在创建的时候提供&#xff0c;并且在整个生命周期中都不可改变&#xff0c;否则报错&#xff01; 举个例子&#xff1a;网页上的一些敏感数据并不想让用户…...

    2024/4/13 18:02:03
  7. K8s系列之:DaemonSet在每个Node上调度一个Pod

    K8s系列之&#xff1a;DaemonSet在每个Node上调度一个PodDaemonSet用于管理在集群中每个Node上仅运行一份Pod副本实例。 这种用法适合一些有这种需求的应用。 在每个Node上运行一个GlusterFS存储或者Ceph存储的Daemon进程。在每个Node上运行一个日志采集程序&#xff0c;例如…...

    2024/4/13 18:01:48
  8. 前端面试2022

    常用函数&#xff1a;https://blog.csdn.net/mus123/article/details/106499506 这一次我应聘的岗位是前端开发工程师。成为一名优秀的程序员是我的追求&#xff0c;为此在研究生阶段我很早就结合岗位要求进行了准备。今年6月到8月份&#xff0c;我在武汉金山办公软件有限公司…...

    2024/4/7 20:15:02
  9. MultiMedia知识汇总

    MediaCodec相关 crop信息&#xff08;x,y,m,n&#xff09;左上坐标&#xff08;x,y&#xff09; 右下坐标&#xff08;m,n&#xff09;&#xff0c;也就是解码宽高&#xff0c;宽m-x1&#xff0c;高y-n1 mediacodec的状态&#xff1a;&#xff08;思考一下如果你创建mediacodec…...

    2024/4/13 18:01:58
  10. 7-1 念数字 (10 分)

    输入一个整数&#xff0c;输出每个数字对应的拼音。当整数为负数时&#xff0c;先输出fu字。十个数字对应的拼音如下&#xff1a; 0: ling 1: yi 2: er 3: san 4: si 5: wu 6: liu 7: qi 8: ba 9: jiu输入格式&#xff1a; 输入在一行中给出一个整数&#xff0c;如&#xff1…...

    2024/4/13 18:01:43
  11. 初学python之文件处理

    今日总结 报错&#xff1a;ValueError: I/O operation on closed file. 原因为代码结构上提示行未正确缩进&#xff0c;导致程序尝试修改已经关闭的文件&#xff0c;在用with语句时要注意此问题。代码中倒数第三行若没有缩进与倒数第二行平齐就会出现此问题。 def exchange(…...

    2024/4/15 22:52:19
  12. 第二节: 沥青路面结构组成特点(1)结构组成

    1、基本原则 城镇沥青路面是城市道路的典型路面&#xff0c;道路结构由面层、基层和路基组成&#xff0c;层间结合必须紧密稳定&#xff0c;以保证结构的整体性和应力传递的连续性。   行车何在和自然因素对路面结构的影响随深度的增加而逐渐减弱&#xff0c;因而对路面材料的…...

    2024/4/18 3:51:03
  13. redis基础知识储备

    NoSql&#xff1a;&#xff08;Not Only SQL&#xff09;意思是不仅仅是sql&#xff0c;泛指非关系型数据库。 NoSql不依赖业务逻辑方式存储&#xff0c;而是以简单的key - value 模式存储&#xff0c;因此大大增加了数据库的扩展能力&#xff0c;有一下特点 不遵循SQL标准不支…...

    2024/4/13 18:01:43
  14. ffmpeg实战之ffplay简单过滤器

    ...

    2024/4/18 15:17:54
  15. 实施领域驱动设计(Implementing Domain Driven Design翻译)

    使用 ABP 框架实现领域驱动设计的实用指南 Apractical guide for implementing the Domain Driven Designwith the ABP Framework 引言 介绍 这是实现领域驱动的实用指南设计&#xff08;DDD&#xff09;。虽然实现细节依赖于ABP 框架基础设施,但是核心概念、原则和模式适用…...

    2024/4/17 7:05:06
  16. Base64算法,kotlin密封接口

    密码协议&#xff1a;也称安全协议&#xff0c;指以密码学为基础的消息交换的通信协议&#xff0c;目的是在网络环境中提供安全服务。 密码系统&#xff1a;指用于加密、解密的系统。 柯克霍夫原则&#xff1a;数据的安全基于密钥而不是算法的保密。即系统的安全起决于密钥&a…...

    2024/4/13 18:01:48
  17. 制药实验室设备的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告

    本文研究全球与中国市场制药实验室设备的发展现状及未来发展趋势&#xff0c;分别从生产和消费的角度分析制药实验室设备的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场…...

    2024/4/25 10:28:51
  18. ASP.net 异步刷新

    一.说明 此文是小白在学习张晨光老师的视频教学<<Asp.Net WEB服务器编程技术>>中做的学习笔记,一些知识点也是跟着教程走的,大家也可以去老师的主页去学习,谢谢大家. 二.前端代码 <% Page Language"C#" AutoEventWireup"true" CodeBehin…...

    2024/4/19 19:37:56
  19. 15.第20、21章.项目集、项目组合管理

    文章目录项目集和组合管理的区别项目集管理项目组合管理项目集和组合管理的区别 项目&#xff1a;经常被作为实现组织战略计划的一种手段。 项目集&#xff1a;是一组相互关联且被协调管理的项目。协调管理是为了获得对单个项目分别管理所无法实现的利益和控制项目集管理重点关…...

    2024/4/20 3:09:25
  20. SpringCloud Day12---SpringCloud Alibaba Sentinel 服务熔断与限流

    文章目录15. SpringCloud Alibaba Sentinel实现熔断与限流15.1 概述15.1.1 官网15.1.2 介绍15.1.3 去哪下15.1.4 能干嘛15.1.5 怎么玩15.2 安装Sentinel控制台15.2.1 sentinel组件由2部分构成15.2.2 安装步骤15.3 初始化演示工程15.3.1 前提条件15.3.2 建立sentinel804115.3.3 …...

    2024/4/7 20:14:49

最新文章

  1. Python面试十问2

    一、如何使用列表创建⼀个DataFrame # 导入pandas库 import pandas as pd# 创建一个列表&#xff0c;其中包含数据 data [[A, 1], [B, 2], [C, 3]]# 使用pandas的DataFrame()函数将列表转换为DataFrame df pd.DataFrame(data, columns[Letter, Number]) # 列名# 显示创建的…...

    2024/5/4 1:39:21
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. C++ //练习 11.14 扩展你在11.2.1节练习(第378页)中编写的孩子姓到名的map,添加一个pair的vector,保存孩子的名和生日。

    C Primer&#xff08;第5版&#xff09; 练习 11.14 练习 11.14 扩展你在11.2.1节练习&#xff08;第378页&#xff09;中编写的孩子姓到名的map&#xff0c;添加一个pair的vector&#xff0c;保存孩子的名和生日。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#x…...

    2024/5/1 13:00:20
  4. 一个浮动绝对居中的tailwindcss

    今天改进图片组件&#xff0c;遇到个SVG绝对居中的问题。想起之前大概是通过top left来实现&#xff0c;由于组件的宽高需要动态输入。不能定死宽高&#xff0c;于是想起来问GPT。刚开始老是给一些很菜的代码&#xff0c;不是我想要的 气不打一处来&#xff0c;索性给他限死框框…...

    2024/5/1 13:34:37
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/3 23:10:03
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/2 15:04:34
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57