SSM学习笔记(二)——框架、Spring

Spring

  • 一、Spring简述:
    • Spring的核心就是IOC和AOP。
    • IOC
    • AOP
  • 二、Spring的Bean
    • (一)、Bean 的完整生命周期
        • 记住
    • (二)SpringBean循环依赖和三级缓存
    • (三)、Bean的两种创建方式
      • 引入支持pom.xml
      • (1)基于配置文件的Bean
          • Spring配置文件创建Bean对象
          • 测试类1 ⭐
      • (2)基于注解的Bean
          • Spring注解创建Bean对象 ⭐
          • 测试类2⭐
      • (3) @Component标签
  • 三、依赖注入(DI)
    • (一)三种方式:构造器、setter方法、接口注入
      • (1)构造器注入
      • (2) setter注入
      • (3)接口注入
    • (二)两种方式:配置文件 and 注解(3)
      • 1. @Autowired注解
      • 2. @Resource注解
      • 3. @Value注解
  • 四、Bean的五种作用域
      • 正常spring框架支持两个:
      • web项目,5个:
      • **Spring默认的作用域是 :singleton**
  • 五、Bean的五种自动装配By配置文件

框架是半成品软件,由它生成通用的程序,需要做的是补充配置信息,指导框架。由配置文件、注解两种方法实现。两者各有优缺点。配置文件可以统一配置,清晰明了;但代码量比较大。注解更简单便捷但信息不一目了然。

一、Spring简述:

Spring集成形框架,浅集成,更复杂。 1.0
SpringMVC属于Spring的web部分,是以Spring为基础二次开发形成的框架
SpingBoot是对Spring 的扩展,不仅仅是浅集成,深入集成,更加简便。 2.0
Spring Cloud是在SpringBoot基础上扩展的,完全针对于分布式、微服务的一套框架。 3.0
SSM学习笔记(二)——框架、Spring_第1张图片

Spring的核心就是IOC和AOP。

IOC

  • IOC的本质就是——把bean的管理交给框架去做,spring自己维护一个bean容器,将所有bean进行统一管理,这样一来,所有需要用到实例的场景都不需要写繁琐且重复的实例化代码,而是——简单地完成bean声明和注入依赖就可以了。
class QueryController{
    UserService userService;
    BookService bookservice;
    QueryController() {
            //需要程序自己实例化对象。
        userService = new UserService;
        userService.setUserDao(new UserDao());
        bookservice = new BookService;
        bookservice.setBookDao(new BookDao());
    }
    public static void main(Strings[] args) {
        QueryController queryController = new QueryController();
    }
}
@Controller
class QueryController{
    @Autowired
    UserService userService;
    
    @Autowired
    BookService bookservice;
}

AOP

  • AOP的本质是——利用动态代理完成统一切面的功能,利用AOP,我们可以把一些横向的同一类型代码进行复用,比如登录拦截,身份校验,安全管理等等,这些——不需要内嵌到业务代码中,但却经常要用到的东西,就可以利用AOP来做成一个切面,然后指定需要拦截的方法,AOP通过动态代理会将切面代码加入到代理对象中,于是你执行业务代码时,相当于在执行代理对象,就会相应地调用切面方法。

一个最简单的动态代理实现如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
}

二、Spring的Bean

JavaBean(Java中的对象Bean)→ Spring的Bean既不用实现接口也不用继承父类,只用创建自己的类;Spring负责创建对象和处理关系。(由SPring创建对象交出创建对象权力的Bean就是Spring的Bean对象) ——Bean对象在beanfactory工厂创建——引入动态代理模式 ——SSM符合Spring的要求

(一)、Bean 的完整生命周期

记住

  1. spring负责实例化和关系注入——IOC的核心
  2. 实例化后依旧不可以对对象进行操作,还需要进行一系列的操作,也是丰富对象的过程
  3. 容器开对象创建,容器关对象才被销毁。

传统的Java应用中,bean的生命周期: 很简单,使用Java关键字 new 进行Bean 的实例化 → 然后该Bean 就能够使用了。 → 一旦bean不再被使用,则由Java自动进行垃圾回收。

Spring管理Bean的生命周期: 复杂多了,正确理解Bean 的生命周期非常重要,因为Spring对Bean的管理可扩展性非常强,下面展示了一个Bean的构造过程
SSM学习笔记(二)——框架、Spring_第2张图片
如上图所示,Bean 的生命周期还是比较复杂的,下面来对上图每一个步骤做文字描述:

  1. Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化【beanfactory创建对象】

  2. Bean实例化后对将Bean的引入和值注入到Bean的属性中【依赖注入di,属性注入】

  3. 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法【名字方法注入】

  4. 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入【工厂方法注入】

  5. 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。【上下文(对象创建时所带配置信息的指令等)】

  6. 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。

  7. 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。

  8. 如果bean使用init-method声明了初始化方法,该方法也会被调用

  9. 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。

此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。【单例模式,保证对象使用,但该对象使用的生命周期被拉长了,长期占用内存】

  1. 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。——结束

(二)SpringBean循环依赖和三级缓存

三级缓存
https://www.cnblogs.com/semi-sub/p/13548479.html
https://baijiahao.baidu.com/s?id=1711380208642133437&wfr=spider&for=pc
只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题

(三)、Bean的两种创建方式

注解逐渐替代XML配置文件

引入支持pom.xml

maven——简化jar的引入

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

spring-beans——创建对象
spring-context——读上下文——读取配置文件

(1)基于配置文件的Bean

一般类 ——new对象

public class SomeBean {}
//调用
{
     SomeBean someBean = new SomeBean();
}
Spring配置文件创建Bean对象

【任意类都是对象,且类不能由new,不能创建对象。必须由Spring来创建对象】——单例,唯一对象
=>bean factory


<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="someBean" class="com.jd.javaconfig.SomeBean">bean>
beans>
 对比 
  SomeBean someBean = new SomeBean();
  
测试类1 ⭐
public class MyTest {
    @Test
    public void test1(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        //读配置文件
        SomeBean sb = (SomeBean)ctx.getBean("someBean");
        //getBean获得对象
        System.out.println(sb);
        //使用
    }
}
(ClassPath☞类的根路径——src文件夹下)

src源文件,真正可以用的是编译后出现的class下的

(2)基于注解的Bean

public class SomeBean {}
public class OtherBean {}
Spring注解创建Bean对象 ⭐

=>bean factory

// 作为Spring的主配置文件
@Configuration
public class AppConfig {
  //@Bean标签表示让Spring托管bean
  @Bean
  public SomeBean someBean(){
    return new SomeBean();
  }
  @Bean
  public OtherBean otherBean(){
    return new  OtherBean();
  }
}

AppConfig这个类是一个配置文件,不是XML配置文件——工厂模式

测试类2⭐
@Test
public void test() {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    //读取配置信息
    SomeBean sb = ctx.getBean(SomeBean.class);
    OtherBean ob = ctx.getBean(OtherBean.class);
    //getBean()获取对象
    System.out.println(sb);
    System.out.println(ob);
    //使用
}

这个是基于AnnotationConfigApplicationContext来配置的。(Annotation注解,Config配置)

到这里我们就已经学完了两个重要的标签 @Configuration和@Bean, @Configuration标签表示这个类可被Spring识别的配置对象的类,只有有这个标记的标签的类才能使用 @Bean标签作用于对应的方法上面 @Bean(destroyMethod = "destory", initMethod = "init")也可以通过这样的写法来配置bean的销毁方法和初始化方法

(3) @Component标签

@Component
public class SomeBean {}
@Component
public class OtherBean {}

Spring注解创建Bean

//@Configuration标签表示这个类可被Spring识别的配置对象的类,这有有这个标记的标签的类才能使用@Bean标签作用于对应的方法上面
// @ComponentScan:开启组件自动扫描;默认情况下,它会扫描当前类所在的包及其子包中的所有标签对象加载到Spring容器
@Configuration
@ComponentScan(basePackages="com.jd.scan")
public class AppConfig {}

测试类

public class MyTest {
  @Test
  public void test() {
      ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
      SomeBean sb = ctx.getBean(SomeBean.class);
      OtherBean ob = ctx.getBean(OtherBean.class);
      System.out.println(sb);
      System.out.println(ob);
  }
}
@ComponentScan:开启组件自动扫描; 默认情况下,它会扫描当前类所在的包及其子包中的所有标签对象加载到Spring容器
  • Spring注解扩展:
    主要有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:
    @Component:可以用于注册所有bean
    @Repository:主要用于注册dao层的bean [@Mapper]
    @Controller:主要用于注册控制层的bean
    @Service:主要用于注册服务层的bean

三、依赖注入(DI)

控制对象与对象的关系,注入——> 设置属性

(一)三种方式:构造器、setter方法、接口注入

例子:打印机
ink 、paper是打印机printer的两个组件
用不同注入方式——都是为了给ink和paper这两个属性赋值

(1)构造器注入

Printer类

public Printer(Ink ink, Paper paper, String brand) {
  this.brand = brand;
  this.ink = ink;
  this.paper = paper;
}

application.xml

<bean id="a4Paper" class="org.lanqiao.print.A4Paper">bean>
<bean id="blackInk" class="org.lanqiao.print.BlackInk">bean>

<bean id="printer" class="org.lanqiao.print.Printer">
  <constructor-arg name="ink" ref="blackInk">constructor-arg>
  <constructor-arg name="paper" ref="a4Paper">constructor-arg>
  <constructor-arg name="brand" value="惠普">constructor-arg>
bean>

构造器注解注入使用注解@Autowired(对非值构造方法使用@Autowired注解)

可能有循环注入问题(类似于死锁)。 抛出BeanCurrentlyInCreationException异常表示循环依赖 → 根本原因:Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态。而构造器是完成实例化的东东,所以构造器的循环依赖无法解决~~~

(2) setter注入

一定要提供setter方法,才可以实现setter注入。

<bean id="printer" class="org.lanqiao.print.Printer">
  <constructor-arg name="brand" value="佳能"/>
  <property name="ink" ref="blackInk"/>
  <property name="paper" ref="a4Paper"/>
bean>

(3)接口注入

接口注入有点复杂,被注入对象如果想要IOC容器为其注入依赖对象,就必须实现某个接口,这个接口提供一个方法,用来为被注入对象注入依赖对象,IOC容器通过接口方法将依赖对象注入到被注入对象中去。相对于前两种注入方式,接口注入比繁琐和死板,被注入对象就必须专声明和实现另外的接口

(二)两种方式:配置文件 and 注解(3)

1. @Autowired注解

Spring提供。 @Autowired(自动注入)修饰符有三个属性:Constructor,byType,byName。默认按照byType注入。 如果默认byType注入,前提条件是只能有一个实现类。
  • constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。
  • byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。需要和@Qualifier注解配合使用。
  • byType:查找所有的set方法,将符合符合参数类型的bean注入。这是默认的注入方式。

2. @Resource注解

Java提供 javax.annotation.Resource这个注解同样可以实现注入功能。 @Resource注解和@Autowired有所区别的是: @AutoWried按byType自动注入,而@Resource默认按byName自动注入。
@Resource(name="a4Paper")
private Paper paper;

3. @Value注解

由于@Autowired、@Qualifier、@Resource三者自动装配只能针对于注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。因此有了@Value这个注解,@Value专门用来服务基本类型和String类型。
注意:@Value无法作用于构造方法。

四、Bean的五种作用域

正常spring框架支持两个:

  • singleton(默认)
    ——单例模式,有且只有一个实列。好处:不会频繁的进行对象创建和销毁,内存压力小。但只能在描述一个固定作用的类且大家都可以用才可以使用(打印机),不通用的对象不可以使用(筷子)。
  • prototype
    ——多例模式,需要频繁创建对象。可以用属性设置。每次请求对象不同

web项目,5个:

  • singleton:使用该属性定义Bean时,IOC容器仅创建一个Bean实例,IOC容器每次返回的是同一个Bean实例。
  • prototype:使用该属性定义Bean时,IOC容器可以创建多个Bean实例,每次返回的都是一个新的实例。
  • request:该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。(一次请求响应,新对象)
  • session:该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。
  • global-session:该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例。

Spring默认的作用域是 :singleton

SSM仅有singleton,所有数据绑在方法的参数里

五、Bean的五种自动装配By配置文件

  1. no
    该模式表示默认情况下,不自动装配,通过“ref”attribute手动设定。
<bean id="printer" class="org.lanqiao.printer.Printer">
    <property name="ink" ref="blackInk"/>
    <property name="paper" ref="b5"/>
bean>
<bean id="colorInk" class="org.lanqiao.printer.ColorInk"/>
<bean id="blackInk" class="org.lanqiao.printer.BlackInk"/>
<bean id="a4" class="org.lanqiao.printer.A4Paper"/>
<bean id="b5" class="org.lanqiao.printer.B5Paper"/>

  1. byName
    @Resource ——byName
    该模式表示根据Property的Name自动装配,如果一个bean的name,和另一个bean中的Property的name相同,则自动装配这个bean到Property中。当一个bean节点带有 autowire byName的属性时,将查找其类中所有的set方法名,获得将set去掉并且首字母小写的字符串,然后去spring容器中寻找是否有此字符串名称id的对象。如果有,就取出注入;如果没有,就报空指针异常。
<bean id="printer" class="org.lanqiao.printer.Printer" autowire="byName"/>
<bean id="colorInk" class="org.lanqiao.printer.ColorInk"/>
<bean id="ink" class="org.lanqiao.printer.BlackInk"/>
<bean id="a4" class="org.lanqiao.printer.A4Paper"/>
<bean id="paper" class="org.lanqiao.printer.B5Paper"/>
  1. byType
    @Autowire —— byType
    该模式表示根据Property的数据类型(Type)自动装配,Spring会总动寻找与属性类型相同的bean,若一个bean的数据类型,兼容另一个bean中Property的数据类型,则自动装配。
    注意:使用byType首先需要保证同一类型的对象,在spring容器中唯一,若不唯一会报不唯一的异常。
<bean id="printer" class="org.lanqiao.printer.Printer" autowire="byType"/>
<bean id="blackInk" class="org.lanqiao.printer.BlackInk"/>
<bean id="b5Paper" class="org.lanqiao.printer.B5Paper"/>
  1. constructor
    使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。即Spring会寻找与参数数据类型相同的bean,通过构造函数将其注入。
<bean id="printer" class="org.lanqiao.printer.Printer" autowire="constructor"/>
<bean id="blackInk" class="org.lanqiao.printer.BlackInk"/>
<bean id="b5" class="org.lanqiao.printer.B5Paper"/>
  1. default
    表示默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的。

XML 配置里的 Bean 自动装配的缺点:
1、在 Bean 配置文件里设置 autowire 属性进行自动装配将会装配 Bean 的所有属性,然而,,若只希望装配个别属性时, autowire 属性就不够灵活了。

2、autowire 属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之。

3、一般情况下,在实际的项目中很少使用自动装配功能,因为和自动装配功能所带来的好处比起来,明确清晰的配置文档更有说服力一些。

你可能感兴趣的:(SSM,后端)