Spring之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"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--  注册一个Cat对象-->
    <bean class="com.zsc.pojo.Cat" id="cat" p:nick="花花" p:color="黑色"/>

    <bean class="com.zsc.pojo.UserBean" id="user">
        <!-- 设值注入 -->
        <property name="cat" ref="cat">
            <!--<bean class="com.zsc.pojo.Cat" />-->
        </property>
        <property name="favorites">
            <array>
                <value>篮球</value>
                <value>爬山</value>
                <value>逛街</value>
            </array>
        </property>
        <property name="cats">
            <list>
                <bean class="com.zsc.pojo.Cat" p:nick="小花1" p:color="红色"/>
                <bean class="com.zsc.pojo.Cat" p:nick="小花2" p:color="绿色"/>
                <bean class="com.zsc.pojo.Cat" p:nick="小花3" p:color="黄色"/>
            </list>
        </property>

        <property name="map" >
            <map>
                <entry key="name1" value="张三1"/>
                <entry key="name2" value="张三2"/>
                <entry key="name3" value="张三3"/>
            </map>
        </property>
        <property name="props">
            <props>
                <prop key="username" >root</prop>
                <prop key="password">123</prop>
            </props>
        </property>
    </bean>
</beans>

1. IOC控制反转

IOC本质上是一个概念,是一种思想,控制反转就是对对象控制权的转移,SpringIOC容器创建对象,然后把对象的使用权交出去

2. 基于XML配置文件方式实现

2.1 基本使用

a.引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.17.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

</dependencies>

b. 添加spring的配置文件

<?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>

c.注册Bean

将需要被Ioc容器管理的类型通过<bean>标签来注册

<?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">

    <!--  添加需要被容器管理的内容-->
    <bean class="com.gupaoedu.pojo.UserBean" />
</beans>

d.测试获取

/**
     * IoC的方式获取 UserBean 对象
     */
@Test
public void fun2(){
    // 1.IoC容器的初始化操作 调用UserBean中无参构造器创建对象
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    // 2.从容器中获取UserBean对象 没有显示的new
    UserBean user = (UserBean) ac.getBean("userBean");
    user.say();
}

2.2 从容器中获取对象的方式

2.2.1 根据ID

id只能声明一个

2.2.2 根据name

可以声明一个或者多个

<?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">

    <!--  添加需要被容器管理的内容-->
    <!--<bean class="com.zsc.pojo.UserBean" id="userBean" name="userBean2"/>-->
    <bean class="com.zsc.pojo.UserBean" id="user1,user2,user3" name="u1,u2,u3"/>
</beans>

 id="user1,user2,user3" 只表示一个

name="u1,u2,u3" 表示会被拆分为3个name属性【拆分会根据 ',' ';' ' ' 空格 】

2.2.3 根据类型 

我们可以根据需要获取的对象的类型从容器中获取对象

@Test
public void fun6(){
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    UserBean bean = ac.getBean(UserBean.class);
    bean.say();
}

如果同一类型的对象在容器中有多个,我们仅仅只是通过类型来查找,那么就会报错

<?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">

    <!--  添加需要被容器管理的内容-->
    <!--<bean class="com.gupaoedu.pojo.UserBean" id="userBean" name="userBean2"/>-->
    <bean class="com.gupaoedu.pojo.UserBean" id="user1,user2,user3" name="u1 u2 u3"/>

    <bean class="com.gupaoedu.pojo.UserBean" id="userBean1" name="ub1"/>
</beans>

那么怎么解决呢?在getBean方法中通过组合条件来查找

@Test
public void fun6(){
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    // UserBean bean = ac.getBean(UserBean.class);
    UserBean bean = ac.getBean("u1",UserBean.class);
    bean.say();
}

 还要就是我们可以在<bean>中设置primary属性为true,那么当同一类型有多个对象时,就会优先返回primary属性的对象

2.3 BeanFactory和ApplicationContext的区别

 /**
     * ApplicationContext
     *    默认在IoC容器初始化的时候就会实例化对象
     */
    @Test
    public void fun1(){
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");

    }

    /**
     * BeanFactory
     *    IoC容器初始化的时候不会实例化对象
     *    在调用获取的时候才会创建对象
     */
    @Test
    public void fun2(){
        BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        factory.getBean("u1");
    }

类图:

 

 从类图上我们可以很清晰的看到ApplicationContext具有BeanFactory的所有功能,同时扩展了很多BeanFactory不具备的功能(如事件广播,资源加载,web支持等等...)

2.4 工厂注入

2.4.1 静态工厂注入

<?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">

    <!--  通过静态工厂的方式注入 -->
    <bean class="com.zsc.factory.StaticFactoryDemo" factory-method="getInstance" id="user"></bean>

</beans>
 public static Map<String,UserBean> hashMap ;

    static {
        hashMap = new HashMap<String, UserBean>();
        hashMap.put("a1",new UserBean());
        hashMap.put("a2",new UserBean());
        hashMap.put("a3",new UserBean());
    }

    /**
     * 静态工厂提供的方法
     * @return
     */
    public static UserBean getInstance(){
        return hashMap.get("a1");
    }

 2.4.2 动态工厂注入

public class DynamicFactoryDemo {


    public UserBean getInstance(){
        return new UserBean();
    }
}
?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">

    <!--  通过动态工厂的方式注入 -->
    <bean class="com.zsc.factory.DynamicFactoryDemo" id="dynamicFactoryDemo" ></bean>

    <!--  从工厂对象中获取 需要的对象-->
    <bean id="user2" factory-bean="dynamicFactoryDemo" factory-method="getInstance"/>

</beans>

2.5 属性注入 

属性注入主要是指如何给对象中的属性赋值

2.5.1 构造注入

通过构造方法注入,首先得提供对应得构造方法,既可以通过name也可以通过index来指定要赋值得参数

<?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">

    <bean class="com.zsc.pojo.UserBean" id="user">
        <!-- 构造注入 -->
        <!--<constructor-arg name="id" value="666"/>
        <constructor-arg name="userName" value="bobo"/>-->
        <constructor-arg index="0" value="999" />
        <constructor-arg index="1" value="gp" />
    </bean>
</beans>

 2.5.2 设值注入

注入的属性必须提供对应的setter方法

<bean class="com.gupaoedu.pojo.UserBean" id="user1">
    <!-- 设值注入 -->
    <property name="id" value="1"/>
    <property name="userName" value="张三"/>
</bean>

 2.5.3 其它注入类型

private Cat cat;

private String[] favorites;

private List<Cat> cats;

private Map<String,Object> map;

private Properties props;

3. 基于Java配置的实现方式

springboot流行之后,java配置的方式开始被广泛使用

java配置类

@Configuration
public class JavaConfig {

    /**
     * @Bean 作用和我们在applicationContext.xml中添加的<bean> 效果一样</>
     * 默认的name是方法名称
     * 自定义的name 可以通过value属性或者name属性来指定
     * @return
     */
    @Bean(name = {"aaa","bbb"})
    public User getUser(){
        User user = new User();
        //user.set....
        return user;
    }
}

测试

public class MainTest1 {

    @Test
    public void fun1(){
        // 通过@Configuraction注解来初始化IoC容器
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        System.out.println(ac.getBean("aaa",User.class));
    }
}

4.注解编程

4.1 配置注解

4.2 赋值注解

4.3 注解编程的使用

 我们需要将被IOC容器管理的类型通过@Component注解标注

@Component // 需要被IoC容器加载
public class UserController {

    @Autowired
    private IUserService service;

    public List<User> query(){

        return service.query();
    }
}

 @Autowired和@Resource的区别

@Autowired:默认只能根据类型来查找,可以结合@Qualifier("abc")注解来实现通过name查找

@Resource:默认同样是根据类型来查找,但是提供的有type和name属性类实现不同的查找方式

 4.3.1 基于java配置类的方式

我们需要在Java配置类中通过@ComponentScan注解来指定扫描的路径

默认的情况下扫描的是当前路径及其子路径下的所有被@Component @Comtroller @Service @Repository标注的类型

@Configuration
@ComponentScans({
        @ComponentScan(value = {"com.zsc.controller"}
                ,useDefaultFilters = false
                ,includeFilters = {@ComponentScan.Filter(Controller.class)})
        ,@ComponentScan(value = {"com.zsc.service","com.zsc.dao"}
                ,useDefaultFilters = true
                ,excludeFilters = {@ComponentScan.Filter(Controller.class)})
})
public class JavaConfig {

}

4.3.2 @Value注解介绍

@value帮助我们给数组动态的设值

@Component
@Data
public class User {

    @Value("bobo") // 注入普通的字符串
    private String userName ;

    @Value("#{systemProperties['os.name']}")
    private String systemPropertiesName; // 注入操作系统的信息

    @Value("#{T(java.lang.Math).random()*100}")
    private double randomNumber; // 注入表达式的结果

    @Value("#{person.personName}")
    private String fromPersonName; // 注入其他Bean的属性


    @Value("classpath:test.txt")
    private Resource resourceFile;

    @Value("http://www.baidu.com")
    private Resource baiduFile;
}
@Component
@Data
public class Person {

    @Value("PersonInfo")
    private String personName;
}

java配置类

@Configuration
@ComponentScan("com.zsc")
public class JavaConfig {

}

测试

public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        System.out.println(ac.getBean(User.class));
        User user = ac.getBean(User.class);
    }

若需要读取第三方的properties文件中的信息

创建第三方文件,在java配置类中通过@PropertySource注解来显示引入属性文件

@Configuration
@ComponentScan("com.zsc")
// 显示的指定要加载的属性文件
@PropertySource({"classpath:spring-db.properties"})
public class JavaConfig {

}

 4.3.3 @PostContruct @PreDestroy @DependsOn

@PostConstruct 系统初始化完成后的回调方法

@PreDestroy 系统销毁时的回调方法

@DependsOn 指定实例化对象的先后顺序

@Component
@DependsOn({"user"}) // Person的实例化依赖于User对象的实例化,也就是User先于Person实例化
public class Person {

    public Person(){
        System.out.println("Person 构造方法执行了...");
    }
}

 4.3.4 @Import注解

将类型加入到IOC容器中的方式有哪些

1.基于XML文件的方式<bean >

2.基于XML文件的方式context:Component-Scan + @Component

3.基于Java配置类@Bean

4.基于Java配置类@ComponentScan + @Component

5.FactoryBean + getObject方法

6.@Import

4.3.4.1 静态使用

直接在@Import注解中直接定义要引入到IOC容器中的类型

缺点:  无法灵活的指定引入的类型

4.3.4.2 动态使用-ImportSelector

通过重写selectImports方法来达到灵活控制引入类型的目的

public class GpImportSelector implements ImportSelector {

    /**
     *
     * @param annotationMetadata
     * @return
     *    IoC 要加载的类型的全路径的字符串数组
     */
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 在此处实现不同的业务逻辑控制
        return new String[]{LoggerService.class.getName(),CacheService.class.getName()};
    }
}
 4.3.4.3 动态使用-ImportBeanDefinitionRegistrar
public class GpImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param annotationMetadata
     * @param beanDefinitionRegistry IoC容器中管理对象的一个注册器
     */
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        // 需要将添加的对象包装为一个RootBeanDefinition对象
        RootBeanDefinition cache = new RootBeanDefinition(CacheService.class);
        beanDefinitionRegistry.registerBeanDefinition("cache",cache);

        RootBeanDefinition logger = new RootBeanDefinition(LoggerService.class);
        beanDefinitionRegistry.registerBeanDefinition("logger",logger);
    }
}

5.Bead对象的作用域

默认的情况是singleton

@Bean
@Scope("prototype")
public Person person(){
    return new Person();
}@Bean
@Scope("prototype")
public Person person(){
    return new Person();
}
@Bean
@Scope("singleton")
public Person person(){
    return new Person();
}

 6. 总结

        本文主要介绍了SPring IOC的简单使用,Bean XML配置方式的对象获取以及DI注入,随着技术的发展,注解编程成为了时代的潮流,于是又介绍了注解结合java配置类的使用怎么更加灵活的获取对象以及属性注入的方式,最后介绍了Bean对象的作用域。对Spring的IOC有了初步的认识

上一篇:Ant Design+react 表单只读


下一篇:Bean的作用域