ssm学习

内容来源于”尚硅谷“B站视频

一、mybatis

二、spring

在这里插入图片描述

2 IOC反转控制

2.1IOC容器

2.1.1 IOC思想

在这里插入图片描述
DI是ioc的具体实现
自己的理解:对于成员变量赋好值,传统方式是自己new,如今是ioc来,即接收di对于想要的对象赋好值。在这里插入图片描述

2.1.2 IOC容器在Spring中的实现

在这里插入图片描述
在这里插入图片描述

2.2 基于xml管理bean

2.2.1 实验一:入门案例

在这里插入图片描述
在这里插入图片描述

配置配置文件

在这里插入图片描述

获取容器和bean

在这里插入图片描述

2.2.2 Bean依赖注入配置(DI)
构造器依赖注入

在这里插入图片描述

基于setter的依赖注入

在这里插入图片描述

2.2.3 IOC容器创建和使用
容器实例化

在这里插入图片描述

获取Bean对象

在这里插入图片描述

高级特性:组件(Bean)作用域和周期方法配置
周期方法
我们可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用!这两个方法我们成为生命周期方法!

类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/502c6a635a244934bc9f7ad52186919e.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/893564cc8df94eeebc3ab67b562b9fea.png)
组件作用域配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

高级特性:FactoryBean特性和使用
简介

在这里插入图片描述

使用场景

在这里插入图片描述

FactoryBean 应用

a 准备FactoryBean实现类

// 实现FactoryBean接口时需要指定泛型
// 泛型类型就是当前工厂要生产的对象的类型
public class HappyFactoryBean implements FactoryBean<HappyMachine> {
    
    private String machineName;
    
    public String getMachineName() {
        return machineName;
    }
    
    public void setMachineName(String machineName) {
        this.machineName = machineName;
    }
    
    @Override
    public HappyMachine getObject() throws Exception {
    
        // 方法内部模拟创建、设置一个对象的复杂过程
        HappyMachine happyMachine = new HappyMachine();
    
        happyMachine.setMachineName(this.machineName);
    
        return happyMachine;
    }
    
    @Override
    public Class<?> getObjectType() {
    
        // 返回要生产的对象的类型
        return HappyMachine.class;
    }
}```
b 配置FactoryBean实现类

```java

```java
<!-- FactoryBean机制 -->
<!-- 这个bean标签中class属性指定的是HappyFactoryBean,但是将来从这里获取的bean是HappyMachine对象 -->
<bean id="happyMachine7" class="com.atguigu.ioc.HappyFactoryBean">
    <!-- property标签仍然可以用来通过setXxx()方法给属性赋值 -->
    <property name="machineName" value="iceCreamMachine"/>
</bean>```

```![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/8d0d02bc9a5542fea91adf3f1611acda.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/5df078b5561548539ad7ba973acc0c89.png)
1. XMLIoC方式问题总结
    1. 注入的属性必须添加setter方法、代码结构乱!
    2. 配置文件和Java代码分离、编写不是很方便!
    3. XML配置文件解析效率低
#### 实验六: 基于XML方式整合三层架构组件
todo 后面看吧
大致就是controller  ---service---dao


### 2.3 基于注解方式管理bean
#### 实验一: Bean注解标记和扫描 (IoC)
##### 1. **注解理解**XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

    本质上:所有一切的操作都是 Java 代码来完成的,XML 和注解只是告诉框架中的 Java 代码如何执行。

    举例:元旦联欢会要布置教室,蓝色的地方贴上元旦快乐四个字,红色的地方贴上拉花,黄色的地方贴上气球。

    ![](https://img-blog.csdnimg.cn/img_convert/46ebb739ed7af2d343b602d7b17285fb.png)

    班长做了所有标记,同学们来完成具体工作。墙上的标记相当于我们在代码中使用的注解,后面同学们做的工作,相当于框架的具体操作。
2. **扫描理解**

    Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。
#####  注解列举
1. 组件标记注解和区别

    Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean|注解|说明|
|-|-|
|@Component|该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。|
|@Repository|该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。|
|@Service|该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。|
|@Controller|该注解通常作用在控制层(如SpringMVCController),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。|


    ![](https://img-blog.csdnimg.cn/img_convert/88d64eaa95294f9e2d5c2f9067cd0bc5.png)

    通过查看源码我们得知,@Controller@Service@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。

    对于Spring使用IOC容器管理这些组件来说没有区别,也就是语法层面没有区别。所以@Controller@Service@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。

    注意:虽然它们本质上一样,但是为了代码的可读性、程序结构严谨!我们肯定不能随便胡乱标记。
##### 配置文件确定扫描范围
情况1:基本扫描配置

```XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置自动扫描的包 -->
    <!-- 1.包要精准,提高性能!
         2.会扫描指定的包和子包内容
         3.多个包可以使用,分割 例如: com.atguigu.controller,com.atguigu.service等
    -->
    <context:component-scan base-package="com.atguigu.components"/>
  
</beans>


```情况2:指定排除组件
 情况2:指定排除组件

```XML
<!-- 情况三:指定不扫描的组件 -->
<context:component-scan base-package="com.atguigu.components">
    
    <!-- context:exclude-filter标签:指定排除规则 -->
    <!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 -->
    <!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

情况3:指定扫描组件
情况3:指定扫描组件

<!-- 情况四:仅扫描指定的组件 -->
<!-- 仅扫描 = 关闭默认规则 + 追加规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<context:component-scan base-package="com.atguigu.ioc.components" use-default-filters="false">
    
    <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  1. 组件BeanName问题

    在我们使用 XML 方式管理 bean 的时候,每个 bean 都有一个唯一标识——id 属性的值,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。

    默认情况:

    类名首字母小写就是 bean 的 id。例如:SoldierController 类对应的 bean 的 id 就是 soldierController。

    使用value属性指定:

@Controller(value = "tianDog")
public class SoldierController {
}
当注解中只设置一个属性时,value属性的属性名可以省略:
@Service("smallDog")
public class SoldierService {
}


  1. 总结
    1. 注解方式IoC只是标记哪些类要被Spring管理
    2. 最终,我们还需要XML方式或者后面讲解Java配置类方式指定注解生效的包
    3. 现阶段配置方式为 注解 (标记)+ XML(扫描)
实验二: 组件(Bean)作用域和周期方法注解
  1. 组件周期方法配置
    1. 周期方法概念

      我们可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用!这两个方法我们成为生命周期方法!

      类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。

    2. 周期方法声明

public class BeanOne {

  //周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
  @PostConstruct  //注解制指定初始化方法
  public void init() {
    // 初始化逻辑
  }
}

public class BeanTwo {
  
  @PreDestroy //注解指定销毁方法
  public void cleanup() {
    // 释放资源逻辑
  }
}
  1. 组件作用域配置
    1. Bean作用域概念

      <bean 标签声明Bean,只是将Bean的信息配置给SpringIoC容器!

      在IoC容器中,这些<bean标签对应的信息转成Spring内部 BeanDefinition 对象,BeanDefinition 对象内,包含定义的信息(id,class,属性等等)!

      这意味着,BeanDefinition概念一样,SpringIoC容器可以可以根据BeanDefinition对象反射创建多个Bean对象实例。

      具体创建多少个Bean的实例对象,由Bean的作用域Scope属性指定!

    2. 作用域可选值

取值含义创建对象的时机默认值
singleton在 IOC 容器中,这个 bean 的对象始终为单实例IOC 容器初始化时
prototype这个 bean 在 IOC 容器中有多个实例获取 bean 时
    如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):
取值含义创建对象的时机默认值
request请求范围内有效的实例每次请求
session会话范围内有效的实例每次会话
3. 作用域配置
**@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON) //单例,默认值
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) //多例  二选一**
public class BeanOne {

  //周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
  @PostConstruct  //注解制指定初始化方法
  public void init() {
    // 初始化逻辑
  }
}
`
####  实验三: Bean属性赋值:引用类型自动装配 (DI) 
``1. **自动装配实现**
    1. 前提

        参与自动装配的组件(需要装配、被装配)全部都必须在IoC容器中。

        注意:不区分IoC的方式!XML和注解都可以!
    2. @Autowired注解

        在成员变量上直接标记@Autowired注解即可,不需要提供setXxx()方法。以后我们在项目中的正式用法就是这样。
    3. 给Controller装配Service

```Java
@Controller(value = "tianDog")
public class SoldierController {
    
    @Autowired
    private SoldierService soldierService;
    
    public void getMessage() {
        soldierService.getMessage();
    }
    
}
4. 给Service装配Dao
@Service("smallDog")
public class SoldierService {
    
    @Autowired
    private SoldierDao soldierDao;
    
    public void getMessage() {
        soldierDao.getMessage();
    }
}
  1. @Autowired注解细节
    1. 标记位置
      1. 成员变量

        这是最主要的使用方式!

        与xml进行bean ref引用不同,他不需要有set方法!

@Service("smallDog")
public class SoldierService {
    
    @Autowired
    private SoldierDao soldierDao;
    
    public void getMessage() {
        soldierDao.getMessage();
    }
}
    2. 构造器
@Controller(value = "tianDog")
public class SoldierController {
    
    private SoldierService soldierService;
    
    @Autowired
    public SoldierController(SoldierService soldierService) {
        this.soldierService = soldierService;
    }
    ……
    3. setXxx()方法
@Controller(value = "tianDog")
public class SoldierController {

    private SoldierService soldierService;

    @Autowired
    public void setSoldierService(SoldierService soldierService) {
        this.soldierService = soldierService;
    }
    ……
2. 工作流程

    ![](http://heavy_code_industry.gitee.io/code_heavy_industry/assets/img/img018.2ff0ae09.png)

    - 首先根据所需要的组件类型到 IOC 容器中查找
        - 能够找到唯一的 bean:直接执行装配
        - 如果完全找不到匹配这个类型的 bean:装配失败
        - 和所需类型匹配的 bean 不止一个
            - 没有 @Qualifier 注解:根据 @Autowired 标记位置成员变量的变量名作为 bean 的 id 进行匹配
                - 能够找到:执行装配
                - 找不到:装配失败
            - 使用 @Qualifier 注解:根据 @Qualifier 注解中指定的名称作为 bean 的id进行匹配
                - 能够找到:执行装配
                - 找不到:装配失败
@Controller(value = "tianDog")
public class SoldierController {
    
    @Autowired
    @Qualifier(value = "maomiService222")
    // 根据面向接口编程思想,使用接口类型引入Service组件
    private ISoldierService soldierService;
  1. 扩展JSR-250注解@Resource
    • 理解JSR系列注解

      JSR(Java Specification Requests)是Java平台标准化进程中的一种技术规范,而JSR注解是其中一部分重要的内容。按照JSR的分类以及注解语义的不同,可以将JSR注解分为不同的系列,主要有以下几个系列:

      1. JSR-175: 这个JSR是Java SE 5引入的,是Java注解最早的规范化版本,Java SE 5后的版本中都包含该JSR中定义的注解。主要包括以下几种标准注解:
      • @Deprecated: 标识一个程序元素(如类、方法或字段)已过时,并且在将来的版本中可能会被删除。
      • @Override: 标识一个方法重写了父类中的方法。
      • @SuppressWarnings: 抑制编译时产生的警告消息。
      • @SafeVarargs: 标识一个有安全性警告的可变参数方法。
      • @FunctionalInterface: 标识一个接口只有一个抽象方法,可以作为lambda表达式的目标。
      1. JSR-250: 这个JSR主要用于在Java EE 5中定义一些支持注解。该JSR主要定义了一些用于进行对象管理的注解,包括:
      • @Resource: 标识一个需要注入的资源,是实现Java EE组件之间依赖关系的一种方式。
      • @PostConstruct: 标识一个方法作为初始化方法。
      • @PreDestroy: 标识一个方法作为销毁方法。
      • @Resource.AuthenticationType: 标识注入的资源的身份验证类型。
      • @Resource.AuthenticationType: 标识注入的资源的默认名称。
      1. JSR-269: 这个JSR主要是Java SE 6中引入的一种支持编译时元数据处理的框架,即使用注解来处理Java源文件。该JSR定义了一些可以用注解标记的注解处理器,用于生成一些元数据,常用的注解有:
      • @SupportedAnnotationTypes: 标识注解处理器所处理的注解类型。
      • @SupportedSourceVersion: 标识注解处理器支持的Java源码版本。
      1. JSR-330: 该JSR主要为Java应用程序定义了一个依赖注入的标准,即Java依赖注入标准(javax.inject)。在此规范中定义了多种注解,包括:
      • @Named: 标识一个被依赖注入的组件的名称。
      • @Inject: 标识一个需要被注入的依赖组件。
      • @Singleton: 标识一个组件的生命周期只有一个唯一的实例。
      1. JSR-250: 这个JSR主要是Java EE 5中定义一些支持注解。该JSR包含了一些支持注解,可以用于对Java EE组件进行管理,包括:
      • @RolesAllowed: 标识授权角色
      • @PermitAll: 标识一个活动无需进行身份验证。
      • @DenyAll: 标识不提供针对该方法的访问控制。
      • @DeclareRoles: 声明安全角色。

      但是你要理解JSR是Java提供的技术规范,也就是说,他只是规定了注解和注解的含义,JSR并不是直接提供特定的实现,而是提供标准和指导方针,由第三方框架(Spring)和库来实现和提供对应的功能。

    • JSR-250 @Resource注解

      @Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?

      • @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
      • @Autowired注解是Spring框架自己的。
      • @Resource注解默认根据Bean名称装配,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型装配。
      • @Autowired注解默认根据类型装配,如果想根据名称装配,需要配合@Qualifier注解一起用。
      • @Resource注解用在属性上、setter方法上。
      • @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

      @Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【高于JDK11或低于JDK8需要引入以下依赖

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>
- @Resource使用
@Controller
public class XxxController {
    /**
     * 1. 如果没有指定name,先根据属性名查找IoC中组件xxxService
     * 2. 如果没有指定name,并且属性名没有对应的组件,会根据属性类型查找
     * 3. 可以指定name名称查找!  @Resource(name='test') == @Autowired + @Qualifier(value='test')
     */
    @Resource
    private XxxService xxxService;

    //@Resource(name = "指定beanName")
    //private XxxService xxxService;

    public void show(){
        System.out.println("XxxController.show");
        xxxService.show();
    }
}
实验四: Bean属性赋值:基本类型属性赋值 (DI)-@Value

@Value 通常用于注入外部化属性

声明外部配置

application.properties

catalog.name=MovieCatalog

xml引入外部配置

<!-- 引入外部配置文件-->
<context:property-placeholder location="application.properties" />

@Value注解读取配置

package com.atguigu.components;

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

/**
 * projectName: com.atguigu.components
 *
 * description: 普通的组件
 */
@Component
public class CommonComponent {

    /**
     * 情况1: ${key} 取外部配置key对应的值!
     * 情况2: ${key:defaultValue} 没有key,可以给与默认值
     */
    @Value("${catalog:hahaha}")
    private String name;

    public String getName() {
        return name;
    }

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

catalog

实验五: 基于注解+XML方式整合三层架构组件

完全注解方式(配置类+注解)—目前主流

4.4.2 实验一:配置类和扫描注解
4.4.1 完全注解开发理解

Spring 完全注解配置(Fully Annotation-based Configuration)是指通过 Java配置类 代码来配置 Spring 应用程序,使用注解来替代原本在 XML 配置文件中的配置。相对于 XML 配置,完全注解配置具有更强的类型安全性和更好的可读性。

两种方式思维转化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
xml+注解方式

配置文件application.xml

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


    <!-- 配置自动扫描的包 -->
    <!-- 1.包要精准,提高性能!
         2.会扫描指定的包和子包内容
         3.多个包可以使用,分割 例如: com.atguigu.controller,com.atguigu.service等
    -->
    <context:component-scan base-package="com.atguigu.components"/>

    <!-- 引入外部配置文件-->
    <context:property-placeholder location="application.properties" />
</beans>

测试创建IoC容器

 // xml方式配置文件使用ClassPathXmlApplicationContext容器读取
 ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("application.xml");

配置类+注解方式(完全注解方式)

配置类

使用 @Configuration 注解将一个普通的类标记为 Spring 的配置类。

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

//标注当前类是配置类,替代application.xml    
@Configuration
//使用注解读取外部配置,替代 <context:property-placeholder标签
@PropertySource("classpath:application.properties")
//使用@ComponentScan注解,可以配置扫描包,替代<context:component-scan标签
@ComponentScan(basePackages = {"com.atguigu.components"})
public class MyConfiguration {
    
}

测试创建IoC容器

// AnnotationConfigApplicationContext 根据配置类创建 IOC 容器对象
ApplicationContext iocContainerAnnotation = 
new AnnotationConfigApplicationContext(MyConfiguration.class);

可以使用 no-arg 构造函数实例化 AnnotationConfigApplicationContext ,然后使用 register() 方法对其进行配置。此方法在以编程方式生成 AnnotationConfigApplicationContext 时特别有用。以下示例演示如何执行此操作:

// AnnotationConfigApplicationContext-IOC容器对象
ApplicationContext iocContainerAnnotation = 
new AnnotationConfigApplicationContext();
//外部设置配置类
iocContainerAnnotation.register(MyConfiguration.class);
//刷新后方可生效!!
iocContainerAnnotation.refresh();

总结:

@Configuration指定一个类为配置类,可以添加配置注解,替代配置xml文件

@ComponentScan(basePackages = {"包","包"}) 替代<context:component-scan标签实现注解扫描

@PropertySource("classpath:配置文件地址") 替代 <context:property-placeholder标签

配合IoC/DI注解,可以进行完整注解开发!
4.4.3 实验二:@Bean定义组件

场景需求:将Druid连接池对象存储到IoC容器

需求分析:第三方jar包的类,添加到ioc容器,无法使用@Component等相关注解!因为源码jar包内容为只读模式!

xml方式实现

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


    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 实验六 [重要]给bean的属性赋值:引入外部属性文件 -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

</beans>

配置类方式实现

`@Bean` 注释用于指示方法实例化、配置和初始化要由 Spring IoC 容器管理的新对象。对于那些熟悉 Spring 的 `<beans/>` XML 配置的人来说, `@Bean` 注释与 `<bean/>` 元素起着相同的作用。
//标注当前类是配置类,替代application.xml    
@Configuration
//引入jdbc.properties文件
@PropertySource({"classpath:application.properties","classpath:jdbc.properties"})
@ComponentScan(basePackages = {"com.atguigu.components"})
public class MyConfiguration {

    //如果第三方类进行IoC管理,无法直接使用@Component相关注解
    //解决方案: xml方式可以使用<bean标签
    //解决方案: 配置类方式,可以使用方法返回值+@Bean注解
    @Bean
    public DataSource createDataSource(@Value("${jdbc.user}") String username,
                                       @Value("${jdbc.password}")String password,
                                       @Value("${jdbc.url}")String url,
                                       @Value("${jdbc.driver}")String driverClassName){
        //使用Java代码实例化
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driverClassName);
        //返回结果即可
        return dataSource;
    }
}
4.4.4 实验三:高级特性:@Bean注解细节
  1. @Bean生成BeanName问题

    @Bean注解源码:

public @interface Bean {
    //前两个注解可以指定Bean的标识
    @AliasFor("name")
    String[] value() default {};
    @AliasFor("value")
    String[] name() default {};
  
    //autowireCandidate 属性来指示该 Bean 是否候选用于自动装配。
    //autowireCandidate 属性默认值为 true,表示该 Bean 是一个默认的装配目标,
    //可被候选用于自动装配。如果将 autowireCandidate 属性设置为 false,则说明该 Bean 不是默认的装配目标,不会被候选用于自动装配。
    boolean autowireCandidate() default true;

    //指定初始化方法
    String initMethod() default "";
    //指定销毁方法
    String destroyMethod() default "(inferred)";
}

  指定@Bean的名称:
@Configuration
public class AppConfig {

  @Bean("myThing") //指定名称
  public Thing thing() {
    return new Thing();
  }
}
  `@Bean` 注释注释方法。使用此方法在指定为方法返回值的类型的 `ApplicationContext` 中注册 Bean 定义。缺省情况下,Bean 名称与方法名称相同。下面的示例演示 `@Bean` 方法声明:
@Configuration
public class AppConfig {

  @Bean
  public TransferServiceImpl transferService() {
    return new TransferServiceImpl();
  }
}
  前面的配置完全等同于下面的Spring XML:
<beans>
  <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
  1. @Bean 初始化和销毁方法指定

    @Bean 注解支持指定任意初始化和销毁回调方法,非常类似于 Spring XML 在 bean 元素上的 init-methoddestroy-method 属性,如以下示例所示:

public class BeanOne {

  public void init() {
    // initialization logic
  }
}

public class BeanTwo {

  public void cleanup() {
    // destruction logic
  }
}

@Configuration
public class AppConfig {

  @Bean(initMethod = "init")
  public BeanOne beanOne() {
    return new BeanOne();
  }

  @Bean(destroyMethod = "cleanup")
  public BeanTwo beanTwo() {
    return new BeanTwo();
  }
}
  1. @Bean Scope作用域

    可以指定使用 @Bean 注释定义的 bean 应具有特定范围。您可以使用在 Bean 作用域部分中指定的任何标准作用域。

    默认作用域为 singleton ,但您可以使用 @Scope 注释覆盖此范围,如以下示例所示:

@Configuration
public class MyConfiguration {

  @Bean
  @Scope("prototype")
  public Encryptor encryptor() {
    // ...
  }
}
  1. @Bean方法之间依赖

    准备组件

public class HappyMachine {
    
    private String machineName;
    
    public String getMachineName() {
        return machineName;
    }
    
    public void setMachineName(String machineName) {
        this.machineName = machineName;
    }
}
public class HappyComponent {
    //引用新组件
    private HappyMachine happyMachine;

    public HappyMachine getHappyMachine() {
        return happyMachine;
    }

    public void setHappyMachine(HappyMachine happyMachine) {
        this.happyMachine = happyMachine;
    }

    public void doWork() {
        System.out.println("HappyComponent.doWork");
    }

}
  **Java配置类实现:**

  方案1:

  直接调用方法返回 Bean 实例:在一个 `@Bean` 方法中直接调用其他 `@Bean` 方法来获取 Bean 实例,虽然是方法调用,也是通过IoC容器获取对应的Bean,例如:
@Configuration
public class JavaConfig {

    @Bean
    public HappyMachine happyMachine(){
        return new HappyMachine();
    }

    @Bean
    public HappyComponent happyComponent(){
        HappyComponent happyComponent = new HappyComponent();
        //直接调用方法即可! 
        happyComponent.setHappyMachine(happyMachine());
        return happyComponent;
    }

}
  方案2:

  参数引用法:通过方法参数传递 Bean 实例的引用来解决 Bean 实例之间的依赖关系,例如:
package com.atguigu.config;

import com.atguigu.ioc.HappyComponent;
import com.atguigu.ioc.HappyMachine;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * projectName: com.atguigu.config
 * description: 配置HappyComponent和HappyMachine关系
 */

@Configuration
public class JavaConfig {

    @Bean
    public HappyMachine happyMachine(){
        return new HappyMachine();
    }

    /**
     * 可以直接在形参列表接收IoC容器中的Bean!
     *    情况1: 直接指定类型即可
     *    情况2: 如果有多个bean,(HappyMachine 名称 ) 形参名称等于要指定的bean名称!
     *           例如:
     *               @Bean
     *               public Foo foo1(){
     *                   return new Foo();
     *               }
     *               @Bean
     *               public Foo foo2(){
     *                   return new Foo()
     *               }
     *               @Bean
     *               public Component component(Foo foo1 / foo2 通过此处指定引入的bean)
     */
    @Bean
    public HappyComponent happyComponent(HappyMachine happyMachine){
        HappyComponent happyComponent = new HappyComponent();
        //赋值
        happyComponent.setHappyMachine(happyMachine);
        return happyComponent;
    }

}

@Import 注释允许从另一个配置类加载 @Bean 定义,如以下示例所示:

@Configuration
public class ConfigA {

  @Bean
  public A a() {
    return new A();
  }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

  @Bean
  public B b() {
    return new B();
  }
}

现在,在实例化上下文时不需要同时指定 ConfigA.classConfigB.class ,只需显式提供 ConfigB ,如以下示例所示:

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

  // now both beans A and B will be available...
  A a = ctx.getBean(A.class);
  B b = ctx.getBean(B.class);
}

此方法简化了容器实例化,因为只需要处理一个类,而不是要求您在构造期间记住可能大量的 @Configuration 类。

4.4.6 实验五:基于注解+配置类方式整合三层架构组件
  1. 需求分析

    搭建一个三层架构案例,模拟查询全部学生(学生表)信息,持久层使用JdbcTemplate和Druid技术,使用注解+配置类方式进行组件管理!

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 数据库准备

create database studb;

use studb;

CREATE TABLE students (
  id INT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  gender VARCHAR(10) NOT NULL,
  age INT,
  class VARCHAR(50)
);

INSERT INTO students (id, name, gender, age, class)
VALUES
  (1, '张三', '男', 20, '高中一班'),
  (2, '李四', '男', 19, '高中二班'),
  (3, '王五', '女', 18, '高中一班'),
  (4, '赵六', '女', 20, '高中三班'),
  (5, '刘七', '男', 19, '高中二班'),
  (6, '陈八', '女', 18, '高中一班'),
  (7, '杨九', '男', 20, '高中三班'),
  (8, '吴十', '男', 19, '高中二班');

  1. 项目准备
    1. 项目创建

      spring-java-practice-06

    2. 依赖导入

<dependencies>
      <!--spring context依赖-->
      <!--当你引入SpringContext依赖之后,表示将Spring的基础依赖引入了-->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>6.0.6</version>
      </dependency>

      <!-- 数据库驱动和连接池-->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.25</version>
      </dependency>

      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.2.8</version>
      </dependency>
      
      <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
       </dependency>

      <!-- spring-jdbc -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>6.0.6</version>
      </dependency>

</dependencies> 
  3. 实体类准备
public class Student {

    private Integer id;
    private String name;
    private String gender;
    private Integer age;
    private String classes;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getClasses() {
        return classes;
    }

    public void setClasses(String classes) {
        this.classes = classes;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                ", classes='" + classes + '\'' +
                '}';
    }
}

  1. 三层架构搭建和实现
    1. 持久层
//接口
public interface StudentDao {

    /**
     * 查询全部学生数据
     * @return
     */
    List<Student> queryAll();
}

//实现类
@Repository
public class StudentDaoImpl implements StudentDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 查询全部学生数据
     * @return
     */
    @Override
    public List<Student> queryAll() {

        String sql = "select id , name , age , gender , class as classes from students ;";

        /*
          query可以返回集合!
          BeanPropertyRowMapper就是封装好RowMapper的实现,要求属性名和列名相同即可
         */
        List<Student> studentList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));

        return studentList;
    }
}

  2. 业务层
//接口
public interface StudentService {

    /**
     * 查询全部学员业务
     * @return
     */
    List<Student> findAll();

}

//实现类
@Service
public class StudentServiceImpl  implements StudentService {

    @Autowired
    private StudentDao studentDao;

    /**
     * 查询全部学员业务
     * @return
     */
    @Override
    public List<Student> findAll() {

        List<Student> studentList =  studentDao.queryAll();

        return studentList;
    }
}

  3. 表述层
@Controller
public class StudentController {

    @Autowired
    private StudentService studentService;

    public void  findAll(){
       List<Student> studentList =  studentService.findAll();
        System.out.println("studentList = " + studentList);
    }
}

  1. 三层架构IoC配置类
@Configuration
@ComponentScan(basePackages = "com.atguigu")
@PropertySource("classpath:jdbc.properties")
public class JavaConfig {

    @Value("${atguigu.url}")
    private String url;
    @Value("${atguigu.driver}")
    private String driver;
    @Value("${atguigu.username}")
    private String username;
    @Value("${atguigu.password}")
    private String password;

    @Bean(destroyMethod = "close")
    public DruidDataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driver);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

}
  1. 运行测试
public class ControllerTest {

    @Test
    public  void testRun(){

        AnnotationConfigApplicationContext applicationContext =
                new AnnotationConfigApplicationContext(JavaConfig.class);

        StudentController studentController = applicationContext.getBean(StudentController.class);

        studentController.findAll();

    }
}
  1. 注解+配置类 IoC方式总结
    1. 完全摒弃了XML配置文件
    2. 自定义类使用IoC和DI注解标记
    3. 第三方类使用配置类声明方法+@Bean方式处理
    4. 完全注解方式(配置类+注解)是现在主流配置方式

4.5 三种配置方式总结

4.5.1 XML方式配置总结
1. 所有内容写到xml格式配置文件中
2. 声明bean通过<bean标签
3. <bean标签包含基本信息(id,class)和属性信息 <property name value / ref
4. 引入外部的properties文件可以通过<context:property-placeholder
5. IoC具体容器实现选择ClassPathXmlApplicationContext对象
4.5.2 XML+注解方式配置总结
1. 注解负责标记IoC的类和进行属性装配
2. xml文件依然需要,需要通过<context:component-scan标签指定注解范围
3. 标记IoC注解:@Component,@Service,@Controller,@Repository 
4. 标记DI注解:@Autowired @Qualifier @Resource @Value
5. IoC具体容器实现选择ClassPathXmlApplicationContext对象
4.5.3 完全注解方式配置总结
1. 完全注解方式指的是去掉xml文件,使用配置类 + 注解实现
2. xml文件替换成使用@Configuration注解标记的类
3. 标记IoC注解:@Component,@Service,@Controller,@Repository 
4. 标记DI注解:@Autowired @Qualifier @Resource @Value
5. <context:component-scan标签指定注解范围使用@ComponentScan(basePackages = {"com.atguigu.components"})替代
6. <context:property-placeholder引入外部配置文件使用@PropertySource({"classpath:application.properties","classpath:jdbc.properties"})替代
7. <bean 标签使用@Bean注解和方法实现
8. IoC具体容器实现选择AnnotationConfigApplicationContext对象

4.6 整合Spring5-Test5搭建测试环境

  1. 整合测试环境作用

    好处1:不需要自己创建IOC容器对象了

    好处2:任何需要的bean都可以在测试类中直接享受自动装配

  2. 导入相关依赖

<!--junit5测试-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.3.1</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>6.0.6</version>
    <scope>test</scope>
</dependency>
  1. 整合测试注解使用
//@SpringJUnitConfig(locations = {"classpath:spring-context.xml"})  //指定配置文件xml
@SpringJUnitConfig(value = {BeanConfig.class})  //指定配置类
public class Junit5IntegrationTest {
    
    @Autowired
    private User user;
    
    @Test
    public void testJunit5() {
        System.out.println(user);
    }
}

AOP 面向切面编程

5.3 面向切面编程思维(AOP)

  1. 面向切面编程思想AOP

    AOP:Aspect Oriented Programming面向切面编程

    AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

    使用AOP,可以在不修改原来代码的基础上添加新功能。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. AOP思想主要的应用场景

    AOP(面向切面编程)是一种编程范式,它通过将通用的横切关注点(如日志、事务、权限控制等)与业务逻辑分离,使得代码更加清晰、简洁、易于维护。AOP可以应用于各种场景,以下是一些常见的AOP应用场景:

    1. 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后或异常抛出时记录日志。
    2. 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务。
    3. 安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
    4. 性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。
    5. 异常处理:系统中可能出现各种异常情况,如空指针异常、数据库连接异常等,可以使用AOP来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志、发送邮件等)。
    6. 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
    7. 动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。

    综上所述,AOP可以应用于各种场景,它的作用是将通用的横切关注点与业务逻辑分离,使得代码更加清晰、简洁、易于维护。

  3. AOP术语名词介绍

    1-横切关注点

    从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。

    这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。

    AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务、异常等。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

    2-通知(增强)

    每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

    • 前置通知:在被代理的目标方法前执行
    • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝
    • 异常通知:在被代理的目标方法异常结束后执行(死于非命
    • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论
    • 环绕通知:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

    3-连接点 joinpoint

    这也是一个纯逻辑概念,不是语法定义的。

    指那些被拦截到的点。在 Spring 中,可以被动态代理拦截目标类的方法

    4-切入点 pointcut

    定位连接点的方式,或者可以理解成被选中的连接点!

    是一个表达式,比如execution(* com.spring.service.impl..(…))。符合条件的每个方法都是一个具体的连接点。

    5-切面 aspect

    切入点和通知的结合。是一个类。

    6-目标 target

    被代理的目标对象。

    7-代理 proxy

    向目标对象应用通知之后创建的代理对象。

    8-织入 weave

    指把通知应用到目标上,生成代理对象的过程。可以在编译期织入,也可以在运行期织入,Spring采用后者。

三、spring mvc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值