Spring

Ioc容器

1.1 Ioc的功能及原理

ioc容器的功能主要有两个,反转控制和依赖注入

ioc的原理:工厂模式+反射+xml

1.2 IOC容器在Spring中的实现

IOC容器读取bean的实例之前,需要先将IOC容器本身实例化

有两种方法创建ioc容器

1、BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。

2、ApplicationContext :beanFactory的子接口,提供了更多高级特性。(常用)

//iocContainer为IOC容器的实例
//1.创建IOC容器对象
        ApplicationContext iocContainer = new ClassPathXmlApplicationContext("getting-start.xml");

//2.根据id值获取bean实例对象
        Student student = (Student) iocContainer.getbean("student");

image-20210213214112803

ApplicationContext的主要实现类

  1. ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件(常用)
  2. FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件

ConfigurableApplicationContext 接口

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

WebApplicationContext 接口

WebApplicationContext,是继承于ApplicationContext的一个接口,扩展了ApplicationContext,是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化

1.3 bean对象的获取

在容器的顶级接口 beanFactory接口中,定义了如下几个方法,用于获取 bean 实例

1、Object getbean(String name) throws beansException;:通过 bean name 获取 bean 实例(常用
2、 T getBean(Class requiredType) throws BeansException;:通过 bean class 获取 bean 实例
3、 T getBean(String name, Class requiredType) throws BeansException;:通过 bean name 和 bean class 获取 bean 实例

1.4 bean属性的赋值(注入)

1、通过 bean 的 setter 方法注入属性

​ 通过 标签指定属性名,Spring 会帮我们找到该属性对应的 setter 方法,注入其属性值

<bean id="student" class="com.oneby.entity.Student">
    <property name="stuId" value="007"/>
    <property name="stuName" value="Oneby"/>
</bean>

2、通过构造器注入属性值

通过 constructor-arg 标签为对象的属性赋值,通过 name 指定属性名,value 指定属性值(bean中需提供有参构造

<bean id="student" class="com.oneby.entity.Student">
    <constructor-arg name="stuId" value="1" />
    <constructor-arg name="stuName" value="Oneby" />
</bean>

3、通过级联属性赋值

Computer 类(get,set,tostring和构造器 都没写进去,影响观看)

public class Computer {

    String computerId;
    String computerName;
 }

Student 类(get,set,tostring和构造器 都没写进去,影响观看)

public class Student {

    private Integer stuId;
    private String stuName;
    private Computer computer;
}

xml

<!-- 演示级联属性赋值 -->
<bean id="student" class="com.oneby.entity.Student">
    <property name="computer" ref="computer"/>
    <!-- 设置级联属性(了解) -->
    <property name="computer.computerId" value="233"/>
    <property name="computer.computerName" value="HP"/>
</bean>

<bean id="computer" class="com.oneby.entity.Computer"/>

运行结果

image-20210213232201626

1.5 属性赋值的类型

1、字面量

  1. 可以使用字符串表示的值,可以通过value属性或value子节点的方式指定
  2. 基本数据类型及其封装类、String等类型都可以采取字面值注入的方式
  3. 若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来
<!-- 演示字面量的使用 -->
<bean id="student" class="com.oneby.entity.Student">
    <property name="stuId" value="233"/>
    <property name="stuName" value="Oneby"/>
</bean>

2、null 值

通过 <null/> 标签将引用类型字段的值设置为 null

<!-- 演示 null 值的使用-->
<bean id="student" class="com.oneby.entity.Student">
    <property name="stuId" value="233"/>
    <!-- 将 stuName 字段的值设置为 null -->
    <property name="stuName">
        <null/>                                      《-----重点在这里----》》
    </property>
    <!-- 将 computer 字段的值设置为 null -->
    <property name="computer">
        <null/>
    </property>
</bean>

3、引用外部 bean

通过 <property> 标签中的 ref 属性引用外部 bean

<!-- 引用外部声明的 bean -->
<bean id="student" class="com.oneby.entity.Student">
    <property name="stuId" value="233"/>
    <property name="stuName" value="Oneby"/>
    <!-- 通过 ref 属性引用外部的 bean -->
    <property name="computer" ref="computer"/>              《《-----重点在这里----》》
</bean>
<!-- 外部 bean -->
<bean id="computer" class="com.oneby.entity.Computer">
    <property name="computerId" value="255"/>
    <property name="computerName" value="HP"/>
</bean>

4、引用内部 bean

当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在**<property><constructor-arg>**元素里,不需要设置任何idname属性,内部bean不能使用在任何其他地方

<!-- 引用内部声明的 bean -->
<bean id="student" class="com.oneby.entity.Student">
    <property name="stuId" value="233"/>
    <property name="stuName" value="Oneby"/>
    <property name="computer">
        <!-- 通过 <bean> 标签定义内部 bean -->
        <bean class="com.oneby.entity.Computer">
            <property name="computerId" value="255"/>
            <property name="computerName" value="HP"/>
        </bean>
    </property>
</bean>

1.6 对集合属性赋值

在Spring中可以通过一组内置的XML标签来配置集合属性,比如:<array><list><set><map><props>,并且可以用过引入 util 名称空间来提取集合类型的 bean

1、数组

通过 <array> 标签定义数组集合,并且可以通过<value>指定简单的常量值,通过<ref>指定对其他bean的引用。通过<bean>指定内置bean定义。通过<null/>指定空元素,甚至可以内嵌其他集合。

<!-- 演示数组的赋值 -->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
    <property name="array">
        <array>
            <value>Oneby</value>
            <value>Heygo</value>
        </array>
    </property>
</bean>

2、List

通过 <list> 标签定义数组集合,并且可以通过<value>指定简单的常量值,通过<ref>指定对其他bean的引用。通过<bean>指定内置bean定义。通过<null/>指定空元素,甚至可以内嵌其他集合。

<!-- 演示 List 的赋值 -->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
    <property name="list">
        <list>
            <value>Oneby</value>
            <value>Heygo</value>
        </list>
    </property>
</bean>

3、Set

通过 <set> 标签定义数组集合,并且可以通过<value>指定简单的常量值,通过<ref>指定对其他bean的引用。通过<bean>指定内置bean定义。通过<null/>指定空元素,甚至可以内嵌其他集合。

<!-- 演示 Set 的赋值-->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
    <property name="set">
        <set>
            <value>Oneby</value>
            <value>Heygo</value>
        </set>
    </property>
</bean>

4、Map

Java.util.Map通过标签定义,标签里可以使用多个作为子标签,每个中包含一个键和一个值。因为键和值的类型没有限制,所以可以自由地为它们指定、、或元素。因此对于常量型的key-value键值对可以使用key和value来定义;bean引用通过key-ref和value-ref属性定义。

<!-- 演示 Map 的赋值-->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
    <property name="map">
        <map>
            <entry key="name" value="Oneby"></entry>
            <entry key="hobby" value="code"></entry>
        </map>
    </property>
</bean>

5、Properties

使用<props>定义java.util.Properties,该标签使用多个<prop>作为子标签,每个<prop>标签中定义keyvalue

<!-- 演示 properties 的赋值-->
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
    <property name="properties">
        <props>
            <prop key="name">Oneby</prop>
            <prop key="hobby">code</prop>
        </props>
    </property>
</bean>

6、把集合注入部分提取出来**

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

将 beans 名称空间对应的这两项 xmlns:util="http://www.springframework.org/schema/util 和 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd,将 beans 全部替换为 util 就行啦~

image-20210215214206350

使用 <util> 标签完成对集合类型 bean 的抽取,并为其设置 id 属性,方便其他地方进行引用

<!-- 集合类型的 bean-->
<util:list id="list">
    <value>Oneby</value>
    <value>Heygo</value>
</util:list>
<bean id="collectionExample" class="com.oneby.entity.CollectionExample">
    <property name="list" ref="list"/>
</bean>

1.7 创建bean

1、Spring 有两种类型 bean**,一种普通** bean**,另外一种工厂** bean**(FactoryBean)**

2、普通** bean**:在配置文件中定义** bean 类型就是返回类型

3、工厂bean:在配置文件定义 bean 类型可以和返回类型不一样

第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean

第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型

1、FactoryBean 接口**(工厂bean)

我们一般通过实现 FactoryBean 接口,并重写其中的方法来获得工厂类

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

FactoryBean 接口

FactoryBean 接口中有如下三个方法,getObject() 方法负责将创建好的 bean 实例返回给 IOC 容器;getObjectType() 方法负责返回工厂生产的 bean 类型;isSingleton() 方法用于指示该 bean 实例是否为单例,默认是单例 bean

image-20210215220059978

演示 FactoryBean 接口的使用

创建 StudentFactory 类,该类实现了 FactoryBean 接口,并重写了其中的 getObject()getObjectType() 方法

public class StudentFactory implements FactoryBean<Student> {
    @Override
    public Student getObject() throws Exception {
        return new Student(233,"Oneby");
    }

    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }
}

在 Spring 配置文件中使用 StudentFactory 工厂创建 Student 对象

<bean id="student" class="com.oneby.entity.StudentFactory"/>

2、普通bean

public class Books {
    private int bookID;
    private String bookName;
    private String bookCounts;
    private String detail;

}
<bean id="book" class="com.oneby.dao.Books"/>

1.7 bean 的高级配置

1、bean 的作用域

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

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

两种 bean 的作用域

singleton:在Spring IOC容器中仅存在一个bean实例,bean以单实例的方式存在

prototype:每次调用getBean()时都会返回一个新的实例

<!-- 演示 单例bean 的作用域 -->
<bean id="student" class="com.oneby.entity.Student" scope="singleton">
    <property name="stuId" value="233" />
    <property name="stuName" value="Oneby" />
</bean>
<!-- 演示 多例bean 的作用域 -->
<bean id="student" class="com.oneby.entity.Student" scope="prototype">
    <property name="stuId" value="233" />
    <property name="stuName" value="Oneby" />
</bean>

1.8 bean 的生命周期

1、Spring IOC 管理下的 bean 生命周期

Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务,Spring IOC容器对bean的生命周期进行管理的过程:

通过构造器或工厂方法创建bean实例
为bean的属性设置值和对其他bean的引用
调用bean的初始化方法
bean可以使用了
当容器关闭时,调用bean的销毁方法
注:在配置bean时,通过init-method和destroy-method属性为bean指定初始化和销毁方法

代码演示

创建 Order 类,用于演示 bean 的生命周期

public class Order {

    private String name;

    public Order() {
        System.out.println("第一步:执行无参数构造创建 bean 实例");
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步:调用 setter 方法为属性赋值");
    }

    //  init-method 初始化方法
    public void initMethod(){
        System.out.println("第三步:执行 init-method 初始化方法");
    }

    //  destroy-method 销毁方法
    public void destroyMethod(){
        System.out.println("第五步:执行 destroy-method 初销毁方法");
    }

}

<bean> 标签中指定 order 对象的 init-method 方法(初始化方法)和 destroy-method 方法(销毁方法)

<!-- 演示 bean 的生命周期 -->
<bean id="order" class="com.oneby.entity.Order"
      init-method="initMethod" destroy-method="destroyMethod">
    <property name="name" value="iPad" />
</bean>

测试代码:记得要关闭 IOC 容器才会执行 destroy-method 方法,并且接口类型需要上升到 ConfigurableApplicationContext 才会提供 close() 方法

public class SpringTest {

    @Test
    public void test() {
        //1.创建IOC容器对象(使用的是ConfigurableApplicationContex)
        ConfigurableApplicationContext iocContainer =
                new ClassPathXmlApplicationContext("spring-advance-config.xml");

        //2.根据id值获取bean实例对象
        Order order = (Order) iocContainer.getBean("order");

        //3.打印bean
        System.out.println("第四步:使用创建好的 order 对象" + order);

        //4.关闭IOC容器
        iocContainer.close();
    }

}

2、添加 BeanPostProcessor 后的 bean 生命周期

1、bean的后置处理器(添加之后所有bean都会执行后置处理器)

1、bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
2、bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
3、bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
4、postProcessBeforeInitialization(Object, String)
5、postProcessAfterInitialization(Object, String)

2、添加bean后置处理器后bean的生命周期

1、通过构造器或工厂方法创建bean实例
2、为bean的属性设置值和对其他bean的引用
3、将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
4、调用bean的初始化方法
5、将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
6、bean可以使用了
7、当容器关闭时调用bean的销毁方法

代码演示

创建 MyBeanPost 类,实现接口 BeanPostProcessor 类,并重写其中的 postProcessBeforeInitializationpostProcessAfterInitialization 方法

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第三步:执行 postProcessBeforeInitialization 方法");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第五步:执行 postProcessAfterInitialization 方法");
        return bean;
    }
}

配置文件:在配置文件中实例化我们自定义的 MyBeanPost 后置处理器

<!-- 演示 bean 的生命周期 -->
<bean id="order" class="com.oneby.entity.Order"
      init-method="initMethod" destroy-method="destroyMethod">
    <property name="name" value="iPad" />
</bean>

<!-- 加上 BeanPostProcessor 后的生命周期 -->
<bean id="myBeanPost" class="com.oneby.config.MyBeanPost"/>

3、读取 properties 文件

1、引用外部 properties 配置文件单独存放数据库配置信息

引入 context 名称空间

将 xmlns=“http://www.springframework.org/schema/beans” 和 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 复制,并将出现 beans 的位置全部替换为 context
img

代码测试

在类路径下创建 jdbc.properties 数据库配置文件

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

通过 <context:property-placeholder> 标签中的 location 来制定配置文件的路径,classpath: 表示该配置文件位于类路径下,并通过 ${prop.userName} 的方式来取出配置文件中的属性值

<!-- 引用外部属性文件来配置数据库连接池 -->
<!-- 指定 properties 属性文件的位置,classpath:xxx 表示属性文件位于类路径下 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 从properties属性文件中引入属性值 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="${prop.userName}"/>
    <property name="password" value="${prop.password}"/>
    <property name="url" value="${prop.url}"/>
    <property name="driverClassName" value="${prop.driverClass}"/>
</bean>

1。9 bean 的自动装配

1、自动装配的概念

[1]手动装配:以value或ref的方式明确指定属性值都是手动装配。

[2]自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。

2、装配模式

[1]根据类型自动装配(byType):将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配

[2]根据名称自动装配(byName):必须将目标bean的名称和属性名设置的完全相同

[3]通过构造器自动装配(constructor):当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。

3、代码举例

通过 <bean> 标签的 autowire="byType",指定 student 对象中的 bean 按照类型进行装配

<!-- 自动装配 -->
<bean id="student" class="com.oneby.entity.Student" autowire="byType">
    <property name="stuId" value="233"/>
    <property name="stuName" value="Oneby"/>
</bean>

<bean id="computer" class="com.oneby.entity.Computer">
    <property name="computerId" value="666"/>
    <property name="computerName" value="HP"/>
</bean>

image-20210222105512783

4、配置信息的继承(bean中使用 标签)

Spring允许继承bean的配置,被继承的bean称为父bean,继承这个父bean的bean称为子bean

子bean从父bean中继承配置,包括bean的属性配置,子bean也可以覆盖从父bean继承过来的配置

不使用继承配置 bean (两位社畜的 companyhobbyprofession 属性的值均相同,这样配置显得有些冗余)

<!-- 不使用继承配置 bean -->
<bean id="corporateSlave1" class="com.oneby.entity.CorporateSlave">
    <property name="id" value="1"/>
    <property name="name" value="Oneby"/>
    <!-- 以下都是重复的属性 -->
    <property name="company" value="OneTech"/>
    <property name="hobby" value="Code"/>
    <property name="profession" value="Programer"/>
</bean>

<bean id="corporateSlave2" class="com.oneby.entity.CorporateSlave">
    <property name="id" value="2"/>
    <property name="name" value="Heygo"/>
    <!-- 以下都是重复的属性 -->
    <property name="company" value="OneTech"/>
    <property name="hobby" value="Code"/>
    <property name="profession" value="Programer"/>
</bean>

使用配置信息的继承配置 bean

配置信息的继承:Heygo 的配置信息继承于 Oneby(指定 bean 的 parent 属性),自然就获得了 Oneby 社畜的所有配置信息,只需要重写自己不一样的配置信息即可

<!-- 演示配置信息的继承 -->
<bean id="corporateSlave1" class="com.oneby.entity.CorporateSlave">
    <property name="id" value="1"/>
    <property name="name" value="Oneby"/>
    <!-- 以下都是重复的属性 -->
    <property name="company" value="OneTech"/>
    <property name="hobby" value="Code"/>
    <property name="profession" value="Programer"/>
</bean>

<bean id="corporateSlave2" parent="corporateSlave1">
    <!-- 重写不同值的属性即可 -->
    <property name="id" value="2"/>
    <property name="name" value="Heygo"/>
</bean>

注意事项:配置信息的继承

父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置<bean>abstract 属性为true,这样Spring将不会实例化这个bean

1.10 bean 之间的依赖

有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Student对象的时候必须创建Computer。这里需要注意的是依赖关系不等于引用关系,Student即使依赖Computer也可以不引用它

<!-- 演示 bean 之间的依赖 -->
<bean id="student" class="com.oneby.entity.Student" depends-on="computer">
    <property name="stuId" value="233"/>
    <property name="stuName" value="Oneby"/>
</bean>

<bean id="computer" class="com.oneby.entity.Computer"/>

1、11 注解方式配置 bean

1、用于标识 bean 的四个注解

①普通组件:@Component,用于标识一个受Spring IOC容器管理的组件

②持久化层组件:@Respository,用于标识一个受Spring IOC容器管理的持久化层组件

③业务逻辑层组件:@Service,用于标识一个受Spring IOC容器管理的业务逻辑层组件

④表述层控制器组件:@Controller,用于标识一个受Spring IOC容器管理的表述层控制器组件

注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。

2、组件命名规则

[1]默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id

**[2]使用组件注解的value属性指定bean的id**

3、扫描组件

1、引入 AOP 依赖

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

引入 context 名称空间

将 xmlns=“http://www.springframework.org/schema/beans” 和 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 复制,并将出现 beans 的位置全部替换为 context

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

开启组件扫描(开启组件扫描,并指明要扫描的包路径)

<!-- 开启组件扫描 -->
<context:component-scan base-package="com.oneby"/>

组件扫描的详细说明

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

[2]当需要扫描多个包时可以使用逗号分隔。

[3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:

<context:component-scan 
	base-package="com.oneby" 
	resource-pattern="autowire/*.class"/>

包含与排除

1、context:include-filter>子节点表示要包含的目标类。注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
2、context:exclude-filter>子节点表示要排除在外的目标类
3、component-scan下可以拥有若干个include-filter和exclude-filter子节点

<!--示例1:use-default-filters="false" 表示现在不使用默认filter,自己配置filter 
	context:include-filter用于设置扫描哪些内容(这里配置只扫描 Controller 注解) -->
<context:component-scan base-package="com.oneby" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> 

<!--示例2:下面配置扫描包所有内容 context:exclude-filter: 设置哪些内容不进行扫描(这里排除 Controller 注解) -->
<context:component-scan base-package="com.oneby">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

1.12 依赖注入的实现

(1)@Autowired:根据属性类型进行自动装配

第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解

第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解

@Service
public class UserService {
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add.......");
userDao.add();
} }

(2)@Qualifier:根据名称进行注入

这个@Qualifier 注解的使用,和上面@Autowired 一起使用

@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;

(3)@Resource:可以根据类型注入,可以根据名称注入

@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;

(4)@Value:注入普通类型属性

@Value(value = "abc")
private String name;

6、完全注解开发

(1)创建配置类,替代 xml 配置文件

@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
}

AOP

1、什么是 AOP(面向切面编程)

不通过修改源代码方式,在主干功能里面添加新功能

2、底层原理(动态代理)

(1)有两种情况动态代理

第一种 有接口情况,使用 JDK 动态代理

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

第二种 没有接口情况,使用 CGLIB 动态代理

⚫ 创建子类的代理对象,增强类的方法

3、AOP术语

1**、连接点**

类里面哪些方法可以被增强,这些方法称为连接点

2**、切入点**

实际被真正增强的方法,称为切入点

3**、通知**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9jcz5SDL-1619174024022)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312112256294.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-la082Mxw-1619174024023)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312112230771.png)]

4、切面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sMZTH6Ub-1619174024025)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312112138000.png)]

4、AOP 操作(准备工作)

1、Spring框架一般都是基于AspectJ 实现 AOP 操作

(1)AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使

用,进行 AOP 操作

2、基 AspectJ 实现 AOP 操作

(1)基于 xml 配置文件实现

(2)基于注解方式实现(使用)

1、实现 AOP 操作的步骤

编写切面类(通过 @Aspect 注解标识这是一个切面类),并且不要忘记将切面类交给 Spring IOC 管理(Component 注解),并编写相应的通知方法与切入点表达式

在 Spring 配置文件中开启 aop 功能:通过 <aop:aspectj-autoproxy/> 注解开启 aop 功能。当Spring IOC容器侦测到bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为与AspectJ切面匹配的bean创建代理

AspectJ支持5种类型的通知注解

[1]@Before:前置通知,在方法执行之前执行

[2]@After:后置通知,在方法执行之后执行

[3]@AfterRunning:返回通知,在方法返回结果之后执行

[4]@AfterThrowing:异常通知,在方法抛出异常之后执行

[5]@Around:环绕通知,围绕着方法执行

1*准备工作:在 Spring 中使用 AspectJ*

引入 maven 依赖:引入 aop 和 aspects 相关的依赖

编写 Spring 配置文件:引入 contextaop 名称空间;开启组件扫描,并指明包路径;开启自动代理功能

<?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.oneby"/>

    <!-- 开启 Aspect生成代理对象 -->
    <aop:aspectj-autoproxy/>

</beans>
2、切入点表达式的相关细节

切入点的作用:通过表达式的方式定位一个或多个具体的连接点(哪些方法需要被增强)

切入点表达式的语法格式execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

切入点表达式的举例一

表达式:execution(* com.atguigu.spring.ArithmeticCalculator.*(…))

含义:增强 ArithmeticCalculator 接口中声明的所有方法

解释说明:第一个“”代表任意修饰符及任意返回值;第二个“”代表任意方法;“…”匹配任意数量、任意类型的参数

注:若目标类、接口与该切面类在同一个包中可以省略包名

切入点表达式的举例二

表达式:execution(public * ArithmeticCalculator.*(…))

含义: 增强 ArithmeticCalculator 接口的所有公有方法(TMD 接口中的方法不都是 public 吗)

切入点表达式的举例三

表达式:execution(public double ArithmeticCalculator.*(…))

含义:增强 ArithmeticCalculator 接口中返回double类型数值的方法

切入点表达式的举例四

表达式:execution(public double ArithmeticCalculator.*(double, …))

含义:第一个参数为double类型的方法。“…” 匹配任意数量、任意类型的参数

切入点表达式的举例五

表达式: execution(public double ArithmeticCalculator.*(double, double))

含义:参数类型为double,double类型的方法

切入点表达式的举例六:在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。

表达式:execution (* .add(int,…)) || execution( *.sub(int,…))

含义:任意类中第一个参数为int类型的add方法或sub方法

5、AOP **操作(**AspectJ 注解)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XgXfZCh0-1619174024028)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312113836285.png)]

3、在普通l类上加注解@Component,在增强类上面添加注解@Component(生成bean)和 @Aspect(生成代理对象)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-te0iQqoy-1619174024029)(C:\Users\yu\AppData\Roaming\Typora\typora-user-images\image-20210312114453186.png)]

@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws 
Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
} }

3、相同的切入点抽取

//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.........");
}

6、完全使用注解开发**

(1)创建配置类,不需要创建 xml 配置文件

@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}

7、AOP **操作(**AspectJ 配置文件)

1**、创建两个类,增强类和被增强类,创建方法**

2**、在** spring 配置文件中创建两个类对象

<!--创建对象--> <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>

3**、在** spring 配置文件中配置切入点

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

eed();
System.out.println(“环绕之后…”);
} }


### 3、**相同的切入点抽取**

//相同切入点抽取
@Pointcut(value = “execution(* com.atguigu.spring5.aopanno.User.add(…))”)
public void pointdemo() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = “pointdemo()”)
public void before() {
System.out.println(“before…”);
}




## 6、完全使用注解开发**

**(1)创建配置类,不需要创建 xml 配置文件**

@Configuration
@ComponentScan(basePackages = {“com.atguigu”})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}




## 7、**AOP** **操作(****AspectJ** **配置文件)**

### **1****、创建两个类,增强类和被增强类,创建方法**

### **2****、在** **spring** **配置文件中创建两个类对象**


### **3****、在** **spring** **配置文件中配置切入点**

com.atguigu.spring5.aopxml.Book.buy(…))"/>

</aop:aspect>
</aop:config>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值