Java之Spring5:IOC容器

什么是IOC容器

    IOC全称Inversion of Control,直译为控制反转,它是具有依赖注入功能的容器,负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring中BeanFactory是IOC容器的实际代表者。使用IOC的目的是为了降低耦合度。

IOC容器中的Bean

     Bean就是由Spring容器初始化、装配及管理的对象。那IOC怎样确定如何实例化Bean、管理Bean之间的依赖关系以及管理Bean呢?先让我们来做个简单的例子。

小试牛刀

搭建Spring5环境

    打开Inteillj然后new一个Java项目:
在这里插入图片描述
    导入相关的jar包:
在这里插入图片描述

写一个HelloWorld

    写一个简单的Person类:

package com.jackma.spring5;

public class Person {
    public void sayHello(){
        System.out.println("Hello World");
    }
}

    创建一个xml文件,用来告诉Spring的IOC容器应该如何创建并组装Bean,其中id是一个Bean的唯一标识,class表示类全路径:
在这里插入图片描述

<?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 name="person" class="com.jackma.spring5.Person"></bean>
</beans>

    然后写一个单元测试方法:

public class MySpringTest {

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Person person = context.getBean("person", Person.class);
        person.sayHello();
    }
}

    测试:
在这里插入图片描述

IOC底层原理

    先来看个实例:假设有两个类,UserService和UserDao,UserDao中有一个add()方法,我们要在UserService调用它,那么使用原始的方式是这样实现的:

public class UserDao {
    public void add(){
        System.out.println("add.......");
    }
}

public class UserService {
    public void execute(){
        UserDao userDao = new UserDao();
        userDao.add();
    }
}

    但原始的方法会存在一个问题,即耦合度太高了,UserService和UserDao关系过于紧密,解决方法是提供一个工厂类,把new userDao的工作交给工厂类,实现解耦:

public class UserFactory {
    public static UserDao getuserDao(){
        return new userDao();
    }
}
public class UserService {

    public void execute(){
        UserDao userDao = UserFactory.getuserDao();
        userDao.add();
    }
}

    Spring的IOC的底层原理使用到了xml解析、工厂模式和反射:
    第一步先配置xml文件:

<bean id="userdao" class="com.jackma.spring5.dao.UserDao"></bean>

    第二步是Spring会根据User类和userDao类提供一个UserFactory类,且类中根据反射来实现创建userDao对象:

public class UserFactory {
    public static UserDao getuserDao(){
        String classValue = class属性值; // 该值通过xml解析得到
        Class clazz = Class.forName(classValue);
        return (UserDao)clazz.newInstance();
    }
}

IOC的Bean管理

    Bean管理指的是Spring创建对象和Spring注入属性两个操作,实现方式有两种:
        ①:基于 xml 配置文件方式实现
        ②:基于注解方式实现

基于 xml 方式创建对象

    像上面的HelloWorld例子使用的就是 xml 方式创建对象,在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建,且创建对象时候,默认也是执行无参数构造方法完成对象创建。
在这里插入图片描述

基于 xml 方式的属性注入

    基于 xml 方式的属性注入有两种方式:set方法注入和构造器注入,还是拿User类来举例 。

set方法注入

    先在User类中添加两个属性,然后记得要提供对应的set方法:

public class User {
    private int id;
    private String name;

    public void setId(int id) {
        this.id = id;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

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

    然后在配置文件中添加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">
<!--    set 方法注入-->
    <bean id="user"  class="com.jackma.spring5.User" >
    <property name="id" value="1"></property>
    <property name="name" value="Jackma"></property>
</bean>
</beans>

    然后测试:

@Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user);
        user.add();
    }

在这里插入图片描述

构造器注入

    要在User类中添加构造器方法:

package com.jackma.spring5;

public class User {
    private int id;
    private String name;

    public User() {
    }

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

//    public void setId(int id) {
//        this.id = id;
//    }
//
//    public void setName(String name) {
//        this.name = name;
//    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

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

<?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="user" class="com.jackma.spring5.User">
        <constructor-arg name="id" value="2"></constructor-arg>
        <constructor-arg name="name" value="马保国"></constructor-arg>
    </bean>
 </beans>

    测试代码同上,结果:
在这里插入图片描述

基于XML方式注入其他类型的属性

注入null

    使用set方法注入为上面的name属性注入null,那么就要修改xml文件(注意User类要提供set方法):

    <bean id="user" class="com.jackma.spring5.User">
        <property name="id" value="3"></property>
        <property name="name">
            <null></null> // 就是这个
        </property>
    </bean>

    测试代码同上,结果:
在这里插入图片描述

注入特殊符号

    使用set方法注入为上面的name属性注入含有特殊符号<>的数据,那么就要修改xml文件,使用<![CDATA[...内容...]]>,这样它就会将内容之间输出:

<!--    属性值包含特殊符号-->
        <bean id="user" class="com.jackma.spring5.User">
            <property name="id" value="3"></property>
            <property name="name">
                <value><![CDATA[<马云>]]></value> // 就是这个
            </property>
        </bean>

    测试代码同上,结果:
在这里插入图片描述

注入引用数据类型

    看一个案例:有一个公司Company,有一个部门类Dept和一个员工类Emp,Emp中有一个Dept类型的属性,用来表示员工的部门:
    Dept:

package com.jackma.spring5.company;
// 部门类
public class Dept {
    private String dname;
    public void setDname(String dname) {
        this.dname = dname;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "dname='" + dname + '\'' +
                '}';
    }
}

    Emp:

package com.jackma.spring5.company;

// 员工类
public class Emp {
    private String ename;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Dept dept;
    public void setDept(Dept dept) {
        this.dept = dept;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }

    public Dept getDept() {
        return dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "ename='" + ename + '\'' +
                ", gender='" + gender + '\'' +
                ", dept=" + dept +
                '}';
    }
}

使用外部bean

xml中添加外部bean:

<!--    外部bean-->
    <bean id="emp" class="com.jackma.spring5.company.Emp">
        <property name="ename" value="马云"></property>
        <property name="gender" value="男"></property>
        <property name="dept" ref="dept" ></property>
    </bean>
    // 外部bean
        <bean name="dept" class="com.jackma.spring5.company.Dept">
            <property name="dname" value="退休了"></property>
        </bean>

测试:

@Test
    public void test3(){
        // 测试内部bean
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        Emp emp = context.getBean("emp", Emp.class);
        System.out.println(emp);
    }

在这里插入图片描述

使用内部bean

xml中添加内部bean:

<!--    内部bean-->
    <bean id="emp" class="com.jackma.spring5.company.Emp">
        <property name="ename" value="马云"></property>
        <property name="gender" value="男"></property>
        <property name="dept" >
        // 内部bean
            <bean name="dept" class="com.jackma.spring5.company.Dept">
                <property name="dname" value="总裁"></property>
            </bean>
        </property>
    </bean>

    测试代码同上,结果:
在这里插入图片描述

级联赋值

先关联(外部bean或者内部bean),后赋值:先使用外部bean关联,然后赋值为首席执行官

<bean id="emp" class="com.jackma.spring5.company.Emp">
        <property name="ename" value="马云"></property>
        <property name="gender" value="男"></property>
        <property name="dept" ref="dept"></property>
        <property name="dept.dname" value="首席执行官"></property>
    </bean>
    <bean name="dept" class="com.jackma.spring5.company.Dept">
        <property name="dname" value="总裁"></property>
    </bean>

    测试代码同上,结果:
在这里插入图片描述

注入集合类型

    注入集合类型包括:数组、List、Set、Map。使用Stu类来演示:
    第一步是添加属性,并提供对应set方法:

package com.jackma.spring5.collectiontype;

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

public class Stu{

    private String[] course;
    private List<String> list;
    private Set<String> set;
    private Map<String, String> maps;

    public void setCourse(String[] course) {
        this.course = course;
    }

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

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

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }
    
	public void test(){
        // 测试类
        System.out.println(Arrays.toString(course));
        System.out.println(list);
        System.out.println(set);
        System.out.println(maps);
    }
}

    第二步是在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="stu" class="com.jackma.spring5.collectiontype.Stu">
<!--    数组类型属性注入-->
    <property name="course">
        <array>
            <value>JavaSE</value>
            <value>JavaWeb</value>
            <value>JavaEE</value>
        </array>
    </property>

<!--    数组类型属性注入-->
    <property name="list">
        <list>
            <value>语文</value>
            <value>数学</value>
            <value>英语</value>
        </list>
    </property>

<!--    Set类型属性注入-->
    <property name="set">
        <set>
            <value>早上</value>
            <value>中午</value>
            <value>晚上</value>
        </set>
    </property>

    <!--    Map类型属性注入-->
    <property name="maps">
        <map>
            <entry key="蒙牛" value="酸酸乳"></entry>
            <entry key="瓜子" value="二手车"></entry>
            <entry key="O泡" value="时间到"></entry>
        </map>
    </property>
</bean>
</beans>

    测试:
在这里插入图片描述

设置集合属性对象类型的值

    先创建一个类Course表示一个学生学的课程

package com.jackma.spring5.collectiontype;

public class Course {
    private String name;

    public void setName(String name) {
        this.name = name;
    }
    
@Override
    public String toString() {
        return "Course{" +
                "name='" + name + '\'' +
                '}';
    }
}

    然后Stu类中添加一个存放Course类型数据的属性List,然后提供set方法:

// 学生所学的多门课程
    private List<Course> list2;
    
    public void setList2(List<Course> list2) {
        this.list2 = list2;

	// 用来测试
	public void test1(){
        System.out.println(list2);
    }
}

    现在我们要对List中Course类型的对象进行赋值,xml文件配置:

<!--    设置集合属性对象类型的值-->
    <bean id="stu" class="com.jackma.spring5.collectiontype.Stu">
        <property name="list2">
            <list>
                <ref bean="course1" ></ref>
                <ref bean="course2" ></ref>
            </list>
        </property>
    </bean>
    <bean id="course1" class="com.jackma.spring5.collectiontype.Course">
        <property name="name" value="物理"></property>
    </bean>
    <bean id="course2" class="com.jackma.spring5.collectiontype.Course">
        <property name="name" value="化学"></property>
    </bean>

    测试:

@Test
    public void test5(){
        // 测试设置集合属性对象类型的值
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
        Stu stu = context.getBean("stu", Stu.class);
        stu.test1();
    }

在这里插入图片描述

提取集合类型属性的注入部分

    先写一个book类:

package com.jackma.spring5.collectiontype;

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

public class Book {
    private List<String> book;
    private List<String> book2;

    public void setBook(List<String> book) {
        this.book = book;
    }

    public void setBook2(List<String> book2) {
        this.book2 = book2;
    }

   public void test(){
        System.out.println("bean1" + book);
    }

    public void test2(){
        System.out.println("bean2:" + book2);
    }

}

    步骤:

  1. 在Spring配置文件中引入名称空间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: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">
</beans>
  1. 使用util标签完成提取list集合类型属性的提取(相当于是一个公共的,可以注入到多个List类型的bean中),下面把提取出来的booklist注入到list和list2两个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"
       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="booklist">
        <value>语文</value>
        <value>数学</value>
        <value>英语</value>
    </util:list>

    <bean id="list" class="com.jackma.spring5.collectiontype.Book">
        <property name="book" ref="booklist"></property>
    </bean>

    <bean id="list2" class="com.jackma.spring5.collectiontype.Book">
        <property name="book2" ref="booklist"></property>
    </bean>
</beans>

    测试:

@Test
    public void test5(){
        // 测试设置集合属性对象类型的值
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        Book book = context.getBean("list:", Book.class);
        book.test();
        Book book2 = context.getBean("list2", Book.class);
        book2.test2();
    }

在这里插入图片描述

普通bean和工厂bean

    什么是普通 bean?前面我们在配置文件中定义的bean就是普通bean,其创建的类型就是返回类型:
在这里插入图片描述
在这里插入图片描述

    而工厂 bean就是在配置文件中定义bean的类型可以和返回类型不一样,下面来举个例子:

  1. 先新建一个包factorybean,然后在包里新建一个类Mybean:
package com.jackma.spring5.factorybean;

public class Mybean {

}

    改之前先看一下使用普通bean的结果会是什么样子,创建xml文件(注意这里写的类型是Mybean):

<?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="myBean" class="com.jackma.spring5.factorybean.Mybean"></bean>
</beans>

    测试和结果:

@Test
    public void test6(){
        // 测试工厂bean
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
        Mybean mybean = context.getBean("myBean", Mybean.class);
        System.out.println(mybean);
    }

在这里插入图片描述
    可以看到这里是一个Mybean类型的,接下来把它改成Coursr类型的。

  1. 让这个类作为工厂bean,实现接口FactoryBean,并实现 接口里面的方法,在实现的方法中定义返回的bean类型:
package com.jackma.spring5.factorybean;

import com.jackma.spring5.collectiontype.Course;
import org.springframework.beans.factory.FactoryBean;

public class Mybean implements FactoryBean<Course> {

    // 定义返回bean的类型
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setName("newbean");
        return course;
    }

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

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

    修改测试代码:

@Test
    public void test6(){
        // 测试工厂bean
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
        Course course = context.getBean("myBean", Course.class);
        System.out.println(course);
    }

在这里插入图片描述

bean的作用域

  1. 在Spring里,我们可以设置bean是单实例还是多实例。
  2. 默认为单实例。
    拿前面那个Book类来测试:
// 测试单实例多实例
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        Book book1 = context.getBean("list", Book.class);
        Book book2 = context.getBean("list", Book.class);
        System.out.print("book1 == book2 ?     ");
        System.out.println(book1 == book2);
        System.out.println(book1);
        System.out.println(book2);

在这里插入图片描述
    可以看到两个book的地址相同,就说明是单实例,那么如何改成多实例?bean 标签里面有属性scope来实现:
在这里插入图片描述
    可以看到有两个值,其中singleton表示是单实例对象,是默认值;而prototype表示是多实例对象。那么修改后结果就变成:
在这里插入图片描述
    注意事项:设置scope值是 singleton时候,加载spring配置文件时候就会创建单实例对象。 设置scope值是prototype时候,不是在加载spring配置文件时候创建对象,而是在调用getBean方法时候创建多实例对象。

bean的生命周期

什么是生命周期?就是从对象创建到对象销毁的过程,bean的生命周期如下:

  1. 通过构造器创建bean实例(无参数构造)
  2. 为bean的属性设置值和对其他bean引用(调用 set 方法)
  3. 调用bean的初始化的方法(需要进行配置初始化的方法)
  4. 获取到了创建的bean实例对象(对象获取到了)
  5. 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)

    创建一个Orders类来演示bean的生命周期:

package com.jackma.spring5.bean;

public class Orders {
    private String oname;

    public Orders() {
        System.out.println("第一步,执行了无参构造函数,创建实例");
    }

    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二步,set注入属性");
    }

    public void initMethod(){
        System.out.println("第三步,调用bean的初始化的方法");
    }

    public void destroyMethod(){
        System.out.println("第五步,销毁bean");
    }
}

    需要在配置文件中配置bean的初始化的方法和bean的销毁的方法init-method和destroy-method:

<?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="orders" class="com.jackma.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="手机"></property>
</bean>
</beans>

    bean的销毁的方法还需要自行调用:

@Test
    public void test7(){
        // 测试生命周期
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
        Orders orders = context.getBean("orders", Orders.class);
        System.out.println("第四步,获取到了创建的bean实例对象:");
        System.out.println(orders);
        // 手动让bean实例销毁
        context.close();
    }

    以上是bean基本的生命周期,总共有5步,但如果添加了bean的后置处理器之后,还会有两个步骤,即生命周期变成7步:

  1. 通过构造器创建bean实例(无参数构造)
  2. 为bean的属性设置值和对其他bean引用(调用 set 方法)
  3. 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
  4. 调用bean的初始化的方法(需要进行配置初始化的方法)
  5. bean实例传递bean后置处理器的方法postProcessAfterInitialization
  6. 获取到了创建的bean实例对象(对象获取到了)
  7. 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)

    怎么添加bean的后置处理器?首先要创建一个类来实现BeanPostProcessor接口:

package com.jackma.spring5.bean;

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("在初始化之前执行的方法");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return 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="orders" class="com.jackma.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="手机"></property>
</bean>
<!--    配置后置处理器-->
    <bean id="mybeanpost" class="com.jackma.spring5.bean.MyBeanPost"></bean>
</beans>

    测试代码同上,结果:
在这里插入图片描述

XML的自动装配

    什么是自动装配?根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入。举例:

  1. 创建一个包autowire,创建一个Emp类和一个Dept类:

    Emp:

package com.jackma.spring5.autowire;

public class Emp{
    private Dept dept;

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "dept=" + dept +
                '}';
    }

    public void test(){
        System.out.println(this);
    }
}

    Dept:

package com.jackma.spring5.autowire;

// 部门类
public class Dept {
    @Override
    public String toString() {
        return "Dept{}";
    }
}
  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="emp" class="com.jackma.spring5.autowire.Emp">
    <property name="dept" ref="dept"></property>
</bean>
    <bean id="dept" class="com.jackma.spring5.autowire.Dept"></bean>
</beans>

    测试和结果:

@Test
    public void test8(){
        // 测试自动装载
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
        Emp emp = context.getBean("emp", Emp.class);
        emp.test();
    }

在这里插入图片描述
    下面来配置自动装载,配置文件中的bean标签有一个属性autowire,可以用来配置自动装配:
在这里插入图片描述
    autowire 属性常用两个值:byName根据属性名称注入 ,注入值bean的id值和类属性名称一样;byType根据属性类型注入(如果使用byType,那么相同类型的bean不能定义多个,不然就不知道是哪一个了):

<!--    配置自动装配-->
        <bean id="emp" class="com.jackma.spring5.autowire.Emp" autowire="byName">
    </bean>
        <bean id="dept" class="com.jackma.spring5.autowire.Dept"></bean>
<!--    配置自动装配-->
        <bean id="emp" class="com.jackma.spring5.autowire.Emp" autowire="byType">
    </bean>
        <bean id="dept" class="com.jackma.spring5.autowire.Dept"></bean>

    测试和结果:
在这里插入图片描述

外部属性文件

    应用场景:假设要创建一个数据库的连接池,按照以往的方法创建,假如我们的用户名或者密码之类的信息要修改,那么同时就要对xml文件中的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="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--        直接配置-->
       <property name="driverClassName" value="com,mysql.jdbc.Driver"></property>-->
       <property name="url" value="jdbc.mysql://localhost:3306/userDb"></property>-->
       <property name="username" value="root"></property>-->
       <property name="password" value="123456"></property>-->
    </bean>
</beans>

    改进方法是创建一个properties外部属性文件,然后把信息注入到xml文件中,这样在修改信息时只需要修改properties文件即可:
    首先提供properties文件:
在这里插入图片描述
    然后需要在xml文件中引入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: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:property-placeholder location="classpath:jdbc.properties"/>
<!--        配置连接池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
            <property name="driverClassName" value="${prop.driverClass}"></property>
            <property name="url" value="${prop.url}"></property>
            <property name="username" value="${prop.userName}"></property>
            <property name="password" value="${prop.passWord}"></property>
        </bean>
</beans>

基于注解方式创建对象

    Spring针对Bean管理中创建对象提供的注解:

  1. @Component
  2. @Service
  3. @Controller
  4. @Repository

    注解方式创建对象的步骤:

  1. 引入AOP依赖。
    在这里插入图片描述
  2. 开启组件扫描:扫描类中的注解,需要用到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: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.jackma.spring5.service"></context:component-scan>
</beans>
  1. 新建一个类,在类上面添加创建对象注解
package com.jackma.spring5.service;

import org.springframework.stereotype.Component;

// 注解里的value相当于bean里的id值,也可以不写,若不写则默认为首字母小写的类名
@Component(value = "userService") // <bean id="userService">
public class UserService {
    public void add(){
        System.out.println("Service add......");
    }
}

    测试和结果:

@Test
    public void test9(){
        // 测试自动装载
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");
        UserService userservice = context.getBean("userService", UserService.class);
        userservice.add();
    }

在这里插入图片描述
    开启组件扫描时默认扫描包中所有的类,但也可以进行配置,使得它扫描指定的类:

<context:component-scan base-package="com.jackma.spring5.service" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    context标签中有属性use-default-filters,true表示使用默认的扫描,而false表示不使用默认的扫描,然后在下面自己设置扫描的方法:
    context:include-filter:设置扫描哪些内容;context:exclude-filter:设置哪些内容不进行扫描,type表示根据什么来扫描,这里我设置成按照注解来扫描,然后expression选择注解的类型,我在这是扫描注解类型是Component的,不扫描Controller类型的。
    此外,type的类型还有:
在这里插入图片描述

基于注解方式注入属性

    和上面的创建对象一样,Spring针对Bean管理中注入属性提供的注解有:

  1. @Autowired:根据属性类型进行自动装配
  2. @Qualifier:根据名称进行注入
  3. @Resource:可以根据类型注入,可以根据名称注入
  4. @Value:注入普通类型属性

    实例:有一个ManService类,一个ManDaoImpl类实现ManDAO接口和它里面的upDate()方法,现在要在ManService类中调用这个方法:
    步骤:

  1. 在Man类和ManDaoImpl类添加创建对象的注解
  2. 在service中注入dao对象,在service类添加dao类型属性,在属性上面使用注解,不用提供set方法

@Autowired

    ManDao 接口:

package com.jackma.spring.dao;

public interface ManDao {
    public void upDate();
}

    接口实现类ManDaoImpl :

package com.jackma.spring.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "manDaoImpl")
public class ManDaoImpl implements ManDao {

    @Override
    public void upDate() {
        System.out.println("dao upDate......");
    }
}

    ManService :

package com.jackma.spring;

import com.jackma.spring.dao.ManDao;
import com.jackma.spring.dao.ManDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository(value = "manService")
public class ManService {

    // 定义DAO类型属性,添加注入属性注解
    // 不用提供set方法
    @Autowired // 根据类型注入
    ManDao manDao = new ManDaoImpl();
    
    public void add(){
        System.out.println("service add......");
        manDao.upDate();
    }
}

    测试:

@Test
    public void test10(){
        // 测试注解注入属性
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");
        ManService manService = context.getBean("manService", ManService.class);
        manService.add();
    }

在这里插入图片描述

@Qualifier

    这个@Qualifier 注解的使用,和上面@Autowired 一起使用。因为万一ManDao有多个实现类,而仅仅使用@Autowired的话它不知道是哪个实现类,因此可以使用@Qualifier来指定特定名字的实现类。

package com.jackma.spring;

import com.jackma.spring.dao.ManDao;
import com.jackma.spring.dao.ManDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;

@Repository(value = "manService")
public class ManService {
    // 定义DAO类型属性,添加注入属性注解
    // 不用提供set方法
    @Autowired // 根据类型注入
    @Qualifier(value = "manDaoImpl")
    ManDao manDao = new ManDaoImpl();

    public void add(){
        System.out.println("service add......");
        manDao.upDate();
    }
}

@Resource

    结合了上两个注解的效果:

package com.jackma.spring;

import com.jackma.spring.dao.ManDao;
import com.jackma.spring.dao.ManDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

@Repository(value = "manService")
public class ManService {
    // 定义DAO类型属性,添加注入属性注解
    // 不用提供set方法
//    @Autowired // 根据类型注入
//    @Qualifier(value = "userDaoImpl")
//    @Resource // 根据类型注入
    @Resource(name = "manDaoImpl") // 根据名称注入
    ManDao manDao = new ManDaoImpl();

    public void add(){
        System.out.println("service add......");
        manDao.upDate();
    }
}

@Value

    注入普通类型属性:

package com.jackma.spring;

import com.jackma.spring.dao.ManDao;
import com.jackma.spring.dao.ManDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

@Repository(value = "manService")
public class ManService {
    // 定义DAO类型属性,添加注入属性注解
    // 不用提供set方法
//    @Autowired // 根据类型注入
//    @Qualifier(value = "userDaoImpl")
//    @Resource // 根据类型注入
    @Resource(name = "manDaoImpl") // 根据名称注入
    ManDao manDao = new ManDaoImpl();

    @Value("马保国")
    private String name;

    public void add(){
        System.out.println("service add......");
        manDao.upDate();
        System.out.println("name:" + name);
    }
}

    测试代码相同:在这里插入图片描述

完全注解开发

    完全注解开发,即使用一个配置类来替代 xml 配置文件。
    创建配置类,替代 xml 配置文件:

package com.jackma.spring;

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

@Configuration
@ComponentScan(basePackages = "com.jackma.spring")
public class SpringConfig {

}

    测试:

@Test
    public void test11(){
        // 测试纯注解开发

        // 加载配置类
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        ManService man = context.getBean(ManService.class);
        man.add();
    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值