Spring框架入门(二):IOC容器

一、IOC介绍

IOC:Inversion of Control 控制反转

​ spring核心容器也可以称为IOC容器,该容器主要负责管理各种对象,主要包括项目的生命周期(对象的创建、初始化、销毁等)、不同对象之间依赖关系的建立、将不同的对象及对象代码在程序运行期间动态的结合在一起等等。

​ IOC容器并没有实现更多的功能,但它的存在使我们不需要很多代码、不需要考虑对象间复杂的耦合关系就能从IOC容器中获取合适的对象,而且提供了各种对象的可靠的管理,极大地降低了开发的复杂性。

控制反转:是一种编程思想,或者说是一种新的设计模式,由于推出较晚,所以没有纳入GoF23种设计模式当中。

控制反转了什么呢?

  1. 不需要使用硬编码的方式创建对象;
  2. 不需要使用硬编码的方式维护对象之间的关系;

DI:Dependency Injection 依赖注入

​ DI的概念的提出是用来代替IOC的,表示让调用类对某一接口实现类的依赖关系由容器注入,以移除调用类对某一接口实现类的依赖。

依赖注入 这个名词显然比 控制反转 更直接明了,并且易于理解。 它是控制反转的一种实现方式,并不是唯一的方式,例如:依赖查找、服务定位器、容器注入、策略模式和观察者模式等,也可以实现控制反转。

二、IOC容器

​ Spring的IoC(Inversion of Control,控制反转)之所以又称为IoC容器,主要是因为IoC在Spring框架中承担了一个核心的角色——作为容器来管理应用程序中的对象(通常称为Bean)。

​ Spring提供了两种主要的IoC容器实现:BeanFactoryApplicationContext

  1. BeanFactory
    1. 是Spring中最基础的IoC容器接口,提供了IoC服务的基本支持。
    2. 主要用于访问容器中的Bean,但功能相对简单,不提供诸如国际化、事件发布等高级功能。
  2. ApplicationContext
    1. BeanFactory的子接口,提供了更丰富的功能,如国际化、资源访问、事件发布等。
    2. 是Spring框架推荐使用的IOC容器实现,因为它提供了比BeanFactory更全面的服务支持。

核心API

// 根据指定名称返回一个Bean实例
Object getBean(String name)

// 判断名称为name的Bean是否是原型,即是否总是返回一个新实例(非单例)
boolean isPrototype(String name)

// 判断名称为name的Bean是否是单例
boolean isSingleton(String name)
 
// 判断容器中是否包含给定名称的Bean实例
boolean containsBean(String name)

// 如果名称为name的Bean有别名则返回其别名
String[] getAliases(String name)

三、IOC的注入功能

1. set方式注入(必须依靠set方法)

1)基本类型的装配

XML配置

<bean id="student" class="com.it.bean.Student">
    <property name="id" value="001"></property>
    <property name="age">
        <value>23</value>
    </property>
</bean>

java配置类

@Bean
public Student student() {
    Student student = new Student();
    student.setName("name");
    student.setAge(18);
    return student;
}

注意:

  1. id:是Bean的唯一标识,要求在整个配置文件中要唯一,也可使用name属性,bean标签里面的id和name属性都可以用来标识这个配置的对象,但是id会帮我们检查给对象起的名字是否规范(名字不能重复、不能用数字开头、不能有空格等等),如果检查出来了就会报错,而name属性不会检查这些东西;

    XML配置

    <bean name="t1 t2" class="com.it.bean.Teacher"></bean>
    
    <bean id="t1 t2" class="com.it.bean.Teacher"></bean>
    

    java配置类

    @Bean(name = {"stu", "student", "s"})
    public Student student() {
        return new Student();
    }
    
  2. property:对于所有用set方式来注入的必须使用该标签。

  3. value:基本数据类型,都用value(标签/属性)来注入,可以实现自动的数据类型转换。

2)对象类型的装配

  1. 引用对象在当前配置文件中:

    <property name="student">
        <ref local="objName"/>
    </property>
    
  2. 引用对象不在当前配置文件中:

    <property name="student">
        <ref bean="objName"/>
    </property>
    
  3. 使用property的ref属性引用:

    <property name="student" ref="objName"></property>
    

3)集合的装配

  1. List集合:

    <!-- 使用value注入 -->
    <property name="list">
        <list>
            <value>hello1</value>
            <value>hello2</value>
            <value>hello3</value>
        </list>
    </property>
    
    <!-- 使用ref引用spring容器中的其他对象 -->
    <property name="list">
        <list>
            <ref bean="t1"/>
            <ref bean="t2"/>
        </list>
    </property>
    
  2. Set集合:

    <property name="set">
        <set>
            <value>world1</value>
            <value>world2</value>
            <value>world3</value>
        </set>
    </property>
    
  3. Map集合:

    <property name="map">
        <map>
            <entry key="tom1" value="23"></entry>
            <entry key="tom2" value="24"></entry>
            <entry key="tom3" value="25"></entry>
        </map>
    </property>
    
  4. Propertis:

    <property name="prop">
        <props>
            <prop key="a">山西</prop>
            <prop key="b">北京</prop>
            <prop key="c">上海</prop>
        </props>
    </property>
    

2. 构造器注入

配置 <constructor-arg> 元素,在Bean中不用写set方法,但是要有相应的构造器。

1)根据参数类型

<bean name="student1" class="com.it.bean.Student">
    <constructor-arg type="long" value="2020001"></constructor-arg>
    <constructor-arg type="String" value="tom"></constructor-arg>
    <constructor-arg type="int" value="23"></constructor-arg>
</bean>

2)根据参数下标位置

<bean name="student2" class="com.it.bean.Student">
    <constructor-arg index="0" value="2020002"></constructor-arg>
    <constructor-arg index="1" value="lisa"></constructor-arg>
    <constructor-arg index="2" value="24"></constructor-arg>
</bean>

3. 自动注入

注意:自动装配只对【对象类型】起作用,对基本类型不起作用。

1)配置默认装载方式

default-autowire,该属性可以指定值为:default、no、byName、byType、constructor。
在【根元素】beans中加入这个属性,那么下面所有的bean都会使用byName的方式进行自动注入,如果在下面的某一个bean里面想使用其他的方式进行注入,可以用 autowire="***" 属性进行注入。

<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-4.3.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.3.xsd"
           default-autowire="byName">
    
    <bean name="teacher" class="com.it.bean.Teacher">
        <property name="id" value="1001"/>
        <property name="name" value="李四"/>
    </bean>
    
</beans>

2)手动指定装置方式

在bean标签中指定配置方式:

<bean name="teacher" class="com.it.bean.Teacher" autowire="byType">
    <property name="id" value="1001"/>
    <property name="name" value="李四"/>
</bean>

<bean name="teacher" class="com.it.bean.Teacher" autowire="byName">
    <property name="id" value="1001"/>
    <property name="name" value="李四"/>
</bean>

3)继承

配置文件中,一个bean的配置可以继承另一个bean的配置。bean标签中的俩个属性:

  • abstract属性:<bean name=".." class=".." abstract="true">。bean中一旦定义为了 abstract="true",则说明该bean是用来被继承的,不能通过该bean获得对象了。
  • parent属性:<bean name=".." parent="另一个bean的名字">,指定继承哪个bean的配置,配置和配置的继承像java中的类和类直接的继承一样,子类会把父类中的对象继承过来。当然在子配置里面依然是可以覆盖父配置中已经写的配置信息。
<bean name="teacher" class="com.briup.bean.Teacher" abstract="true">
    <property name="student" ref="student"></property>
</bean>

<bean name="t" parent="teacher">
    <property name="id">
        <value>11</value>
    </property>
    <property name="name">
        <value>TeacherWang</value>
    </property>
</bean>

四、Bean对象的生命周期

1. 单例Bean

1)五步

  1. 第一步:实例化Bean;
  2. 第二步:Bean属性赋值;
  3. 第三步:初始化Bean;
  4. 第四步:使用Bean;
  5. 第五步:销毁Bean;

2)七步

  1. 第一步:实例化Bean;
  2. 第二步:Bean属性赋值;
  3. 第三步:执行Bean后置处理器的before方法;
  4. 第四步:初始化Bean;
  5. 第五步:执行Bean后置处理器的after方法;
  6. 第六步:使用Bean;
  7. 第七步:销毁Bean;

3)十步

  1. 第一步:实例化Bean;
  2. 第二步:Bean属性赋值;
  3. 第三步:如果实现了Aware相关接口,则调用这些接口中的方法;
  4. 第四步:执行Bean后置处理器的before方法;
  5. 第五步:如果实现了InitializingBean相关接口,则调用这些接口中的方法;
  6. 第六步:初始化Bean;
  7. 第七步:执行Bean后置处理器的after方法;
  8. 第八步:使用Bean;
  9. 第九步:如果Bean实现了DisposableBean接口,将执行destory方法;
  10. 第十步:销毁Bean;

2. 原型Bean

  1. 第一步:使用这个对象的时候,spring容器会创建这个对象;
  2. 第二步:Bean属性赋值;
  3. 第三步:调用 init-method="..",如果有该配置的话;
  4. 第四步:使用Bean;

五、配置导入

如果我们在spring框架中配置了多个文件,我们可以在读取配置文件的时候把其他文件全都读取,也可以只读一个总的文件,在这个总的文件中把其他的导入进来。

XML配置

<import resource="student.xml"/>

java配置类

@Import(OtherConfig.class)		// @Import用于导入spring扫描不到的java配置类
@ImportResource("classpath:student.xml")	// @ImportResource用于导入XML或groovy文件
@Configuration
public class SpringConfig {
}

六、创建Bean对象的方式

1. 常规方式

xml文件中有bean的配置,而且这个bean所对应的java类中存在一个无参构造器,那么这个时候spring容器就可以使用反射调用无参构造器来创建实例了。

2. 使用工厂类

工厂类交给spring管理,希望通过配置工厂类,可以直接获得工厂类产生的对象,而不是工厂类对象本身,所以需要告诉spring:

  1. 谁是工厂类;
  2. 工厂类中创建对象的方法是哪个

怎么告诉:(3种方式)

  1. 实现FactoryBean接口

    1. 工厂类:

      @Data
      public class ConnectionFactory implements FactoryBean<Connection> {
          private String driver;
          private String url;
          private String username;
          private String password;
      
          @Override
          public Connection getObject() throws Exception {
              Class.forName(driver);
              return DriverManager.getConnection(url, username, password);
          }
      
          @Override
          public boolean isSingleton() {
              return false;
          }
      
          @Override
          public Class<Connection> getObjectType() {
              return Connection.class;
          }
      }
      
    2. 配置类:

      @Bean("connection")
      public ConnectionFactory connectionFactory() {
          ConnectionFactory factory = new ConnectionFactory();
          factory.setUrl("url");
          factory.setDriver("driver");
          factory.setUsername("username");
          factory.setPassword("password");
          return factory;
      }
      
    3. XML配置:

      <bean name="conn" class="com.briup.ioc.factory.ConnectionFactory">
          <property name="driver" value="driver"/>
          <property name="url" value="url"/>
          <property name="username" value="username"/>
          <property name="password" value="password"/>
      </bean>
      
    4. 获取对象:

      // 获取工厂类,工厂类在spring容器中以&开头
      Object factory = SpringUtil.getBean("&connection");
      
      // 获取connection对象,运行时自动执行工厂类的getObject方法
      Object connection = SpringUtil.getBean("connection");
      
  2. 实例工厂:在xml文件中告诉spring容器哪个是工厂类,以及哪个是工厂方法;

    1. 工厂类:

      @Data
      public class ConnectionFactory {
          private String driver;
          private String url;
          private String username;
          private String password;
      
          public Object getConnection() throws Exception {
              Class.forName(driver);
              return DriverManager.getConnection(url,username,password);
          }
      }
      
    2. 配置类:

      @Bean("connection")
      public Connection connectionFactory() throws Exception {
          ConnectionFactory factory = new ConnectionFactory();
          factory.setUrl("url");
          factory.setDriver("driver");
          factory.setUsername("username");
          factory.setPassword("password");
          // 这里调用工厂方法
          return factory.getConnection();
      }
      
    3. XML配置:

      <bean name="factory" class="com.bean.ConnectionFactory">
          <property name="driver" value="driver"/>
          <property name="url" value="url"/>
          <property name="username" value="username"/>
          <property name="password" value="password"/>
      </bean>
      
      <bean name="connection" factory-bean="factory" factory-method="getConnection"/>
      
  3. 静态工厂

    1. 工厂类:

      @Data
      public class ConnectionFactory {
          private static String driver = "driver";
          private static String url = "url";
          private static String username = "username";
          private static String password = "password";
      
          public static Connection getConnection() throws Exception {
              return new MockConnection();
          }
      }
      
    2. 配置类:

      @Bean("connection")
      public Connection connectionFactory() throws Exception {
          return ConnectionFactory.getConnection();
      }
      
    3. XML配置:

      <bean name="connection" 
            class="com.bean.ConnectionFactory" factory-method="getConnection"/>
      

七、IOC中的常用注解

1. 组件定义注解

  • @Component:这是一个通用的组件注解,用于将类标记为Spring容器中的一个组件。它可以被@Controller、@Service、@Repository等注解所替代,这些注解提供了更具体的用途描述。
  • @Controller:用于标注在MVC架构中的控制器类上。
  • @Service:通常用于标注服务层组件。
  • @Repository:用于标注数据访问层组件,主要用于访问数据库。

2. 自动装配注解

  • @Autowired:自动装配Bean,可以作用于构造器、字段或setter方法上。默认情况下,按照类型装配,如果需要按名称装配,可以结合@Qualifier注解使用。
  • @Qualifier:与@Autowired配合使用,指定要注入Bean的名称。
  • @Resource:与@Autowired类似,但@Resource默认按照名称装配,如果找不到名称匹配的Bean,则按照类型装配。
  • @Inject:是JSR-250规范提供的注解,功能与@Autowired相似,但它是Java标准的一部分,而不是Spring特有的。

3. 配置类注解

  • @Configuration:表明该类是一个配置类,用于定义Bean。配置类中的@Bean注解方法将返回的对象注册为Spring容器中的Bean。
  • @Bean:用于在配置类中声明一个Bean,并指定其初始化方法。可以通过name属性指定Bean的名称。

4. 条件注解

  • @Conditional:根据指定的条件决定是否创建Bean。这通常与实现了Condition接口的类一起使用,以提供具体的条件逻辑。

5. 扫描注解

  • @ComponentScan:用于指定Spring在初始化时需要扫描的包路径,以便发现注解了@Component、@Service、@Repository、@Controller等注解的类,并将它们注册为Bean。可以通过excludeFilters和includeFilters属性来定义扫描的过滤规则。

6. 作用域注解

  • @Scope:用于指定Bean的作用域。常见的值有"singleton"(单例,默认值)、“prototype”(原型,每次请求都会创建一个新的Bean实例)、“request”(Web环境中,每个HTTP请求都会创建一个新的Bean实例)、“session”(Web环境中,每个HTTP会话都会创建一个新的Bean实例)等。

7. 懒加载注解

  • @Lazy:用于指定Bean的懒加载模式。默认情况下,Spring容器在启动时就会创建并初始化所有单例Bean。使用@Lazy注解后,Bean将在第一次被使用时才进行创建和初始化。

8. 生命周期注解

  • @PostConstruct:用于标注在方法上,表示该方法是在Bean的依赖注入完成后执行的初始化方法。
  • @PreDestroy:用于标注在方法上,表示该方法是在Bean销毁之前执行的清理方法。

9. 导入注解

  • @Import:用于快速导入其他配置类、组件或ImportSelector/ImportBeanDefinitionRegistrar实现,以便将它们注册到Spring容器中。

八、自定义属性编辑器

Spring中我们可以使用属性编辑器来将特定的字符串转换为对象:String --> 转换 --> object

java.beans.PropertyEditor(JDK中的接口)用于将xml文件中字符串转换为特定的类型,同时JDK为我们提供一个实现类 java.beans.PropertyEditorSupport,将来我们自定义的属性编辑器类可以继承该实现类。

Spring在注入时,如果遇到类型不一致(例如需要Address类型,但是用户传了个String),则会去调用相应的属性编辑器进行转换。

Spring会调用属性编辑器的 setAsText(String str) 进行处理用户传的字符串,并调用 getValue() 方法获取处理后得到的对象。

注意:在代码中处理完后需要调用 setValue 方法,要不然spring调用 getValue 方法拿不到处理后转换成的对象。

下面是一个示例:

  1. Address类:

    @Data
    @AllArgsConstructor
    public class Address {
        private String city;
        private String province;
        private String country;
    }
    
  2. Student类:

    @Data
    public class Student {
        private String name;
        private int age;
        private Address address;
    }
    
  3. PropertyEditorSupport类:

    public class AddressEditor extends PropertyEditorSupport {
    
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            String[] str = text.split(",");
            String city = str[0];
            String province = str[1];
            String country = str[2];
    
            Address add = new Address(city, province, country);
    
            setValue(add);
        }
    }
    
  4. XML配置:

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="com.bean.Address"
                       value="com.bean.editor.AddressEditor"/>
            </map>
        </property>
    </bean>
    
  5. 配置类:

    @Bean
    public CustomEditorConfigurer editorConfigurer() {
        CustomEditorConfigurer configurer = new CustomEditorConfigurer();
    
        Map<Class<?>, Class<? extends PropertyEditor>> editors = new HashMap<>();
        editors.put(Address.class, AddressEditor.class);
    
        configurer.setCustomEditors(editors);
        return configurer;
    }
    
  6. 这样Address即可支持字符串的赋值:

    <bean id="student" class="com.bean.Student">
        <property name="address">
            <value>changzhi,shanxi,China</value>
        </property>
    </bean>
    

注意:按照JavaBeans的规范,JavaBeans的基础设施会在JavaBean相同类包下查找是否存在 JavaBeanEditor 的类,如果存在,自动使用 JavaBeanEditor 作为该JavaBean的PropertyEditor。

如:UserEditor会自动成为User对应的PropertyEditor。Spring也支持这个规范,也即如果采用这种规约命令PropertyEditor,就无须显式在CustomEditorConfigurer中配置了,Spring将自动查找并注册这PropertyEditor。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风於尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值