Spring笔记

一、Spring了解

Spring理念:使现有的技术更加容易使用,整合了现有的技术框架,简化企业级应用开发;

优点:

Spring是一个免费开源的框架
Spring是一个轻量级、非入侵式(在原先代码上加上Spring框架后没有影响)的框架
支持事务的处理,对框架整合的支持

缺点:

配置繁琐

核心:

控制反转(IOC),面向切面编程(AOP)

总结:

Spring就是一个轻量级的控制反转(IOC)和面向切面编程的框架;

在这里插入图片描述

二、Spring核心1:控制反转(IOC)理论

概念理解:

IOC在这里简单来说就是:对象由Spring创建、管理、装配,不再需要自己new对象了

其实之前学的servlet中也涉及到了IOC原理,当把一个Servlet实现类配置到web.xml文件中后,创建Servlet对象的工作就不再需要我们自己了,而是交给了WEB服务器(Tomcat)来创建管理,包括Filter、Listener都是由Tomcat容器统一控制实例化、初始化、执行方法、销毁等等;

Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称 为 Bean;Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式 来管理 Bean 之间的依赖关系,使用 IOC实现对象之间的解耦和;

使用IOC目的:

开闭原则(对拓展开放,对修改关闭);实现代码的解耦合;

通过IOC来创建对象:

1,首先需要一个XML配置文件(一般命名为: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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="..." class="...">  <!-- collaborators and configuration for this bean go here --></bean><bean id="..." class="..."><!-- collaborators and configuration for this bean go here --></bean><!-- more bean definitions go here --></beans>

可以看到 <beans> 标签下有很多 <bean> 标签,这些bean标签就代表位于Spring容器中的一个个对象;

自己的理解:Spring容器就像一个加工咖啡豆的盒子(beans),我可以把需要的对象(bean)交给它来加工配置;这样盒子中(beans)就有了各种各样的咖啡豆(bean),当我需要哪个咖啡豆(bean)时直接从这个咖啡豆盒子(beans)中取出来就可以了;

就是一个标签对应一个对象


2,假设定义了一个Hello类:

public class Hello {private String name;Hello() {System.out.println("Hello的无参构造方法");}// 有参构造Hello(String str) {this.name = str;System.out.println("Hello的有参构造方法");}// set和get方法public void setName(String name) {this.name = name;}public String getName() {return name;}// sayHello方法public void sayHello() {System.out.println("Hello " + name);}
}

注意:这里定义的类如果想要通过Spring容器创建对象,那么必须写上其中所有属性set方法(get方法最好也写上,反正idea自动生成不废劲),只有这样Spring容器才可以通过配置文件的信息设置相应对象的属性值;


3,在applicationContext.xml文件中配置该类创建对象

使用默认构造器

<bean id="hello" class="com.yang.service.Hello"><property name="name" value="Spring"/>
</bean>

这就已经代表一个Hello对象了,相当于java代码:

Hello hello = new Hello();
hello.setName("Spring");

通过和实际java代码对比来解释一下标签中的元素:

  • id:所创建的对象的名称,就是变量名,是自定义的,程序通过 id 属性访问 bean,bean 与 bean 间的依赖关系也是通过 id 属性关联的。
  • class:创建的对象类型(使用全限定名称),这里只能是类,不能是接口。

其中的 <property> 标签作用:

  • property:给该对象中的对应属性赋值

    • name:赋值的属性名称
    • value:所赋的值

    当有多个属性时可以写多个 <property> 标签,一个标签对应一个属性;

如果是使用有参构造:

<bean id="hello" class="com.yang.service.Hello"><constructor-arg name="str" value="Java"/><property name="name" value="Spring"/>
</bean>

那么就等同于java代码:

Hello hello = new Hello("Java");
hello.setName("Spring");

可以看出来:

<constructor-arg> 标签的作用:

  • constructor-arg:当使用对象的有参构造时给对应的形参赋值

    • name:对应有参构造方法中形参的名称
    • vaule:对应形参所赋的值

    当构造方法有多个参数时,可以写多个 <constructor-arg> 标签,一个标签对应一个形参


4,测试代码

通过第三步其实就已经创建了我需要的Hello对象存放到了Spring容器中了,接下来测试时就需要从容器中获取对应的对象了;

@Test
public void springTest01() {// 定位到配置所需对象的xml文件ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 从Spring容器中获取配置好的Hello对象,getBean("hello")中的hello就是配置的bean的id值Hello hello = (Hello) context.getBean("hello"); hello.sayHello(); // 调用方法测试
}

这里额外说一点:Hello hello = (Hello) context.getBean("hello");这段代码等效于下面这段代码:

Hello hello = context.getBean("hello", Hello.class); 这只是getBean()方法的重载版本,至于用哪一种就要根据实际情况了;


通过这个例子来总结一下:

  1. Hello对象由Spring来自动创建,不需要我们管,我们只需要把想要创建的对象配置到xml文件就可以了;
  2. 创建好的Hello对象的初始化、赋值等一系列操作也是由Spring完成的,我们也只需要配置好就可以了;

三、依赖注入

依赖:bean对象的创建依赖于容器

注入:bean对象中的所有属性值由容器来注入

构造器注入(掌握):

这里还是引用上面的Hello的例子:

其实就是使用 <constructor-arg> 标签,这里就不过多展示了;

Set方式注入(掌握):

1,假设这里自己定义了两个类:Student类Adress类

Adress类:

public class Address {private String address;// 下面还要有一系列属性对应的set和get方法,这里就不写了
}

Student类:

public class Student {private String name;private Address address; private String[] books;private List<String> hobbies;private Set<String> games;private Map<String,String> card;private String wife;private Properties info;// 下面还要有一系列属性对应的set和get方法,这里就不写了
}

2,现在需要给对应的applicationContext.xml配置文件配置信息;

注意Student对象中各个类型属性的配置方式;

<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!--引用类型对象(一个Address对象)--><bean name="addressBean" class="com.yang.pojo.Address"><property name="address" value="北京"/></bean><!--一个Student对象--><bean name="student" class="com.yang.pojo.Student"><!--第一种:普通值注入,value--><property name="name" value="张三"/><!--第二种:引用类型注入,ref--><property name="address" ref="addressBean"/><!--第三种:数组类型注入--><property name="books"><array><value>三国演义</value><value>水浒传</value><value>西游记</value><value>红楼梦</value></array></property><!--第四种:list集合注入--><property name="hobbies"><list><value>篮球</value><value>足球</value><value>羽毛球</value><value>乒乓球</value></list></property><!--第五种:set集合注入--><property name="games"><set><value>LOL</value><value>CF</value><value>CS</value><value>BOB</value></set></property><!--第六种:map集合注入--><property name="card"><map><entry key="身份证" value="666666666"/><entry key="学生证" value="000000000"/><entry key="银行卡" value="111111111"/></map></property><!--第七种:空字符串注入--><property name="wife"><null/></property><!--第八种:properties类型注入--><property name="info"><props><prop key="driver">Driver</prop><prop key="url">mysql</prop><prop key="root">root</prop><prop key="password">020216</prop></props></property></bean></beans>

一共就这八种形式,没事就看看混个眼熟,其中有几个新标签,多看看知道怎么用就行,idea都有语法提示,一定要有个印象;

拓展方式注入(了解):

这个了解就行了,知道有这种方式,实际不会用;(可以跳过)

这里就不过多解释了,直接借用官方文档了:

XML Shortcut with the p-namespace

p-namespace就是给属性赋值,p就代表: <property> 标签的效果;

使用前需要在xml文件上面加上这句:

 xmlns:p="http://www.springframework.org/schema/p"

看看官方的配置:

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean name="classic" class="com.example.ExampleBean"><property name="email" value="someone@somewhere.com"/></bean><bean name="p-namespace" class="com.example.ExampleBean" p:email="someone@somewhere.com"/>
</beans>

p:email="someone@somewhere.com"这句就等同于

<property name="email" value="someone@somewhere.com"/>

并且还要注意p:email="someone@somewhere.com"这句代码是写在 <bean> 内的;

XML Shortcut with the c-namespace

c-namespace就是给有参构造器中的属性赋值了,等同 <constructor-arg> 标签的效果;

使用前需要在xml文件上面加上这句:

 xmlns:c="http://www.springframework.org/schema/c"

官方配置:

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:c="http://www.springframework.org/schema/c"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="beanTwo" class="x.y.ThingTwo"/><bean id="beanThree" class="x.y.ThingThree"/><!-- traditional declaration with optional argument names --><bean id="beanOne" class="x.y.ThingOne"><constructor-arg name="thingTwo" ref="beanTwo"/><constructor-arg name="thingThree" ref="beanThree"/><constructor-arg name="email" value="something@somewhere.com"/></bean><!-- c-namespace declaration with argument names --><bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"c:thingThree-ref="beanThree" c:email="something@somewhere.com"/></beans>

可以看到构造器中的形参名为:thingTwo、thingThree、email,

<constructor-arg name="thingTwo" ref="beanTwo"/>
<constructor-arg name="thingThree" ref="beanThree"/>
<constructor-arg name="email" value="something@somewhere.com"/><!--上面三句等同于下面的:-->
c:thingTwo-ref="beanTwo" c:thingThree-ref="beanThree" c:email="something@somewhere.com"
<!--当然这句代码也是放在bean标签中的-->

四、Spring的配置

Spring有一些配置,比如起别名等;这里只说一个比较重要的:

import导入其他xml配置文件

因为实际开发过程中是团队协作开发,每个人会负责一个模块,而每个人都有一个xml配置文件,不同的类注册在不同的xml文件中;最后将所有的模块合到一块,同样这些配置文件也需要合到一起,这就需要import把所有配置文件合成一个;

合并后的配置文件名:applicationContext.xml

合并六个配置文件到该文件:

applicationContext.xml

<!--在`applicationContext.xml`文件中导入这几个配置文件-->
<import resource="bean01.xml"/>
<import resource="bean02.xml"/>
<import resource="bean03.xml"/>
<import resource="bean04.xml"/>
<import resource="bean05.xml"/>
<import resource="bean06.xml"/>

五、Bean 的作用域(scopes)

官方给的有六个,这里就先说两个,分别是:singleton和prototype

Singleton Scope

这个就是单例模式,一个对象在Spring中创建后会使用多次;也是Spring创建对象默认的一种模式;

在这里插入图片描述

由图可以清楚的看到,一个对象实例只会被创建一次并在多个地方使用;

声明作用域只需要在bean标签中加上scope元素即可;

官方示例:

<bean id="accountService" class="com.something.DefaultAccountService"/><!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

因为单例模式是默认的,所以上下两个bean声明的都是一样的;

可以做个小测试:(还是上面使用的Hello样例)

这里我在bean中多加了scope元素,不加也是一样默认单例;

<bean id="hello" class="com.yang.service.Hello" scope="singleton"><constructor-arg name="str" value="Java"/><property name="name" value="Spring"/>
</bean>

java测试代码:

@Test
public void springTest01() {ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");Hello hello01 = (Hello) context.getBean("hello");Hello hello02 = (Hello) context.getBean("hello");System.out.println(hello01 == hello02);
}

因为是单例模式,所以hello01和hello02肯定是从Spring中取出同一个对象;

结果是true

Prototype Scope

这个是原型模式,每次从Spring中获取时,都会创建一个新的对象,如图:

在这里插入图片描述

官方示例:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

就是scope中写成prototype就行了;

下面还是hello小示例:

bean声明:

<bean id="hello" class="com.yang.service.Hello" scope="prototype"><constructor-arg name="str" value="Java"/><property name="name" value="Spring"/>
</bean>

java测试代码(和上面一样):

@Test
public void springTest01() {ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");Hello hello01 = (Hello) context.getBean("hello");Hello hello02 = (Hello) context.getBean("hello");System.out.println(hello01 == hello02);
}

结果和单例模式相反,输出值为false,这就说明虽然hello01和hello02都是通过一个id获取的对象,但是每次获取都创建了一个新的对象,就是原型模式的特点;

小总结

在实际中主要还是使用默认的情况,就是单例模式,因为使用原型模式每次获取都创建一个对象也很占内存;所以没有什么特殊情况就不要管作用域了;

六、引用类型自动注入

对于引用类型属性的注入,可不在配置文件中显示的注入;而是通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性);

根据自动注入判断标准的不同,可以分为两种:

  1. byName:根据名称自动注入
  2. byType: 根据类型自动注入

这个就简单说说,实际情况下不建议使用,可读性不高;

这里也就不过多阐述了,直接用网上找的资料了;

ByName自动注入

byName:会自动在容器上下文中查找和自己对象set方法形参值对应的bean id

在这里插入图片描述


ByType自动注入

byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean
在这里插入图片描述

小总结

这两个方法各有优缺点,但是实际情况下还是不建议使用,了解一下,看到别人项目如果有这个能认出来就行;

  • ByName:需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的形参值一致
  • ByType:需要保证所有bean的class唯一(如果有多个重复类型bean肯定不行),并且这个bean需要和自动注入的属性的类型一致

七、基于注解的 DI

Spring2.5开始支持注解,进一步简化了配置;;

需要在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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/></beans>

@Autowired实现byType 自动注入

在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配(ByType) Bean 的方式;

使用该注解完成属性注入时,类中不再需要些set方法,但是还是建议写上;

@Autowired可以写在属性上,也可以写在方法上;

示例:

Student类和Address类(set和get还是建议写上,这里就不写那么多了):

public class Address {private String address;
}
public class Student {@Autowiredprivate Address addressName;
}

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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/><bean id="student" class="com.yang.pojo.Student"/><bean id="address" class="com.yang.pojo.Address"><property name="address" value="郑州"/></bean></beans>

可以从配置文件中发现,Student类的实例student并没有给addressName属性赋值,但是在Student类中使用了@Autowired,就代表默认情况下addressName会被xml文件中Address类型的实例赋值,也就是xml文件中的address对象会自动赋给addressName;

当然@Autowired是按类型(ByType)自动装配的,上面也说过按类型自动装配的缺点:当有多个重复类型的bean就无法判断了,这时可以考率按id自动装配;

@Autowired 与@Qualifier实现byName 自动注入

如果按ByName的方式只需要联合使用注解@Autowired 与@Qualifier就可以了;

@Qualifier的value属性可以用来制定匹配的bean的id

示例:

还是上面的例子,这里就只把不同之处写出来了;

public class Student {@Autowired@Qualifier(value = "address")private Address addressName;
}

这样就可以指定唯一的bean对象注入了;

这里再提最后一点:

@Autowired还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行;若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null;

在这里插入图片描述
有个印象就行;

JDK 注解@Resource 自动注入

Spring提供了对 jdk中@Resource注解的支持;@Resource 注解既可以按名称匹配Bean, 也可以按类型匹配 Bean;默认是按名称(ByName)注入;使用该注解,要求 JDK 必须是 6 及以上版本; @Resource 可在属性上,也可在 set 方法上;

还是刚才的示例,修改一下:

public class Student {@Resourceprivate Address addressName;
}

如果这样写就是默认情况,那么就会从Spring容器中找到底有没有id为addressName的bean,从上面的applicationContext.xml文件中可以看到并没有id为addressName的bean,只有id为address的bean,所以这样就会报错;但是如果有对应id的bean,就可以自动注入成功;

那么没有对应id的bean怎么办?@Resource 也是有一个name属性可以指定id的

可以这样写:

public class Student {@Resource(name = "address")private Address addressName;
}

这样就可以注入成功了;

@Resource注解无法引入的问题

我在使用@Resource时遇到了无法引入的问题,查了一下资料发现JDK11以后移除了javax扩展,所以不能使用@Resource注解;

解决方法也很简单,直接加个对应的maven依赖就好了;

<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version>
</dependency>

注解@Value对简单数据类型注入

对于String、Integer等简单数据类型,可以使用注解@Value的value属性进行注入,这样更简单;

但是对于像list、Map等复杂的数据类型,最好还是使用property注入;

示例:

import org.springframework.beans.factory.annotation.Value;public class User {// 使用@Value进行注入@Value(value = "张三")private String name;
}

上面就等同于applicationContext.xml中的:

<property name="name" value="张三"/>

注解@Component定义bean

既然属性可以通过注解自动注入,那么有没有注解可以实现定义bean的功能呢?

注解@Component就可以实现定义bean的功能,这样再通过注解@Value的配合,在一些简单场合下就不再需要向配置文件写<bean>标签了;

@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean;


首先使用前需要先在applicationContext.xml中添加配置

<context:component-scan base-package="扫描的包路径"/>

这个就是在applicationContext.xml中配置了一个扫描器,它可以扫描对应包下的所有注解;

假设我有一个com.yang包,扫描它以下的注解:

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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--扫描com.yang包下的所有注解--><context:component-scan base-package="com.yang"/><context:annotation-config/>
</beans>

配置文件写好后,如果使用@Component注解,这样你就不用再在这个配置文件中配置了

看看@Component注解在代码中如何使用:

User.java

package com.yang.pojo;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component // 默认id值为user
public class User {@Value(value = "张三")private String name;@Value(value = "18")private Integer age;
}

标注上@Component注解后就意味着User就是一个组件类,所以Spring会自动创建一个User对象,这个对象的 id 默认是类名首字母小写:user

(如果不想使用默认id,@Component有一个value属性可以设置id值 @Component(value = "自定义id值")

所以上面的注解达到的效果 等同于applicationContext.xml文件中这样配置:

<bean id="user" class="com.yang.pojo.User"><property name="name" value="张三"/><property name="age" value="18"/>
</bean>

下面可以用测试代码测试一下:

@Test
public void annotationTest01() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");User user = context.getBean("user", User.class);System.out.println(user);
}

Spring同样提供了三个创建对象的注解:

  • @Repository 用于对 DAO 实现类进行注解 (数据访问层)

  • @Service 用于对 Service 实现类进行注解 (业务逻辑层)

  • @Controller 用于对 Controller 实现类进行注解(界面层)

这三个和@Component有什么区别呢?

其实在功能上没有一点区别用法作用一模一样,这一点一定要记住;

那为啥还要整这么多花样?通过名字就可以猜到,它们只是分别用在mvc三层不同的架构目的就是为了通过不同的名字区分开来

注解和XML配置的对比

注解优点是: 方便 、 直观 、高效(代码少,没有配置文件的书写那么复杂);

弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的;

XML 方式优点是: 配置和代码是分离的;在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。;

xml 的缺点是:编写麻烦,效率低,大型项目过于复杂;

拓展:使用纯Java程序进行配置

JavaConfig本来是Spring的一个子项目,但是在Spring4之后成为了核心功能;

这是一种使用Spring的新方式,完全使用注解来配置所有内容

但是实际情况下使用Spring开发并不会完全抛弃XML配置,一般是混合着使用,那么为什么在这提这一点呢?

因为在Springboot中这种方式是随处可见的,提前了解为了以后的Springboot打基础;

注解@Configuration

如果被@Configuration注解标注的类就代表这是一个配置类,那么这个类就等同于applicationContext.xml配置文件;

被@Configuration注解标注的类也会Spring容器托管,注册到容器中,因为它本来就是一个@Component,可以看一下它的源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.context.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {@AliasFor(annotation = Component.class)String value() default "";boolean proxyBeanMethods() default true;
}

示例演示:

下面示范一下如何使用;

1,在com.yang.pojo包下创建一个User类:

package com.yang.pojo;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.Objects;@Component
public class User {@Value(value = "张三")private String name;@Value(value = "10")private Integer age;// 下面重写的toString等方法就不放出来了
}

2,创建一个配置类MyConfig

package com.yang.config;import com.yang.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration // 声明该类为配置类
public class MyConfig {// 注册一个bean,就相当于一个bean标签// 方法的名字,就相当于bean标签中id属性// 方法的返回值,就相当于bean标签中的class属性@Bean // 给User类配置bean,id默认是方法名:getUserpublic User getUser() {return new User();}
}

一旦这个类添加了@Configuration注解那么就是配置类,XML配置文件有的它都可以通过注解实现

@Bean是用来注册一个bean的,因为目前了解不多,也只知道这种写法,可以模仿尝试一下;

@Bean有一个value属性,可以指定id值,这样就不用使用默认方法名了,value用法和其他注解都一样;

3,测试:

@Test
public void annotationTest01() {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);User user01 = applicationContext.getBean("getUser", User.class);System.out.println(user01 + " 哈希值:" + user01.hashCode());User user02 = applicationContext.getBean("user", User.class);System.out.println(user02 + " 哈希值:" + user02.hashCode());
}

这里和使用配置文件有一点不同:

配置文件在生成ApplicationContext对象时使用的是:ClassPathXmlApplicationContext("配置文件名")

而使用配置类生成ApplicationContext对象时使用的是:AnnotationConfigApplicationContext(配置类的Class对象)

获取bean对象还是一样的,都是通过getBean方法获取的;


在这里这种方法只是作为了解,在Springboot中才会应用到;

补充(关于@Bean和@Component):

@Bean和@Component都可以在spring容器中注入bean,那么有什么区别呢?

就比如上面的例子,可以发现通过@Bean和@Component两种方法注册了bean,所以在spring容器中实际存在两个User对象,下面测试中我也使用输出哈希值的方法证明了这一点(可以尝试一下,哈希值不同,前提是没有重写hashCode方法);

下面说一下区别:

@Component用于自动检测和使用类路径扫描自动配置bean,注释类和bean之间存在隐式的一对一映射(即每个类一个bean);这种方法对需要进行逻辑处理的控制非常有限,因为它纯粹是声明性的;

@Bean用于显式声明单个bean,而不是让Spring像上面那样自动执行它,它将bean的声明与类定义分离,并允许您精确地创建和配置bean;


如果想将第三方的类变成组件,又没有没有源代码,也就没办法使用@Component进行自动配置,这种时候使用@Bean就比较合适了(也可以通过xml方式来定义)

另外@Bean注解的方法返回值是对象,可以在方法中为对象设置属性;

八、AOP面向切面编程

基本概念

百度百科:

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性,同时提高了开发的效率

简单描述: AOP就是可以在不改变源代码的基础上,给核心代码部分增加新的功能;

底层原理:

AOP底层原理就是动态规划(这里不多介绍,但是如果想理解深入的话这个是必须了解的)

术语

连接点(join point)

类中哪些方法可以增强,那么这些方法就是连接点;

切入点(point cut)

类中实际增强的方法,称为切入点;

切入点表达式

切入点表达式在下面写代码时会用到,现在了解一下,看到代码知道是什么什么意思就行;

作用:表示对哪个类的中的哪个方法进行增强;

语法结构:

execution(权限修饰符 返回类型 类全路径.方法名称(参数列表))假设在com.yang.dao下有个User的类,想要增强类中的 public void add()方法
execution(public void com.yang.dao.User.add())但是这样只能指定一个类中的对应方法,如果想灵活使用,要学会通配符'*'的用法
其实 访问修饰符 可以省略,并且返回值使用通配符'*'
execution(* com.yang.dao.User.*(..))
参数列表中写的'..'表示有无参数均可,有参数时可以是任意类型
这句表达式意思就是:增强com.yang.dao.User类中的所有方法切入点表达式要会灵活运用,不清楚的话可以自己摸索尝试一下

通知(advice)

实际用来增强的逻辑部分(功能,一般是一个增强方法),称为通知;

通知有多种类型:

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 返回通知(最终通知)

切面(aspect)

共有功能的实现(日志切面、事务切面等),就是一个Java类,在配置中将其指定为切面;切面本质上是通知切入点的结合;

通知规定了增加什么功能什么时候增加;

切入点规定了在哪里增加功能;

引入(introduction)

引入允许我们向现有的类添加新方法或属性;

织入(weaving)

把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中;在目标对象的生命周期里有多个点可以进行织入:编译期,类加载期,运行期;


这几个概念中着重了解切入点、通知、切面,在代码操作中只有属性它们的概念才能知道每一步是在做什么;

AOP操作演示

不管用什么方法,首先得需要把AOP对应的jar包导入进去,对应的maven依赖:

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

applicationContext.xml文件中需要加上:

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop 
https://www.springframework.org/schema/aop/spring-aop.xsd

创建一个user包,创建一个UserService接口和对应实现类

UserService.java

package com.yang.user;public interface UserService {// 增void add();// 删void delete();// 改void update();// 查void select();
}

UserServiceImp.java

package com.yang.user;public class UserServiceImp implements UserService{@Overridepublic void add() {System.out.println("增加了一条数据");}@Overridepublic void delete() {System.out.println("删除了一条数据");}@Overridepublic void update() {System.out.println("更新了一条数据");}@Overridepublic void select() {System.out.println("查询了一条数据");}
}

下面的三种放法都是基于这两个类进行的操作;

第一种方法

增强类实现java内置接口;

增强部分(我感觉这就是通知):

在目标方法前增强部分,实现了MethodBeforeAdvice接口,这个接口的意思就是前置通知,实现before方法,该方法就是增强的内容,这个类就是一个切面

package com.yang.log;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class BeforeLog implements MethodBeforeAdvice {// target:目标对象(被代理对象)// args:目标方法的参数// method:目标方法@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println(target.getClass().getName() + " 的对象调用了:" + method.getName() + "方法");}
}

在目标方法后增强部分,实现了AfterReturningAdvice接口

package com.yang.log;import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class AfterLog implements AfterReturningAdvice {//returnValue: 返回值@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("执行了" + method.getName() + "方法,返回值为:" + returnValue);}
}

有了增强方法,下面就是织入过程了,这一步在配置文件中完成:

<!--目标类,其中的方法就是连接点-->
<bean id="userServiceImp" class="com.yang.user.UserServiceImp"/>
<!--增强类-->
<bean id="before" class="com.yang.log.BeforeLog"/>
<bean id="after" class="com.yang.log.AfterLog"/><!--第一种方法:实现内部接口进行切入-->
<aop:config><!--设置切入点(需要增强的方法)--><aop:pointcut id="pointcut" expression="execution(* com.yang.user.UserServiceImp.*(..))"/><!--增强作用(通知)配置到对应的切入点(方法)上--><!--环绕增强--><aop:advisor advice-ref="before" pointcut-ref="pointcut"/><aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
</aop:config>

这里设置的切入点就是UserServiceImp.java中的所有方法,所以对应的切入点表达式就是:execution(* com.yang.user.UserServiceImp.*(..))

注意一点:目标类一定要配置对应的bean,因为你要以它内部的一些方法为切入点,只有配置到spring中后,在<aop:pointcut />标签设置切入点的时候才能找到对应的方法;

测试代码:

@Test
public void springTest01() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 目前猜测这里创建的userService是个代理类,所以要声明成接口类型UserService userService = context.getBean("userServiceImp", UserService.class);userService.select(); // 代理类执行目标类方法
}

因为下一个方法也是使用这个测试方法,所以下面就不再写测试代码了;

输出结果:

com.yang.user.UserServiceImp 的对象调用了:select方法
查询了一条数据
执行了select方法,返回值为:null

这种方法优点就是可以通过实现内置类从而重写对应方法,而对应方法中的参数可以调用,这样就可以获取到对应的信息;

但是正因为需要实现内置方法,这也成为了它的缺点;

第二种方法

第二种方法和第一种的主要区别就在于:第一种方法增强部分是实现的接口,但是第二种方法使用的是自己定义的类中的方法作为切面;

自定义切面:

package com.yang.secondmethod;public class MyDefinition {// 该方法作为前置通知public void before() {System.out.println("=====before=====");}// 该方法作为后置通知public void after() {System.out.println("=====after=====");}
}

下面就是在xml文件中配置:

<!--第二种方法:使用自定义类进行切入-->
<bean id="definition" class="com.yang.secondmethod.MyDefinition"/>
<aop:config><!--自定义切面--><aop:aspect ref="definition"><!--切入点--><aop:pointcut id="pointcut" expression="execution(* com.yang.user.UserServiceImp.*(..))"/><!--增强作用(通知)配置到对应的切入点(方法)上--><aop:before method="before" pointcut-ref="pointcut"/><aop:after method="after" pointcut-ref="pointcut"/></aop:aspect>
</aop:config>

测试输出:

=====before=====
查询了一条数据
=====after=====

这种方法最大的好处就是你可以通过自定义切面实现自己想要实现的功能,拓展性更好,并且如果需要多种增强方式就不像第一种那样一种增强方法实现一个接口那样复杂了;

但是同样的第二种就是失去了第一种获取内部信息的渠道,所以还是要根据实际情况使用;一般情况下第二种用的比较多;

第三种方法

该方法就是使用注解来实现了;

当然可以注解和配置文件混合使用,但是在这里我就使用纯注解来做示范了;

配置类:

package com.yang.annotation;import com.yang.service.UserServiceImp;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration // 声明为配置类
@EnableAspectJAutoProxy // 开启AOP注解支持
@Aspect // 使用注解将该类声明为一个切面
public class MyAnnotation {// 注册bean@Bean(value = "userService")public UserServiceImp userServiceImp() {return new UserServiceImp();}@Before("execution(* com.yang.service.UserServiceImp.add(..))")public void before() {System.out.println("==========before==========");}@After("execution(* com.yang.service.UserServiceImp.delete(..))")public void after() {System.out.println("==========after==========");}@Around("execution(* com.yang.service.UserServiceImp.update(..))")public void around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("------环绕通知前------");joinPoint.proceed();System.out.println("------环绕通知后------");}
}

将该类声明为了配置类,并作为切面使用,内部定义了各种增强方法和通知;

注意区分我在切入点表达式种对不同方法不同通知类型

然后直接测试就行:

@Test
public void springTest01() {ApplicationContext context = new AnnotationConfigApplicationContext(MyAnnotation.class);UserService userService = context.getBean("userService", UserService.class);userService.add();System.out.println("\n");userService.delete();System.out.println("\n");userService.update();
}

输出结果:

==========before==========
增加了一条数据删除了一条数据
==========after==========------环绕通知前------
更新了一条数据
------环绕通知后------

现掌握这三种方法就行了,尤其是第二种,第一次接触很容易糊涂,各个概念理不清楚,所以首先一定要清楚那几个术语,再把它们带入到代码中去理解;

AOP还是需要在实际项目中能体会到它的神奇,这些小案例只会让人觉得很麻烦,所以学完ssm后一定要做一个小项目;

Mybatis-Spring整合

学习Spring框架肯定不是单独使用的,spring框架主要用于业务逻辑层,mybatis框架用于数据访问层,在一个项目中它们两者之间比然会有交集,那么如何将两者整合在一起使用呢?下面我就来演示一下整合步骤;

准备工作(mybatis回顾)

首先还是要先导入对应的jar包,这里使用maven加入相应的依赖即可:

因为spring对应依赖也比较多,所以我展示一些目前pom.xml文件的所有内容,需添加的会标注;

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.yang</groupId><artifactId>spring-learning</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>16</maven.compiler.source><maven.compiler.target>16</maven.compiler.target></properties><dependencies><!--下面是使用spring需要导入的依赖--><!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.15</version></dependency><!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version></dependency><!--下面是使用mybatis额外添加的依赖--><!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.6</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.14</version></dependency><!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency></dependencies><build><resources><resource><!--所在的目录--><directory>src/main/java</directory><!--包括目录下的.properties,.xml 文件都会扫描到--><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources></build>
</project>

导入对应的jar包后,还需要一个数据库表来进行操作,还是使用之前学习mybatis的表:
在这里插入图片描述
接下来还是mybatis的几个固定步骤:

创建一个student类,属性名对应表的字段名,并提供对应的set、get等方法;

student.java

package com.yang.pojo;import java.util.Objects;public class Student {private int id;private String name;private String email;private int age;public Student() {}public Student(int id, String name, String email, int age) {this.id = id;this.name = name;this.email = email;this.age = age;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +", email='" + email + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (!(o instanceof Student)) return false;Student student = (Student) o;return id == student.id && age == student.age && Objects.equals(name, student.name) && Objects.equals(email, student.email);}@Overridepublic int hashCode() {return Objects.hash(id, name, email, age);}
}

这里就以一个简单的查询为例,同样需要一个StudentDao接口和一个对应的mapper文件:StudentDao.xml

StudentDao.java

package com.yang.dao;import com.yang.pojo.Student;import java.util.List;public interface StudentDao {// 查询方法List<Student> selectStudent();
}

StudentDao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yang.dao.StudentDao"><select id="selectStudent" resultType="com.yang.pojo.Student">select * from student</select>
</mapper>

接下来就是最后一步了,配置主配置文件config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--    <properties resource="jdbc.properties"/>--><!--settings:控制mybatis全局行为--><settings><!--设置mybatis输出日志--><setting name="logImpl" value="STDOUT_LOGGING"/></settings>
<!--    <environments default="test">-->
<!--        <environment id="test">-->
<!--            <transactionManager type="JDBC"/>-->
<!--            <dataSource type="POOLED">-->
<!--                &lt;!&ndash;数据库的驱动类名&ndash;&gt;-->
<!--                <property name="driver" value="${jdbc.driver}"/>-->
<!--                &lt;!&ndash;连接数据库的url字符串&ndash;&gt;-->
<!--                <property name="url" value="${jdbc.url}"/>-->
<!--                &lt;!&ndash;访问数据库的用户名&ndash;&gt;-->
<!--                <property name="username" value="${jdbc.username}"/>-->
<!--                &lt;!&ndash;密码&ndash;&gt;-->
<!--                <property name="password" value="${jdbc.password}"/>-->
<!--            </dataSource>-->
<!--        </environment>-->
<!--    </environments>--><!-- sql mapper(sql映射文件)的位置-->
<!--    <mappers>-->
<!--        <mapper resource="com/yang/dao/StudentDao.xml"/>-->
<!--    </mappers>-->
</configuration>

对应的jdbc.properties文件为:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_learning?serverTimezone=UTC
jdbc.username=root
jdbc.password=020216

可能会发现在我的config配置文件中好多部分注释掉了,为什么呢?因为接下来我将使用spring来配置这部分内容,这样就可以对比来看我提取了什么东西;

注:这里提前说一下:其实这个config配置文件完全没有存在必要的,因为spring完全可以配置完所有东西;但是为了体现用到了mybatis,可以加上该文件,只在该文件中声明别名和设置相关内容;

实现代码

第一点不同就是需要对StudentDao接口有一个额外的实现,虽然看起来麻烦,但是最后测试实际使用却可以简单很多;

StudentDaoImp.java

package com.yang.dao;import com.yang.pojo.Student;
import org.mybatis.spring.SqlSessionTemplate;import java.util.List;public class StudentDaoImp implements StudentDao{private SqlSessionTemplate session;// 这个session将会在spring配置文件中配置,所以提供一个set方法public void setSession(SqlSessionTemplate session) {this.session = session;}@Overridepublic List<Student> selectStudent() {// 直接调用sessionStudentDao studentDao = session.getMapper(StudentDao.class);return studentDao.selectStudent();}
}

配置spring对应的配置文件:

spring-mybatis.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"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><context:annotation-config/><!--读取jdbc.properties配置文件的支持--><context:property-placeholder location="classpath:jdbc.properties"/><!--DataSource:使用Spring数据源替换Mybatis的配置--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><!--SqlSessionFactory--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!--载入数据源dataSource--><property name="dataSource" ref="dataSource"/><!--绑定mybatis的config主配置文件--><property name="configLocation" value="classpath:mybatis.xml"/><!--绑定mybatis的mapper配置文件--><property name="mapperLocations" value="classpath:com/yang/dao/*.xml"/></bean><!--如果使用SqlSessionDaoSupport那么就不需要这一步了--><!--SqlSessionTemplate--><bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"><!--使用构造器注入sqlSessionFactory,因为SqlSessionTemplate没有set方法--><constructor-arg ref="sqlSessionFactory"/></bean><!--StudentDaoImp的bean的配置--><bean id="studentDaoImp" class="com.yang.dao.StudentDaoImp"><!--直接调用上面配置的SqlSessionTemplate就可以了--><property name="session" ref="sqlSessionTemplate"/></bean>
</beans>

我来解释一下该配置文件:

  • 第一部分:获取数据源DataSource,这就代替了mybatis中config主配置文件中的<environments>标签,这个使用的class是java内置的DriverManagerDataSource类,直接调用就行,前提导入相应的jar包;属性就是那几个,没什么好介绍的;

  • 第二部分:获取SqlSessionFactory对象,在执行mybatis项目中,我们就需要SqlSessionFactory对象用来创建SqlSession对象,一般我们会把它写在一个工具类中,在这里只需要配置相应的bean即可;

    它的属性第一个是数据源,直接使用上面声明过的即可;第二个是和mybatis的主配置文件绑定,在mybatis中是通过java代码绑定的:

    String config = "mybatis.xml";
    InputStream in = Resources.getResourceAsStream(config);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    

    所以在这里一定要对比联想学习,不然会越来越乱;

    第三个参数是mapper文件,在mybatis中mapper文件就是配置到主配置文件中的;这里建议直接绑定一个包下的mapper文件,因为属性标签不能有重复,如果有多个mapper文件就不能像在mybatis中的主配置文件中使用<mapper>标签来配置多个;

  • 第三部分:获取SqlSessionTemplate对象,这个是什么呢?其实就是和SqlSession对象一样,只不过在spring中变成了这样,所以还是按照SqlSession对象使用即可;

    这里用到了构造器注入,可以看一下SqlSessionTemplate的源码,会发现其中没有set方法,所以只能使用构造器注入;

  • 第四部分:配置StudentDaoImp的bean,这个就没什么说的了,配置到spring中最后测试时直接获取对应的bean对象调用方法即可;

测试代码:

@Test
public void selectStudent02() {ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml");// 创建代理类StudentDao studentDao = context.getBean("studentDaoImp", StudentDao.class);List<Student> students = studentDao.selectStudent();students.forEach(stu-> System.out.println(stu));
}

这就是一个完整的整合步骤,看起来很麻烦(确实有点…),但是在实际开发项目中已经节省了很多步骤了;

补充:不写dao接口实现类方式

上面说可以不用写dao接口实现类也可以注册dao接口的bean,其实很简单,只需要在mybatis的主配置文件config.xml中加入以下代码即可;

<!--新操作:配置dao接口扫描包,动态实现Dao接口自动注入到Spring中之前都是:<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"><constructor-arg ref="sqlSessionFactory"/></bean>对应接口的实现类<bean id="BooksDaoImp" class="com.yang.dao.BooksDaoImp"><property name="session" ref="sqlSessionTemplate"/></bean>需要在dao接口额外写实现类,因为接口无法注入到Spring中,只能通过额外的实现类注入,但是下面的操作就可以实现不用写实现类就可以自动注入--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!--这里为什么要用value呢?因为该属性name注入的参数是一个String类型,可以点开sqlSessionFactoryBeanName看看源码的参数--><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/><!--要扫描的dao包,会把dao包下的所有mapper.xml文件扫描到,所以上面就不用写了--><property name="basePackage" value="com.yang.dao"/></bean>

这样不仅不需要写实现类,而且也不用关心dao接口的注入问题,所有事情都由MapperScannerConfigurer完成了;

Spring中事务管理

事务是在数据库接触的一个概念,对于数据库修改的操作都需要考虑到事务的处理,要么都成功,要么都失败;事物的处理在开发中至关重要!

同样如果在spring中定义的一个方法中会同时执行两个操作:先删除后增加,如果没有事务处理,那么假设删除操作执行成功后,增加操作由于sql语句错误没有执行,这样造成的后果就好像方法只执行了一半;这样是万万不可的;

可以想象如果去银行取钱,取完钱后余额没有减少,那么银行不久亏死;

说这么多就是希望以后写有关数据库修改操作的代码时一定要考虑到事务的管理;

实际操作

编程式事务管理

将事务管理代码嵌到业务方法中来控制事务的提交和回滚

缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理

一般情况下比编程式事务好用

将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理

事务管理作为横切关注点,通过aop方法模块化,Spring中通过Spring AOP框架支持声明式事务管理

我们可以通过AOP很轻松实现事务的织入;

下面就演示一下具体操作;

还是上面student表,这里只是演示一下增加事务的地方;

首先这里的StudentDaoImp.java中selectStudent方法有些不同:

@Override
public List<Student> selectStudent() {// 直接调用sessionStudentDao studentDao = session.getMapper(StudentDao.class);studentDao.insertStudent(new Student(1013, "小二", "xiaoer@123.com", 12));studentDao.deleteStudent(1012);return studentDao.selectStudent();
}

在这里我查询方法中需要先执行insertStudent方法插入学生后再执行deleteStudent方法删除学生,为了保证这两个方法要么都成功,要么都失败,就需要事务管理;

spring-mybatis.xml

在该配置文件中加入头文件的约束:

xmlns:tx="http://www.springframework.org/schema/tx"http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置声明事务-->
<!--JDBC事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean><!--使用AOP实现事务织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager"><!--给哪些方法配置事务--><!--配置事务的传播特性--><!--method中的name是对应sql语句的方法名--><tx:attributes><tx:method name="insertStudent" propagation="REQUIRED"/><tx:method name="deleteStudent" propagation="REQUIRED"/><tx:method name="selectStudent" propagation="REQUIRED"/><!--下面这一行可以涵盖所有情况--><!--            <tx:method name="*" propagation="REQUIRED"/>--></tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config><!--dao包下所有类的所有方法都作为切入点--><aop:pointcut id="txPointcut" expression="execution(* com.yang.dao.*.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

首先就是配置一个DataSourceTransactionManager的bean;

然后通过AOP织入事务处理;

spring事务传播特性:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择;

  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行;

  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常;

  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起;

  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起;

  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常;

  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作;

    Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况;

再次强调为什么需要事务

  • 如果不配置事务,可能存在数据提交不一致的情况;
  • 如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务;
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,数据的处理是需要万分谨慎的;

若有新知识点会持续补充。。。
参考:Spring官方文档

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 150道大厂软件测试常见面试题,认真思考对你面试很有价值

    软件测试基础 什么是软件测试?软件测试的目的?软件测试的目标测试环境如何部署&#xff1f;软件测试的原则测试的工作流程测试工程师的职责软件有多少种分类?软件的分类测试的主要方面软件测试的对象什么是“测试案例"?怎么编写案例?软件测试的两种方法测试结束的标…...

    2024/4/27 17:03:06
  2. WebGPU 中的缓冲映射机制

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 1. 什么是缓冲映射 就不给定义了&#xff0c;直接简单的说&#xff0c;映射&#xff08;Mapping&#xff09;后的某块显存…...

    2024/4/27 18:31:21
  3. 社交网络分析的 R 基础:(五)图的导入与简单分析

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 如何将存储在磁盘上的邻接矩阵输入到 R 程序中&#xff0c;是进行社交网络分析的起点。在前面的章节中已经介绍了基本的数据…...

    2024/4/27 19:24:18
  4. .netcore基础知识(一)

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 先来说说web服务器 先来一张图 一个典型的进程外托管模型 我们先看kestrel这一部分 我们在它前面放了一个方向代理服务器n…...

    2024/4/27 19:12:16
  5. 使用Hot Chocolate和.NET 6构建GraphQL应用(8) —— 实现Mutate添加数据

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在讨论完GraphQL中的查询需求后&#xff0c;这篇文章…...

    2024/4/27 14:22:25
  6. 数据结构 - 链表

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 简介 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CiSlM7zl-1644617304947)(https://image.…...

    2024/4/27 19:34:46
  7. C++实现对Json数据的友好处理

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 背景 C/C客户端需要接收和发送JSON格式的数据到后端以实现通讯和数据交互。C没有现成的处理JSON格式数据的接口&#xff0…...

    2024/4/27 17:08:10
  8. IM系统的简单介绍

    作为一个互联网时代的公司无论你的产品是什么&#xff0c;你都需要一个网站还是app亦或是一个pc软件作为自己的网络平台&#xff0c;而在网络平台当中&#xff0c;即时收集用户反馈、与用户进行互动、方便用户之间互动提高产品活跃度只要存在这些需求&#xff0c;那么你的产品都…...

    2024/4/27 14:17:27
  9. SAP IDoc应用介绍及示例--配置IDoc应用示例之测试数据

    测试过程如下&#xff1a; 第一步 在发送方创建物料 T-code&#xff1a;MMF1 我把物料起名为demomat&#xff0c;选择了Mechanical Engineering&#xff0c;点选择视图 选择最基本的视图 维护基本属性&#xff0c;保存 此时登录到810用T-code&#xff1a;MM03来查找这个物料…...

    2024/4/27 14:12:44
  10. python操作mongodb数据库-修改集合数据

    修改集合中一条数据 使用updata_one修改集合中一条数据 import pymongomyclient pymongo.MongoClient("mongodb://localhost:27017/") # 连接数据库 mydb myclient["test"] # 指定数据库 mycol mydb["sites"] # 指定集合…...

    2024/4/27 17:26:58
  11. 客快物流大数据项目(三十五):CDH使用注意

    目录 CDH使用注意 一、HDFS使用前配置 二、​​​​​​​Kafka使用前配置 CDH使用注意 一、​​​​​​​HDFS使用前配置 默认情况下&#xff0c;CDH的HDFS服务是开启权限检查的&#xff0c;为方便使用HDFS&#xff0c;需先将HDFS的dfs.permissions选项取消选中&#xf…...

    2024/4/13 6:15:59
  12. Linux服务器开发,网络io与select,poll,epoll

    推荐一个零声学院免费公开课程&#xff0c;个人觉得老师讲得不错&#xff0c;分享给大家&#xff1a;Linux&#xff0c;Nginx&#xff0c;ZeroMQ&#xff0c;MySQL&#xff0c;Redis&#xff0c;fastdfs&#xff0c;MongoDB&#xff0c;ZK&#xff0c;流媒体&#xff0c;CDN&am…...

    2024/4/13 6:15:59
  13. 《OpenSSL3.0学习之十八 提供程序-signature|CSDN创作打卡》

    &#x1f4d2;博客首页&#xff1a;男星的博客 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ❤️ 期待一起交流&#xff01; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff0c;求告知&#xff0c;多谢&#xff01; &#x…...

    2024/4/20 8:50:33
  14. Golang/云原生/Docker/DevOps/K8s,GO语言基础3知识,接口与发射

    推荐一个零声学院免费公开课程&#xff0c;个人觉得老师讲得不错&#xff0c;分享给大家&#xff1a;Linux&#xff0c;Nginx&#xff0c;ZeroMQ&#xff0c;MySQL&#xff0c;Redis&#xff0c;fastdfs&#xff0c;MongoDB&#xff0c;ZK&#xff0c;流媒体&#xff0c;CDN&am…...

    2024/4/13 6:16:09
  15. Linux实用细节命令5,入门精选

    推荐一个零声学院免费公开课程&#xff0c;个人觉得老师讲得不错&#xff0c;分享给大家&#xff1a;Linux&#xff0c;Nginx&#xff0c;ZeroMQ&#xff0c;MySQL&#xff0c;Redis&#xff0c;fastdfs&#xff0c;MongoDB&#xff0c;ZK&#xff0c;流媒体&#xff0c;CDN&am…...

    2024/4/19 13:48:41
  16. Java数据结构---逆波兰计算器代码实现

    即可就不说逆波兰的实现了,具体实现过程直接看《大话数据结构》即可 package algorithm.DataStruct.Stack;import java.util.*;/*** author: Serendipity* Date: 2022/1/28 18:50* 以下是几个关键点的总结&#xff1a;* 1.使用 Map 作为运算符的优先级集合&#xff1b;* 2.栈…...

    2024/4/19 22:37:16
  17. CSS学习07之字体样式

    回顾 属性选择器为带有属性的 HTML 元素设置样。 顾名思义可以设置带有特定属性或属性值的 HTML 元素的样式。 使用方式属性选择器&#xff1a;元素类型[属性名属性值&#xff08;正则&#xff09;] 字体样式和span标签&#xff08;追加&#xff09; 文字设置设置文字的风格…...

    2024/4/5 2:07:52
  18. leetcode——【前导0和二进制1个数】

    前导0和二进制1个数 int length(int num) {int ret 0;if (num >> 16 0) {ret 16;num << 16;}if (num >> 24 0) {ret 8;num << 8;}if (num >> 28 0) {ret 4;num << 4;}if (num >> 30 0) {ret 2;num << 2;}if (num >…...

    2024/4/20 2:22:51
  19. SQL36 创建一个actor_name表

    文章目录1. 题目2. 题解1. 题目 题目来源&#xff1a;SQL36 创建一个actor_name表 题目描述 创建一个actor_name表&#xff0c;并且将actor表中的所有first_name以及last_name导入该表. 表格 actor_id first_name last_name last_update 1 PENELOPE GUINESS 2006-…...

    2024/4/19 21:06:07
  20. 线段树题目

    链接&#xff1a;https://www.acwing.com/problem/content/description/247/ 246. 区间最大公约数 神仙题。 区间加数&#xff0c;求区间gcd。n,q<500000 #include<bits/stdc.h> using namespace std; typedef long long LL; const int N500005; struct node{int l…...

    2024/4/19 10:19:55

最新文章

  1. Java --- equals()方法重写

    引言&#xff1a;这个equals()方法重写在我这篇博客已经提到了&#xff0c;这里单独拿出来便于观看。 Java --- 字符串&#xff08;就这一篇带你学习字符串的绝大多数内容&#xff09;-CSDN博客 equals()&#xff1a;用这种方法比较两个用String类定义的对象&#xff0c;如果两…...

    2024/4/27 20:00:12
  2. 梯度消失和梯度爆炸的一些处理方法

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

    2024/3/20 10:50:27
  3. 从头开发一个RISC-V的操作系统(二)RISC-V 指令集架构介绍

    文章目录 前提ISA的基本介绍ISA是什么CISC vs RISCISA的宽度 RISC-V指令集RISC-V ISA的命名规范模块化的ISA通用寄存器Hart特权级别内存管理与保护异常和中断 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提…...

    2024/4/24 5:34:22
  4. Django实现的登录注册功能

    1 前言 在Web开发中&#xff0c;用户登录和注册是最基本且必不可少的功能。Django&#xff0c;作为一个高级的Python Web框架&#xff0c;为我们提供了强大的工具和库来快速实现这些功能。下面&#xff0c;我将详细介绍如何使用Django来实现用户登录和注册功能。 2 功能介绍 …...

    2024/4/23 6:25:06
  5. 【外汇早评】美通胀数据走低,美元调整

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

    2024/4/26 18:09:39
  6. 【原油贵金属周评】原油多头拥挤,价格调整

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

    2024/4/26 20:12:18
  7. 【外汇周评】靓丽非农不及疲软通胀影响

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

    2024/4/26 23:05:52
  8. 【原油贵金属早评】库存继续增加,油价收跌

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

    2024/4/27 4:00:35
  9. 【外汇早评】日本央行会议纪要不改日元强势

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

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

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

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

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

    2024/4/26 21:56:58
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

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

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

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

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

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

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

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

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

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

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

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

    2024/4/26 22:01:59
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

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

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

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

    2024/4/26 23:04:58
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

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

    2024/4/25 2:10:52
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

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

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

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

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

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

    2024/4/27 11:43:08
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

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

    2024/4/27 8:32:30
  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