Spring IOC 控制反转--源码级详细解读

SpringIOC

1、IOC(Invervion Of Control):控制反转

1.1控制:资源的获取方式

1、主动式:(每次需要资源的时候就自己new一个对象,然后用对象去调用)
Person{
Animal cat = new Animal();//简单对象可以这样去创建
Car car = new Car();//这样new出来的car是没有任何功能的,car是一个比较复杂的对象,由很多零件构成,对于这样的对象,再用new对象的方法,就不合适了
}

**2、被动式:**资源的获取不是我们自己创建,交给容器(IOC)创建即可
Person{
Animal cat;
Public void test{
cat.eat;
}
}

1.2、容器:

管理所有组件(有功能的类),如果Person和Animal都受容器的管理,容器可以自动探查出哪些组件(类)需要另一些组件(类),容器会创建好需要的对象(Animal),并把对象赋值过去

控制反转:从以前的new对象到现在的接收对象
婚介所:只需要把你需要什么条件的对象告诉婚介所人员,他们就会帮你找到符合的。

1.3、DI(Dependency Injection):依赖注入

容器能知道哪些组件(类)运行的时候需要另外哪些组件,容器通过反射的形式,将容器中准备好的对象注入(反射赋值)到需要这个对象的类中。
以前是自己new对象,现在交给容器

2、框架编写流程:

1、导包
核心容器:

spring-expression-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar

Spring运行的时候还依赖一个日志包,没有就会报错

commons-logging-1.1.3.jar

2、写配置

Spring的配置文件中,集合了所有Spring的IOC容器管理的所有组件

3、测试

3.1、测试person类

1、创建一个简单的person类,并生成get,set,tostring方法
在这里插入图片描述

2、创建一个xml文件,要注意是Spring的xml
在这里插入图片描述

3、在xml文件里开始配置
在这里插入图片描述

4、配置好的person类

<?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">
   <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--注册一个Person对象,Spring会自动创建这个Spring对象-->
<!--
一个bean标签对应一个组件(类)
class:要注册的对象,写对象的全类名
id:对象的唯一标识
-->
<bean id="person1" class="com.fxp.bean.Person">
    <!--使用property为person对象可以为person对象的属性赋值,name代表的是属性名,并不是person的属性。
    name="name":这是指定属性名
    value="zhangsan":为属性赋值
    -->
    <property name="name" value="zhangsan"></property>
    <property name="age" value="23"></property>
    <property name="email" value="[email protected]"></property>
    <property name="gender" value="男"></property>

</bean>

5、创建一个测试类,IOCtest,使用junit测试

遇到的问题:

1、idea如何创建junit类
选中要创建的那个类的类名,右键选中GO TO ,选中TEST,然后添加@test注解
在这里插入图片描述
在这里插入图片描述

2、注解无效可能是junit的版本太低

选中file–setting–搜索javacompiler–将版本设置到较新的即可

6、源码:

Person类:

package com.fxp.bean;

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

public String getName() {
    return name;
}

public Integer getAge() {
    return age;
}

public String getGender() {
    return gender;
}

public String getEmail() {
    return email;
}

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

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

public void setGender(String gender) {
    this.gender = gender;
}

public void setEmail(String email) {
    this.email = email;

}

@Override
public String toString() {
    return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", gender='" + gender + '\'' +
            ", email='" + email + '\'' +
            '}';
}

}

IOCtest类:

package com.fxp.test;
import com.fxp.bean.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.junit.Assert.*;

/**
 *
 * 从容器中拿到这个组件
 */
public class IOCtest {
@Test
public void test() {
    // ApplicationContext:代表IOC容器
    //ClassPathXmlApplicationContext:代表当前应用的xml配置文件在classpath(类路径)下
    //根据Spring的xml文件得到ioc容器对象
    ApplicationContext ioc = new ClassPathXmlApplicationContext("11.xml");
            //根据xml配置的person的id拿到person对象
   Person bean = (Person) ioc.getBean("person1");
    System.out.println(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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--注册一个Person对象,Spring会自动创建这个Spring对象-->
<!--
一个bean标签对应一个组件(类)
class:要注册的对象,写对象的全类名
id:对象的唯一标识
-->
<bean id="person1" class="com.fxp.bean.Person">
    <!--使用property为person对象可以为person对象的属性赋值,name代表的是属性名,并不是person的属性。
    name="name":这是指定属性名
    value="zhangsan:为属性赋值
    -->
    <property name="name" value="zhangsan"></property>
    <property name="age" value="23"></property>
    <property name="email" value="[email protected]"></property>
    <property name="gender" value="男"></property>

</bean>
</beans>

3.2、要注意的问题:

1、xml文件要放在src目录下,src是源码包开始的路径,称为类路径的开始
所有源码包里的东西都会被合并放在类路径里面。

普通java文件:/bin/文件夹下
Web项目:/WEB-INF/classes

2、先导包,再创建配置文件

3.3、person对象什么时候被创建

1、问题
查看person到底是什么时候被创建的?
创建ioc容器的时候还是获取person对象的时候?

2、证明:
先把获取person对象注释掉,这样就只剩下了创建ioc容器,再运行test方法

public class IOCtest {
@Test
public void test() {
    // ApplicationContext:代表IOC容器
    //ClassPathXmlApplicationContext:代表当前应用的xml配置文件在classpath(类路径)下
    //根据Spring的xml文件得到ioc容器对象
    ApplicationContext ioc = new ClassPathXmlApplicationContext("11.xml");
            //根据xml配置的person的id拿到person对象
    
   //Person bean = (Person) ioc.getBean("person1");
    //System.out.println(bean);
}

Person的无参构造器里的内容已经输出到控制台了,说明person类是在创建ioc容器的时候就已经被创建好了
在这里插入图片描述

结论:容器中的对象,是在创建ioc容器的时候就已经创建好了。

3.4、对象在容器里是单例还是多例

1、在xml文件里再创建一个对象
在这里插入图片描述

2、再次运行test方法,看看无参构造器会被打印几次
在这里插入图片描述

很明显,无参构造器被调用了2次。

3、再获取一次person1,判断他们是否是同一个对象
在这里插入图片描述

输出的是true,说明是同一个对象
在这里插入图片描述

4、结论:同一个组件在ioc容器中是单例的

3.5、Property标签的赋值:

1、Property是通过set方法进行赋值的,例如setName就是先把set去掉,然后将N改成n即可。
由此可知,javabean的属性名是由set方法所决定的,就是将set去掉,然后把前面的大写字母改成小写字母

2、证明:
将setName改为setAame
在这里插入图片描述

3、查看xml文件,这里的name属性已经报错了
在这里插入图片描述

4、当把name属性改为aame的时候,又显示正确了
在这里插入图片描述

5、建议:所有的get,set方法都自动生成,不要乱改

6、结论:javabean的属性名是由set方法决定的

3.6、通过类的类型获取bean的实例

1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--注册一个Person对象,Spring会自动创建这个Spring对象-->
<!--
  通过类型获取bean的实例对象
-->
<bean  class="com.fxp.bean.Person">

    <property name="name" value="zhangsan"></property>
    <property name="age" value="23"></property>
    <property name="email" value="[email protected]"></property>
    <property name="gender" value="男"></property>

</bean>

</beans>

IOCtest:

@Test
public void test2(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("22.xml");
//ioc的getBean方法有很多种,其中一种就是参数为class的,里面输入要获取的那个//类的类型即可
Object bean = ioc.getBean(Person.class);
System.out.println(bean);
}

2、问题:如果ioc容器中有两个person类
在这里插入图片描述

3、再次运行test2方法,报如下错误

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 	[com.fxp.bean.Person] is defined: expected single matching bean but found 2: com.fxp.bean.Person#0,person2

意思是:person这个类不是唯一的类,找到了两个person类

结论:如果ioc容器中,这个类型的bean有多个,根据类型去创建就会报错。

3.7、通过构造器为bean的属性赋值

1、先给person多加一个有参构造器
Person源码:

package com.fxp.bean;

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

   //创建一个有参构造器,根据有参构造器为bean的属性赋值
public Person(String name, Integer age, String gender, String email) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.email = email;
System.out.println("有参构造器被调用.....");

}

//创建一个无参构造器,看看Person类是什么时候创建的
public Person() {
    //创建person对象时,输出这句话
    System.out.println("已经创建好了....");

}

public String getName() {
    return name;
}

public Integer getAge() {
    return age;
}

public String getGender() {
    return gender;
}

public String getEmail() {
    return email;
}

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

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

public void setGender(String gender) {
    this.gender = gender;
}

public void setEmail(String email) {
    this.email = email;

}

@Override
public String toString() {
    return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", gender='" + gender + '\'' +
            ", email='" + email + '\'' +
            '}';
}

}

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

<!--注册一个Person对象,Spring会自动创建这个Spring对象-->
<!--
调用有参构造器进行创建对象
如果name和value的值都写的话,参数的顺序可以随便写,如果name的值不写,那value的值就要严格按照有参构造器	里的顺序进行填写
扩展:如果name不写,value的位置也没有按照有参构造函数的来写,则可以在value的后面加一个index的属性,index类似于数组的角标,也是从0开始,有参构造器里的第一个参数的角标为0,依次类推。

-->
<bean  id="person1" class="com.fxp.bean.Person">
    <!--Person(String name, Integer age, String gender, String email)
    写4个constructor-arg就代表是4个参数的有参构造器
    -->
    <constructor-arg name="name" value="ouyang"></constructor-arg>
    <constructor-arg name="age" value="23"></constructor-arg>
    <constructor-arg name="email" value="[email protected]"></constructor-arg>
    <constructor-arg name="gender" value="女"></constructor-arg>
</bean>

</beans>

IOCtest源码:

/**
 * 通过有参构造器为javabean赋值,拿到对象
 *
 */
@Test
public void test3(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("33.xml" );
Object bean = ioc.getBean("person1");
System.out.println(bean);

}

运行结果:
在这里插入图片描述

3.8、通过P名称空间为bean赋值

1、IDEA中想引入spring的标签,要先在xml文件的首部添加:

xmlns:context="http://www.springframework.org/schema/context"

在这里插入图片描述

引入p标签后,可以当成一个属性使用
在这里插入图片描述

Xml源码:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   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">

<!--
  通过p名称空间为bean赋值,p名称空间需要导入
  名称空间是为了防止标签重复
  例如:这里的name标签就重复了,有书名和作者名,这个时候就用到了p标签,可以在name的前面加个标签,书名叫b:name
  作者名叫a:name
  <book>
        <b:name>西游记</b:name>
        <price></price>
        <author
                <a:name>吴承恩</a:name>
                <gender>男</gender>
        </author>
  </book>
-->
<bean id="person1" class="com.fxp.bean.Person"
      p:age="23" p:email="ouyang......" p:gender="nv" p:name="ooooooo">
</bean>

</beans>

Ioctest源码:

/**
 * 通过P名称空间为bean赋值
 */
@Test
public void test4(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("44.xml");
Object bean = ioc.getBean("person1");
System.out.println(bean);

}

运行结果:
在这里插入图片描述

4、复杂属性赋值

之前都是为基本数据类型赋值,可以直接用property标签进行赋值,现在把对象作为属性进行赋值

4.1、为自定义对象类型进行赋值

1、源码:
Person源码:

package com.fxp.bean;

import java.awt.*;
import java.util.List;
import java.util.Map;
import java.util.Properties;

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

//添加一些比较复杂的类型
private Car car;
private List<Book> books;
private Map<String,Object> map;
private Properties properties;

   //创建一个有参构造器,根据有参构造器为bean的属性赋值
public Person(String name, Integer age, String gender, String email) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.email = email;
    System.out.println("有参构造器被调用.....");
}

//创建一个无参构造器,看看Person类是什么时候创建的
public Person() {
    //创建person对象时,输出这句话
    System.out.println("已经创建好了....");

}


public String getName() {
    return name;
}

public Integer getAge() {
    return age;
}

public String getGender() {
    return gender;
}

public String getEmail() {
    return email;
}

public void setName(String name) {
    this.name = name;
}
public void setAge(Integer age) {
    this.age = age;
}
public void setGender(String gender) {
    this.gender = gender;
}
public void setEmail(String email) {
    this.email = email;
}
public Car getCar() {
    return car;
}

public List<Book> getBooks() {
    return books;
}
public Map<String, Object> getMap() {
    return map;
}
public Properties getProperties() {
    return properties;
}
public void setCar(Car car) {
    this.car = car;
}
public void setBooks(List<Book> books) {
    this.books = books;
}
public void setMap(Map<String, Object> map) {
    this.map = map;
}
public void setProperties(Properties properties) {
    this.properties = properties;
}
@Override
public String toString() {
    return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", gender='" + gender + '\'' +
            ", email='" + email + '\'' +
            ", car=" + car +
            ", books=" + books +
            ", map=" + map +
            ", properties=" + properties +
            '}';
}
}

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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--为各种属性赋值,复杂类型的赋值-->

    <bean id="car" class="com.fxp.bean.Car">
        <property name="name" value="宝马"></property>
        <property name="color" value="red"></property>
        <property name="price" value="50000"></property>
    </bean>
    <bean id="person" class="com.fxp.bean.Person">
        <!--所有复杂的类型都是在property标签体里面进行赋值-->
        <property name="car" ref="car">
            <!--car是一个类:
            1、在xml中类就是一个组件,一个bean代表一个类,所以car要用bean包裹起来
            2、xml中如果本来就定义好了一个car的bean,那在这里可以直接进行引用,ref就是引用的意思,ref="car的id"
            -->
        </property>
    </bean>
</beans>

Ioctest源码:

@Test
public void  test5(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("55.xml");
Person bean = (Person) ioc.getBean("person");
System.out.println(bean.getCar());
}

运行结果
在这里插入图片描述

2、为一个对象赋值有两种方式:
第一种:
因为在xml里面,一个bean就代表一个对象,为对象赋值的时候就可以使用bean标签来实现,例如:在person里面再定义个car的bean,然后为car的属性赋值。
在这里插入图片描述

第二种:
在xml文件中单独写一个car的bean,在person里就可以直接使用ref来将car引用进去
在这里插入图片描述

4.2、为List进行赋值

1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans 							http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--为List属性赋值-->
<!--这是外部的book类的bean-->
    <bean id="book2" class="com.fxp.bean.Book">
    <property name="authonr" value="夫斯基"></property>
    <property name="bookname" value="炼钢"></property>
</bean>
<bean id="person" class="com.fxp.bean.Person">
    <!--如何为List属性赋值-->
    <property name="books">
        <!--相当于books = new Arraylist<Book>();-->
        <list>
            <!--在List标签体中添加每一个list
            注意:内部bean的id不能被获取到,只能内部使用
            -->
            <bean id="book1" class="com.fxp.bean.Book">
                <!--第一本书,正常赋值-->
                <property name="bookname" value="西游记"></property>
                <property name="authonr" value="吴承恩"></property>
            </bean>
            <!--第二本书,使用ref引用外部的book-->
            <ref bean="book2"/>
        </list>
    </property>

</bean>
</beans>

Ioctest:

@Test
public void test6(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("66.xml");
Person bean = (Person) ioc.getBean("person");
System.out.println(bean.getBooks());
}

4.3、为Map赋值

1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <!--Map赋值-->
<bean id="book1" class="com.fxp.bean.Book">
    <property name="bookname" value="西游记"></property>
    <property name="authonr" value="吴承恩"></property>
</bean>

<bean id="car" class="com.fxp.bean.Car">
    <property name="color" value="blue"></property>
    <property name="price" value="8000000"></property>
    <property name="name" value="aaa"></property>
</bean>

<bean id="person" class="com.fxp.bean.Person">
   <property name="map">
       <!--相当于map = new LinkedHashMap<>();-->
       <map>
           <!--map里面存的是键值对,一个entry就代表一个键值对-->
           <entry key="key1" value="zhangsan"></entry>
           <entry key="key2" value="lisi"></entry>
           <!--引用外部的book-->
           <entry key="key3" value-ref="book1"></entry>
           <!--引用外部的car-->
           <entry key="key4" value-ref="car"></entry>
           <!--内部的car-->
           <entry key="key5">
               <bean class="com.fxp.bean.Book">
                   <property name="bookname" value="东游记"></property>
                   <property name="authonr" value="55555"></property>
               </bean>
           </entry>
       </map>
   </property>
</bean>

</beans>

Ioctest:

@Test
public void test7(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("77.xml");
Person bean = (Person) ioc.getBean("person");
System.out.println(bean.getMap());
}

3、结论:其实各种类型赋值的时候,是可以相互嵌套的,只要记住各种类型的赋值方式即可

4.4、为properties赋值

1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="person" class="com.fxp.bean.Person">
    <property name="properties">
        <!--pro = new Properties();properties全部都是kv,而且都是String类型-->
        <props>
            <!--因为kv都是字符串,所以值可以直接写在标签体中-->
            <prop key="username">root</prop>
            <prop key="password">123456</prop>
            <prop key="utils">jdbc</prop>
        </props>
    </property>
</bean>
</beans>

Ioctest:

@Test
public void test8(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("88.xml");
Person bean = (Person) ioc.getBean("person");
System.out.println(bean.getProperties());

}

4.5、Util名称空间创建集合类型的bean方便引用

1、源码:
Xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   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-4.0.xsd
">

<!--util名称空间创建集合类型的bean,方便使用-->
<bean id="person" class="com.fxp.bean.Person">
    <property name="map" ref="mymap"></property>
    <property name="name" value="zhangsan13"></property>
    <property name="age" value="45"></property>

</bean>
<util:map id="mymap">
    <!--注意:这里不能再加map标签,因为util:map已经等于new了一个map,只要在添加元素即可。
    如果再加一个map标签,就以为着这个map集合里,有一个元素是map集合
    -->
    <entry key="key1" value="zhangsan"></entry>
    <entry key="key2" value="lisi"></entry>
</util:map>
</beans>

Ioctest:

@Test
public void test9(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("9util.xml");
Person bean = (Person) ioc.getBean("person");
System.out.println(bean);
}

运行结果
在这里插入图片描述

小结:map或者list这种类型不是自定义的类型,它没有id这个选项,所以当别人想从外部引用他们的时候,就无法引用,很麻烦,所以这个时候用一个util名称空间,就可以让它有id这个选项,引用起来就方便很多。

4.6、级联属性

(属性的属性:person类里的car里的价格)
1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


<!--级联属性,属性的属性(person类的里的car里的价格)-->

<bean id="car" class="com.fxp.bean.Car">
    <property name="name" value="宝马"></property>
    <property name="color" value="red"></property>
    <property name="price" value="50000"></property>
</bean>

<bean id="person" class="com.fxp.bean.Person">
    <property name="car" ref="car"></property>
    <!--直接用car.price进行赋值-->
    <property name="car.price" value="100000"></property>

</bean>

</beans>

Ioctest:

@Test
public void test10(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("union.xml");
Person bean = (Person) ioc.getBean("person");
System.out.println(bean.getCar());
}

2、注意:级联属性用的是ref引用,引用和原来的是同一个对象,如果改了某个属性,那原来的那个属性的值也会改变。

5、其他

5.1、通过继承实现bean配置信息的重用

1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="person1" class="com.fxp.bean.Person">

    <property name="name" value="zhangsan"></property>
    <property name="age" value="23"></property>
    <property name="email" value="[email protected]"></property>
    <property name="gender" value="男"></property>

</bean>
 <!--这个人除了名字不一样,其他属性都一样-->
<!--parent:指定当前bean继承于哪个,仅仅只是配置信息的继承,他们并不是父类与子类的关系,而且他们都是person类,不可能存在子父关系-->
<!--在property中,只需要修改不同的属性即可,其他会直接继承过来,parent里面填写的是要继承的那个bean的id值-->
<bean id="person2" class="com.fxp.bean.Person" parent="person1">
    <property name="name" value="lisi"></property>

</bean>

</beans>

Ioctest:

@Test
public void test11(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("jicheng.xml");
Object bean = ioc.getBean("person1");
Object bean1 = ioc.getBean("person2");
System.out.println(bean);
System.out.println(bean1);
}

运行结果:
在这里插入图片描述

5.2、通过abstract创建一个模板bean

1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--abstract创建一个bean模板,abstract="true":这个bean不能用来创建实例,只能用来继承-->
<bean id="person1" class="com.fxp.bean.Person" abstract="true">

    <property name="name" value="zhangsan"></property>
    <property name="age" value="23"></property>
    <property name="email" value="[email protected]"></property>
    <property name="gender" value="男"></property>

</bean>

<bean id="person2" class="com.fxp.bean.Person" parent="person1">
<property name="name" value="aaaaa0"></property>

</bean>
</beans>

2、因为id为person1的bean是abstrac修饰的,不能实例化,所以当我们在ioctest获取person1的时候,会报错,如下

@Test
public void test12(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("abstract.xml");
Object bean = ioc.getBean("person1");
System.out.println(bean);
}

运行结果:
在这里插入图片描述

3、现在换成person2,如下

@Test
public void test12(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("abstract.xml");
Object bean = ioc.getBean("person2");
System.out.println(bean);
}

运行结果:
在这里插入图片描述

5.3、Bean之间的依赖

1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--bean之间的依赖-->
<!--这三者之间的创建有无先后顺序?在car和book的无参构造器里都输入一句话-->
<!--bean的先后顺序决定了对象的创建顺序-->
<!--bean之间的依赖:depends-on:(只是改变创建的先后顺序)-->
<!-- depends-on="person:意思就是car对象会在person对象之后创建-->

<bean id="car" class="com.fxp.bean.Car" depends-on="person"></bean>
<bean id="person" class="com.fxp.bean.Person"></bean>
<bean id="book" class="com.fxp.bean.Book"></bean>
</beans>

Ioctest:

@Test
public void test13(){
    //什么都不做,就只是创建一个ioc容器,因为在容器里的类会在ioc容器被创建的时候就会被创建,这样就可以知道他们创建的先后顺序
    ApplicationContext ioc =new ClassPathXmlApplicationContext("dependency.xml");
}

运行结果:
在这里插入图片描述

然后把xml里的person和car的位置换一下

<bean id="car" class="com.fxp.bean.Car"></bean>
<bean id="person" class="com.fxp.bean.Person"></bean>
<bean id="book" class="com.fxp.bean.Book"></bean>

再看结果
在这里插入图片描述

也可以不用手动换顺序,加一个depends-on即可,car会在person创建之后创建

<bean id="car" class="com.fxp.bean.Car" depends-on="person"></bean>

结论:由此可见对象被创建的顺序跟xml里面bean的配置顺序有关。

5.4、创建单例和多例的bean

1、Scope属性
在这里插入图片描述

2、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--创建bean的单例和多实例-->
   <!--bean的作用域:指定bean是否是单实例,默认是单例-->
<!--scope属性:代表作用域
prototype:多实例
1、容器启动默认不会去创建多实例bean
2、获取的时候才创建bean实例
3、每次获取都会创建一个的对象
singleton:单实例,默认
1、单实例bean在容器启动完成之前就已经创建好了
2、任何时候获取的同一个组件都是同一个对象
-->
<bean id="book" class="com.fxp.bean.Book" scope="prototype">

</bean>
</beans>

Ioctest:

@Test
public void test14(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("singlemany.xml");
//获取两次book
Object bean = ioc.getBean("book");
Object bean1 = ioc.getBean("book");
System.out.println(bean==bean1);

}

运行结果:
在这里插入图片描述

3、Book的构造函数被调用了两次,bean和bean1不是同一个对象,证明是多实例的

5.5、静态工厂和实例工厂

5.5.1、静态工厂

1、源码:
飞机源码:

package com.fxp.bean;
public class Airplane {
private String head;
private String body;
private String tail;
private Integer num;//载客量
private String jzName;//机长名字
public String getHead() {
    return head;
}

public String getBody() {
    return body;
}

public String getTail() {
    return tail;
}

public Integer getNum() {
    return num;
}

public String getJzName() {
    return jzName;
}

public void setHead(String head) {
    this.head = head;
}

public void setBody(String body) {
    this.body = body;
}

public void setTail(String tail) {
    this.tail = tail;
}

public void setNum(Integer num) {
    this.num = num;
}
public void setJzName(String jzName) {
    this.jzName = jzName;
}
@Override
public String toString() {
return "Airplane{" +
        "head='" + head + '\'' +
        ", body='" + body + '\'' +
        ", tail='" + tail + '\'' +
        ", num=" + num +
        ", jzName='" + jzName + '\'' +
        '}';
}
}

静态工厂源码:

package com.fxp.Factory;
import com.fxp.bean.Airplane;
import javax.xml.stream.XMLOutputFactory;
/**
 * 静态工厂
 * 静态工厂其实就是,不创建工厂本身的类,写一个静态方法用静态工厂名.工厂方法名,去调用这个工厂
 * 具体的创建airplane的类的工作都放在静态方法里面
 */
public class staticFactory {

//staticFactory.getAirPlane()
public static Airplane getAirPlane(String jzName){
    System.out.println("静态工厂造飞机......");

    Airplane airplane = new Airplane();
    airplane.setBody("机身");
    airplane.setHead("机首");
    airplane.setNum(100);
    airplane.setTail("机尾");
    airplane.setJzName(jzName);
    return airplane;
}
}

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

<!--静态工厂方法和实例工厂方法创建bean,FactoryBean-->
<!--bean的创建默认就是框架利用反射new出来的bean实例-->
<!--有一个飞机的类,假设每架飞机除了机长的名字不一样,其他都一样

以前我们可以用继承,只需要修改机长的名字即可,把其他一样的属性都继承过来即可
现在可以用工厂模式,工厂会帮我们创建对象
AirPlane ap = AirPlaneFactory.getAirPlane(String jzName);
AirPlaneFactory这个类会帮我们创建对象,这个类就是工厂。

静态工厂:工厂本身不用创建对象,通过静态的方法调用,对象 = 工厂类.工厂方法名();
实例工厂:工厂本身需要创建对象,工厂类 工厂对象 = new 工厂类();
         工厂对象.工厂方法():工厂对象.getAirPlane("张三");
-->
   <!-- <bean id="airplane1" class="com.fxp.bean.Airplane">

</bean>-->

<!--第一种:静态工厂配置(不需要创建工厂对象)-->
<bean id="airplane1" class="com.fxp.Factory.staticFactory"
      factory-method="getAirPlane">
    <!--如果没有指定factorymethod,就会为staticFactory创建对象,factorymethod的作用就是指定哪个方法是工厂方法-->
    <!--getAirPlane(String jzName),这个方法需要传入一个参数,可以用constructor进行传参-->
    <constructor-arg name="jzName" value="张三"></constructor-arg>
</bean>

</beans>

Ioctest:

@Test
public void test15(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("Factorybean.xml");
//测试静态工厂
Object bean = ioc.getBean("airplane1");
System.out.println(bean);

运行结果:
在这里插入图片描述

2、总结:配置静态工厂:

1、先创建一个需要创建对象的类(airplane),写入一些基本信息

2、创建一个静态工厂类,创建一个静态方法,在静态方法里面new一个需要创建的那个对象的类,然后调用目标类的set方法,为其赋值,最后返回目标类的类型

3、Xml的配置:以前bean里面的class配置的都是目标类的全类名,现在class里需要写的是静态工厂的全类名,然后指定一个工厂方法,如果需要传参,就在标签体里面选择传参的constractor属性即可。

5.5.2、实例工厂:

1、源码:
实例工厂源码:

package com.fxp.Factory;
import com.fxp.bean.Airplane;
/**
 * 实例工厂
 */
public class instanceFactory {

   // new  instanceFactory().getAirPlane
    public Airplane getAirPlane(String jzName){
    System.out.println("实例工厂造飞机.....");

    Airplane airplane = new Airplane();
    airplane.setBody("机身");
    airplane.setHead("机首");
    airplane.setNum(100);
    airplane.setTail("机尾");
    airplane.setJzName(jzName);
    return airplane;
}
}

Xml:

<!--第二种,实例工厂配置(工厂本身需要创建对象)-->
<bean id="insair" class="com.fxp.Factory.instanceFactory"></bean>
<!--实例工厂配置:需要配置两个bean,一个是实例工厂的bean,一个是airplane本身的bean,
但是不是airplane本身new自己创建的对象,而是通过实例工厂创建的对象,所以需要让实例工厂和airplane类
产生联系,这个联系就是factory-bean,这个属性是指定当前对象创建使用哪个工厂
-->

<!--注意:
实例工厂配置,
1、都是先配置出实例工厂对象
2、配置我们要创建的对象使用哪个工厂创建
1、factory-bean指定哪个工厂
2.factory-method指定工厂方法
-->
<bean id="airplane2" class="com.fxp.bean.Airplane"
factory-bean="insair" factory-method="getAirPlane">
<constructor-arg name="jzName" value="zhangsan"></constructor-arg>
</bean>

Ioctest:

@Test
public void test15(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("Factorybean.xml");
//测试静态工厂
Object bean = ioc.getBean("airplane1");
System.out.println(bean);

//测试实例工厂
Object bean1 = ioc.getBean("airplane2");
System.out.println(bean1);
}

运行结果:
在这里插入图片描述

5.6、FactoryBean

1、源码:
MyFactoryBeanImpl:

package com.fxp.Factory;
import com.fxp.bean.Book;
import org.springframework.beans.factory.FactoryBean;

import java.util.UUID;

/**
 * implements Factory<?>这里最好添加泛型,这个泛型也就是需要创建实例的类
 * 实现了FactoryBean接口的类都是spring认识的工厂类
 * spring会自动调用工厂方法创建实例
 *步骤:
 * 1、编写一个FactoryBean的实现类
 * 2、在spring配置文件中进行注册
 */
public class MyFactoryBeanImpl implements FactoryBean<Book> {

/**
 * getObject:工厂方法,spring会自动调用
 * 返回创建的对象
 * @return
 * @throws Exception
 */
@Override
public Book getObject() throws Exception {
    System.out.println("MyFactoryBeanImpl创建了对象....");
    Book book = new Book();
    book.setAuthonr("夫斯基");
    book.setBookname(UUID.randomUUID().toString());
    return book;
}

/**
 * 返回创建的对象的类型,spring会自动调用这个方法来确认要确认的对象是什么类型
 * @return
 */
@Override
public Class<?> getObjectType() {
    return Book.class;
}

/**
 * 这是判断是单例吗
 * false:不是单例
 * true:是单例
 * @return
 */
@Override
public boolean isSingleton() {
    return false;
}
}

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

  <!--FactoryBean是Spring规定的一个接口,只要是这个接口的实现类,spring都认为是一个工厂-->
<!--FactoryBean不管是单实例还是多实例,都不是在ioc容器创建的时候创建实例,都是在获取的时候才创建实例-->

<bean id="myFactoryBean" class="com.fxp.Factory.MyFactoryBeanImpl"></bean>

</beans>

Ioctest:

@Test
public void test16(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("FactoryBean.xml");
Object bean = ioc.getBean("myFactoryBean");
System.out.println(bean);
}

运行结果:
在这里插入图片描述

5.7、Bean后置处理器(不重要)

1、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--创建带有生命周期的bean-->
<!--在book类里面创建一个init-method,用于下面测试后置处理器-->
<bean id="book" class="com.fxp.bean.Book" init-method="Myinitial"></bean>


<!--后置处理器:BeanPostProcessor-->
<!--Spring有一个接口,后置处理器,可以在bean初始化前后调用方法-->
<bean id="myBeanPostProcessor" class="com.fxp.bean.MyBeanPostProcessor">

</bean>
</beans>

MyBeanProcessor:

package com.fxp.bean;

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

public class MyBeanPostProcessor implements BeanPostProcessor {
/**
 * postProcessBeforeInitialization:初始化之前调用
 * Object bean:将要初始化的bean
 * String beanName:xml里面bean的id
 */
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("【"+beanName+"】bean将要调用初始化方法.....,这个bean是这样的"+bean);
    return bean;
}

/**
 * postProcessAfterInitialization:初始化方法之后调用
 */

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("【"+beanName+"】bean初始化方法结束..."+bean);
    return bean;
}
}

Ioctest:

@Test
public void test17(){
//ConfigurableApplicationContext这个是ApplicationContext的子接口
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("houzhi.xml");

}

运行结果:
在这里插入图片描述

5.8、Spring管理数据库连接池

1、添加jar包

c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar

在这里插入图片描述

不同的c3p0版本对应不同的mchange-commons,要注意版本问题

2、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--数据库的连接池是单例的,一个项目就一个连接池,正好可以用Spring来管理连接池-->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!--我们需要配置用户名,密码等,但是在连接池中这些属性已经存在,所以直接用property配置即可-->

    <property name="user" value="root"></property>

    <property name="password" value="fxp123456"></property>

    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>

    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
</beans>

Ioctest:

@Test
public void test18() throws SQLException {
ApplicationContext ioc = new ClassPathXmlApplicationContext("linkpool.xml");
//1、按照ID获取,还需要进行强转,注意:这里的Datasouce是java.sql包下的
DataSource bean = (DataSource) ioc.getBean("dataSource");
System.out.println(bean.getConnection());

//2、按照类型获取
//DataSource bean1 = ioc.getBean(DataSource.class);
//获取连接
//System.out.println(bean.getConnection());

}

运行结果:
在这里插入图片描述

从连接池里 拿到了连接。

5.9、引入外部配置文件

(以5.8的数据库为例,需要依赖context名称空间)
1、源码:
Properties配置文件(普通的file)

username=root
password=fxp123456
jdbcUrl=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.jdbc.Driver

Dbconf.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   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
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context.xsd
">

   <!--引入外部配置文件,依赖context名称空间,以dbconf.properties为例-->
<!-- <context:property-placeholder:加载外部配置文件-->
<!--classpath:固定写法,提示里就有,表示引用类路径下的一个资源-->
<context:property-placeholder location="classpath:dbconf.properties"></context:property-placeholder>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!--${外部配置文件里的key}:表示动态取出配置文件里的value-->
    <property name="user" value="${username}"></property>

    <property name="password" value="${password}"></property>

    <property name="jdbcUrl" value="${jdbcUrl}"></property>

    <property name="driverClass" value="${driverClass}"></property>
</bean>

注意:当我们引入名称空间的时候,必须引入这个名称空间的xsd解析文件。
参考:https://blog.csdn.net/MarkusZhang/article/details/104460748

例如:context名称空间:(这三个都是必不可少的)

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

Ioctest:

@Test
public void test19() throws SQLException {
ApplicationContext ioc = new ClassPathXmlApplicationContext("dbconf.xml");
DataSource bean = ioc.getBean(DataSource.class);
System.out.println(bean.getConnection());
}

运行结果:
在这里插入图片描述
没有拿到连接,运行停止了

2、原因:
我们在dbconf.xml里配置的文件里有一个选项

<property name="user" value="${username}"></property>

Username是spring里的关键字,所以当我们用这个进行获取的时候,并不能获取到我们配置文件里的username,而是获取到了关键字username里面的内容。

3、将dbconf.properties和dbconf.xml的username都改为username1
在这里插入图片描述
拿到了连接

4、username到底是什么
Username就是系统的用户名

5.10、基于XML的自动装配

5.10.1、autowire:byname

1、自动装配:为自定义类型自动赋值(重点在思想)
如果一个类里面自定义的类太多,每一个都要手动进行赋值的话,是很麻烦的,所以就用到了自动赋值。Autowire这个属性可以帮助自动赋值。
在这里插入图片描述

2、源码:
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="car" class="com.fxp.bean.Car">
    <property name="name" value="汽车"></property>
    <property name="color" value="红色"></property>
    <property name="price" value="6000000"></property>
</bean>

<!--为person里面的自定义类型属性赋值(手动赋值)-->
   <!-- <bean id="person" class="com.fxp.bean.Person">
    <property name="car" ref="car"></property>
</bean>-->

<!--自动赋值-->
<!--autowire:自动赋值,默认是default/no,不自动装配
autowire="byName":按照名字自动装配
    	private Car car:将属性名作为id去容器中找到这个组件,给他赋值
autowire="byType":按照类型自动装配
autowire="constructor":按照构造器自动装配

-->
<bean id="person" class="com.fxp.bean.Person" autowire="byName">
</bean>
</beans>

Ioctest:

@Test
public void test20(){

ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("auto.xml");
Object bean = ioc.getBean("person");
System.out.println(bean);
}

运行结果
在这里插入图片描述

3、结论

自动装配的时候,在同一个xml文件里,必须有自定义类的bean,比如car,然后person中的autowire:byname才能在这个xml文件里找到。

注意:autowire:byname是用属性名作为id去寻找的,所以属性名和id必须是一样的,否则会找不到。

5.10.2、autowire:bytype

1、源码
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="car" class="com.fxp.bean.Car">
    <property name="name" value="汽车"></property>
    <property name="color" value="红色"></property>
    <property name="price" value="6000000"></property>
</bean>

<!--为person里面的自定义类型属性赋值(手动赋值)-->
   <!-- <bean id="person" class="com.fxp.bean.Person">
    <property name="car" ref="car"></property>
</bean>-->

<!--自动赋值-->
<!--autowire:自动赋值,默认是default/no,不自动装配
autowire="byName":按照名字自动装配
    private Car car:将属性名作为id去容器中找到这个组件,给他赋值;如果属性名和id不一样,就会找不到,找不到就赋值为null

autowire="byType":按照类型自动装配,以属性的类型为依据去容器中查找到这个组件
    car = ioc.getBean(Car.class);

autowire="constructor":按照构造器自动装配

-->
<bean id="person" class="com.fxp.bean.Person" autowire="byType">
</bean>


</beans>

Ioctest:

@Test
public void test20(){

ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("auto.xml");
Object bean = ioc.getBean("person");
System.out.println(bean);
}

运行结果:
在这里插入图片描述

注意:如果按照类型进行自动装配,在同一个容器中(iocxml配置文件中),不能同时有两个相同的类,与之前的ioc.getBean(XXX.Class)同理,不然就会报错。

5.10.3、autowire:constructor

在person类加一个只有一个car参数的构造器

//autowire-constructor使用
public Person(Car car) {
this.car = car;
System.out.println("我是autowire的只有car参数的构造器....");
}

为了不影响效果,注释掉其他有参构造器

1、源码
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="car" class="com.fxp.bean.Car">
    <property name="name" value="汽车"></property>
    <property name="color" value="红色"></property>
    <property name="price" value="6000000"></property>
</bean>

<!--为person里面的自定义类型属性赋值(手动赋值)-->
   <!-- <bean id="person" class="com.fxp.bean.Person">
    <property name="car" ref="car"></property>
</bean>-->

<!--自动赋值-->
<!--autowire:自动赋值,默认是default/no,不自动装配
autowire="byName":按照名字自动装配
    private Car car:将属性名作为id去容器中找到这个组件,给他赋值;如果属性名和id不一样,就会找不到,找不到就赋值为null

autowire="byType":按照类型自动装配,以属性的类型为依据去容器中查找到这个组件
    car = ioc.getBean(Car.class);

autowire="constructor":按照构造器自动装配

-->
<bean id="person" class="com.fxp.bean.Person" autowire="constructor">
</bean>

</beans>

Ioctest:

@Test
public void test20(){

ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("auto.xml");
Object bean = ioc.getBean("person");
System.out.println(bean);
}

运行结果:
在这里插入图片描述

2、有参构造器赋值:

1、先按照有参构造器的参数类型进行赋值(找到就赋值),如果没有就装配null

2、如果类型找到了多个,那就以参数的名作为id继续匹配,找到就赋值,找不到就null

3、按照构造器,无论怎样不会报错(找到就赋值,找不到就null)

注意:自动装配仅限于自定义类型,基本类型无法进行自动装配,例如int类型,无法装配具体的数字,自定义类型,可以在容器中找,找到就装配,找不到就null

5.12、通过注解创建DAO,Service,controller

1、源码
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.xsd
">

<!--通过注解分别创建Dao。Service、controller
通过给bean上添加某些注解,可以快速的将bean加入到ioc容器中
某个类上添加任何一个注解,都可以将这个组件快速加入到ioc容器中
spring有四个注解:
@controller:推荐给控制器层(servlet)的注解
@Service:推荐给业务逻辑层的注解
@Repository:推荐给数据库层(持久化层,Dao层)的注解
@component:推荐给除上面三层以外的注解

注解可以随便加,推荐加的只是给程序员看的

使用注解加入到ioc容器的步骤:
1、给要添加的组件上标注解
2、告诉spring自动扫描加了注解的组件,依赖context名称空间
3、一定要导入aop的jar包,支持注解模式

-->
<!--context:component-scan:自动组件扫描
base-package="":扫描的基础包,把基础包及它下面的所有包的所有加了注解的类,自动扫描进ioc容器中
-->
<context:component-scan base-package="com.fxp"></context:component-scan>

</beans>

BookServlet:

package com.fxp.servelet;

import org.springframework.stereotype.Controller;

@Controller
public class BookServlet {
}

BookDao:

package com.fxp.Dao;

import org.springframework.stereotype.Repository;

@Repository
public class BookDao {
}

BookService:

package com.fxp.Service;

import org.springframework.stereotype.Service;

@Service
public class BookService {
}

Ioctest:

@Test
public void test22(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("DaoService.xml");
Object bean = ioc.getBean("bookDao");
Object bean1 = ioc.getBean("bookDao");
System.out.println(bean==bean1);
}

运行结果
在这里插入图片描述

结论:使用注解和使用bean生成的类都是单例的

**注意:
1、一定要加入aop的jar包,不然会报错,aop的jar包支持注解模式
2、一定要使用context名称空间,前面有讲怎么加入
3、以前bean里面可以加id,现在的id就是类名首字母小写

4、注解的id也可以手动改变,只要在注解的后面加上自己想要的名字即可
在这里插入图片描述

5、如果想让某个组件变成多实例的,只需要加一个@scope注解即可
在这里插入图片描述

5.13、context:include-filter

指定扫描包需要包含的类
1、源码:
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.xsd
">

  <!--使用context:include-filter指定扫描包时要包含的类,默认是全部扫描进容器-->
  <!--use-default-filters="":使用默认的过滤器规则
        true:使用默认的,也就是全部扫描进来,跟没写这个参数一样的效果
        false:禁用默认的规则,使用自己的规则
  -->
<context:component-scan base-package="com.fxp" use-default-filters="false">
    <!--指定只扫描哪些组件
    参数与exclude-filter一样
    如果想多几个,就多写几遍
    -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

</beans>

5.14、context:exclude-filter

指定扫描包时不包含的类
1、源码:
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.xsd
">

  <!--使用context:exclude-filter指定扫描包时不包含的类-->
<!--扫描时可以排除一些不要的组件-->
<context:component-scan base-package="com.fxp">
    <!--type="":指定排除规则
    例如:一般只用前面两种
    type="annotation":按照注解进行排除,标注了指定注解的组件不要
        type="":指定排除规则
        expression="":注解的全类名
    type="assignable":指定排除某个具体的类
        expression="":类的全类名

    ***不常用:
    type="aspectj":aspectj表达式
    type="custom":自定义一个TypeFilter接口,此接口只有一个match方法,返回true就排除,返回false就不排除
    type="regex":正则表达式

    -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>

</beans>

5.11、Spel

Spring Expression language spring的表达式语言

1、源码
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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="car" class="com.fxp.bean.Car">
    <property name="name" value="car"></property>
</bean>
   <!--spel测试:#{}

   -->
<bean id="person" class="com.fxp.bean.Person">
    <!--假设每个月工资是6000,可以直接在{}里写上表达式-->
    <property name="salary" value="#{6000*12}"></property>
    <!--引用其他bean的某个属性值-->
    <!--假设person的名字就是Car类的名字-->
    <property name="name" value="#{car.name}"></property>
    <!--引用其他bean-->
    <property name="car" value="#{car}"></property>
    <!--调用静态方法
    #{T(全类名).静态方法名(参数1,参数....)}
    -->
    <property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(0,5)}"></property>

    <!--非静态方法调用:对象.方法名-->
    <property name="gender" value="#{car.getName()}"></property>

</bean>
</beans>

Ioctest:

public void test21(){
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spel.xml");
Object bean = ioc.getBean("person");
System.out.println(bean);
}

运行结果:
在这里插入图片描述

6、DI(依赖注入)

6.1、使用@Autowired注解

1、源码:
Bookservlet:

package com.fxp.servelet;

import com.fxp.Service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

/**
 * 在以前,如果想实现自动赋值,可以用autowire属性进行自动装配
 * 如果用xml配置,还需要有get和set方法
 * 但是现在只需要在属性上添加一个@Autowired注解即可
 */
@Controller
public class BookServlet {
//自动为这个属性赋值
@Autowired
private BookService bookService;
public void doGet(){
    bookService.save();
}
}

Bookservice:

package com.fxp.Service;

import com.fxp.Dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BookService {
//自动装配
@Autowired
private BookDao bookDao;
public void save(){
    System.out.println("BookService正在调用Dao帮你保存图书......");
    bookDao.save();
}
}

BookDao:

package com.fxp.Dao;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;

@Repository
//@Scope(value = "prototype")
public class BookDao {
public void save(){
    System.out.println("图书正在保存.............");
}
}

6.2、Autowired原理

例子:@Autowired
private BookService bookService;

1、先按照类型去容器中找对应的组件:bookservice = ioc.getBean(BookService.class)

1.1、找到一个,直接赋值
1.2、没找到,抛异常
1.3、找到多个BookService(默认id:bookService)、BookServiceExt(默认id:bookServiceExt)

(1)按照变量名作为id继续匹配
创建一个BookService类的子类:BookServiceExt

import com.fxp.Dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BookServiceExt extends BookService{
@Autowired
private BookDao bookDao;

public void save(){
    System.out.println("BookServiceExt..........");
}

}

(2)验证找到多个相同类的组件时,会按照id去找
将servlet代码改变

@Controller
public class BookServlet {
//自动为这个属性赋值
@Autowired
private BookService bookServiceExt;
public void doGet(){
    bookServiceExt.save();
}
}

运行结果:
在这里插入图片描述

按照变量名作为ID去找又分为两种情况:
1、匹配到了:赋值

2、没有匹配到:报错
①解决:使用@Qualifier注解可以指定按照什么进行匹配,让spring别使用变量名去查找

@Controller
public class BookServlet {
//自动为这个属性赋值
@Autowired
@Qualifier("bookService")
private BookService bookServiceExt2;//bookServiceExt2这个是不存在的,//如果直接找会找不到而报错,所以这时候可以用Qualifier注解指定自己想要的来查找
public void doGet(){
    bookServiceExt2.save();
}
}

运行结果:
在这里插入图片描述

6.3、在方法上使用@Autowired注解

在这里插入图片描述

运行结果:
在这里插入图片描述

Ioctest:

@Test
public void test24(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("DaoService.xml");
}

6.4、在方法的形参位置标@Qualifier注解

在这里插入图片描述

运行结果:
在这里插入图片描述

6.5、autowired和resource的区别

1、将BookService的@Autowired注解改为@Resource注解

@Service
public class BookService {
//自动装配
//@Autowired
@Resource
private BookDao bookDao;
public void save(){
    System.out.println("BookService正在调用Dao帮你保存图书......");
    bookDao.save();
}
}

2、运行程序,可以看到结果是一样的
在这里插入图片描述

3、@Autowired,@Resource,@Inject都是自动装配的注解

区别:

@Autowired注解,有一个参数@Autowired(require=false)意思是找到组件就装配,找不到就装配null,不会报错。Autowired最强大,是Spring规定的
@Resource没有这个参数,是j2ee规定的,java的标准

Resource的扩展性更强,如果切换成另外一个容器框架,也可以用,Autowired就不行了

6.7、泛型依赖注入

6.7.1、没有使用泛型

1.源码:
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.xsd
">

<!--泛型依赖注入-->
<context:component-scan base-package="com.fxp"></context:component-scan>

</beans>

BookService:

package com.fxp.Service;

import com.fxp.Dao.BaseDao;
import com.fxp.Dao.BookDao;
import com.fxp.bean.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BookService {
@Autowired
//BookDao bookDao;
BookDao bookDao;
public void save(){
    bookDao.save();

}
}

BookDao:

package com.fxp.Dao;
import com.fxp.bean.Book;
import org.springframework.stereotype.Repository;

/**
 * 定义了基本的增删改查方法
 */
@Repository
public class BookDao extends BaseDao<Book> {
@Override
public void save() {

    System.out.println("保存图书......");
}
}

UserService:

package com.fxp.Service;

import com.fxp.Dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
@Autowired
UserDao userDao;
public void save(){
    userDao.save();
}
}

userDao:

package com.fxp.Dao;

import com.fxp.bean.User;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao extends BaseDao<User> {
@Override
public void save() {
    System.out.println("保存用户......");
}
}

BaseDao:

package com.fxp.Dao;
public abstract class BaseDao<T> {
public abstract void save();
}

Ioctest:

package com.fxp.test;
import com.fxp.Service.BookService;
import com.fxp.Service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.junit.jupiter.api.Assertions.*;

class IOCTest {
ApplicationContext ioc = new ClassPathXmlApplicationContext("DI.xml");

@Test
public void test(){
    BookService bookService = ioc.getBean(BookService.class);
    UserService userService = ioc.getBean(UserService.class);
    bookService.save();
    userService.save();
}
}

运行结果
在这里插入图片描述

6.7.2、使用泛型之后

1、源码
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.xsd
">

<!--泛型依赖注入-->
<!--扫描所有组件加载进ioc容器-->
<context:component-scan base-package="com.fxp"></context:component-scan>

</beans>

BookService:继承了BaseService

package com.fxp.Service;

import com.fxp.Dao.BaseDao;
import com.fxp.Dao.BookDao;
import com.fxp.bean.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService extends BaseService<Book>{

}

UserService:继承了BaseService

package com.fxp.Service;
import com.fxp.Dao.UserDao;
import com.fxp.bean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService extends BaseService<User>{

}

BaseService:

package com.fxp.Service;

import com.fxp.Dao.BaseDao;
import com.fxp.Dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 我们并没有把BaseService也加进容器中,按照以前说这个类没有加进容器中,方法中的@Autowired注解是没用的
 * 但是,BookService和UserService继承了BaseService,而BookService和UserService都被加进了容器中,
 * 这就相当于把BaseService中的内容复制到了BookService和UserService中,所以相当于也被加进了容器里面
 * @Autowire就起作用了
 * @param <T>
 */
public class BaseService <T>{
@Autowired
BaseDao<T> baseDao;
public void save(){
    System.out.println("自动注入的Dao"+baseDao);
    baseDao.save();
}
}

运行结果:
在这里插入图片描述

小结:Servlet层调用Service层,Service层调用Dao层。

1、创建一个抽象的BaseDao,让BookDao和UserDao都继承于它,在BaseDao里写一个抽象方法,BookDao和UserDao分别去实现这个方法。

2、创建一个BaseService层,让BookService和UserService继承于它,因为BookService和UserService都要调用他们的Dao层,所以就可以把调用的方法放在他们的父类BaseService类里面去调用。

6.7.3、泛型依赖注入原理

在这里插入图片描述

6.7.4、带泛型的父类类型

//检测带泛型的父类类型

System.out.println(bookService.getClass().getGenericSuperclass());

运行结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42389904/article/details/114394506