全面理解Spring6框架到熟悉与掌握

个人笔记梳理,仅供参考

Spring是一款主流的JavaEE轻量级开源框架

Spring的狭义和广义

广义的Spring:Spring技术栈

泛指以Spring Framework为核心的Spring技术栈

经过十多年的发展,Spring已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、Spring Mvc、SpringBoot、spring Cloud、spring Data、springSecurity 等,其中 Spring Framework 是其他子项目的基础。
这些子项目涵盖了从企业级应用开发到云计算等各方面的内容,能够帮助开发人员解决软件发展过程中不断产生的各种实际问题,给开发人员带来了更好的开发体验。


狭义的 Spring:spring Framework


狭义的 Spring 特指 Spring Framework,通常我们将它称为 Spring 框架。
Spring 框架是一个分层的、面向切面的Java 应用程序的一站式轻量级解决方案,它是 Spring 技术栈的核心和基础,是为了解决企业级应用开发的复杂性而创建的。
Spring 有两个最核心模块: loC 和 AOP.
loC:Inverse of control 的简写,译为“控制反转”,指把创建对象过程交给 Spring 进行管理

AOP:Aspect Oriented Programming 的简写,译为"面向切面编程"。AOP 用来封装多个类的公共行为,将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP 还解决一些系统层面上的问题,比如日志、事务、权限等。 

Spring Framework特点


非侵入式:使用 Spring Framework 开发应用程序时,Spring对应用程序本身的结构影响非常小。对领域模型可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序时结构清晰、简洁优雅。
控制反转:10C--Inversion ofControl,翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
面向切面编程:AOP--Aspect Oriented Programming,在不修改源代码的基础上增强代码功能。容器:Spring 10℃是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率。
组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML 和Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统

一站式:在 I0C和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且 Spring,旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在 Spring Framework 的基础上全部使用 Spring 来实现

引入spring-first的pom.xml文件依赖

<dependencies>
    <dependency>
<!--        引入springcontext依赖-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.0.2</version>
    </dependency>
<!--    junit-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.6.3</version>
    </dependency>
</dependencies>

建类定义方法->按照spring规范建立xml文件,配置对象-->测试文件

入门案例:

User.java

package com.zzq;

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

    public static void main(String[] args) {
        User user=new  User();
        user.add();

    }
}

bean.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">
<!-- 完成user对象创建
bean标签
id属性:唯一标识
class属性:要创建对象所在类的全路径(包名称+类名称)
-->
    <bean id="user" class="com.zzq.User"></bean>
</beans>

TestUser.java

package com.zzq;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUser {
    @Test
    public void  testUserObject(){
        //加载spring配置文件,对象创建
        ApplicationContext context=
                new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象
        User user=(User)context.getBean("user");
        System.out.println(user);
        //使用对象调用方法进行测试
        user.add();
    }
}

无参数构造执行

使用反射根据类全路径创建对象

package com.zzq;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.InvocationTargetException;

public class TestUser {
    @Test
    public void  testUserObject(){
        //加载spring配置文件,对象创建
        ApplicationContext context=
                new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象
        User user=(User)context.getBean("user");
        System.out.println(user);
        //使用对象调用方法进行测试
        user.add();
    }
    //反射创建对象
    @Test
    public void  testUserObject1() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取类Class对象
        Class clazz=Class.forName("com.zzq.User");
        //调用方法创建对象
        //Object o=clazz.newInstancce();
        User user=(User)clazz.getDeclaredConstructor().newInstance();
        System.out.println(user);
    }
}

Log4j2日志

引入Log4j2依赖

 <!-- 引入log4j2的依赖-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.19.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j2-impl</artifactId>
        <version>2.19.0</version>
    </dependency>

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数(最小是5秒钟)-->
<configuration monitorInterval="5" status="warn">
    <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->


    <!--变量配置-->
    <Properties>
        <!-- 格式化输出:%date表示日期(可缩写成%d,后同),%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
        <!-- %logger{36} 表示 Logger 名字最长36个字符 -->
        <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %highlight{%-5level} [%t] %highlight{%c{1.}.%M(%L)}: %msg%n" />
        <!-- 定义日志存储的路径 -->
        <property name="FILE_PATH" value="log" />
        <!--<property name="FILE_NAME" value="myProject" />-->
    </Properties>

    <!--此节点有三种常见的子节点:Console,RollingFile,File-->
    <appenders>

        <!--console节点用来定义输出到控制台的Appender-->
        <!--target:SYSTEM_OUT或SYSTEM_ERR,一般只设置默认:SYSTEM_OUT-->
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式,默认为:%m%n,即只输出日志和换行-->
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <!--阈值过滤器,控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>

        <!--        &lt;!&ndash;文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用&ndash;&gt;-->
        <!--        <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">-->
        <!--            <PatternLayout pattern="${LOG_PATTERN}"/>-->
        <!--        </File>-->


        <!-- 这个会打印出所有的debug及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileDebug" fileName="${FILE_PATH}/debug.log" filePattern="${FILE_PATH}/debug/DEBUG-%d{yyyy-MM-dd}_%i.log.gz">
            <!--阈值过滤器,控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <!--如果配置的是“%d{yyyy-MM}”,滚动时间单位就是月。“%d{yyyy-MM-dd}”,滚动时间单位就是天-->
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <!--指定滚动日志的策略,就是指定新建日志文件的时机-->
            <Policies>
                <!--interval属性用来指定多久滚动一次,时间单位取决于<PatternLayout pattern>,modulate属性调整时间,true:0点为基准滚动,false:服务器启动时间开始滚动-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy size="100MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15">
                <!--删除15天之前的日志-->
                <Delete basePath="${FILE_PATH}" maxDepth="2">
                    <IfFileName glob="*/*.log.gz" />
                    <IfLastModified age="360H" />
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>


        <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/info/INFO-%d{yyyy-MM-dd}_%i.log.gz">
            <!--阈值过滤器,控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,时间单位取决于<PatternLayout pattern>,modulate属性调整时间,true:0点为基准滚动,false:服务器启动时间开始滚动-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy size="100MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>


        <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/error/ERROR-%d{yyyy-MM-dd}_%i.log.gz">
            <!--阈值过滤器,控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,时间单位取决于<PatternLayout pattern>,modulate属性调整时间,true:0点为基准滚动,false:服务器启动时间开始滚动-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy size="100MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>
        <!--启用异步日志,阻塞队列最大容量为20000,超出队列容量时是否等待日志输出,不等待将直接将日志丢弃-->
        <Async name="Async" bufferSize="20000" blocking="true">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFileDebug"/>
            <AppenderRef ref="RollingFileInfo"/>
            <AppenderRef ref="RollingFileError"/>
        </Async>
    </appenders>


    <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
    <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.mybatis" level="info" additivity="false">
            <AppenderRef ref="Async"/>
        </logger>
        <!--监控系统信息-->
        <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
        <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Async"/>
        </Logger>
        <!--root 节点用来指定项目的根日志,level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.-->
        <root level="debug">
            <AppenderRef ref="Async" />
        </root>
    </loggers>


</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <loggers>
        <!--
            level指定日志级别,从低到高的优先级:
                TRACE < DEBUG < INFO < WARN < ERROR < FATAL
                trace:追踪,是最低的日志级别,相当于追踪程序的执行
                debug:调试,一般在开发中,都将其设置为最低的日志级别
                info:信息,输出重要的信息,使用较多
                warn:警告,输出警告的信息
                error:错误,输出错误信息
                fatal:严重错误
        -->
        <root level="DEBUG">
            <appender-ref ref="spring6log"/>
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="log"/>
        </root>
        <logger name="TestUser" additivity="false">
            <appender-ref ref="CONSOLE" />
        </logger>
    </loggers>

    <appenders>
        <!--输出日志信息到控制台-->
        <console name="spring6log" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
        </console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
        <File name="log" fileName="d:/spring6_log/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>

        <!-- 这个会打印出所有的信息,
            每次大小超过size,
            则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,
            作为存档-->
        <RollingFile name="RollingFile" fileName="d:/spring6_log/app.log"
                     filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <SizeBasedTriggeringPolicy size="50MB"/>
            <!-- DefaultRolloverStrategy属性如不设置,
            则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
    </appenders>
</configuration>

手动写日志

package com.zzq;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.InvocationTargetException;

public class TestUser {
    //创建Logger对象
    private Logger logger= LoggerFactory.getLogger(TestUser.class);


    @Test
    public void  testUserObject(){
        //加载spring配置文件,对象创建
        ApplicationContext context=
                new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象
        User user=(User)context.getBean("user");
        System.out.println(user);
        //使用对象调用方法进行测试
        user.add();
     //手动写日志
        logger.info("执行调用成功了");


    }
    //反射创建对象
    @Test
    public void  testUserObject1() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取类Class对象
        Class clazz=Class.forName("com.zzq.User");
        //调用方法创建对象
        //Object o=clazz.newInstancce();
        User user=(User)clazz.getDeclaredConstructor().newInstance();
        System.out.println(user);
    }
}

容器:IOC

控制反转(Ioc)

                                                                

IoC容器在Spring的实现

Spring 的 IoC 容器就是 IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建IoC 容器。Spring 提供了IoC 容器的两种实现方式:

**①BeanFactory**

这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

**②ApplicationContext**

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

**③ApplicationContext的主要实现类**

获取bean的三种方式:

package com.zzq.spring6.iocxml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUser {
    public static void main(String[] args) {
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean.xml");
        //根据id获取bean
        User user1=(User) context.getBean("user1");
        System.out.println(""+user1);
        //根据类型获取bean
        User user2=context.getBean(User.class);
        System.out.println(""+user2);
        //根据id和类型获取bean
        User user3=context.getBean("user",User.class);
        System.out.println(""+user3);
    }
}

如果组件类实现了接口,根据接口类型可以获取 bean 吗?

> 可以,前提是bean唯一

如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?

> 不行,因为bean不唯一
依赖注入:

1、类有属性,创建对象过程中,向属性设置值

第一种方式:基于set方法完成

package com.zzq.spring6.iocxml.di;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestBook {
    @Test
    public void testSetter(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean-di.xml");
        Book book = context.getBean("book", Book.class);
        System.out.println(book);
    }
}

book.java

package com.zzq.spring6.iocxml.di;

public class Book {
    private String bname;
    private  String author;

    @Override
    public String toString() {
        return "Book{" +
                "bname='" + bname + '\'' +
                ", author='" + author + '\'' +
                '}';
    }

    public String getBname() {
        return bname;
    }

    public void setBname(String bname) {
        this.bname = bname;
    }

    public String getAuthor() {
        return author;
    }

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

    public Book(String bname, String author) {
        this.bname = bname;
        this.author = author;
    }

    public Book() {
    }
}

bean-di.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="book" class="com.zzq.spring6.iocxml.di.Book">
    <property name="author" value="后端"></property>
    <property name="bname" value="java"></property>
</bean>
</beans>

第二种方式:基于构造器完成

<?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">
<!--1、set方法注入-->
    <bean id="book" class="com.zzq.spring6.iocxml.di.Book">
        <property name="author" value="后端"></property>
        <property name="bname" value="java"></property>
    </bean>
    <!--    2、构造器注入-->
    <bean id="bookCon" class="com.zzq.spring6.iocxml.di.Book">
        <constructor-arg name="author" value="java开发"></constructor-arg>
        <constructor-arg name="bname" value="spring"></constructor-arg>
    </bean>
</beans>
package com.zzq.spring6.iocxml.di;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestBook {
  
    @Test
    public void testConstructor(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean-di.xml");
        Book book = context.getBean("bookCon", Book.class);
        System.out.println(book);
    }
}

特殊值处理

##### ①字面量赋值

> 什么是字面量?
>
> int a = 10;
>
> 声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际上拿到的值是10。
>
> 而如果a是带引号的:'a',那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。

```xml
<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>
```

##### ②null值

```xml
<property name="name">
    <null />
</property>
```

> 注意:
>
> ```xml
> <property name="name" value="null"></property>
> ```
>
> 以上写法,为name所赋的值是字符串null

##### ③xml实体

```xml
<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 解决方案一:使用XML实体来代替 -->
<property name="expression" value="a &lt; b"/>
```

##### ④CDATA节

```xml
<property name="expression">
    <!-- 解决方案二:使用CDATA节 -->
    <!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
    <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
    <!-- 所以CDATA节中写什么符号都随意 -->
    <value><![CDATA[a < b]]></value>
</property>
```


为对象类型属性赋值:

第一种方式:

引入外部bean

1、创建两个类对象:dept和emp

2、在emp的bean标签里面,使用property引入dept的bean

package com.zzq.spring6.iocxml.ditest;
//部门类
public class Dept {

    private  String dname;

    public void setDname(String dname) {
        this.dname = dname;
    }

    public  void info(){
        System.out.println("部门名称:"+dname);
    }
}
package com.zzq.spring6.iocxml.ditest;
//员工类
public class Emp {
    //对象类型属性:员工属于某个部门
    private Dept dept;
    private  String ename;
    private Integer age;

    public Dept getDept() {
        return dept;
    }
    public  void  work(){
        System.out.println(ename+"emp work...."+age);
        dept.info();
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }


}
package com.zzq.spring6.iocxml.ditest;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestEmp {
    public static void main(String[] args) {


    ApplicationContext context=new
            ClassPathXmlApplicationContext("bean-ditest.xml");
        Emp emp = context.getBean("emp", Emp.class);
        emp.work();
    }
}
<?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="dept" class="com.zzq.spring6.iocxml.ditest.Dept">
        <property name="dname" value="维和部"></property>
    </bean>
    <bean id="emp" class="com.zzq.spring6.iocxml.ditest.Emp">
        <!--普通属性注入-->
        <property name="age" value="23"></property>
        <property name="ename" value="aa"></property>
<!--        注入对象类型属性-->
        <property name="dept" ref="dept"></property>
    </bean>
</beans>

第二种方式:

内部bean的注入

<!--    内部bean注入-->
    <bean id="emp2" class="com.zzq.spring6.iocxml.ditest.Emp">
        <!--普通属性注入-->
        <property name="age" value="2344"></property>
        <property name="ename" value="aabbb"></property>
        <!--        注入对象类型属性-->
        <property name="dept" >
            <bean id="dept2" class="com.zzq.spring6.iocxml.ditest.Dept">
                <property name="dname" value="维和部111"></property>
            </bean>
        </property>
    </bean>

 第三种方式:

级联赋值

<!--    第三种方式  级联赋值-->
    <bean id="dept3" class="com.zzq.spring6.iocxml.ditest.Dept">
        <property name="dname" value="技术研发部"></property>
    </bean>
    <bean id="emp3" class="com.zzq.spring6.iocxml.ditest.Emp">
        <property name="ename" value="Tom"></property>
        <property name="age" value="22"></property>
        <property name="dept" ref="dept3"></property>
        <property name="dept.dname" value="测试部"></property>
    </bean>

注入数组类型的属性 

package com.zzq.spring6.iocxml.ditest;

import java.lang.reflect.Array;
import java.util.Arrays;

//员工类
public class Emp {
    //对象类型属性:员工属于某个部门
    private Dept dept;
    private String ename;
    private Integer age;
    private String[] loves;

    public void work() {
        System.out.println(ename + "emp work...." + age);
        dept.info();
        System.out.println(Arrays.toString(loves));
    }
    public void setLoves(String[] loves) {
        this.loves = loves;
    }

    public Dept getDept() {
        return dept;
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }


}
<?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="dept" class="com.zzq.spring6.iocxml.ditest.Dept">
        <property name="dname" value="维和部"></property>
    </bean>
    <bean id="emp" class="com.zzq.spring6.iocxml.ditest.Emp">
        <!--普通属性注入-->
        <property name="age" value="23"></property>
        <property name="ename" value="aa"></property>
        <!--        注入对象类型属性-->
        <property name="dept" ref="dept"></property>
<!--        数组类型属性-->
        <property name="loves">
            <array>
                <value>睡觉</value>
                <value>听歌</value>
                <value>追剧</value>
            </array>
        </property>
    </bean>
</beans>

为集合类型属性赋值

List集合属性的注入

<?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="empone" class="com.zzq.spring6.iocxml.ditest.Emp">
    <!--普通属性注入-->
    <property name="age" value="23"></property>
    <property name="ename" value="aa"></property>
    </bean>
    <bean id="emptwo" class="com.zzq.spring6.iocxml.ditest.Emp">
        <!--普通属性注入-->
        <property name="age" value="33"></property>
        <property name="ename" value="agga"></property>
    </bean>
<bean id="dept" class="com.zzq.spring6.iocxml.ditest.Dept">
    <property name="dname" value="技术部"></property>
    <property name="empList">
        <list>
            <ref bean="empone"></ref>
            <ref bean="emptwo"></ref>
        </list>
    </property>
</bean>


</beans>
package com.zzq.spring6.iocxml.ditest;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestDept {
    public static void main(String[] args) {


    ApplicationContext context=new
            ClassPathXmlApplicationContext("bean-dilist.xml");
       Dept dept = context.getBean("dept", Dept.class);
        dept.info();
    }
}
package com.zzq.spring6.iocxml.ditest;

import java.util.List;

//部门类
public class Dept {
//一个部门有很多员工
    private List<Emp> empList;

    public List<Emp> getEmpList() {
        return empList;
    }

    public void setEmpList(List<Emp> empList) {
        this.empList = empList;
    }

    public String getDname() {
        return dname;
    }

    private  String dname;

    public void setDname(String dname) {
        this.dname = dname;
    }

    public  void info(){
        System.out.println("部门名称:"+dname);
        for (Emp emp:empList) {
            System.out.println(emp.getEname());
        }
    }
}

map集合属性的注入

Student类

package com.zzq.spring6.iocxml.dimap;

import java.util.Map;

public class Student {
    private String sid;
    private String name;
    private Map<String, Teacher> teacherMap;

    public void run() {
        System.out.println("学生编号: " + sid +
                "学生名称: " + name);
        System.out.println(teacherMap);
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid='" + sid + '\'' +
                ", name='" + name + '\'' +
                ", teacherMap=" + teacherMap +
                '}';
    }

    public Map<String, Teacher> getTeacherMap() {
        return teacherMap;
    }

    public void setTeacherMap(Map<String, Teacher> teacherMap) {
        this.teacherMap = teacherMap;
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

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

Teacher类

package com.zzq.spring6.iocxml.dimap;

public class Teacher {
    private String teacherId;
    private String teacherName;

    @Override
    public String toString() {
        return "Teacher{" +
                "teacherId='" + teacherId + '\'' +
                ", teacherName='" + teacherName + '\'' +
                '}';
    }

    public String getTeacherId() {
        return teacherId;
    }

    public void setTeacherId(String teacherId) {
        this.teacherId = teacherId;
    }

    public String getTeacherName() {
        return teacherName;
    }

    public void setTeacherName(String teacherName) {
        this.teacherName = teacherName;
    }
}

package com.zzq.spring6.iocxml.dimap;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestStu {
    @Test
    public void testStu(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean-dimap.xml");
        Student student = context.getBean("student", Student.class);
        student.run();
    }
}
<?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="student" class="com.zzq.spring6.iocxml.dimap.Student">
    <property name="name" value="aa"></property>
    <property name="sid" value="111"></property>
    <property name="teacherMap">
        <map>
            <entry>
                <key>
                    <value>10001</value>
                </key>
                <ref bean="teacherone"></ref>
            </entry>
            <entry>
                <key>
                    <value>10099</value>
                </key>
                <ref bean="teachertwo"></ref>
            </entry>
        </map>
    </property>
</bean>
    <bean id="teacherone" class="com.zzq.spring6.iocxml.dimap.Teacher">
        <property name="teacherId" value="22"></property>
        <property name="teacherName" value="王五"></property>
    </bean>
    <bean id="teachertwo" class="com.zzq.spring6.iocxml.dimap.Teacher">
        <property name="teacherId" value="22"></property>
        <property name="teacherName" value="王五"></property>
    </bean>
</beans>

引入集合类型的bean

1、创建三个对象

2、注入普通类型属性

3、使用util:类型 定义

4、在学生bean引入util:类型定义bean,完成list、map类型属性注入

lesson

package com.zzq.spring6.iocxml.dimap;

public class Lesson {
    private String lessonName;

    @Override
    public String toString() {
        return "Lesson{" +
                "lessonName='" + lessonName + '\'' +
                '}';
    }

    public String getLessonName() {
        return lessonName;
    }

    public void setLessonName(String lessonName) {
        this.lessonName = lessonName;
    }
}
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd
               http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="student" class="com.zzq.spring6.iocxml.dimap.Student">
        <property name="name" value="lucy"></property>
        <property name="sid" value="111"></property>
<!--        注入list map类型属性-->
        <property name="lessonList" ref="lessonList"></property>
        <property name="teacherMap" ref="teacherMap"></property>
    </bean>
<util:list id="lessonList">
    <ref bean="lessonone"></ref>
    <ref bean="lessontwo"></ref>
</util:list>
    <util:map id="teacherMap">
        <entry>
            <key>
                <value>10001</value>
            </key>
            <ref bean="teacherone"></ref>
        </entry>
        <entry>
            <key>
                <value>1208</value>
            </key>
            <ref bean="teachertwo"></ref>
        </entry>
    </util:map>

    <bean id="lessonone" class="com.zzq.spring6.iocxml.dimap.Lesson">
        <property name="lessonName" value="java开发"></property>
    </bean>
    <bean id="lessontwo" class="com.zzq.spring6.iocxml.dimap.Lesson">
        <property name="lessonName" value="前端开发"></property>
    </bean>
    <bean id="teacherone" class="com.zzq.spring6.iocxml.dimap.Teacher">
        <property name="teacherId" value="2201"></property>
        <property name="teacherName" value="王五"></property>
    </bean>
    <bean id="teachertwo" class="com.zzq.spring6.iocxml.dimap.Teacher">
        <property name="teacherId" value="2200"></property>
        <property name="teacherName" value="刘五"></property>
    </bean>
</beans>

引入p命名空间

<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd
               http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--p命名空间得到注入-->
    <bean id="studentp" class="com.zzq.spring6.iocxml.dimap.Student"
          p:sid="111" p:name="流水" p:lessonList-ref="lessonList" p:teacherMap-ref="teacherMap">

    </bean>

    <bean id="student" class="com.zzq.spring6.iocxml.dimap.Student">
        <property name="name" value="lucy"></property>
        <property name="sid" value="111"></property>
        <!--        注入list map类型属性-->
        <property name="lessonList" ref="lessonList"></property>
        <property name="teacherMap" ref="teacherMap"></property>
    </bean>
    <util:list id="lessonList">
        <ref bean="lessonone"></ref>
        <ref bean="lessontwo"></ref>
    </util:list>
    <util:map id="teacherMap">
        <entry>
            <key>
                <value>10001</value>
            </key>
            <ref bean="teacherone"></ref>
        </entry>
        <entry>
            <key>
                <value>1208</value>
            </key>
            <ref bean="teachertwo"></ref>
        </entry>
    </util:map>

    <bean id="lessonone" class="com.zzq.spring6.iocxml.dimap.Lesson">
        <property name="lessonName" value="java开发"></property>
    </bean>
    <bean id="lessontwo" class="com.zzq.spring6.iocxml.dimap.Lesson">
        <property name="lessonName" value="前端开发"></property>
    </bean>
    <bean id="teacherone" class="com.zzq.spring6.iocxml.dimap.Teacher">
        <property name="teacherId" value="2201"></property>
        <property name="teacherName" value="王五"></property>
    </bean>
    <bean id="teachertwo" class="com.zzq.spring6.iocxml.dimap.Teacher">
        <property name="teacherId" value="2200"></property>
        <property name="teacherName" value="刘五"></property>
    </bean>
</beans>
package com.zzq.spring6.iocxml.dimap;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestStu {
    @Test
    public void testStu(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean-diref.xml");
        Student student = context.getBean("studentp", Student.class);
        student.run();
    }
}

引入外部属性文件

引入依赖

 <!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.30</version>
</dependency>

<!-- 数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.15</version>
</dependency>

创建外部属性文件

jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

配置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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--    完成数据库信息的注入-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
    </bean>
</beans>

测试

package com.zzq.spring6.iocxml.jdbc;

import com.alibaba.druid.pool.DruidDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestJdbc {
    @Test
    public void demo1() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");

    }
    @Test
    public void  demo2(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean-jdbc.xml");
        DruidDataSource dataSource = context.getBean(DruidDataSource.class);
        System.out.println(dataSource.getUrl());
    }
}

bean的作用域

bean的生命周期

package com.zzq.spring6.iocxml.life;

public class User {
    private String name;
//无参构造
    public User() {
        System.out.println("1 bean对象创建,调用无参构造");
    }
//初始化方法
    public void  initMethod(){
        System.out.println("4、bean对象初始化,调用指定的初始化方法");
    }
    //销毁的方法
    public void destroyMethod(){
        System.out.println("7、bean对象销毁,调用指定的销毁方法");
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("2 给bean对象设置属性值");
        this.name = name;
    }
}
package com.zzq.spring6.iocxml.life;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;

public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("3、bean后置处理器,初始化之前执行");
    System.out.println(beanName+"::"+bean);
        return bean;
    }
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("5、bean后置处理器,初始化之后执行");
    System.out.println(beanName+"::"+bean);
    return bean;
    }
}
package com.zzq.spring6.iocxml.life;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUser {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context=new
                ClassPathXmlApplicationContext("bean-life.xml");
        User user = context.getBean("user", User.class);
        System.out.println("6  bean对象创建完成,可以使用");
        System.out.println(user);
        context.close();//销毁对象
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.zzq.spring6.iocxml.life.User"
scope="singleton" init-method="initMethod" destroy-method="destroyMethod"
>
    <property name="name" value="lucy"></property>
</bean>
<!--    bean的后置处理器要放入IOC容器才能生效-->
    <bean id="myBeanPost" class="com.zzq.spring6.iocxml.life.MyBeanPost"></bean>
</beans>

FactoryBean

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

package com.zzq.spring6.iocxml.factorybean;

import org.springframework.beans.factory.FactoryBean;

public class MyFactoryBean implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}
package com.zzq.spring6.iocxml.factorybean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUser {
    public static void main(String[] args) {
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean-factorybean.xml");
       User user =(User) context.getBean("user");
        System.out.println(user);
    }
}
package com.zzq.spring6.iocxml.factorybean;

public class User {
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.zzq.spring6.iocxml.factorybean.MyFactoryBean"></bean>
</beans>

基于xml自动装配

package com.zzq.spring6.iocxml.auto.controller;

import com.zzq.spring6.iocxml.auto.service.UserService;
import com.zzq.spring6.iocxml.auto.service.UserSeviceImpl;

public class UserController {
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void addUser(){
        System.out.println("controller方法执行了");
        //调用service的方法
        userService.addUserService();
//        UserService userService=new UserSeviceImpl();
//        userService.addUserService();
    }
}
package com.zzq.spring6.iocxml.auto.service;

public interface UserService {
    public void addUserService();
}
package com.zzq.spring6.iocxml.auto.service;

import com.zzq.spring6.iocxml.auto.dao.UserDao;
import com.zzq.spring6.iocxml.auto.dao.UserDaoImpl;

public class UserSeviceImpl implements UserService {
    private  UserDao userDao;

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

    @Override
    public void addUserService() {
        System.out.println("userService方法执行了");
        userDao.addUserDao();
//        UserDao userDao=new UserDaoImpl();
//        userDao.addUserDao();
    }
}
package com.zzq.spring6.iocxml.auto.dao;

public class UserDaoImpl implements UserDao {
    @Override
    public void addUserDao(){
        System.out.println("userDao方法执行了");
    }
}
package com.zzq.spring6.iocxml.auto.dao;

import com.zzq.spring6.iocxml.auto.service.UserService;

public interface UserDao {
public void addUserDao();

}
package com.zzq.spring6.iocxml.auto;

import com.zzq.spring6.iocxml.auto.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUser {
    public static void main(String[] args) {
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean-auto.xml");
        UserController controller = context.getBean("userController", UserController.class);
        controller.addUser();
    }
}
<?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="userController" class="com.zzq.spring6.iocxml.auto.controller.UserController" autowire="byType"></bean>-->
<!--<bean id="userService" class="com.zzq.spring6.iocxml.auto.service.UserSeviceImpl" autowire="byType"></bean>-->
<!--<bean id="userDao" class="com.zzq.spring6.iocxml.auto.dao.UserDaoImpl"></bean>-->
<!--    根据名称自动装配-->
    <bean id="userController" class="com.zzq.spring6.iocxml.auto.controller.UserController" autowire="byName"></bean>
    <bean id="userService" class="com.zzq.spring6.iocxml.auto.service.UserSeviceImpl" autowire="byName"></bean>
    <bean id="userDao" class="com.zzq.spring6.iocxml.auto.dao.UserDaoImpl"></bean>
</beans>

自动装配方式:byName
>
> byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值

基于注解管理Bean

开启组件扫描

<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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.zzq"></context:component-scan>
</beans>

情况一:最基本的扫描方式**

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

**情况二:指定要排除的组件**

```xml
<context:component-scan base-package="com.atguigu.spring6">
    <!-- context:exclude-filter标签:指定排除规则 -->
    <!-- 
         type:设置排除或包含的依据
        type="annotation",根据注解排除,expression中设置要排除的注解的全类名
        type="assignable",根据类型排除,expression中设置要排除的类型的全类名
    -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <!--<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
</context:component-scan>
```

**情况三:仅扫描指定组件**

```xml
<context:component-scan base-package="com.atguigu" use-default-filters="false">
    <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
    <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
    <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
    <!-- 
         type:设置排除或包含的依据
        type="annotation",根据注解排除,expression中设置要排除的注解的全类名
        type="assignable",根据类型排除,expression中设置要排除的类型的全类名
    -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
</context:component-scan>
```

bean对象的创建

以上四个注解都可实现bean的创建

package com.zzq.bean;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

@Component(value = "user")//<bean id="user" class="...">属性可不写,默认为user
//@Controller
//@Repository
//@Service
public class User {
}

属性注入

单独使用@Autowired注解,**默认根据类型装配**。【默认是byType】

package com.zzq.autowired.controller;

import com.zzq.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    //注入service
    //第一种方式属性的注入
    @Autowired//根据类型找到对应对象,完成注入
    private UserService userService;

    public  void add(){
        System.out.println("controller...");
        userService.add();
    }
}
package com.zzq.autowired.service;

import com.zzq.autowired.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{
    //注入dao

    //第一种方式属性注入
//@Autowired
//    private UserDao userDao;

    //第二种方式set方法注入
//    @Autowired
//    private UserDao userDao;
//
//    public void setUserDao(UserDao userDao) {
//        this.userDao = userDao;
//    }

    //第三种方式:构造方法中注入
  private UserDao userDao;
@Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void add() {
        System.out.println("service...");
        userDao.add();
    }
}
package com.zzq.autowired.controller;

import com.zzq.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    //注入service
    //第一种方式属性的注入
//    @Autowired//根据类型找到对应对象,完成注入
//    private UserService userService;

    //第二种方式set方法注入
//    private UserService userService;
//@Autowired
//    public void setUserService(UserService userService) {
//        this.userService = userService;
//    }
    //第三种方式 构造方法的注入
//    private UserService userService;
//@Autowired
//    public UserController(UserService userService) {
//        this.userService = userService;
//    }

    //第四种方式
    private UserService userService;

    public UserController( @Autowired UserService userService) {
        this.userService = userService;
    }

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

@Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter方法上。
- 当带参数的构造方法只有一个,@Autowired注解可以省略。()
- @Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier注解一起使用。

@Resource注入

@Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?

- @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
- @Autowired注解是Spring框架自己的。
- **@Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。**
- **@Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。**
- @Resource注解用在属性上、setter方法上。
- @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【**如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。**】

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

package com.zzq.resource.controller;

import com.zzq.resource.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller("myUserController")
public class UserController {
    //根据名称进行注入
//@Resource(name = "myUserService")
//private UserService userService;
    //根据类型进行注入
    @Resource
    private UserService userService;

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

Spring全注解开发

package com.zzq.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration//配置类
@ComponentScan("com.zzq")//开启组件扫描
public class SpringConfig {
}
package com.zzq.resource;

import com.zzq.config.SpringConfig;
import com.zzq.resource.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUserControllerAnno {
    public static void main(String[] args) {
        //加载配置类
       ApplicationContext context=
               new AnnotationConfigApplicationContext(SpringConfig.class);
        UserController controller = context.getBean(UserController.class);
        controller.add();
    }

}

java反射机制

package com.zzq;

import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestCar {
    //1、获取clas对象多种方式

    @Test
    public void test01() throws Exception {
//1、类名.class
        Class clazz1 = Car.class;
        //2、对象.getclass()
        Class clazz2 = new Car().getClass();
        //3、Class.forname("全路径")
        Class clazz3 = Class.forName("com.zzq.Car");
//实例化
        Car car = (Car) clazz3.getDeclaredConstructor().newInstance();
        System.out.println(car);
    }

    //2、通过反射获取构造方法
    @Test
    public void test02() throws Exception {
        Class clazz = Car.class;
        //获取所有构造
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor c : constructors) {
            System.out.println("方法个数" + c.getName() + "参数个数" + c.getParameterCount());

        }
        //指定有参数构造创建对象
        //1、构造public
//    Constructor c1=clazz.getConstructor(String.class,int.class, String.class);
//    Car car1 = (Car)c1.newInstance("本部", 10, "黄色");
//    System.out.println(car1);
        //2、构造private
        Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
        c2.setAccessible(true);
        Car car2 = (Car) c2.newInstance("部", 11, "黄色");
        System.out.println(car2);
    }

    //3、获取属性
    @Test
    public void test03() throws Exception {
        Class clazz = Car.class;
        //实例化
        Car car = (Car) clazz.getDeclaredConstructor().newInstance();
        //获取所有的public属性
        //    Field[]fields=clazz.getFields();
        //获取所有属性(包含私有属性)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().equals("name")) {
                field.setAccessible(true);
                field.set(car, "奥迪");
            }
            System.out.println(field.getName());
            System.out.println(car);
        }

        }
    //4、获取方法
    @Test
    public void test04() throws Exception {
Car car=new Car("宝马",19,"黑色");
Class clazz=car.getClass();
//public方法
        Method[]methods=clazz.getMethods();
        for (Method m1:methods
             ) {
          //  System.out.println(m1.getName());
            //执行方法toString
            if (m1.getName().equals("toString")){
                String invoke=(String) m1.invoke(car);
              //  System.out.println("toString执行了:"+invoke);
        }
            //private方法
            Method[] methodsAll = clazz.getDeclaredMethods();
            for (Method m:methodsAll){
                if (m.getName().equals("run")){
                    m.setAccessible(true);
                    m.invoke(car);
                }


            }
        }

    }




}

实现Spring的IoC

ApplicationContext.java

package com.zzq.Bean;

import com.zzq.anno.Bean;

public interface ApplicationContext {

    Object getBean(Class clazz);
}

AnnotationApplicationContext.java

package com.zzq.Bean;

import com.zzq.anno.Bean;
import com.zzq.anno.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationApplicationContext implements ApplicationContext {
    //创建map集合,放bean对象
    private   Map<Class, Object> beanFactory = new HashMap<>();
      private static String rootPath;

    //返回对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //设置包扫描规则
    //当前包及其子包,拿个类有@Bean注解,把这个类通过反射实例化
     public AnnotationApplicationContext(String basePackage) {
   // public static void pathdemo1(String basePackage) {
        //把.替换成\
        try {
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            //获取包绝对路径
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //获取包前面路径得到部分,字符串截取
                  rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                //包扫描
                  loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //属性得到注入
         loadDi();
    }


    //    //包扫描的过程,实例化
   private  void loadBean(File file) throws Exception {
       //1、判断当前是否文件夹
       if (file.isDirectory()) {
           //2、获取文件夹里面所有内容
           File[] childrenFiles = file.listFiles();
           //3、判断文件夹里面为空,直接返回
           if (childrenFiles == null || childrenFiles.length == 0) {
               return;
           }
           //4、如果文件夹里面不为空,遍历文件夹所有内容
           for (File child : childrenFiles) {
               //4.1遍历得到某个File对象,继续判断,如果还是文件,
               if (child.isDirectory()) {
                   //递归
                   loadBean(child);
               } else {
                   //4.2遍历得到File对象不是文件夹,是文件
                   //4.3得到包路径+类名称部分-字符串截取
                   String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                   //4.4判断当前文件类型是否.class
                   if (pathWithClass.contains(".class")) {
                       //4.5如果是.class类型,把路径\替换. 把class去掉
                       String allName = pathWithClass.replaceAll("\\\\", ".")
                               .replace(".class", "");
                       //4.6判断类上面是否有注解@Bean,如果有实例化过程
                       //4.6.1获取类发class
                       Class<?> clazz = Class.forName(allName);
                       //4.6.2判断不是接口
                       if (!clazz.isInterface()) {
                           //4.6.3判断类上面是否有注解@Bean
                           Bean annotation = clazz.getAnnotation(Bean.class);
                           if ((annotation != null)) {
                               //4.6.4实例化
                               Object instance = clazz.getConstructor().newInstance();
                               //4.7把对象实例化之后,放到map集合beanFactory
                               //4.7.1判断当前类如果有接口,让接口class作为map的key
                               if (clazz.getInterfaces().length > 0) {
                                   beanFactory.put(clazz.getInterfaces()[0], instance);
                               } else {
                                   beanFactory.put(clazz, instance);
                               }
                           }
                       }
                   }
               }
           }

       }

   }
//属性注入
    private void loadDi()  {
        //实例化对象在beanFactory的map集合里面
        //1、遍历beanFactory的map集合
        Set<Map.Entry<Class,Object>>entries=beanFactory.entrySet();
        for (Map.Entry<Class,Object>entry:entries) {
//2、获取map集合每个对象(value),每个对象属性获取到
            Object obj=entry.getValue();
            //获取对象Class
                Class<?> clazz=obj.getClass();
        //获取每个对象的属性
            Field[] declaredFields = clazz.getDeclaredFields();
            //遍历得到每个对象属性数组,得到每个属性
            for (Field field:declaredFields) {
                //4、判断属性上面是否有@Di注解
                Di annotation = field.getAnnotation(Di.class);
                if (annotation != null) {
                    //如果私有属性,设置可以设置值
                    field.setAccessible(true);
                    //5、如果有@Di注解,把对象进行设置(注入)
                    try {
                        field.set(obj, beanFactory.get(field.getType()));
                    }catch (IllegalAccessException e){
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

}
package com.zzq.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
package com.zzq.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
package com.zzq;

import com.zzq.Bean.AnnotationApplicationContext;
import com.zzq.Bean.ApplicationContext;
import com.zzq.service.UserService;

public class TestUser {
    public static void main(String[] args)  {
      ApplicationContext context=
              new AnnotationApplicationContext("com.zzq");
        UserService userService  = (UserService)context.getBean(UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

面向切面:AOP

package com.zzq.example;

public interface Calculator {
    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);
}
package com.zzq.example;
//基本实现类
public class CalculatorImpl implements Calculator{
    @Override
    public int add(int i, int j) {

        int result = i + j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int sub(int i, int j) {

        int result = i - j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int mul(int i, int j) {

        int result = i * j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int div(int i, int j) {

        int result = i / j;

        System.out.println("方法内部 result = " + result);

        return result;
    }
}
package com.zzq.example;
//带日志实现类
public class CalculatorLogImpl implements Calculator{
    @Override
    public int add(int i, int j) {

        System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);

        int result = i + j;

        System.out.println("方法内部 result = " + result);

        System.out.println("[日志] add 方法结束了,结果是:" + result);

        return result;
    }

    @Override
    public int sub(int i, int j) {

        System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);

        int result = i - j;

        System.out.println("方法内部 result = " + result);

        System.out.println("[日志] sub 方法结束了,结果是:" + result);

        return result;
    }

    @Override
    public int mul(int i, int j) {

        System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);

        int result = i * j;

        System.out.println("方法内部 result = " + result);

        System.out.println("[日志] mul 方法结束了,结果是:" + result);

        return result;
    }

    @Override
    public int div(int i, int j) {

        System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);

        int result = i / j;

        System.out.println("方法内部 result = " + result);

        System.out.println("[日志] div 方法结束了,结果是:" + result);

        return result;
    }
}

代理模式

静态代理

package com.zzq.example;

public class CalculatorStaticProxy implements Calculator{
    //被代理的目标对象传递过来
    private  Calculator calculator;

    public CalculatorStaticProxy(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public int add(int i, int j) {
        //输出日志
        System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
         //调用目标对象得到方法实现核心业务
        int result=calculator.add(i,j);
        System.out.println("[日志] mul 方法结束了,结果是:" + result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        return 0;
    }

    @Override
    public int mul(int i, int j) {
        return 0;
    }

    @Override
    public int div(int i, int j) {
        return 0;
    }
}

静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。

动态代理

package com.zzq.example;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class ProxyFactory {
    //目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //创建方法,返回代理对象
    public Object getProxy() {
        /*
         * Proxy.newProxyInstance方法
         * 有三个参数
         * 1、ClassLoader:加载动态生成代理类的加载器
         * 2、Class[]interfaces:目标对象实现所有接口的在class类型数组
         * 3、InvocationHandler:设置代理对象实现目标对象方法的过程
         *
         * */
        //ClassLoader:加载动态生成代理类的加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        //Class[]interfaces:目标对象实现所有接口的在class类型数组
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //InvocationHandler:设置代理对象实现目标对象方法的过程
        InvocationHandler invocationHandler = new InvocationHandler() {
            //第一个参数:代理对象
            //第二个参数:需要重写目标对象的方法
            //第三个参数:method方法里面参数
            @Override
            public Object invoke(Object proxy,
                                 Method method,
                                 Object[] args) throws Throwable {
                //方法调用之前输出
                System.out.println("[动态代理][日志] " + method.getName() + ",参数:" + Arrays.toString(args));
                //调用目标的方法
                Object result = method.invoke(target, args);
                //方法调用之后输出
                System.out.println("[动态代理][日志] " + method.getName() + ",结果:" + result);
                return result;
            }
        };
        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}
package com.zzq.example;

public class TestCal {
    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());
   Calculator proxy=(Calculator) proxyFactory.getProxy();
  // proxy.add(1,2);
        proxy.mul(2,4);
    }
}

AOP

①横切关注点

分散在每个各个模块中解决同一样的问题,如用户验证、日志管理、事务处理、数据缓存都属于横切关注点。

从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。

这个概念不是语法层面的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。

②通知(增强)

**增强,通俗说,就是你想要增强的功能,比如 安全,事务,日志等。**

每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

- 前置通知:在被代理的目标方法**前**执行
- 返回通知:在被代理的目标方法**成功结束**后执行(**寿终正寝**)
- 异常通知:在被代理的目标方法**异常结束**后执行(**死于非命**)
- 后置通知:在被代理的目标方法**最终结束**后执行(**盖棺定论**)
- 环绕通知:使用try...catch...finally结构围绕**整个**被代理的目标方法,包括上面四种通知对应的所有位置

 ③切面

封装通知方法的类。

 ④目标

被代理的目标对象。

 ⑤代理

向目标对象应用通知之后创建的代理对象。

 ⑥连接点

这也是一个纯逻辑概念,不是语法定义的。

把方法排成一排,每一个横切位置看成x轴方向,把方法从上到下执行的顺序看成y轴,x轴和y轴的交叉点就是连接点。

⑦切入点

定位连接点的方式。

每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。

如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句。

作用

- 简化代码:把方法中固定位置的重复的代码**抽取**出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。

- 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被**套用**了切面逻辑的方法就被切面给增强了。

 AspectJ:是AOP思想的一种实现。本质上是静态代理,**将代理逻辑“织入”被代理的目标类编译得到的字节码文件**,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。

切入点表达式

语法细节**

- 用*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限
- 在包名的部分,一个“*”号只能代表包的层次结构中的一层,表示这一层是任意的。
  - 例如:*.Hello匹配com.Hello,不匹配com.atguigu.Hello
- 在包名的部分,使用“*..”表示包名任意、包的层次深度任意
- 在类名的部分,类名部分整体用*号代替,表示类名任意
- 在类名的部分,可以使用*号代替类名的一部分
  - 例如:*Service匹配所有名称以Service结尾的类或接口

- 在方法名部分,可以使用*号表示方法名任意
- 在方法名部分,可以使用*号代替方法名的一部分
  - 例如:*Operation匹配所有方法名以Operation结尾的方法

- 在方法参数列表部分,使用(..)表示参数列表任意
- 在方法参数列表部分,使用(int,..)表示参数列表以一个int类型的参数开头
- 在方法参数列表部分,基本数据类型和对应的包装类型是不一样的
  - 切入点表达式中使用 int 和实际方法中 Integer 是不匹配的
- 在方法返回值部分,如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符
  - 例如:execution(public int *..*Service.*(.., int))    正确
       例如:execution(* int *..*Service.*(.., int))    错误

- 前置通知:使用@Before注解标识,在被代理的目标方法**前**执行
- 返回通知:使用@AfterReturning注解标识,在被代理的目标方法**成功结束**后执行(**寿终正寝**)
- 异常通知:使用@AfterThrowing注解标识,在被代理的目标方法**异常结束**后执行(**死于非命**)
- 后置通知:使用@After注解标识,在被代理的目标方法**最终结束**后执行(**盖棺定论**)
- 环绕通知:使用@Around注解标识,使用try...catch...finally结构围绕**整个**被代理的目标方法,包括上面四种通知对应的所有位置

 各种通知的执行顺序:
>
> - Spring版本5.3.x以前:
>   - 前置通知
>   - 目标操作
>   - 后置通知
>   - 返回通知或异常通知
> - Spring版本5.3.x以后:
>   - 前置通知
>   - 目标操作
>   - 返回通知或异常通知
>   - 后置通知

基于注解方式的AOP

package com.zzq.annoaop;

import org.springframework.stereotype.Component;

//基本实现类
@Component
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int i, int j) {

        int result = i + j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int sub(int i, int j) {

        int result = i - j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int mul(int i, int j) {

        int result = i * j;

        System.out.println("方法内部 result = " + result);

        return result;
    }

    @Override
    public int div(int i, int j) {

        int result = i / j;

        System.out.println("方法内部 result = " + result);

        return result;
    }
}
package com.zzq.annoaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Array;
import java.util.Arrays;

//切面类
@Aspect//切面类
@Component//ioc容器
public class LogAspect {
    //设置切入点和通知类型
    //切入点表达式:execution(访问修饰符 增强方法返回类型 增强方法所在类全路径。方法名称(方法参数))
    //通知类型
    //前置@Before(value=“切入点表达式配置切入点”)
    @Before(value = "execution(public int com.zzq.annoaop.CalculatorImpl.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->前置输出,方法名称" + methodName + ",参数" + Arrays.toString(args));
    }

    //返回  @AfterReturning
    @AfterReturning(value = "execution(* com.zzq.annoaop.CalculatorImpl.*(..))", returning = "result")
    public void afterRunningMethod(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名称" + methodName + ",目标方法返回的通知" + result);
    }

    //异常   @AfterThrowing
    //目标方法出现异常,这个通知执行
    @AfterThrowing(value = "execution(* com.zzq.annoaop.CalculatorImpl.*(..))", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置输出,方法名称" + methodName + ",返回异常的通知" + ex);
    }

    //后置   @After()
    @After(value = "execution(* com.zzq.annoaop.CalculatorImpl.*(..))")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置输出,方法名称" + methodName);
    }

    //环绕  @Around()
    @Around(value = "execution(* com.zzq.annoaop.CalculatorImpl.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String argstring = Arrays.toString(args);
        Object result = null;
        try {
            System.out.println("环绕通知==目标方法之前执行");
            //调用目标方法
            result = joinPoint.proceed();
            System.out.println("环绕通知==目标方法之后执行");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知==目标方法出现异常执行");
        } finally {
            System.out.println("环绕通知==目标方法执行完毕执行");
        }
        return result;
    }


}
package com.zzq.annoaop;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAop {
    @Test
    public void testAdd(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("bean.xml");
        Calculator calculator = context.getBean(Calculator.class);
        calculator.add(2,1);
    }
}

重用切入点表达式

package com.zzq.annoaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Array;
import java.util.Arrays;

//切面类
@Aspect//切面类
@Component//ioc容器
public class LogAspect {
    //设置切入点和通知类型
    //切入点表达式:execution(访问修饰符 增强方法返回类型 增强方法所在类全路径。方法名称(方法参数))
    //通知类型
    //前置@Before(value=“切入点表达式配置切入点”)
    @Before(value = "pointCut()")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->前置输出,方法名称" + methodName + ",参数" + Arrays.toString(args));
    }

    //返回  @AfterReturning
    @AfterReturning(value = "pointCut()", returning = "result")
    public void afterRunningMethod(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名称" + methodName + ",目标方法返回的通知" + result);
    }

    //异常   @AfterThrowing
    //目标方法出现异常,这个通知执行
    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置输出,方法名称" + methodName + ",返回异常的通知" + ex);
    }

    //后置   @After()
    @After(value = "pointCut()")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置输出,方法名称" + methodName);
    }

    //环绕  @Around()
    @Around(value = "pointCut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String argstring = Arrays.toString(args);
        Object result = null;
        try {
            System.out.println("环绕通知==目标方法之前执行");
            //调用目标方法
            result = joinPoint.proceed();
            System.out.println("环绕通知==目标方法之后执行");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知==目标方法出现异常执行");
        } finally {
            System.out.println("环绕通知==目标方法执行完毕执行");
        }
        return result;
    }
//重用切入点表达式
    @Pointcut(value = "execution(* com.zzq.annoaop.CalculatorImpl.*(..))")
public void pointCut(){

    }
}

切面优先级

基于xml的AOP

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启组件扫描-->
    <context:component-scan base-package="com.zzq.xmlaop"></context:component-scan>
<!--配置aop的五种通知类型-->
<aop:config>
<!--    配置切面类-->
    <aop:aspect ref="logAspect">
<!--  配置切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.zzq.xmlaop.CalculatorImpl.*(..))"/>
<!--配置五种通知类型-->
<!--        前置通知-->
        <aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before>
<!--        后置通知-->
        <aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after>
<!--        返回通知-->
        <aop:after-returning method="afterRunningMethod" pointcut-ref="pointcut" returning="result" ></aop:after-returning>
<!--        异常通知-->
        <aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="pointcut"></aop:after-throwing>
<!--        环绕通知-->
        <aop:around method="aroundMethod" pointcut-ref="pointcut"></aop:around>
    </aop:aspect>


</aop:config>


</beans>
package com.zzq.xmlaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

//切面类

@Component//ioc容器
public class LogAspect {

    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->前置输出,方法名称" + methodName + ",参数" + Arrays.toString(args));
    }


    public void afterRunningMethod(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名称" + methodName + ",目标方法返回的通知" + result);
    }


    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置输出,方法名称" + methodName + ",返回异常的通知" + ex);
    }


    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置输出,方法名称" + methodName);
    }


    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String argstring = Arrays.toString(args);
        Object result = null;
        try {
            System.out.println("环绕通知==目标方法之前执行");
            //调用目标方法
            result = joinPoint.proceed();
            System.out.println("环绕通知==目标方法之后执行");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知==目标方法出现异常执行");
        } finally {
            System.out.println("环绕通知==目标方法执行完毕执行");
        }
        return result;
    }


}
package com.zzq.xmlaop;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAop {
    @Test
    public void testAdd(){
        ApplicationContext context=new
                ClassPathXmlApplicationContext("beanaop.xml");
        Calculator calculator = context.getBean(Calculator.class);
        calculator.add(8,5);
    }
}

单元测试:JUnit

添加依赖

<dependencies>
    <!--spring context依赖-->
    <!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.0.2</version>
    </dependency>

    <!--spring对junit的支持相关依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>6.0.2</version>
    </dependency>

    <!--junit5测试-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.9.0</version>
    </dependency>
package com.zzq.junit5;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringJUnitConfig(locations = "classpath:bean.xml")
public class SpringTestJunit5 {
    @Autowired
    private User user;
    @Test
    public void testUser(){
        System.out.println(user);
        user.run();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.zzq"></context:component-scan>


</beans>

单元测试:JUnit4

添加依赖

<!-- junit测试 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
package com.zzq.junit4;

import com.zzq.junit5.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean.xml")
public class SpringTestJunit4 {
    @Autowired
    private User user;
    @Test
    public  void testUser4(){
        System.out.println(user);
        user.run();
    }
}

事务

加入依赖

<dependencies>
    <!--spring jdbc  Spring 持久化层支持jar包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>6.0.2</version>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    <!-- 数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.15</version>
    </dependency>
</dependencies>

测试增删改功能

//package com.zzq;
//
//import org.junit.jupiter.api.Test;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.jdbc.core.JdbcTemplate;
//import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

package com.zzq;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringJUnitConfig(locations = "classpath:beans.xml")
public class JDBCTemplateTest {
@Autowired
    private JdbcTemplate jdbcTemplate;
@Test
    public void testUpdate(){
    //第一步编写sql语句
    String sql = "INSERT INTO t_emp VALUES(null,?,?,?)";
    //第二步 调用jdbcTemplate的方法,传入相关参数
//    Object[]params={"王五", 21, "男"};
//    int rows = jdbcTemplate.update(sql,params );
    int result = jdbcTemplate.update(sql, "张二", 21, "男");
    System.out.println(result);
}
}

//@SpringJUnitConfig(locations = "classpath:beans.xml")
//public class JDBCTemplateTest {
//
//    @Autowired
//    private JdbcTemplate jdbcTemplate;
//    @Test
测试增删改功能
//    public void testUpdate(){
//        //添加功能
//        String sql = "insert into t_emp values(null,?,?,?)";
//        int result = jdbcTemplate.update(sql, "张三", 23, "男");
//        System.out.println(result);
//        //修改功能
//        //String sql = "update t_emp set name=? where id=?";
//        //int result = jdbcTemplate.update(sql, "张三atguigu", 1);
//
//        //删除功能
//        //String sql = "delete from t_emp where id=?";
//        //int result = jdbcTemplate.update(sql, 1);
//    }
//}

查询

//package com.zzq;
//
//import org.junit.jupiter.api.Test;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.jdbc.core.JdbcTemplate;
//import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

package com.zzq;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.util.List;

@SpringJUnitConfig(locations = "classpath:beans.xml")
public class JDBCTemplateTest {
@Autowired
    private JdbcTemplate jdbcTemplate;
//查询:返回对象
    //写法1
//    @Test
//    public  void  testSelectObject(){
//        String sql="select * from t_emp where id=?";
//     Emp empResult  = jdbcTemplate.queryForObject(sql,(rs, rowNum) -> {
//            Emp emp=new Emp();
//            emp.setId(rs.getInt("id"));
//            emp.setName(rs.getString("name"));
//            emp.setAge(rs.getInt("age"));
//            emp.setSex(rs.getString("sex"));
//            return emp;
//        },1);
//        System.out.println(empResult);
//    }
    //写法2
@Test
 public  void  testSelectObject() {
    String sql = "select * from t_emp where id=?";
    Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class),1);
    System.out.println(emp);
}
    //查询:返回list集合
    @Test
    public void testSelectList(){
    String sql="select * from t_emp";
        List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class));
        System.out.println(list);
    }
    //查询  返回单个值
@Test
public  void testSelectValue(){
    String sql="select count(*)from t_emp";
    Integer count=jdbcTemplate.queryForObject(sql,Integer.class);
    System.out.println(count);
}

    @Test
    public void testUpdate () {
        //第一步编写sql语句
        String sql = "INSERT INTO t_emp VALUES(null,?,?,?)";
        //第二步 调用jdbcTemplate的方法,传入相关参数
//    Object[]params={"王五", 21, "男"};
//    int rows = jdbcTemplate.update(sql,params );
        int result = jdbcTemplate.update(sql, "张二", 21, "男");
        System.out.println(result);
    }
}

//@SpringJUnitConfig(locations = "classpath:beans.xml")
//public class JDBCTemplateTest {
//
//    @Autowired
//    private JdbcTemplate jdbcTemplate;
//    @Test
测试增删改功能
//    public void testUpdate(){
//        //添加功能
//        String sql = "insert into t_emp values(null,?,?,?)";
//        int result = jdbcTemplate.update(sql, "张三", 23, "男");
//        System.out.println(result);
//        //修改功能
//        //String sql = "update t_emp set name=? where id=?";
//        //int result = jdbcTemplate.update(sql, "张三atguigu", 1);
//
//        //删除功能
//        //String sql = "delete from t_emp where id=?";
//        //int result = jdbcTemplate.update(sql, 1);
//    }
//}
package com.zzq;

public class Emp {
    private Integer id;
    private String name;
    private Integer age;
    private String sex;

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

①什么是事务

数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

②事务的特性

**A:原子性(Atomicity)**

一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

**C:一致性(Consistency)**

事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。

如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。

如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。

**I:隔离性(Isolation)**

指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。

**D:持久性(Durability)**

指的是只要事务成功结束,它对数据库所做的更新就必须保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。

@Transactional注解标识的位置

@Transactional标识在方法上,则只会影响该方法

@Transactional标识的类上,则会影响类中所有的方法

添加事务注解

因为service层表示业务逻辑层,一个方法表示一个完成的功能,因此处理事务一般在service层处理

**在BookServiceImpl的buybook()添加注解@Transactional**
 

事务属性:只读

@Transactional(readOnly = true)

对增删改操作设置只读会抛出下面异常:

Caused by: java.sql.SQL
Exception: Connection is read-only. Queries leading to data modification are not allowed

事务属性:超时

@Transactional(timeout = 3)

事务属性:回滚策略


声明式事务默认只针对运行时异常回滚,编译时异常不回滚。

可以通过@Transactional中相关属性设置回滚策略

- rollbackFor属性:需要设置一个Class类型的对象
- rollbackForClassName属性:需要设置一个字符串类型的全类名

- noRollbackFor属性:需要设置一个Class类型的对象
- rollbackFor属性:需要设置一个字符串类型的全类名

@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")

事务属性:隔离级别

隔离级别一共有四种:

- 读未提交:READ UNCOMMITTED

  允许Transaction01读取Transaction02未提交的修改。

- 读已提交:READ COMMITTED、

  要求Transaction01只能读取Transaction02已提交的修改。

- 可重复读:REPEATABLE READ

  确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。

- 串行化:SERIALIZABLE

  确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化

事务属性:传播行为

什么是事务的传播行为?

在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。

一共有七种传播行为:

- REQUIRED:支持当前事务,如果不存在就新建一个(默认)**【没有就新建,有就加入】**
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行**【有就加入,没有就不管了】**
- MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常**【有就加入,没有就抛异常】**
- REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起**【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】**
- NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务**【不支持事务,存在就挂起】**
- NEVER:以非事务方式运行,如果有事务存在,抛出异常**【不支持事务,存在就抛异常】**
- NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。**【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】**

在数据库中将用户的余额修改为100元

**③观察结果**

可以通过@Transactional中的propagation属性设置事务传播行为

修改BookServiceImpl中buyBook()上,注解@Transactional的propagation属性

@Transactional(propagation = Propagation.REQUIRED),默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行。经过观察,购买图书的方法buyBook()在checkout()中被调用,checkout()上有事务注解,因此在此事务中执行。所购买的两本图书的价格为80和50,而用户的余额为100,因此在购买第二本图书时余额不足失败,导致整个checkout()回滚,即只要有一本书买不了,就都买不了

@Transactional(propagation = Propagation.REQUIRES_NEW),表示不管当前线程上是否有已经开启的事务,都要开启新事务。同样的场景,每次购买图书都是在buyBook()的事务中执行,因此第一本图书购买成功,事务结束,第二本图书购买失败,只在第二次的buyBook()中回滚,购买第一本图书不受影响,即能买几本就买几本。

<?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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.zzq.tx"></context:component-scan>
    <!--引入外部文件,创建数据源对象-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="username" value="${jdbc.user}"></property>

    </bean>
    <!--创建jdbcTemplate对象,注入数据源-->
    <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--
        开启事务的注解驱动
        通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务
    -->
    <!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 -->
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>
package com.zzq.tx.controller;

import com.zzq.tx.service.BookService;
import com.zzq.tx.service.CheckoutService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

//@Controller
//public class BookController {
//    @Autowired
// private BookService bookService;
//    //买书的方法:图书的id和用户id
//    public void buyBook(Integer bookId,Integer userId){
调用service方法
//        bookService.buyBook(bookId,userId);
//    }
//}
@Controller
public class BookController {

//    @Autowired
//    private BookService bookService;

//买书的方法:图书id和用户id
//    public void buyBook(Integer bookId, Integer userId){
//        bookService.buyBook(bookId, userId);
//    }


    @Autowired
    private CheckoutService checkoutService;

 public void checkout(Integer[]bookIds,Integer userId){
     checkoutService.checkout(bookIds,userId);
 }
}
package com.zzq.tx.service;

public interface CheckoutService {
    //买多本书的方法
    void checkout(Integer[]bookIds,Integer userId);
}

package com.zzq.tx.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CheckoutServiceImpl implements CheckoutService {
    //注入bookService
    @Autowired
    private BookService bookService;


    //买多本书的方法
@Transactional
    @Override
    public void checkout(Integer[] bookIds, Integer userId) {
for (Integer bookId:bookIds){
    bookService.buyBook(bookId,userId);
}
    }
}
package com.zzq.tx;

import com.zzq.tx.controller.BookController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

//package com.zzq.tx;
//
//import com.zzq.tx.controller.BookController;
//import org.junit.jupiter.api.Test;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
//
//@SpringJUnitConfig(locations = "classpath:beans.xml")
//public class TestBookTx {
//
//    @Autowired
//    private BookController bookController;
//
//    @Test
//    public void testBuyBook(){
//        bookController.buyBook(1, 1);
//    }
//}
@SpringJUnitConfig(locations = "classpath:beans.xml")
public class TxByAnnotationTest {

    @Autowired
    private BookController bookController;

    @Test
    public void testBuyBook(){
      // bookController.buyBook(1, 1);
        Integer[]bookIds={1,2};
        bookController.checkout(bookIds,1);
    }

}

全注解声明式事务开发

package com.zzq.tx.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration//配置类
@ComponentScan("com.zzq.tx")
@EnableTransactionManagement//开启事务管理
public class SpringConfig {
    @Bean
    public DruidDataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        return dataSource;
    }

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

基于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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.zzq.xmltx"></context:component-scan>
    <!--引入外部文件,创建数据源对象-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="username" value="${jdbc.user}"></property>

    </bean>
    <!--创建jdbcTemplate对象,注入数据源-->
    <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>
    <!--    事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>
    <!--配置事务增强-->
    <tx:advice id="tsAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="update*" read-only="false" propagation="REQUIRED"></tx:method>
        </tx:attributes>
    </tx:advice>
    <!--    配置切入点和通知使用方法-->
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.zzq.xmltx.service.*.*(..))"/>
        <aop:advisor advice-ref="tsAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>
</beans>

资源操作:Resources

Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。Resource一般包括这些实现类:UrlResource、ClassPathResource、FileSystemResource、ServletContextResource、InputStreamResource、ByteArrayResource

UrlResource访问网络资源

Resource的一个实现类,用来访问网络资源,它支持URL的绝对路径。

http:------该前缀用于访问基于HTTP协议的网络资源。

ftp:------该前缀用于访问基于FTP协议的网络资源

file: ------该前缀用于从文件系统中读取资源

package com.zzq;

import org.springframework.core.io.UrlResource;

import java.net.MalformedURLException;

//演示UrlResource访问网络资源
public class UrlResourceDemo {
    public static void main(String[] args) {
        //http前缀
      loadUrlResource("http://www.baidu.com");
      //file前缀
        loadUrlResource("file:zzq.txt");
    }
    //访问前缀http、file
    public static void loadUrlResource(String path){
//创建Resource实现类对象UrlResource
        try {
            UrlResource url=new UrlResource(path);
            //获取资源信息
            System.out.println(url.getFilename());
            System.out.println(url.getURL());
            System.out.println(url.getDescription());
            System.out.println(url.getInputStream().read());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

ClassPathResource 访问类路径下资源

ClassPathResource 用来访问类加载路径下的资源,相对于其他的 Resource 实现类,其主要优势是方便访问类加载路径里的资源,尤其对于 Web 应用,ClassPathResource 可自动搜索位于 classes 下的资源文件,无须使用绝对路径访问。

package com.zzq;

import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;

//访问类路径下的资源
public class ClassPathResourceDemo {
    public static  void loadClasspathResource(String path){
        //创建对象ClassPathResource
        ClassPathResource resource=new ClassPathResource(path);
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
        try {
            InputStream in=resource.getInputStream();
            byte[] b=new byte[1024];
            while (in.read(b)!=-1){
                System.out.println(new String(b));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        loadClasspathResource("zzq.txt");
    }
}

FileSystemResource 访问文件系统资源

Spring 提供的 FileSystemResource 类用于访问文件系统资源,使用 FileSystemResource 来访问文件系统资源并没有太大的优势,因为 Java 提供的 File 类也可用于访问文件系统资源。
 

package com.zzq;

import org.springframework.core.io.FileSystemResource;

import java.io.IOException;
import java.io.InputStream;

//访问系统资源
public class FileSystemResourceDemo {
    public static void main(String[] args) {
        loadFileResource("D:\\zzq.txt");
    }
    public  static void loadFileResource(String path){
        FileSystemResource resource=new FileSystemResource(path);
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
        try {
            InputStream in=resource.getInputStream();
            byte[]b=new byte[1024];
            while (in.read()!=-1){
                System.out.println(new String(b));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

ServletContextResource

这是ServletContext资源的Resource实现,它解释相关Web应用程序根目录中的相对路径。它始终支持流(stream)访问和URL访问,但只有在扩展Web应用程序存档且资源实际位于文件系统上时才允许java.io.File访问。无论它是在文件系统上扩展还是直接从JAR或其他地方(如数据库)访问,实际上都依赖于Servlet容器。

InputStreamResource

InputStreamResource 是给定的输入流(InputStream)的Resource实现。它的使用场景在没有特定的资源实现的时候使用(感觉和@Component 的适用场景很相似)。与其他Resource实现相比,这是已打开资源的描述符。 因此,它的isOpen()方法返回true。如果需要将资源描述符保留在某处或者需要多次读取流,请不要使用它。

ByteArrayResource

字节数组的Resource实现类。通过给定的数组创建了一个ByteArrayInputStream。它对于从任何给定的字节数组加载内容非常有用,而无需求助于单次使用的InputStreamResource。
 

提前编译:AOT

AOT概述

JIT与AOT的区别

JIT和AOT 这个名词是指两种不同的编译方式,这两种编译方式的主要区别在于是否在“运行时”进行编译

**(1)JIT, Just-in-time,动态(即时)编译,边运行边编译;**

在程序运行时,根据算法计算出热点代码,然后进行 JIT 实时编译,这种方式吞吐量高,有运行时性能加成,可以跑得更快,并可以做到动态生成代码等,但是相对启动速度较慢,并需要一定时间和调用频率才能触发 JIT 的分层机制。JIT 缺点就是编译需要占用运行时资源,会导致进程卡顿。

**(2)AOT,Ahead Of Time,指运行前编译,预先编译。**

AOT 编译能直接将源代码转化为机器码,内存占用低,启动速度快,可以无需 runtime 运行,直接将 runtime 静态链接至最终的程序中,但是无运行时性能加成,不能根据程序运行情况做进一步的优化,AOT 缺点就是在程序运行前编译会使程序安装的时间增加。                                                           

**简单来讲:**JIT即时编译指的是在程序的运行过程中,将字节码转换为可在硬件上直接运行的机器码,并部署至托管环境中的过程。而 AOT 编译指的则是,在程序运行之前,便将字节码转换为机器码的过程。

```
.java -> .class -> (使用jaotc编译工具) -> .so(程序函数库,即编译好的可以供其他程序使用的代码和数据)
```

![image-20221207113544080](images\spring6\image-20221207113544080.png)

**(3)AOT的优点**

**简单来讲,**Java 虚拟机加载已经预编译成二进制库,可以直接执行。不必等待及时编译器的预热,减少 Java 应用给人带来“第一次运行慢” 的不良体验。

在程序运行前编译,可以避免在运行时的编译性能消耗和内存消耗
可以在程序运行初期就达到最高性能,程序启动速度快
运行产物只有机器码,打包体积小

**AOT的缺点**

由于是静态提前编译,不能根据硬件情况或程序运行情况择优选择机器指令序列,理论峰值性能不如JIT
没有动态能力,同一份产物不能跨平台运行

第一种即时编译 (JIT) 是默认模式,Java Hotspot 虚拟机使用它在运行时将字节码转换为机器码。后者提前编译 (AOT)由新颖的 GraalVM 编译器支持,并允许在构建时将字节码直接静态编译为机器码。

现在正处于云原生,降本增效的时代,Java 相比于 Go、Rust 等其他编程语言非常大的弊端就是启动编译和启动进程非常慢,这对于根据实时计算资源,弹性扩缩容的云原生技术相冲突,Spring6 借助 AOT 技术在运行时内存占用低,启动速度快,逐渐的来满足 Java 在云原生时代的需求,对于大规模使用 Java 应用的商业公司可以考虑尽早调研使用 JDK17,通过云原生技术为公司实现降本增效。

Graalvm

Spring6 支持的 AOT 技术,这个 GraalVM  就是底层的支持,Spring 也对 GraalVM 本机映像提供了一流的支持。GraalVM 是一种高性能 JDK,旨在加速用 Java 和其他 JVM 语言编写的应用程序的执行,同时还为 JavaScript、Python 和许多其他流行语言提供运行时。 GraalVM 提供两种运行 Java 应用程序的方法:在 HotSpot JVM 上使用 Graal 即时 (JIT) 编译器或作为提前 (AOT) 编译的本机可执行文件。 GraalVM 的多语言能力使得在单个应用程序中混合多种编程语言成为可能,同时消除了外语调用成本。GraalVM 向 HotSpot Java 虚拟机添加了一个用 Java 编写的高级即时 (JIT) 优化编译器。

GraalVM 具有以下特性:

(1)一种高级优化编译器,它生成更快、更精简的代码,需要更少的计算资源

(2)AOT 本机图像编译提前将 Java 应用程序编译为本机二进制文件,立即启动,无需预热即可实现最高性能

(3)Polyglot 编程在单个应用程序中利用流行语言的最佳功能和库,无需额外开销

(4)高级工具在 Java 和多种语言中调试、监视、分析和优化资源消耗

总的来说对云原生的要求不算高短期内可以继续使用 2.7.X 的版本和 JDK8,不过 Spring 官方已经对 Spring6 进行了正式版发布。

Native Image

目前业界除了这种在JVM中进行AOT的方案,还有另外一种实现Java AOT的思路,那就是直接摒弃JVM,和C/C++一样通过编译器直接将代码编译成机器代码,然后运行。这无疑是一种直接颠覆Java语言设计的思路,那就是GraalVM Native Image。它通过C语言实现了一个超微缩的运行时组件 —— Substrate VM,基本实现了JVM的各种特性,但足够轻量、可以被轻松内嵌,这就让Java语言和工程摆脱JVM的限制,能够真正意义上实现和C/C++一样的AOT编译。这一方案在经过长时间的优化和积累后,已经拥有非常不错的效果,基本上成为Oracle官方首推的Java AOT解决方案。
Native Image 是一项创新技术,可将 Java 代码编译成独立的本机可执行文件或本机共享库。在构建本机可执行文件期间处理的 Java 字节码包括所有应用程序类、依赖项、第三方依赖库和任何所需的 JDK 类。生成的自包含本机可执行文件特定于不需要 JVM 的每个单独的操作系统和机器体系结构。

常用注解说明**


@NotNull    限制必须不为null
@NotEmpty    只作用于字符串类型,字符串不为空,并且长度不为0
@NotBlank    只作用于字符串类型,字符串不为空,并且trim()后不为空串
@DecimalMax(value)    限制必须为一个不大于指定值的数字
@DecimalMin(value)    限制必须为一个不小于指定值的数字
@Max(value)    限制必须为一个不大于指定值的数字
@Min(value)    限制必须为一个不小于指定值的数字
@Pattern(value)    限制必须符合指定的正则表达式
@Size(max,min)    限制字符长度必须在min到max之间
@Email    验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
 

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃java的羊儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值