Spring

一、Spring概述

1.1 web项目开发中的耦合度问题

  • 在Servlet中需调用service中的方法,则需在Servlet类中通过new关键字创建Service的实例;

这样会导致servlet和service之间的耦合度增加

  •  在service实现类中需调用DAO中的方法,也需在service实现类通过new关键字创建DAO实现类对象;
  • 如果使用new关键字创建对象:

1)失去了面向接口编程的灵活性;

2)代码的侵入性增强(增加了耦合度)、降低了代码的灵活度;

1.2 面向接口编程

 解决方案:

  • 在Servlet中定义Service接口的对象变量,不使用new关键字创建实现类对象,在Servlet实例化的时候,通过反射动态给Service接口对象变量赋值

如何实现?Spring can do it  !!!

1.3 Spring介绍

Spring是一个轻量级的控制反转和面向切面的容器框架,用来解决企业项目开发的复杂度问题,比如解耦

  • 轻量级:体积小,对代码没有侵入性
  • 控制反转:IOC(Inverse of Control),把创建对象的工作交由给Spring完成,Spring在创建对象的时候同时可以完成对象属性的赋值操作(DI)
  • 面向切面:AOP(Aspect Oriented Programming)面向切面编程,可以在不改变原有业务逻辑的情况下实现对业务的增强,比如事务、日志管理
  • 容器:实例的容器,管理创建的对象

1.4 Spring架构

 1)Core Container

Spring容器组件,用于完成实例的创建和管理

  • core
  • beans 实例管理
  • context  Spring容器上下文

2)AOP、Aspects

Spring AOP组件,实现面向切面编程

  • aop
  • aspects

3)web

Spring web组件实际指的是SpringMVC框架,实现web项目的MVC控制

  • web(Spring对web项目的支持)
  • web mvc(SpringMVC组件)

4)Data Access

Spring数据访问组件,是一个基于JDBC封装的持久层框架(即使没有mybatis,Spring也可以完成持久化操作)

  • ORM

5)Test

Spring的单元测试组件,提供了Spring环境下的单元测试支持

  • test

 二、Spring IoC

Spring IoC容器组件,可以完成对象的创建、对象属性赋值、对象管理等

2.1 Spring框架部署(IoC)

1)创建Maven工程

  • Java
  • Web

2)添加SpringIoC依赖

  • core
  • beans
  • context
  • aop
  • expression
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.3.15</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.3.13</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.13</version>
</dependency>

只要导入spring-context,其余依赖会自动导入,可以简化为如下:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.13</version>
</dependency>

3)创建Spring配置文件

通过配置文件“告诉”Spring容器创建什么对象,给对象属性赋什么形式的值

  • 在resources目录下创建名为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">
    <!--
        对于一个xml文件如果作为框架的配置文件,需遵守框架的配置规则。
        通常一个框架为了让开发者能够正确的配置,都会提供xml的规范文件(dtd/xsd)
     -->
    
    
</beans>

2.2 SpringIoC使用

使用SpringIoC组件创建并管理对象

1)创建一个实体类

public class Student {

    private String stuNum;
    private String stuName;
    private String stuGender;
    private int stuAge;
    private Date entranceTime;//入学日期
    
}

2)在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">

    <!-- 通过bean将实体类配置给Spring进行管理,id表示实体类的唯一标识 -->
    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <property name="stuNum" value="10001"/>
        <property name="stuName" value="张三"/>
        <property name="stuGender" value="女"/>
        <property name="stuAge" value="20"/>
        <property name="entranceTime" value="Sat Nov 26 00:00:00 CST 2022"/>
    </bean>
</beans>

3)初始化Spring对象工厂,获取对象

  • ClassPathXmlApplicationContext
/* 1、初始化Spring容器,加载Spring配置文件 */
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext(
                "applicationContext.xml");
/* 2、通过Spring容器获取Student对象 */
Student student=(Student)context.getBean("stu");

2.3 IoC和DI

  • IoC(Inverse of Control)控制反转,通过Spring对象工厂完成对象的创建
  • DI(Dependency Injection)依赖注入,在Spring完成对象创建的同时依赖Spring容器完成对象属性的赋值

1)IoC

当需通过Spring对象工厂创建某个类对象时,需将这个交给Spring进行管理-通过bean标签配置

    <bean id="stu" class="com.xsbc.ioc.bean.Student"></bean>
    <bean id="date" class="java.util.Date"></bean>

2)DI

通过Spring容器给创建的对象属性赋值

    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <property name="stuNum" value="10001" />
        <property name="stuName" value="张三" />
        <property name="stuGender" value="女" />
        <property name="stuAge" value="20" />
        <property name="entranceTime" ref="date" />
    </bean>

2.4 DI依赖注入

1)依赖注入三种方式

Spring通过加载配置文件之后,通过反射创建类的对象,并给属性赋值;

Spring容器通过反射实现属性注入有三种注入方式:

  • set方法注入
  • 构造器注入
  • 接口注入(不常用)

2)set方法注入

在bean标签中通过配置property标签给属性赋值,实际上是通过反射调用set方法完成属性的注入。

简单类型及字符串

  • 直接通过property标签的value属性赋值
    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <!-- 简单类型及字符串 -->
        <property name="stuNum" value="10001" />
        <property name="stuName" value="张三" />
        <property name="stuGender" value="女" />
        <property name="stuAge" value="20" />
        <property name="weight" value="92.3" />
    </bean>

日期对象-Java自身对象

  • 方式1:在property标签中通过ref引用Spring容器中的一个对象
    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <!--  日期类型 -->
        <property name="entranceTime" ref="date" />
    </bean>
    <bean id="date" class="java.util.Date" />
  • 方式2:在property标签中添加子标签bean来指定对象
    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <!--  日期类型 -->
        <property name="entranceTime">
            <bean class="java.util.Date" />
        </property>
    </bean>

自定义对象

  • 方式1:在property标签中通过ref引用Spring容器中的一个对象
    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <!-- 自定义对象类型 -->
        <property name="clazz" ref="cla" />
    </bean>

    <bean id="cla" class="com.xsbc.ioc.bean.Clazz">
        <property name="classId" value="2022" />
        <property name="className" value="Java2022" />
    </bean>
  • 方式2:在property标签中添加子标签bean来指定对象
    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <!-- 自定义对象类型 -->
        <property name="clazz">
            <bean class="com.xsbc.ioc.bean.Clazz">
                <property name="classId" value="2022" />
                <property name="className" value="Java2022" />
            </bean>
        </property>
    </bean>

集合类型

  • List

基本类型或字符串,

比如:List<String>

<property name="hobbies" value="旅游,电影" />

或者

<property name="hobbies">
      <list>
             <value>旅游</value>
             <value>电影</value>
       </list>

</property>

对象类型,List<Object>

<property name="hobbies">
      <list>

                <bean class="com.xsbc.ioc.bean.Book" />

                <bean class="com.xsbc.ioc.bean.Book" />

                <bean class="com.xsbc.ioc.bean.Book" />

       </list>

</property>

或者

<property name="hobbies">
      <list>

                <ref bean="book1" />  <!-- 引用容器中的备案 -->

                <ref bean="book2" />

       </list>

</property>

  • Set
        <property name="sets">
            <set>
                <!-- 此处与List一致 -->
                <value>1234</value>
                <value>3456</value>
            </set>
        </property>
  • Map
方式一:
        <property name="maps">
            <map>
               <entry>
                    <key>
                        <value>k2</value>
                    </key>
                    <value>456/value>
                </entry>
            </map>
        </property>

方式二:
        <property name="maps">
            <map>
                <entry value="123" key="k1"/>
            </map>
        </property>

3)构造器注入

public class Student {

    private String stuNum;
    private String stuName;
    private String stuGender;
    private int stuAge;
    private double weight;
    private Date date;//入学日期
    private Clazz clazz;
    public List<String> hobbies;
    public Set<String> sets;
    public Map<String,Object> maps;

    public Student(List<String> hobbies, Set<String> sets, 
            Map<String, Object> maps) {
        this.hobbies = hobbies;
        this.sets = sets;
        this.maps = maps;
    }
}
  • 简单类型、字符串、对象
    <!-- index表示参数列表的位置 -->
    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <constructor-arg index="0" value="10001" />  <!--字符串类型-->
        <constructor-arg value="小花" />
        <constructor-arg value="女" />
        <constructor-arg value="21" /><!--简单类型-->
        <constructor-arg value="50.2" />
        <constructor-arg ref="date" /><!--对象类型两种方式-->
        <constructor-arg>
            <bean class="com.xsbc.ioc.bean.Clazz" />
        </constructor-arg>
    </bean>
    <bean id="date" class="java.util.Date" />
  • 集合类型属性
    <bean id="stu" class="com.xsbc.ioc.bean.Student">
        <constructor-arg index="0">
            <list>
                <value>11</value>
                <value>22</value>
                <value>33</value>
            </list>
        </constructor-arg>
        <constructor-arg index="1">
            <set>
                <value>aa</value>
                <value>bb</value>
                <value>cc</value>
            </set>
        </constructor-arg>
        <constructor-arg index="2">
            <map>
                <entry key="k1" value="hua" />
                <entry key="k2" value="you" />
            </map>
        </constructor-arg>
    </bean>

2.5 Bean的作用域

在bean标签可以通过scope属性指定对象的作用域

  • ​​​​​​​​​scope="singleton",表示当前bean对象是单例模式,而且默认为饿汉模式(Spring容器初始化阶段就会完成此对象的创建;当bean标签中设置lazy-init="true"时,则会变为懒汉模式)
  • scope="prototype",表示当前bean非单例模式,每次通过Spring容器获取此bean对象时都会创建一个新的对象

2.6 Bean的生命周期方法

  • Bean类
public class Book {

    private int bookId;
    private String bookName;

    public void init(){
        //初始化方法:在创建当前类对象时调用的方法,常用于对象属性的初始化,
        //数据库连接初始化等操作
        System.out.println("-----init-------");
        this.bookId=10001;
        this.bookName="初始值";
    }

    public void destroy(){
        //销毁方法:在Spring销毁对象时调用此方法,进行一些资源回收性的操作
        System.out.println("------destroy------");
    }
}
  • Spring配置文件

在bean标签中通过init-method属性指定bean的初始化方法,初始化方法在构造器执行之后执行。 通过destroy-method属性指定bean的销毁方法,销毁方法在对象销毁之前执行。

    <bean id="book" class="com.xsbc.ioc.bean.Book" scope="prototype"
          init-method="init" destroy-method="destroy"/>

2.7 自动装配

自动装配:Spring在实例化当前bean时从Spring容器中找到匹配的实例赋值给当前bean的属性
  • byName 根据当前Bean的属性名在Spring容器中寻找匹配的对象,若根据name找了bean但类型不匹配则报错。
<bean id="stu" class="com.xsbc.ioc.bean.Student" autowire="byName"/>
  • byType 根据当前Bean的属性类型在Spring容器中寻找匹配的对象,若根据类型找到了多个备案也会报错。
<bean id="stu" class="com.xsbc.ioc.bean.Student" autowire="byType"/>

2.8 SpringIoC工作原理

三、Spring IoC—基于注解 

SpringIoC的使用,需通过XML将类声明给Spring容器进行管理,从而通过Spring工厂完成对象的创建及属性值的注入;

Spring除了提供XML的配置方式,同时提供了基于注解的配置:直接在实体类中添加注解声明给Spring容器管理,以简化开发步骤。 

3.1 Spring框架部署

1)创建Maven项目

同上

2)添加Spring IoC依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.13</version>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

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">
 
    
</beans>
  • 因为Spring容器初始化时,只会加载applicationContext.xml文件,则在实体类中添加注解就不会被Spring扫描,那么就需要在applicationContext.xml文件声明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"
       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:annotation-config />
    <!-- 声明Spring工厂注解的扫描范围 -->
    <context:component-scan base-package="com.xsbc.beans" />
</beans>

3.2 IoC常用注解

1)@Component

  • 类注解,声明此类被Spring容器管理,相当于bean标签的作用
  • @Component(value="stu") value属性用于指定当前bean的id,相当于bean标签的id属性;value属性也可以省略,此时默认值为类名首字母小写

@Service、@Controller、@Repository这三个注解也可以将类声明给Spring管理,区别是:

  • @Service 注解主要声明将控制器类配置给Spring管理,例如Servlet;
  • @Controller 注解主要声明业务类配置Spring管理,Service接口的实现类;
  • @Repository注解主要声明持久化类配置给Spring管理,DAO接口;
  • @Component除了控制器、service和DAO之外的一律使用此注解声明;

2)@Scope

  • 类注解,用于声明当前类单例模式还是非单例模式,相当于bean标签的scope属性;
  • @Scope(value="prototype") 表示当前类为非单例模式(默认是单例模式)

3)@Lazy

  • 类注解,用于声明一个单例模式的Bean是否为懒汉模式;
  • @Lazy(true)表示声明为懒汉模式,默认为饿汉模式;

4)@PostConstruct、@PreDestroy

  • @PostConstruct方法注解,声明一个方法为当前类的初始化方法(在构造器之后执行),相当于bean标签的init-method属性;
  • @PreDestroy方法注解,声明一个方法为当前类的销毁方法(在对象从容器中释放之前执行),相当于bean标签的destroy-method属性;

5)Autowired

  • 属性、方法(set)注解,声明当前属性自动装配,默认byType,默认必须(若没有找到类型与属性类型匹配的bean则抛出异常);
  • @Autowire(required=false) required属性设置当前装配是否为必须;
    byType

    ref引用

    @Autowired
    private Clazz clazz;

    @Autowired
    public void setClazz(@Qualifier("c2") Clazz clazz) {
        this.clazz = clazz;
    }

6)@Resource

  • 属性注解,也用于声明属性自动装配;
  • 默认装配方式为byName,若根据byName没找到对应的bean,则继续根据byType寻找对应的bean,根据byType如果仍然没有找到Bean或者找到不止一个类型匹配的bean,则抛出异常;

四、代理设计模式

4.1 生活中的代理

 代理设计模式的优点:

  • 将通用性的工作都交给代理对象完成,被代理对象只需专注自己的核心业务。

4.2 静态代理

 静态代理,代理类只能够为特定的类生产代理对象,不能代理任意类。

 使用代理的好处

  1. 被代理类只需关注核心业务的实现,将通用的管理型逻辑(事务管理、日志管理)和业务逻辑分离;
  2. 将通用的代码放在代理类中实现,提供了代码的复用性;
  3. 通过在代理类添加业务逻辑,实现对原有业务逻辑的扩展(增强);

4.3 动态代理

动态代理,几乎可以为所有的类产生代理对象。动态代理的实现方式有两种:

  • JDK动态代理
  • CGLib动态代理

1)JDK动态代理

  • JDK动态代理类实现
/*
    JDK动态代理:是通过被代理对象实现的接口产生其代理对象的
    1、创建一个类,实现InvocationHandler接口,重新invoke方法
    2、在类中定义一个Object类型的变量,并提供这个变量的有参构造器,用于将被代理对象传递进来
    3、定义getProxy方法,用于创建并返回代理对象
*/
public class JDKDynamicProxy implements InvocationHandler {
    //被代理对象
    private Object obj;
    public JDKDynamicProxy(Object obj) {
        this.obj = obj;
    }

    //产生代理对象,返回代理对象
    public Object getProxy() {
        //1、获取被代理对象的类加载器
        ClassLoader classLoader = obj.getClass().getClassLoader();
        //2、获取被代理对象的类实现的接口
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        //3、产生代理对象
        //第一个参数:被代理对象的类加载器
        //第二个参数:被代理对象实现的接口
        //第三个参数:使用产生代理对象调用方法时,用于拦截方法执行的处理器(拦截器)
        Object o = Proxy.newProxyInstance(classLoader, interfaces, this);
        return o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
        begin();
        Object returnValue = method.invoke(obj);//执行method方法
        commit();
        return returnValue;
    }

    public void begin() {
        System.out.println("----开启事务-----");
    }
    public void commit() {
        System.out.println("----提交事务-----");
    }
}
  • 测试类
import com.xsbc.dao.BookDAOImpl;
import com.xsbc.dao.GeneralDAO;
import com.xsbc.dao.JDKDynamicProxy;
import com.xsbc.dao.StudentDAOImpl;

public class TestDynamicProxy {
    public static void main(String[] args) {
        //1、被代理对象
        BookDAOImpl bookDAO=new BookDAOImpl();
        StudentDAOImpl studentDAO = new StudentDAOImpl();
        //2、创建动态代理类对象,并将代理对象传递到代理类中
        JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(studentDAO);
        //3、proxy就死产生的代理对象:产生的代理对象可以强转成被代理对象实现的接口类型
        GeneralDAO proxy=(GeneralDAO)jdkDynamicProxy.getProxy();

        //4、使用代理对象调用方法,并不会执行调用的方法,而是进入到创建
        //代理对象时指定InvocationHandler
        //的invoke方法,调用的方法作为一个参数,传入给了invoke方法
        proxy.insert();
    }
}

 2)JDK动态代理类实现

由于JDK动态代理是通过被代理类实现的接口来创建对象的,因此JDK动态代理只能代理实现接口的类对象。如果一个类没有实现任何接口,该如何产生代理对象呢?

  • CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此即使没有实现任何接口的类也可以通过CGLib产生代理对象;
  • CGLib动态代理不能为final类创建代理对象;
  • 添加CGLib的依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
  • CGLib动态代理实现
/*
    1、添加cglib依赖
    2、创建一个类,事项MethodInterceptor接口,同时实现接口中的intercept方法
    3、在类中定义一个Object类型的变量,并提交这个变量的有参构造器,用于传递被代理对象
    4、定义getProxy方法创建并返回代理对象(代理对象是通过被代理类的子类来创建的)
*/
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLibDynamicProxy implements MethodInterceptor {
    private Object obj;
    public CGLibDynamicProxy(Object obj) {
        this.obj = obj;
    }

    public Object getProxy(){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxy=enhancer.create();
        return proxy;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, 
            MethodProxy methodProxy) throws Throwable {
        begin();
        Object returnValue=method.invoke(obj,objects);//通过反射调用被代理方法
        commit();
        return returnValue;
    }

    public void begin() {
        System.out.println("----开启事务-----");
    }

    public void commit() {
        System.out.println("----提交事务-----");
    }
}
  • 测试
//1、创建被代理对象
BookDAOImpl bookDAO=new BookDAOImpl();
StudentDAOImpl studentDAO = new StudentDAOImpl();
  
//2、通过cglib动态代理类创建代理对象
CGLibDynamicProxy cgLibDynamicProxy= new CGLibDynamicProxy(bookDAO);
//3、代理对象实际上是被代理对象子类,因此代理对象可直接强制转换为被代理类型
BookDAOImpl proxy=(BookDAOImpl)cgLibDynamicProxy.getProxy();
//4、使用对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法,
//将当前调用的方法以及方法中的参数传递到intercept中
proxy.insert();

五、Spring AOP

5.1 AOP概念

Aspect Oriented Programmimg面向切面编程,是一种利用“横切”的技术(底层实现就是动态代理),对原有的业务逻辑进行拦截,并且可以在这个拦截的横切面上添加特定的业务逻辑,对原有的业务进行增强。

基于动态代理实现在不改变原有业务的情况下对业务逻辑进行增强。

 5.2 Spring AOP框架部署

1)创建Maven项目

同上

2)添加依赖

  • context
  • aspects
       <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.13</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.2</version>
        </dependency>

3)创建Spring配置文件

  • 需引入aop的命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">


</beans>

5.3 AOP配置-基于XML文件

在DAO的方法添加开启事务和提交事务的逻辑

1)创建一个类,定义要添加的业务逻辑

public class TxManager {
    
    public void begin(){
        System.out.println("---------开启事务---------");
    }
    public void commit(){
        System.out.println("---------提交事务---------");
    }
}

2)配置aop

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="bookDAO" class="com.xsbc.dao.BookDAOImpl" />
    <bean id="studentDAO" class="com.xsbc.dao.StudentDAOImpl" />

    <bean id="txManager" class="com.xsbc.utils.TxManager" />
    <aop:config>
        <!-- 声明切入点 -->
        <aop:pointcut id="book_all" expression="
        execution(* com.xsbc.dao.*.*())"/>
         <!-- 末尾的星号代表某个包中类的所有方法-->
        <!-- 声明txManager为切面 -->
        <aop:aspect ref="txManager">
            <!-- 通知 -->
            <aop:before method="begin" pointcut-ref="book_all"/>
            <aop:after method="commit" pointcut-ref="book_all"/>
        </aop:aspect>
    </aop:config>
</beans>

AOP开发步骤:

  • 创建切面类,在切面类定义切点方法;
  • 将切面类配置给Spring容器;
  • 声明切入点;
  • 配置AOP的通知策略;

5.4 切入点的声明

1)各种切入点声明方式

        <!-- 使用aop:pointcut标签声明切入点:切入点可以是一个方法 -->
        <!-- 切入点:dao包中所有类所有无参数的方法 -->
        <aop:pointcut id="pc" expression="
        execution(* com.xsbc.dao.*.*())"/> 
        <!-- 末尾的星号代表某个包中类的所有方法-->

        <!-- 切入点:BookDAOImp类中所有无参数没有返回值的方法 -->
        <aop:pointcut id="pc1" expression="
        execution(void com.xsbc.dao.BookDAOImpl.*())" />
        <!-- 切入点:BookDAOImp类中所有没有返回值的方法 -->
        <aop:pointcut id="pc2" expression="
        execution(void com.xsbc.dao.BookDAOImpl.*(..))" />
        <!-- 切入点:BookDAOImp类中所有无参数的方法 -->
        <aop:pointcut id="pc3" expression="
        execution(* com.xsbc.dao.BookDAOImpl.*())" />
        <!-- 切入点:java包下类中所有的方法 -->
        <aop:pointcut id="pc4" expression="execution(* *(..))" />

2)AOP使用注意事项

  • 如果要使用Spring aop面向切面编程,调用切入点方法的对象必须通过Spring容器获取;
  • 如果一个类中的方法被声明为切入点之后,通过Spring容器获取该类对象,实际上是获取的是一个代理对象;
  • 如果一个类中方法没有被声明为切入点,则通过Spring容器获取的及时这个类的真实创建的对象;
BookServiceImpl bookService=(BookServiceImpl) 
context.getBean("bookServiceImpl");
bookService.addBook();

5.5 AOP通知策略

AOP通知策略:将切面类中的切点方法如何织入到切入点

  • before
  • after
  • after-throwing
  • after-returning
  • around

1)定义切面类

public class MyAspect {

    public void method1(){
        System.out.println("......method1");
    }
    public void method2(){
        System.out.println("......method2");
    }
    public void method3(){
        System.out.println("......method3");
    }
    public void method4(){
        System.out.println("......method4");
    }

    /*
        环绕通知的切点方法,必须要遵循以下的定义规则:
        1、必须带有一个ProceedingJoinPoint类型的参数
        2、必须带有Object类型的返回值
        3。在前后增强的业务逻辑之间执行Object v=point.proceed();
        4、方法最后返回v
    */
    public Object method5(ProceedingJoinPoint point) throws Throwable {
        System.out.println("......method5.....before");
        Object v=point.proceed();
        System.out.println("......method5.....after");
        return v;
    }

}

2)配置切面类

<aop:aspect ref="myAspect">
     <!-- aop:before 前置通知,切入到指定切入点之前 -->
     <aop:before method="method1" pointcut-ref="book_insert" />
     <!-- aop:after 后置通知,切入到指定切入点之后 -->
     <aop:after method="method2" pointcut-ref="book_insert" />
     <!-- aop:after-throwing 异常通知,切入点抛出异常之后 -->
     <aop:after-throwing method="method3" pointcut-ref="book_insert" />
     <!--
         aop:after-returning 方法返回之后,对于一个Java方法而言 return返回值
         也是方法的一部分,因此方法返回值之后和方法执行结束是同一个时间点,所以after
         和after-returning根据配置的顺序决定执行顺序
     -->
     <aop:after-returning method="method4" pointcut-ref="book_insert" />
     <!-- aop:around -->
     <aop:around method="method5" pointcut-ref="book_insert" />
</aop:aspect>

六、Spring AOP注解配置

6.1 Spring AOP注解配置框架部署

1)创建Maven工程

同上

2)添加Spring依赖

  • context
  • apects

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"
       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:annotation-config />
    <context:component-scan base-package="com.xsbc" />
    
    <!-- 基于注解配置的aop代理 -->
    <aop:aspectj-autoproxy />
</beans>

6.2 AOP注解配置案例

@Component
@Aspect
public class TransactionManager {
    @Pointcut("execution(* com.xsbc.dao.*.*())")
    public void point(){}

    @Before("point()") 
    //等同于@Before("execution(* com.xsbc.dao.*.*())")
    public void begin(){
        System.out.println("-----开启事务-----");
    }

    @After("point()")
    public void commit(){
        System.out.println("-----提交事务-----");
    }

    @Around("point()")
    public Object printExecuteTime(ProceedingJoinPoint pt) throws Throwable {
        long start=System.currentTimeMillis();
        Object v=pt.proceed();
        long end=System.currentTimeMillis();
        System.out.println("-----time:"+(end-start));
        return v;
    }
}

注意:注解使用虽然使用方便,但是只能在源码上添加注解,因此自定义类提倡使用注解进行配置,但如果使用第三方提供的类则需要通过xml配置完成。

七、Spring整合MyBatis

Spring两大核心思想:IoC和AOP

  • IoC:控制反转,Spring容器可以完成对象的创建、属性注入、对象管理等工作;
  • AOP:面向切面,在不修改原有业务逻辑情况下,实现原有业务的增强;

7.1 Spring可以对MyBatis提供哪些支持?

1)SpringIoC

  • 需创建数据源DataSource
  • 需创建SqlSessionFactory对象
  • 需创建SqlSession对象
  • 需创建DAO对象(Mapper)

SpringIoc 可以为MyBatis完成DataSource、SqlSessionFactory、SqlSession以及DAO对象的创建

2)Spring AOP

  • 使用Spring提供的事务管理切面类完成MyBatis数据库操作中的事务管理

7.2 Spring整合MyBatis准备工作

1)创建Maven工程

2)部署MyBatis框架

  • 添加依赖

Mysql驱动、mybatis

        <!-- MyBatis依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
  • 创建配置文件之后无需进行任何配置:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

</configuration>

3)部署Spring框架

  • 添加依赖
       <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.13</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.13</version>
        </dependency>
  • 创建Spring配置文件: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"
       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">


</beans>

4)添加Spring整合MyBatis的依赖

  • mybatis-spring就是mybatis提供的兼容Spring的补丁
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>

7.3 Spring整合MyBatis的配置

1)整合Druid连接池

  • 添加druid依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>
  • 创建druid.properties属性文件
druid.driver=com.mysql.jdbc.Driver
druid.url=http://localhost:3306/db_2021_mybatis?characterEncoding=utf-8
druid.username=root
druid.password=1234

# 连接池参数
druid.pool.init=2
druid.pool.minIdle=3
druid.pool.maxActive=20
druid.pool.timeout=30000
  • 在applicationContext.xml中配置DruidDataSource
    <!-- 加载druid.properties属性文件 -->
    <context:property-placeholder location="classpath:druid.properties" />

    <!-- 依赖Spring容器完成数据源dataSource的创建 -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${druid.driver}" />
        <property name="url" value="${druid.url}" />
        <property name="username" value="${druid.username}" />
        <property name="password" value="${druid.password}" />

        <property name="initialSize" value="${druid.pool.init}" />
        <property name="minIdle" value="${druid.pool.minIdle}" />
        <property name="maxActive" value="${druid.pool.maxActive}" />
        <property name="maxWait" value="${druid.pool.timeout}" />
    </bean>

2)整合MyBatis-创建SqlSessionFactory

    依赖Spring容器完成MyBatis的sqlSessionFactory对象的创建 

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 配置数据源 -->
    <property name="dataSource" ref="druidDataSource" />
    <!-- 配置mapper文件的路径 -->
    <property name="mapperLocations" value="classpath:mappers/*Mapper.xml" />
    <!-- 配置需要定义别名实体类的包 -->
    <property name="typeAliasesPackage" value="com.xsbc.pojo" />
    <!-- 可选:配置MyBatis的主配置文件 -->
    <property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>

3)整合MyBatis-创建Mapper

加载DAO包中所有DAO接口,通过sqlSessionFactory获取SqlSession,然后创建所有DAO接口对象,存储在Spring容器中。
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" 
        value="sqlSessionFactory" />
        <property name="basePackage" value="com.xsbc.dao" />
    </bean>

7.4 Spring整合MyBatis完成AOP配置

使用Spring提供的事务管理切面类完成DAO中增删改操作的事务管理

1)事务的隔离级别

isolation 设置事务隔离级别。假设两个事务T1、T2

  • SERIALIZABLE:T1在执行过程中,T2既不能读也不能写;
  • REPEATABLE_READ:T1在执行过程中,T2只能读,但不能修改,可以添加数据(幻读)
  • READ_COMMITTED:T1在执行过程中,T2可以读也可以写, 但是T1只能读取到T2提交后的数据(不可重复读)(幻读)
  • READ_UNCOMMITTED:T1在执行过程中,T2既可以读也可以写, T1可以读取到T2未提交的数据(脏读)(不可重复读)(幻读)

2)事务的传播机制

 

  • REQUIRED 如果上层方法没有事务,则创建一个新的事务,如果存在事务,则加入到当前事务中。
  • SUPPORTS 如果上层方法没有事务,则以非事务方式执行,如果存在事务,则加入到当前事务中。
  • REQUIRES_NEW 如果上层方法没有事务,则创建一个新的事务,如果存在事务,则将当前事务挂起。
  • NOT_SUPPORTED 如果上层方法没有事务,则以非事务方式执行,如果存在事务,则将当前事务挂起。
  • NEVER 如果上层方法没有事务,则以非事务方式执行,如果存在事务,则抛出异常。 MANDATORY 如果上层方法已经存在事务,则加入事务中执行,如果不存在事务,则抛出异常。
  • NESTED 如果上层方法没有事务,则创建一个新的事务,如果存在事务,则嵌套当前事务中。

3)Spring AOP事务管理配置-XML配置

<!-- 1、将Spring提供的事务管理配置给Spring容器 -->
<bean id="transactionManager" 
 class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="druidDataSource" />
</bean>
<!-- 2、通过Spring jdbc提供的tx标签,声明事务管理策略 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="insert*" isolation="REPEATABLE_READ" 
        propagation="REQUIRED"/>
        <tx:method name="delete*" isolation="REPEATABLE_READ" 
        propagation="REQUIRED"/>
        <tx:method name="update*" isolation="REPEATABLE_READ" 
        propagation="REQUIRED"/>
        <tx:method name="query*" isolation="REPEATABLE_READ" 
        propagation="SUPPORTS" />
    </tx:attributes>
</tx:advice>
<!-- 3、将事务管理策略以AOP配置应用于DAO操作方法 -->
<aop:config>
    <aop:pointcut id="crud" expression=
    "execution(* com.xsbc.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="crud" />
</aop:config>

4)Spring AOP事务管理配置-注解配置

  • 配置事务管理,声明使用注解方式进行事务管理
<!-- 1、将Spring提供的事务管理配置给Spring容器  -->
<bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="druidDataSource" />
</bean>
<!-- 2、声明使用注解完成事务配置 -->
<tx:annotation-driven transaction-manager="transactionManager" />
  • 在需要Spring进行事务管理的方法上添加@Transactional注解
@Transactional(isolation=Isolation.REPEATABLE_READ,
propagation=Propagation.SUPPORTS)
public List<User> listUsers(){
    return userDAO.queryUsers();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

路上的追梦人

您的鼓励就是我最大的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值