Spring从零单排

105 阅读13分钟

同步环境:
系统:macOS 开发工具:Idea + maven

创建项目

  1. 打开idea --> File --> new --> project --> maven,不选择模板,填写项目名称,创建项目,然后删除src目录,然后鼠标右击项目名称-->new-->module-->maven 重新填写项目名称,继承自原来新建项目。这样新建的module就会和原来的项目形成父子模块的依赖,我们在学习开发的过程当中可以把项目公共的一些坐标依赖放到项目下的pom.xml文件当中,module中的子pom.xml只引入当前模块需要的依赖,这样我们新建的子module就可以永久的共享项目的依赖。

image.png

配置和注解 配置开发有助于我们理解,注解开发有助于我们提高开发效率

Spring Framework 4.x Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基

核心:IoC控制反转 image.png

AOP:面向切面编程,Aspects是AOP思想实现 这是一种设计思想,和面向对象编程一样,是一种编程的思想,AOP在不变动原始代码的基础上,扩展增强代码功能 image.png

数据访问/数据集成
image.png

web开发
image.png

单元测试
image.png

IoC 控制反转

  • 使用对象时,有主动的new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部(就是IOC容器提供),此思想称为控制反转。Spring技术对IoC思想进行了实现

image.png

  • IOC容器负责对象的创建,初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean image.png

DI 依赖注入

  • 在容器中建立bean与bean之间的依赖关系的过程,统称为依赖注入

image.png

IoC入门思路

image.png

在项目下的pom.xml中添加Spring Framework的maven坐标

image.png


<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>
  • 依赖导入以后,右击resources文件夹 --> new --> xml --> spring config --> applicationContext.xml

image.png


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       <!--向容器内装配bean-->
    <bean id="bookService" class="com.leo.service.impl.BookServiceImpl"/>
     <!--
     bean作用:添加class到IoC
     bean参数:
     id :相当于声明了一个class参数的类型变量
     class : 声明class的类型。要不然无法确定class的类型
     name : 声明别名,别名之间可以使用空格或者分号或者逗号,相当于多个id
     scope : 默认值singleton:单例模式,还可以选择prototype:原型模式
     init-method : bean初始化使用
     destroy-method : bean销毁后调动,IoC容器关闭后调用
     factory-bean :  指向IoC中的bean实例
     factory-method : 指向IoC中bean类的class方法


     -->
     
   
</beans>

image.png

使用注解实现

我们还可以通过 JSR-250 的 @PostConstruct 和 @PreDestroy 注解,指定 Bean 的生命周期回调方法。

注解描述
@PostConstruct指定初始化回调方法,这个方法会在 Spring Bean 被初始化后被调用,执行一些自定义的回调操作。
@PreDestroy指定销毁回调方法,这个方法会在 Spring Bean 被销毁前被调用,执行一些自定义的回调操作。
  • 新建测试文件
package com.leo;

import com.leo.service.impl.BookServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
    // 获取容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 容器内获取bean
        BookServiceImpl bookService = ctx.getBean("bookService", BookServiceImpl.class);
        // 拿到对象
        bookService.save();
    }
}
  • 我们注入依赖,解耦对象
package com.leo.service.impl;

import com.leo.dao.BookDao;
import com.leo.dao.impl.BookDaoImpl;
import com.leo.service.BookService;

// class 实例化对象的时候IoC容器会调用class的无参数的构造方法,无论是共有还是私有,暴力反射
public class BookServiceImpl implements BookService {
    //下面的代码要注释掉了,因为我们要通过注入的方式,变为一个属性
   // private BookDao bookDao = new BookDaoImpl();
   private BookDao bookDao;

    // 
    @Override
    public void save() {
        System.out.println("book service save");
        bookDao.save();
    }
    // 添加属性的setter方法,然后去ApplicationContext.xml配置文件下配置
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}
  • 添加bookDaoImpl到applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookDao" class="com.leo.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.leo.service.impl.BookServiceImpl">
    
     <!--
     property作用:配置bean属性
     property参数详解:
     name : 指向的是bean.Class指向的类的属性,例如:com.leo.service.impl.BookServiceImpl的bookDao
     ref : 指向的是配置文件中的bean.id
     
     -->
        <property name="bookDao" ref="bookDao"/>
       
    </bean>
    
 
<!-- 静态方式实例化类 -->
<bean id="orderDao" class="com.leo.factory.OrderDaoFactory" factory-method="getOrderDao" />
<!-- 实例化类,这样使用的不多,了解即可 -->
<!--先实例化工厂类,-->
<bean id="instanceOrder" class="com.leo.factory.InstanceForOrderFactory"/>
<!-- 然后在指定工厂类实例化对象的方法,获取的时候就可以直接获取order-->
<bean id="Order"  factory-method="getInstanceOrderDao" factory-bean="instanceOrder"/>

    

</beans>
  • 实例工厂 image.png

工厂类方式获取类

package com.leo.factory;

import com.leo.dao.OrderDao;
import com.leo.dao.impl.OrderDaoImpl;

public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        return  new OrderDaoImpl();
    }
}
  • 配置静态方式获取bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookDao" class="com.leo.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.leo.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>

<!--    静态方式实例化类-->
    <bean id="orderDao" class="com.leo.factory.OrderDaoFactory" factory-method="getOrderDao"/>

</beans>
public class App2 {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

       // 获取类
        OrderDao oderDao = ctx.getBean("orderDao", OrderDao.class);
        oderDao.save();

    }
}
  • 使用FactoryBean实例化对象
<?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="bookDao" class="com.leo.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.leo.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>

    <!-- 静态方式实例化类 -->
    <bean id="orderDao" class="com.leo.factory.OrderDaoFactory" factory-method="getOrderDao" />
    <!-- 实例化类 -->
    <!-- 先实例化工厂类 -->
    <bean id="instanceOrder" class="com.leo.factory.InstanceForOrderFactory"/>
    <!-- 然后在指定工厂类实例化对象的方法,获取的时候就可以直接获取order-->
    <bean id="Order"  factory-method="getInstanceOrderDao" factory-bean="instanceOrder"/>
    <!--  使用factoryBean实例化对象,只要实现FactoryBean<?>接口  -->
    <bean id="bookBean" class="com.leo.factory.UseDaoFactoryBean"/>

</beans>

实现FactoryBean


import com.leo.dao.BookDao;
import com.leo.dao.impl.BookDaoImpl;
import org.springframework.beans.factory.FactoryBean;

public class UseDaoFactoryBean implements FactoryBean<BookDao> {
    // 这就相当于工厂类中的生产实例的方法
    @Override
    public BookDao getObject() throws Exception {
        return new BookDaoImpl();
    }
    // 生产什么类型
    @Override
    public Class<?> getObjectType() {
        return BookDaoImpl.class;
    }
    // 默认true是单例模式,如果是false就是prototype模式
    @Override
    public boolean isSingleton() {

        return true;
    }

}

bean生命周期 使用注解控制

public class AnnotationLifeCycleBean {
    private static final Log LOGGER = LogFactory.getLog(AnnotationLifeCycleBean.class);
    //网站名称
    private String webName;
    //网站地址
    private String url;
    public AnnotationLifeCycleBean(String webName, String url) {
        this.webName = webName;
        this.url = url;
    }
    @Override
    public String toString() {
        return "AnnotationLifeCycleBean{" +
                "webName='" + webName + ''' +
                ", url='" + url + ''' +
                '}';
    }
    /**
     * 初始化回调方法
     */
    @PostConstruct
    public void init() {
        LOGGER.info("通过 @PostConstruct 注解,指定初始化方法:init() 方法");
    }
    /**
     * 销毁回调方法
     */
    @PreDestroy
    public void destroy() {
        LOGGER.info("通过 @PreDestroy 注解,指定初始化方法:destroy() 方法");
    }
}
package com.leo;

import com.leo.dao.OrderDao;
import com.leo.dao.impl.OrderDaoImpl;
import com.leo.factory.InstanceForOrderFactory;
import com.leo.service.impl.BookServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //ctx.registerShutdownHook(); 这个钩子函数注册后出作用域执行destroy-method;
        BookServiceImpl bookService = ctx.getBean("bookService", BookServiceImpl.class);
        bookService.save();
        OrderDao oderDao = ctx.getBean("orderDao", OrderDao.class);
        oderDao.save();

        //ctx.close();这个不需要关注

    }
}

bean 生命周几的几个阶段

image.png

image.png

依赖注入方式

  1. 数据传递方式
  • 普通方式(set方法)
    • setter注入简单类型
    • setter引用类型
  • 构造方法
    • 引用类型
    • 简单类型
  1. 数据类型
  • 引用类型
  • 简单类型
<bean id="bookService" class="com.leo.service.impl.BookServiceImpl">
   <!--类内必须提供相关属性的setter方法-->
   <!--注入引用类型-->
   <property name="bookDao" ref="bookDao"/>
   <!--注入基础类型-->
   <property name="bookDao" value="bookDao"/>
</bean>

构造器注入

public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    public BookServiceImpl(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}
<bean id="bookService" class="com.leo.service.impl.BookServiceImpl">
    <!-- 
    constructor-arg:
    name : 指向类的构造器的参数名
    ref : 指向IoC容器中的实例(bean)
    value : 对应基础类型的value值
    -->   
    <constructor-arg name="bookDao" ref="bookDao"/>
</bean>

<!--多个参数配置-->
<bean id="bookService" class="com.leo.service.impl.BookServiceImpl">
    <!-- 
    constructor-arg:
    name : 指向类的构造器的参数名
    ref : 指向IoC容器中的实例(bean)
    value : 对应基础类型的value值
    -->   
    <constructor-arg name="username" value="root"/>
    <constructor-arg name="number" value="10"/>
    <!--不指定字段,如果顺序乱了赋值就发生错误-->
    <constructor-arg  value="root"/>
    <constructor-arg  value="10"/>
    
    <!--指定类型,但是有俩个String字段顺序错误的情况下依然有bug-->
    <constructor-arg type="java.lang.String" value="root"/>
    <constructor-arg type="int" value="10"/>
    
    <!--根据参数使用索引,解决参数重复问题-->
    <constructor-arg index="0" value="root"/>
    <constructor-arg index="1" value="10"/> 
</bean>

image.png

依赖自动装配,自动装配需要提供属性的setter方法

<?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="bookDao" class="com.leo.dao.impl.BookDaoImpl"/>
    <!--
    
    autowire : 自动装配,不能对基础类型使用 
    byName : 根据名称自动的装配,是根据setter方法的名称去掉set然后把首字母小写去匹配配置文件中的bean.id,也可以认为是类中的属性名称,因为setter方法基本都是根据属性生成的标准方法
    byType : 根据类型自动的装配,但是有多个类型的时候会出错,类型需要唯一,如果按照类型可以没有bean.id (推介使用byType)
    constructor : 根据构造器自动的装配
    default : 默认的装配方式
    
    -->
    <bean id="bookService" class="com.leo.service.impl.BookServiceImpl"  autowire="byName"/>
</beans>

image.png

集合注入

package com.leo.dao.impl;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JavaCollection {
    //1 数组类型属性
    private String[] courses;
    //2 list 集合类型属性
    private List<String> list;
    //3 map 集合类型属性
    private Map<String, String> maps;
    //4 set 集合类型属性
    private Set<String> sets;
    private Properties properties;
    
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setCourses(String[] courses) {
        this.courses = courses;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }
    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
    @Override
    public String toString() {
        return "JavaCollection{" +
                "courses=" + Arrays.toString(courses) +
                ", list=" + list +
                ", maps=" + maps +
                ", sets=" + sets +
                '}';
    }
}
  • 配置文件
<bean id="javaCollection" class="com.leo.dao.impl.JavaCollection">
    <!--数组类型-->
    <property name="courses">
        <array>
            <value>Java</value>
            <value>PHP</value>
            <value>C 语言</value>
            <!--如果有引用类型可以使用ref,其他的集合也一样-->
            <ref bean="beanId"/>
        </array>
    </property>
    <!--List 类型-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>李四</value>
            <value>王五</value>
            <value>赵六</value>
        </list>
    </property>
    <!--Map 类型-->
    <property name="maps">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>
    <!--Set 类型-->
    <property name="sets">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>
 
    <!--properties 类型-->
    <property name="properties">
        <props>
            <prop key="JAVA">java</prop>
            <prop key="PHP">php</prop>
        </props>
    </property>

</bean>

p/c短命名的方式

短命名空间注入

我们在通过构造函数或 setter 方法进行属性注入时,通常是在 元素中嵌套 和 元素来实现的。这种方式虽然结构清晰,但书写较繁琐。

Spring 框架提供了 2 种短命名空间,可以简化 Spring 的 XML 配置,如下表。

短命名空间简化的 XML 配置说明
p 命名空间 元素中嵌套的  元素是 setter 方式属性注入的一种快捷实现方式
c 命名空间 元素中嵌套的 元素是构造函数属性注入的一种快捷实现方式

p 命名空间注入

p 命名空间是 setter 方式属性注入的一种快捷实现方式。通过它,我们能够以 bean 属性的形式实现 setter 方式的属性注入,而不再使用嵌套的 元素,以实现简化 Spring 的 XML 配置的目的。

首先我们需要在配置文件的 元素中导入以下 XML 约束。

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

在导入 XML 约束后,我们就能通过以下形式实现属性注入。

<bean id="Bean 唯一标志符" class="包名+类名" p:普通属性="普通属性值" p:对象属性-ref="对象的引用">

使用 p 命名空间注入依赖时,必须注意以下 3 点:

  • Java 类中必须有 setter 方法;
  • Java 类中必须有无参构造器(类中不包含任何带参构造函数的情况,无参构造函数默认存在);
  • 在使用 p 命名空间实现属性注入前,XML 配置的 元素内必须先导入 p 命名空间的 XML 约束。

c 命名空间注入

c 命名空间是构造函数注入的一种快捷实现方式。通过它,我们能够以 属性的形式实现构造函数方式的属性注入,而不再使用嵌套的 元素,以实现简化 Spring 的 XML 配置的目的。

首先我们需要在配置文件的 元素中导入以下 XML 约束。

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

在导入 XML 约束后,我们就能通过以下形式实现属性注入。

<bean id="Bean 唯一标志符" class="包名+类名" c:普通属性="普通属性值" c:对象属性-ref="对象的引用">

使用 c 命名空间注入依赖时,必须注意以下 2 点:

  • Java 类中必须包含对应的带参构造器;
  • 在使用 c 命名空间实现属性注入前,XML 配置的 元素内必须先导入 c 命名空间的 XML 约束。

setter 方式注入内部 Bean,依然可以使用setter和constructor的方式 注意:内部 Bean 都是匿名的,不需要指定 id 和 name 的。即使制定了,IoC 容器也不会将它作为区分 Bean 的标识符,反而会无视 Bean 的 Scope 标签。因此内部 Bean 几乎总是匿名的,且总会随着外部的 Bean 创建。内部 Bean 是无法被注入到它所在的 Bean 以外的任何其他 Bean 的。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="outerBean" class="……">
    <property name="……" >
        <!-- 定义内部 Bean -->
        <bean class="……">
            <property name="……" value="……" ></property>
            ……
        </bean>
    </property>
</bean>
</beans>

属性注入null值

<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-3.0.xsd">
<bean id="exampleBean" class="net.biancheng.c.ExampleBean">
    <!--使用null 标签注入 Null 值-->
    <property name="propertyNull">
        <null/>
    </property>
</bean>
</beans>

特殊类型和数据的的赋值 我们可以在 的 子元素中,为它所依赖的 Bean 的属性进行赋值,这就是所谓的“级联属性赋值”。

使用级联属性赋值时,需要注意以下 3点:

  • Java 类中必须有 setter 方法;
  • Java 类中必须有无参构造器(默认存在);
  • 依赖其他 Bean 的类中,必须提供一个它依赖的 Bean 的 getXxx() 方法。
public class DependBean {
    private String dependProperty;
    public void setDependProperty(String dependProperty) {
        this.dependProperty = dependProperty;
    }
    @Override
    public String toString() {
        return "DependBean{" +
                "dependProperty='" + dependProperty + ''' +
                '}';
    }
}
package net.biancheng.c;

public class ExampleBean {
    //Null值
    private String propertyNull;
    //空字符串
    private String propertyEmpty;
    //包含特殊符号的字面量
    private String propertyLiteral;
    //依赖的 Bean(对象属性)
    private DependBean dependBean;

    public void setPropertyEmpty(String propertyEmpty) {
        this.propertyEmpty = propertyEmpty;
    }

    public void setPropertyNull(String propertyNull) {
        this.propertyNull = propertyNull;
    }

    public void setPropertyLiteral(String propertyLiteral) {
        this.propertyLiteral = propertyLiteral;
    }

    public void setDependBean(DependBean dependBean) {
        this.dependBean = dependBean;
    }

    //使用级联属性赋值时,需提供一个依赖对象的 getXxx() 方法
    public DependBean getDependBean() {
        return dependBean;
    }

    @Override
    public String toString() {
        return "ExampleBean{" +
                "propertyNull='" + propertyNull + ''' +
                ", propertyEmpty='" + propertyEmpty + ''' +
                ", propertyLiteral='" + propertyLiteral + ''' +
                ", dependBean=" + dependBean +
                '}';
    }
}
<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-3.0.xsd">
<bean id="exampleBean" class="net.biancheng.c.ExampleBean">
    <!--使用null 标签注入 Null 值-->
    <property name="propertyNull">
        <null/>
    </property>
    <!--使用空参数注入空字符串-->
    <property name="propertyEmpty" value=""></property>
    <!--使用 <![CDATA[]]> 将包含特殊符号的字面量注入-->
    <property name="propertyLiteral">
        <value><![CDATA[<c.biancheng.net>]]></value>
    </property>
    <!--注入依赖的 Bean-->
    <property name="dependBean" ref="dependBean"></property>
    <!--级联属性赋值-->
    <property name="dependBean.dependProperty" value="级联属性赋值"></property>
</bean>
<!--对 ExampleBean 依赖的 Bean 进行定义-->
<bean id="dependBean" class="net.biancheng.c.DependBean">
    <!--对属性进行赋值-->
    <property name="dependProperty" value="依赖 Bean 内部赋值"></property>
</bean>
</beans>

使用注解开发

  • 配置 image.png
<?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.leo.dao.impl"/>

</beans>
  • java文件
package com.leo.dao.impl;

import com.leo.dao.BookDao;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

// 实现InitializingBean, DisposableBean 这俩个接口后就不用再配置文件bean中指定init-method和destroy-method属性
@Component("bookDao")
public class BookDaoImpl implements BookDao , InitializingBean, DisposableBean {

    @Override
    public void save() {
        System.out.println("book dao save");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
    // 类内的属性执行完之后才执行
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(" init ");
    }
}

image.png

注解说明
@Component该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。
@Repository该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller该注解通常作用在控制层(如 Struts2 的 Action、SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

注解类配置加载

image.png

  • 新建SpringConfig.java
package com.leo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 声明为配置类的注解
@Configuration
// 扫描包下的类,会把包下的类声明到容器的类加载进来
@ComponentScan("com.leo")
public class SpringConfig {


}

image.png

  • MainApp.java
public class App {
    public static void main(String[] args) {
      
        ApplicationContext ctx =new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDaoImpl bookDao = ctx.getBean("bookDao", BookDaoImpl.class);
        bookDao.save();
    }
}

@Scope注解控制单例和非单例模式

// 实现InitializingBean, DisposableBean 这俩个接口后就不用再配置文件bean中指定init-method和destroy-method属性
@Component("bookDao")
@Scope("prototype") // 非单例模式
public class BookDaoImpl implements BookDao {

    @Override
    public void save() {
        System.out.println("book dao save");
    }
    // 构造方法后
    @PostConstruct
    public void init() throws Exception {
        System.out.println("init");
    }
    // 销毁前执行
    @PreDestroy
    public void destroy() throws Exception {
        System.out.println(" destroy ");
    }
}

使用注解控制生命周期

@Component("bookDao")
@Scope("prototype") // 非单例模式
public class BookDaoImpl implements BookDao {

    @Override
    public void save() {
        System.out.println("book dao save");
    }
    // 构造方法后
    @PostConstruct
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
    // 销毁前执行
    @PreDestroy
    public void afterPropertiesSet() throws Exception {
        System.out.println(" init ");
    }
}

使用注解注入

@Service
public class BookServiceImpl implements BookService {
    // 使用的是暴力反射,对私有属性的注入
    // @Autowired
    // 按照名称注入,使用的比较少
    //@Qualifier("bookDao")
    // 根据类型和名称自动的推导寻找
    @Resource
    private BookDao bookDao;

    @Value("leo")
    private String name;

    @Override
    public void save() {
        System.out.println("book service save"+name);
        bookDao.save();
    }

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

加载properties文件

@Configuration
@ComponentScan("com.leo")
// 加载配置文件,可以以数组的形式加载多个配置文件,不能使用通配符
@PropertySource("jdbc.properties")
public class SpringConfig {


}
  • 引用文件的属性到类中
@Service
public class BookServiceImpl implements BookService {
    // 使用的是暴力反射,对私有属性的注入
//    @Autowired
    // 按照名称注入,使用的比较少
    //@Qualifier("bookDao")
    // 根据类型和名称自动的推导寻找
    @Resource
    private BookDao bookDao;

    @Value("${name}")
    private String name;

    @Override
    public void save() {
        System.out.println("book service save"+name);
        bookDao.save();
    }

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

注解开发管理第三方bean

  • 方式一
@Configuration
@ComponentScan({"com.leo"})
@PropertySource("jdbc.properties")

public class SpringConfig {
    // 定义一个方法获得要管理的对象
    // 表示方法的返回值是一个bean
    @Bean
    //    @Bean("dataSource")
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://110.42.230.125/3306/jdbc");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
}
  • 方式二
    • 新建JdbcConfig.java
@Configuration
public class JdbcConfig {
    // 定义一个方法获得要管理的对象
    // 表示方法的返回值是一个bean
    @Bean
//    @Bean("dataSource")
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///jdbc");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
}

---
// 
@Configuration
// 扫描配置文件的路径,加载
@ComponentScan({"com.leo.config"})
@PropertySource("jdbc.properties")

public class SpringConfig {
    // 定义一个方法获得要管理的对象
    // 表示方法的返回值是一个bean
    @Bean
    //    @Bean("dataSource")
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://110.42.230.125/3306/jdbc");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
}

  • 使用@Import加载bean
@Configuration
@PropertySource("jdbc.properties")
@Import({JdbcConfig.class})
public class SpringConfig {
    // 定义一个方法获得要管理的对象
    // 表示方法的返回值是一个bean
    @Bean
    //    @Bean("dataSource")
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://110.42.230.125/3306/jdbc");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
}

方法中引用参数的注入是按照类型注入的

  • BookDao接口的实现类加入到IoC容器
  • 参数使用的时候直接就会注入
  • bean可以让返回的引用类型装载到IoC容器
@Configuration
@ComponentScan("com.leo")
@PropertySource("jdbc.properties")
public class JdbcConfig {
    @Value("${diver}")
    private String driver;
    @Value("${url}")
    private String url;
    @Value("${username}")
    private String username;
    @Value("${password}")
    private String password;

    // 定义一个方法获得要管理的对象
    // 表示方法的返回值是一个bean
    
    //    @Bean("dataSource")
    @Bean
    public DataSource dataSource(BookDao bookDao){
        System.out.println(bookDao.getClass().getName());
        System.out.println(url);
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

image.png

image.png

xml与注解对比

image.png

AOP 概念点

image.png

  • 连接点:想要去执行的所有方法或者功能叫做连接点
  • 切入点: 某些方法需要具体的执行一些公共的功能和方法叫做切入点
  • 通知:就是切入点上要执行的公共的方法,也可以理解为通知扩展了连接点的功能,但是不变动连接点的代码,而是通过切面告知切入点哪些连接点要执行通知的功能
  • 切面: 具体哪些切入点上执行哪些通知
  • 通知类:因为通知基本上在一个类内,通知所在的类叫做通知类

image.png

AOP 实现

  • 导入aop maven依赖 image.png
  • 通知实现

image.png

  • 注解开启AOP

image.png

  • 定义切入点的规则

image.png

@Contended
@Aspect
public class AopDemo {
    @Pointcut("execution(void com.leo.service.impl.BookServiceImpl.save())")
    public void pt(){}

    @Before("pt()")
    public void  method(){
        System.out.println("hello aop");
    }
}

AOP工作流

image.png

image.png

切入点表达式

image.png

AOP获取类名+方法名

  • ProceedingJoinPoint:: Signatrue getSignature()
  • Signatrue:: getName() // 获取执行的方法名
  • Signatrue:: getDeclaringTypeName() // 获取类名
  • ProceedingJoinPoint:: Object proceed()// 执行目标方法

image.png

AOP获取参数 JoinPoint :: Object getArgs() //获取目标层方法传递的参数 Arrays.toString(joinPoint.getArgs())

afterReturning获取执行的返回值

  • afterReturning方法如果有JoinPoint必须是第一个参数,第二个参数最好是object类型,如果没有只写object类型,防止返回值类型转换错误 image.png

image.png

使用形参接收AOP的异常 image.png

AOP处理业务层的数据

  • 获取业务层的数据
  • 处理业务层的数据
  • 把处理完的数据赋值给原索引位置上
  • 把从新赋值完的数据传递到proceed方法函数 image.png

AOP总结

image.png

名称说明
Joinpoint(连接点)AOP 的核心概念,指的是程序执行期间明确定义的一个点,例如方法的调用、类初始化、对象实例化等。 在 Spring 中,连接点则指可以被动态代理拦截目标类的方法。
Pointcut(切入点)又称切点,指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。
Advice(通知)指拦截到 Joinpoint 之后要执行的代码,即对切入点增强的内容。
Target(目标)指代理的目标对象,通常也被称为被通知(advised)对象。
Weaving(织入)指把增强代码应用到目标对象上,生成代理对象的过程。
Proxy(代理)指生成的代理对象。
Aspect(切面)切面是切入点(Pointcut)和通知(Advice)的结合。

Advice 直译为通知,也有人将其翻译为“增强处理”,共有 5 种类型

通知说明
before(前置通知)通知方法在目标方法调用之前执行
after(后置通知)通知方法在目标方法返回或异常后调用
after-returning(返回后通知)通知方法会在目标方法返回后调用
after-throwing(抛出异常通知)通知方法会在目标方法抛出异常后调用
around(环绕通知)通知方法会将目标方法封装起来

Spring事务

image.png

  • 业务层开启事务注解,可以写在接口或者方法上

image.png

  • 在JdbcConfig.java配置事务管理器

image.png

  • 在SpringConfig中配置注解式启动事务 image.png

image.png

image.png

  • jdbc中和mybatis中必须用同一个dataSource才能保证事务
  • 事务相关的配置

image.png

  • Error类的异常和运行时异常会回滚,但是其他的异常类型不会回滚

  • 可以通过注解设置让其回滚

image.png

image.png

  • 事务的传播行为

image.png