左璞凡的博客

日出之美便在于它脱胎于最深的黑暗

0%

SSM_Spring

[Spring Framework]

系统架构

image-20231018201210964

Spring Framework是spring家族中的底层框架及设计框架

核心概念&&入门案例

IOC(Inversion of Control)控制反转

使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到 外部,此思想称为控制反转。

Spring与IOC的关系:Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的”外部”。

IOC容器中存放的是一个个数据层和业务层的bean对象

DI(Dependency Injection)依赖注入

在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入

IOC实现案例

  1. 创建项目:

image-20231020121944399

  1. pom.xml添加Spring的依赖jar包
1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
  1. 创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface BookDao {
public void save();
}

public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}

public interface BookService {
public void save();
}

public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();

public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
  1. resources下添加spring配置文件,并完成bean的配置

image-20231020122221806

1
2
<bean id="bookDao" class="dao.impl.BookDaoImpl"/>
<bean id="bookService" class="service.impl.BookServiceImpl"/>
  1. 使用Spring提供的接口完成IOC容器的创建并加载配置文件
1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
//获取IOC容器
//1.加载类路径下的配置文件,默认为立即加载
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
//2.从文件系统下加载配置文件
//ApplicationContext ctx = new FileSystemXmlApplicationContext("绝对路径");

//3.已过时,延迟加载
// Resource resources = new ClassPathResource("applicationContext.xml");
//BeanFactory bf = new XmlBeanFactory(resources);
}
  1. 从容器中获取对象进行方法调用(获取bean)
1
2
3
4
5
6
7
8
//在主方法下接着上文:
//法一:
//BookService bookService = (BookService) ctx.getBean("bookService");
//法二:
//BookService bookService = ctx.getBean("bookService",bookDao.class);
//法三:
BookService bookService = ctx.getBean(bookDao.class);
bookService.save();

DI入门案例

(项目创建在下文中不再讲解)

  1. 删除业务层中使用new的方式创建的dao对象

  2. 在业务层提供BookDao的setter方法

  3. 在配置文件中添加依赖注入的配置

1
2
3
4
5
6
7
8
9
<bean id="bookDao" class="dao.impl.BookDaoImpl"/>
<bean id="bookService" class="service.impl.BookServiceImpl">
<!--配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean
-->
<property name="bookDao" ref="bookDao"/>
</bean>
  1. 运行程序调用方法

bean配置,实例化及生命周期

bean配置

  1. bean的别名
1
2
3
4
5
<bean id="bookService" name="service service2 bookEbi" class="service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>

//用name标签给bean起别名并在main方法中修改,如下
1
2
3
4
5
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("service");
bookService.save();
}
  1. bean的作用范围
1
2
<bean id="bookDao" name="dao" class="dao.impl.BookDaoImpl" scope="singleton"/>
<-- 添加标签scope限制作用范围,singleton为单例,prototype为非单例-->
  1. 适合交给spring管理的bean对象:表现层对象 业务层对象 数据层对象 工具对象

bean实例化

  1. 用无参构造方法来实例化对象
1
2
3
4
//用无参构造方法来实例化对象
public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
  1. 使用静态工厂实例化对象
1
2
3
4
5
6
7
//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");
return new OrderDaoImpl();
}
}
1
<bean id="orderDao" class="factory.OrderDaoFactory" factory-method="getOrderDao"/>
  1. 使用实例工厂实例化bean
1
2
3
4
5
//创建实例工厂对象(main方法中)
UserDaoFactory userDaoFactory = new UserDaoFactory();
//通过实例工厂对象创建对象
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
1
2
3
4
5
6
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
//工厂类
1
2
3
<bean id="userFactory" class="factory.UserDaoFactory"/>

<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
  1. 使用FactoryBean实例化bean

    • 创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
    return new UserDaoImpl();
    }
    //返回所创建类的Class对象
    public Class<?> getObjectType() {
    return UserDao.class;
    }
    }
    • 在Spring的配置文件中进行配置
    1
    <bean id="userDao" class="factory.UserDaoFactoryBean"/>
    • FactoryBean接口其实会有三个方法
    1
    2
    3
    4
    5
    T getObject() throws Exception;
    Class<?> getObjectType();
    default boolean isSingleton() {
    return true;
    }

bean生命周期

方法一

  1. 添加初始化和销毁方法
1
2
3
4
5
6
7
8
9
10
11
12
13
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
}
  1. 配置生命周期
1
2
<bean id="bookDao" class="dao.impl.BookDaoImpl" init-method="init"
destroy-method="destory"/>
  1. 两种方法关闭容器
  • (法1)close关闭容器
1
2
3
4
ClassPathXmlApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
//调用ctx的close方法
ctx.close();
  • (法2)注册钩子关闭容器
1
ctx.registerShutdownHook();

方法二

修改BookServiceImpl类,添加两个接口InitializingBean, DisposableBean并实现接口中的 两个方法afterPropertiesSet和destroy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;}

public void save() {
System.out.println("book service save ...");
bookDao.save();}

public void destroy() throws Exception {
System.out.println("service destroy");}

public void afterPropertiesSet() throws Exception {
System.out.println("service init");}

}

注入问题

两种常见的依赖注入方式:强制注入(Setter 注入)和构造器注入

[1]. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现,强制依赖指对象在创建的过程中必须要注入指定的参数

[2]. 可选依赖使用setter注入进行,灵活性强,可选依赖指对象在创建过程中注入的参数可有可无

[3]. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相 对严谨

[4]. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选 依赖的注入

[5]. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入

[6]. 自己开发的模块推荐使用setter注入

setter引用类型注入(在实现类中提供setter方法)

1
2
3
<bean id="" class="">
<property name="" ref=""/>
</bean>

setter简单类型注入(在实现类中提供setter方法)

1
2
3
<bean id="" class="">
<property name="" value=""/>
</bean>

构造器注入引用类型(在实现类中提供构造方法)

1
2
3
<bean id="" class="">
<constructor-arg name="" index="" type="" ref=""/>
</bean>

构造器注入简单类型(在实现类中提供构造方法)

1
2
3
<bean id="" class="">
<constructor-arg name="" index="" type="" value=""/>
</bean>

依赖自动装配(提供setter方法)

  1. 按类型,类型匹配唯一
1
2
3
4
<bean class="dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="service.impl.BookServiceImpl"
autowire="byType"/>
  1. 按名称,具有指定名称的bean
1
2
3
4
<bean class="dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="service.impl.BookServiceImpl"
autowire="byName"/>

集合注入

  1. array注入
1
2
3
4
5
6
7
<bean id="" class="">
<property name="array">
<array>
<value>...</value>
</array>
</property>
</bean>
  1. list注入
1
2
3
4
5
6
7
<bean id="" class="">
<property name="list">
<list>
<value>...</value>
</list>
</property>
</bean>
  1. set注入
1
2
3
4
5
6
7
<bean id="" class="">
<property name="set">
<set>
<value>...</value>
</set>
</property>
</bean>
  1. map注入
1
2
3
4
5
6
7
<bean id="" class="">
<property name="map">
<map>
<entry key="" value=""/>
</map>
</property>
</bean>
  1. properties注入
1
2
3
4
5
6
7
<bean id="" class="">
<property name="properties">
<props>
<prop key="">...</prop>
</props>
</property>
</bean>

容器

容器类结构层次

image-20231020132429374

IOC/DI注解开发IOC/DI注解开发

注解开发定义bean

  1. 在需要添加注解的表现层、业务层或是数据层的类上添加注解
1
2
3
4
5
6
7
8
9
@Component("...")
public class ...impl implements ...

/**
*@Component
*在表现层上可以添加 *@Controller
*在业务层上可以添加 *@Service
*在数据层上可以添加 *@Repository
*/
  1. 配置Spring的注解包扫描
1
<context:component-scan base-package="包名"/>
  1. 从IOC容器中获取对应的bean对象
1
2
3
4
5
6
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
//按类型获取bean,例如
BookService bookService = ctx.getBean(BookService.class);
}

纯注解开发模式,使用Java类替代配置文件

  1. 将配置文件applicationContext.xml删除掉
  2. 创建一个配置类SpringConfig,标识该类为配置类,并用注解替换包扫描配置
1
2
3
4
@Configuration
@ComponentScan("包名")//此注解只能添加一次,多个数据用数组格式
public class SpringConfig {
}
  1. 运行
1
2
3
4
5
6
7
8
public class AppForAnnotation {
public static void main(String[] args) {
ApplicationContext ctx = new
AnnotationConfigApplicationContext(SpringConfig.class);
//调用,例如
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
}
}

注解开发bean作用范围与生命周期管理

  1. 管理是否单例
1
2
3
4
5
6
@Repository
//@Scope设置bean的作用范围
@Scope("prototype")
public class ...Impl implements ... {
}
}
  1. 生命周期
1
2
3
4
5
6
7
8
9
10
11
12
@Repository
public class ...impl implements ...{

@PostConstruct //在构造方法之后执行,替换 init-method
public void init() {
System.out.println("init ...");
}
@PreDestroy //在销毁方法之前执行,替换 destroy-method
public void destroy() {
System.out.println("destroy ...");
}
}

注意:@PostConstruct和@PreDestroy注解如果找不到,需要导入下面的jar包

1
2
3
4
5
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>

注解开发依赖注入

  1. 注解实现按照类型注入
1
2
3
4
5
6
7
//在相应实现方法上添加@Autowired注解,例如
@Service
public class BookServiceImpl implements BookService {
@Autowired//不需要setter方法
private BookDao bookDao;
}
}
  1. 注解实现按照名称注入
1
2
3
4
5
6
7
@Service
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao1")
//@Qualifier来指定注入哪个名称的bean对象,@Qualifier不能独立使用,必须和@Autowired一起使用
private BookDao bookDao;
}
  1. 简单数据类型注入
1
2
3
4
5
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
@Value("...")
private String name;
}
  1. 注解读取properties配置文件
  • 在配置类上添加@PropertySource注解
1
2
3
4
5
@Configuration
@ComponentScan("包名")
@PropertySource("配置文件.properties")
public class SpringConfig {
}
  • 使用@Value读取配置文件中的内容
1
2
3
4
5
6
7
8
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
@Value("${...}")
private String name;
}
//如果读取的properties配置文件有多个,可以使用@PropertySource的属性来指定多个
// @PropertySource({"jdbc.properties","xxx.properties"})
//@PropertySource注解属性中不支持使用通配符*,运行会报错

注解开发管理第三方bean,以下以druid连接池为例

  1. 现在pom文件中导入druid依赖
  2. 对于数据源的bean,我们新建一个JdbcConfig配置类,并把数据源配置到该类下
1
2
3
4
5
6
7
8
9
10
11
public class JdbcConfig {
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
  1. 将该配置类引入到spring配置类中
  • 方法一:使用包扫描引入(不推荐使用)

    • 在Spring的配置类上添加包扫描

      1
      2
      3
      4
      @Configuration
      @ComponentScan("config")
      public class SpringConfig {
      }
    • 在JdbcConfig上添加配置注解

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      @Configuration
      public class JdbcConfig {
      @Bean
      public DataSource dataSource(){
      DruidDataSource ds = new DruidDataSource();
      ds.setDriverClassName("com.mysql.jdbc.Driver");
      ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
      ds.setUsername("root");
      ds.setPassword("root");
      return ds;
      }
      }
  • 方法二:使用@Import引入

    • 在spring配置类中引入

      1
      2
      3
      4
      5
      6
      @Configuration
      //@ComponentScan("config")
      @Import({JdbcConfig.class})//@Import参数需要的是一个数组,可以引入多个配置类。

      public class SpringConfig {
      }

注解开发实现为第三方bean注入资源

  1. 注入简单类型数据
  • 在JdbcConfig配置类中进行修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class JdbcConfig {
@Value("com.mysql.jdbc.Driver")
private String driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("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;
}
}
  1. 注入引用类型
  • 在SpringConfig中扫描BookDao
1
2
3
4
5
@Configuration
@ComponentScan("dao")
@Import({JdbcConfig.class})
public class SpringConfig {
}
  • 在JdbcConfig类的方法上添加参数
1
2
3
4
5
6
7
8
9
10
@Bean
public DataSource dataSource(BookDao bookDao){
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}

注解总结:XML配置和注解的区别

image-20231020135842530

Spring整合Mybatis&&Junit

整合Mybatis

  1. 在pom文件中导入以下依赖

    Spring-context Druid Mybatis Mysql-connector-java Spring-jdbc Mybatis-Spring

  2. 创建Spring的主配置类

1
2
3
4
5
6
//配置类注解
@Configuration
//包扫描,主要扫描的是项目中的AccountServiceImpl类
@ComponentScan("...")
public class SpringConfig {
}
  1. 创建数据源的配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}
}
  1. 主配置类中读properties并引入数据源配置类
1
2
3
4
5
6
@Configuration
@ComponentScan("...")
@PropertySource("classpath:jdbc.properties")
@Import(JdbcConfig.class)
public class SpringConfig {
}
  1. 创建Mybatis配置类并配置SqlSessionFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MybatisConfig {
//定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
//设置模型类的别名扫描
ssfb.setTypeAliasesPackage("domain");
//设置数据源
ssfb.setDataSource(dataSource);
return ssfb;
}
//定义bean,返回MapperScannerConfigurer对象
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
  1. 主配置类中引入Mybatis配置类
1
2
3
4
5
6
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
  1. 从IOC中获取对象并运行

整合junit

  1. 引入依赖Junit Spring-test

  2. 编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//设置类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//设置Spring环境对应的配置类
@ContextConfiguration(classes = {SpringConfiguration.class}) //加载配置类
//@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载
配置文件
public class AccountServiceTest {
//支持自动装配注入bean
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
System.out.println(accountService.findById(1));
}
@Test
public void testFindAll(){
System.out.println(accountService.findAll());
}
}

Spring_AOP简介

AOP面向切面编程

作用:在不惊动原始设计的基础上为其进行功能增强

Spring理念:无侵入式

SpringAOP本质:代理模式

AOP概念

image-20231020142905246

image-20231020142538463

AOP工作流程

  1. Spring容器启动
  2. 读取所有切面配置中的切入点
  3. 初始化bean,判定bean对应的类中方法是否匹配到任意切入点
    • 匹配失败,创建对象
    • 匹配成功,创建原始对象(目标对象)的代理对象
  4. 获取bean执行方法(获取bean,调用方法执行,完成操作)

AOP切入点表达式

  1. 标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
1
execution(public User service.UserService.findById(int))
  • 访问修饰符:public等,可省略

  • 异常名:方法定义中抛出指定异常,可省略

  1. 通配符
1
2
3
4
5
6
//  * 单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
executionpublic * com.itheima.*.UserService.find*(*))
// .. 多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
executionpublic User com..UserService.findById(..))
// + 专用于匹配子类类型
execution(* *..*Service+.*(..))

image-20231020143747051

AOP通知类型

  1. 前置通知 @Before
  2. 后置通知@After
  3. 环绕通知@Around
  4. 返回后通知@AfterReturning
  5. 抛出异常后通知@AfterThrowing

重点:环绕通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
@Aspect
public class MyAdvice {

@Pointcut("execution(dao.BookDao.select())")
private void pt2(){}

@Around("pt2()")
public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before advice ...");
//表示对原始操作的调用
Object ret = pjp.proceed();//原始操作的返回值
System.out.println("around after advice ...");
return ret;
}
}

AOP通知获取数据

获取参数: 环绕通知 ProceedingJoinPoint方法

Spring事务简介

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

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

事务角色

事务管理员:发起事务方

事务协调员:加入事务方

开启事务注解

@Transactional(xxx,xxx)

事务属性

image-20231020144716354

image-20231020144736866