2 - 配置/管理 Bean-IOC

1. 基于 XML 配置 Bean

在上一篇里已经模拟了通过 id 来配置获取 bean

依然是使用 Monster 类

1.1 通过类型获取

1)xml 配置

XML 复制代码
<bean id="monster01" class="com.hspedu.spring.beans.Monster">
    <property name="monsterId" value="1"/>
    <property name="name" value="牛魔王"/>
    <property name="skill" value="牛魔王拳"/>
</bean>

2)测试

java 复制代码
@Test
public void getMonsterByType() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster = ioc.getBean(Monster.class);
    System.out.println("monster=" + monster);
    Monster monster2 = ioc.getBean(Monster.class);
    System.out.println("monster == monster2 的值= " + (monster == monster2));
    //是同一个对象
}

3)细节说明

  1. 按类型来获取 bean, 要求 ioc 容器中的同一个类的 bean 只能有一个
  2. 这种方式的应用场景:比如 XxxAction/Servlet/Controller, 或 XxxService在一个线程
    中只需要一个对象实例(单例)的情况
  3. 在容器配置文件(比如 beans.xml)中给属性赋值, 底层是通过setter 方法完成的, 这也是为什么我们需要提供 setter 方法的原因

1.2 通过构造器配置

1)xml 配置

index 表示构造器的第几个参数,从 0 开始计数

XML 复制代码
<bean id="monster02" class="com.hspedu.spring.beans.Monster">
    <constructor-arg value="2" index="0"/>
    <constructor-arg value="蜘蛛精" index="1"/>
    <constructor-arg value="吐口水" index="2"/>
</bean>

还可以通过 name/type 来指定参数方式

XML 复制代码
<bean id="monster03" class="com.hspedu.spring.beans.Monster">
    <constructor-arg value="3" type="java.lang.Integer"/>
    <constructor-arg value="白骨精" type="java.lang.String"/>
    <constructor-arg value="白骨鞭" type="java.lang.String"/>
</bean>

<bean id="monster04" class="com.hspedu.spring.bean.Monster">
    <constructor-arg value="200" name="monsterId"/>
    <constructor-arg value="白骨精" name="name"/>
    <constructor-arg value="吸人血" name="skill"/>
</bean>

2)测试

java 复制代码
@Test
public void getMonsterByConstructor() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Object monster02 = ioc.getBean("monster02");
    Object monster03 = ioc.getBean("monster03");
    Object monster04 = ioc.getBean("monster03");
    System.out.println("monster02= " + monster02);
    System.out.println("monster03= " + monster03);
    System.out.println("monster04= " + monster04);
}

3)细节说明

  1. 通过 index 属性来区分是第几个参数
  2. 通过 type 属性来区分是什么类型(按照顺序)

1.3 通过 p 名称空间配置

1)xml 配置

增加命名空间配置

java 复制代码
xmlns:p="http://www.springframework.org/schema/p"

通过 p 名称空间来配置 bean 对象,其实将光标放在 p 的地方,alt+enter 就可以添加 xmlns

java 复制代码
<bean id="monster06" class="com.hspedu.spring.bean.Monster"
      p:monsterId="500"
      p:name="红孩儿"
      p:skill="吐火"
/>

2)测试

java 复制代码
@Test
public void getMonsterByP() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster04 = ioc.getBean("monster06", Monster.class);
    System.out.println("monster06=" + monster06);
}

1.4 引用/注入其它 bean 对象

在 spring 的 ioc 容器, 可以通过 ref 来实现 bean 对象的相互引用

1)创建对象

...\spring\dao\MemberDAOImpl.java

java 复制代码
public class MemberDAOImpl {
    public MemberDAOImpl() {
        System.out.println("MemberDAOImpl 构造器...");
    }
    public void add() {
        System.out.println("MemberDAOImpl add()方法");
    }
}

...\spring\service\MemberServiceImpl.java

java 复制代码
public class MemberServiceImpl {
    private MemberDAOImpl memberDAO;
    public MemberServiceImpl() {
        System.out.println("MemberServiceImpl 构造器~");
    }
    public void add() {
        System.out.println("MemberServiceImpl add()...");
        memberDAO.add();
    }
    public void setMemberDAO(MemberDAOImpl memberDAO) {
        this.memberDAO = memberDAO;
    }
    public MemberDAOImpl getMemberDAO() {
        return memberDAO;
    }
}

2)xml 配置

ref 表示 memberDAO 这个属性将引用/指向 id = memberDAOImpl 对象

就体现了 Spring 容器的依赖注入

注:在Spring容器中是作为一个整体运行的,创建引用是不分前后顺序

XML 复制代码
<bean id="memberServiceImpl" class="com.hspedu.spring.service.MemberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
<bean id="memberDAOImpl" class="com.hspedu.spring.dao.MemberDAOImpl"/>

3)测试

java 复制代码
@Test
public void setBeanByRef() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    MemberServiceImpl memberServiceImpl = ioc.getBean("memberServiceImpl",
        MemberServiceImpl.class);
    memberServiceImpl.add();
}

4)结构

说明确实是引用关系

1.5 引用/注入内部 bean 对象

在 spring 的 ioc 容器, 可以直接配置内部 bean 对象

1)修改 xml 配置

自己配置一个内部 bean

java 复制代码
<bean id="memberServiceImpl02" class="com.hspedu.spring.service.MemberServiceImpl">
    <property name="memberDAO">
        <bean class="com.hspedu.spring.dao.MemberDAOImpl"/>
    </property>
</bean>

2)测试

java 复制代码
@Test
public void setBeanByPro() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    MemberServiceImpl memberServiceImpl02 = ioc.getBean("memberServiceImpl02",
            MemberServiceImpl.class);
    memberServiceImpl02.add();
}

1.6 引用/注入集合/数组类型

如何给 bean 对象的集合/数组类型属性赋值

1)创建对象

...\spring\beans\Master.java

java 复制代码
public class Master {
    private String name;
    private List<Monster> monsterList;
    private Map<String, Monster> monsterMap;
    private Set<Monster> monsterSet;
    private String[] monsterName;
    //这个 Properties 是 Hashtable 的子类 , 是 key-value 的形式
    //这里 Properties key 和 value 都是 String
    private Properties pros;
    public Master() {}
    public Master(String name) {this.name = name;}
    public Set<Monster> getMonsterSet() {return monsterSet;}
    public void setMonsterSet(Set<Monster> monsterSet) {
        this.monsterSet = monsterSet;
    }
    public String[] getMonsterName() {return monsterName;}
    public void setMonsterName(String[] monsterName) {
        this.monsterName = monsterName;
    }
    public String getName() {return name;}
    public void setName(String name) {
        this.name = name;
    }
    public List<Monster> getMonsterList() {return monsterList;}
    public void setMonsterList(List<Monster> monsterList) {
        this.monsterList = monsterList;
    }
    public Map<String, Monster> getMonsterMap() {return monsterMap;}
    public void setMonsterMap(Map<String, Monster> monsterMap) {
        this.monsterMap = monsterMap;
    }
    public Properties getPros() {return pros;}
    public void setPros(Properties pros) {
    this.pros = pros;
    }
}

2)xml 配置

给集合属性注入值

XML 复制代码
<bean id="master01" class="com.hspedu.spring.beans.Master">
    <property name="name" value="太上老君"/>
    <!-- 给 bean 对象的 list 集合赋值 -->
    <property name="monsterList">
        <list>
            <ref bean="monster03"/>
            <ref bean="monster02"/>
        </list>
    </property>
    <!-- 给 bean 对象的 map 集合赋值 -->
    <property name="monsterMap">
        <map>
            <entry>
                <key>
                    <value>monsterKey01</value>
                </key>
                <ref bean="monster01"/>
            </entry>
            <entry>
                <key>
                    <value>monsterKey02</value>
                </key>
                <ref bean="monster02"/>
            </entry>
        </map>
    </property>
    <!-- 给 bean 对象的 properties 集合赋值 -->
    <property name="pros">
        <props>
            <prop key="k1">Java 工程师</prop>
            <prop key="k2">前端工程师</prop>
            <prop key="k3">大数据工程师</prop>
        </props>
    </property>
    <!-- 给 bean 对象的数组属性注入值 -->
    <property name="monsterName">
        <array>
            <value>银角大王</value>
            <value>金角大王</value>
        </array>
    </property>
    <!-- 给 bean 对象的 set 属性注入值 -->
    <property name="monsterSet">
        <set>
            <ref bean="monster01"/>
            <bean class="com.hspedu.spring.beans.Monster">
                <property name="monsterId" value="10"/>
                <property name="name" value="玉兔精"/>
                <property name="skill" value="钻地洞"/>
            </bean>
        </set>
    </property>
</bean>

3)测试

java 复制代码
@Test
public void setCollectionByPro() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Master master01 = ioc.getBean("master01", Master.class);
    //获取 list 集合
    System.out.println("======list=======");
    List<Monster> monster_list = master01.getMonsterList();
    for (Monster monster : monster_list) {
        System.out.println(monster);
    }
    //获取 map 集合
    System.out.println("======map=======");
    Map<String, Monster> monster_map = master01.getMonsterMap();
    Set<Map.Entry<String, Monster>> entrySet = monster_map.entrySet();
    for (Map.Entry<String, Monster> entry : entrySet) {
        System.out.println(entry);
    }
    //获取 properties 集合
    System.out.println("======properties=======");
    Properties pros = master01.getPros();
    String property1 = pros.getProperty("k1");
    String property2 = pros.getProperty("k2");
    String property3 = pros.getProperty("k3");
    System.out.println(property1 + "\t" + property2 + "\t" + property3);
    //获取数组
    System.out.println("======数组=======");
    String[] monsterName = master01.getMonsterName();
    for (String s : monsterName) {
        System.out.println("妖怪名= " + s);
    }
    //获取 set
    System.out.println("======set=======");
    Set<Monster> monsterSet = master01.getMonsterSet();
    for (Monster monster : monsterSet) {
        System.out.println(monster);
    }
}

4)细节

Properties 集合的特点:

  1. Properties 是 Hashtable 的子类 , 是 key-value 的形式
  2. key 是 string 而 value 也是 string

1.7 通过 util 名称空间创建 list

spring 的 ioc 容器, 可以通过 util 名称空间创建 list 集合

1)创建对象

java 复制代码
public class BookStore {//书店
    private List<String> bookList;
    public BookStore() {}
    public List<String> getBookList() {
        return bookList;
    }
    public void setBookList(List<String> bookList) {
        this.bookList = bookList;
    }
}

2)xml 配置

增加配置

XML 复制代码
xmlns:util="http://www.springframework.org/schema/util"

对象配置:

XML 复制代码
<util:list id="myListBook">
    <value>三国演义</value>
    <value>西游记</value>
    <value>红楼梦</value>
    <value>水浒传</value>
</util:list>

<bean id="bookStore" class="com.hspedu.spring.beans.BookStore">
    <property name="bookList" ref="myListBook"/>
</bean>

3)测试

java 复制代码
@Test
public void getListByUtil() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    BookStore bookStore = ioc.getBean("bookStore", BookStore.class);
    List<String> bookList = bookStore.getBookList();
    for (String s : bookList) {
        System.out.println(s);
    }
}

1.8 级联属性赋值

可以直接给对象属性的属性赋值, 即级联属性赋值

1)创建对象

...\spring\beans\Dept.java

java 复制代码
public class Dept {
    private String name;
    public Dept() {}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

...\spring\beans\Emp.java

java 复制代码
public class Emp {
    private String name;
    private Dept dept;
    public Emp() {}
    public String getName() {return name;}
    public void setName(String name) {
        this.name = name;
    }
    public Dept getDept() {return dept;}
    public void setDept(Dept dept) {
        this.dept = dept;
    }
}

2)xml 配置

XML 复制代码
<bean id="emp" class="com.hspedu.spring.beans.Emp">
    <property name="name" value="jack"/>
    <property name="dept" ref="dept"/>
    <property name="dept.name" value="Java 开发部"/>
</bean>

<bean id="dept" class="com.hspedu.spring.beans.Dept"/>

3)测试

java 复制代码
@Test
public void setProByRelation() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Emp emp = ioc.getBean("emp", Emp.class);
    System.out.println(emp.getDept().getName());
}

1.9 通过静态工厂获取对象

1)创建对象

...\spring\factory\MyStaticFactory.java

java 复制代码
public class MyStaticFactory {
    private static Map<String, Monster> monsterMap;
    // 使用 static 代码块进行初始化
    static {
        monsterMap = new HashMap<String, Monster>();
        monsterMap.put("monster01", new Monster(100, "黄袍怪", "一阳指"));
        monsterMap.put("monster02", new Monster(200, "九头金雕", "如来神掌"));
    }
    public static Monster getMonster(String key) {
        return monsterMap.get(key);
    }
}

2)xml 配置

  1. 通过静态工厂获取/配置bean

  2. class 是静态工厂类的全路径

  3. factory-method 表示是指定静态工厂类的哪个方法返回对象

  4. constructor-arg value="monster02" value是指定要返回静态工厂的哪个对象

java 复制代码
<bean id="my_monster01"
          class="com.hspedu.spring.factory.MyStaticFactory"
          factory-method="getMonster">
    <constructor-arg value="monster02"/>
</bean>

3)测试

java 复制代码
@Test
public void getBeanByStaticFactory() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster my_monster = ioc.getBean("my_monster01", Monster.class);
    System.out.println(my_monster);
}

1.10 通过实例工厂获取对象

与静态工厂不同的地方在于,需要先有一个实例工厂才能获得对象

1)创建对象

...\spring\factory\MyInstanceFactory.java

java 复制代码
public class MyInstanceFactory {
    private Map<String, Monster> monster_map;
    //非静态代码块
    {
        monster_map = new HashMap<String, Monster>();
        monster_map.put("monster_01", new Monster(100, "猴子精", "吃人"));
        monster_map.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));
    }
    public Monster getMonster(String key) {
        return monster_map.get(key);
    }
}

2)xml 配置

java 复制代码
<bean id="myInstanceFactory" class="com.hspedu.spring.factory.MyInstanceFactory"/>
<bean id="my_monster2" factory-bean="myInstanceFactory"
    factory-method="getMonster">
    <constructor-arg value="monster_02"/>
</bean>

3)测试

java 复制代码
@Test
public void getBeanByInstanceFactory() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster my_monster = ioc.getBean("my_monster2", Monster.class);
    System.out.println(my_monster);
}

1.11 通过 FactoryBean 获取对象(重点)

1)创建对象

...\spring\factory\MyFactoryBean.java

java 复制代码
public class MyFactoryBean implements FactoryBean<Monster> {
    private String keyVal;
    private Map<String, Monster> monster_map;
    {   // 代码块完成初始化任务
        monster_map = new HashMap<String, Monster>();
        monster_map.put("monster_01", new Monster(100, "黄袍怪", "一阳指"));
        monster_map.put("monster_02", new Monster(200, "九头金雕", "如来神掌"));
    }
    public void setKeyVal(String keyVal) {
        this.keyVal = keyVal;
    }

    @Override
    public Monster getObject() throws Exception {
        // TODO Auto-generated method stub
        return this.monster_map.get(keyVal);
    }
    @Override
    public Class getObjectType() {
        // TODO Auto-generated method stub
        return Monster.class;
    }
    @Override
    public boolean isSingleton() {
        // TODO Auto-generated method stub
        // 指定是否是返回单例
        return true;
    }
}

2)xml 配置

java 复制代码
<bean id="myFactoryBean" class="com.hspedu.spring.factory.MyFactoryBean">
    <property name="keyVal" value="monster_01"/>
</bean>

3)测试

java 复制代码
@Test
public void getBeanByFactoryBean() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster = ioc.getBean("myFactoryBean", Monster.class);
    System.out.println(monster);
}

1.12 bean 配置信息重用(继承)

在 spring 的 ioc 容器, 提供了一种继承的方式来实现 bean 配置信息的重用

1)xml 配置

如果要创建一个 配置信息是一样的对象的话,可以进行继承

java 复制代码
<bean id="monster10" class="com.hspedu.spring.beans.Monster">
    <property name="monsterId" value="10"/>
    <property name="name" value="蜈蚣精"/>
    <property name="skill" value="蜇人"/>
</bean>

<!-- parent="monster10" 就是继承使用了 monster10 的配置信息 -->
<bean id="monster11" class="com.hspedu.spring.beans.Monster" parent="monster10"/>

当我们把某个 bean 设置为 abstract="true" 这个 bean只能被继承,而不能实例化了,类似与java 的抽象类

java 复制代码
<bean id="monster12" class="com.hspedu.spring.beans.Monster" abstract="true">
    <property name="monsterId" value="12"/>
    <property name="name" value="美女蛇"/>
    <property name="skill" value="吃人"/>
</bean>
<!-- parent="monster12" 就是继承使用了 monster12 的配置信息 -->
<bean id="monster13" class="com.hspedu.spring.beans.Monster" parent="monster12"/>

2)测试

java 复制代码
@Test
public void getBeanByExtends() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster1 = ioc.getBean("monster11", Monster.class);
    System.out.println(monster1);
    Monster monster2 = (Monster) ioc.getBean("monster13", Monster.class);
    System.out.println(monster2);
}

1.13 bean 创建顺序

在 spring 的 ioc 容器, 默认是按照配置的顺序创建 bean 对象

java 复制代码
<bean id="student01" class="com.hspedu.bean.Student" />
<bean id="department01" class="com.hspedu.bean.Department" />

会先创建 student01 这个 bean 对象,然后创建 department01 这个 bean 对象

如果这样配置,增加了 depends-on="xxx"

java 复制代码
<bean id="student01" class="com.hspedu.bean.Student"
    depends-on="department01"/>
<bean id="department01" class="com.hspedu.bean.Department" />

会先创建 department01 对象,再创建 student01 对象

1.14 bean 对象的单例和多例

在默认是按照单例创建的,即配置一个 bean 对象后,ioc 容器只会创建一个 bean 实例

想要以多个实例形式创建某个 bean 对象的则可以通过配置scope="prototype" 来指定

1)创建对象

...\spring\beans\Car.java

java 复制代码
public class Car {
    public Car() {
        System.out.println("car 构造器");
    }
}

2)xml 配置

java 复制代码
<bean name="car" scope="prototype" class="com.hspedu.spring.beans.Car"/>

3)测试

java 复制代码
@Test
public void getBeanByPrototype() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    for (int i = 0; i < 3; i++) {
        Car car = ioc.getBean("car", Car.class);
        System.out.println(car);
    }
}

4)细节

  1. 默认是单例 singleton, 在启动容器时, 默认就会创建 , 并放入到 singletonObjects 集合
  2. <bean scope="prototype" > 设置为多实例机制后,该 bean 是在 getBean()时才创建
  3. 如 果 是 单 例 singleton, 同 时 希 望 在 getBean 时 才创建 , 可指定懒加载
    lazy-init="true" (注意默认是 false)
  4. lazy-init 就使用默认值 false , 在开发看来, 用空间换时间是值得的, 除非有特殊的要求
  5. scope="prototype" 这时你的 lazy-init 属性的值不管是 ture, 还是 false 都是在 getBean 时,才创建对象

1.15 bean 的生命周期

bean 对象创建是由 JVM 完成的,然后执行如下方法

  1. 执行构造器
  2. 执行 set 相关方法
  3. 调用 bean 的初始化的方法(需要配置)
  4. 使用 bean
  5. 当容器关闭时候,调用 bean 的销毁方法(需要配置)

1)创建对象

...\spring\beans\House.java

java 复制代码
public class House {
    private String name;
    public House() {
        System.out.println("House() 构造器");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        System.out.println("House setName()...");
        this.name = name;
    }
    public void init() {
        System.out.println("House init()..");
    }
    public void destory() {
        System.out.println("House destory()..");
    }
}

2)xml 配置

配置 bean 的初始化方法和销毁方法

java 复制代码
<bean id="house" class="com.hspedu.spring.beans.House"
    init-method="init"
    destroy-method="destory">
    <property name="name" value="北京豪宅"/>
</bean>

3)测试

java 复制代码
@Test
public void beanLife() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    House house = ioc.getBean("house", House.class);
    System.out.println(house);
    //关闭容器  ConfigurableApplicationContext 是有 close 方法的
    ((ConfigurableApplicationContext) ioc).close();
}

4)使用细节

  1. 初始化 init 方法和 destory 方法, 是程序员来指定,名字也不一定是叫这个名字,在配置文件里进行指定是调用那个方法作为初始化/销毁方法
  2. 销毁方法就是当关闭容器时,才会被调用

1.16 配置 bean 的后置处理器(重点)

  1. 在 spring 的 ioc 容器,可以配置 bean 的后置处理器
  2. 该处理器/对象会在 bean 初始化方法调用前和初始化方法调用后被调用
  3. 在后置处理器中编写自己的代码

1)创建后置处理器

...\spring\beans\MyBeanPostProcessor.java

要实现 BeanPostProcessor 接口的两个方法作为一个前一个后

java 复制代码
public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
    * 在 bean 初始化之前完成某些任务
    * @param bean: 就是 ioc 容器返回的 bean 对象,
    *              如果被替换会修改,返回的 bean 对象也会被修改
    * @param beanName: 就是 ioc 容器配置的 bean 的名称
    * @return Object: 就是返回的 bean 对象
    */
    public Object postProcessBeforeInitialization(Object bean, String beanName)
        throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("postProcessBeforeInitialization被 调 用" 
        + beanName + "bean= " + bean.getClass());
        return bean;
    }
    /**
    * 在 bean 初始化之后完成某些任务
    * @param bean: 就是 ioc 容器返回的 bean 对象, 如果被替换会修改,
    *        返回的 bean 对象也会被修改
    * @param beanName: 就是 ioc 容器配置的 bean 的名称
    * @return Object: 就是返回的 bean 对象
    */
    public Object postProcessAfterInitialization(Object bean, String beanName)
        throws BeansException {
        System.out.println("postProcessAfterInitialization 被调用 " 
            + beanName + " bean= "+ bean.getClass());
        return bean;
    }
}
    

2)xml 配置

java 复制代码
<bean id="house" class="com.hspedu.spring.beans.House"
        init-method="init"
        destroy-method="destory">
    <property name="name" value="北京豪宅"/>
</bean>

bean 后置处理器的配置,配置后这个后置处理器作用在该容器创建的 bean 对象中

java 复制代码
<bean id="myBeanPostProcessor"
    class="com.hspedu.spring.beans.MyBeanPostProcessor" />
</beans>

3)测试

java 复制代码
@Test
public void testBeanPostProcessor() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans02.xml");
    House house = ioc.getBean("house", House.class);
    System.out.println(house);
    //关闭容器
    ((ConfigurableApplicationContext) ioc).close();
}

就已经可以触发后置处理器了!!!

4)说明

1. 怎么执行到这个方法?

使用 AOP(反射+动态代理+IO+容器+注解)

2、有什么用?

可以对 IOC 容器中所有的对象进行统一处理 ,比如 日志处理/权限的校验/安全的验证/事务管理

3、针对容器的所有对象吗?

是的 => 这也就是AOP切面编程特点

1.17 通过属性文件给 bean 注入值

1)创建 my.properties

在 src 目录下创建 注以是 Unicode 编码

java 复制代码
name=\u9EC4\u888D\u602A
id=10
skill=\u72EE\u5B50\u543C

2)xml 配置

  1. 通过属性文件给 bean 注入值,

  2. 需要导入: xmlns:context 名字空间,并指定属性文件路径 location="xxxx"

XML 复制代码
<context:property-placeholder location="classpath:my.properties"/>
<bean id="monster100" class="com.hspedu.spring.beans.Monster">
    <property name="monsterId" value="${id}"/>
    <property name="name" value="${name}"/>
    <property name="skill" value="${skill}"/>
</bean>

3)测试

java 复制代码
@Test
public void setProByProFile() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    Monster monster100 = ioc.getBean("monster100", Monster.class);
    System.out.println(monster100);
}

1.18 基于 XML 的 bean 的自动装配

1)创建对象

...\spring\dao\OrderDao.java

java 复制代码
public class OrderDao {
    public void saveOrder() {
        System.out.println("保存 一个订单...");
    }
}

...\spring\service\OrderService.java

java 复制代码
public class OrderService {
    private OrderDao orderDao;
    public OrderDao getOrderDao() {
        return orderDao;
    }
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }
}

...\spring\action\OrderAction.java

java 复制代码
public class OrderAction {
    private OrderService orderService;
    public OrderService getOrderService() {
        return orderService;
    }
    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }
}

2)xml 配置

autowire="byType" 表示根据类型进行自动组装

比如OrderService 对象有 private OrderDao orderDao,就会在容器中去找有没有 OrderDao类型对象;如果有,就会自动的装配 --> 这个容器中,不能有两个的OrderDao类型对象

XML 复制代码
<bean id="orderAction" autowire="byType" 
            class="com.hspedu.spring.action.OrderAction"/>
<bean id="orderService" autowire="byType" 
            class="com.hspedu.spring.service.OrderService"/>
<bean id="orderDao" class="com.hspedu.spring.dao.OrderDao"/>

autowire = "byName" 表示通过名字完成自动装配

OrderService 属性 private OrderDao orderDao,根据这个属性的setXxx()方法的 xxx 来找对象id,public void setOrderDao() 就会找id=orderDao对象来进行自动装配,如果没有就装配失败

XML 复制代码
<bean autowire="byName" class="com.hspedu.spring.service.OrderService"
          id="orderService"/>
<bean autowire="byName" class="com.hspedu.spring.web.OrderAction" 
          id="orderAction"/>
<bean id="orderDao" class="com.hspedu.spring.dao.OrderDao"/>

3)测试

java 复制代码
@Test
public void setProByAutowire() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    OrderAction orderAction = ioc.getBean("orderAction", OrderAction.class);
    orderAction.getOrderService().getOrderDao().saveOrder();
}

4)说明

这个知识点作为了解即可,后面主要还是使用基于注解的方式

1.19 spring el 表达式

支持运行时查询并可以操作对象

1)创建对象

...\spring\beans\SpELBean.java

java 复制代码
public class SpELBean {
    private String name;
    private Monster monster;
    private String monsterName;
    private String crySound;
    private String bookName;
    private Double result;
    public SpELBean() {}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}

    public Monster getMonster() {return monster;}
    public void setMonster(Monster monster) {this.monster = monster;}

    public String getMonsterName() {return monsterName;}
    public void setMonsterName(String monsterName) {this.monsterName = monsterName;}

    public String getCrySound() {return crySound;}
    public void setCrySound(String crySound) {this.crySound = crySound;}

    public String getBookName() {return bookName;}
    public void setBookName(String bookName) {this.bookName = bookName;}

    public Double getResult() {return result;}
    public void setResult(Double result) {this.result = result;}

    public String cry(String sound) {return "发出 " + sound + "叫声...";}

    public static String read(String bookName) {return "正在看 " + bookName;}
}

2)xml 配置

XML 复制代码
<bean id="spELBean" class="com.hspedu.spring.beans.SpELBean">
    <!-- sp el 给字面量 -->
    <property name="name" value="#{'hsp教育'}"/>
    <!-- sp el 引用其它 bean -->
    <property name="monster" value="#{monster01}"/>
    <!-- sp el 引用其它 bean 的属性值 -->
    <property name="monsterName" value="#{monster02.name}"/>
    <!-- sp el 调用普通方法赋值 -->
    <property name="crySound" value="#{spELBean.cry('喵喵的..')}"/>
    <!-- sp el 调用静态方法 赋值 -->
    <property name="bookName" value="#{T(com.hspedu.spring.beans.SpELBean).
        read('天龙八部')}"/>
    <!-- sp el 通过运算赋值 -->
    <property name="result" value="#{89*1.2}"/>
</bean>

3)测试

java 复制代码
@Test
public void setProBySpel() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    SpELBean spELBean = ioc.getBean("spELBean", SpELBean.class);
    System.out.println(spELBean.getName());
    System.out.println(spELBean.getMonster());
    System.out.println(spELBean.getMonsterName());
    System.out.println(spELBean.getCrySound());
    System.out.println(spELBean.getBookName());
    System.out.println(spELBean.getResult());
}

4)说明

不是重点,如果看到有人这样使用,能看懂即可


2. 基于注解配置 bean

基于注解的方式配置 bean, 主要是项目开发中的组件,比如 Controller、Service、和 Dao

组件注解的形式有:

  1. @Component 表示当前注解标识的是一个组件
  2. @Controller 表示当前注解标识的是一个控制器,通常用于 Servlet
  3. @Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于 Service 类
  4. @Repository 表示当前注解标识的是一个持久化层的类,通常用于 Dao 类

2.1 快速入门

1)引入包 spring-aop-5.3.8.jar

2)创建对象

java 复制代码
@Repository
public class UserDao {
}

@Service
public class UserService {
}

@Controller
public class UserAction {
}

@Component
public class MyComponent {
}

3)xml 配置

配置自动扫描的包,注意需要加入 context 名称空间

  1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器
  2. base-package 指定要扫描的包
  3. 当spring容器创建/初始化时,就会扫描com.hspedu.spring.component包下的所有的 有注解 @Controller / @Service / @Respository / @Component类 将其实例化,生成对象,放入到ioc容器
  4. resource-pattern="User*.class" 表示只扫描com.hspedu.spring.component 和它的子包下的User打头的类(使用较少,其实只要不加注解就能解决
XML 复制代码
<context:component-scan base-package="com.hspedu.spring.component" />

4)测试

java 复制代码
@Test
public void getBeanByAnnotation() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    UserAction userAction = ioc.getBean(UserAction.class);
    System.out.println(userAction);
    UserDao userDao = ioc.getBean(UserDao.class);
    System.out.println(userDao);
    MyComponent myComponent = ioc.getBean(MyComponent.class);
    System.out.println(myComponent);
    UserService userService = ioc.getBean(UserService.class);
    System.out.println(userService);
}

调试查看 ioc 容器

5)注意

Spring 的 IOC 容器不能检测一个使用了@Controller 注解的类到底是不是一个真正的控制器

注解的名称是用于程序员自己识别当前标识的是什么组件。

其它的@Service @Repository 也是一样的道理 (也就是说 spring 的 IOC 容器只要检查到注解就会生成对象,但是这个注解的含义 spring 不会识别,注解是给程序员编程方便看的)

6)排除某种类型下的某种类型的注解

XML 复制代码
<context:component-scan base-package="com.hspedu.spring.component">
    <context:exclude-filter type="annotation"
    expression="org.springframework.stereotype.Service"/>
</context>
  1. <context:exclude-filter> 放在<context:component-scan>内,表示扫描过滤掉当前包的某些类
  2. type="annotation" 按照注解类型进行过滤
  3. expression :就是注解的全类名,这里就是 @Service 注解的全类名
  4. 上面表示过滤掉 com.hspedu.spring.component 包下,加入了@Service 注解的类

7)指定自动扫描哪些注解类

目的:只扫描 Service Controller

XML 复制代码
<context:component-scan base-package="com.hspedu.spring.component"
    use-default-filters="false">
    <context:include-filter type="annotation"
        expression="org.springframework.stereotype.Service"/>
    <context:include-filter type="annotation"
        expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

8)注解类名

默认情况:标记注解后,类名首字母小写作为 id 的值

也可以使用注解的 value 属性指定 id 值,并且 value 可以省略

java 复制代码
@Controller(value="userAction01")

@Controller("userAction01")

9)拓展

链接:彻底弄懂@Controller 、@Service、@Component

2.2 自动装配

基于注解配置 bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource

1) @AutoWired 的规则说明

  1. 在 IOC 容器中查找待装配的组件的类型,如果有唯一的 bean 匹配,则使用该 bean 装
  2. 如待装配的类型对应的 bean 在 IOC 容器中有多个,则使用待装配的属性的属性名作
    为 id 值再进行查找
    , 找到就装配,找不到就抛异常

2)@Resource 的规则说明

  1. @Resource 有两个属性是比较重要的,分是 name 和 type, Spring 将@Resource 注解的
    name 属性解析为 bean 的名字,而 type 属性则解析为 bean 的类型.所以如果使用 name 属
    性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略
  2. 如果@Resource 没有指定 name 和 type ,则先使用byName注入策略, 如果匹配不上,
    再使用 byType 策略
    , 如果都不成功,就会报错

3)使用推荐

不管是@Autowired 还是 @Resource 都保证属性名是规范的写法就可以注入

但是更推荐使用@Resource

4)注意说明

java 复制代码
@Autowired
@Qualifier(value = "userService02")
//指定 id=userService02 的 UserService 对象进行组装
private UserService userService;

指定 id 进行组装, 这时,是装配的 id=userService02 , 需要两个注解都需要写上

2.3 泛型依赖注入

为了更好的管理有继承和相互依赖的 bean 的自动装配,spring 还提供基于泛型依赖的注入机制

在继承关系复杂情况下,泛型依赖注入就会有很大的优越性

1)创建对象

java 复制代码
public class Book {}
======================
public class Phone {}
======================
public abstract class BaseDao<T> {
    public abstract void save();
}
======================
@Repository
public class BookDao extends BaseDao<Book> {
    @Override
    public void save() {
        System.out.println("BookDao 的 save()");
    }
}
======================
@Repository
public class PhoneDao extends BaseDao<Phone> {
    @Override
    public void save() {
        System.out.println("PhoneDao 的 save()");
    }
}
======================
public class BaseService<T> {
    @Autowired
    private BaseDao<T> baseDao;
    public void save() {
        baseDao.save();
    }
}
======================
@Service
public class BookService extends BaseService<Book> {}
======================
@Service
public class PhoneService extends BaseService<Phone> {}

2)xml 配置

XML 复制代码
<context:component-scan base-package="com.hspedu.spring.depinjection"/>

3)测试

XML 复制代码
@Test
public void setProByDepinjectionAutowired() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    BookService bookService = ioc.getBean(BookService.class);
    bookService.save();
    PhoneService phoneService = ioc.getBean(PhoneService.class);
    phoneService.save();
}

debug,可以看到 baseDao 的 BookDao 已经注入到 bookService 中

相关推荐
程序员-小李14 分钟前
餐厅下单助手系统(Java+MySQL)
java·开发语言·mysql
开心工作室_kaic18 分钟前
springboot496基于java手机销售网站设计和实现(论文+源码)_kaic
java·开发语言·智能手机
像少年啦飞驰点、19 分钟前
SpringBoot + HttpSession 自定义生成sessionId
java·开发语言
珊珊来吃24 分钟前
EXCEL中给某一列数据加上双引号
java·前端·excel
我曾经是个程序员31 分钟前
使用C#生成一张1G大小的空白图片
java·算法·c#
向阳121835 分钟前
mybatis SqlSessionFactory
java·mybatis
mask哥36 分钟前
算法:LeetCode470_用Rand7()实现Rand10()_java实现
java·开发语言
搬码后生仔38 分钟前
将 ASP.NET Core 应用程序的日志保存到 D 盘的文件中 (如 Serilog)
后端·asp.net
Suwg20940 分钟前
《手写Mybatis渐进式源码实践》实践笔记(第七章 SQL执行器的创建和使用)
java·数据库·笔记·后端·sql·mybatis·模板方法模式
丁总学Java44 分钟前
优化 invite_codes 表的 SQL 创建语句
java·数据库·sql