Spring IoC

Spring 概述

Spring 框架是一个轻量级的开源应用框架,为Java平台提供了全面的基础设施支持。

Spring 的出现主要是为了解决 JavaEE 项目开发的繁琐,它在简化企业级应用开发的同时,也提高了代码的可测试性和可维护性

Spring 框架的核心特性包括 依赖注入 IoC/DI (Dependency Injection)和 面向切面编程 AOP(Aspect-Oriented Programming)

  • Spring 功能
    • 可以将开发过程中使用到的 组件对象 放入到 Spring容器中,以便实现单例效果(一个类只有一个实例对象)
    • Spring 可以对容器中的 bean对象进行增强,实现 AOP面向切面编程的效果

注解总结

注解名称作用
@Configuration声明这个类是一个配置类
@Bean在配置类中标注的方法会被 Spring 扫描、运行
@Qulifier需要取出同一个类型的多个实例对象
@ComponentScan设定要扫描的包目录
@Autowired

IoC/DI

Spring 的核心特性之一是 IoC/DI,翻译过来就是 控制反转依赖注入这是同一个功能的两种不同角度的称呼

Spring 通过 依赖注入容器 管理组件之间的依赖关系,可以通过 配置文件 或者 注解 来实现,从而实现 松耦合 的组件设计。

  • 控制反转:控制 指的是 开发人员对于某个对象的创建控制权,反转 指的是 将这个权利由开发人员转交给 Spring 框架
  • 依赖注入:Spring 会自动维护对象之间的依赖关系,使用这个对象时,我们只需要从 Spring 中获取即可

关于 IoC/DI ,底层原理就是: Spring 内部有一个容器(map),Spring 帮助开发人员去创建好实例对象(IoC),并且把这些对象放入到 Spring 容器中,后续使用的时候,直接从容器中取出来即可(DI)

为什么使用Spring,使用容器来维护?
降低服务器的开销成本,不需要创建多个对象,只需要交给容器去维护
便于维护对象和对象之间的依赖关系
减少耦合性

Spring 容器 在 spring 框架中是由 ApplicationContext 来充当的

实例化Bean

创建对象

构造函数

虽然 Spring 会借助于反射来创建 Bean对象,其实本质上来说依然是要借助于构造函数,绝大数情况下使用的是无参构造函数

静态工厂及实例工厂

在 xml文件中 使用静态工厂、实例工厂创建实例化Bean对象的方式了解即可,后续会使用注解、配置类的方法

FactoryBean

这个基于 FactoryBean 的方法需要重点关注

  • 某个类实现了 FactoryBean 接口,那么直接利用其编号取出来的 并不是该对象本身,而是 getObject()返回值的结果
    比如:
  • 配置:
<bean id="userfb" class="com.cskaoyan.th58.factorBean.UserFactorBean"/>
  • 代码:
public class UserFactorBean implements FactoryBean<User> {  
    @Override  
    public User getObject() throws Exception {  
        return new User();  
    }  
  
    @Override  
    public Class<?> getObjectType() {  
        return User.class;  
    }  
}
  • 单元测试:
@Test  
public void test2(){  
    ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");  
    // 用FactoruBean方法取出来的类型是getBean()方法的返回值 --- User   
    User userfb = (User) context.getBean("userfb");  
}

组件注册方式

放入Spring容器

xml文件

了解即可

  1. 在 pom.xml 文件导入依赖
<dependencies>  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-context</artifactId>  
        <version>5.3.30</version>  
    </dependency>  
    <dependency>        <groupId>junit</groupId>  
        <artifactId>junit</artifactId>  
        <version>4.13.2</version>  
        <scope>test</scope>  
    </dependency>  
</dependencies>
  1. 在 resources 目录下新建 app.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"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
    
    <!--放入Spring容器中的对象称之为 bean,id指的是放入容器中的对象编号,class是其全限定类名-->  
    <bean id="userService" class="com.cskaoyan.th58.service.UserServiceImpl"/>  
    <bean id="userMapper" class="com.cskaoyan.th58.mapper.UserMapperImpl"/>  
  
</beans>
  1. 编写单元测试用例
@Test  
public void test1(){  
    // 使用Spring容器去管理对象,前提是需要有一个Spring容器  
    // Spring容器在 spring 框架中是由 ApplicationContext 来充当的,他是一个接口,具体的子类实现我们选择的是读取xml来实例化容器  
    // 我们要输入一个位于 classpath目录下 xml文件的信息  
    ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");  
  
    // Spring会帮助我们去创建对应的实例对象,并且放入到Spring容器中  
  
    // 只需要利用容器的方法来获取位于容器中的组件对象即可  
    // key值是什么呢,就是刚刚注册的时候设置的id编号,编号要求是唯一的  
    UserService userService = (UserService) context.getBean("userService");  
    UserMapper userMapper = (UserMapper) context.getBean("userMapper");  
    System.out.println(userService);  
    System.out.println(userMapper);  
}

配置类+@Bean注解

操作步骤

  1. 新建一个config包,新建一个配置类,标注 @Configuration 注解,声明其是一个 配置类

  2. 在配置类中去编写一个一个的方法,方法的要求如下:

    • 编写一个方法,方法的修饰符要求是 public
    • 方法的返回值类型 便是注册到 Spring 容器中的组件类型,一般建议 使用父接口类型来接收
    • 方法的名称 便是注册到 Spring 容器中的组件的编号
    • 如果需要注入依赖,那么使用@Bean注解的话非常简单:直接在方法的形参中编写你需要从容器中取出来的组件的类型,Spring容器便会自动从容器中取出对应的类型的组件
    • 如果同一个类型的实例对象有多个的话,可以使用 @Qualifier 注解,注解中写明编号即可

原理:凡是配置类中标注了 @Bean 注解的方法,那么 Spring 会依次去扫描,依次去运行,得到一个实例对象,把该实例对象放入到 Spring 容器中。

  • 如果不需要维护对象和对象之间的依赖关系,那么下面的写法就ok了:
@Configuration  
public class SpringConfig {//你希望向 Spring容器 中去注册哪个组件,那么便编写哪个对象的创建语句  
    @Bean  
    public UserService userService(){  
        UserServiceImpl userService = new UserServiceImpl();  
        //userService.setUserMapper();  
        return userService;  
    }@Bean  
    public OrderService orderService(){  
        OrderServiceImpl orderService = new OrderServiceImpl();  
       // orderService.setUserMapper();  
        return orderService;  
    }@Bean  
    public UserMapper userMapper(){  
        UserMapper userMapper = new UserMapperImpl();  
        return userMapper;  
    }  
}
  • 但是如果需要维护对象和对象之间的关系:
//声明其是一个配置类  
@Configuration  
public class SpringConfig {//你希望向 Spring容器中去注册哪个组件,那么便编写哪个对象的创建语句  
    @Bean  
    public UserService userService(UserMapper userMapper){  
        UserServiceImpl userService = new UserServiceImpl();  
        //service实现类需要提供set方法即可  
        userService.setUserMapper(userMapper);  
        return userService;  
    }//Spring 处理过程:
    //1.根据方法的返回值类型,得知最终注入到 Spring容器中的是 OrderService类型的对象  
    //2.方法的名称叫做 orderService,所以注册到 Spring容器中的对象的编号为 orderService  
    //3.方法的形参列表有一个叫做 UserMapper,所以 Spring会扫描容器,从容器中取出一个 UserMapper实例对象,在调用当前方法时传递进来
    @Bean  
    // 如果某个类型的实例对象有多个的话,并且希望指定从容器中获取对应的实例对象,可以使用 @Qualifier 注解
    public OrderService orderService(@Qualifier("userMapperImpl2") UserMapper userMapper){  
        OrderServiceImpl orderService = new OrderServiceImpl();  
        orderService.setUserMapper(userMapper);  
        return orderService;  
    }@Bean  
    public UserMapper userMapper(){  
        UserMapper userMapper = new UserMapperImpl();  
        return userMapper;  
    }  
}

使用场景
主要用在整合第三方框架时,把第三方框架里面的类库放入到spring容器中

配置类+@ComponentScan

操作步骤:

  1. 编写一个配置类,标注 @Configuration 注解,声明这是一个配置类

  2. 配置类还需要标注一个注解 @ComponentScan(),设定 扫描的包目录

  3. 业务代码,如果希望放入到 Spring容器 中,按照三层架构划分:@Controller@Service@Reposiroty,如果不是这三层架构的就添加 @Component

  4. 如果同一类型的组件对象有多个,希望可以获取特定某个对象,可以使用 @Qualifier 注解

  5. 如果需要维护对象之间的关系需要添加 @Autowired 注解

  • 配置
// 我们还需要将那些需要放入到 Spring容器中的类头上添加 @Component 注解  
// @Component 与 @Controller  @Service  @Repository 等价,对应三层架构中的三个组件  
@Configuration       // 声明这是一个配置类  
@ComponentScan("com.cskaoyan.th58")        // 设定扫描的包目录(可以通过递归的方式扫描到所有的类)  
public class SpringConfig {  
}
  • 编写代码
@Service  
public class OrderServiceImpl implements OrderService {  
  
    @Autowired  // 表明需要从容器中取出符合类型的数据对象  
    @Qualifier("userMapperImpl2")  
    UserMapper userMapper;
}


@Service  
public class UserServiceImpl implements UserService {  
  
    @Autowired  
    @Qualifier("userMapperImpl")  
    UserMapper userMapper;
}
  • 单元测试
public class SpringTest {  
  
    @Test  
    public void test1(){  
        // 首先进行容器的实例化  
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);  
        Object userServiceImpl = context.getBean("userServiceImpl");  
        Object orderServiceImpl = context.getBean("orderServiceImpl");  
        Object userMapperImpl = context.getBean("userMapperImpl");  
        Object userMapperImpl2 = context.getBean("userMapperImpl2");  
        System.out.println(userServiceImpl);  
        System.out.println(orderServiceImpl);  
        System.out.println(userMapperImpl);  
        System.out.println(userMapperImpl2);  
    }  
}

使用场景
项目中编写的业务代码,使用的就是这种方式

![[两种组件注册方式比对.png|800]]

组件获取方式

从Spring容器获取

getBean 方法

利用容器的 getBean方法,可以从容器中获取指定类型的实例对象

public class SpringTest {  
    @Test  
    public void test1() {  
        // 首先进行容器的实例化  
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);  
        Object userServiceImpl = context.getBean("userServiceImpl");  
        Object orderServiceImpl = context.getBean("orderServiceImpl");  
        Object userMapperImpl = context.getBean("userMapperImpl");  
        Object userMapperImpl2 = context.getBean("userMapperImpl2");  
        System.out.println(userServiceImpl);  
        System.out.println(orderServiceImpl);  
        System.out.println(userMapperImpl);  
        System.out.println(userMapperImpl2);  
}  }

关于 容器,我们之前提及的 ApplicationContext,是一个接口,但是他不是最顶层接口,向上还有一个 BeanFactory接口,那么该接口其实也是容器的实现

ApplicationContext 继承自 BeanFactory,BeanFactory 其实就是最初的容器实现,ApplicationContext继承自他,功能都具有,又做了一些扩展。

![[容器继承关系.png]]

FactoryBean 和 BeanFactory 没有关系
FactoryBean 强调的是 Bean,是一种创建对象的方式
BeanFactory 强调的是 Factory,指的是容器

注解

使用注解来获取容器中的组件对象

最常用的注解是 @Autowired 注解,背后的原理就是 Spring 会帮助我们在背后调用 getBean 方法,将获取到的组件对象注入到当前的引用类型变量中

除此之外还可以使用 @Resource 注解,不过需要导入一个 javax.annotation-api 依赖,这是一个 JDK内嵌的注解

Spring 整合 Junit

如果我们希望再进行 单元测试用例 的时候,可以直接使用注解的方式 来获取指定的实例对象,那么可以这么配置(也交给Spring去管理):

  1. 导入 spring-test 依赖
  2. 单元测试类需要添加 @RunWith() 注解,表示会读取 Spring 的配置信息,用于整合 junit 所必须的步骤
  3. 添加 @ContextConfiguration 注解,用于实例化容器
  4. 直接使用 @Autowired 注解来获取指定类型的实例对象即可
// Spring 整合 junit 的环境所必须要处理的步骤  
@RunWith(SpringJUnit4ClassRunner.class)  
// 用于进行读取配置类或者读取xml配置文件的,用于实例化容器  
@ContextConfiguration(classes = SpringConfig.class)  
public class SpringTest2 {  
  
    // 如果希望在某个类中可以直接通过Autowired来获取容器中的对象,那么当前类对象也必须要交给Spring管理  
    @Autowired  
    UserService userService;  
  
    @Test  
    public void test1(){  
        System.out.println(userService);  
}  }

组件的生命周期

组件的生命周期,就是指的是 组件从诞生到最终销毁的整个阶段。在特定的阶段,Spring容器会调用组件相对应的方法。

过程如下
![[组件的生命周期.png]]

  1. 容器启动
ApplicationContext context = new ClassPathXmlApplicationContext(xml);

ApplicationContext context = new AnnotationConfigApplicationContext(配置类.class);
  1. 实例化Bean对象

    主要借助于反射来进行对象的实例化
    反射需要的信息可以从 xml 配置文件中获取,也可以从配置类中获取

    IoC 是在这一步体现的

  2. 设置对象的属性值(赋值操作)

    这一步实际是在维护对象之间的依赖关系
    如果需要注入的对象没有被实例化,会先将该对象进行实例化

    DI 主要是在这一步体现的

  3. 根据当前对象是否实现 aware接口,对bean对象进行类型转换,去调用接口对应的 set方法

    最大的意义是可以将容器的引用传递给当前对象,可以很方便的操作容器

  4. BeanPostProcessor 对 bean 对象进行处理

    如果需要这个功能,我们需要创建一个 BeanPostProcessor 进行前置处理

// 前置处理  
@Override  
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
    System.out.println(bean + "=======" + beanName);  
    // 很重要 这个是AOP的基石  
    return bean;  
}  
  1. init 阶段

    类似于 Servlet 的 init 方法

    会根据当前的 bean对象 是否实现了 InitializingBean接口,调用对应的方法,或者实现了自定义的 init 方法,调用对应的自定义 init方法,调用对应的自定义 init方法,可以去做一些初始化的业务逻辑

// 实现 InitializingBean 接口  
@Override  
public void afterPropertiesSet() throws Exception {  
    System.out.println("Initialiizing Bean...初始化的业务逻辑");  
}

// 除了上面还有另外一种自定义 init方法,可以不用实现 InitializingBean 接口  
// 1.导入 javax.annotation-api 依赖  
// 2.添加注解 @PostConstruct
@PostConstruct  
public void myinit() throws Exception {  
    System.out.println("Myinit");  
}
  1. 后置处理,放入容器

    init 阶段完成之后,再次经过 beanPostsProcessor后置 处理,处理过后便放入到 Spring容器 中,便是 可使用的状态 了。

// 后置处理  
@Override  
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
    return bean;  
}
  1. 调用销毁方法

    最后根据是否实现 DisposableBean 接口,决定是否调用 destroy方法 (一般框架会用该方法来完成一些善后、销毁工作,比如释放资源
    或者根据当前 Bean 是否实现 自定义destroy方法,决定是否调用 自定义destroy方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值