Spring-IOC-DI

2. Spring6

2.1 工程的体系结构

  1. 单体架构
  2. 所有模块和业务写在一个工程中,其中MVC为单体应用的主要架构。
  3. 分布式架构
  4. 将多个业务模块分布在不同的机器上,每一个模块独立为一个工程。

2.2 Spring IOC

​ 在IOC容器理念实行之前,我们需要通过new一个对象来进行对对象内部功能的调用或使用例如(new Object()),此时我们就需要关注对象如何去创建,假设我们在没有这个对象的情况下,去运行这段代码会在编译期给出错误提示,例如List list = new ArrayList<>(); 假定ArrayList对象不存在,那么编译期就会报错。

​ 在IOC容器理念实行之后,我们只需要将对象的信息做简单配置放在IOC容器中,需要时从IOC从其中取出对象实例即可,我们不再需要关注对象是否真的存在以及创建对象的细节,至少在编译期间不会出现错误。(Class.forName(“com.sean.Main”))这段代码实际上就可以说明刚才的问题,我们不必关心com.sean.Main这一个类是否存在,我们只需要知道现在需要这样一个类即可。

  1. 使用IOC容器并不是只引入一个Spring的jar包就可以了,框架=配置文件+jar包,我们需要对IOC进行配置,将对象注入到IOC容器中,这样在程序运行期间才会通过容器找到对应的实例对象。
  2. 配置文件的读取方式
    1. ClassPathXmlApplicationContext: 配置文件在类路径下classpath:

    2. **FileSystemXmlApplicationContext: ** 配置文件在系统的其它文件下,显然这一个是极少情况下才会用到的

    3. **AnnotationConfigApplicationContext: **不需要解析配置文件,通过配置类以及注解的方式初始化IOC容器

    4. **WebApplicationContext:**针对于web应用,web环境创建IOC容器对象,将对象实例存入到ServletContext域中

    5. 容器

      1. **BeanFactory:**规定IOC容器的基本动作
      2. **ApplicationContext:**对BeanFactory做扩展,AOP/WEB

2.3 DI

依赖注入: 依赖注入在IOC容器中发生,管理Object与Object之间的关系

2.4 IOC与DI的配置与使用

2.4.1 XML方式

创建spring.xml文件,引入xml文件头。声明一个bean为entityComponent标识名在当前IOC空间中唯一。

<?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">
		<beans>
        <bean id="entityComponent" class="com.sean.entity.EntityComponent"> </bean>
    </beans>
</beans>

使用ClassPathXMLApplicationContext获取到对应的Bean

/**
 * Hello world!
 * @author sean
 */
public class App {
    public static void main( String[] args ) {
        ClassPathXmlApplicationContext cpa =
                new ClassPathXmlApplicationContext("spring-ioc.xml");
        EntityComponent entityComponent = cpa.getBean("entityComponent", EntityComponent.class);
        entityComponent.doWork();
    }
}
// entity component do work...
2.4.1.1 静态工厂返回实例
<bean id="staticClient1" class="com.sean.entity.StaticClient" factory-method="getInstance"> </bean>
2.4.1.2 实例之间的依赖注入
<bean id="user1" class="com.sean.entity.User">
  <constructor-arg value="jock" />
  <constructor-arg value="123456" />
  <constructor-arg value="hb hd" />
</bean>

<bean id="user2" class="com.sean.entity.User">
  <constructor-arg name="uname" value="jock" />
  <constructor-arg name="password" value="123456" />
  <constructor-arg name="address" value="hb hd" />
</bean>

<bean id="user3" class="com.sean.entity.User">
  <constructor-arg index="0" value="jock" />
  <constructor-arg index="1" value="123456" />
  <constructor-arg index="2" value="hb hd" />
</bean>
2.4.1.3 set注入:
<!-- ref属性为引用类型  value属性为基本类型 -->
<bean id="user4" class="com.sean.entity.User">
  <property name="password" value="123456"/>
  <property name="uname" value="jock"/>
  <property name="address" value="hb hd"/>
</bean>
2.4.1.4 周期方法

一个对象实例的创建和销毁时需要做的事情,分别为init-method和destory-method属性,分别设置当前实例被创建和被销毁时所需要做的事情,这里创建指的是IOC容器被创建后创建对象,销毁指的是IOC容器销毁时销毁对象,ClassPathXMLApplicationContext.close();

<bean id="cycle" class="com.sean.entity.CycleMethod" init-method="init" destroy-method="destroy"/>
public class CycleMethod {
    private List<String> list;
    public void init() {
        list = new ArrayList<>(Arrays.asList("jock", "marry", "milan"));
    }
    public void service() {
        list.removeIf((o1)-> o1.charAt(0) == 'm');
    }
    public void destroy() {
        System.out.println("列表中还存在" + list.size() + "个人。");
    }
}
2.4.1.5 组件作用域

bean标签声明时,只是将Bean配置给了IOC容器。在IOC容器中,bean标签将转换成spring内部的BeanDefinition对象,BeanDefinition对象内包含定义的id和class等属性信息。Spring可以反射出多个IOC中的实例,具体创建多少个实例对象,由Bean的作用域Scope属性指定。

取值范围说明
singleton默认单例
prototype多例
request实例存入到Request域中
session实例存入到Session域中
global session在Portlet环境,如果没有Porlet环境那么globalSession相当于session
<bean id="scopeComponent" class="com.sean.entity.ScopeComponent" scope="prototype"/>
2.4.1.6 自定义bean工厂模式

当我们需要创建一个非常复杂的对象时,例如MyBatis中的SqlSession对象。我们得到SqlSession对象前需要做一系列的事情的时候,就可以使用Spring的工厂模式来创建。

  1. 定义想要获取的对象,例JavaBean
  2. 创建JavaBeanFactoryBean用于获取JavaBean实例,需要实现FactoryBean<>
  3. 重写 getObject getObjectType 以及 isSingleton方法,获取对象、获取对象类型、是否为单例创建
<!-- 注入一个factoryBean Spring会自动执行getObject方法获取到想要的实例对象 -->
<bean id="beanFactory" class="com.sean.entity.JavaBeanFactoryBean">
	<property name="value"  value="Jock"/>
</bean>
import org.springframework.beans.factory.FactoryBean;
public class JavaBeanFactoryBean implements FactoryBean<JavaBean> {
  
  	private String value;
  
  	public void setValue(String value) {
      	this.value = value;
    }
  
    @Override
    public JavaBean getObject() throws Exception {
        return new JavaBean(this.value);
    }

    @Override
    public Class<?> getObjectType() {
        return JavaBean.class;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}
2.4.1.7 搭建Dao持久层

持久层中需要操作MySQL数据库,这里引入Spring-JDBC.jar包,用来对MySQL进行CRUD操作,但是在使用时需要使用数据源DataSource,才可以操作数据库。这里使用SpringIOC的概念来进行创建JdbcTemplate。

sean.username=root
sean.password=root
sean.url=jdbc:mysql://localhost:3306/studb?useSSL=false&serverTimezone
							=UTC&useUnicode=true&characterEncoding=utf-8
sean.driver=com.mysql.cj.jdbc.Driver
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="${sean.username}"/>
    <property name="password" value="${sean.password}"/>
    <property name="url" value="${sean.url}"/>
    <property name="driverClassName" value="${sean.driver}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>
2.4.1.8 搭建基础三层架构基于IOC概念

主要列出 spring-ioc.xml的配置文件以及类的结构搭建

// 正常的三层架构  controller(StudentService -> StudentServiceImpl) -> service(StudentDao -> StudentDaoImpl) -> dao -> JdbcTemplate
@Test
public void getStudentsTest() {
  ClassPathXmlApplicationContext cpa =
    new ClassPathXmlApplicationContext("spring-ioc.xml");
  StudentController sc = cpa.getBean("studentController", StudentController.class);
  sc.getStudents().forEach(System.out::println);
}
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <property name="username" value="${sean.username}"/>
  <property name="password" value="${sean.password}"/>
  <property name="url" value="${sean.url}"/>
  <property name="driverClassName" value="${sean.driver}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="studentDao" class="com.sean.dao.impl.StudentDaoImpl">
  <property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>

<bean id="studentService" class="com.sean.service.impl.StudentServiceImpl">
  <property name="studentDao" ref="studentDao"/>
</bean>

<bean id="studentController" class="com.sean.controller.StudentController">
  <property name="studentService" ref="studentService"/>
</bean>

2.4.2 XML 和 Annotation方式

XML方式配置IOC容器中的Bean,可以看出比较繁琐,发现比之前需要写的代码还要多,虽然它接了耦合。Annotation方式来使用IOC容器就无需在xml文件中写大量的Bean配置了。

  1. @Component: 将当前Bean加入到IOC容器中
    1. Controller : 被Component注解,功能一致,中不过是区别了身份
    2. Service
    3. Repositry
2.4.2.1 如何使用Annotation

使用@Controller注解测试 是否可以在IOC容器中取到 ShopController的实例对象. 在spring-ioc.xml配置文件中使用(context:component-scan标签引入需要解释的包), Spring会通过扫描包的方式去实例化添加了@Component注解的类

  1. 扫描指定包下的所有子包中得文件
  2. 扫描指定包下除了排除被 (type)类型的(package)外, 进行类的实例化
  3. 扫描指定包下(type)类型的(package)进行类的实例化
/**
 * @author sean
 * @version 1.0
 * @date 2024-4-15 10:14
 */
@Controller
public class ShopController {}
<context:component-scan base-package="com.sean.shop" />
<context:component-scan base-package="com.sean.shop">
  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<context:component-scan base-package="com.sean.shop">
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
2.4.2.2 init clear scope 的使用

在XML方式时通过Bean标签中的 init-method 和 destroy-method 和 scope 属性规定. 在注解中分别使用 @PostConstruct @PreDestroy @Scope. 当设置当前类为多例时, 则不会触发销毁方法

// 单例  多例
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Controller(value = "shopController")
public class ShopController {

  	// 实例创建前
    @PostConstruct
    public void init() {
        System.out.println("init");
    }
  	// 实例销毁时
    @PreDestroy
    public void destory() {
        System.out.println("destory");
    }
}
2.4.2.3 DI注入

使用@Autowired @Qualifier @Resource 进行自动装配, 推荐使用@Autowired注解进行DI装配.

  1. @Autowired注解通常在IOC容器中只有一个与之匹配的时候使用, 通常项目中只有一个与之匹配的. 如果出现多个类型超过一个则会报错UnsatisfiedDependencyException, 预期一个出现n个与之匹配的. @Autowired(required = false) 设置required=false虽然不会在IOC容器中报错,但是后期使用会出现NullPointerException异常.
  2. @Qualifier 注解通常在IOC容器中出现多个与之匹配的时候使用. 例如ShopService的子类中有n个, 那么在装配ShopService时就可以使用Qualifier注解, 需要配合Aurowired注解使用, @Autowired @Qualifier(value = “shopServiceImpl”)
  3. @Resource
  4. @Value 基本类型注入: @Value(“${sean.username:admin}”) 需要在spring-ioc文件中引入配置文件 , 当读取不到时使用冒号后面的值
2.4.2.4 搭建基础三层架构

将原来写在XML中的Bean配置,用Annotation代替即可.

@Test
public void getShopControllerTest() {
    ClassPathXmlApplicationContext aca = new ClassPathXmlApplicationContext("spring-ioc.xml");
    ShopController shopController = aca.getBean("shopController", ShopController.class);
    shopController.findAll().forEach(System.out::println);
    aca.close();
}
// controller -> service -> dao(JdbcTemplate)
// @Controller  @Service  @Repository @Autowired   jdbcTemplate使用xml配置的方式放入到IOC容器中
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="${sean.username}"/>
    <property name="password" value="${sean.password}"/>
    <property name="url" value="${sean.url}"/>
    <property name="driverClassName" value="${sean.driver}"/>
</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>

Annotation方式

新增配置类, 使得编程人员完全脱离XML配置文件.

  1. ComponentScan: 扫描那个包包下的文件
  2. PropertySource: 加载properties配置文件
  3. Import: 书写分支配置类
  4. Configuration: 表示当前是一个配置类
package com.sean.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

/**
 * @author sean
 * @version 1.0
 * @date 2024-4-15 11:59
 */
@ComponentScan(value = "com.sean")
@PropertySource(value = "classpath:jdbc.properties")
@Import(value = JdbcConfig.class)
@Configuration
public class ProjectConfiguration {

}
package com.sean.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/**
 * @author sean
 * @version 1.0
 * @date 2024-4-15 12:06
 */
public class JdbcConfig {
    @Value("${sean.username}")
    private String username;

    @Value("${sean.password}")
    private String password;

    @Value("${sean.url}")
    private String url;

    @Value("${sean.driver}")
    private String driver;

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

    @Bean
    public JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
}
// 通过注解的方式加载配置类,  IOC和DI都是用注解去完成即可  上一种方式上jdbcTemplate在配置类中声明配置即可
@Test
public void findAllTest() {
  ApplicationContext ac = new AnnotationConfigApplicationContext(ProjectConfiguration.class);
  StudentController studentController = ac.getBean("studentController", StudentController.class);
  studentController.findAll().forEach(System.out::println);
}

🔴 BeanFactory 和 FactoryBean 的区别

BeanFactory: 是Spring框架的基础,作为一个顶级接口定义了IOC容器的基本行为,例如管理Bean的生命周期,排至文件的加载和解析,bean的装配和依赖注入等。BeanFactory接口提供了访问Bean的方式,例如getBean()方法获取指定的bean实例,可以从不同的来源获取Bean定义,将其转换为bean实例。BeanFactory还包含了多个子类,例如(Application接口)提供了额外强大的功能。

FactoryBean: 是Spring中一种特殊的bean,可以在getObject()工厂方法中自定义bean的创建过程,是一种能够生产其他bean的bean。FactoryBean在容器启动时被创建,而在实际使用时则是通过调用getObject()方法来创建所需要生产的bean。因此FactoryBean可以自定义任何所需要的初始化逻辑,从而生产特定的Bean实例。

  • 43
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值