Spring快速入门(2022优化版)

Spring快速入门

一、Spring回顾

Spring简介

1、Spring是轻量级的开源的JavaEE框架

JavaEE就是用java开发企业级的应用,它是一个规范和标准

Spring框架就是实现了JavaEE这个规范,所以Spring常用来开发企业级的应用

2、Spring可以解决企业应用开发的复杂性

3、Spring有两个核心部分:IOC和Aop

Spring架构

image-20220610113048217

IOC

IOC(Inversion of Control):控制反转,把创建对象过程交给Spring进行管理

在一个软件系统中,对象A依赖于对象B,当对象A需要对象B的时候需要自己创建对象B,这个创建对象的控制权是掌握在对象A手中的。在引入IOC容器之后,当对象A在需要对象B的时候,由IOC容器主动创建并注入A中,从而实现了解耦。这个过程创建对象的控制权全部交给了IOC容器,这就是控制反转

DI

依赖注入

扫描二维码关注公众号,回复: 14421618 查看本文章

依赖注入是IOC的实现方式,前面我们说ioc是控制反转,它这个反转其实就是获得依赖对象的过程被反转了。依赖注入就是IOC在运行期间,动态地将依赖关系注入到对象之中

依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情

二、未使用Spring程序开发步骤

com.itheima.dao.UserDaoImpl
    create 方法;
    delete 方法;
    update 方法;
    select 方法;
    
com.itheima.service.UserServiceImpl
    //通过 new 一个 UserDaoImpl 对象来使用
	UserDao	userDao = new UserDaoImpl();

在业务层调用持久层的方法时,需要先new持久层的对象,然后调用持久层方法进而操作数据库。1`1

未使用Spring之前都是需要什么对象就new什么对象,这样也能做,不足点是耦合性太强。

怎么解耦呢?

应天命而产生。 现指适应时机而产生

Spring框架就应运而生就出来了,它可以让对象都在一个容器里,需要的时候直接通过spring的api从容器中获取

Spring框架是怎么让对象都在容器里的呢?

学习过Spring框架后,被放在容器里的对象就成为bean

前面的学习是通过配置文件的形式以<bean>标签来把对象放到容器里

是通过xml配置文件的形式用特殊的标签,指定对象的全类名和id(id是用来唯一标识的),Spring框架通过id获取Bean的全类名,之后通过java中的反射机制创建对象

image-20220608111508745

三、使用Spring程序开发步骤

  1. 通过maven导Spring相关的包
  2. 编写dao接口和实现类
  3. 创建Spring核心配置文件
  4. 在spring配置文件中配置UserDaoImpl
  5. 使用Spring的API获得Bean实例

通过maven导Spring相关的包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.heima</groupId>
    <artifactId>spring_ioc</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
    </dependencies>

</project>

编写dao接口和实现类

public interface UserDao {
    
    
    void save();
}

实现类

package com.heima.impl;

import com.heima.UserDao;

public class UserDaoImpl implements UserDao {
    
    
    @Override
    public void save() {
    
    
        System.out.println("save running.......");
    }
}

创建Spring核心配置文件

image-20220608182633608

在spring配置文件中配置UserDaoImpl

<?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="userDao" class="com.heima.impl.UserDaoImpl">

    </bean>

</beans>

使用Spring的API获得Bean实例

这里new的ClassPathXmlApplicationContext其实就相当于spring的客户端对吧,

之后通过spring客户端api获取配置文件中的对象,从而调用方法

package com.heima.demo;

import com.heima.UserDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserDaoDemo {
    
    
    public static void main(String[] args) {
    
    
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

四、Spring的配置文件

Bean 标签基本配置

使用Spring来创建对象,每一个对象在Spring中都被称为bean

  • 用于配置对象交由 Spring 来创建
  • 默认情况下它( Spring ) 调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功
基本属性:
	id:Bean 实例在 Spring 容器中的唯一标识
	class:Bean 的 全限定名称 / 全包名

Bean 标签范围配置

  • scope:指对象的作用范围,取值如下:
取值范围				说明

singleton		默认值,单例的

prototype		多例的,原型模式

request			Web 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中

session			Web 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中

global session	Web 项目中,应用在 Portlet 环境,如果没有 Portlet 环境,那么 globalSession 相当于 				Session

image-20220608183428620

为什么bean默认为单例

为了提高效率!

比如你每次调用dao对象执行里面的方法,dao对象我们需要每次都重新创建吗?不需要,因为都是同一个对象只是每次调用对象里不同的方法罢了,所以Sping里的bean默认都是单例的。

那么哪些bean适合交给ioc容器管理呢?

  • 表现层对象
  • 业务层对象
  • 数据层对象
  • 工具类对象

不适合交给容器进行管理的bean

  • 封装实体的域对象

Bean 生命周期配置

  • init-method:指定类中的初始化方法名称
  • destory-method:指定类中销毁方法名称

在要注入的bean中,可以写bean的初始化方法和销毁方法

在spring的配置文件中进行指定

image-20220609103150537

image-20220609103249802

Bean 实例化的三种方式

无参构造方法实例化

public class UserDaoImpl implements UserDao {
    
    
    public UserDaoImpl(){
    
    
        System.out.println("Dao ------ 无参构造方法......");
    }
}


<?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="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
</beans>
     
        
public class UserDaoDemo {
    
    
    public static void main(String[] args) {
    
    
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
        UserDao userDao = (UserDao) app.getBean("getUserDao");
        userDao.save();
    }
}


通过无参构造函数实例化对象

工厂静态方法实例化

image-20220609104227794

public class UserDaoImpl implements UserDao {
    
    
    public UserDaoImpl(){
    
    
        System.out.println("Dao ------ 无参构造方法......");
    }
}


<!--    getUserDao在StaticFactory中是静态方法,所以可以直接获取-->
<?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="getUserDao" class="com.heima.factory.StaticFactory" factory-method="getUserDao">

    </bean>
     
        
public class UserDaoDemo {
    
    
    public static void main(String[] args) {
    
    
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
        UserDao userDao = (UserDao) app.getBean("getUserDao");
        userDao.save();
    }
}


通过无参构造函数实例化对象
save running.......

工厂实例方法实例化

public class StaticFactory {
    
    
    public UserDao getUserDao(){
    
    
        return new UserDaoImpl();
    }
}

public class UserDaoImpl implements UserDao {
    
    
    public UserDaoImpl(){
    
    
        System.out.println("Dao ------ 无参构造方法......");
    }
}


<!--    getUserDao在StaticFactory中是静态方法,所以可以直接获取-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
           
<!--动态Bean工厂需先配置 动态工厂,再根据动态工厂名指定工厂需执行的方法-->
<bean id="getUserDao" class="com.heima.factory.StaticFactory"/>
<bean id="getUserDaoBean" factory-bean="getUserDao" factory-method="getUserDao"/>
     
        
    
public class UserDaoDemo {
    
    
    public static void main(String[] args) {
    
    
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
        //这里获取的bean为getUserDaoBean
        UserDao userDao = (UserDao) app.getBean("getUserDaoBean");
        userDao.save();
    }
}


通过无参构造函数实例化对象
save running.......


Spring 的重点配置

<beans>
    <bean>	标签
        id 属性:在容器中 Bean 实例的唯一标识,不允许重复
        class 属性:要实例化的 Bean 的全限定名
        scope 属性:Bean 的作用范围,常用是 singleton(单例模式)和 prototype(原型模式/多例模式)
        <property>	标签:属性注入
            name 属性:属性名称
            value 属性:注入的普通属性值
            ref 属性:注入的对象引用值
            <list>标签
                <value>value</value>
            </list>
            <map>标签(key 和 value)
                <entry key="key" value-ref="value-ref"></entry>
            </map>
            <properties>标签(key 和 value 均为 字符串)
                <props><prop key="key">value</prop></props>
            </properties>
        </property>
        <constructor-arg>有参结构体 set 注入 标签</constructor-arg>
        <constructor-arg name="userDao" ref="set 方法 - set 转 小驼峰"></constructor-arg>
    </bean>
    <import>标签:导入其他的 Spring 的分文件</import>
    <import resource="applicationContext-user.xml"></import>
</beans>

image-20220610182651010

image-20220610182709538

Bean 的依赖注入分析

通过set方法注入

1、在要注入的对象中设置属性和对应的set方法

image-20220609110608148

2、在spring配置文件中赋值

image-20220609110847400

3、测试

image-20220609110347733

有参数构造进行注入

1、在要注入的对象中设置属性和有参构造

image-20220609111318844

2、在spring配置文件中赋值

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

    <bean id="userDao" class="com.heima.impl.UserDaoImpl">
        <constructor-arg name="name" value="ls"/>
        <constructor-arg name="sex" value="woman"/>
    </bean>

</beans>

3、测试

image-20220609111127534

注入字面量

null值

和set方法设置属性值一样,设置null值,只需要在property里加两个null标签即可

<bean id="userDao" class="com.heima.impl.UserDaoImpl">
    <property name="name" value="zs"/>
    <property name="sex">
        <null></null>
    </property>
</bean>

属性值包含特殊符号

//方法一:转义字符
<property name="address" value="&lt;北京&dt;"></property>
//方法二:CDATA
<property name="address">
    <value>    <![CDATA[<北京>]]>    </value>
</property>

注入外部bean

不使用Spring情况

image-20220609120545803

使用Spring情况

image-20220609120938131

<bean id="UserDaoImpl" class="com.heima.impl.UserDaoImpl">
    <property name="userDaoImpl2" ref="userDaoImpl2"/>
</bean>

<bean id="userDaoImpl2" class="com.heima.impl.UserDaoImpl2"/>

image-20220609121131304

自动装配

<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
  • 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  • 使用按类型装配时( byType )必须保障容器中相同类型的bean唯一,推荐使用
  • 使用按名称装配时( byName )必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用自动装配
  • 优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

第三方对象管理

前面我们学的都是自己的类啊对象用ioc管理

如果是第三方的类啊对象啊我们怎么管理呢?

下面用druid连接池对象做测试:

1、引入依赖,在spring配置文件创建bean

image-20220610164818000

2、加载配置文件,获取bean

image-20220610165006369

加载properties文件

1、开启context命名空间

image-20220610170147046

2.使用context空问加载properties文件

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

3、使用占位符${}读取properties文件

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

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

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

</beans>

4、测试

怎么测试它是否读取成功呢?

我们可以写个dao接口和实现类,在实现类中写个属性并给它设置set方法

之后在配置文件中给这个属性通过set注入值为读取的外部jdbc.properties文件的值,看是否打印成功即可

image-20220610171245694

image-20220610171302491

测试成功

image-20220610171318703

五、Spring 注解开发

Spring 是轻代码而重配置的框架。配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替 xml 配置文件,可以简化配置,提高开发效率。

Spring 原始注解主要是替代 的配置

使用目的:简化xml配置

常用注解

  • @Component 普通用法
  • @Service 用于service业务逻辑层
  • @Controller 用于web层
  • @Repository 用于DAO持久层

以上注解功能和@Component 一样,它们的作用是见名知意

开发流程

(1)创建配置类,替代 xml 配置文件

package com.heima.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration  //作为配置类,代替xml配置文件
@ComponentScan("com.heima")
public class SpringConfig {
    
    
}

使用配置文件是如下形式:

image-20220610183003337

(2)持久层编码

image-20220609183002464

(3)业务层编码

因业务层会调用持久层中的方法

所以注入持久层对象

image-20220609183042336

(4)测试

package com.heima.demo;


import com.heima.config.SpringConfig;
import com.heima.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class UserDaoDemo {
    
    

    public static void main(String[] args) {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

        UserService userServiceImpl = (UserService) context.getBean("UserServiceImpl");
        
        userServiceImpl.saveData();
        
    }
}

bean作用范围与生命周期管理

1、配置类

配置类的作用就是代替配置文件

@Configuration
@ComponentScan("com.caq.study")
public class SpringConfig {
    
    
}

2、通过注解配置bean

指定bean的作用范围@Scope

生命周期注解@PostConstruct、@PreDestroy

package com.caq.study.dao.impl;

import com.caq.study.dao.UserDao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component("UserDao")
@Scope("singleton")
public class UserDaoImpl implements UserDao {
    
    

    private String name;

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

    //在构造方法之后执行
    @PostConstruct
    public void init(){
    
    
        System.out.println("init");
    }

    //在容器销毁之前执行
    @PreDestroy
    public void destory(){
    
    
        System.out.println("destory");
    }

    @Override
    public void selectAllData() {
    
    
        System.out.println("查询的数据为" + name);
    }
}

image-20220610184638212

3、测试

public class demo {
    
    

    public static void main(String[] args) {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserDao userDao = (UserDao)context.getBean("UserDao");
        userDao.selectAllData();
    }
}

依赖注入

注入引用类型

注意︰自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法

注意︰自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法

引用数据类型包括:类、接口类型、数组类型、枚举类型、注解类型,字符串型。

1、配置类

@Configuration
@ComponentScan("com.caq.study")
public class SpringConfig {
    
    
}

2、Service

这里通过@Autowired在UserServiceImpl对象中注入UserDaoImpl对象,从而调用持久层的方法

package com.caq.study.service.impl;

import com.caq.study.dao.impl.UserDaoImpl;
import com.caq.study.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService {
    
    

    @Autowired
    UserDaoImpl userDao;

    @Override
    public void select() {
    
    
        userDao.selectAllData();
    }
}


持久层对象如下:
@Component("userDao")
@Scope("singleton")
public class UserDaoImpl implements UserDao {
    
    

    private String name;

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

    //在构造方法之后执行
    @PostConstruct
    public void init(){
    
    
        System.out.println("init");
    }

    //在容器销毁之前执行
    @PreDestroy
    public void destory(){
    
    
        System.out.println("destory");
    }

    @Override
    public void selectAllData() {
    
    
        System.out.println("查询的数据为" + name);
    }
}

3、测试

package com.caq.study;

import com.caq.study.config.SpringConfig;
import com.caq.study.dao.UserDao;
import com.caq.study.dao.impl.UserDaoImpl;
import com.caq.study.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;

public class demo {
    
    

    public static void main(String[] args) {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//        DataSource dataSource = (DataSource)ctx.getBean("dataSource");
//        System.out.println(dataSource);
//        UserDao userDao = (UserDao)ctx.getBean("userDao");
        UserService userService = (UserService)context.getBean("userService");
        userService.select();
    }
}

注入简单类型

@Value

image-20220610192808605

加载外部配置文件

在配置类中加上@PropertySource注解引入外部配置文件

image-20220610193057344

加载多个配置文件

@PropertySource不支持通配符

image-20220610193259046

第三方bean管理

前面都是自己的类可以用注解的方式,那如果是第三方的jar包我们怎么用ioc容器来管理呢?

1、定义一个方法获得要管理的对象

在配置类中写

@Configuration
public class SpringConfig {
    
    

    @Bean
    public DataSource dataSource(){
    
    
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("${jdbc.driver}");
        ds.setUrl("${jdbc.url}");
        ds.setUsername("${jdbc.username}");
        ds.setPassword("${jdbc.password}");
        return ds;
    }
}

2、添加@Bean,表示当前方法的返回值是一个bean

image-20220610193954588

3、测试

public class demo {
    
    

    public static void main(String[] args) {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        DataSource dataSource = (DataSource)context.getBean(DataSource.class);
        System.out.println(dataSource);
    }
}

4、优化

在Spring中,一般配置相关的会提取出来,不能所有的配置都写Spring配置类中。如上实例,jdbc的配置我们可以在提取出来一个类来写,之后呢通过@Import注解的形式来导入到Spring的配置类中,这样是不是就更清晰了呢?

image-20220610194745289

image-20220610194754219

特殊的引用数据类型

引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象

public class JdbcConfig {
    
    
    @Bean
    public DataSource dataSource(UserDao userDao){
    
    
        System.out.println(userDao);
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("${jdbc.driver}");
        ds.setUrl("${jdbc.url}");
        ds.setUsername("${jdbc.username}");
        ds.setPassword("${jdbc.password}");
        return ds;
    }
}

打印内容如下:
init
com.caq.study.dao.impl.UserDaoImpl@69b794e2
{
    
    
	CreateTime:"2022-06-10 20:02:38",
	ActiveCount:0,
	PoolingCount:0,
	CreateCount:0,
	DestroyCount:0,
	CloseCount:0,
	ConnectCount:0,
	Connections:[
	]
}

XML配置比对注解配置

功能 XML配置 注解
定义bean <bean>id属性 class属性 @Component
@Controller,@Service,@Repository
Component
设置依赖注入 sett注入
构造器注入
自动装配
@Autowire
@Resource(@Autowire+@Qualifier)
@Value
配置第三方bean bean标签 @Bean
作用范围 scope属性 @Scope
生命周期 标准接口
init-method
destory-method
@PostConstruct
@PreDestory

Spring整合Mybatis

一、导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring_study</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.4</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
    </dependencies>


</project>

二、配置类

数据源配置

package com.caq.study.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;

public class JdbcConfig {
    
    
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
    
    
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

mybatis配置类

彻底解放XML配置mybatis主配置文件

mybatis主配置文件主要配置的是什么?

数据源和mapper的位置

这些配置通过Spring的方式怎么配置呢?

1、定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象

2、定义bean,返回MapperScannerConfigurer对象

在配置类中将这两个对象放到ioc容器中就可以让spring来进行管理了

package com.caq.study.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MybatisConfig {
    
    
    //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
    
    
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.caq.study");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    //定义bean,返回MapperScannerConfigurer对象
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
    
    
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.caq.study.dao");
        return msc;
    }
}

Spring配置类

package com.caq.study.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.*;


@Configuration
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.caq")
@Import({
    
    JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
    
    


}

三、实体类,接口,mapper映射文件

@Data
public class User implements Serializable {
    
    
    private Integer id;
    private String name;
    private String gender;
    private Integer age;
}
public interface UserMapper {
    
    
    @Insert("insert into `user` ( `name`, `gender`, `age` ) VALUES(#{user.name},#{user.gender},#{user.age})")
    boolean insertUserTest(@Param("user") User user);

}

映射文件我这里没有写,这里是通过注解的方式简单的进行插入

注意点:@Params这个注解一定要写!!!

四、测试

public class Demo2 {
    
    

    public static void main(String[] args) {
    
    
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

        UserService bean = ctx.getBean(UserService.class);

        User user = new User();
        user.setName("sss");
        user.setGender("ss");
        user.setAge(23);
        boolean b = bean.insertUserService(user);
        System.out.println(b);


    }
}

六、AOP

不通过修改源代码方式,在主干功能里面添加新功能(符合设计模式中的开闭原则,不修改源码,但是能打开扩展功能)

核心概念

连接点

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

切入点

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

通知(增强)

(1)实际增强的逻辑部分称为通知(增强)

(2)通知有多钟类型前置通知

  • 后前通知:在要增强方法前执行
  • 后置通知:在要增强方法后执行
  • 环绕通知:在要增强方法前后都执行
  • 异常通知:增强方法发生异常后执行
  • 最终通知:类似与finally,无论发生不发生异常都会执行

切面

是动作(过程)

(1)把通知应用到切入点过程

目标对象(Target)

原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的

代理( Proxy )

目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

快速入门

一、导入相关坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.alibaba.aop</groupId>
    <artifactId>spring-aop</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>
    
</project>

二、定义dao接口与实现类

package com.alibaba.dao;

public interface UserDao {
    
    
    public void save();

    public void update();

    public void delete();

    public void select();
}
package com.alibaba.dao.impl;

import com.alibaba.dao.UserDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {
    
    

    public void save() {
    
    
        //记录程序当前执行执行(开始时间)
        Long startTime = System.currentTimeMillis();
        //业务执行万次
        for (int i = 0;i<10000;i++) {
    
    
            System.out.println("book dao save ...");
        }
        //记录程序当前执行时间(结束时间)
        Long endTime = System.currentTimeMillis();
        //计算时间差
        Long totalTime = endTime-startTime;
        //输出信息
        System.out.println("执行万次消耗时间:" + totalTime + "ms");
    }

    public void update(){
    
    
        System.out.println("book dao update ...");
    }

    public void delete(){
    
    
        System.out.println("book dao delete ...");
    }

    public void select(){
    
    
        System.out.println("book dao select ...");
    }
}

三、定义通知类,制作通知

public class MyAdvice {
    
    

//    这个定义的是切入点,就是要实际增强的连接点
    @Pointcut("execution(void com.alibaba.dao.UserDao.update())")
    private void pt(){
    
    }

}

四、绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置

public class MyAdvice {
    
    

//    这个定义的是切入点,就是要实际增强的连接点
    @Pointcut("execution(void com.alibaba.dao.UserDao.update())")
    private void pt(){
    
    }

    //这个做的是共性功能,也就是通知
//    通过Before注解进行绑定
    @Before("pt()")
    public void method(){
    
    
        System.out.println(System.currentTimeMillis());
    }
}

五、定义通知类受Spring容器管理,并定义当前类为切面类

@Aspect 把当前类当成一个切面供容器处理

@Component
@Aspect
public class MyAdvice {
    
    

//    这个定义的是切入点,就是要实际增强的连接点
    @Pointcut("execution(void com.alibaba.dao.UserDao.update())")
    private void pt(){
    
    }

    //这个做的是共性功能,也就是通知
//    通过Before注解进行绑定
    @Before("pt()")
    public void method(){
    
    
        System.out.println(System.currentTimeMillis());
    }
}

六、开启Spring对AOP注解驱动支持

@EnableAspectJAutoProxy开启aop动态代理功能

@Configuration
@ComponentScan("com.alibaba")
@EnableAspectJAutoProxy
public class SpringConfig {
    
    
}

AOP的工作流程

  1. Spring的容器启动
    • 容器启动就去加载bean,哪些bean被加载呢?
      • 需要被增强的类,通知类,这个时候bean还没有被创建成功
  2. 读取所有切面配置中的切入点
    • image-20220616180253001
    • 这个例子中有两个切入点的配置,但是第一个ptx()没有被使用,所以不会被读取
  3. 初始化bean,判断bean对应类中的方法是否匹配到任意切入点
    • 匹配失败,创建原始对象,直接调用原始对象的方法即可。
    • 匹配成功,创建原始对象(目标对象)的代理对象
  4. 获取bean执行方法
    • 获取bean是原始对象时,调用方法并执行,完成操作
    • 获取bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

AOP配置管理

AOP切入点表达式

image-20220616195402997

切入点∶要进行增强的方法

切入点表达式:要进行增强的方法的描述方式

切入点达式标准格式︰动作关键字(访问修饰符(返回值.包名.类.接口名.方法名(参数)异常名)

通知类型的使用

image-20220616200852844

环绕通知注意事项

image-20220616201131430

七、事物管理

事物简介

事物是数据库操作最基本单元,逻辑上一组操作,要么都成功要么都失败,如果有一个失败所有操作都失败

spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败

事物的特性(ACID)

ACID

(1)原子性(Atomicity)

一组事物要么都成功要么都失败

(2)一致性(Consistency)

操作之前和操作之后它的总量是不变的

(3)隔离性(Isolation)

即一个事务内部的操作及正在操作的数据必须封锁起来,不被其它企图进行修改的事务看到。

两人同时去操作同一条数据,这个过程是不会产生影响的

(4)持久性(Durability)

事物提交,表中数据发生变化

事务操作(注解声明式事务管理)

在 spring 配置文件,开启事务注解

(1)在 spring 配置文件引入名称空间 tx

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

<!--    组件扫描-->
    <context:component-scan base-package="com.caq"></context:component-scan>


    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql:///user_db" />
        <property name="username" value="root" />
        <property name="password" value="root" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    </bean>

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

<!--    创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>


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

</beans>

(2)开启事务注解

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

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

@Transactional,这个注解添加到类上面,也可以添加方法上面

如果把这个注解添加类上面,这个类里面所有的方法都添加事务

如果把这个注解添加方法上面,为这个方法添加事务

事务操作(完全注解声明式事务管理)

注解类

package com.caq.spring5.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration //配置类
@ComponentScan(basePackages = "com.caq")    //扫描com.caq包下所有
@EnableTransactionManagement    //开启事物
public class TxConfig {
    
    

    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
    
    
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///user_db");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    //    JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
    
    
//        到ioc容器中根据数据类型找到dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //        注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //声明事物管理器
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
    
    
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }

}

测试

@Test
public void test3(){
    
    
    ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();

}

事物会发生回滚,数据库表数据不变

猜你喜欢

转载自blog.csdn.net/qq_45714272/article/details/125323003