SSM-回炉重造-Spring IOC

Spring 主要两部分:

  1. IOC (Inversion Of Control): 控制反转
  2. AOP (Aspect Oriented Programming) : 面向切面编程

IOC

控制: 资源的获取方式

  • 主动式:手动创建资源

    BookController{
       BookService bs = new BookService(); 
        public void test(){
            bs.check();
        }
    }
  • 被动式: 交给容器来创建资源

    BookController{
       BookService bs;   //由容器来帮助我们创建此对象,并且赋值
        public void test(){
            bs.check();
        }
    }

获取资源的方式由主动new变成被动接收

DI(Dependency Injection): 依赖注入

容器通过反射的形式,将需要的对象注入到相应的变量中,只要容器管理的组件,即可使用容器提供的功能

HelloWorld 示例

  1. 创建空的Java工程
  2. 导入必需的Spring Jar包依赖包

    spring-beans-4.0.0.RELEASE.jar

    spring-context-4.0.0.RELEASE.jar

    spring-core-4.0.0.RELEASE.jar

    spring-expression-4.0.0.RELEASE.jar

  3. 创建JavaBean对象

    package com.oylong.bean;
    
    /**
     * @ProjectName: springioc
     * @Description:
     * @Author: OyLong
     * @Date: 2020/9/10 0:35
     */
    
    public class Person {
        private String name;
        private Integer age;
        private String email;
        ......
    }
    
  1. 创建Spring 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 id="person" class="com.oylong.bean.Person">
            <property name="age" value="15"></property>
            <property name="name" value="oyLong"></property>
            <property name="email" value="[email protected]"></property>
        </bean>
    </beans>
  2. 通过ApplicationContext对象以及对象的id来获取容器中的对象

    public class IOCTest {
        @Test
        public void test(){
    
            ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
            Person person = ioc.getBean("person", Person.class);    //person为bean对象的id
    
            System.out.println(person);
        }
    }
  3. 打印了由IOC容器管理的对象

    Person{name='oyLong', age=15, email='[email protected]'}

ClassPathXmlApplicationContext: 从当前的类路径下去寻找IOC配置文件

注意事项

  1. 容器中的对象在容器创建完成的时候,也创建好了
  2. ioc容器中的对象是单例的
  3. 容器中若没有相应的组件,则获取组件时会报异常
  4. ioc容器创建对象时会利用相应的setter方法进行赋值(property标签)
  5. javaBean的属性名,由getter/setter方法决定(尽量自动生成setter/getter方法)

通过类型直接获取Bean实例

ioc容器中只有一个同类型的bean对象时,如下:

ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
Person person = ioc.getBean(Person.class);

System.out.println(person);

我们将可以直接得到对应的bean对象

Person{name='oyLong', age=15, email='[email protected]'}

但是当容器中有多个相同类型的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 id="person" class="com.oylong.bean.Person">
        <property name="age" value="15"></property>
        <property name="name" value="oyLong"></property>
        <property name="email" value="[email protected]"></property>
    </bean>

    <bean id="person1" class="com.oylong.bean.Person">
        <property name="age" value="15"></property>
        <property name="name" value="oyLong"></property>
        <property name="email" value="[email protected]"></property>
    </bean>
</beans>

报错:

No qualifying bean of type [com.oylong.bean.Person] is defined: expected single matching bean but found 2: person,person1

报错提示的也很明显,没有匹配的bean对象,因为找到了2个,不知道你需要的是哪一个,因此此时不适合使用类型来获取bean对象

通过调用有参构造器创建bean对象

前提是这个bean对象本身有对应的构造器,如下:

public class Person {
    private String name;
    private Integer age;
    private String email;

    public Person() {
    }

    public Person(String name, Integer age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
    ...
}

然后通过xml配置:

<bean id="person2" class="com.oylong.bean.Person">
    <constructor-arg name="age" value="16"></constructor-arg>
    <constructor-arg name="email" value="[email protected]"></constructor-arg>
    <constructor-arg name="name" value="oyLong"></constructor-arg>
</bean>

在获取bean对象即可

ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
Person person = ioc.getBean("person2", Person.class);

System.out.println(person);
}

property标签 复杂属性赋值

复杂类型不能直接通过value属性来赋值

1.空值(null)

如果不设置,则对象属性默认为null,其他基本类型属性则根据类的创建规则来自动赋值,也可手动设空值

如下:

<bean id="person1" class="com.oylong.bean.Person">
    <property name="age" value="15"></property>
    <property name="name" value="oyLong"></property>
    <property name="email">
        <null/>
    </property>
</bean>

2.对象赋值

car对象:

public class Car {
    private String name;
    private String color;
    private BigDecimal price;
    ......
}

person对象:

public class Person {
    private String name;
    private Integer age;
    private String email;
    private Car car;
    ......

通过引用为Person的Car赋值,如下:

<bean id="car" class="com.oylong.bean.Car">
    <property name="name" value="大卡车"></property>
    <property name="color" value="绿色"></property>
    <property name="price" value="2800"></property>
</bean>

<bean id="person2" class="com.oylong.bean.Person">
    <property name="age" value="15"></property>
    <property name="name" value="oyLong"></property>
    <property name="email" value="[email protected]"></property>
    <property name="car" ref="car"></property>
</bean>
public void test1(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
    Person person = ioc.getBean("person2", Person.class);

    System.out.println(person);

    System.out.println(person.getCar() == ioc.getBean("car", Car.class));
}

输出如下:

Person{name='oyLong', age=15, email='[email protected]', car=Car{name='大卡车', color='绿色', price=2800}}
true

同时可以看到,引用赋值成功了,同时从ioc容器获取的bean对象与赋值的对象是同一个

如果要设置一个不同的car,则需要内部创建一个 如下:

<bean id="person2" class="com.oylong.bean.Person">
    <property name="age" value="15"></property>
    <property name="name" value="oyLong"></property>
    <property name="email" value="[email protected]"></property>
    <property name="car">
        <bean class="com.oylong.bean.Car">
            <property name="name" value="测试车"></property>
        </bean>
    </property>
</bean>

输出如下:

Person{name='oyLong', age=15, email='[email protected]', car=Car{name='测试车', color='null', price=null}}
false

注:这个内部创建的bean对象不能通过ioc容器获取到,只能在内部使用

### 3.集合赋值

更改Person对象:

public class Person {
    private String name;
    private Integer age;
    private String email;
    private Car car;
    private List list;
    private Map map;
    private Properties properties;
    ......
}

bean配置如下:

<bean id="person3" class="com.oylong.bean.Person">
    <property name="age" value="15"></property>
    <property name="name" value="oyLong"></property>
    <property name="email" value="[email protected]"></property>
    <property name="car" ref="car"></property>

    <!-- list赋值-->
    <property name="list">
        <list>
            <value>中文</value>
            <value>英文</value>
            <ref bean="car"></ref>
        </list>
    </property>

    <!--    map赋值    -->
    <property name="map">
        <map>
            <entry key="m1" value="m1"></entry>
            <entry>
                <key>
                    <value>car</value>
                </key>
                <ref bean="car"></ref>
            </entry>
        </map>
    </property>
    <!--    properties赋值    -->
    <property name="properties">
        <props>
            <prop key="aa">aa</prop>
            <prop key="bb">bb</prop>
        </props>
    </property>
</bean>

运行结果

Person{name='oyLong', age=15, email='[email protected]', car=Car{name='大卡车', color='绿色', price=2800}, list=[中文, 英文, Car{name='大卡车', color='绿色', price=2800}], map={m1=m1, car=Car{name='大卡车', color='绿色', price=2800}}, properties={aa=aa, bb=bb}}

4. 通过xml为JavaBean属性的属性赋值

如下xml

<bean id="person4" class="com.oylong.bean.Person">
    <property name="age" value="15"></property>
    <property name="name" value="oyLong"></property>
    <property name="email" value="[email protected]"></property>
    <property name="car" ref="car"></property>
    <property name="car.name" value="大车"></property>
</bean>

其中car.name就是将引用的car的名字更改为相应的值,需要注意的就是,假如更改了,那么本身在ioc容器里面的这个car对象的属性也会随之更改

如下:

Person{name='oyLong', age=15, email='[email protected]', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null}

car的名字变成了大车

5. xml中bean标签的继承

通过parent属性就可以直接将对应的属性的值直接拷贝一份,然后将需要更改的再使用property标签更改即可,如下:

<bean id="person4" class="com.oylong.bean.Person">
    <property name="age" value="15"></property>
    <property name="name" value="oyLong"></property>
    <property name="email" value="[email protected]"></property>
    <property name="car" ref="car"></property>
    <property name="car.name" value="大车"></property>
</bean>

<bean id="person5" class="com.oylong.bean.Person" parent="person4">
    <property name="name" value="parent=p4"></property>
</bean>

输出如下:

Person{name='parent=p4', age=15, email='[email protected]', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null}

创建单实例和多实例的bean对象

singleton: 单实例(默认)

  • 容器启动时就创建好对象,保存在容器中
  • 任何时候获取都是之前创建好的对象

prototype:多实例

  • 容器启动时默认不创建对象
  • 每次获取时再创建对象
  • 每次获取都会创建一个新的对象

通过设置bean标签的scope属性即可设置不同的创建方式

<bean id="person4" class="com.oylong.bean.Person" scope="singleton">
    <property name="age" value="15"></property>
    <property name="name" value="oyLong"></property>
    <property name="email" value="[email protected]"></property>
    <property name="car" ref="car"></property>
    <property name="car.name" value="大车"></property>
</bean>

自动装配

默认情况下,xml中设置的autowired属性的值为default/no,即不自动装配,不为指定的属性赋值

  • byName

    以属性名作为id,去容器中找到这个组件,为其赋值,找不到则为null

    <bean id="car" class="com.oylong.bean.Car">
        <property name="name" value="大卡车"></property>
        <property name="color" value="绿色"></property>
        <property name="price" value="2800"></property>
        </bean>
    
        <bean id="person6" class="com.oylong.bean.Person" autowire="byName">
            <property name="age" value="15"></property>
            <property name="name" value="oyLong"></property>
            <property name="email" value="[email protected]"></property>
            </bean>

    如上xml中,由于Person类中的Car属性的属性名为car,所以将在ioc容器中去寻找一个id为car的组件,找到了之后为其赋值

    输出:

    Person{name='oyLong', age=15, email='[email protected]', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null}
  • byType

    以属性的类型为依据去ioc容器寻找组件,如果容器中有多个同类型的组件,那么就会报异常,没找到则赋值为null

  • constructor

    按照构造器进行赋值

    1. 按照有参构造器的类型进行装配(在ioc中寻找指定类型然后通过构造器赋值)
    2. 如果按照类型找到了多个,参数名作为id,继续装配,找不到就为null
    3. 如果有List集合,且在容器中有多个匹配的类型,那么它们会被封装进List
    4. 基本类型不会自动装配
    5. 不会报错

SPEL

通过注解将组件加入ioc容器

Controller

控制器:推荐给控制器层的组件加这个注解

Service

业务逻辑:推荐给业务逻辑层的组件加这个注解

Repository

持久层(dao层):给数据库层(持久化,dao)的组件添加这个注解

Component

给除以上外的组件添加这个注解

component-scan 标签

在xml中配置标签

<context:component-scan base-package="com.oylong"/>

只有配置了component-scan后,才能扫描指定的目录下的注解,才能将相应的组件添加至ioc容器

使用注解之前,需要先导入Spring的aop包

默认情况下添加的组件的id为类名首字母小写

更改id则在相应的注解后面添加即可,如:

@Service("myGoodService")
@Scope(value = "prototype")
public class GoodService{
}

同样,如果需要更改组件的作用域,添加Scope注解在设置其value值即可

@AutoWired注解

Spring通过@AutoWired这个注解自动为属性赋值(从ioc容器中去查找相应的值),如下代码:

@Controller
public class CarServlet {
    @Autowired
    private CarService carService;
}

将自动从ioc容器中找到carService并为其赋值

@AutoWired注入流程

  1. 首先按照类型去ioc容器中寻找对应的组件,类似:

    bookService = ioc.getBean(BookService.class);
  2. 如果没找到,会抛异常
  3. 如果找到多个,按照变量名作为id继续进行匹配(BookService[bookService])
  4. 如果没有匹配的变量名,就抛异常
  5. 可以使用@Qualifier注解进行指定id匹配

    @Controller
    public class CarServlet {
        @Autowired
        @Qualifier("carService")
        private CarService carServiceTmp;
    }

    上面的代码就是通过@Qualifier这个注解,让carServiceTmp这个变量去指定装配id为carService的组件

  6. 如果使用@Qualifier注解指定id还是没找到,那还是会抛异常

@Resource注解

这个注解的功能与@Autowired注解功能类似,也是用于自动注入,如下:

@Controller
public class CarServlet {
    @Resource
    private CarService carService;
}

@AutoWired是最强大的,由Spring规定。@Resource是j2ee规定的Java标准,与之类似的还有一个@Inject注解,由EJB规定。

  • @Resource: 因为是java标准,所以拓展性更强,切换不同的容器框架,都可使用,而@AutoWired则不行,但是目前市面上的容器基本上也就只有Spring一家。默认按照id进行匹配,如果没有匹配的id,则会进行类型匹配,也可以单独指定其name属性的值去按指定名称进行注入
Last modification:September 14, 2020
If you think my article is useful to you, please feel free to appreciate