Spring第二章:Bean管理

该系列为imooc Spring从入门到进阶笔记,跟随课程加入自己见解,同时也为项目中碰到一些问题做了解答

1、Spring的工厂类

1.1、Spring工厂类结构

在这里插入图片描述

现在用的是:ApplicationContext
生成Bean的时机:ApplicationContext是在加载配置文件,就会将配置文件中所有单例模式生成的类全部
实例化

以前用的是老的工厂类:BeanFactory
生成Bean的时机:BeanFactory是在完成工厂实例化后,调用getBean后,才会去创建Java Bean的实例

具体的实现为下面两种,仅是创建Spring工厂类的方式不一样
ClassPathXmlApplicationContext:加载类路径下的配置文件(项目中的)
FileSystemXmlApplicationContext:加载磁盘系统中的配置文件(不在项目中,而是在某个磁盘中的配置文件)

1.2、两种创建Spring工厂类对比

    @Test
    //Spring的方式实现-读取项目中的配置文件
    public void demo2(){
        //创建Spring的工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类
        UserService userService=(UserService) applicationContext.getBean("userService");

        userService.sayHello();
    }
    @Test
    //Spring的方式实现-读取磁盘系统中的配置文件
    public void demo3(){
        //创建Spring的工厂
        ApplicationContext applicationContext=new FileSystemXmlApplicationContext("C:\\Users\\35208\\Desktop\\applicationContext.xml");
        //通过工厂获得类
        UserService userService=(UserService) applicationContext.getBean("userService");

        userService.sayHello();
    }

2、Spring的Bean管理(XML方式)

我们把类交给Spring,然后Spring来帮我们生成这个类的实例,那它是如何来生成的呢?那我们首先来看Spring三种实例化Bean的方式

2.1、三种实例化Bean的方式

  • 使用类构造器实例化(默认无参数)
  • 使用静态工厂方法实例化(简单工厂模式)
  • 使用实例工厂方法实例化(工厂方法模式)

2.1.1、使用类构造器实例化(默认无参数)

  1. 新建com.imooc.ioc.demo2>新建Bean1
package com.imooc.ioc.demo2;

/**
 * Bean的实例化的三种方式:第一种采用无参数的构造方法的方式
 */
public class Bean1 {
    public Bean1() {
        System.out.println("Bean1被实例化了...");
    }
}

  1. 在applicationContext.xml中进行bean标签配置
<!--Bean的实例化的三种方式-->
    <!--第一种:无参构造器的方式-->
    <bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>
  1. 在com.imooc.ioc.demo2>新建测试类SpringDemo2
package com.imooc.ioc.demo2;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Bean的实例化的三种方式
 */
public class SpringDemo2 {
    //第一种:无参构造器的方式
    @Test
    public void demo1(){
        //创建工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类的实例
        Bean1 bean1=(Bean1) applicationContext.getBean("bean1");
    }
}
  1. 执行测试代码
    在这里插入图片描述
    可见Bean1已经被成功实例化

2.1.2、使用静态工厂方法实例化(简单工厂模式)

  1. 在com.imooc.ioc.demo2>新建Bean2
package com.imooc.ioc.demo2;

/**
 * Bean的实例化的三种方式:第二种静态工厂实例化方式
 */
public class Bean2 {
}

  1. 在com.imooc.ioc.demo2>新建工厂类Bean2Factory
package com.imooc.ioc.demo2;

/**
 * Bean2的静态工厂
 */
public class Bean2Factory {
    public static Bean2 createBean2(){
        System.out.println("Bean2Factory的方法已经执行了...");
        return new Bean2();
    }

}

  1. 在applicationContext.xml中进行bean标签配置
    <!--第一种:无参构造器的方式-->
    <bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>
    <!--第二种:静态工厂的方式-->
    <bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>
  1. 在com.imooc.ioc.demo2>在测试类SpringDemo2新增使用静态工厂方法实例化的测试代码
package com.imooc.ioc.demo2;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Bean的实例化的三种方式
 */
public class SpringDemo2 {
    //第一种:无参构造器的方式
    @Test
    public void demo1(){
        //创建工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类的实例
        Bean1 bean1=(Bean1) applicationContext.getBean("bean1");

    }
    //第二种:静态工厂的方式
    @Test
    public void demo2(){
        //创建工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类的实例
        Bean2 bean1=(Bean2) applicationContext.getBean("bean2");

    }
}

  1. 执行测试代码
    在这里插入图片描述
    可见Bean2已经被成功实例化

2.1.3、使用实例工厂方法实例化(工厂方法模式)

  1. 在com.imooc.ioc.demo2>新建Bean3
package com.imooc.ioc.demo2;

/**
 * Bean的实例化的三种方式:第二种实例工厂实例化方式
 */
public class Bean3 {
}
  1. 在com.imooc.ioc.demo2>新建工厂类Bean3Factory
package com.imooc.ioc.demo2;

/**
 *Bean2的实例工厂
 */
public class Bean3Factory {
    public Bean3 createBean3(){
        System.out.println("Bean3Factory的方法已经执行了...");
        return new Bean3();
    }
}
  1. 在applicationContext.xml中进行bean标签配置
    <!--第一种:无参构造器的方式-->
    <bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>
    <!--第二种:静态工厂的方式-->
    <bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>
    <!--第三种:实例工厂的方式-->
    <bean id="bean3Factory" class="com.imooc.ioc.demo2.Bean3Factory"/>
    <bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>
  1. 在com.imooc.ioc.demo2>在测试类SpringDemo2新增使用实例工厂方法实例化的测试代码
package com.imooc.ioc.demo2;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Bean的实例化的三种方式
 */
public class SpringDemo2 {
    //第一种:无参构造器的方式
    @Test
    public void demo1(){
        //创建工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类的实例
        Bean1 bean1=(Bean1) applicationContext.getBean("bean1");

    }
    //第二种:静态工厂的方式
    @Test
    public void demo2(){
        //创建工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类的实例
        Bean2 bean1=(Bean2) applicationContext.getBean("bean2");

    }
    //第三种:实例工厂的方式
    @Test
    public void demo3(){
        //创建工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类的实例
        Bean3 bean1=(Bean3) applicationContext.getBean("bean3");

    }
}

  1. 执行测试代码
    在这里插入图片描述
    可见Bean3已经被成功实例化

2.2、Bean的常规配置

2.2.1、id和name

  • 一般情况下,装配一个Bean时,通过指定一个id属性作为Bean的名称
  • id 属性在IOC容器中必须是唯一的
  • id和name : 这两个一样,都是作为Bean的名称。区别:Bean的名称中含有特殊字符,需要使用name属性

2.2.2、class

  • class用于设置一个类的完全路径名称,主要作用是IOC容器生成类的实例

2.2.3、scope

scope的取值即为Bean 的作用域,有以下四种

  • singleton 为默认值(常用)
  • prototype通常是在Spring去整合Struts2的时候,Struts2的action是多例的,要用Spring来管理,所以要设置为prototype (常用)
  • request与session主要应用于Web项目
    在这里插入图片描述

2.2.4、Bean的常规配置的测试

  1. 在com.imooc.ioc包下>新建demo3>新建Person类
package com.imooc.ioc.demo3;

public class Person {
}
  1. 在applicationContext.xml中进行bean标签配置,scope为默认
    <!--Bean的作用范围-->
    <bean id="person" class="com.imooc.ioc.demo3.Person"/>

3.在com.imooc.ioc包下>demo3>新建测试代码SpringDemo3类

package com.imooc.ioc.demo3;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Bean的作用范围测试
 */
public class SpringDemo3 {
    //第一种:单例模式
    @Test
    public void demo1(){
        //创建工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类的实例
        Person person1=(Person) applicationContext.getBean("person");
        Person person2=(Person) applicationContext.getBean("person");
        //当scope为默认或者singleton时比较两个示例地址是否一致,答案是一致的
        //当scope为prototype时比较两个示例地址是否一致,答案是不一致的
        System.out.println(person1);
        System.out.println(person2);

    }
}
  1. 执行代码,当scope为默认或者singleton时比较两个示例地址是否一致,答案是一致的,为单例模式
    在这里插入图片描述
  2. 修改applicationContext.xml中bean标签配置,scope为prototype
    <!--Bean的作用范围-->
    <bean id="person" class="com.imooc.ioc.demo3.Person" scope="prototype"/>
  1. 执行代码,当scope为prototype时比较两个示例地址是否一致,答案是不一致的,为多里模式
    在这里插入图片描述

2.3、Spring容器中Bean的生命周期

2.3.1、Spring容器中Bean的生命周期的两个属性的配置

Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法。

<bean id=“xxx” class=“...Yoo”
 init-method=“init”
 destory-method=“destroy”/>
  • init-method: “init” 方法名称,即bean被载入到容器时调用init()方法(单利、多例都有效),该方法名称可自定义

  • destroy-method: "destroy"方法名称,即 bean从容器中删除时调用destroy()方法(只有单利有效,即scope= singleton有效),该方法名称可自定义

下面我们在代码中测试一下

  1. 在com.imooc.ioc.demo3包下>新建Man类
package com.imooc.ioc.demo3;

public class Man {
    public Man() {
        System.out.println("Man被实例化了...");
    }
    public void setup(){
        System.out.println("Man被初始化...");
    }
    public void teardown(){
        System.out.println("Man被销毁了...");
    }
}
  1. 在applicationContext.xml中进行bean标签配置
    <bean id="man" class="com.imooc.ioc.demo3.Man" init-method="setup" destroy-method="teardown"/>
  1. 在com.imooc.ioc.demo3包下>完善测试代码SpringDemo3类
    //测试spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法
    @Test
    public void demo2(){
        //创建工厂
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过工厂获得类的实例
        Man man=(Man) applicationContext.getBean("man");
        //关闭工厂
        applicationContext.close();
    }
  1. 运行测试代码demo2
    在这里插入图片描述

2.3.2、Bean的生命周期的完整过程

Spring创建类的时候,实际经历了11个过程
在这里插入图片描述
Spring bean生命周期

1. instantiate bean对象实例化
2. populate properties 封装属性
3. 如果Bean实现BeanNameAware 执行setBeanName(该方法用于获取applicationContext.xml配置文件中bean标签的id或name的值)
4. 如果Bean实现BeanFactoryAware或者applicationAontextAware设置工厂setBeanFactory或者上下文对象setApplicationContext
5. 如果存在类实现BeanPostProcessor(后处理Bean),执行postProcessBeforInitialization()方法
6. 如果Bean实现InitializingBean执行afterPropertieSet()方法
7. 调用<bean init-method="init">指定的初始化方法init()
8 .如果存在类实现BeanPostProcessor(处理Bean),执行postProcessAfterinitializtion()方法  从而实现类的增强
8. 执行业务方法
9. 如果Bean实现DisposableBean执行destroy()方法
10. 调用<bean destroy-method="customerDestroy">指定销毁方法      

在代码中演示Spring bean的完整生命周期

  1. 修改com.imooc.ioc.demo3包下的Man类
package com.imooc.ioc.demo3;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class Man implements BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean {
    private String name;

    public Man() {
        System.out.println("第一步:Man被实例化了");
    }

    public void setName(String name) {
        System.out.println("第二步:设置属性");
        this.name = name;
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("第三步:设置Bean的名称");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("第四步:了解工厂的信息");
    }
    
    //第五步由实现了BeanPostProcessor接口的MyBeanPostProcessor类执行postProcessBeforeInitialization方法
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("第六步:属性设置后执行的方法");
    }

    public void setup(){
        System.out.println("第七步:Man被初始化");
    }
    
    //第八步由实现了BeanPostProcessor接口的MyBeanPostProcessor类执行postProcessAfterInitialization方法
    
    public void run(){
        System.out.println("第九步:执行自身的业务方法");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("第十步:执行Spring销毁方法");
    }

    public void teardown(){
        System.out.println("第十一:Man被销毁了");
    }
}

  1. 在com.imooc.ioc.demo3包下新建MyBeanPostProcessor(处理第5、8步)
    在Spring容器中,每一个Bean被实例化时,Spring都会调用该类执行第5、8步
package com.imooc.ioc.demo3;

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

public class MyBeanPostProcessor implements BeanPostProcessor {
    //在Spring容器中,每一个Bean被实例化时,Spring都会调用该类执行第5、8步
    @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;
    }
}
  1. 在applicationContext.xml中进行bean标签配置(配置BeanPostProcessor,同时注释掉其他Bean即注释其他的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">
    <!--UserService的创建权交给Spring    -->
<!--    <bean id="userService" class="com.imooc.ioc.demo1.UserServiceImpl">-->
<!--        &lt;!&ndash;   设置属性     &ndash;&gt;-->
<!--        <property name="name" value="李四"/>-->
<!--    </bean>-->
    <!--Bean的实例化的三种方式-->
    <!--第一种:无参构造器的方式-->
    <!--<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>-->
    <!--第二种:静态工厂的方式-->
    <!--<bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>-->
    <!--第三种:实例工厂的方式-->
    <!--<bean id="bean3Factory" class="com.imooc.ioc.demo2.Bean3Factory"/>-->
    <!--<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>-->

    <!--Bean的作用范围-->
<!--    <bean id="person" class="com.imooc.ioc.demo3.Person" scope="prototype"/>-->
    <bean id="man" class="com.imooc.ioc.demo3.Man" init-method="setup" destroy-method="teardown">
        <property name="name" value="张三" />
    </bean>
    <bean class="com.imooc.ioc.demo3.MyBeanPostProcessor"/>
</beans>
  1. 完善com.imooc.ioc.demo3包下SpringDemo3测试代码
    @Test
    public void demo2(){

        //创建工厂
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

        //通过工厂获得类的实例
        Man man=(Man) applicationContext.getBean("man");

        man.run();

        //关闭工厂
        applicationContext.close();
    }

启动测试代码,试验成功
在这里插入图片描述

2.3.3、BeanPostProcessor的作用

使用BeanPostProcessor可以增强一个类中的方法,为AOP做铺垫,在类的实例化过程中对原有的类进行增强

演示怎么去增强一个类里面的某个方法?
以下例子中的代码不是很标准,我们现在没有学习第三方的技术,只能是面向接口编程,这样的话我们可以使用JDK的动态代理

  1. com.imooc.ioc.demo3包下>新建UserDao接口
    该接口提供了增删改查的4个方法
package com.imooc.ioc.demo3;

public interface UserDao {
    public void findAll();
    public void save();
    public void update();
    public void delete();
}
  1. com.imooc.ioc.demo3包下>新建UserDaoImpl类(UserDao接口的实现类)
package com.imooc.ioc.demo3;

public class UserDaoImpl implements UserDao{
    @Override
    public void findAll() {
        System.out.println("查询用户");
    }

    @Override
    public void save() {
        System.out.println("保存用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }
}
  1. 在applicationContext.xml中进行bean标签配置(保留配置BeanPostProcessor,同时注释掉其他Bean即注释其他的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">
    <!--UserService的创建权交给Spring    -->
<!--    <bean id="userService" class="com.imooc.ioc.demo1.UserServiceImpl">-->
<!--        &lt;!&ndash;   设置属性     &ndash;&gt;-->
<!--        <property name="name" value="李四"/>-->
<!--    </bean>-->
    <!--Bean的实例化的三种方式-->
    <!--第一种:无参构造器的方式-->
    <!--<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>-->
    <!--第二种:静态工厂的方式-->
    <!--<bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>-->
    <!--第三种:实例工厂的方式-->
    <!--<bean id="bean3Factory" class="com.imooc.ioc.demo2.Bean3Factory"/>-->
    <!--<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>-->

    <!--Bean的作用范围-->
<!--    <bean id="person" class="com.imooc.ioc.demo3.Person" scope="prototype"/>-->
<!--    <bean id="man" class="com.imooc.ioc.demo3.Man" init-method="setup" destroy-method="teardown">-->
<!--        <property name="name" value="张三" />-->
<!--    </bean>-->
    <bean class="com.imooc.ioc.demo3.MyBeanPostProcessor"/>
    <bean id="userDao" class="com.imooc.ioc.demo3.UserDaoImpl"/>
</beans>
  1. 完善com.imooc.ioc.demo3包下SpringDemo3测试代码
    //测试使用BeanPostProcessor可以增强一个类中的方法
    @Test
    public void demo3(){

        //创建工厂
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

        //通过工厂获得类的实例
        UserDao userDao=(UserDao) applicationContext.getBean("userDao");

        userDao.findAll();
        userDao.save();
        userDao.update();
        userDao.delete();

    }

启动测试
在这里插入图片描述
那么此时我们想增强一下save()方法,想实现保存之前进行校验,看是不是管理员的操作,该怎么编码呢?

  1. 在com.imooc.ioc.demo3包下>修改MyBeanPostProcessor
package com.imooc.ioc.demo3;

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

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyBeanPostProcessor implements BeanPostProcessor {
    //在Spring容器中,每一个Bean被实例化时,Spring都会调用该类执行第5、8步
    @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;
//    }
    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        //System.out.println("第八步:初始化后执行的方法");
        if ("userDao".equals(beanName)){
            Object proxy=Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if ("save".equals(method.getName())){
                        System.out.println("权限校验===========================");
                        return method.invoke(bean,args);
                    }
                    return method.invoke(bean,args);
                }
            });
            return proxy;
        }else {
            return bean;
        }

    }
}

启动测试代码
在这里插入图片描述
可见在保存用户前进行了权限校验

3、Spring的属性注入(XML方式)

3.1、Spring的属性注入的三种方式

  • 对于类成员变量,注入方式有三种
    • 构造方法注入
    • 属性setter方法注入
    • 接口注入
  • Spring支持前两种

3.1.1、Spring的属性注入-构造方法注入

  • 通过构造方法注入Bean 的属性值或依赖的对象,它保证了 Bean 实例在实
    例化后就可以使用。
  • 构造器注入在<constructor-arg>元素里声明的属性
    • name表示属性名称
    • value表示普通属性,填写 基本类型的值
    • ref表示对象属性,填写 对象所属类配置的bean的id(该例子在3.1.2代码中有提现,原理相同)

代码示例

  1. 在com.imooc.ioc下>新建包demo4>新建User类
package com.imooc.ioc.demo4;

public class User {
    private String name;
    private Integer age;

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  1. 在applicationContext.xml中进行bean标签配置
    <!--Bean的构造方法的属性注入-->
    <bean id="user" class="com.imooc.ioc.demo4.User">
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="age" value="23"/>
    </bean>
  1. 在com.imooc.ioc.demo4>新建SpringDemo4测试类
package com.imooc.ioc.demo4;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo4 {
	//测试构造方法注入属性
    @Test
    public void demo1(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        User user=(User) applicationContext.getBean("user");
        System.out.println(user);
    }
}

启动demo1测试代码
在这里插入图片描述
可见已成功进行属性注入

3.1.2、Spring的属性注入-set方法注入

  • 使用set方法注入,在Spring配置文件中,通过<property>设置注入的属性
    • name表示属性名称
    • value表示普通属性,填写 基本类型的值
    • ref表示对象属性,填写 对象所属类配置的bean的id

代码示例

  1. 在com.imooc.ioc.demo4>新建Person类
package com.imooc.ioc.demo4;

public class Person {
    private String name;
    private Integer age;
    private Cat cat;
    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", cat=" + cat +
                '}';
    }
}
  1. 在com.imooc.ioc.demo4>新建Cat类
package com.imooc.ioc.demo4;

public class Cat {
    private String name;

    public String getName() {
        return name;
    }

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

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

  1. 在applicationContext.xml中进行bean标签配置
    <!--Bean的set方法的属性注入-->
    <bean id="person" class="com.imooc.ioc.demo4.Person">
        <property name="name" value="李妹几"/>
        <property name="age" value="18"/>
        <property name="cat" ref="cat1"/>
        <!--
        name表示属性名称
        value表示普通属性,填写 基本类型的值
        ref表示对象属性,填写 对象所属类配置的bean的id
        -->
    </bean>

    <bean id="cat1" class="com.imooc.ioc.demo4.Cat">
        <property name="name" value="饼干"/>
    </bean>
  1. 在com.imooc.ioc.demo4>完善SpringDemo4测试类
    //测试set方法注入属性
    @Test
    public void demo2(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person=(Person) applicationContext.getBean("person");
        System.out.println(person);
    }

启动测试
在这里插入图片描述
可见set方法注入属性成功

3.1.3、Spring的属性注入-p名称空间

  • 使用p命名空间
  • 为了简化XML文件配置,Spring从2.5开始引入一个新的p名称空间
  • 引入
    • <beans>标签内添加属性 xmlns:p=“http://www.springframework.org/schema/p”
  • 使用
    • p:<属性名>="xxx"引入常量值
    • p:<属性名>-ref="xxx" 引用其它Bean对象

代码示例

  1. 在applicationContext.xml中进行bean标签配置(只保留该节内容的bean标签,避免影响试验及观看学习体验)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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的p名称空间的属性注入-->
    <bean id="person" class="com.imooc.ioc.demo4.Person" p:name="李妹几" p:age="18" p:cat-ref="cat1"/>
    <bean id="cat1" class="com.imooc.ioc.demo4.Cat" p:name="饼干"/>

</beans>

启动测试
在这里插入图片描述
可见p名称空间方式注入属性成功

3.1.4、Spring的属性注入-SpEL注入

  • SpEL:spring expression language ,spring表达式语言,对依赖注入进行简化
  • 语法:#{表达式}
  • <bean id="" value="#{表达式}">

SpEL表达式语言

 语法:#{}
 #{'hello'} :使用字符串
 #{beanId}: 使用另一个bean
 #{beanId.content.toUpperCase()}: 使用指定名属性,并使用方法
 #{T(java.lang.Math).PI}: 使用静态字段或方法

代码示例

  1. 在com.imooc.ioc.demo4>新建Product类
package com.imooc.ioc.demo4;

public class Product {
    private String name;
    private Double price;
    private Category category;

    public String getName() {
        return name;
    }

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

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                ", price=" + price +
                ", category=" + category +
                '}';
    }
}

  1. 在com.imooc.ioc.demo4>新建Category类
package com.imooc.ioc.demo4;

public class Category {
    private String name;

    public String getName() {
        return name;
    }

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

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

  1. 在com.imooc.ioc.demo4>新建ProductInfo类
package com.imooc.ioc.demo4;

public class ProductInfo {
    public Double calculatePrice(){
        return Math.random()*199;
    }
}

  1. 在applicationContext.xml中进行bean标签配置(只保留该节内容的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的SpEL的属性注入-->
    <bean id="category" class="com.imooc.ioc.demo4.Category">
        <property name="name" value="#{'服装'}"/>
    </bean>

    <bean id="productInfo" class="com.imooc.ioc.demo4.ProductInfo"/>

    <bean id="product" class="com.imooc.ioc.demo4.Product">
        <property name="name" value="#{'男装'}"/>

        <!--使用ProductInfo类的方法,需要先配置ProductInfo类的bean标签,使得该类实例化-->
        <property name="price" value="#{productInfo.calculatePrice()}"/>

        <property name="category" value="#{category}"/>
    </bean>
</beans>
  1. 在com.imooc.ioc.demo4>完善SpringDemo4测试类
    //测试SpEL的注入属性
    @Test
    public void demo3(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        Product product=(Product) applicationContext.getBean("product");
        System.out.println(product);
    }

启动测试代码
在这里插入图片描述
可见SpEL方式注入属性成功

3.1.5、复杂类型的属性注入

  • 数组类型的属性注入
  • List集合类型的属性注入
  • Set集合类型的属性注入
  • Map集合类型的属性注入
  • Properties类型的属性注入

SpringBean为List/Map/Set/数组注入自定义类型

代码示例

  1. 在com.imooc.ioc下>新建包demo5>新建CollectionBean类
package com.imooc.ioc.demo5;

import java.util.*;

public class CollectionBean {
    private String[] arrs;//数组类型

    private List<String> list;//List集和类型

    private Set<String> set;//Set集和类型

    private Map<String,Integer> map;//Map集和类型

    private Properties properties;//属性类型

    public String[] getArrs() {
        return arrs;
    }

    public void setArrs(String[] arrs) {
        this.arrs = arrs;
    }

    public List<String> getList() {
        return list;
    }

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

    public Set<String> getSet() {
        return set;
    }

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

    public Map<String, Integer> getMap() {
        return map;
    }

    public void setMap(Map<String, Integer> map) {
        this.map = map;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "CollectionBean{" +
                "arrs=" + Arrays.toString(arrs) +
                ", list=" + list +
                ", set=" + set +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}

  1. 在applicationContext.xml中进行bean标签配置(只保留该节内容的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的复杂类型的属性注入-->
    <bean id="collectionBean" class="com.imooc.ioc.demo5.CollectionBean">
        <!--数组类型属性注入-->
        <property name="arrs">
            <list>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
        </property>

        <!--List集合类型属性注入-->
        <property name="list">
            <list>
                <value>111</value>
                <value>222</value>
                <value>333</value>
                <!--如果是普通数据类型的值使用value标签-->
                <!--如果是对象类型的值使用ref标签-->
            </list>
        </property>

        <!--Set集合类型属性注入-->
        <property name="set">
            <set>
                <value>ddd</value>
                <value>eee</value>
                <value>fff</value>
                <!--如果是普通数据类型的值使用value标签-->
                <!--如果是对象类型的值使用ref标签-->
            </set>
        </property>

        <!--Map集合类型属性注入-->
        <property name="map">
            <map>
                <entry key="aaa" value="111"></entry>
                <entry key="bbb" value="222"></entry>
                <entry key="ccc" value="333"></entry>
                <!--如果是普通数据类型的值,在entry标签中使用key,value属性-->
                <!--如果是对象类型的值使用ref标签,在entry标签中使用key-ref,value-ref属性-->
            </map>
        </property>

        <!--properties类型属性注入-->
        <property name="properties">
            <props>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>
</beans>

  1. 在com.imooc.ioc.demo5包下>新建测试类SpringDemo5
package com.imooc.ioc.demo5;

import com.imooc.ioc.demo4.Product;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo5 {
    //测试复杂类型的属性注入
    @Test
    public void demo1(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        CollectionBean collectionBean=(CollectionBean) applicationContext.getBean("collectionBean");
        System.out.println(collectionBean);
    }
}

启动测试代码
在这里插入图片描述
可见复杂类型的属性成功注入

4、Spring的Bean管理(注解方式)

4.1、使用注解定义Bean

注解方式管理Spring Bean

  • 需额外引入spring-aop包

  • 需在配置文件额外添加context的依赖

    1. xmlns:context=“http://www.springframework.org/schema/context”
    2. xsi:schemaLocation=""中添加
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
  • 配置文件中开启包注解扫描<context:component-scan base-package="使用注解的包名" />包含属性注入,类注入功能

  • Java中使用注解

    1. @Component(“beanId”)注解Bean类
      以下3个注解功能基本与@Component等价
    2. @Repository 用于对DAO实现类进行标注
    3. @Service 用于对Service实现类进行标注
    4. @Controller 用于对Controller实现类进行标注

代码示例

  1. 使用idea Maven创建Web项目
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 导入Spring核心开发包到创建好的project中,在项目pom文件中引入依赖
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>4.2.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>4.2.4.RELEASE</version>
    </dependency>
  </dependencies>
  1. 编写代码
    在main下新建java文件夹,右键java文件夹
    在这里插入图片描述
    这样就可以在java文件夹下创建包或编写java代码了

  2. 在java文件夹>新建com.imooc.demo1包>新建UserService类
    使用Component(“beanId”)注解Bean类

package com.imooc.demo1;

import org.springframework.stereotype.Component;

/**
 * Spring的Bean管理的注解方式:
 * 传统XML方式要去applicationContext中配置<bean id="" class=""></bean>
 */
@Component("userService")

public class UserService {
    public String sayHello(String name){
        return "Hello"+name;
    }
}
  1. 在com.imooc.demo1包>新建SpringDemo1测试类
package com.imooc.demo1;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo1 {
    @Test
    public void demo1(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService=(UserService) applicationContext.getBean("userService");
        String s=userService.sayHello("李妹几");
        System.out.println(s);
    }
}
  1. 创建Spring配置文件
    在main下>新建resources文件夹>新建Spring Config XML文件(Spring配置文件默认命名为applicationContext.xml该名称我们可以自定义)
  • 需在配置文件额外添加context的依赖
    1. xmlns:context=“http://www.springframework.org/schema/context”
    2. xsi:schemaLocation=""中添加
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
  • 配置文件中开启注解扫描<context:component-scan base-package="使用注解的包名" />
<?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.imooc.demo1"/>

</beans>

启动测试代码
在这里插入图片描述
可见使用注解定义Bean成功

5、Spring的属性注入(注解方式)

5.1、Spring的属性注入-注解方式

属性注入注解

  • 基本类型的注解
    • @Value(“属性值”) 有setter()方法注解方法,没有就注解属性
  • 引用类型的注解
    • @Autowired

      1. 自动注入,默认按照类型进行注入,如果存在两个相同Bean类型,则按照名称注入
      2. 注入可以针对成员变量或者setter方法
      3. 通过@Autowired的required属性,设置一定要找到匹配的Bean
      4. @Autowired(required=true):当使用@Autowired注解的时候,其实默认就是@Autowired(required=true),表示注入的时候,该bean必须存在,否则就会注入失败。
      5. @Autowired(required=false):表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。
    • @Qualifier

      1. 与@Autowired联用,在@Qualifier指定注入Bean的名称,注解的Bean必须指定相同名称的Bean
      2. @Autowired
        @Qualifier(“userDao”)
        两个注解联合使用,表示一定要找到指定的Bean"userDao",否则注入失败
    • @Resource(naem=“id”)和@Autowired+@Qualifier(“id”)的功能类似

代码示例

  1. 在com.imooc.demo1包>新建UserDao类
package com.imooc.demo1;

import org.springframework.stereotype.Repository;

@Repository("userDao")
public class UserDao {

    public void save(){
        System.out.println("DAO中保存用户");
    }
}

  1. 在com.imooc.demo1包>修改UserService类,新增基本类型属性,新增引用类型属性
package com.imooc.demo1;

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.Component;

import javax.annotation.Resource;

/**
 * Spring的Bean管理的注解方式:
 * 传统XML方式要去applicationContext中配置<bean id="" class=""></bean>
 */
@Component("userService")

public class UserService {
    @Value("米饭")
    private String something;

/*  @Autowired 方式1:自动注入*/
/*  @Autowired 方式2:两个注解联合使用,表示一定要找到指定的Bean,否则注入失败
    @Qualifier("userDao")*/
    @Resource(name = "userDao")/*方式3:表示一定要找到指定的Bean,否则注入失败,相当于方式2*/
    private UserDao userDao;

    public String sayHello(String name){
        return "Hello"+name;
    }

    public void eat(){
        System.out.println("eat"+something);
    }

    public void save(){
        System.out.println("Service中保存用户");
        userDao.save();
    }
}
  1. 在com.imooc.demo1包>SpringDemo1测试类中新增方法

    //测试使用注解进行Bean基本类型属性注入
    @Test
    public void demo2(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService=(UserService) applicationContext.getBean("userService");
        userService.eat();
    }
    //测试使用注解进行Bean引用类型属性注入
    @Test
    public void demo3(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService=(UserService) applicationContext.getBean("userService");
        userService.save();
    }
}

启动测试代码中的demo2方法
在这里插入图片描述
可见使用注解进行Bean基本类型属性注入成功

启动测试代码中的demo3方法
在这里插入图片描述
可见使用注解进行Bean引用类型属性注入成功

5.2、Spring的其他注解

  • Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建
    和拆卸bean的时候调用bean的两个生命周期方法

XML方式与注解方式的联系
在这里插入图片描述

  • 使用注解配置的Bean和配置的一样,默认作用范围都是
    singleton
  • @Scope注解用于指定Bean的作用范围

代码示例

  1. 新建com.imooc.demo2包>新建bean1类
package com.imooc.demo2;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component("bean1")
public class Bean1 {

    @PostConstruct
    public void init(){
        System.out.println("initBean...");
    }

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

    @PreDestroy
    public void destory(){
        System.out.println("destoryBean...");
    }
}
  1. 在com.imooc.demo2包>新建bean2类
package com.imooc.demo2;

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

@Component("bean2")
@Scope("prototype")
public class Bean2 {

}
  1. 在com.imooc.demo2包>新建SpringDemo2测试类
package com.imooc.demo2;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo2 {
    //测试使用注解配置bean的两个生命周期方法
    @Test
    public void demo1(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        Bean1 bean1 = (Bean1)applicationContext.getBean("bean1");

        bean1.say();

        applicationContext.close();
    }

    //测试使用注解配置Scope
    @Test
    public void demo2(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        Bean2 bean1 = (Bean2)applicationContext.getBean("bean2");
        Bean2 bean2 = (Bean2)applicationContext.getBean("bean2");

        System.out.println(bean1 == bean2);

    }
}
  1. 修改applicationContext.xml配置文件,扩大包扫码范围
    <context:component-scan base-package="com.imooc"/>

5.启动测试

  • 启动测试demo1
    在这里插入图片描述
    可见测试使用注解配置bean的两个生命周期方法成功
  • 启动测试demo2
    在这里插入图片描述
    可见测试使用注解配置Scope成功

6、传统XML配置和注解配置混合使用

  • XML方式的优势

    • 结构清晰,易于阅读
  • 注解方式的优势

    • 开发便捷,属性注入方便
  • XML与注解的整合开发

    1. 引入context命名空间,既参照4.1额外添加context的依赖(用于使用注解)
    2. 在配置文件中添加<context:annotation-config>标签(这个是单独使用属性注入的标签,添加后可使用注解来注入属性)(注意包扫描标签<context:component-scan base-package="使用注解的包名" />包含属性注入,类注入功能)
  • 传统XML配置和注解配置混合使用:

    • xml用于配置类,注解主要用于属性注入
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翘脚猴子耍把戏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值