IOC的基本使用

IOC

前言

了解Spring IOC,及其它的XML和注解实现。

一、IOC

1、IOC底层原理

底层用XML解析+工厂设计模式+反射。
以前降低耦的方式,
1)写一个properties文件,然后load一下对于该文件的输入流,读取配置文件信息,然后通过getString("key")来获取里面的内容,从而可改配置文件而不用重写编译。
2)工厂模式,解除两个类之间的耦合,但是工厂和两类之间都有高耦合。
以前学的反射,
1)通过classForName(classpath)来动态创建实例。

有了前面的基础,就可以读取properties文件的类名,然后在工厂中使用反射来获取类实例,这样就进一步降低了耦合。而IOC使用的是XML解析而不是去加载properties文件。

2、IOC接口(BeanFactory)

IOC思想是基于IOC容器完成,IOC容器底层就是对象工厂
IOC容器实现的两种方式(两种接口),BeanFactory、ApplicationContext。
1)BeanFactory
IOC的基本接口,用于Spring内部使用。
注:在加载配置文件时,不会去创建对象,在获取时才会去创建对象。
2)ApplicationContext
该为BeanFactory的子接口,提供更加强大的功能,提供给开发人员使用。
注:加载配置文件时,就会把配置文件配置的bean全部创建。

ApplicationContext的两个主要实现类,FileXmlApplicationContext(系统盘上的文件)、ClassPathXmlApplicationContext(src下,即类路径)

3、IOC操作Bean管理(基于xml)

Bean管理即,Spring创建什么对象?给创建的对象赋什么值?

1、 Bean标签的属性,

id,给该Bean取一个唯一的标识。
class,类的全路径。
name,类id的一个早期属性,可以加特殊字符。

2、xml注入基本属性,

DI,依赖注入,注入属性值。
1)setter方法去注入,

	<!-- 配置一个User Bean -->
    <bean id="user" class="com.xhu.spring5.User">
        <property name="name" value="root">
        </property>
    </bean>

2)通过有参构造来注入,

	<!-- 配置一个User Bean -->
    <bean id="user" class="com.xhu.spring5.User">
        <constructor-arg name="name" value="root">
        </constructor-arg>
    </bean>

3)p名称空间注入,用于简化太多的properties,

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置一个User Bean -->
    <bean id="user" class="com.xhu.spring5.User" p:name="root">
    </bean>
</beans>

3、注入其它类型属性

null值,

<!-- 配置一个User Bean -->
    <bean id="user" class="com.xhu.spring5.User" p:name="root">
        <!--null值的设置 -->
        <property name="name">
            <null></null>
        </property>
    </bean>

特殊符号,

	<!-- 配置一个User Bean -->
    <bean id="user" class="com.xhu.spring5.User" p:name="root">
        <!--null值的设置 -->
        <property name="name">
            <value>
                <![CDATA[<<北京>]]>
            </value>
        </property>
    </bean>
    <!-- 配置一个User Bean -->
    <bean id="user" class="com.xhu.spring5.User" p:name="root">
        <!--null值的设置 -->
        <property name="name">
            <value>
                &lt;&lt;北京&gt;&gt;
            </value>
        </property>
    </bean>
</beans>

5、注入属性,外部Bean

<bean id="service" class="com.xhu.service.impl.ServiceImp">
        <property name="dao" ref="dao"></property>
    </bean>
    <bean id="dao" class="com.xhu.dao.impl.DAOImpl"></bean>

注:service中必须有setter方法。

6、属性注入,内部bean和级联赋值,用于一对多的关系。

	<bean id="emp" class="com.xhu.spring5.Emp">
        <property name="name" value="root"></property>
        <property name="gender" value="男"></property>
        <property name="dept">
            <bean id="dept" class="com.xhu.spring5.Dept">
                <property name="name" value="IT部"></property>
            </bean>
        </property>
    </bean>
<bean id="emp" class="com.xhu.spring5.Emp">
        <property name="name" value="root"></property>
        <property name="gender" value="男"></property>
        <property name="dept" ref="dept"></property>
        <property name="dept.name" value="IT部"></property>
    </bean>
    <bean id="dept" class="com.xhu.spring5.Dept">
    </bean>

注:dept.name,需要生成获取dept的get方法。

7、属性注入,Array、List、Set、Map

package com.xhu.spring5;

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

public class TestCollection {
    private String[] names;
    private List<String> list;
    private Map<String,String> map;
    private Set<String> set;

    public TestCollection() {
    }

    public void setNames(String[] names) {
        this.names = names;
    }

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

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

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

<bean id="testCollection" class="com.xhu.spring5.TestCollection">
        <property name="names">
            <array>
                <value>a</value>
                <value>b</value>
                <value>c</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>a</value>
                <value>b</value>
                <value>c</value>
            </list>
        </property>
        <property name="map">
            <map>
                <entry key="root" value="root"></entry>
                <entry key="root" value="root"></entry>
                <entry key="root" value="root"></entry>
            </map>
        </property>
        <property name="set">
            <set>
                <value>a</value>
                <value>b</value>
                <value>c</value>
            </set>
        </property>
    </bean>

8、集合等+JavaBean

<property name="list">
            <list>
                <ref bean="bean1"></ref>
                <ref bean="bean2"></ref>
                <ref bean="bean3"></ref>
            </list>
        </property>

9、集合等的抽取,需引入名称空间util

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:list id="list">
        <value>1</value>
    </util:list>
    <bean id="testCollection" class="com.xhu.spring5.TestCollection">
        <property name="list">
            <ref bean="list"></ref>
        </property>
    </bean>
    </beans>

10、工厂Bean

FactoryBean,不同于上面的普通bean,配置什么类型就返回什么类型,而FactoryBean可以返回不同的类型。
step)建立类实现FactoryBean,然后重写其方法,可以返回想要的类型。

package com.xhu.spring5;

import org.springframework.beans.factory.FactoryBean;

public class MyBean implements FactoryBean<User> {

    @Override
    public User getObject() throws Exception {
        User user = new User();
        user.setName("root");
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

<bean id="myBean" class="com.xhu.spring5.MyBean"></bean>

11、bean的作用域,可以设置配置的一个bean为单实例还是多实例。

<!-- singleton为单实例,prototype为多实例-->
    <bean id="myBean" scope="singleton" class="com.xhu.spring5.MyBean"></bean>
    <bean id="myBean2" scope="prototype" class="com.xhu.spring5.MyBean"></bean>

12、bean的生命周期

A)执行其无参构造方法
B)通过set方法来赋值
C)调用bean的初始化方法(需要进行配置)
D)bean的使用
E)容器在关闭时,调用bean的销毁方法(需要进行配置销毁的方法)

package com.xhu.spring5;

public class BeanLife {
    private String name;

    public BeanLife() {
        System.out.println("第一步,调用无参构造器");
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步,执行赋值操作");
    }

    public void initMethod() {
        System.out.println("第三步,执行初始化方法,需配置");
    }

    public void use() {
        System.out.println("第四步,使用bean,需调用此方法");
    }

    public void destroy() {
        System.out.println("第五步,执行销毁bean方法,需配置");
    }
}

<bean id="beanLife" class="com.xhu.spring5.BeanLife" init-method="initMethod" destroy-method="destroy">
        <property name="name" value="root"></property>
    </bean>

注:销毁需要((ClassPathXmlApplicationContext)context).close()
注:如果加上后置处理器,还要在bean init方法的前调用两个方法,这两步都是把bean实例传入后置处理器方法中,

package com.xhu.spring5;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之前来操作一下bean");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之后来操作一下bean");
        return bean;
    }
}

<bean id="beanLife" class="com.xhu.spring5.BeanLife" init-method="initMethod" destroy-method="destroy">
        <property name="name" value="root"></property>
    </bean>
    <bean id="beanPostProcessor" class="com.xhu.spring5.MyBeanPost"></bean>

13、自动装配,可以根据名字或者类型来注入属性值,

<!-- 通过bean 的id来看是否与属性名相等-->
<bean id="service" class="com.xhu.service.impl.ServiceImp" autowire="byName">
        <!--<property name="dao" ref="dao"></property>-->
    </bean>
    <bean id="dao" class="com.xhu.dao.impl.DAOImpl"></bean>
<!-- 找到该类的bean,但是相同类型的bean不能定义多个-->
<bean id="service" class="com.xhu.service.impl.ServiceImp" autowire="byType">
        <!--<property name="dao" ref="dao"></property>-->
    </bean>
    <bean id="dao" class="com.xhu.dao.impl.DAOImpl"></bean>

14、配置连接池,需要引入命名空间context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/db"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123"></property>
    </bean>
    <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    <context:property-placeholder location="classpath:druid.properties"></context:property-placeholder>
</beans>

4、IOC操作Bean管理(基于注解)

用注解来简化XML配置,Spring提供了以下几个创建bean的注解,
@Component、@Service、@Controller、@Repostory。

1、Step

1)需引入aop的jar包。
2)开启组件扫描,扫描到有组件的类就实例化。
3)创建类,并在其上面加上注解。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 开启组件扫描,即类扫描,扫描注解,进行该类的bean管理-->
    <!-- 多个package用逗号隔开,伙子直接用父包-->
    <context:component-scan base-package="com.xhu.service,com.xhu.dao"></context:component-scan>
</beans>
package com.xhu.spring5;

import org.springframework.stereotype.Component;

/**
 * 注解方式实现bean的生成,然后给spring管理
 * value的方式可以取别名,否则默认类名且第一个字母小写,这里相当于xml中的id
 */
@Component(value = "annoClass")
public class AnnoClass {


}

注:扫描可以有配置的扫,设置默认filter为false,

<!-- 扫带Controller注解的类-->
    <context:component-scan base-package="com.xhu.service,com.xhu.dao" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>

4)属性-Bean注入
A)AutoWired,根据属性类型进行注入,对应XML的byType。
B)Qualifier,根据属性名称进行注入,对应XML的byName。如果一个接口有很多实现类,就需要用Qualifier来指定实现类。
C)Resource,即可以根据类型,也可以根据名称。
D)Value,针对普通类型,前三个是Bean的注入。

package com.xhu.service.impl;

import com.xhu.dao.DAO;
import com.xhu.service.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.Resource;

public class ServiceImp implements Service {
    @Autowired
    private DAO dao;

    public void update() {
        dao.update();
    }

    @Autowired
    @Qualifier(value = "daoImpl")
    private DAO dao1;

    @Resource(name = "daoImpl")
    private DAO dao2;

    @Resource
    private DAO dao3;

    @Value(value = "root")
    private String name;
}

2、完全注解开发

都不用在XML中取扫描包,创建一个配置类取替代XML文件。

package com.xhu.spring5;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @Configuration 告诉spring这是一个配置类
 */
@Configuration
@ComponentScan(basePackages = {"com.xhu.service", "com.xhu.dao"})
public class SpringConfig {
}

	 @Test
    public void annotation() {
        //1.获取ApplicationContext对象来关联配置文件,为接下来的IOC做准备
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        //2.获取对象实例
        User user = ac.getBean("user", User.class);
        System.out.println(user);
    }

总结

1)Spring IOC

参考文献

[1] Spring5 尚硅谷

上一篇:【Java高级工程师蜕变之路】010 Spring IOC源码分析


下一篇:spring入门