spring IOC

IOC (Inversion of Control)

控制反转:对象之间的关系不在由传统的程序来控制,而是由spring容器来统一控制着这些对象的创建、协调、销毁,而对象只需要来完成业务逻辑即可。

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

    <!--
        向容器中注册一个person对象,spring容器将自动创建这个对象
        id是对象的唯一标识,class是组件的全类名

    -->
    <bean id="person01" class="bean.Person">
        <!--
            为属性赋值:
            属性的值获取不是根据属性的名字,而是set方法去掉,set后的属性值。
        -->
        <property name="id" value="1001"></property>
        <property name="name" value="mahao"></property>
        <property name="age" value="18"></property>
        <property name="birth" value="Sun Sep 15 21:28:04 CST 2019"></property>
        <property name="noFiled" value="属性值"></property>
    </bean>
</beans>
package chapter1;


import bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * spring环境测试:
 * <p>
 * 1.导入依赖,四个主要依赖
 * 2.配置文件,将bean添加容器
 * 3.依赖注入,获取对象
 * <p>
 * 案例:
 * 编写配置文件,将Person注入到spring管理中,之后从容器中获取。
 *
 * @author: mahao
 * @date: 2019/9/15
 */


public class Hello {


    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("chapter1.xml");
        Person person01 = (Person) applicationContext.getBean("person01");
        System.out.println("first get person01 : " + person01.toString());
        person01.setName("lishuo");
        Person person02 = (Person) applicationContext.getBean("person01");
        System.out.println("second get person01 after setName :" + person02.toString());
        System.out.println("==? " + (person01 == person02));
    }

    /*
    验证组件的属性值是通过set方法获取的
    属性值
    first get person01 : Person{id=1001, name='mahao', age=18, birth=Mon Sep 16 11:28:04 CST 2019}
    second get person01 after setName :Person{id=1001, name='lishuo', age=18, birth=Mon Sep 16 11:28:04 CST 2019}
    ==? true

    结果得知:
     1.组件的属性是根据setXXX区分的,XXX是属性,而不是属性
     2.容器会在初始化的时候,创建对象,可以根据在组件的构造方法中打印日志
     3. 同一个组件在容器中是单例的。
     */
}

2. 属性注入
 <!--===============属性赋值================-->
    <!--
      实验3:
    通过构造器为bean的属性赋值(index,type属性介绍)(测试)
    通过p名称空间为bean赋值

    实验4:正确的为各种属性赋值
    测试使用null值 、
    引用类型赋值(引用其他bean、引用内部bean)(测试)
    集合类型赋值(List、Map、Properties)、(测试)
    util名称空间创建集合类型的bean
    级联属性赋值
    -->

    <bean id="user01" class="bean.User">
        <property name="id" value="1001"></property>
        <property name="name" value="mahao"></property>
        <property name="age" value="18"></property>
        <property name="birth" value="Sun Sep 15 21:28:04 CST 2019"></property>
        <!--为对象赋值-->
        <property name="parent" ref="person01">
            <!--  为对象属性赋值,可以通过ref引用外部组件,或自定义创建一个用bean标签
                  <bean class="bean.Person"></bean>
              -->
        </property>

        <!--为set赋值-->
        <property name="friends">
            <set>
                <ref bean="person01"></ref>
                <bean class="bean.Person"></bean>
            </set>
        </property>

        <!--为map赋值-->
        <property name="maps">
            <map>
                <entry key="key1" value="v1"></entry>
                <entry key="key2" value-ref="person02"></entry>
            </map>
        </property>

        <!--为properties赋值-->
        <property name="properties">
            <props>
                <prop key="p01">aa</prop>
                <prop key="p02">bb</prop>
                <prop key="p03">cc</prop>
            </props>
        </property>

        <!--级联属性的赋值,为parent的name属性赋值为...-->
        <property name="parent.name" value="啊哈哈"></property>
    </bean>

	<!--====================组件之间的依赖关系=====================-->
    <!--
        实验6:通过继承实现bean配置信息的重用(测试)
        实验7:通过abstract属性创建一个模板bean(acstract=true)
        实验8:bean之间的依赖(改变bean的创建顺序)
    -->
    <!--实现bean信息的重用,通过parent属性,将父类bean设置为模版 abstract="true"-->
    <bean id="user03" class="bean.User" parent="person02" >
        <property name="name" value="我的名字"></property>
    </bean>
3. 工厂创建bean
  • 简单工厂设计模式
  • 工厂方法设计模式
  • 使用spring容器提供的工厂接口
 <!--====================工厂方式创建bean==========-->
    <!--
        实验9:测试bean的作用域,分别创建单实例和多实例的bean★(测试)singleton 和 prototype
        实验5:配置通过静态工厂方法创建的bean、实例工厂方法创建的bean、(FactoryBean测试)★
    -->

    <!--实验5:配置通过静态工厂方法创建的bean、实例工厂方法创建的bean、FactoryBean★  -->
    <!-- bean的创建默认就是框架利用反射new出来的bean实例 -->
    <!-- 工厂模式;工厂帮我们创建对象;有一个专门帮我们创建对象的类,这个类就是工厂
        AirPlane ap = AirPlaneFactory.getAirPlane(String jzName);

        静态工厂:工厂本身不用创建对象;通过静态方法调用,对象 = 工厂类.工厂方法名();
        实例工厂:工厂本身需要创建对象;
                工厂类 工厂对象 = new 工厂类();
                工厂对象.getAirPlane("张三");
     -->


    <!--   ☆ ☆ ☆ 
        1. 通过静态工厂的方法创建bean(就是简单工厂设计模式,根据参数,获取不同的实例对象)
        创建的组件实例,就是bean。是通过调用实例工厂的方法,根据传入的参数,创建的对象
        指定哪个方法是工厂方法
        class:指定静态工厂全类名
        factory-method:指定工厂方法
        constructor-arg:可以为方法传参
     -->
    <bean id="mysqlObject" class="chapter1.AsimpleFactory.SQLFactory" factory-method="getSqlObject">
        <constructor-arg value="mysql"></constructor-arg>
    </bean>

    <!--2. 通过实例工厂的方法创建bean(实际是工厂方法设计模式) ☆ ☆ ☆ -->
    <!--2.1 先指定工厂的实例-->
    <bean id="appleFactory" class="chapter1.BfactoryMethod.AppleFactory"></bean>

    <!--2.2 创建实例
        id: 创建实例的名字
        class: 实例属性
        factory_bean: 实例工厂的id
        factory-method: 属性创建方法
    -->
    <bean id="apple01" class="chapter1.BfactoryMethod.Apple"
                factory-bean="appleFactory" factory-method="getFruit"></bean>

 	<!--
        3.使用spring提供的接口去创建    ☆ ☆ ☆ 
        由于MyBeanFactory实现了Spring提供的BeanFactory接口,spring知道
        创建的对象的类型,以及调用那个方法去创建对象。本质上也是使用的工厂方法设计模式
        优势:
        1、ioc容器启动的时候不会创建实例
		2、FactoryBean;获取的时候的才创建对象
    -->
    <bean id="user04" class="chapter1.MyBeanFactory"></bean>

测试:

/**
 * 测试FactoryBean,创建实例
 *
 * @author: mahao
 * @date: 2019/9/16
 */
public class Test3 {

    ApplicationContext context = new ClassPathXmlApplicationContext("chapter1.xml");

    /**
     * 1.静态工厂创建bean(简单工厂模式):
     * SQLObject对象会在容器初始化的时候,把对象创建出来,而不是等到使用对象在创建。
     */
    @Test
    public void demo1() {
        SQLObject sqlObject = (SQLObject) context.getBean("mysqlObject");
        sqlObject.getConnect();
    }

    /**
     * 2.实例工厂创建bean(工厂方法模式,将每个产品的创建设计成一个工厂):
     */
    @Test
    public void demo2() {
        Apple apple = (Apple) context.getBean("apple01");
        System.out.println(apple);
    }


    /**
     * 使用spring提供的beanfactory
     */
    @Test
    public void demo3() {
        User user04 = (User) context.getBean("user04");
        System.out.println(user04);
    }
}

/**
 * 创建spring提供的bean创建方法
 */
class MyBeanFactory implements FactoryBean<User> {


    @Override
    public User getObject() throws Exception {
        return new User();
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}
4. spring 生命周期

实例化 > 后置处理器before > init-method > 后置处理器after > 完成 > 销毁

5. 引用外部属性文件 (数据库配置文件)
 <!--读取配置文件信息-->
    <context:property-placeholder location="classpath:jdbc.properties">
        
	</context:property-placeholder>

	location="classpath:jdbc.properties"
	中的classpath是指类路径下,对于java项目是指bin目录下,对于web项目是class路径下。
6. 自动装配

①自动装配的概念

​ [1]手动装配:以value或ref的方式明确指定属性值都是手动装配。

​ [2]自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。

②装配模式

​ [1]根据类型自动装配:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配

​ [2]根据名称自动装配:必须将目标bean的名称和属性名设置的完全相同

​ [3]通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。

③选用建议

​ 相对于使用注解的方式实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。

<?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: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/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <bean id="parent" class="bean.Person"></bean>
   <!-- <bean id="p2" class="bean.Person"></bean>
    <bean id="p3" class="bean.Person"></bean>-->
	<!--
		autowire的取值有几种:
		byName: 根据属性声明的值,从spring容器中获取去匹配
		byType: 根据属性类型,去注入
		constructor:根据构造器参数去注入
	-->
    <bean id="user" class="bean.User" autowire="byName"></bean>

</beans>

7. 注解自动装配

自动装配和依赖注入的关系

依赖注入的本质就是装配,装配是依赖注入的具体行为。
首先,确定一下装配的概念。《spring实战》中给装配下了一个定义:创建应用对象之间协作关系的行为称为装
配。也就是说当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化。这就是装配。如果
一个对象只通过接口来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行
切换。但是这样会存在一个问题,在传统的依赖注入配置中,我们必须要明确要给属性装配哪一个bean的引用,一
旦bean很多,就不好维护了。基于这样的场景,spring使用注解来进行自动装配,解决这个问题。自动装配就是开
发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。与自动装配配合的还有“自动检
测”,这 个动作会自动识别哪些类需要被配置成bean,进而来进行装配。这样我们就明白了,自动装配是为了将依
赖注入“自动化”的一个简化配置的操作。

spring的自动装配实现是依赖Spring Aop的,需要导入依赖;

注解标识组件:

①普通组件:@Component
	标识一个受Spring IOC容器管理的组件
②持久化层组件:@Respository
	标识一个受Spring IOC容器管理的持久化层组件
③业务逻辑层组件:@Service
	标识一个受Spring IOC容器管理的业务逻辑层组件
④表述层控制器组件:@Controller
	标识一个受Spring IOC容器管理的表述层控制器组件
⑤组件命名规则
	[1]默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id
	[2]使用组件注解的value属性指定bean的id
@Controller @Service @Responsity @Component四个注解,是对spring组件的标识注解,通过表示,让spring容器扫描到,可以装配到容器中,这些注解将Bean的id设置为类名的首字母小写。四个注解没有明显区分,是为了程序员识别使用了。

扫描组件:

上面被spring注解表示的类,必须有spring进行扫描才可以识别到。

 <!--自动装配扫描的基础包,用逗号分隔多个包-->
    <context:component-scan base-package="chapter5,chapter3">
        <!--
            可以设定某些包需要排除扫描
        -->
        <!--不扫描注解下的组件-->
        <context:exclude-filter type="annotation"
                           expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

组件装配:

上面的注解将组件注册到容器中,通过xml配置,可以通过构造器注入,和set方法注入,完成属性的注入。比如通过构造器参数,指定参数位置和组件bean完成注入,或者用set方法,完成属性的赋值(依赖注入只有这两种方式)。

<bean id="user1" class="chapter2.User" init-method="init" destroy-method="destory">
        <constructor-arg name="name" value="名字1"></constructor-arg>
        <property name="name" value="名字2"></property>
</bean>

现在可以通过spring的注解,来完成依赖的自动装配。

在指定要扫描的包时,context:component-scan 元素会自动注册一个bean的后置处理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记了@Autowired、@Resource或@Inject注解的属性。

自动装配的注解有:
	@Autowired @Qualifier @Resource @Inject
三个注解都能实现自动装配bean,也有区别:
	/**
     * 1.使用spring提供的自动装配功能:
     * @Autowired注解的装配方式是,通过依赖的类型,去容器中查找组件
     *     1. 找不到?  则会发生错误  org.springframework.beans.factory.UnsatisfiedDependencyException:
     *     2.正常找到,则装配bean
     *     3.如果找到多个同一类型的组件,比如userservie 和 UserServiceExt?这个将会根据属性变量名去去寻找
     *         1.找打了,则会正常装配
     *         2. 找不到和变量相同的组件id,则会报错。
     *              对于存在这种情况,则使用另一个注解@Qualifiter,通过让指定装载的bean名称
     *                  @Qualifier("UserService")
     *                  @Autowired
     *                  UserService userService;
     *      
     *
     */
    @Test
    public void demo2() {
        UserController userController = (UserController) context.getBean("userController");
        userController.doGet();
    }
    
上面是@Autowired注解的使用,对于@Qulifier注解作用只是可以使用组件的id进行获取。

	/**
     * 注解作用在方法上,将会为参数的属性注入依赖
     * @param userService
     * @param userDao
     */
    @Autowired
    public UserController(@Qualifier("UserService") UserService userService,UserDao userDao){
        System.out.println(userDao +" "+ userService);
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值