Spring Xml 方式 Bean 管理

一、Spring 工厂类

​ 传统的由 BeanFactory 创建工厂的方法已过时,现在直接从文件路径或类路径得到 ApplicationContext Spring工厂。

  • 上面是从类路径中读取配置文件: ClassPathXmlApplicationContext
  • 从磁盘目录中读取配置文件 : FileSystemXmlApplicationContext
public void demo3() {
	// 创建Spring工厂
	ApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
	// 通过工厂获得类
	UserService userService = (UserService) applicationContext.getBean("userService");
	userService.sayHello();
}
  • Spring老版本的工厂类(了解,已过时)
public void demo4() {
    // 创建工厂类
    BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    // 通过工厂获得类
    UserService userService = (UserService) beanFactory.getBean("userService");
    userService.sayHello();
}

二、XML方法实例化Bean的三种方式

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

//1***** Bean1
public class Bean1 {
    public Bean1() {
        System.out.println("Bean1实例化成功。");
    }
}

//2***** applicationContext.xml 中添加配置
<!-- Bean实例化方式1:无参构造器方法 -->
<bean id="bean1" class="com.moc.ioc.demo2.Bean1" />

//3***** 测试
public class BeanTestDemo {
    @Test
    public void demo1() {
        // 创建工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 通过工厂获得类的实例
        Bean1 bean1 = (Bean1)applicationContext.getBean("bean1");
    }
}       

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

//1***** Bean2
public class Bean2 {
}

//2***** Bean2Factory
public class Bean2Factory {
    public static Bean2 createBean2() {
        System.out.println("Bean2工厂创建Bean2");
        return new Bean2();
    }
}

//3***** applicationContext.xml 中添加配置
<!-- Bean实例化方式2:静态工厂方法 -->
<bean id="bean2" class="com.moc.ioc.demo2.Bean2Factory" factory-method="createBean2" />

//4***** 测试
@Test
public void demo2() {
    // 创建工厂 (加载配置文件时,会把配置文件中所有的类都实例化)
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 通过工厂获得类的实例
    Bean2 bean2 = (Bean2)applicationContext.getBean("bean2");
}

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

//1***** Bean3
public class Bean3 {
}

//2***** Bean3Factory
public class Bean3Factory {
    public Bean3 createBean3() {
        System.out.println("Bean3工厂创建Bean3");
        return new Bean3();
    }
}

//3***** applicationContext.xml 中添加配置
<!-- Bean实例化方式2:实例工厂方法 -->
<bean id="bean3Factory" class="com.moc.ioc.demo2.Bean3Factory" />
<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3" />
    
//4***** 测试
@Test    
public void demo3() {
    // 创建工厂
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 通过工厂获得类的实例
    Bean3 bean3 = (Bean3)applicationContext.getBean("bean3");
}

4、Bean标签的常见属性

id和name

  1. 一般情况下,装配一个Bean时,通过指定一个id属性作为Bean的名称
  2. id 属性在IOC容器中必须是唯一的
  3. 如果Bean的名称中含有特殊字符,就需要使用name属性

class

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

scope

类别 描述
singleton 在SpringIOC容器中仅存在一个Bean实例,Bean以单实例的方式存在(默认)
prototype 每次调用getBean()时都会返回一个新的实例(整合Struct2时用到)
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同的HTTPSession使用不同的Bean。该作用域仅适用于WebApplicationContext环境

三、Bean的生命周期

1、Bean标签中配置的两个生命周期方法

init-method:当bean被载入到容器的时候调用init
destory-method:当bean从容器中删除的时候调用destroy(scope=singleton有效)​

<bean id="xxx"  class="..." init-method="init"  destory-method="destroy" />
  • 测试
//1***** Bean
public class LifeDemo {
    public LifeDemo() {
        System.out.println("Demo被实例化啦...");
    }

    public void setup() {
        System.out.println("Demo被初始化啦...");
    }

    public void teardown() {
        System.out.println("Demo被销毁啦...");
    }
}

//2***** applicationContext.xml 中添加配置
<bean id="LifeDemo" class="com.moc.ioc.demo3.LifeDemo" init-method="setup" destroy-method="teardown" />
    
//3***** 测试
public class LifeTestDemo {
    @Test
    public void demo1() {
        // 创建工厂 (这里要关闭工厂,需要使用实现类而不是接口)
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 通过工厂获得类的实例
        LifeDemo lifeDemo = (LifeDemo)applicationContext.getBean("LifeDemo");

        // 关闭工厂,会执行Bean的销毁
        applicationContext.close();
    }
}    

2、Bean的完整生命周期

  1. instantiate bean对象实例化
  2. populate properties 封装属性
  3. 如果Bean实现BeanNameAware 执行 setBeanName
  4. 如果Bean实现BeanFactoryAware 或者ApplicationContextAware 设置工厂setBeanFactory 或者上下文对象 setApplicationContext
  5. 如果存在类实现BeanPostProcessor(后处理Bean) 执行postProcessBeforelnitialization
  6. 如果Bean实现InitializingBean 执行 afterPropertiesSet
  7. 调用 <bean init-method= “init” >指定初始化方法 init
  8. 如果存在类实现BeanPostProcessor(处理Bean) 执行postProcessAfterInitialization
  9. 执行业务方法
  10. 如果Bean实现DisposableBean 执行 destroy
  11. 调用<bean destroy-method= “customerDestroy” >指定销毁方法customerDestroy
  • 测试
//1***** Bean
public class FullLife implements BeanNameAware, ApplicationContextAware,
        InitializingBean, DisposableBean {
    private String name;

    public FullLife() {
        System.out.println("第一步:初始化 ...");
    }

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

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

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("第四步:了解工程信息 ...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("第六步:属性设置后执行 ...");
    }

    public void setup() {
        System.out.println("第七步:执行<bean>中指定的init-method方法");
    }

    public void run() {
        System.out.println("第九步:执行Bean自身的业务方法(除了其余步骤以外的其他方法,方法名任意)");
    }

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

    public void teardown() {
        System.out.println("第十一步:执行<bean>中指定的destroy-method方法");
    }
}

//2***** 存在类实现BeanPostProcessor
// 该类在每个Bean的生命周期中都会只执行
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException {
        System.out.println("第五步:初始化前方法 ...");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String s) throws BeansException {
        System.out.println("第八步:初始化后方法 ...");
        return bean;
    }
}

//3***** applicationContext.xml 中添加配置
<!-- Bean的全生命周期 -->
<bean id="fullLife" class="com.moc.ioc.demo3.FullLife" init-method="setup" destroy-method="teardown">
    <property name="name" value="小红"/>
</bean>
<bean class="com.moc.ioc.demo3.MyBeanPostProcessor"/>

//4***** 测试
public class LifeTestDemo {
    @Test
    public void demo2() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        FullLife fullLife = (FullLife)applicationContext.getBean("fullLife");
        
        fullLife.run();
        applicationContext.close();
    }
}

Spring Xml 方式 Bean 管理

3、增强一个Bean类的方法(aop)

​ 使用 实现BeanPostProcessor类 来增强 Bean类中的方法。

//1***** 一个Bean类的接口
public interface UserDao {
    public void findAll();
    public void save();
}

//2***** 一个Bean实现类
public class UserDaoImpl implements UserDao {
    @Override
    public void findAll() { System.out.println("查询用户..."); }

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

//3***** applicationContext.xml 中添加配置
<bean id="userDao" class="com.moc.ioc.demo3.UserDaoImpl"/>
<bean class="com.moc.ioc.demo3.MyBeanPostProcessor"/>

//4***** 实现BeanPostProcessor的类来增强Bean类中的指定方法
public class MyBeanPostProcessor implements BeanPostProcessor {
	//第八步:初始化后方法
    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        
        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;
        }
    }
}

//5***** 测试
public class TestDemo {
    @Test
    public void demo() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao)applicationContext.getBean("userDao");

        userDao.findAll();
        userDao.save();
    }
}

Spring Xml 方式 Bean 管理

四、XML方式的属性注入

  • 对于类成员变量,注入方式有三种
  1. 构造函数注入

  2. 属性setter方法注入

  3. 接口注入

    Spring支持前两种。

1、构造方法注入

​ 通过构造方法注入Bean 的属性值或依赖的对象,它保证了Bean 实例在实例化后就可以使用。
构造器注入在 <constructor-arg> 元素里声明的属性。

//*****1 Bean实体类
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() {...}
}

//*****2 applicationContext.xml 配置Bean
<bean id="user" class="com.imooc.ioc.demo4.User">
    <constructor-arg name="name" value="张三" />
    <constructor-arg name="age" value="23"/>
</bean>

//*****3 测试
public class SpringDemo {
    @Test
    public void demo1(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User)applicationContext.getBean("user");
        System.out.println(user);
    }
}

2、set方法注入

使用set方法注入,在Spring配置文件中,通过 <property> 设置注入的属性。

//*****1 Bean实体类
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() { ... }
}

//*****2 Bean实体类注入的引用类
public class Cat {
    private String name;

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

    @Override
    public String toString() { ... }
}


//*****3 applicationContext.xml 配置Bean
<bean id="person" class="com.imooc.ioc.demo4.Person">
    <property name="name" value="李四"/>
    <property name="age" value="32"/>
    <property name="cat" ref="cat"/>
</bean>
<bean id="cat" class="com.imooc.ioc.demo4.Cat">
    <property name="name" value="ketty"/>
</bean>

//*****4  SpringDemo类添加测试方法
@Test
public void demo2(){
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	Person person = (Person)applicationContext.getBean("person");
	System.out.println(person);
}

3、p名称空间

为了简化XML文件配置,Spring从2.5开始引入一个新的p名称空间。

p:<属性名>="xxx”引入常量值
p:<属性名>-ref="xxx”引用其它Bean对象

<!-- 采用p名称空间完成上面Person的Bean配置 -->
<beans>里添加属性:  xmlns:p="http://www.springframework.org/schema/p"
<bean id="person" class="com.imooc.ioc.demo4.Person" p:name="大黄" p:age="34" p:cat-ref="cat"/>
<bean id="cat" class="com.imooc.ioc.demo4.Cat" p:name="小黄"/>

4、SpEL注入

SpEL(spring expression language,spring表达式语言):对依赖注入进行简化

  • 语法:#{表达式}
  • <bean id=" " value="#{表达式}">

#{‘hello’}:使用字符串(单引号)
#{Beanld}:使用另一个bean
#{Beanld.method()}:使用另一个bean的方法
#{T(java.lang.Math).PI}:使用静态字段或方法

//*****1 Bean实体类
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() { ... }
}

//*****2 演示使用SpEL提供的类
public class ProductInfo {
    public Double calculatePrice(){ return Math.random() * 200; }
}
public class Category {
    private String name;
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    @Override
    public String toString() { ... }
}

//*****3 applicationContext.xml 配置Bean
<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="#{'男装'}"/>
    <property name="price" value="#{productInfo.calculatePrice()}"/>
    <property name="category" value="#{category}"/>
</bean>

//*****4  SpringDemo类添加测试方法
@Test
public void demo3(){
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	Product product = (Product)applicationContext.getBean("product");
	System.out.println(product);
}

4、复杂类型的属性注入

  1. 数组类型的属性注入
  2. List集合类型的属性注入
  3. Set集合类型的属性注入
  4. Map集合类型的属性注入
  5. Properties类型的属性注入
//*****1 Bean实体类
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() { ... }
}

//*****2 applicationContext.xml 配置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>
        </list>
    </property>
    <!--Set集合的属性注入-->
    <property name="set">
        <set>
            <value>ddd</value>
            <value>eee</value>
            <value>fff</value>
        </set>
    </property>
    <!--Map集合的属性注入-->
    <property name="map">
        <map>
            <entry key="aaa" value="111"/>
            <entry key="bbb" value="222"/>
            <entry key="ccc" value="333"/>
        </map>
    </property>
    <!--Properties的属性注入-->
    <property name="properties">
        <props>
            <prop key="username">root</prop>
            <prop key="password">1234</prop>
        </props>
    </property>
</bean>

//*****4  SpringDemo类添加测试方法
    @Test
    public void demo4(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

       CollectionBean collectionBean = (CollectionBean)applicationContext.getBean("collectionBean");

       System.out.println(collectionBean);
    }
}