Java 之 Spring原理篇

文章目录

  • 1.1框架简介
  • 1.2 Spring 概述
  • 1.3 环境配置
  • 1.4 环境测试 SpringTest
  • 2.1 控制反转(IOC)
    • 2.1.1 ApplicationContext 的主要实现类
    • 2.1.2 ConfigurableApplicationContext
    • 2.1.3 WebApplicationContext
  • 2.2 给 bean 的属性赋值
  • 2.3 给Bean的属性赋值
    • 2.3.1 依赖注入的方式
    • 2.3.2 p命名空间
    • 2.3.3 可以使用的值
  • 2.4 集合属性
    • 2.4.1 数组和List
    • 2.4.2 Map
    • 2.4.3 集合类型的 bean
  • 2.5 FactoryBean
  • 2.6 bean 的作用域
  • 2.7 bean 的生命周期
  • 2.8 引用外部属性文件
    • 2.8.1 直接配置
  • 2.8.2 使用外部的属性文件
  • 2.9 自动装配
    • 2.9.1 自动装配的概念
    • 2.9.2 装配模式
    • 2.9.3 选用建议
  • 2.10 通过注解配置的 bean
    • 2.10.1 概述
    • 2.10.2 使用注解标识组件
    • 2.10.3 扫描组件之包含和排除
    • 2.10.4 基于注解的自动装配
  • 3. AOP前奏--动态代理
  • 4. AOP
  • 4.1 概述
  • 4.2 AOP 术语
  • 4.3 AspectJ(不属于Spring中的内容)
    • 4.3.1 简介
  • 6.以 xml 的方式配置切面
    • 6.1 概述
    • 6.2 配置细节
    • 6.3 声明切入点
    • 6.4 声明通知
  • 7. JdbcTemplate
  • 7.1 概述
  • 7.2 环境准备
  • 7.3 JdbcTemplate 之 batchUpdate(批量增删改查)
  • 7.4 JdbcTemplate 之 query(查询单条数据)
  • 8. 声明事务管理
  • 8.1 事务概述
  • 8.2 Spring 事务管理

1.1框架简介

什么是框架?
框就是把东西框起来,也就是“约束性”
架就是支撑,所以可以把架理解成“支撑性”
所以说框架就是具有约束性的去支撑我们完成各种项目的半成品软件
约束性就是框架会定义一些标准,我们要使用框架,就得遵循这些标准
Spring的底层就是对Java底层的各种功能实现了封装
半成品软件是指,他具体的功能都有,但是缺少业务逻辑
所以框架+业务逻辑就是一个完整的项目

MVC:客户端发送一个请求,由控制层进行处理,由模型层装载并传输数据,并且在视图层进行展示

常见的框架有:
Struts1、Struts2、hibernate、spring、springMVC、mybatis
MVC 框架:struts1、struts2、springMVC
持久层框架:hibernate、mybatis
整合型框架,设计型框架:spring

1.2 Spring 概述

  1. Spring 是一个开源框架

  2. Spring 为简化企业级开发而生,使用Spring, JavaBean 就可以实现很多以前要靠EJB才能实现的功能。同样的功能,在EJB中要通过繁琐的配置和复杂的代码才能够实现,而在Spring中却非常的优雅和简洁。

  3. Spring 是一个IOC(DI)AOP容器框架。

  4. Spring 的优良特性
    非侵入式: 基于Spring开发的应用中的对象可以不依赖于Spring的API

    依赖注入: DI------Dependency Injection,反转控制(IOC)最经典的实现。 DI 和 IOC的关系就是:IOC是一种思想,DI 是 IOC的具体实现

    面向切面编程 : Aspect Oriented Programming-- AOP

    容器: Spring是一个容器,因为它包含并且管理应用对象的生命周期

    组件化: Spring 实现了使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用XML和Java注解组合这些对象。

    一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。

  • 所谓轻量级就是你使用了之后对你原来的技术不会造成影响
  • OOP叫做面向对象编程,因为存在某些缺陷,所以就有了AOP对OOP进行补充
  • Spring里面的组件,指的就是Spring管理的对象
  1. Spring模块
    Java 之 Spring原理篇_第1张图片
  • 在开发中尽量使用包装类型,因为数据库里面的东西会有NULL,而基本数据类型没有

1.3 环境配置

新建一个正常的Java项目导入依赖即可
在pom.xml中导入依赖步骤:

  1. 百度 Spring Maven(直接进也可以:https://mvnrepository.com/search?q=spring)
  2. 然后点开第五个Spring Web MVC
    Java 之 Spring原理篇_第2张图片
    然后下面图片里的随便点开一个即可,我点人数多的那个
    Java 之 Spring原理篇_第3张图片
    点开之后,这个就是我们需要的依赖,导入到pom.xml中:

Java 之 Spring原理篇_第4张图片


<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.2.6.RELEASEversion>
dependency>

复制之后等待一会,导入完之后如下图:
Java 之 Spring原理篇_第5张图片
3. 配置applicationContext.xml
1. 进入Spring官网(https://spring.io/)
2. 点击Projects->Spring Framework
3. 切换到LEARN
Java 之 Spring原理篇_第6张图片 点击Reference DOC—>点击Core
—> 点击左侧的1.2.1,如下图
Java 之 Spring原理篇_第7张图片
然后往下翻一点点,你就可以发现需要配置的内容了,如下图:
Java 之 Spring原理篇_第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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  
        
    bean>

    <bean id="..." class="...">
        
    bean>

    

beans>

  1. 在Resource下面新建一个File名为applicationContext.xml的文件,如下图:
    Java 之 Spring原理篇_第9张图片
    再把上面文档的内容复制进去,完成之后如下图:
    Java 之 Spring原理篇_第10张图片

1.4 环境测试 SpringTest

  1. 在main —> Java 里面新建一个Java文件(com.xp.Hello)

    package com.xp;
    
    public class Hello {
    String name="xp";
    public void hi(){
        System.out.println(name+"你好!");
       }
    }
    
  2. 在前面建的applicationContext.xml文件中如下配置

        <bean id="hello" class="com.xp.Hello">
    	</bean>
    
  3. 要使用@Test注解先导入依赖

            <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.10version>
        dependency>
    
        <dependency>
            <groupId>org.hamcrestgroupId>
            <artifactId>hamcrest-coreartifactId>
            <version>RELEASEversion>
        dependency>
    
  4. 在test—>Java 里面新建一个文件(TestHello)

    import com.xp.Hello;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import 	org.springframework.context.support.ClassPathXmlApplicationContext;
    
    
    public class TestHello {
    @Test
    public void test1() throws Exception {
        /**
         * 1.加载Spring的配置文件
         * 2.取出Bean容器中的实例
         * 3.调用bean方法
         */
        // 1.加载Spring的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2.取出Bean容器中的实例
        Hello hello = (Hello) context.getBean("hello");
        // 3.调用bean方法
        hello.hi();
     }
    
    }
    
    
  5. 测试结果,目录结构:
    Java 之 Spring原理篇_第11张图片
    Java 之 Spring原理篇_第12张图片

2.1 控制反转(IOC)

什么是控制反转:就是本该我们程序员自己管理对象,我们却交给了程序本身,Spring去管理他
依赖注入:注入就是赋值

2.1.1 ApplicationContext 的主要实现类

(1) ClassPathXmlApplicationContext:对应类路径下的xml格式的配置文件,是相对路径
(2) FileSystemXMLApplicationCntext:对应文件系统中的XML格式的配置文件
(3)在初始时就创建的单例的bean ,也可以通过配置的方式指定创建的Bean是多实例的

2.1.2 ConfigurableApplicationContext

(1)是ApplicationContext的子接口,包含 一些扩展方法
(2)refresh() 和 close() 让ApplicationContext 具有启动、关闭和刷新上下文的能力

2.1.3 WebApplicationContext

(1)专门为 WEB 应用准备的,它允许从相对于 WEB 根目录的路径完成初始化工作

2.2 给 bean 的属性赋值

(1)从IOC容器中获取Bean时,除了通过id值获取,还可以通过bean的类型获取。但如果同一个类型的bean在xml文件中配置了多个,则会抛出异常,所以同一个类型的bean在容器中必须是唯一的

HelloWord helloWord=cxt.getBean(HelloWord.class)

(2)也可以使用另外一个重载的方法,同时指定 bean 的 id 值和类型

HelloWord helloWord=cxt.getBean("helloWord",HelloWord.class)

2.3 给Bean的属性赋值

通过属性给对象赋值的时候,name应该填的是对应Set方法后面的名字,比如,SetId() 方法,填的就应该是id。标签也只会通过set方法注入

2.3.1 依赖注入的方式

  1. 通过 bean 的 setXxx() 方法赋值
    <bean id="student" class="com.xp.di">
    	<property name="id" value="123">property>
    	<property name="name" value="张三">property>
    	<property name="age" value="18">property>
    bean>
    
  2. 通过 bean 的构造器赋值
    (1)Spring 自动匹配合适的构造器
    Student 中的构造方法:
    public Student(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

Spring会自动匹配上面的构造方法

	<bean id="student" class="com.xp.di">
		<constructor-arg value="123"/>
		<constructor-arg value="张三"/>
		<constructor-arg value="18"/>
	bean>

如果再多一个构造方法如下,和上面的构造方法数量一样,我们本意是想通过这个方法赋值的,可能就会找到上面的方法,这时候可以通过index + type 区分,如下:

    public Student(Integer id, String name, Double score) {
        this.id = id;
        this.name = name;
        this.score = score;
    }

(2)通过索引值指定参数位置

	<bean id="student" class="com.xp.di">
		<constructor-arg value="123" index=0/>
		<constructor-arg value="张三" index=1/>
		<constructor-arg value="18" index=2/>
	bean>

(3)通过类型区分重载的构造器,如下面的score是Double类型,这样就能区分上面的那种构造方法了

	<bean id="student" class="com.xp.di">
		<constructor-arg value="123" index=0/>
		<constructor-arg value="张三" index=1/>
		<constructor-arg value="90" index=2 type="java.lang.Double"/>
	bean>

2.3.2 p命名空间

为了简化XMl文件的配置,越来越多的XML文件采用属性而非子元素配置信息,Spring 从 2.5 版本开始引入了一个新的p命名空间,可以通过 元素属性的方式配置 Bean 的属性

先配置xml文件,在 beans标签后面添加:

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

xml文件整体如下:
Java 之 Spring原理篇_第13张图片其实这种添加都是有小技巧的,就是xmlns:p="http://www.springframework.org/schema/p复制这句话,把p替换成你想添加的那个就行了

使用 p 命名空间后,基于 xml 的配置方式将进一步简化:


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="Student" p:id="100" p:age="18" p:name="张三">
    bean>
beans>

2.3.3 可以使用的值

(1) 字面量
字面量就是你能够把值写成字符串形式的就叫字面量
可以使用字符串表示的值,可以通过 value 属性或 value 字节点的方式指定,如

<bean id="student" class="com.xp.di">

 	<property name="id" value="123">property>
 	<property name="name" value="张三">property>
 
     <property name="age">
         <value>18value>
     property>
 bean>
 ```
 

(2)**null 值**
```xml
     <bean id="student" class="Student">
     <property name="id" value="100">property>
     <property name="age">
         <null/>
     property>
 bean>

(3)外部已声明的bean ,引用其他的bean
比如在Student中多加一个 Student 属性,Student 是个引用类型,就不能通过 value 直接赋值了,使用应该如下

    <bean id="teacher" class="Teacher">
        <property name="tid" value="1001">property>
        <property name="age" value="30">property>
    bean>

    <bean id="student" class="Student">
        <property name="teacher" ref="teacher"/>
    bean>

(4)给 bean 的级联属性赋值


    <bean id="teacher" class="Teacher">
        <property name="tid" value="1001">property>
        <property name="age" value="30">property>
    bean>

    <bean id="student" class="Student">
        <property name="teacher" ref="teacher">property>
        <property name="teacher.age" value="32">property>
    bean>

值得注意的是:为级联属性赋值,属性先要初始化之后才可以为级联属性赋值,否则会有异常。

(5)内部bean
当 bean 实例仅仅给一个特定的属性使用时,可以将其声明为内部 bean ,内部 bean 声明直接包含在 元素里,不需要设置任何 id 和 name 属性

可以对比上面的写法,你会发现,这里就是把 bean 写到了 property 内部而已

    <bean id="student" class="Student">
        <property name="teacher">
            <bean id="teacher" class="Teacher">
                <property name="tid" value="1001">property>
                <property name="age" value="30">property>
            bean>
        property>
    bean>

2.4 集合属性

注意:在要用到setXxx()的时候,变量的命名一定不能一个小写字母,然后跟大写,比如sList就是错误的命名,在xml配置的时候会报错,如果没有报错的话,说明是 Idea 自动给你改成了大写

2.4.1 数组和List

配置 java.util.List 类型的属性,需要指定 标签,在标签里包含一些元素,这些标签可以通过 指定简单的常量值,通过 指定对其他 Bean 的引用,通过 指定内置 bean 定义,通过 指定空元素。甚至可以内嵌其他集合

数组的定义和 List 一样,都是用 元素

配置 java.util.Set 需要使用 标签,定义的方法与 List 一样
Java 之 Spring原理篇_第14张图片

 <bean id="teacher" class="Teacher">
        
        <property name="cls">
            <list>
                <value>Avalue>
                <value>Bvalue>
                <value>Cvalue>
            list>
        property>

        
        <property name="studentList">
            <list>
                <ref bean="s1"/>
                <ref bean="s2"/>
            list>
        property>

        
        <property name="bookID">
            <array>
                <value>1value>
                <value>2value>
            array>
        property>

    bean>

2.4.2 Map

可以将 Map 的键和值作为 的属性定义:简单常量使用 key 和 value 来定义。bean 可以通过 key-ref 和 value-ref 属性定义
在这里插入图片描述

        <property name="mp">
            <map>
                <entry>
                    <key>
                        <value>Avalue>
                    key>
                    <value>1001value>
                entry>
            map>
        property>

2.4.3 集合类型的 bean

如果只能将集合对称配置在某个 bean 内部,则这个集合的配置将不能重用。我们需要将集合 bean 的配置拿到外面,供其他 bean 引用。

配置集合类型的 bean 需要引入 util 名称空间

    <util:list id="studentList">
        <ref bean="s1"/>
        <ref bean="s2"/>
    util:list>

    <util:list id="cls">
        <value>Avalue>
        <value>Bvalue>
    util:list>

    <bean id="teacher2" class="Teacher">
        <property name="studentList" ref="studentList">property>
        <property name="cls" ref="cls">property>
    bean>

2.5 FactoryBean

Spring 中有两种类型的 bean ,一种是普通 bean,另一种是工厂 bean ,即 FactoryBean。
工厂 bean 跟普通 bean 不同,其返回的对象不是指定类的一个实例。其返回的是该工厂 bean 的 getObject 方法所返回的对象。
工厂 bean 必须实现 org.springframework.beans.factory.FactoryBean 接口。

Car 类:

public class Car {
    private String Brand;
    private Double price;
    ....

MyFactory 类:

import org.springframework.beans.factory.FactoryBean;

public class MyFactory implements FactoryBean<Car> {

    //将创建好的bean返回给IOC容器
    public Car getObject() throws Exception {
        Car car=new Car();
        car.setBrand("奥迪");
        car.setPrice(100000.0);
        return car;
    }

    //返回 bean 类型
    public Class<?> getObjectType() {
        return Car.class;
    }

    //是否是单例模式
    public boolean isSingleton() {
        return false;
    }
}

xml 配置:

 <bean id="factory" class="MyFactory">bean>

测试类

    @Test
    public void testFactory()
    {
        // 1.加载Spring的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("Factory.xml");
        // 2.取出Bean容器中的实例
        Object object=context.getBean("factory");
        // 3.调用bean方法
        System.out.println(object);
    }

在测试类这里要注意一下,在使用 context.getBean(“factory”)的时候,不要指定具体的类型,比如你要指定 context.getBean(“factory”,MyFactory.class)就会报错。如果一定要指定具体的类型,应该这样写:Car car=context.getBean(“factory”,Car.class);
因为工厂 bean 跟普通 bean 不同,其返回的对象不是指定类的一个实例。其返回的是该工厂 bean 的 getObject 方法所返回的对象。

2.6 bean 的作用域

在Spring中,bean作用域用于确定哪种类型的bean实例应该从Spring容器中返回给调用者

在 Spring 中,可以在 元素的 scope 属性里设置 bean 的作用域,以决定这个 bean 是单实例的还是多实例的

默认情况下,Spring 只为每个在 IOC 容器里声明的 bean 创建唯一一个实例,整个 IOC 容器范围内都能共享该实例:所有后续的 getBean() 调用和 bean 引用都将返回这个唯一的 bean 实例。该作用域被称为 singleton ,他是所有 bean 的默认作用域

作用域 描述
singleton 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。
application 限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。

当 bean 的作用域是单例的时候,Spring 会在容器创建的时候就创建 bean 的实例对象。而当 bean 的作用域是多例的时候, IOC 容器会在获取 bean 实例时创建 bean 的实例对象

2.7 bean 的生命周期

(1)Spring IOC 容器可以管理 bean 的生命周期,Spring 允许在 bean 生命周期内特定时间点执行特定任务

(2)Spring IOC 容器对 bean 的生命周期进行管理的过程:
① 通过构造器或工厂方法创建 bean 实例
② 为 bean 属性设置值和其他 bean 的引用
③ 调用 bean 的初始方法
④ 使用 bean
⑤ 当容器关闭时,调用 bean 的销毁方法

(3)在配置 bean 时,通过 init-method 和 destroy-method 属性为 bean 指定初始化和销毁方法

(4)bean 的后置处理器
① bean 的后置处理器允许在调用初始方法前后对 bean 进行额外的处理
② bean 后置处理器对 IOC 容器里的所有 bean 实例逐一处理,而非单一实例。意思就是:设置了后置处理器之后,就对Spring管理的每个对象都有效果,而非单个 Bean 或者某多个 Bean。其典型应用是:检查 bean 属性的正确性或根据特定的标准更改 bean 的属性
③ bean 后置处理器需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor 。在初始化方法被调用前后,Spring 将每个 bean 实例分别传递给上述接口的以下两个方法:

  • public Object postProcessBeforeInitialization(Object bean, String beanName)
  • public Object postProcessAfterInitialization(Object bean, String beanName)
    注意在实现这两个方法的时候,可能在实现接口之后,Idea 不会报红,那就直接点进源码,复制这两个方法出来改改

代码示例如下,记得在xml中配置这个类,只有在xml中配置

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

public class AfterHandler implements BeanPostProcessor {
    //初始化之前,这个返回对象就是经过处理的新的 bean
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Person person=(Person)bean;
        person.setAge(18);
        return bean;
    }

    //初始化之后
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

(5) 添加 bean 后置处理器后 bean 的生命周期
① 通过构造器或工厂方法创建 bean 实例
② 为 bean 的属性设置值和其他 bean 的引用
③ 将 bean 的实例传递给 bean 后置处理器的 postProcessBeforeInitialization 方法
④ 调用 bean 的初始化方法
⑤ 将 bean 实例传递给 bean 后置处理器的 postProcessAfterInitialization 方法
⑥ 使用 bean
⑦ 当容器关闭时调用 bean 的销毁方法

2.8 引用外部属性文件

当 bean 的配置信息逐渐增多时,查找和修改一些 bean 的配置信息就变得愈加困难。这时可以将一部分信息提取到 bean 配置文件的外部,以 properties 格式的属性文件保存起来,同时在 bean 配置文件中引用 properties 属性文件中的内容,从而实现一部分属性值在发生变化时仅修改 properties 属性文件即可。这种技术多用于连接数据库的基本信息的配置

2.8.1 直接配置

要用到一个 druid 这样的jar包,先在Idea导入依赖

    <dependencies>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.12version>
        dependency>
    dependencies>

顺便记得导入mysql数据库连接的依赖,不然运行就会报错,如下

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.38version>
        dependency>

接下来就是在xml文件(我命名为datasource.xml)直接配置的代码,如下

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

测试的代码:

public class MyTest {
    @Test
    public void testDataSource() throws SQLException {
        // 1.加载Spring的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("datasource.xml");
        // 2.取出Bean容器中的实例
        DruidDataSource bean = context.getBean("dataSource",DruidDataSource.class);
        // 3.调用bean方法
        System.out.println(bean.getConnection());
    }
}

2.8.2 使用外部的属性文件

1.创建 properties 属性文件

prop.userName=root
prop.password=123456
prop.url=jdbc:mysql:///test
prop.driveClass=com.mysql.jdbc.Driver

2.指定 properties 属性文件的位置

有两种方式:
① 第一种(现在已经不推荐使用了):

    
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="db.properties">property>
    bean>

② 第二种:

 <context:property-placeholder location="db.properties"/>

3.从 properties 属性文件中引入属性值

Tip:${}为固定语法,从jdbc配置文件中加载值

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driveClass}">property>
        <property name="url" value="${prop.url}">property>
        <property name="username" value="${prop.userName}">property>
        <property name="password" value="${prop.password}">property>
    bean>

测试的代码和上面是一样的

2.9 自动装配

2.9.1 自动装配的概念

(1)手动装配:以 value 或 ref 的方式明确指定属性值都是手动装配的
(2)自动装配:根据指定的装配规则,不需要明确指定,Spring 自动将匹配的属性值注入 bean 中。

自动装配又叫自动注入、自动赋值
自动装配只针对非字面量的属性,也就是需要用 ref 的那些属性

2.9.2 装配模式

(1)根据类型自动装配:将类型匹配的 bean 作为属性注入到另一个 bean 中,若 IOC 容器中有多个与目标 bean 类型一致的 bean ,Spring 将无法判断哪个 bean 最适合该属性,所以不能执行自动装配
(2)根据名称自动装配:必须将目标 bean 的名称与属性名设置的完全相同(反正我测试出来应该是 Set方法后面的名字相同才对,这是老师的笔记)
(3)通过构造器自动装配:当 bean 中存在多个构造器的时候 ,此种自动装配方式将会很复杂,不推荐使用

自动装配语法:在 bean 标签后 autowire=“byType” 或者 autowire=“byName”

byName:根据名称自动装配,只要需要赋值的属性名(其实是Set方法后面的名称)和你xml里面的bean 的 id一样,就可以自动找得到

也就是说,下面的 setCls 中的 Cls 和第二张图的 id 必须一致:
在这里插入图片描述
Java 之 Spring原理篇_第15张图片

byType:通过类型自动装配,和 bean 后面的 class 有关,不仅可以给 type 类型的类赋值,还可以给他的父类或者他所实现的接口赋值(兼容性)

2.9.3 选用建议

相对于使用注解的方式实现的自动装配,在 XML 文档中进行的自动装配略显笨拙,项目中更多的使用注解的方式实现

通过xml自动注入有个很大的缺陷就是,当设置 autowire属性,会作用于该bean中所有的非字面量属性,因此 ByType 和 ByName 两个都不建议使用

Tip:Spring 里管理的对象都是针对类的,不能针对接口和抽象类

2.10 通过注解配置的 bean

2.10.1 概述

相对于 XML 而言,通过注解的方式配置 bean 更加简洁优雅,而且和 MVC 组件化开发的理念十分契合,是开发中常用的方式

2.10.2 使用注解标识组件

(1)普通组件:@Component
标识一个受 Spring IOC 容器管理的组件
(2)持久化组件:@Repository
标识一个受 Spring IOC 容器管理的持久化组件
(3)业务逻辑层组价::@Service
标识一个受 Spring IOC 容器管理的业务逻辑层组件
(4)表述层控制组件:@Controller
表述一个受 Spring IOC 容器管理的表述层控制器组件
(5)组件命名规则:
① 默认情况下:会自动在 Spring 的配置文件中生成相对应的 bean ,这些 bean 的 id 会以类的首字母小写为值
② 使用组件注解的 value 属性指定 bean 的 id
注意:事实上 Spring 并没有能力识别一个组件到底是不是他所标记的类型,也就是说上面的四种组件,只是为了业务逻辑上的区分,见名知意,功能上都是一样的,甚至可以互相交换着写(业务逻辑允许的话)

Tip:
Spring 里面的组件只有对象,所以看到组件可以直接理解为对象

我们平时见到的注解要么加在类上的,要么加在方法上的,要么加在属性上的

控制层接收请求,再调用业务层处理业务逻辑,再调用 Dao 来实现数据持久化,把最终持久化的结果返回给 Service ,再返回给控制层,最终响应到客户端

代码演示:

1.先在xml文件里加上这句话,扫描整个包,因为一个项目如果 bean 很多的话,一个个配置很麻烦

如果有多个包需要扫描的时候,可以逗号隔开

<context:component-scan base-package="com.xp.ioc.userMethod">context:component-scan>

2.然后在类上面加上注解,例如,ServiceDao 就加 @Service 注解,如下

@Service
public class ServiceDao {
    public ServiceDao() {
        System.out.println("ServiceDao 层已经创建");
    }
}

然后测试就行了

2.10.3 扫描组件之包含和排除

:扫描组件,对设置的宝下面的类进行扫描,会将加上注解的类作为 Spring 的组件进行加载

  • base-package 属性指定一个需要扫描的基类包,Spring 容器会扫描这个基类包里及其子包中的所有类

  • 当需要扫描多个包的时候,可以使用逗号隔开

  • 如果仅希望扫描特定的类而非基包下的所有类可以使用 resource - pattern 属性过滤特定的类,示例:
    在这里插入图片描述还可以使用子标签:
    :子节点要包含的目标类
    注意:在使用包含的时候,一定要设置 use-default-filters="false"

    :子节点要排除在外的目标类
    注意: 在使用排除时,一定要设置use-default-filters="false" ,或者不加这句话也可以(这句话的意思是,将默认的过滤也即扫描包下所有的类打开)

    <context:component-scan base-package="com.xp.ioc.userMethod" use-default-filters="false">
        
        
        
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        
        <context:exclude-filter type="assignable" expression="com.xp.ioc.userMethod.ServiceDao"/>
    context:component-scan>
    

注意:一个 中可以出现多个 include ,也可以同时出现多个 exclude ,但是两个不能同时出现

注:我自己测出来,两个同时存在也不会报错,功能能否实现取决于use-default-filters=true 还是 false,如果是 true 的话,只有排除有作用,否则,是包含发挥作用

IOC:工厂模式
AOP:代理模式

context:include-filter 和 context:exclude-filter 子节点支持多种类型的过滤表达式:
Java 之 Spring原理篇_第16张图片

2.10.4 基于注解的自动装配

context:component-scan 元素还会自动注册 AutowiredAnnotationBeanPostProcessor 实例, 该实例可以自动装配具有 @Autowired@Resource@Inject注解的属性.

@Autowired 注解自动装配具有兼容类型的单个 Bean属性
构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired 注解
默认情况下, 所有使用 @Autowired 注解的属性都需要被设置. 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Autowired 注解的 required 属性为 false
默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时, 通过类型的自动装配将无法工作。此时可以在 @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 @Qualifiter 已指定注入 Bean 的名称
@Autowired 注解也可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配.
@Autowired 注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean.
@Autowired 注解用在 java.util.Map 上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值

Spring 还支持 @Resource 和 @Inject 注解,这两个注解和 @Autowired 注解的功用类似
@Resource 注解要求提供一个 Bean 名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为 Bean 的名称
@Inject 和 @Autowired 注解一样也是按类型匹配注入的 Bean, 但没有 reqired 属性
建议使用 @Autowired 注解

3. AOP前奏–动态代理

代理模式里的三个角色:原始对象、代理对象、目标对象
或者说两个对象也行:代理对象、目标对象

动态代理是什么:就是不管我的目标对象是什么,我都可以通过这样一个类去生成相对应的代理对象帮助我去完成功能

代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.

Java 里面所有的动态代理类全部都继承于 Proxy 这个类

4. AOP

面向对象的宗旨就是我们在实现功能的时候,不需要直接实现功能,只要能找到能实现功能的对象即可

纵向继承、横向抽取

面向对象:往功能实现里面加代码
AOP:把某些代码抽取出来,抽取到另外一个类中,然后再作用到这个类上
切面:来存取公共功能的类就叫做切面

4.1 概述

AOP(Aspect-Oriented Programming, 面向切面编程):是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充。
AOP 的主要编程对象是切面(aspect),而切面模块化横切关注点
在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。

AOP 的好处:
① 每个事物逻辑位于一个位置,代码不分散, 便于维护和升级
② 业务模块更简洁, 只包含核心业务代码。
③ AOP 图解
Java 之 Spring原理篇_第17张图片

验证参数、前置日志、后置日志就是横切关注点、他们凑在一起就是一个切面

4.2 AOP 术语

通知:在方法里面的时候叫横切关注点,在切面里面的时候就叫通知
连接点:功能执行时的各个位置
切入点:就是使用切面的一种条件,就是我们什么时候才能够将切面作用到目标对象中
切入点表达式:有了这个表达式,才知道作用于谁

切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice): 切面必须要完成的工作
目标(Target): 被通知的对象
代理(Proxy): 向目标对象应用通知之后创建的对象
连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
Java 之 Spring原理篇_第18张图片

4.3 AspectJ(不属于Spring中的内容)

4.3.1 简介

Java 社区里最完整最流行的 AOP 框架,也就是说Spring 中的 AOP 是一种思想,AspectJ 是这种思想的具体实现
在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP

finally 算是在方法之后

6.以 xml 的方式配置切面

6.1 概述

除了使用 AspectJ 注解声明切面,Spring 也支持在 bean 配置文件中声明切面,这种声明是通过 aop 名称空间的 xml 元素完成的

正常情况下,基于注解的声明要优于基于 xml 色声明,通过 AspectJ 注解,切面可以与 AspectJ 兼容,而基于 xml 的配置则是Spring 专有的。由于 AspectJ 得到越来越多的 AOP 框架的支持,所以注解风格编写的切面会有更多的重用机会

6.2 配置细节

在 bean 配置文件中,所有的 Spring AOP 配置都必须定义在 元素内部。对于每个切面而言,都要创建一个 元素为具体的切面实现引用后端 bean 实例。
切面 bean 必须有个标识符,供 元素引用

声明切面的实例代码:
Java 之 Spring原理篇_第19张图片

6.3 声明切入点

切入点使用 aop:pointcut 元素声明
切入点必须定义在 aop:aspect 元素下, 或者直接定义在 aop:config 元素下。
① 定义在 aop:aspect 元素下: 只对当前切面有效
② 定义在 aop:config 元素下: 对所有切面都有效
基于 XML 的 AOP 配置不允许在切入点表达式中用名称引用其他切入点。

声明切入点的示例代码:
Java 之 Spring原理篇_第20张图片

6.4 声明通知

在 aop Schema 中,每种通知类型都对应一个特定的 XML 元素。
通知元素需要使用 来引用切入点,或用 直接嵌入切入点表达式。method 属性指定切面类中通知方法的名称。

声明通知示例的代码:
Java 之 Spring原理篇_第21张图片
声明引入:

可以利用 aop:declare-parents 元素在切面内部声明引入
Java 之 Spring原理篇_第22张图片

我自己写的代码:

代码跑了一个早上没跑出来,报错,少了aspectj.jar包。想用Idea直接导入依赖,发现并不能够。难受,我用的Spring的版本是5.2.6的,我去仓库(地址:https://mvnrepository.com/artifact/org.springframework/spring-aop)看了这个版本,发现这个版本没有对应的aspectj.jar包,然后我就把Spring换成了5.1.1的,这个版本应该有对应的jar包了,是1.9.1,结果还是不行,百度一下,然后发现也许Idea不支持这个 jar 包吗,因为有搜到这个插件的配置使用,反正搜到的那个帖子,里面说的插件,我的Idea也没有,我的应该是专业版的Idea,既然不行,那就算了吧,就写写代码,加强加强记忆

xml 文件里面的配置

    <context:component-scan base-package="com.xp.spring.aopxml">context:component-scan>

    <aop:config>
        <aop:aspect ref="myLogger">
            
            <aop:pointcut id="cut" expression="execution(* com.xp.spring.aopxml.*.*(..)))"/>
            <aop:before method="before" pointcut-ref="cut">aop:before>
        aop:aspect>
    aop:config>

我的 MyLogger 类

@Component
public class MyLogger {
    public void  before(){
        System.out.println("前置通知");
    }
}

报错:

Caused by: java.lang.ClassNotFoundException: org.aspectj.lang.JoinPoint
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 61 more

7. JdbcTemplate

7.1 概述

为了使 JDBC 更易于使用,Spring 在 JDBC API 上定义了一个抽象层,以此建立一个 JDBC 存取框架

作为 Spring JDBC 框架的核心,JDBC 模板的设计目标是为不同类型的 JDBC 操作提供模板方法,通过这种方式,可以再尽可能暴力流灵活性的情况下,将数据库存取的工作量降到最低

可以将 Spring 的 JdbcTemplate 看作是一个小型的轻量的持久化层框架,和我们之前使用过的 DBUtils 风格非常接近

7.2 环境准备

JdbcTemplate 的存在仅仅是为了减少 Spring 操作数据库的代码量

基于之前的配置,先在 pom.xml 中导入依赖,这样就可以直接使用类org.springframework.jdbc.core.JdbcTemplate:

        
        <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-jdbcartifactId>
        <version>5.2.6.RELEASEversion>
        dependency>

再在application.xml里面配置:

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

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource">property>
    bean>

测试类:

    @Test
    public void test01(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("jdbc.xml");
        JdbcTemplate jdbcTemplate =ac.getBean("jdbcTemplate",JdbcTemplate.class);
        String  sql="insert into emp values(null,?,?)";
        jdbcTemplate.update(sql,"张三",28);
    }

如果测试有报错,大概意思是 bean 注入错误的话,建议使用自己的仓库就好了
Java 之 Spring原理篇_第23张图片

7.3 JdbcTemplate 之 batchUpdate(批量增删改查)

    @Test
    public void test02(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("jdbc.xml");
        JdbcTemplate jdbcTemplate =ac.getBean("jdbcTemplate",JdbcTemplate.class);
        String  sql="insert into emp values(null,?,?)";
        //jdbcTemplate.update(sql,"张三",28);
        List<Object[]> list=new ArrayList<Object[]>();
        for(int i=1;i<=3;i++)
        {
            Object []objects=new Object[]{i,i};
            list.add(objects);
        }
        jdbcTemplate.batchUpdate(sql,  list);
    }

Tip:在使用 preStatement 的时候,通配符是通过 setString 的方式加到数据库的,而 setString 方法会自动给 属性加上单引号,所以用 in 什么去匹配的时候就不对,相当于比较了 123=‘123’,所以,in 和 like 都是不可以用通配符去赋值的

7.4 JdbcTemplate 之 query(查询单条数据)

查询多行:
Java 之 Spring原理篇_第24张图片
查询单行:
Java 之 Spring原理篇_第25张图片

8. 声明事务管理

8.1 事务概述

事务管理是企业级应用程序开发中必不可少的技术, 用来确保数据的完整性和一致性.
事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用
事务的四个关键属性(ACID):
原子性(atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.
一致性(consistency): 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.
隔离性(isolation): 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.
持久性(durability): 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中

8.2 Spring 事务管理

作为企业级应用程序框架, Spring 在不同的事务管理 API 之上定义了一个抽象层. 而应用程序开发人员不必了解底层的事务管理 API, 就可以使用 Spring 的事务管理机制.
Spring 既支持编程式事务管理, 也支持声明式的事务管理.
编程式事务管理: 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚. 在编程式管理事务时, 必须在每个事务操作中包含额外的事务管理代码.
声明式事务管理: 大多数情况下比编程式事务管理更好用. 它将事务管理代码从业务方法中分离出来, 以声明的方式来实现事务管理. 事务管理作为一种横切关注点, 可以通过 AOP 方法模块化. Spring 通过 Spring AOP 框架支持声明式事务管理.

Spring 从不同的事务管理 API 中抽象了一整套的事务机制. 开发人员不必了解底层的事务 API, 就可以利用这些事务机制. 有了这些事务机制, 事务管理代码就能独立于特定的事务技术了.
Spring 的核心事务管理抽象是
在这里插入图片描述
它为事务管理封装了一组独立于技术的方法. 无论使用 Spring 的哪种事务管理策略(编程式或声明式), 事务管理器都是必须的

可重复读的意思就是:自己选中读的数据,别人不能进行修改了,但是表里面的其他数据,别人还是可以操作的,所以在自己读的时候,别人又往表里面新增了几条数据,每次结果不一样,就像出现了幻觉一样,所以叫幻读

事务的隔离级别一般用在并发的时候

脏读和不可重复读都是针对一个表里面一条数据的一个字段来说的
而幻读是针对一个表里面的一条数据来说的

感觉从 AOP 开始听的有点迷糊,没怎么做笔记,后面应该从AOP开始再看一遍

你可能感兴趣的:(Java 之 Spring原理篇)