【开发】后端框架——Spring

前置知识:JSP&Servlet

学习视频:https://www.bilibili.com/video/BV1WE411d7Dv?spm_id_from=333.999.0.0

  • IoC:控制反转

    IoC的理解:IoC思想,IoC怎么创建对象,IoC是Spring的核心

    依赖注入三种方式:构造器、set方法、第三方

  • Bean的作用域:2+4

    单例与原型

    request,session,appication=websocket

  • Bean属性的自动装配:四种

    xml显示配置

    隐式自动配置(ByName——参数名,ByType——参数类型)

    注解

    实现配置类:不用xml配置,把配置项作为类属性

  • Bean的生命周期

  • 代理模式

  • AOP

Spring

Spring简介

解决企业级应用开发的复杂性创建,简化开发

轻量级控制反转(IOC)和面向切面编程(AOP)的容器框架

  • 2002,interface21 Spring框架雏形
  • Spring理念:整合现有的框架
  • SSH:Struct2 + Spring + Hibernate
  • SSM:SpringMVC + Spring + MyBatis

官网

文档地址

各版本文档

下载地址

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.3.3version>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-jdbcartifactId>
    <version>5.3.3version>
dependency>

优点

  • 开源的框架
  • 轻量级的、非入侵式的框架
  • 控制反转(IOC)、面向切面编程(AOP)
  • 支持事务处理
  • 对框架整合的支持

弊端:发展太久,配置十分繁琐

  • 配置地狱

组成

【开发】后端框架——Spring_第1张图片

拓展

  • Spring Boot:快速开发脚手架
    • 快速开发单个微服务
    • 约定大于配置
  • Spring Cloud:协调
    • 基于Spring Boot实现
    • 微服务的整合

IoC容器

为什么有IoC思想

以JDBC连接数据库为例

原先

【开发】后端框架——Spring_第2张图片

  1. UserDao接口

    public interface UserDao {
        public void getUser();
    }
    
  2. UserDaoImpl实现类

    //UserDaoImpl.class
    public class UserDaoImpl implements UserDao{
        public void getUser(){
            System.out.println("获取用户数据");
        }
    }
    
    //UserDaoMysqlImpl
    public class UserDaoMysqlImpl implements UserDao{
        public void getUser(){
            System.out.println("MySql获取用户数据");
        }
    }
    
    //UserDaoOracleImpl
    public class UserDaoOracleImpl implements UserDao{
        public void getUser(){
            System.out.println("Oracle实现获取用户数据");
        }
    }
    
  3. UserService业务接口

    public interface UserService {
        public void getUser();
    }
    
  4. UserServicelmpl业务实现类

    public class UserServiceImpl implements UserService{
        private UserDao userDao = new UserDaoMysqlImpl();
    						//  = new UserDaoOracleImpl();
       						//  = new UserDaoImpl();
        public void getUser(){
            userDao.getUser();
        }
    }
    
  5. MyTest

    public class MyTest {
        public static void main(String[] args) {
            //用户实际调用的是业务层
            UserService userService = new UserServiceImpl();
            userService.getUser();
        }
    }
    

IoC雏形

【开发】后端框架——Spring_第3张图片

  1. UserDao接口

    public interface UserDao {
        public void getUser();
    }
    
  2. UserDaoImpl实现类

    //UserDaoImpl.class
    public class UserDaoImpl implements UserDao{
        public void getUser(){
            System.out.println("获取用户数据");
        }
    }
    
    //UserDaoMysqlImpl
    public class UserDaoMysqlImpl implements UserDao{
        public void getUser(){
            System.out.println("MySql获取用户数据");
        }
    }
    
    //UserDaoOracleImpl
    public class UserDaoOracleImpl implements UserDao{
        public void getUser(){
            System.out.println("Oracle实现获取用户数据");
        }
    }
    
  3. UserService业务接口

    public interface UserService {
        public void getUser();
    }
    
  4. UserServicelmpl业务实现类

    public class UserServiceImpl implements UserService{
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao){
            this.userDao = userDao;
        }
    
        public void getUser(){
            userDao.getUser();
        }
    }
    
  5. MyTest

    public class MyTest {
        public static void main(String[] args) {
            //用户实际调用的是业务层
            UserService userService = new UserServiceImpl();
    
            ((UserServiceImpl)userService).setUserDao(new UserDaoOracleImpl());
            userService.getUser();
        }
    }
    

控制反转关键

public void setUserDao(UserDao userDao){
    this.userDao = userDao;
}

之前,程序主动创建对象,控制权在程序

使用set注入,程序变成了被动接收对象

  • 程序员不用再去管理对象的创建
  • 系统的耦合性降低,更加专注在业务实现上,这是IOC的原型

IoC是Spring框架的核心

控制反转是一种通过描述(XML或注解)及第三方去生产或获取特定对象的方式

  • 可使用XML配置

    采用XML方式配置Bean,Bean的定义与实现分离

  • 使用注解配置

    采用注解方式将Bean的定义和实现合为一体

    Bean的定义信息直接以注解的形式定义在实现类中,从而达到零配置目的

IOC是一种设计思想,DI(依赖注入)是实现IOC的一种方法,在Spring中实现IoC的是IoC容器

控制反转:获得对象的方式反转

  • 程序由主动创建对象变为被动接收对象

【开发】后端框架——Spring_第4张图片

依赖注入

  • 利用set方法进行注入

Spring通过IoC容器完成对实例的创建,装配,管理

  • 不再需要改动程序,要实现不同的操作,只需在xml配置文件中进行修改

相关jar包

Theorg.springframework.beansandorg.springframework.contextpackages are the basis for Spring Framework’s IoC container.

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-beansartifactId>
    <version>5.2.0.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>5.2.0.RELEASEversion>
dependency>

xml方式

beans.xml

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    
	<bean id="userServiceImpl" class="com.kuang.service.UserServiceImpl">
        <property name="userDao" ref="userDaoMysqlImpl"/>
    bean>
    
beans>
获取IoC容器(Spring的Context对象)中类资源

The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory.

BeanFactory 定义了能够管理所有类的配置机制,ApplicationContext 是其子接口

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("");

The ApplicationContext is the interface for an advanced factory capable of maintaining a registry of different beans and their dependencies. By using the method T getBean(String name, Class requiredType), you can retrieve instances of your beans.

ApplicationContext 是维护不同bean及其相关依赖的注册工厂

test
@Test
public void test(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
	
    //对象由主动创建变为被动接收
    UserService userService = (UserServiceImpl) applicationContext.getBean("userServiceImpl");
    userService.getUser();
}

Spring配置

bean

<bean id="hello" class="com.kuang.pojo.Hello" name="hello2,u2">
    <constructor-arg name="str" value="Spring"/>
bean>

中配置的是交给IoC容器管理的类资源

  • The property name element refers to the name of the JavaBean property
  • and the ref element refers to the name of another bean definition.

The id attribute is a string that identifies the individual bean definition.

The class attribute defines the type of the bean and uses the fully qualified classname.

全限定名:包名.类型

If you want to introduce other aliases for the bean, you can also specify them in the name attribute, separated by a comma (,), semicolon (;), or white space

bean别名——alias

<bean id="hello" class="com.kuang.pojo.Hello">
    <constructor-arg name="str" value="Spring"/>
bean>

<alias name="hello" alias="h"/>

【开发】后端框架——Spring_第5张图片

import

将多个配置文件导入到一个,便于统一管理


<import resource="beans1.xml">
<import resource="beans2.xml">
<import resource="beans3.xml">

依赖注入

  • 依赖:bean对象的创建依赖于IoC容器
  • 注入:bean对象中的所有属性,由容器来注入

1. IoC创建对象方式——构造器注入

在配置文件加载时,容器中管理的对象就已被初始化

默认使用无参构造器创建对象
使用有参构造器构造对象
  1. 下标赋值-index

    <bean id="hello" class="com.kuang.pojo.Hello">
        <constructor-arg index="0" value="Spring"/>
    bean>
    
  2. 参数类型匹配-type

    <bean id="hello" class="com.kuang.pojo.Hello">
        <constructor-arg type="java.lang.String" value="Spring"/>
    bean>
    
  3. 直接通过参数名设置-name

    <bean id="hello" class="com.kuang.pojo.Hello">
        <constructor-arg name="str" value="Spring"/>
    bean>
    

2. set注入

public class Student {
    private String name;//基本数据类型 DI
    private Address address;//引用数据类型 DI
    private String[] books;//数组 DI
    private List<String> hobbys;//List DI
    private Map<String,String> card;// Map DI
    private Set<String> games;//Set DI
    private String wife;//null DI
    private Properties info;//properties DI
}
基本数据类型
<bean id="student" class="com.kuang.pojo.Student">
    <property name="name" value="Auspice Tian"/>
bean>
bean
<bean id="beijing" class="com.kuang.pojo.Address">
    <property name="address" value="北京"/>
bean>

<bean id="student" class="com.kuang.pojo.Student">
	<property name="address" value="beijing"/>
bean>
array
<property name="books">
    <array>
        <value>红楼梦value>
        <value>西游记value>
        <value>水浒传value>
        <value>三国演义value>
    array>
property>
list
<property name="hobbys">
    <list>
        <value>gggvalue>
        <value>pppvalue>
        <value>zzzvalue>
    list>
property>
map
<property name="card">
    <map>
        <entry key="身份证" value="123456789111111"/>
        <entry key="学生证" value="123456489789789"/>
    map>
property>
set
<property name="games">
    <set>
        <value>LOLvalue>
        <value>COCvalue>
        <value>BOBvalue>
    set>
property>
null
<property name="wife">
    <null>null>
property>
property
<property name="info">
    <props>
        <prop key="driver">driverprop>
        <prop key="url">urlprop>
        <prop key="username">rootprop>
        <prop key="password">2017002231prop>
    props>
property>

【开发】后端框架——Spring_第6张图片

第三方注入

可直接注入基本数据类型和bean

p-namespace(set注入)

导入约束:xmlns:p=“http://www.springframework.org/schema/p”

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean name="john-modern" class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    bean>
    
    
beans>
c-namespace(constructor注入)

导入约束:xmlns:c=“http://www.springframework.org/schema/c”

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="[email protected]"/>
    bean>

    
    <bean id="beanOne" class="x.y.ThingOne" 
          c:thingTwo-ref="beanTwo"
          c:thingThree-ref="beanThree"
          c:email="[email protected]"/>

beans>

Bean Scopes(作用域)

<bean id="student" class="com.kuang.pojo.Student" scope="singleton | prototype">
Scope Description
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.全局唯一
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

Singleton(单例模式)-单线程

【开发】后端框架——Spring_第7张图片

  • 全局唯一,共享同一对象

prototype(原型模式)-多线程

【开发】后端框架——Spring_第8张图片

  • 每次getBean()都会得到新的对象

Bean的自动装配

Spring会在上下文中自动寻找,自动装配属性

在xml中显式配置

<bean id="cat" class="com.kuang.pojo.Cat" />
<bean id="dog" class="com.kuang.pojo.Dog" />

<bean id="person" class="com.kuang.pojo.Person">
    <property name="cat" ref="cat"/>
    <property name="dog" ref="dog"/>
    <property name="name" value="Auspice Tian"/>
bean>

隐式自动装配Bean

ByName自动装配

setter的形参名与容器中的bean.id相同

bean的id唯一

  • java.lang.NullPointerException
<bean id="person" class="com.kuang.pojo.Person" autowire="byName" />
ByType自动装配

属性类型与bean类型相同

与属性类型相同的Bean唯一

  • expected single matching bean but found 2: cat1,cat2
<bean id="person" class="com.kuang.pojo.Person" autowire="byType" />

使用注解自动装配

最佳实践:XML用于管理Bean,注解用于管理属性注入

Spring 2.5开始支持注解开发,JDK1.5支持注解

在Spring4之后,若使用注解开发,还必须保证 aop包 导入

  1. 导入约束

    xmlns:context=“http://www.springframework.org/schema/context”

    xsi:schemaLocation=“http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd”

  2. 配置注解的支持

  3. 指定要扫描的包,该包下的注解会生效


<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 
       https://www.springframework.org/schema/beans/spring-beans.xsd     
       http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.kuang.pojo"/>
    <context:annotation-config/>
beans>
@Component——bean

组件,放在类上,说明这个类被Spring管理,就是Bean

/*
* @Component 相当于
* */
@Component
public class User {
    private String name = "Auspice Tian";
    
    /* setters and getters */
}
衍生注解

在Web开发中,会按照MVC分层,与@Component等价

  • Dao
    • @Repository
  • Service
    • @Service
  • Controller
    • @Controller
@Value("")——属性

通过注解注入,在属性与属性的setters()上注入等价

@Component
public class User {
    @Value("Auspice Tian")
    private String name;
    
    @Value("Auspice Tian")
    public void setName(String name) {
        this.name = name;
    }
}
注解实现自动装配

@AutoWired

  • 相当于byType
  • bean属性setters 上注解
  • @AutoWired(required=false):表示该注解修饰的内容可以为空
    • CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION_TYPE

@Qualifier(value="cat1111")

  • 通过bean.id指定容器中装配的Bean
  • 当IoC Container环境复杂,搭配使用

@Nullable:表示这个注解修饰的内容允许为Null

  • METHOD、PARAMETER、FIELD

@Resource

  • @Resource(name="指定bean.id")
  • 当 ByName失效,通过ByType找到唯一的类型匹配的Bean
作用域

@Scope("")

  • singleton
  • prototype

在java中显式配置——零配置xml

JavaConfig是Spring的子项目,Spring4后,称为核心功能

【开发】后端框架——Spring_第9张图片

//com.config.AppConfig.java

@Configuration
//相当于 由IoC容器托管,被@Component注解
@Import(AppConfig2.class)//导入其他配置文件
@ComponentScan("com.kuang")//指定扫描的包,使包下注解生效
public class AppConfig {
    @Bean
    /* 注册一个Bean 相当于
    * 返回值类型相当于 bean.class
    * 函数名相当于bean.id
    * */
    public User user(){
        return new User(); //返回要注入到容器中的对象
    }
}

在这里插入图片描述
【开发】后端框架——Spring_第10张图片

代理模式

对目标类的调用由直接调用变为间接调用

为什么引入代理模式

在代理类调用目标类之前和之后做一些预处理和后处理操作,用来扩展一些不属于目标类的功能

【开发】后端框架——Spring_第11张图片

比如:可以在方法开始和结束前记录日志;在方法执行前进行额外的参数校验;进行事务管理,权限校验

  • 抽象角色:接口或抽象类表示
  • 真实角色:被代理角色
  • 代理角色:代理真实角色
  • 客户:访问代理对象

静态代理(静态生成代理类)

在程序运行之前,就为给真实角色编写并编译了代理角色的代码,生成代理角色的字节码文件,在程序运行时,直接运行这些字节码文件

【开发】后端框架——Spring_第12张图片

如在租房的服务中:

  1. 抽象角色

    /* 抽象角色 */
    public interface Rent {
        public void rent();
    }
    
  2. 真实角色

    /* 真实角色 */
    public class Host implements Rent{
        public void rent(){
            System.out.println("房东出租房子");
        }
    }
    
  3. 代理角色

    /* 代理角色 */
    public class Agent implements Rent{
        Host host;
    
        public Agent(Host host) {
            this.host = host;
        }
    
        public void rent(){
            visit();
            host.rent();
            assign();
            fare();
        }
    
        public void visit(){
            System.out.println("预约看房");
        }
    
        public void assign(){
            System.out.println("签合同");
        }
    
        public void fare(){
            System.out.println("收中介费");
        }
    }
    
  4. 客户访问代理角色

    /* 客户 */
    public class Client {
        public static void main(String[] args) {
            Host host = new Host();
            Agent agent = new Agent(host);
    
            agent.rent();
        }
    }
    

在实现日志功能时

  1. 抽象角色

    /*用户服务:抽象角色*/
    public interface UserDao {
        public void add();
        public void delete();
        public void update();
        public void query();
    }
    
  2. 真实角色

    /*实现用户服务功能:真实角色*/
    public class UserDaoImpl implements UserDao {
    
        @Override
        public void add() {
            System.out.println("实现新增用户");
        }
    
        @Override
        public void delete() {
            System.out.println("实现删除用户");
        }
    
        @Override
        public void update() {
            System.out.println("实现更新用户");
        }
    
        @Override
        public void query() {
            System.out.println("实现查询用户");
        }
    }
    
  3. 代理角色

    public class Proxy implements UserDao{
        UserDaoImpl user;
    
        public void setUser(UserDaoImpl user) {
            this.user = user;
        }
    
        @Override
        public void add() {
            log("add");
            user.add();
        }
    
        @Override
        public void delete() {
            log("delete");
            user.delete();
        }
    
        @Override
        public void update() {
            log("update");
            user.update();
        }
    
        @Override
        public void query() {
            log("query");
            user.query();
        }
    
        public void log(String msg){
            System.out.println("[DEBUG]实现"+msg+"日志功能!");
        }
    }
    
  4. 用户

    /*使用用户功能:客户*/
    public class Client {
        public static void main(String[] args) {
            UserDaoImpl user = new UserDaoImpl();
            Proxy proxy = new Proxy();
    
            proxy.setUser(user);
            proxy.add();
            proxy.query();
            proxy.update();
            proxy.delete();
        }
    }
    

【开发】后端框架——Spring_第13张图片

静态代理特点

优点:

  • 真实角色操作更为简单,不用关注公共业务
  • 公共业务交给代理角色,实现业务分工
  • 公共业务发生扩展,方便集中管理

缺点:

  • 每个真实角色都会产生一个代理角色,代码量增多 --> 动态代理

动态代理(反射生成代理类)

在运行态,通过反射自动生成代理角色

  • 动态代理的代理类利用反射动态生成
  • 代理的是接口

分类

  • 基于接口的动态代理——JDK动态代理
  • 基于类的动态代理——cglib
  • java字节码——Javasist

代理类与调用处理接口

java.lang.reflect.Proxy ,实现 InvocationHandler 接口,实现它的 invoke() 方法

  • 获取代理类

    public static Object newProxyInstance
       					(ClassLoader loader,
                         @NotNull Class<?>[] interfaces,
                         @NotNull reflect.InvocationHandler h)
    
    //params
    loader 		– the class loader to define the proxy class
    interfaces 	– the list of interfaces for the proxy class to implement
    h 			– the invocation handler to dispatch method invocations to
    //return
    a proxy instance with the specified invocation handler of a proxy class that is defined by the specified class loader and that implements the specified interfaces
    
  • Interface InvocationHandler :When a method(抽象角色–>接口) is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

    每个被代理实例都有一个关联的调用处理程序


静态代理对应的动态代理

  1. 抽象角色

    public interface Rent {
        public void rent();
    }
    
  2. 真实角色

    public class Host implements Rent{
        public void rent(){
            System.out.println("房东要出租房屋!");
        }
    }
    
  3. 动态代理角色

    调用处理程序动态生成代理角色、绑定真实角色

    //代理处理程序:自动生成代理类
    public class ProxyInvocationHandler implements InvocationHandler {
        //需要被代理的抽象角色-->接口
        Rent rent;
    
        //通过DI注入真实角色
        public void setRent(Rent rent) {
            this.rent = rent;
        }
    
        //生成代理角色
        public Object getProxy(){
            
            return Proxy.newProxyInstance(
                //抽象角色的类加载器
                rent.getClass().getClassLoader(),
                //抽象角色的接口
                rent.getClass().getInterfaces(),  
                //该抽象角色的代理处理程序
                this);                                          
        }
    
        //处理代理类实例;返回结果
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            visit();
            Object result = method.invoke(rent,args);
            assign();
            fare();
            
            return result;
        }
        
        /*公共业务*/
        public void visit(){
            System.out.println("看房");
        }
        public void assign(){
            System.out.println("签合同");
        }
        public void fare(){
            System.out.println("中介费");
        }
    }
    
  4. 客户

    public class Client {
        public static void main(String[] args) {
            //真实角色
            Host host = new Host();
    
            //通过InvocationHandler处理要被调用的抽象角色
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
            //调用处理程序与真实角色的绑定
            pih.setRent(host);
    
            //生成代理角色
            Rent proxy = (Rent)pih.getProxy();
            proxy.rent();
        }
    }
    

    【开发】后端框架——Spring_第14张图片


【开发】后端框架——Spring_第15张图片

  1. 抽象角色–>接口

    public interface UserDao {
        public void add();
        public void delete();
        public void update();
        public void query();
    }
    
  2. 真实角色–>被代理类

    public class UserDaoImpl implements UserDao{
        @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 query() {
            System.out.println("query");
        }
    }
    
  3. 动态代理角色

    调用处理程序–>动态生成代理类的程序

    public class ProxyInvocationHandler implements InvocationHandler {
        //被调用的接口——>抽象角色
        private Object target;
    
        //注入真实角色
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //返回代理类
        public Object getProxy(){
            return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log(target.getClass().getName());
            Object result = method.invoke(this.target,args);
    
            return result;
        }
    
        /*公共业务*/
        public void log(String msg){
            System.out.println("[DEBUG]调用了"+msg+"方法");
        }
    }
    
  4. 客户

    public class Client {
        public static void main(String[] args) {
            //定义调用处理程序
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
            //调用处理程序与将代理角色与真实角色绑定
            pih.setTarget(new UserDaoImpl());
    
            //获取代理角色,,代理的是接口
            UserDao proxy = (UserDao) pih.getProxy();
            proxy.add();
            proxy.query();
            proxy.update();
            proxy.delete();
        }
    }
    

【开发】后端框架——Spring_第16张图片

好处

  • 一个动态代理类代理的是一个接口,对应的是一类业务
  • 一个动态代理类可以代理实现同一接口多个类

Spring AOP

AOP简介

(Aspect Oriented Programming):面向切面编程

通过 预编译方式和动态代理 实现程序功能的统一维护的技术,就是 Spring对动态代理的实现

【开发】后端框架——Spring_第17张图片

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

  • 横切关注点 公共业务 :跨越应用程序多个模块的方法或功能(公共业务),与业务逻辑无关,但需要关注的部分。如:日志,安全,缓存,事务
  • 切面(aspect) 代理角色 :横切关注点被模块化的对象–>类
  • 通知(advisor) 真实角色中的invoke()实现的公共业务 :切面必须完成的工作–>类中的方法
  • 目标(impl) 真实角色 :被通知对象–>真实角色
  • 切入点(pointcut) 通知的插入位置 :切面指定地点
  • 连接点(JointPoint):切入点对应的执行点

【开发】后端框架——Spring_第18张图片

AOP与动态代理的对应

将Service层抽象为的接口作为 抽象角色 ,Service的实现类作为 真实角色 。Spring为我们完成了动态代理中 代理角色(即 Aspect 可以理解为一个代理)的创建,我们需要做的是

  1. 实现代理角色中的公共业务,即 invoke() 方法中公共函数,对应于 通知 Advice
  2. 用xml的方式将真实角色与代理角色绑定
  3. 通过实现切点接口或者在xml配置通知对应的切点表达式确定通知的位置

若使用自定义切面,则只需要在切面中实现通知,切入点由注解定义,然后将切面注册到IoC容器中

日志切面

【开发】后端框架——Spring_第19张图片

方法一:实现切入点通知

实现这些切点通知,在配置文件中配置这些切点通知作用在哪些Bean上

通知类型 连接点 需实现的接口
前置通知 方法前 org.springframework.aop.MethodBeforeAdvice;
后置通知 方法后 org.springframework.aop.AfterReturningAdvice
环绕通知 方法前后 org.aopalliance.intercept.MethodInterceptor
异常抛出通知 方法抛出异常时 org.springframework.aop.ThrowsAdvice
引介通知 类中新增方法或属性 org.springframework.aop.IntroductionInterceptor
定义接口–>抽象角色
public interface UserService {
    public void add();
    public void delete();
    public int select();
    public void update();
}
接口实现–>真实角色
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("addService");
    }

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

    @Override
    public int select() {
        System.out.println("selectService");

        return 1;
    }

    @Override
    public void update() {
        System.out.println("updateService");
    }
}
实现通知接口–>代理中的公共业务
/*代理要做的公共业务 通知 Log.java
* implements 通过Advice定义切入点pointcut
* */
public class Log implements MethodBeforeAdvice {
    /*
    * method:要执行的目标对象的方法
    * objects:参数
    * o:目标对象 target-->接口
    * */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("执行了"+o.getClass().getName()+"的"+method.getName());
    }
}
/*代理对象的一个公共业务-->通知 AfterLog.java
 * implements 通过Advice定义切入点pointcut
* */
public class AfterLog implements AfterReturningAdvice {

    @Override
    /*
    * o:返回值
    * method:要执行的目标对象的方法
    * objects:args
    * o1:目标对象 target-->接口
    * */
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行了"+o1.getClass().getName()+"的"+method.getName()+"-返回值为:"+o);
    }
}
IoC配置——applicationContext.xml

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    
    <bean id="userServiceImpl" class="com.kuang.service.UserServiceImpl"/>
    <bean id="log" class="com.kuang.log.Log"/>
    <bean id="afterlog" class="com.kuang.log.AfterLog"/>

    
    <aop:config>
        
        <aop:pointcut id="pc_userService" expression="execution(* com.kuang.service.UserServiceImpl.*(..))" />

        
        <aop:advisor advice-ref="log" pointcut="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
        <aop:advisor advice-ref="afterlog" pointcut-ref="pc_userService"/>
    aop:config>
beans>
aop:expression

格式

execution( modifiers-pattern? 
ret-type-pattern declaring-type-pattern?
name-pattern(param-pattern) throws-pattern?)
  • ret-type-pattern、name-pattern、param-pattern是必须的
  • ret-type-pattern:该方法的返回类型必须是什么才能使连接点匹配
  • param-pattern:
    • ()匹配不带参数的方法
    • (…)匹配任意数量(零个或多个)的参数
    • (*)模式与采用任何类型的一个参数的方法匹配
    • (*,String)与采用两个参数的方法匹配。第一个可以是任何类型,而第二个必须是字符串
测试

【开发】后端框架——Spring_第20张图片

方法二:自定义通知接口

自定义切面,在配置文件中将切面中的通知与切点表达式绑定

自定义切面
public class DiyAspect {
    public void before(){
        System.out.println("=========实现前置通知==========");
    }

    public void after(){
        System.out.println("==========实现后置通知==========");
    }
}
定义切入点&切面绑定

<bean id="diy" class="com.kuang.advisor.DiyAspect" />
<aop:config>
    <aop:pointcut id="pc_userService" expression="execution(* com.kuang.service.UserServiceImpl.*(..))" />

    <aop:aspect ref="diy">
        <aop:before method="before" pointcut-ref="pc_userService"/>
        <aop:after method="after" pointcut-ref="pc_userService"/>
    aop:aspect>
aop:config>
Test

【开发】后端框架——Spring_第21张图片

方法三:注解实现AOP

利用内置注解,实现切面内的通知

由注解决定通知相对于主逻辑切点的位置

  • @Before:前置通知
  • @After:后置通知
  • @AfterReturn:返回后通知
  • @AfterThrowing:出现异常后通知
  • @Around:环绕通知
自定义切面
@Aspect
@Component
public class AnnotationAspect {
    @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("======方法执行前====");
    }
    
    @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("======方法执行后====");
    }
	
    @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
    //
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        jp.proceed();
        System.out.println("环绕后");
    }
}
开启注解支持
切面注册到IoC容器
<bean id="diyaspect" class="com.kuang.Aspect.AnnotationAspect" />

<aop:aspectj-autoproxy/>
Test

【开发】后端框架——Spring_第22张图片

Bean的生命周期

Spring Bean从创建到被销毁的过程,叫Bean的生命周期

  1. 定义Bean,创建BeanDefinition实例,BeanDefinition类中定义了很多属性用于描述Bean,如beanClass(Bean的类型),scope(一个Bean的作用范围),primary,islazy,dependsOn(创建之前所依赖的其他Bean),initMethodName(初始化方法)等

  2. 构造方法推断,最终选出一个构造方法

  3. 实例化,利用构造方法反射得到一个实例。在Spring中,可以通过 PostProcessor 对Bean的实例化过程进行干预

  4. 属性填充,被 @AutoWired 或者 @Resource 注解的属性,需要进行属性填充

  5. 初始化前,由 @PostConstruct 注解的方法进行初始化前操作

  6. 初始化:Spring提供了初始化机制,可以通过实现 InitializingBean接口中的afterpropertiesSet()方法 或 由@Bean中initMethod属性指出初始化方法,完成Bean的自定义赋值或校验

  7. 初始化后处理

    如果当前Bean实现了 ApplicationListener 接口,把它添加到事件监听器列表中

    若开启了AOP,则判断当前实例的中的方法是否是某一切面的切点,若是,则生成代理对象作为Bean,注册到Spring容器中

  8. 在初始化后,生成真正的Bean,将Bean注册到Spring容器以及缓存中

  9. 当Spring上下文销毁时,会将其中所有的Bean一并销毁

其中重要的部分是Bean的创建过程

Bean的创建大体步骤

1)利用Class的构造方法,反射得到对应的对象

实例化
?
Class
对象
Bean
//定义一个Bean
@Component
public class Userservice{
    public void test(){
        
    }
}

public class Test{
    public static void main(String[] args){
        //创建一个Spring容器
        AnnotationConfigApplication applicationContext = new AnnotationContext(AppConfig.class);
        //从容器中获取userService Bean
        applicationContext.getBean("userService".Userservice.class);
    }
}

2)给Bean中的属性赋值

实例化
?
Class
对象
属性填充
Bean
//新定义一个
@Component
public class OrderService{
    
}

//定义一个Bean
@Component
public class Userservice{
    //自动注入另一个Bean
    @Autowired
    private OrderService orderService;
    
    public void test(){
        
    }
}

public class Test{
    public static void main(String[] args){
        //创建一个Spring容器
        AnnotationConfigApplication applicationContext = new AnnotationContext(AppConfig.class);
        //从容器中获取userService Bean
        applicationContext.getBean("userService".Userservice.class);
    }
}

3)自定义部分属性的初始化

实例化
?
?
Bean
Class
对象
属性填充
重写InitializingBean接口的afterProperties(初始化)
@PostConstruct注解的方法(初始化)
public class User{}

@Component
public class OrderService{
    
}

//定义一个Bean
@Component
public class Userservice implement InitializingBean{
    //自动注入另一个Bean
    @Autowired
    private OrderService orderService;
    
    private User defaultuser;
    
    @Override
    public void afterPropertiesSet(){
        //其他的自定义初始化动作
    }
    
    @PostConstruct
    public void xxx(){
        //其他的自定义初始化动作
    }
    
    public void test(){
        
    }
}

public class Test{
    public static void main(String[] args){
        //创建一个Spring容器
        AnnotationConfigApplication applicationContext = new AnnotationContext(AppConfig.class);
        //从容器中获取userService Bean
        applicationContext.getBean("userService".Userservice.class);
    }
}

4)若开启AOP且是切面的一个切点,则生成一个代理对象

实例化
?
?
?
AOP
Class
对象
属性填充
重写InitializingBean接口的afterProperties(初始化)
@PostConstruct注解的方法(初始化)
代理对象
Bean
@Aspect
@Component
public class MyAspect{
    public AspectBefore(){
        @Before("execution(public void packageName.service.Uservice.test())")
        System.out.println("Before");
    }
}

判断是否进行AOP:

在定义一个切面时,会用 @Aspect@Component 注解切面,此时将切面的切点与当前实例进行比较。

若切点匹配成功,则进行动态代理,产生代理对象

UserserviceProxy extends Userservice{
    private Userservice targer;
    
    @Override
    public test(){
        //执行切面的代理逻辑
        System.out.println("Before");
        target.test();
    }
}

代理对象不做属性填充

Bean创建的完整步骤

  1. 推断构造方法——AutowiredAnnotationBeanPostProcessor
  2. 通过反射机制和构造器,生成实例
  3. 处理beanDefinition,找出可被填充的属性
  4. 属性填充
  5. 若有 @PostConstruct 注解的方法,则进行相应的初始化前操作
  6. 初始化时:先调用实现了 InitializingBean 接口的 afterPropertiesSet() 方法 ,再调用 @Bean 中的 initMethod 属性配置的初始化方法
  7. 初始化后会进行一些后处理,将实现了 ApplicationListener 接口的Bean添加到事件监听器列表中
  8. 若开启了AOP,则判断当前实例是否是某一切面的切点,若是,则生成代理对象作为Bean,注册到Spring容器中

构造器推断

若有被 @Autowired 注解的构造器,则调用该构造器

若有参和无参构造方法同时存在,优先选无参构造器

若只有一个构造器,则使用唯一的构造器

若没有声明构造器,则用无参构造器

若没有无参构造器且有多个有参构造器,则抛出异常

单例Bean和单例模式

Spring容器中同一个类可以有多个不同名称的Bean

单例模式:一个类只有一个实例

单例Bean:一个Spring容器只有一个同类型的同名Bean

  • applicationContext.getBean("orderService") 执行多次,得到的是同一个Bean
@Component//配置类
public class AppConfig{
    @Bean
    public OrderService orderservice1(){
        return new OrderService();
    }
    
    @Bean
    public OrderService orderservice1(){
        return new OrderService();
    }
    
    @Bean
    public OrderService orderservice2(){
        return new OrderService();
    }
}

执行后,

【开发】后端框架——Spring_第23张图片

属性填充

@Autowired 注解的属性,从Spring容器中找合适的Bean给属性赋值

  1. 先根据类型找(ByType)找到同类型的Bean
  2. 第一轮筛选,会剔除 @Bean(autowiredCandidate=false) 的Bean
  3. 若被 @Qualifier("") 注解中指明Bean所在的目标分组,第二轮筛选会剔除不在该分组的Bean
  4. 若有 @Primary 注解的Bean,则选择主Bean注入
  5. @Priority() 设置了Bean的不同优先级,选择优先级最高的Bean注入
  6. 若ByType找到多个同类型的Bean,但ByName找不到Bean,则会报错

整合MyBatis

方式一

导包

在这里插入图片描述

<dependencies>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.13version>
        <scope>testscope>
    dependency>
    
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatisartifactId>
        <version>3.5.6version>
    dependency>
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.16version>
    dependency>
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-webmvcartifactId>
        <version>5.3.3version>
    dependency>
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-jdbcartifactId>
        <version>5.3.3version>
    dependency>
    
    <dependency>
        <groupId>org.aspectjgroupId>
        <artifactId>aspectjweaverartifactId>
        <version>1.9.4version>
    dependency>
    
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatis-springartifactId>
        <version>2.0.6version>
    dependency>

    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.16version>
    dependency>
dependencies>

<build>
    <resources>
        <resource>
            
            <directory>src/main/javadirectory>
            
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>truefiltering>
        resource>

        <resource>
            
            <directory>src/main/resourcesdirectory>
            
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>truefiltering>
        resource>
    resources>
build>

pojo

配置mybatis-config.xml


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name=""/>
    typeAliases>
    
    <settings>
        <setting name="" value=""/>
    settings>
configuration>

mapper.xml(数据访问层)


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.mapper.UserMapper">
    <select id="getUsers" resultType="user">
        select *from mybatis.user;
    select>
mapper>

Spring整合Mybatis——spring-mybatis.xml


<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="2017002231"/>
    bean>

    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml"/>
    bean>

    
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    bean>
beans>

+从properties文件导入

#db.properties
mysql_driver=com.mysql.cj.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
root=root
root_pwd=2017002231
<beans xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
                    https://www.springframework.org/schema/context/spring-context.xsd">

<context:property-placeholder location="classpath:db.properties"/>

mapperImpl

public class UserMapperImpl implements UserMapper{
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> getUsers() {
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        return userMapper.getUsers();
    }
}

spring-mapper.xml——mapper层bean注入Spring


<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <import resource="spring-mybatis.xml"/>

    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    bean>
beans>

applicationContext.xml——Bean整合


<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <import resource="spring-mapper.xml"/>

beans>

测试

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

List<User> users = userMapper.getUsers();
for (User user : users) {
    System.out.println(user);
}

方式二

导包

在这里插入图片描述

<dependencies>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.13version>
        <scope>testscope>
    dependency>
    
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatisartifactId>
        <version>3.5.6version>
    dependency>
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.16version>
    dependency>
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-webmvcartifactId>
        <version>5.3.3version>
    dependency>
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-jdbcartifactId>
        <version>5.3.3version>
    dependency>
    
    <dependency>
        <groupId>org.aspectjgroupId>
        <artifactId>aspectjweaverartifactId>
        <version>1.9.4version>
    dependency>
    
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatis-springartifactId>
        <version>2.0.6version>
    dependency>

    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.16version>
    dependency>
dependencies>

<build>
    <resources>
        <resource>
            
            <directory>src/main/javadirectory>
            
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>truefiltering>
        resource>

        <resource>
            
            <directory>src/main/resourcesdirectory>
            
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>truefiltering>
        resource>
    resources>
build>

配置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>
    <typeAliases>
        <package name=""/>
    </typeAliases>
    
    <settings>
        <setting name="" value=""/>
    </settings>
</configuration>

pojo

db.properties–>spring-mybatis.xml

mysqlDriver=com.mysql.cj.jdbc.Driver
mysqlUrl=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
root=root
rootPassword=2017002231

<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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:property-placeholder location="db.properties"/>
    
    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${mysqlDriver}"/>
        <property name="url" value="${mysqlUrl}"/>
        <property name="username" value="${root}"/>
        <property name="password" value="${rootPassword}"/>
    bean>
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml"/>
    bean>
beans>

mapper.java

mapper.xml


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
    <select id="getUsers" resultType="user">
        select * from mybatis.user;
    select>
mapper>

mapperImpl.java

继承 Spring的SqlSession类

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> getUsers(){
        return getSqlSession().getMapper(UserMapper.class).getUsers();
    }
}

mapperImpl注册到Spring——springMapper.xml


<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <import resource="spring-mybatis.xml" />
    
    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    bean>
beans>

整合Beans——applicationContext.xml


<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="spring-mapper.xml"/>
    
beans>

测试

【开发】后端框架——Spring_第24张图片

声明式事务

事务

  • 原子性
  • 数据一致性、完整性问题

ACID:

原子性(Atom)

一致性(Consistent)

隔离性(Isolation):多个事务并发执行,防止数据损坏

持久性(durablity):事务一旦提交,无论系统发生什么问题,结果都不会被影响


<mapper namespace="com.kuang.mapper.UserMapper">
    <select id="getUsers" resultType="user">
        select * from mybatis.user;
    select>

    <insert id="addUser" parameterType="user">
        insert into user (id,user_name,pwd) values (#{id},#{user_name},#{pwd});
    insert>
	
    <delete id="deleteUser">
        deletes from user where id = #{id};
    delete>
mapper>
public void op(){
    User user = new User(10,"transaction","123123");

    addUser(user);
    deleteUser(10);
}
  • 虽然op执行失败,但addUser成功

声明式事务——AOP

<bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
bean>


<tx:advice id="txAdvice" transaction-manager="transaction">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    tx:attributes>
tx:advice>

<aop:config>
    <aop:pointcut id="pc_tx" expression="execution(* com.kuang.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc_tx"/>
aop:config>

你可能感兴趣的:(开发,#,后端框架,spring,java,mybatis)