天天看点

Spring使用——IOC篇一、什么是Spring二、IOC

一、什么是Spring

Spring是一个IOC和AOP框架容器

具体描述:

  • 轻量级:

    轻量级体现在Spring是非侵入式的,基于Spring开发的项目不一定要依赖于Spring的API

  • 依赖注入
  • 面向切面编程
  • 容器:

    Spring管理所有对象的生命周期及其依赖的主入

  • 框架:

    Spring通过使用一些简单的组件组合成一个复杂的应用(通过注解和配置文件组合这些对象)

  • 一站式

    Spring支持兼容其他优秀的第三方框架,并且自身也提供了一整套框架

Spring特性

  • 通过IOC实现了类之间的解耦、简化了开发
  • 通过AOP对各个业务逻辑与非业务逻辑代码进行分离解耦
  • 自带了测试功能模块方便测试
  • 兼容其他优秀框架
  • 通过引入事务模块就可以很方便实现事务的支持和操作

核心Jar包

  • Spring-beans-X.X.X.RELEASE.jar:SpringIOC功能模块
  • Spring-context-X.X.X.RELEASE.jar:Spring上下文模块
  • Spring-core-X.X.X.RELEASE.jar
  • Spring-expression-X.X.X.RELEASE.jar

二、IOC

由SpringIOC容器管理类之间的依赖关系,实现类之间的解耦(我认为都是将依赖关系降低到聚合)

1.底层原理

  • XML解析:类信息配置在XML中,由容器解析,加载、注册到容器中
  • 工厂模式:容器创建对象的方式是基于抽象工厂模式进行设计,由工厂(容器)统一管理bean的创建
  • 反射:bean在容器中是通过反射进行实例化

2.IOC实现方式(容器创建方式)

  • BeanFactory:实现了懒加载
  • ApplicationContext:非懒加载,在容器刷新过程的末尾部分完成了所以单例bean对象的实例化,同时ApplicationContext是BeanFactory的子类实现,对BeanFactory进行了拓展增强。

开发过程中我们一般通过两种方式实例化容器加载配置,而根据配置方式的不同我们实现容器的方式也不同

下面是根据配置方式的不同我们使用了不同的容器类

  1. XML配置方式:我们将配置信息放在配置文件中
    1. ClassPathXMLApplicationContext:根据相对路径获取配置文件进行容器初始化
    2. FileSystemXMLApplicationContext:根据配置文件绝对路径获取配置文件进行容器初始化
  2. 完全注解开发:我们以配置类的形式完成配置
    1. AnnotationConfigApplicationContext:根据配置类进行容器初始化,传入的是配置类的类对象

3.Bean配置(容器中管理的Bean从何处而来)

除去bean本身自带的一些为我们提供服务的后置处理器类以外,其他的我们自定义的类我们都需要自己配置,下面介绍几种配置方式。

3.1 XML的Bean标签配置

<bean id="beanName" class="classPath" /> 
           

其中每个bean在容器中都有一个对应的BeanId标识,它们被存放一个HashMap中,而这个HashMap的具体实现我们能够在容器的父类中的DefaultListableBeanFactory类中,通过<String,BeanDefinition>的形式进行存储,其中的String类型就是我们的id,而后面的BeanDefinition是我们配置中bean所对应的类的一个包装类,是我们所以bean在容器中的一个存储形式。

标签的属性和子标签

属性:

  • id:bean的id名称
  • class:全限定类名
  • scope:类的作用域,默认为sigleton
  • autowire:是否自动装配

子标签:

  • <property>:用于setter注入
    • name:成员变量名
    • value:值
    • ref:引入的bean的id
  • <constructor-arg>:用于构造方法注入,我们容器中用于使用的是反射机制来实例化对象,所以默认要调用无参构造
    • name:成员变量名
    • value:值
    • ref:引入的bean的id

同时我们还可以通过引入p空间来简化我们属性的注入

3.2 XML的扫描器+注解配置

xml配置文件中定义包扫描器

类上添加注解

<context:component-scan base-package="packagePath"></context:component-scan>
           
@Component
class beanA{}
           

容器实例化时,会通过我们在配置文件中配置的包扫描器去扫描对应包下所有类。只要类上存在@Component、@Service、@Repository、@Controller注解,就会被加载到我们的容器中

3.3 配置类的@Bean配置

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

Class A{}
           

在这个方式中我们可以看到我们的Config上添加了@Configuration注解,代表着这个类是一个注解类,该类等同于一个配置文件,只是是一个类的形式出现。

其中我们通过@Bean配置Bean类,其中方法的返回类型相当于我们Bean的类型,默认方法名是我们bean的id,也可以在@Bean标签中通过参数显式修改

3.4 配置类的扫描器注解+注解配置

配置类中引入扫描器注解来指定需要扫描的包

类上添加注解

@Configuration
@ComponentScan("packagePath")
public class Config{
    @Bean
    public A a(){
        return new A();
    } 
} 
           
@Component
class beanA{}
           

3.5 FactoryBean

通过实现FactoryBean接口,返回我们需要的Bean,但是FacrotyBean也是需要以前四种方式配置起来才能使用。

public class MyFactoryBean implements FactoryBean<A>{
    
    @Override
    public Object getObject() throws Exception{
        return new A();
    }

    @Override
    public Class<?> getObjectType(){
        return A.class;
    }
    
    @Override
    public boolean isSingleton(){
        return false;
    }
}

class A{}
           

3.6 Bean的属性注入的类型

以Student类和IDCard类为例,完成各种类型值的注入

package com.xmlSpring.beans;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Student {
    private String name;
    private Integer age;
    private IDCard idCard = new IDCard();
    private List<String> list;
    private Set<String> set;
    private Map<String,String> map;
    private String[] array;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public IDCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IDCard idCard) {
        this.idCard = idCard;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Set<String> getSet() {
        return set;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public String[] getArray() {
        return array;
    }

    public void setArray(String[] array) {
        this.array = array;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", idCard=" + idCard +
                ", list=" + list +
                ", set=" + set +
                ", map=" + map +
                ", array=" + Arrays.toString(array) +
                '}';
    }
}
           
package com.xmlSpring.beans;

public class IDCard {
    private String id;
    private String name;

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "IDCard{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}
           
<bean id="student" class="com.xmlSpring.beans.Student">
           <!--注入NULL-->
           <property name="name">
               <null/>
           </property>
           <!---->
           <property name="age" value="33"></property>
           <!--级联注入(需要该对象非空)-->
           <property name="idCard.id" value="111"></property>
           <property name="idCard.name" value="张三"></property>
           <!--注入array-->
           <property name="array">
               <array>
                   <value>1</value>
                   <value>2</value>
               </array>
           </property>
           <!--注入list-->
           <property name="list">
               <list>
                   <value>1</value>
                   <value>2</value>
               </list>
           </property>
           <!--注入set-->
           <property name="set">
               <set>
                   <value>1</value>
                   <value>2</value>
               </set>
           </property>
           <!--注入map-->
           <property name="map">
               <map>
                   <entry key="1" value="1"/>
                   <entry key="2" value="2"/>
               </map>
           </property>
       </bean>
           

上述集合标签中如果需要引入其他对象作为参数,就将value改为ref

4.Bean的生命周期

  1. 通过构造器创建实例
  2. 类成员完成属性注入
  3. bean的后置处理器
  4. 调用Bean的初始化方法
  5. bean的后置处理器
  6. 使用bean
  7. 销毁bean

5.Bean的后置处理器BeanPostProcessor

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }
}
           

6. Bean的作用域

  • sinleton:单例(默认)
  • prototype:多例
  • session:在各自的session内保持单例
  • request:在以此请求中保持单例

7.获取外部文件

<!--import outside properties file-->
    <context:property-placeholder location="classpath:datasource.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--get properties from proerties file by '${}' expression-->
        <property name="driverClassName" value="${driverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
     </bean>
           

8.bean属性的自动装配

我们前面的属性注入是通过手动注入实现注入的,现在我们使用自动注入完成属性注入

<bean id="student2" class="com.xmlSpring.beans.Student" autowire="byType"/>

<bean id="card" class="com.xmlSpring.beans.IDCard">
            <property name="id" value="111"/>
            <property name="name" value="aaa"/>
        </bean>
           

当我们需要获取student2时,容器会为我们自动获取容器内的IDCard类注入到我们的成员变量中

同时我们的autowire可以设为byName,只要我们bean的id和属性名一致就会为其完成属性注入

基于注解的自动装配

  • @Resource:根据类型和名字完成自动注入
  • @Autowire:根据类型注入
  • @Qualifer:根据名字完成注入

9.扫描组件

我们前面在通过注解配置Bean的时候使用到了扫描器,它的作用是扫描目标包中的类的注解,完成注解装配

同时,我们的扫描组件中还能设置一些包含和排除某些组件的属性

<!--关闭默认全部扫描-->
    <context:component-scan base-package="com" use-default-filters="false">
        <!--扫描包含Controller注解的类-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <!--不扫描包含Component注解的类-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
           

下面是注解式的扫描组件配置

@ComponentScan(
     value = "com.annotationSpring.beans",
        includeFilters = {@Filter(type = FilterType.ANNOTATION,value = Controller.class)},
        excludeFilters = {@Filter(type = FilterType.ANNOTATION,value = Component.class)}
)