狂神说 | Spring完整版笔记

6 篇文章 1 订阅

链接: 狂神说 | Mybatis完整版笔记.

目录

1.Spring

1.1简介

  • Spring : 春天 —>给软件行业带来了春天

  • 2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。

  • 2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。

  • 很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。

  • Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术

  • 官网 : http://spring.io/

  • 官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/

  • GitHub : https://github.com/spring-projects

  • SSH:Struct2 + Spring + Hibernate

  • SSM:SpringMvc + Sprting + Mybatis

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.9</version>
</dependency>

1.2优点

  • Spring是一个开源免费的框架 , 容器 .
  • Spring是一个轻量级的框架 , 非侵入式的 .
  • 控制反转 IoC , 面向切面 Aop
  • 对事物的支持 , 对框架的支持

总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

1.3组成

image-20211105161201707

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。

img

成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI

1.4扩展

现代化的java开发,说白了就是基于Spring的开发!

💻[APP] 👉 Spring Boot[构建一切] 👉 Spring Cloud[协调一切] 👉 Spring Cloud Data Flow[连接一切]

Spring Boot与Spring Cloud

  • Spring Boot
    • 是一个快速开发的脚手架
    • 基于Spring Boot 可以快速的开发单个微服务
    • 约定大于配置
  • Spring Cloud
    • Spring Cloud 是基于SpringBoot实现的

现在大多数公式都在使用SpringBoot进行快速开发,学习SpringBoot的提前,需要完全掌握Spring以及SpringMVC

弊端:发展了太久之后,违背了原来的理念!配置十分繁琐

1.5Spring核心

Spring核心就是提供了一个容器(container),通常成为Spring应用上下文(Spring application context)它们会创建和管理组件。这些组件也可以成为bean,会在spring应用上下文中装配在一起,从而行程一个完整的程序应用。

将bean装配在一起的行为是通过==依赖注入(dependency injection,DI)==的模式实现,此时,组件不会再去创建它所依赖的组建并管理他的生命周期,使用依赖注入的应用依赖于单独的实体来创建和维护所有的组建,并将其注入到需要它们的bean中。通常,这是通过构造器参数和属性访问方法来实现的

使用Spring应用上下文将bean装配在一起的方式有两种:

  • XML文件:使用一个或者多个XML文件将bean转配在一起
  • java注解

2.IOC理论

1.UserDao接口

2.UserDaoImpl实现类

3.UserService 业务接口

4.UserService 业务实现类

建立一个空白的Maven项目

  • Dao层

    package xiaoqi.dao;
    
    public interface UserDao {
        void getUser();
    }
    
  • DaoImpl实现

    package xiaoqi.dao;
    
    public class UserDaoImpl implements UserDao{
    
        /**
         * 默认获取用户的接口
         */
        @Override
        public void getUser() {
            System.out.println("默认获取用户数据");
        }
    }
    
  • Service层

    package xiaoqi.Servise;
    
    import xiaoqi.dao.UserDao;
    
    public interface UserServise {
    
        void getUser();
    
    }
    
  • ServiceImpl实现

    package xiaoqi.Servise;
    
    import xiaoqi.dao.UserDao;
    import xiaoqi.dao.UserDaoImpl;
    
    public class UserServiseImpl implements UserServise{
      
        @Override
        public void getUser() {
            userDao.getUser();
        }
    }
    
  • 测试

    import xiaoqi.Servise.UserServise;
    import xiaoqi.Servise.UserServiseImpl;
    import xiaoqi.dao.UserDao;
    import xiaoqi.dao.UserDaoImpl;
    import xiaoqi.dao.UserDaoMysqlImpl;
    
    public class MyTest {
        public static void main(String[] args) {
            UserServise userServise = new UserServiseImpl();
            userServise.getUser();
        }
    }
    

正在这种写法中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码!如果程序代码量十分庞大,修改一次的代价将会十分昂贵

这里我们使用一个动态的Set接口实现

void setUserDao(UserDao userDao);
private UserDao userDao;

public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}
import xiaoqi.Servise.UserServise;
import xiaoqi.Servise.UserServiseImpl;
import xiaoqi.dao.UserDao;
import xiaoqi.dao.UserDaoImpl;
import xiaoqi.dao.UserDaoMysqlImpl;

public class MyTest {
    public static void main(String[] args) {
        UserServise userServise = new UserServiseImpl();
        //动态获取Dao层的实现
        userServise.setUserDao(new UserDaoMysqlImpl());
        userServise.getUser();
    }
}

以前所有东西都是由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者 。程序不用去管怎么创建,怎么实现了 . 它只负责提供一个接口。这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 。耦合性大大降低 . 这也就是IOC的原型 !

image-20211105161820165

IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

IOC就是解耦合

img

IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

container magic

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)

3.HelloSpring

3.1Bean

在 Spring 中,构成应用程序主干并由 Spring IoC 容器 Management 的对象称为 bean。 Bean 是由 Spring IoC 容器实例化,组装和以其他方式 Management 的对象。否则,bean 仅仅是应用程序中许多对象之一。 Bean 及其之间的依赖关系反映在容器使用的配置元数据中

3.2编写代码

Hello实体类

package xiaoqi.pojo;

public class Hello {
    private String name;

    public Hello(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Hello{" +
                "name='" + name + '\'' +
                '}';
    }



}

编写我们的spring文件 , 这里我们命名为beans.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">
    <!--配置元数据
        使用spring来创建对象,在spring中这些都称为bean
        (1) id属性是标识单个 bean 定义的字符串。 变量名
        (2) class属性定义 bean 的类型并使用完全限定的类名。 new的对象
        preperty 相当于给对象中的一个属性设置一个值
        id属性的值是指协作对象。在此示例中未显示用于引用协作对象的 XML。-->
    <!--bean = 对象  相当于 new Hello()
        类型 变量名 = new 类型()-->
    <bean id = "hello" class = "xiaoqi.pojo.Hello">
        <constructor-arg value="xiaoqi.pojo.Hello"/>
        <property name="name" value="Spring"/>
    </bean>
</beans>

测试

import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.pojo.Hello;

public class Mytest {
    public static void main(String[] args) {
        //实例化容器
        //提供给ApplicationContext构造函数的位置路径是资源字符串,这些资源字符串使容器可以从各种外部资源加载配置元数据。
        //获取spring的上下文对象
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象现在都在Spring中管理。我们要使用,直接去里面取出来
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}
  • Hello 对象是谁创建的 ? 【hello 对象是由Spring创建的】
  • Hello 对象的属性是怎么设置的 ? hello 对象的属性是由Spring容器设置的

这个过程就叫控制反转 :

  • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

依赖注入 : 就是利用set方法来进行注入的.

IOC是一种编程思想,由主动的编程变成被动的接收

可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .

org.springframework.context.ApplicationContext接口代表 Spring IoC 容器,并负责实例化,配置和组装 Bean。容器通过读取配置元数据来获取有关要实例化,配置和组装哪些对象的指令,配置元数据以 XML,Java 注解或 Java 代码表示。它使您能够表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。所谓的IOC就是:对象由Spring来创建、管理、装配

3.3例子

新建一个beans.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="mysqlImpl" class="xiaoqi.dao.UserDaoMysqlImpl"></bean>
    <bean id="userImpl" class="xiaoqi.dao.UserDaoImpl"></bean>
    <!--在Service中使用这个对象-->
    <bean id="UserServiceImpl" class="xiaoqi.Servise.UserServiceImpl">
        <!--ref引用Spring容器中,创建好的对象
            value就是具体的值,基本数据类型
        -->
        <property name="userDao" ref="userImpl"/>
    </bean>
</beans>

测试beans.xml

import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.Servise.UserServiceImpl;

public class MyTest {
    public static void main(String[] args) {
        //获取AplicationContext,拿到Spring的容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserServiceImpl userServiceImpl = (UserServiceImpl)context.getBean("UserServiceImpl");
        userServiceImpl.getUser();
    }
}

到了现在 , 我们彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !

4.Ioc创建对象的方式

4.1使用无参构造创建对象,默认实现

package xiaoqi.pojo;

public class User {
    private String name;


    public User() {
        System.out.println("user的无参构造");
    }

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println("name" + name);
    }
}
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.pojo.User;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //这个地方对象已经被创建了
        User user = (User)context.getBean("user");//输出user的无参构造
        user.show();

    }
}

image-20211110093601276

4.2使用有参构造实现

使用下标

<!--第一种,下标赋值
    使用属性明确指定构造参数的索引-->
<bean id="user" class="xiaoqi.pojo.User">
    <constructor-arg index="0" value="小七"></constructor-arg>
</bean>

通过类型创建对象

<constructor-arg type="java.lang.String" value = "xiaoqi"></constructor-arg>

参数名构造

<constructor-arg name="name" value="xiaoqi"></constructor-arg>

总结:在配置文件加载的时候。其中管理的对象都已经初始化了!

package xiaoqi.pojo;

public class UserT {
    private String name;

    public UserT() {
        System.out.println("userT被创建");
    }

    public UserT(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
<bean id="user" class="xiaoqi.pojo.User">
    <constructor-arg name="name" value="xiaoqi"></constructor-arg>
</bean>
<!--bean的时候已经被实例化了-->
<bean id="userT" class="xiaoqi.pojo.UserT">

</bean>
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.pojo.User;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //这个地方对象已经被创建了
        User user = (User)context.getBean("user");//输出user的无参构造
        User user1 = (User) context.getBean("user");
        System.out.println(user == user1);//内存中只有一个实例对象
    }
}

image-20211110095431468

5.Spring配置说明

5.1别名

<!--别名,添加了别名,也可以通过获取别名来获取对象-->
<alias name="user" alias="useralisa"></alias>

5.2Bean的配置

<!--bean的时候已经被实例化了-->
<!--id:bean的唯一标识符,也就是相当于我们学的对象名
    class:bean对象所对应的全限定名:包名+类名
    name:就是别名,name可以同时取多个别名
    -->
<bean id="userT" class="xiaoqi.pojo.UserT" name = "user2,u2">
    <property name="name" value="xiaoqi"/>
</bean>

5.3import

这个import,一般用于团队开发使用,它可以将多个配置文件,导入合并为一个

假设现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,可以来利用import将所有人的beans.xml合并为一个总的

  • 1
  • 2
  • 3
  • applicationContext
<import resource="beans.xml"/>
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>

使用的时候,直接使用总的就可以

6.DI依赖注入

  • 依赖注入(Dependency Injection,DI)。
  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

6.1构造器注入

image-20211113144421874

6.2Set方式注入-属性注入

【环境搭建】

复杂类型
package xiaoqi.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
真实测试对象
package xiaoqi.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> habbies;
    private Map<String,String> card;
    private Set<String> games;
    private Properties info;
    private String wife;

}
beans.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="student" class="xiaoqi.pojo.Student">
        <!--第一种普通值注入,value-->
        <property name="name" value="小七"/>
    </bean>
</beans>
测试类
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.pojo.Student;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getName());

    }
}

完善注入信息

第二种注入方式-Bean注入
<bean id="student" class="xiaoqi.pojo.Student">
     <!--第二种bean注入,ref-->
     <property name = "address"  ref="address"/> 
</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">
    <!--定义address这个类-->
    <bean id="address" class ="xiaoqi.pojo.Address"></bean>

    <bean id="student" class="xiaoqi.pojo.Student">
        <!--第三种数组注入-->
        <property name="books">
           <array>
               <value>red</value>
               <value>yellow</value>
               <value>blue</value>
           </array>
        </property>
    
        <!--list-->
        <property name="habbies">
            <list>
                <value>singing</value>
                <value>watching</value>
                <value>watching</value>
            </list>
        </property>
    
        <!--map-->
        <property name="card">
            <map>
                <entry key="idcard" value="123456789123456"/>
                <entry key="card" value="1234444444"/>
            </map>
        </property>
    
        <!--set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>Qirginal</value>
            </set>
        </property>
    
        <!--String-->
        <property name="wife">
            <null></null>
        </property>
    
        <!--Properties-->
        <property name="info">
            <props>
                <prop key="no">1</prop>
                <prop key="sex">M</prop>
                <prop key="name">xiaoming</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>
</beans>
测试
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.pojo.Student;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
        /*
         Student{name='小七', address=Address{address='null'},
         books=[red, yellow, blue],
         habbies=[singing, watching, watching],
         card={idcard=123456789123456, card=1234444444},
         games=[LOL, Qirginal], info={password=123456,
         name=xiaoming, sex=M, no=1},
         wife='null'}
         */

    }
}

6.3其他方式注入

可以使用p命名空间和c命名空间进行注入

官方解释:

具有 p 名称空间的 XML 快捷方式-property

p-namespace 允许您使用bean元素的属性(而不是嵌套的<property/>元素)来描述协作 Bean 的属性值,或同时使用这两者。

Spring 支持基于 XML Schema 定义的可扩展配置格式with namespaces。本章讨论的beans配置格式在 XML Schema 文档中定义。但是,p 命名空间未在 XSD 文件中定义,仅存在于 Spring 的核心中。

image-20211113144954054

使用:

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p:properity p命名空间注入,可以直接注入属性-->
    <bean id="user" class="xiaoqi.pojo.User" p:name="x" p:age="18">
    </bean>
</beans>

测试:

public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user", User.class);
    System.out.println(user);

}

具有 c-namespace 的 XML 快捷方式-constructor-arg

具有 p-命名空间的 XML 快捷方式相似,在 Spring 3.1 中引入的 c-namespace 允许使用内联属性来配置构造函数参数,而不是嵌套的constructor-arg元素。

image-20211113145026811

使用:

<?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:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--c命名注入是通过构造器注入:construct-args-->
    <bean id="user2" class="xiaoqi.pojo.User" c:age="18" c:name="c"></bean>

</beans>

测试:

public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user2", User.class);
    System.out.println(user);

}

注意点:这里需要在User中把有参构造器加上因为c是构造器注入

6.4Bean的作用域

ScopeDescription
singleton(默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。
prototype将单个 bean 定义的作用域限定为任意数量的对象实例。
request将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext中有效。
session将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
application将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
websocket将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。

单例范围(singleton)-默认

所有人共享这个对象

image-20211113150012267

<!--设置为单例模式-->
<bean id="user2" class="xiaoqi.pojo.User" c:age="18" c:name="c" scope="singleton"></bean>
public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user2", User.class);
    User user2 = context.getBean("user2", User.class);
    System.out.println(user==user2);

}

返回值为true,说明共享一个对象

原型范围

每一次从容器get的时候,都会产生一个新的对象!

image-20211113150551117

<!--设置为原形模式-->
<bean id="user2" class="xiaoqi.pojo.User" c:age="18" c:name="c" scope="prototype"></bean>
public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user2", User.class);
    User user2 = context.getBean("user2", User.class);
    System.out.println(user==user2);

}

返回值为false,说明每一个对象都是独立的

其余的request、session、application

这些只能在web开发中只能使用

image-20211113151237250

7.bean的自动装配

  • 自动装配是Spring满足bean依赖一种方式!
  • Spring会在上下文中自动寻找,并自动给bean装配属性!

在Spring中有三种自动装配的方式

  1. xml显示的配置
  2. java中显示配置
  3. 隐式的自动装配bean【重要】

7.1测试

环境搭建

  1. 编写实体类
package xiaoqi.pojo;

public class Dog {
    public void shout(){
        System.out.println("shout");
    }
}
package xiaoqi.pojo;

public class Cat {
    public void shout(){
        System.out.println("shout");
    }
}
package xiaoqi.pojo;

public class Person {
    private Cat cat;
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}
  1. 编写beans.cml文件
<?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="cat" class="xiaoqi.pojo.Cat"/>
    <bean id="dog" class="xiaoqi.pojo.Dog"/>
    <bean id="person" class="xiaoqi.pojo.Person">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
        <property name="name" value="xiaoqi"/>
    </bean>
</beans>
  1. 测试
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.pojo.Person;

public class MyTest {
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person", Person.class);
        person.getDog().shout();
        person.getCat().shout();
    }
}

7.2 byName自动装配

<bean id="cat" class="xiaoqi.pojo.Cat"/>
<bean id="dog" class="xiaoqi.pojo.Dog"/>
<!--byName:会自动在容器上下文中查找,和自己对象set方法后面的值相对应的bean id
-->
<bean id="person" class="xiaoqi.pojo.Person" autowire="byName">
    <property name="name" value="xiaoqi"/>
</bean>

我们将 cat 的bean id修改为 catXXX

再次测试, 执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。

小结:

当一个bean节点带有 autowire byName的属性时。

  1. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
  2. 去spring容器中寻找是否有此字符串名称id的对象。
  3. 如果有,就取出注入;如果没有,就报空指针异常。
  4. 需要保证所有的bean的id唯一,并且保证这个bean需要和自动注入的属性set方法的值一致。

7.3 byType自动装配

<bean id="cat" class="xiaoqi.pojo.Cat"/>
<bean id="dog1" class="xiaoqi.pojo.Dog"/>
<!--
    byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean,bean中的id可以省略
-->
<bean id="person" class="xiaoqi.pojo.Person" autowire="byType">
    <property name="name" value="xiaoqi"/>
</bean>
  1. 将user的bean配置修改一下 : autowire=“byType”

  2. 测试,正常输出

  3. 在注册一个cat 的bean对象!

<bean id="dog" class="xiaoqi.pojo.Dog"/>
<bean id="cat" class="xioaqi.pojo.Cat"/>
<bean id="cat2" class="xiaoqi.pojo.Cat"/>

<bean id="user" class="xiaoqi.pojo.User" autowire="byType">
   <property name="name" value="xiaoqi"/>
</bean>
  1. 测试,报错:NoUniqueBeanDefinitionException

  2. 删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。

小结:

  1. 需要保证bean的class唯一,并且保证bean需要和自动注入的属性的类型一致

7.4 使用注解实现自动装配

准备工作:利用注解的方式注入属性。

  1. 导入约束:context约束

  2. 配置注解的支持 :< context:annotation-config/>

    <?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/>
        <bean id="cat" class="xiaoqi.pojo.Cat"/>
        <bean id="dog" class="xiaoqi.pojo.Dog"/>
        <bean id="person" class="xiaoqi.pojo.Person"/>
    
    </beans>
    
    
  3. 在java中添加注解

    package xiaoqi.pojo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    public class Person {
        @Autowired
        private Cat cat;
        @Autowired
        private Dog dog;
        private String name;
    
        public Cat getCat() {
            return cat;
        }
        
        public Dog getDog() {
            return dog;
        }
    
    
        @Override
        public String toString() {
            return "Person{" +
                    "cat=" + cat +
                    ", dog=" + dog +
                    ", name='" + name + '\'' +
                    '}';
        }
    
    }
    

@Autowired

  • 直接在属性上使用即可!也可以在set方式上使用
  • 使用Autowried我们可以不用写Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname

科普:

@Nullable 字段标记了这个注解,说明这个字段可以为null
public @interface Autowired {
    boolean required() default true;
}
//如果显示定义了Autowried的required属性为false。说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Cat cat;

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

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

如果@Autowried自动装配的环境比较复杂,自动装配无法通过以一注解【@Autowired】完成的时候,我们还可以使用@Qualifier(value = “xxx”)去配置@Autowried的使用,去指定唯一一个bean对象注入

@Resource

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

小结:

  • @Autowried和@Resource都是用来自动装配的,都可以放在属性字段上
  • @Autowried通常情况下是通过byType实现的,可是在多个实现类的时候,byType不是唯一的方式,而是需要通过byName的方式来注入,而这个name默认就是根据变量名来的
  • @Resource是通过byName方式实现的,如果找不到,再通过byType方式

8.使用注解开发

在spring4之后,想要使用注解形式,必须得要引入aop的包

image-20211114145328821

在配置文件当中,还得要引入一个context约束,增加注解的支持

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>

</beans>

8.1bean

@Component

放在类上说明这个类被Spring管理了,就是bean

//等价于  <bean id="user" class="xiaoqi.pojo.User"/>
//Component 组件

8.2属性如何注入

实体类

package xiaoqi.pojo;

import org.springframework.stereotype.Component;
//等价于  <bean id="user" class="xiaoqi.pojo.User"/>
//Component 组件
@Component
public class User {

    public String name = "xiaoqi";
}

xml文件中没有写入bean

image-20211114150246355

测试类

image-20211114150350843

@Value

package xiaoqi.pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
    /*相当于
     * <bean id="user" class="xiaoqi.pojo.User">
        <property name="name" value="xiaoqi"/>
       </bean>*/
    @Value("xiaoqi")
    public String name;
}

8.3衍生的注解

@Component有几个衍生注解,我们在web开发总,会按照MVC架构分层

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

8.4自动装配

  • @Component
  • @Autowired
  • @Resource
  • @Nullable 字段标记了这个注解,说明这个字段可以为null

8.5作用域

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
//单例模式
@Scope("singleton")

8.6小结

xml与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>  

作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null!

9.基于 Java 的容器配置

我们现在要完全不使用spring的xml配置,全权交给java来做

image-20211115153126470

JavaConfig是Spring的一个子项目,在Spring4之后,它变成了一个核心功能

image-20211115154306104

AnnotationConfigApplicationContext:注解配置的上下文
ClassPathXmlApplicationContext:xml配置的上下文

具体的用法:

要被配置的实体类User

package xiaoqi.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
//注解的意思:这个类被Spring接管了,注册到容器中
public class User {
    private String name;

    public String getName() {
        return name;
    }

    @Value("xiaoqi")//属性注入值
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

Java的配置类

package xiaoqi.pojo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
//用@Configuration注解类表示该类的主要目的是作为 Bean 定义的来源,和bean.xml是一样的
这个也会被Spring托管注册到容器中,他与Component是两种不同的配置方式
@Import(Config2.class)//这两就会把两个类,配置成一个类
public class Config {
    //注册一个bean,就相当于写的一个bean
    //这个方法的名字就是相当于bean标签中id的名字,class属性就是方法的返回值
    @Bean
    //定义一种方法来实现bean中的依赖关系
    public User getUser() {
        return new User();//就是要返回要注入到bean中的对象。
    }
}

另一个配置类

package xiaoqi.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config2 {
}

测试类

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import xiaoqi.pojo.Config;
import xiaoqi.pojo.User;

public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类来做,我们就只通过AnnotationConfigApplicationContext来获取容器,通过配置类Class来加载
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);//注解配置的上下文
        User getUser = applicationContext.getBean("getUser", User.class);//得到类中的方法名,返回User,方法名就是类的名字
        System.out.println(getUser.getName());
    }
}

这种纯java的配置方式,在SpringBoot中随处可见。

10.代理模式

为什么要学习代理模式,因为AOP的底层机制就是动态代理!

代理模式的分类:

  • 静态代理
  • 动态代理
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I5OPbmmc-1638149595700)(C:/Users/77/AppData/Roaming/Typora/typora-user-images/image-20211116082951347.png)]

10.1静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人

代理模式的好处:

  • 可以使用真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共的也就交给代理角色!实现业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

实现代理过程

Rent.Java即抽象角色

package xiaoqi.pojo;

//租房接口
public interface Rent {
    public void rent();
}

Host 房东

package xiaoqi.pojo;

//房东
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房主要租房子");
    }
}

Proxy 代理角色

package xiaoqi.pojo;

public class Proxy implements Rent{
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    @Override
    //帮房东租房子
    public void rent() {
        host.rent();
    }

    //看房
    public void seeHouse(){
        System.out.println("看房");
    }

    public void free(){
        System.out.println("中介费");
    }

    public void hetong(){
        System.out.println("签合同");
    }
}

Client 客户

package xiaoqi.pojo;

public class Client {
    public static void main(String[] args){
        //房东要住房子
        Host host = new Host();
        //代理,中介帮房东租房子,一般代理角色会有一些附属操作
        Proxy proxy = new Proxy(host);
        //通过代理租房子
        proxy.seeHouse();
        proxy.free();
        proxy.hetong();
        proxy.rent();
    }
}

分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。

10.2静态代理加深理解

  1. 创建一个抽象业务角色
package xiaoqi.demo02;

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();

}
  1. 需要一个真实的对象来完成增删改查
package xiaoqi.demo02;

public class UserServiceImpl implements UserService{

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

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void select() {
        System.out.println("select");
    }

}
  1. 需求来了,现在我们需要增加一个日志功能,怎么实现!
  • 思路1 :在实现类上增加代码 【麻烦!】
  • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!
  1. 设置一个代理角色
package xiaoqi.demo02;

public class UserServiceProxy implements UserService{
    UserService userService;

    public UserServiceProxy() {
    }

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void select() {
        log("select");
        userService.select();
    }

    //日志方法
    public void log(String msg){
        System.out.println("使用了"+ msg +"方法");
    }
}
  1. 测试
package xiaoqi.demo02;

public class Client {

    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
        userServiceProxy.add();
    }
}

我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想

image-20211116091623154

10.3动态代理

  • 动态搭理和静态搭理的角色是一样的
  • 动态代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:一类是基于接口的动态代理,一类是基于类的动态代理
    • 基于接口 --JDK的动态搭理
    • 基于类 – cglib
    • 现在用的比较多的是java字节码javasist来生成动态代理
    • 我们这里使用JDK的原生代码来实现

需要了解两个类:

  • Proxy:代理
  • InovcationHandler:调用处理程序的接口

image-20211116092435165

image-20211116093236080

image-20211116094137468

【动态搭理为什么返回的是接口】

  1. 在需要继承proxy类获得有关方法和InvocationHandler构造方法传参的同时,java不能同时继承两个类,我们需要和想要代理的类建立联系,只能实现一个接口

  2. 需要反射获得代理类的有关参数,必须要通过某个类,反射获取有关方法,如本次测试用的 :printSomeThing

  3. 成功返回的是object类型,要获取原类,只能继承/实现,或者就是那个代理类

  4. 对具体实现的方法内部并不关心,这个交给InvocationHandler.invoke那个方法里去处理就好了,我只想根据你给我的接口反射出对我有用的东西。

  5. 考虑到设计模式,以及proxy编者编写代码的逻辑使然

为什么要求被代理对象必须实现一个接口?因为JDK动态代理类已经继承了Proxy这个类,所以只能通过接口来与被代理类建立联系(两个类建立起联系,一是继承的关系【jdk已经不能通过这个方式了,因为java仅支持单继承】,另一种就是实现同一个接口【JDK动态代理选这种】),所以必须要求被代理类也得实现一个接口,这样的话代理类与被代理类就能通过这个接口建立联系了

编写动态代理

接口

package xiaoqi.demo03;

//租房接口
public interface Rent{
    public void rent();
}

真实的被代理类

package xiaoqi.demo03;

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色:现在没有
        ProxyInvocationHamlder proxyInvocationHamlder = new ProxyInvocationHamlder();
        //通过调用程序处理角色,来处理我们要调用的接口对象
        proxyInvocationHamlder.setRent(host);
        Rent proxy = (Rent) proxyInvocationHamlder.getProxy();//这里的proxy就是动态生成的,我们并没有写
        proxy.rent();
    }
}

房东

package xiaoqi.demo03;

import xiaoqi.demo03.Rent;

//房东
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房主要租房子");
    }
}

动态代理类

package xiaoqi.demo03;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//我们会用着类,自动生成代理类
public class ProxyInvocationHamlder implements InvocationHandler {
    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);
    }

    @Override
    //处理代理事例,并返回结果
    /**
     *
     * proxy -代理实例,调用的方法
     * method - 对接口方法的调用相应的method代理实例的方法实例。声明类的方法对象将接口的方法声明,这可能是一个超接口代理接口,代理类继承的方法。
     * args -包含在方法上通过代理实例调用的实参的值对象的数组,或 null如果接口方法不需要参数。原始类型的实参是包裹在适当的原始包装类的实例,如 java.lang.Integer或 java.lang.Boolean。
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理本质:就是使用反射机制
        seeHouse();

        Object result = method.invoke(rent, args);
        fare();
        return result;
    }

    public void seeHouse(){
        System.out.println("看房子");
    }
    public void fare(){
        System.out.println("收费中介");
    }
}

深化理解

通用的动态代理类

package xiaoqi.demo04;

import xiaoqi.demo03.Rent;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//我们会用着类,自动生成代理类
public class ProxyInvocationHamlder implements InvocationHandler {
    //被代理的接口
    private Object traget;

    //获取代理接口
    public void setTraget(Object traget) {
        this.traget = traget;
    }

    //生成得到代理对象,反悔的是接口
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),traget.getClass().getInterfaces(),this);
    }

    @Override
    //处理代理事例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理本质:就是使用反射机制
        log(method.getName());
        Object result = method.invoke(traget, args);
        return result;
    }
    public void log(String msg){
        System.out.println("执行了" + msg + "方法");
    }

}

测试

package xiaoqi.demo04;

import xiaoqi.demo02.UserService;
import xiaoqi.demo02.UserServiceImpl;

public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色
        ProxyInvocationHamlder pih = new ProxyInvocationHamlder();
        pih.setTraget(userService);//设置要代理的对象
        //动态生成代理类
        UserService proxy = (UserService) pih.getProxy();
        proxy.delete();
    }
}

动态代理的好处

静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口!

11.AOP

11.1什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

img

11.2AOP在Spring中的作用

提供声明式事务:允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

img

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

img

即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

11.3使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
    <scope>runtime</scope>
</dependency>

方式一:使用spring接口

编写业务接口和实现类

package xiaoqi.Service;

public interface UserService {
    public void add();
    public void delete();
    public void updata();
    public void select();
}
package xiaoqi.Service;

public class UserServiceIMpl implements UserService{

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

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void updata() {
        System.out.println("updata");
    }

    @Override
    public void select() {
        System.out.println("select");
    }
}

日志类,前置和后置

package xiaoqi.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class log implements MethodBeforeAdvice {
    @Override
    //Method : 要执行的目标对象的方法
    //objects: 参数
    //target : 目标对象
    public void before(Method method, Object[] objects, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+ "的" + method.getName() + "被执行了");
    }
}
package xiaoqi.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class afterlog implements AfterReturningAdvice {
    @Override
    /**
     * returnVal : 返回值
     */
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + method.getName() + "方法,返回结果为:" + returnValue);
    }
}

在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">
   
    <!--注册bean-->
    <bean id="userService" class="xiaoqi.Service.UserServiceIMpl"/>
    <bean id="log" class="xiaoqi.log.log"/>
    <bean id="afterlog" class="xiaoqi.log.afterlog"/>
    <!--方式一:使用原生Spring API接口-->
    <!--配置AOP:需要导入aop的约束-->
    <aop:config>
        <!--首先需要一个切入点:在哪个地方去执行这个方法
        experssion: 表达式 execution(要执行的位置)
        -->
       <aop:pointcut id="pointcut" expression="execution(* xiaoqi.Service.UserServiceIMpl.*(..))"/>
        <!--执行环绕增加!
            advice-ref:执行方法  pointcut-ref:切入点
        -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

测试

import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.Service.UserService;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Springapplication.xml");
        //动态代理的是接口
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .

Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .

execution表达式

execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?) 

通过类定义切点

execution(* com.baobaotao.Waiter.*(…))

匹配Waiter接口的所有方法,它匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()方法。第一个* 代表返回任意类型,com.baobaotao.Waiter.*代表Waiter接口中的所有方法;

execution(* com.baobaotao.Waiter+.*(…))

匹配Waiter接口及其所有实现类的方法,它不但匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()这两个Waiter接口定义的方法,同时还匹配NaiveWaiter#smile()和NaughtyWaiter#joke()这两个不在Waiter接口中定义的方法。

第二种方式:自定义类来实现AOP

写一个切入类

package xiaoqi.DIY;

public class DiyPointCut {
    public void before(){
        System.out.println("before");
    }
    public void after(){
        System.out.println("after");
    }
}

配置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: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-->
    <bean id="userService" class="xiaoqi.Service.UserServiceIMpl"/>
    <bean id="log" class="xiaoqi.log.log"/>
    <bean id="afterlog" class="xiaoqi.log.afterlog"/>vice-ref="afterlog" pointcut-ref="pointcut"/>
    </aop:config>-->

    <!--方式二:自定义-->
    <bean id = "diy" class="xiaoqi.DIY.DiyPointCut"/>
    <aop:config>
        <!--自定义切面:ref 要引用的类-->
        <aop:aspect ref="diy">
            <!--切入点,在哪里执行-->
            <aop:pointcut id="point" expression="execution(* xiaoqi.Service.UserServiceIMpl.*(..))"/>
            <!--通知在哪里执行-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

测试

import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.Service.UserService;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Springapplication.xml");
        //动态代理的是接口
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

第三种方式:使用注解实现

package xiaoqi.DIY;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.io.PrintWriter;

//使用注解方式实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPoinCut {
    @Before("execution(* xiaoqi.Service.UserServiceIMpl.*(..))")
    public void before(){
        System.out.println("-------before--------");
    }
    @After("execution(* xiaoqi.Service.UserServiceIMpl.*(..))")
    public void after(){
        System.out.println("-------after--------");
    }
    //在环绕增强中,我们可以给定一个参数,代表我们要处理切入的点
    @Around("execution(* xiaoqi.Service.UserServiceIMpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint)throws Throwable{
        System.out.println("before");
        //获得签名
        System.out.println(joinPoint.getSignature());
        //执行方式
        Object proceed = joinPoint.proceed();
        System.out.println("after");
        //打印执行方法
        System.out.println(proceed);

    }
}

配置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: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-->
    <bean id="userService" class="xiaoqi.Service.UserServiceIMpl"/>
    <bean id="log" class="xiaoqi.log.log"/>
    <bean id="afterlog" class="xiaoqi.log.afterlog"/>

    <!--方式三:注解-->
    <bean id="annotationpointcut" class="xiaoqi.DIY.AnnotationPoinCut"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>
</beans>

aop:aspectj-autoproxy:说明

通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

12.整合Mybatis

mybatis-spring –官网

步骤:

  1. 导入相关jar包

    • junit
    • mybatis
    • mysq数据库
    • spring相关的
    • aop织入
    • mybatis-spring
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.22</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.13.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.13.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>
    
  2. 编写配置文件

  3. 测试

12.1 回忆Mybatis

  1. 编写实体类

    package xiaoqi.pojo;
    
    public class User {
        private int id;
        private String name;
        private String pwd;
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", pwd='" + pwd + '\'' +
                    '}';
        }
    }
    
  2. 编写核心配置文件

    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <!--核心配置文件-->
    <configuration>
        <typeAliases>
            <package name="xiaoqi.pojo"/>
        </typeAliases>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=GMT"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    
        <!--注册-->
        <mappers>
            <package name="xiaoqi.dao"/>
        </mappers>
    </configuration>
    
  3. 编写接口

    package xiaoqi.dao;
    
    import xiaoqi.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        List<User> selectUser();
    }
    
  4. 编写Mapper.xml

    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="xiaoqi.dao.UserMapper">
        <select id = "selectUser" resultType="User">
            select * from user;
        </select>
    </mapper>
    
  5. 测试

    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;
    import xiaoqi.dao.UserMapper;
    import xiaoqi.pojo.User;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    
    
    public class Mytest {
        @Test
        public void test(){
            String resource = "mybatis-config.xml";
            InputStream input;
            SqlSessionFactory build;
            try {
                input = Resources.getResourceAsStream(resource);
                build = new SqlSessionFactoryBuilder().build(input);
                SqlSession sqlSession = build.openSession(true);
                UserMapper mapper = sqlSession.getMapper(UserMapper.class);
                List<User> userList = mapper.selectUser();
                for (User user :userList){
                    System.out.println(user);
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

12.2 Mybatis-Spring

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。

image-20211128094646287

要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。

在 MyBatis-Spring 中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:

<!--sqlsessionfactoy对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="datasource"/>
    <!--绑定mybatis配置文件--> 
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations" value="classpath:xiaoqi/dao/UserMapper.xml"/>
</bean>

注意:SqlSessionFactory需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。

SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。

一个常用的属性是 configLocation,它用来指定MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings> 或 < typeAliases>元素。

    <property name="configLocation" value="classpath:mybatis-config.xml"/>

需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(< environments>),数据源(< DataSource>)和 MyBatis 的事务管理器(< transactionManager>)都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。

模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。

可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <!--只能使用构造器注入sqlSessionFactory 因为他没有set方法,只有构造方法-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

<bean id="UserMapperImpl" class="xiaoqi.dao.UserMapperImpl">
    <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>

现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:

public class UserMapperImpl implements UserMapper{
    //我们所有的操作都是用sqlSession来操作,现在都是用SqlSessionTemplate;
    private SqlSessionTemplate sqlSessionTemplate;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

在Spring-mybatis中的配置-实现整合方式一

编写数据源配置

<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=GMT"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>

sqlsessionFactory,关联mybatis

<!--sqlsessionfactoy对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="datasource"/>
    <!--绑定mybatis配置文件-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations" value="classpath:xiaoqi/dao/UserMapper.xml"/>
</bean>

sqlSessionTemplate,关联sqlSessionFactory

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <!--只能使用构造器注入sqlSessionFactory 因为他没有set方法,只有构造方法-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

接口、实现类和xml文件

package xiaoqi.dao;

import xiaoqi.pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> selectUser();
}
package xiaoqi.dao;

import org.mybatis.spring.SqlSessionTemplate;
import xiaoqi.pojo.User;

import java.util.List;

public class UserMapperImpl implements UserMapper{
    //我们所有的操作都是用sqlSession来操作,现在都是用SqlSessionTemplate;
    private SqlSessionTemplate sqlSessionTemplate;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

mapper配置文件

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xiaoqi.dao.UserMapper">
    <select id = "selectUser" resultType="User">
        select * from user;
    </select>
</mapper>

在application.xml中注册bean

实现类注入

<?xml version="1.0" encoding="UTF8" ?>
<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">
    <import resource="Spring-dao.xml"/>

    <bean id="UserMapperImpl" class="xiaoqi.dao.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
    </bean>
</beans>

测试

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;;
import xiaoqi.dao.UserMapperImpl;
import xiaoqi.pojo.User;

import java.util.List;

public class Mytest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Application.xml");
        UserMapperImpl userMapperImpl = context.getBean("UserMapperImpl", UserMapperImpl.class);
        List<User> userList = userMapperImpl.selectUser();
        for (User user:userList){
            System.out.println(user);
        }
    }
}

核心配置文件简化

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <typeAliases>
        <package name="xiaoqi.pojo"/>
    </typeAliases>
</configuration>

12.3 实现整合方式二

image-20211128151723733

dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 可跟踪源码查看

编写接口、接口实现类和接口xml文件

package xiaoqi.dao;

import xiaoqi.pojo.User;

import java.util.List;

public interface UserMapper2 {
    List<User> selectUser();
}
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xiaoqi.dao.UserMapper2">
    <select id = "selectUser" resultType="User">
        select * from user;
    </select>
</mapper>
package xiaoqi.dao;

import org.mybatis.spring.support.SqlSessionDaoSupport;
import xiaoqi.pojo.User;

import java.util.List;

public class UserMapper2Impl extends SqlSessionDaoSupport implements UserMapper2{

    @Override
    public List<User> selectUser() {
        //实际上还是整合方式一,只不过通过继承来得到了sqlSessionTemplate
        return getSqlSessionTemplate().getMapper(UserMapper2.class).selectUser();
    }
}

编写application中的bean配置

<bean id="UserMapper2Impl" class="xiaoqi.dao.UserMapper2Impl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

测试

@Test
public void test1(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Application.xml");
    UserMapper2Impl userMapperImpl = context.getBean("UserMapper2Impl", UserMapper2Impl.class);
    List<User> userList = userMapperImpl.selectUser();
    for (User user:userList){
        System.out.println(user);
    }
}

13.声明式事务

13.1回顾事务

  • 事务:同时成功,同时失败
  • 事务在项目开发中,十分的重要,涉及到数据一致性的问题
  • 确保完整性和一致性

事物的ACID原则:

  • 原子性:事务时原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么全部失败,也就是说一个事务不可能只执行了一半就停止了
  • 一致性:一旦所有事务动作完成,事务就要被提交。是指事务的运行并不改变数据库中数据的一致性
  • 隔离性:可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
  • 持久性:事务一旦完成,无论系统发生什么错误,结果都不会受到影响,通常情况下,事务的结果要被持久化到数据库中。

13.2 spring中的十事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

  • 声明式事务:
    • 一般情况下比编程式事务好用。
    • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
    • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
  • 编程式事务:
    • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
    • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

13.3 在原有代码上配置一个事务

导入约束

<?xml version="1.0" encoding="UTF8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xsi:schemaLocation="  http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

在Spring-dao中配置事务管理器

<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <constructor-arg ref="datasource" />
</bean>

结合AOP织入事务

<!-- 结合AOP实现事务的织入-->
 <!--配置事务的类-->
 <tx:advice id="txadvice" transaction-manager="transactionManager">
     <!--给方法配置事务-->
     <!--传播事务的新特性  propagation:传播特性 REQUIRED:默认选项-->
     <tx:attributes>
         <tx:method name="add" propagation="REQUIRED"/>
         <tx:method name="delet"/>
         <tx:method name="*"/>
     </tx:attributes>
 </tx:advice>

 <!--配置事务切入-->
 <aop:config>
     <aop:pointcut id="txpointcut" expression="execution(* xiaoqi.dao.*.*(..))"/>
     <!--执行事务的方法,切入点为xiaoqi.dao下所有类的所有方法-->
     <aop:advisor advice-ref="txadvice" pointcut-ref="txpointcut"/>
 </aop:config>

spring事务传播特性:

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

进行测试

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.dao.UserMapperImpl;
import xiaoqi.pojo.User;

import java.util.List;

public class MyTest {
    @Test
    public void test() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Application.xml");
        UserMapperImpl userMapperImpl = context.getBean("UserMapperImpl", UserMapperImpl.class);
        List<User> userList = userMapperImpl.selectUser();
        for (User u : userList) System.out.println(u);
    }

}

13.4 为什么需要配置事务?

  • 如果不配置,就需要我们手动提交控制事务;
  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题。
    受到影响,通常情况下,事务的结果要被持久化到数据库中。

13.2 spring中的十事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

  • 声明式事务:
    • 一般情况下比编程式事务好用。
    • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
    • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
  • 编程式事务:
    • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
    • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

13.3 在原有代码上配置一个事务

导入约束

<?xml version="1.0" encoding="UTF8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xsi:schemaLocation="  http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

在Spring-dao中配置事务管理器

<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <constructor-arg ref="datasource" />
</bean>

结合AOP织入事务

<!-- 结合AOP实现事务的织入-->
 <!--配置事务的类-->
 <tx:advice id="txadvice" transaction-manager="transactionManager">
     <!--给方法配置事务-->
     <!--传播事务的新特性  propagation:传播特性 REQUIRED:默认选项-->
     <tx:attributes>
         <tx:method name="add" propagation="REQUIRED"/>
         <tx:method name="delet"/>
         <tx:method name="*"/>
     </tx:attributes>
 </tx:advice>

 <!--配置事务切入-->
 <aop:config>
     <aop:pointcut id="txpointcut" expression="execution(* xiaoqi.dao.*.*(..))"/>
     <!--执行事务的方法,切入点为xiaoqi.dao下所有类的所有方法-->
     <aop:advisor advice-ref="txadvice" pointcut-ref="txpointcut"/>
 </aop:config>

spring事务传播特性:

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

进行测试

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import xiaoqi.dao.UserMapperImpl;
import xiaoqi.pojo.User;

import java.util.List;

public class MyTest {
    @Test
    public void test() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Application.xml");
        UserMapperImpl userMapperImpl = context.getBean("UserMapperImpl", UserMapperImpl.class);
        List<User> userList = userMapperImpl.selectUser();
        for (User u : userList) System.out.println(u);
    }

}

13.4 为什么需要配置事务?

  • 如果不配置,就需要我们手动提交控制事务;
  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题。
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
整理自尚硅谷视频教程springboot高级篇,并增加部分springboot2.x的内容 一、Spring Boot与缓存 一、JSR107 Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。 • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可 以在运行 期访问多个CachingProvider。 • CacheManager定义了创建、配置、获取、管理和控制多个唯一命名 的Cache,这些Cache 存在于CacheManager的上下文中。一个CacheManager仅被一个 CachingProvider所拥有。 • Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个 Cache仅被一个 CacheManager所拥有。 • Entry是一个存储在Cache中的key-value对。 • Expiry 每一 个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期 的状态。一旦过期,条 目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。 二、Spring缓存抽象 Spring从3.1开始定义了org.springframework.cache.Cache 和 org.springframework.cache.CacheManager接口来统一不同的缓存技术; 并支持使用JCache(JSR- 107)注解简化我们开发; • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合; • Cache接 口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache 等; • 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否 已经被调用 过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法 并缓存结果后返回给用户。下 次调用直接从缓存中获取。 • 使用Spring缓存抽象时我们需要关注以下两点; 1、确定方法需要被缓存 以及他们的缓存策略 2、从缓存中读取之前缓存存储的数据 Cache 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、 ConcurrentMapCache等 CacheManager 缓存管理器,管理各种缓存(Cache)组件 @Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 @CacheEvict 清空缓存 @CachePut 保证方法被调用,又希望结果被缓存。 @EnableCaching 开启基于注解的缓存 keyGenerator 缓存数据时key生成策略 serialize 缓存数据时value序列化策略 @CacheConfig 抽取缓存的公共配置 三、几个重要概念&缓存注解 1、常用注解 2、常用参数 名字 位置 描述 示例 methodName root object 当前被调用的方法名 #root.methodName method root object 当前被调用的方法 #root.method.name target root object 当前被调用的目标对象 #root.target targetClass root object 当前被调用的目标对象类 #root.targetClass args root object 当前被调用的方法的参数列表 #root.args[0] 3、常用参数SPEL明 名字 位置 描述 示例 caches root object 当前方法调用使用的缓存列表(如 @Cacheable(value= {"cache1","cache2"}) ), 则有两 个cache #root.caches[0].name argument name evaluation context 方法参数的名字. 可以直接 #参数 名 ,也可以使用 #p0或#a0 的形 式,0代表参数的索引; #iban 、 #a0 、 #p0 result evaluation context 方法执行后的返回值(仅当方法执 行之后的判断有效,如‘unless’ , ’cache put’的表达式 ’cache evict’的表达式 beforeInvocation=false ) #result 四、代码中使用缓存 1、搭建基本环境 1、导入数据库文件 创建出department和employee表 2、创建javaBean封装数据 3、整合MyBatis操作数据库 1.配置数据源信息 2.使用注解版的MyBatis; 1)、@MapperScan指定需要扫描的mapper接口所在的包

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小七rrrrr

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值