Spring5.0

Spring5.0

1. Spring概念

  1. Spring是轻量级的开源的JavaEE框架
    轻量级:jar比较少、比较小、不需要依赖其他组件
    框架:让日常开发更简单、简化开发
  2. Spring可以解决企业应用开发的复杂性
  3. Spring的两大核心部分:IOC、AOP
    1. IOC:控制反转,把创建对象过程交给Spring进行管理
    2. AOP:面向切面,不修改源代码进行功能增强
  4. Spring特点
    1. 方便解耦,简化开发
    2. AOP编程支持
    3. 方便程序测试
    4. 方便和其他框架进行整合
    5. 方便进行事务操作
    6. 降低API开发(JDBC)
    7. Spring源码(阅读源码对往后的开发都有好处)
      Spring核心部分

1.1 入门案例

1. 建立父工程

父工程
选择maven工程、next之后填写工程名称和GAV
在父工程的pom.xml在引入依赖
Spring的核心jar:Beans、Core、Context、Expression

<dependencies>
	<!-- 可以和junit配合使用的jar -->
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>3.13.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
    <!-- Spring核心jar -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>RELEASE</version>
        <scope>test</scope>
    </dependency>
    <!-- 添加lombok依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

可以在pom包中添加如下属性,避免乱码和版本问题

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
    <encoding>UTF-8</encoding>

    <!-- versions -->
    <java.version>1.8</java.version>
</properties>

<build>
   <resources>
       <resource>
           <directory>src/main/resources</directory>
           <includes>
               <include>**/*.xml</include>
               <include>**/*.properties</include>
           </includes>
       </resource>
       <resource>
           <directory>src/main/java</directory>
       </resource>
   </resources>
</build>

2. 建立子工程

右击父工程新建module
在java包下新建包xxx.xxx 然后在这个报下新建entity包
实体类

public class User {

    public User() {
        System.out.println("初始化User....");
    }

}

在resource包下新建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">

    <bean id="user" class="com.atguigu.spring5.entity.User"/>
</beans>

在test包下新建测试类

public class Spring5Test {

    @Test
    public void testUserAdd() {
        // 1. 加载Spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        // 2. 获取配置创建的对象
        User user = context.getBean("user", User.class);
        user.add();
    }
}

2. IOC容器

  1. IOC底层原理
  2. IOC接口(BeanFactory)
  3. IOC操作Bean管理(基于xml)
  4. IOC操作Bean管理(基于注解)

2.1 IOC(概念和原理)

  1. 什么是IOC
    1. 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
    2. 使用IOC目的:为了耦合度降低
    3. 入门案例就是IOC实现
  2. IOC底层原理
    1. xml解析、工厂模式、反射
    2. 反射:.java -> .class 反射能拿到.class文件
  3. 画图讲解IOC底层原理
    在这里插入图片描述
    在这里插入图片描述

2.2 IOC过程

  1. xml配置文件,配置创建的对象
  2. 通过service类和dao类,创建工厂类
class UserFactory {
    public static UserDao getDao() {
        // 1. 通过xml解析得到全路径
        String classValue = "xml文件中的bean.class属性值";
        // 通过反射创建对象
        Class<?> aClass = Class.forName(classValue);
        return (UserDao) aClass.newInstance();
    }
}

在这里插入图片描述

2.3 IOC(接口)

  1. IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
  2. Spring提供IOC容器实现两种方式:(两个接口)
    • BeanFactory: IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。加载配置文件时不会创建对象,在获取对象(使用)才去创建对象
    • ApplicationContext: BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。加载配置文件时就会创建对象
      在这里插入图片描述
  3. ApplicationContext接口有实现类
    在这里插入图片描述
    FileSystemXmlApplicationContext:系统文件路径(“D://…xxx”)
    ClassPathXmlApplicationContext:src目录(“classpath://…xxx”)

2.4 IOC操作Bean管理

2.4.1 什么是Bean管理

(1) Spring创建对象
(2) Spring注入属性

2.4.2 Bean管理操作有两种方式

(1) 基于xml配置文件方式实现
(2) 基于注解方式实现

2.5 IOC操作Bean管理(基于xml方式)

2.5.1 基于xml方式创建对象

(1) 在Spring配置文件中,使用Bean标签,标签里面添加对象属性,就可以实现对象创建
(2) 在bean标签有很多属性 【id:唯一标识】 【class属性:类全路径(包类路径)】
(3) 创建对象的时候,默认是执行该类的无参构造方法

2.5.2 基于xml方式注入属性

DI:依赖注入,就是注入属性
①第一种注入方式:set方法
②第二种注入方式:构造器注入
③第三种注入方式:p名称空间注入

set方法注入

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

    <!-- 配置Book对象创建 -->
    <bean id="book" class="com.atguigu.spring5.entity.Book" >
        <property name="name" value="long"/>
    </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">

   <!-- 配置Orders对象创建 -->
   <bean id="orders" class="com.atguigu.spring5.entity.Orders">
       <constructor-arg index="0" value="xxx"/>
       <constructor-arg index="1" value="xiao"/>
   </bean>
</beans> 

<constructor-arg index="0" value="xxx"/>中的index代表有参构造的参数顺序。
p名称空间注入
(1) 添加p名称空间在配置文件中
xmlns:p="http://www.springframework.org/schema/p"
(2) 添加属性
<bean id="book" class="com.atguigu.spring5.entity.Book" p:name="九阳神功"/>
(3) 实质
p名称空间注入 实质是setter注入

2.6 IOC操作Bean管理(xml注入其他类型属性)

2.6.1 字面量

  1. null值
<property name="address">
    <null/>
</property>
  1. 属性值包含特殊符号
    ①把<>进行转义 比如 &lt;
    ②把特殊符号内容写到CDATA中
<property name="address">
     <value>
        <![CDATA[<<南京>>]]>
     </value>
 </property>

2.6.2 注入属性-外部bean

  1. 创建service和dao
  2. 在service调用dao里面的方法
  3. 在Spring配置中进行配置
<?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. service和dao对象创建-->
    <bean id="userService" class="com.atguigu.spring5.service.UserService">
        <!-- 注入userDao对象
        name属性:类里面属性名称
        ref属性:创建userDao对象bean标签id值
        -->
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean id="userDao" class="com.atguigu.spring5.dao.impl.UserDaoImpl"/>
</beans>

2.6.3 注入属性-内部bean

  1. 一对多关系:部门和员工
  2. 在实体类之间表示一对多关系
  3. 在配置文件中配置
<?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="employee" class="com.atguigu.spring5.bean.Employee">
        <property name="name" value="小龙"/>
        <property name="gender" value=""/>
        <!-- 设置对象类型属性 -->
        <property name="dept">
            <!-- 内部bean的方式,但是还是一般使用外部bean-->
            <bean class="com.atguigu.spring5.bean.Dept">
                <property name="name" value="后端组"/>
            </bean>
        </property>
    </bean>
</beans>

2.6.4 级联赋值

<?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="employee" class="com.atguigu.spring5.bean.Employee">
        <property name="name" value="lucy"/>
        <property name="gender" value=""/>
        <property name="dept">
            <bean class="com.atguigu.spring5.bean.Dept"/>
        </property>
        <!-- 级联赋值 -->
        <property name="dept.name" value="后端组"/>
    </bean>
</beans>

2.7 IOC操作Bean管理(xml注入集合属性)

  1. 注入数组类型属性
  2. 注入List
  3. 注入Map集合类型属性
<?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.atguigu.spring5.list.Student">
        <!-- 数组类型属性注入 -->
        <property name="courses">
            <array>
                <value>Java课程</value>
                <value>数据库</value>
            </array>
        </property>
        <!-- list类型属性注入 -->
        <property name="list">
            <array>
                <value>n1</value>
                <value>n2</value>
                <value>n3</value>
                <value>n4</value>
            </array>
        </property>
        <!-- map类型属性注入 -->
        <property name="maps">
            <map>
                <entry key="Java" value="java"/>
                <entry key="PHP" value="php"/>
            </map>
        </property>
        <!-- set类型属性注入-->
        <property name="sets">
            <set>
                <value>s1</value>
                <value>s2</value>
            </set>
        </property>
        <!-- 注入list集合类型 值是对象 -->
        <property name="courseList">
            <list>
                <ref bean="course1"/>
                <ref bean="course2"/>
            </list>
        </property>
    </bean>

    <!-- 创建多个course对象 -->
    <bean id="course1" class="com.atguigu.spring5.list.Course">
        <property name="name" value="Spring5框架"/>
    </bean>
    <bean id="course2" class="com.atguigu.spring5.list.Course">
        <property name="name" value="MyBatis框架"/>
    </bean>
</beans>

2.7.1 集合部分重复利用

  1. 在Spring配置文件中引入名称空间
xmlns:uti="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                 http://www.springframework.org/schema/util http://www.springframework.org/schema/beans/spring-util.xsd">
  1. 使用util标签完成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"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <!-- 1. 提取list集合类型属性注入 -->
    <util:list id="bookList">
        <value>Spring</value>
        <value>SpringBoot</value>
    </util:list>

    <!-- 2. 引用list集合 -->
    <bean id="books" class="com.atguigu.spring5.list.Books" scope="prototype">
        <property name="list" ref="bookList"/>
    </bean>
</beans>

2.8 IOC操作Bean管理(FactoryBean)

  1. Spring有两种类型bean 一种普通bean 另一种工程bean(FactoryBean)
  2. 普通bean:在配置文件中定义bena类型就是返回类型
  3. 工程bean:在配置文件定义bean类型可以和返回类型不一样
    ①创建类,让这个类作为工厂bean实现接口FactoryBean
    ②实现接口里面的方法 在实现的方法中定义返回的bean类型
@Data
public class MyBean implements FactoryBean<Course> {

    @Override
    public Course getObject() throws Exception {
        // 这部分底层是通过工厂模式反射得到的
        Course course = new Course();
        course.setName("abc");
        return course;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
    
    @Test
    public void myBeanTest() {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("factorybean/bean1.xml");
        Course course = context.getBean("myBean", Course.class);
        System.out.println(course);
    }

}

2.9 IOC操作Bean管理(bean作用域)

  1. 在Spring里面 设置创建bean实例是单实例还是多实例
  2. 在Spring里面 默认情况下 bean是单实例对象
public class CollectionTest {

    @Test
    public void bookTest() {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("list2.xml");
        Book book1 = context.getBean("book", Book.class);
        System.out.println(book1.hashCode());

        Book book2 = context.getBean("book", Book.class);
        System.out.println(book2.hashCode());
    }
}

两者输出的hashCode()一致
3. 如何设置单实例还是多实例
(1) 在Spring配置文件bean标签里面有属性用于设置单实例还是多实例
(2) scope属性值【singleton表示单实例对象】【prototype表示多实例对象】

<bean id="book" class="com.atguigu.spring5demo2.collection.Book" scope="prototype">
      <property name="list" ref="bookList"/>
  </bean>

(3) singleton和prototype区别
设置scope=singleton加载Spring配置文件时就会创建单实例对象
设置scope=prototype不是在Spring配置文件时创建对象 在调用getBean方法时 创建多实例对象

2.10 IOC操作bean管理(bean生命周期)

2.10.1 生命周期

从对象创建到对象销毁的过程

2.10.2 bean生命周期

  1. 通过构造器创建bean实例(无参构造)
  2. 为bean的属性设置值和对其他bean的引用(调用set方法)
  3. 调用bean初始化的方法(需要进行配置)
  4. bean可以使用了(对象获取到了)
  5. 当容器关闭时 调用bean的销毁的方法(需要进行配置销毁的方法)

2.10.3 演示bean声明周期

@Getter
public class OrdersLife {
    private String name;

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

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步 调用set方法设置值");
    }

    /**
     * 创建执行的初始化的方法
     */
    public void initMethod() {
        System.out.println("第三步 执行初始化的方法");
    }

    /**
     * 销毁的方法
     */
    public void destroyMethod() {
        System.out.println("第五步 销毁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="ordersLeft" class="com.atguigu.spring5.lifecycle.OrdersLife" init-method="initMethod"
          destroy-method="destroyMethod">
        <property name="name" value="手机"/>
    </bean>

    <!-- 配置后置处理器 -->
    <bean id="myBeanPost" class="com.atguigu.spring5.lifecycle.MyBeanPost"/>
</beans>

测试方法

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:lifeBean.xml"})
public class LifeBeanTest {

    @Autowired
    private OrdersLife ordersLife;

    @Test
    public void ordersLeftTest() {
        System.out.println("第四步 创建对象");
        System.out.println(ordersLife);
    }
}

2.10.4 bean的后置处理器bean生命周期有七步

在(3)之前把bean实例传递bean后置处理器的方法:postProcessBeforeInitialization()
在(3)之后把bean实例传递bean后置处理器的方法:postProcessAfterInitialization()

2.10.5 添加后置处理器

在这里插入图片描述
配置后置处理器之后 该xml的bean被创建实例时 会调用后置处理器的方法

2.11 IOC操作bean管理(xml自动装配)

什么是自动装配
根据指定装配规则(属性名称或者属性类型)Spring自动将匹配的属性值进行注入

2.11.1 根据属性名称自动注入

<?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标签属性 autowire 配置自动装配
        byName 根据属性名称注入 注入值bean的id值和类属性名称一样
        byType 根据属性类型注入
    -->
    <bean id="employee" class="com.atguigu.spring5demo2.autowire.Employee" autowire="byName"/>


    <bean id="dept" class="com.atguigu.spring5demo2.autowire.Dept">
        <property name="name" value="监管部"/>
    </bean>
</beans>

2.11.2 根据属性类型自动注入

<?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标签属性 autowire 配置自动装配
        byName 根据属性名称注入 注入值bean的id值和类属性名称一样
        byType 根据属性类型注入
    -->
    <bean id="employee" class="com.atguigu.spring5demo2.autowire.Employee" autowire="byType">
    </bean>


    <bean id="dept" class="com.atguigu.spring5demo2.autowire.Dept">
        <property name="name" value="监管部"/>
    </bean>
</beans>

使用byType时xml文件不能定义相同class的bean会报错。

2.12 IOC操作Bean管理(外部管理文件)

2.12.1 直接配置数据库信息

配置druid连接池

<?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="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/userDb"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>

2.12.2 引入外部属性文件配置数据库连接池

  1. 创建外部属性文件properties格式文件 写数据库信息
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
  1. 把外部properties属性文件引入到Spring配置文件中引入context命名空间
    <context:property-placeholder location="classpath*:druid/jdbc.properties"/>
<?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:property-placeholder location="classpath*:druid/jdbc.properties"/>

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

2.13 IOC操作bean管理(基于注解方式)

2.13.1 什么是注解

  1. 注解是代码特殊标记 格式:@注解名称(属性名称=属性值…)
  2. 注解作用在类、方法、属性
  3. 使用注解目的:简化xml配置

2.13.2 Spring针对bean管理中创建对象提供注解

  1. @Component
  2. @Service
  3. @Controller
  4. @Repository
    这四个注解的实质是一样的都可以用来创建bean实例

2.13.3 基于注解方式实现对象创建

①引入依赖

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>${spring.aop.version}</version>
 </dependency>

②开启组件扫描

<!-- 开启扫描器
      1. 如果扫描多个包 多个包使用逗号隔开
      2. 扫描包上层目录
  -->
 <context:component-scan base-package="com.atguigu.spring5demo3.*"/>

③在类的上面添加注解

/**
 * @description : 在注解里面value属性值可以省略不写 默认值是类名称 首字母小写
 */
@Component
public class UserService {

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

④开启组件扫描细节配置

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

    <!-- 开启扫描器
        1. 如果扫描多个包 多个包使用逗号隔开
        2. 扫描包上层目录
    -->
    <context:component-scan base-package="com.atguigu.spring5demo3.*"/>

    <!-- 示例1
        use-default-filters="false" 表示 现在不使用默认filter 自己配置filter
        context:include-filter 表示 设置扫描哪些内容

        在 com.atguigu.spring5demo3包中只扫描 带有@Controller注解的类
    -->
    <context:component-scan base-package="com.atguigu.spring5demo3" use-default-filters="false">
        <context:include-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--  示例2
        context:exclude-filter 表示 设置不扫描哪些内容
    -->
    <context:component-scan base-package="com.atguigu.spring5demo3">
        <context:exclude-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

2.13.4 基于注解方式实现属性注入

  1. @Autowire 根据属性类型进行自动装配
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public void add() {
        System.out.println("service add...");
        userDao.add();
    }
}
  1. @Qualifier 根据属性名称进行注入(这个注解要和@Autowired一起使用)
  2. @Resource 可以根据类型注入 也可以根据名称注入
  3. @Value 注入普通类型属性

2.13.5 完全注解开发

  1. 创建配置类 替代xml配置文件
/**
 * @author : wenchao.long
 * @date : Created in 2020/10/13 16:20 周二
 * @description : 作为配置类 替代xml配置文件
 */
@Configuration
@ComponentScan(basePackages = {"com.atguigu.spring5demo3.*"})
public class SpringConfig {
}

@Configuration类似xml中的<beans/>
@ComponentScan扫描包

  1. 测试类
public class ServiceTest {

    @Test
    public void serviceAnnotationTest() {
        ApplicationContext context =
                new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

3. AOP切面

3.1 AOP(概念)

什么是AOP

  1. 面向切面编程(方面)利用AOP可以对业务逻辑的各个部分进行隔离 从而使得业务逻辑各部分之间的耦合度降低
  2. 通俗描述:不通过修改源代码方式 在主干功能中添加新功能
  3. 使用登录例子说明AOP
    在这里插入图片描述

3.2 AOP(底层原理)

3.2.1 AOP底层使用动态代理

有两种情况动态代理

  1. 有接口 使用JDK动态代理
    创建接口实现类代理对象 增强类的方法
    在这里插入图片描述
  2. 没有接口 使用CGLIB动态代理
    创建当前类子类的代理对象
    在这里插入图片描述

3.3 AOP(JDK动态代理)

3.3.1 使用JDK动态代理 使用Proxy类里面的方法创建代理对象

java.lang.reflect.Proxy
调用newProxyInstance方法
第一参数:类加载器
第二参数:增强方法所在的类 这个类实现的接口 支持多个接口
第三参数:实现InvocationHandler创建代理对象 写增强的方法

3.3.2 编写JDK动态代理代码

(1) 创建接口 定义方法
(2) 创建接口实现类 实现方法
(3) 使用Proxy类创建接口代理对象

/**
 * @author : wenchao.long
 * @date : Created in 2020/10/14 9:31 周三
 * @description :
 */
public class JdkProxy {

    public static void main(String[] args) {
        Class<?>[] interfaces = {UserDao.class};
        UserDao userDao = new UserDaoImpl();
        // 创建接口实现类代理对象
        UserDao dao = (UserDao) Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        System.out.println("result:" + dao.add(1, 2));
        System.out.println("result:" + dao.update("143902392054393"));
    }
}

/**
 * 创建代理对象代码
 */
class UserDaoProxy implements InvocationHandler {

    // 1. 因为创建的是UserDaoImpl的代理对象 就需要把UserDaoImpl传递过来

    private final Object obj;

    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 增强的逻辑

        // 方法之前
        System.out.println("方法之前执行..." + method.getName() + ":传递的参数..." + Arrays.toString(args));

        String add = "add";
        String update = "update";

        if (add.equals(method.getName())) {
            StringBuffer sb = new StringBuffer();
            for (Object arg : args) {
                sb.append(arg);
            }
            System.out.println(sb);
        }
        if (update.equals(method.getName())) {
            StringBuffer sb = new StringBuffer();
            for (Object arg : args) {
                sb.append(arg);
                System.out.println(sb.reverse());
            }
        }

        // 被增强的方法执行
        Object res = method.invoke(obj, args);

        // 方法之后
        System.out.println("方法之后执行..." + obj);

        return res;
    }
}

3.4 AOP(术语)

3.4.1 连接点

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

3.4.2 切入点

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

3.4.3 通知(增强)

  • 实际增强的逻辑部分称为通知(增强)
  • 通知有多重类型
    • 前置通知
    • 后置通知
    • 环绕通知
    • 异常通知
    • 最终通知finally

3.4.4 切面

是动作 把通知应用到切入点过程

3.5 AOP操作(准备)

3.5.1 Spring框架一般基于AspectJ实现AOP操作

什么是AspectJ
AspectJ不是Spring组成部分 独立AOP框架 一般把AspectJ和Spring框架一起使用进行AOP操作

3.5.2 基于AspectJ实现AOP操作

  1. 基于xml配置文件
  2. 基于注解方式实现(开发一般使用注解)

3.5.3 在项目工程里面引入AOP相关依赖

<dependency>
    <groupId>net.sourceforge.cglib</groupId>
    <artifactId>com.springsource.net.sf.cglib</artifactId>
    <version>2.2.0</version>
</dependency>
<dependency>
    <groupId>org.aopalliance</groupId>
    <artifactId>com.springsource.org.aopalliance</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>com.springsource.org.aspectj.weaver</artifactId>
    <version>1.6.8.RELEASE</version>
</dependency>

3.5.4 切入点表达式

  1. 切入点表达式作用:知道对那个类里面的那个方法进行增强
  2. 语法结构
    execution(权限修饰符|返回类型|类全路径|方法名称|参数列表)
    举例1 对com.atguigu.dao.BookDao类里面的add进行增强
    execution(* com.atguigu.dao.BookDao.add(…))
    举例2 对com.atguigu.dao.BookDao类所有的方法进行增强
    execution(* com.atguigu.dao.BookDao.*(…))
    举例3 对com.atguigu.dao包里面所有类 类里面所有方法进行增强
    execution(* com.atguigu.dao.*.*(…))

3.6 AOP操作(AspectJ注解)

3.6.1 创建类 在类里面定义方法

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

3.6.2 创建增强类(编写增强逻辑)

在增强类里面 创建方法 让不同方法代表不同通知类型

public class UserProxy {
    /**
     * 前置通知
     */
    public void before() {
        System.out.println("before...");
    }
}

3.6.3 进行通知的配置

①在Spring配置文件中 开启注解扫描
<context:component-scan base-package="com.atguigu.spring5demo4.*"/>
②使用注解创建User和UserProxy对象
@Component
③在增强类上面添加@Aspect

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

④在Spring配置文件中开启生成代理对象
<aop:aspectj-autoproxy/>

3.6.4 配置不同类型的通知

在增强类的里面 在作为通知方法上面添加通知类型注解 使用切入点表达式配置

@Component
@Aspect
public class UserProxy {
    /**
     * 前置通知
     */
    @Before("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
    public void before() {
        System.out.println("before...");
    }

    /**
     * 不管有没有异常都会通知
     * 最终通知
     */
    @After("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
    public void after() {
        System.out.println("after...");
    }

    /**
     * 有异常不执行
     * 后置通知(返回通知)
     */
    @AfterReturning("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
    public void afterReturning() {
        System.out.println("afterReturning...");
    }

    @AfterThrowing("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
    public void afterThrowing() {
        System.out.println("afterThrowing...");
    }

    /**
     * 环绕通知
     * @param pjp pjp
     * @return Object
     */
    @Around("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around...之前");
        pjp.proceed();
        System.out.println("around...之后");
        return pjp;
    }
}

3.6.5 相同切入点进行抽取

@Component
@Aspect
public class UserProxy {

    /**
     * 相同切入点抽取
     */
    @Pointcut("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
    public void pointcut() {

    }

    /**
     * 前置通知
     */
    @Before("pointcut()")
    public void before() {
        System.out.println("before...");
    }
}

3.6.6 多个增强类对同一个方法进行增强,设置优先级

在增强类上面添加注解@Order(数字类型值) 数字越小优先级越高

3.7 AOP操作(AspectJ配置文件)

①创建增强类和被增强类
②在Spring配置文件中创建两个类对象
③在Spring配置文件中配置切入点

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

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

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

配置文件了解,主要通过注解方法开发

3.8 完全使用注解开发

创建配置类 不需要创建xml配置文件

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

@EnableAspectJAutoProxy(proxyTargetClass = true)就相当于在xml中的开启AspectJ生成代理对象

4. JdbcTemplate

4.1 JdbcTemplate(概念和准备)

(1) 什么是JdbcTemplate
Spring框架对JDBC进行封装 使用JdbcTemplate方便实现对数据库操作
(2) 准备工作

  • 引入相关jar包
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
  • 在Spring配置文件配置数据库连接池
  • 配置JdbcTemplate对象 注入DataSource
<?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">

    <!-- JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!-- 注入DataSource -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath*:spring5demo5/jdbc.properties"/>

    <!-- 直接配置连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"/>
        <property name="url" value="${prop.url}"/>
        <property name="username" value="${prop.userName}"/>
        <property name="password" value="${prop.password}"/>
    </bean>
</beans>
  • 创建service类 创建dao类 在dao注入JdbcTemplate对象

4.2 JdbcTemplate操作数据库(添加)

4.2.1 对应数据库创建实体类

create table user_db.user
(
    id     bigint       not null
        primary key,
    name   varchar(100) not null,
    status varchar(50)  not null
);
@Data
public class User {
    private String id;
    private String name;
    private String status;
}

4.2.2 编写service和dao

在dao进行数据库添加操作
在进行查询实体操作时jdbcTemplate.queryForObject(sql, new RowMaper<>,id);RowMapper 是接口 返回不同类型数据 使用这个接口里面实现类完成数据封装

4.3 系列操作

@Repository
@Log
public class BookDaoImpl implements IBookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void insert(Book book) {
        String sql = "insert into `book` values(?,?,?)";
        int update = jdbcTemplate.update(sql, book.getId(), book.getName(), book.getStatus());
        log.info("影响行数:" + update);
    }

    @Override
    public void update(Book book) {
        String sql = "update `book` set name = ?, status = ? where id = ?";
        int update = jdbcTemplate.update(sql, book.getName(), book.getStatus(), book.getId());
        log.info("影响行数:" + update);
    }

    @Override
    public void delete(Long id) {
        String sql = "delete from `book` where id = ?";
        int update = jdbcTemplate.update(sql, id);
        log.info("影响行数:" + update);
    }

    @Override
    public int count() {
        String sql = "select count(*) from `book`";
        return Optional.ofNullable(jdbcTemplate.queryForObject(sql, Integer.class)).orElse(-1);
    }

    @Override
    public Book getById(Long id) {
        String sql = "select * from `book` where id = ?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), id);
    }

    @Override
    public List<Book> list() {
        String sql = "select * from `book`";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
    }

    @Override
    public void saveBatch(List<Book> books) {
        String sql = "insert into `book` values(?,?,?)";
        List<Object[]> list = new ArrayList<>();
        books.forEach(c -> {
            Object[] objects = new Object[3];
            objects[0] = c.getId();
            objects[1] = c.getName();
            objects[2] = c.getStatus();
            list.add(objects);
        });
        int[] ints = jdbcTemplate.batchUpdate(sql, list);
        log.info("影响行数:" + Arrays.toString(ints));
    }

    @Override
    public void updateBatch(List<Book> books) {
        String sql = "update `book` set name = ? , status = ? where id = ?";
        List<Object[]> list = new ArrayList<>();
        books.forEach(c -> {
            Object[] objects = new Object[3];
            objects[0] = c.getName();
            objects[1] = c.getStatus();
            objects[2] = c.getId();
            list.add(objects);
        });
        int[] ints = jdbcTemplate.batchUpdate(sql, list);
        log.info("影响行数:" + Arrays.toString(ints));
    }

    @Override
    public void deleteBatch(List<Long> ids) {
        String sql = "delete from `book` where id = ?";
        List<Object[]> list = new ArrayList<>();
        ids.forEach(c -> {
            Object[] objects = new Object[1];
            objects[0] = c;
            list.add(objects);
        });
        int[] ints = jdbcTemplate.batchUpdate(sql, list);
        log.info("影响行数:" + Arrays.toString(ints));
    }
}

5. 事务管理

5.1 事务概念

5.1.1 什么是事务

  1. 事务是数据库操作最基本单元 逻辑上一组操作 要么都成功 如果有一个失败所有操作都失败
  2. 典型场景:银行转账

5.1.2 事务四个特性(ACID)

  1. 原子性 - 要么都成功 要么都失败
  2. 一致性 - 操作之前和操作之后总量不变
  3. 隔离性 - 多事务操作之间不会产生影响
  4. 持久性 - 提交之后表中的数据发生变化

5.2 事务操作(搭建事务操作环境)

创建数据库表 添加记录
创建service搭建dao完成对象创建和注入关系

@Repository
public class UserDaoImpl implements IUserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void addMoney() {
        String sql = "update account set money = money + ? where name = ?";
        jdbcTemplate.update(sql, 100, "mary");
    }

    @Override
    public void reduceMoney() {
        String sql = "update account set money = money - ? where name = ?";
        jdbcTemplate.update(sql, 100, "lucy");
    }
}

上面代码 如果正常执行没有问题 但是如果代码执行过程中出现异常 有问题

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao userDao;

    @Override
    public void accountMoney() {
        // lucy少100
        userDao.reduceMoney();

        // 模拟异常
        int i = 10 / 0;

        // mary多100
        userDao.addMoney();
    }
}

上面问题如何解决?
使用事务进行解决

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao userDao;

    @Override
    public void accountMoney() {
        try {
            // 1. 开启事务

            // 2. 进行业务操作
            // lucy少100
            userDao.reduceMoney();

            // 模拟异常
            int i = 10 / 0;

            // mary多100
            userDao.addMoney();

            // 第三步 没有发生异常 提交事务
        } catch (Exception e) {
            e.printStackTrace();
            // 第四部 出现异常 事务回滚
        }
    }
}

5.3 事务操作(Spring事务管理介绍)

  1. 事务添加到JavaEE三层结构里面Service层(业务逻辑层)
  2. 在Spring进行事务管理操作
    有两种方式:编程式事务管理和声明式事务管理(使用)
    上述的操作过程就是编程式事务管理
    一般开发不使用 因为代码会很冗余并且很麻烦 所以一般使用声明式事务管理
  3. 声明式事务管理
    (1) 基于注解方式(使用)
    (2) 基于xml配置文件方式
  4. 在Spring进行声明式事务管理 底层使用AOP原理
  5. Spring事务管理API
    提供一个接口 代表事务管理器 这个接口针对不同的框架提供不同的实现类
    在这里插入图片描述
    如果是整合的是MyBatis或者使用JDBCTemplate模板、实现类就是DataSourceTransactionManager

5.4 事务操作(注解方式实现声明式事务管理)

5.4.1 在Spring配置文件中配置事务管理器

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源 -->
    <property name="dataSource" ref="dataSource"/>
</bean>

5.4.2 在Spring配置文件中开启事务注解

<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

5.4.3 在service类上面(或者service类里面方法上面) 添加事务注解

  1. @Transactional 这个注解添加到类上面 也可以添加方法上面
  2. 如果把这个注解添加类上面 这个类里面所有的方法都添加事务
  3. 如果把这个注解添加方法上面,为这个方法添加事务
    注意
    在开启事务注解时 如果是使用接口实现类方式。要添加<tx:annotation-driven proxy-target-class="true"/>
    其中如果事务管理器id为transactionManger则transaction-manager="transactionManager"可以省略不写
    proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。
    如果proxy-target-class="true"那么基于类的代理将起作用(这时需要cglib库)
    如果proxy-target-class="false"或者这个属性被省略 那么标准的JDK 基于接口的代理将起作用
    即使你未声明 proxy-target-class=“true” 但运行类没有继承接口 spring也会自动使用CGLIB代理
    高版本spring自动根据运行类选择 JDK 或 CGLIB 代理

5.5 事务操作(声明式事务管理参数配置)

在Service类上面添加注解@Transactional 在这个注解里面可以配置事务相关参数
在这里插入图片描述
propagation: 事务传播行为
多事务方法直接进行调用 这个过程中事务是如何进行管理的
事务方法: 对数据库表数据进行变化的操作
Spring框架对于事务传播行为有7种
org/springframework/transaction/annotation/Propagation.java

项目地址Spring5.0项目

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值