Java EE SSM框架整合开发(三)Spring Bean

524 阅读6分钟

Bean是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。为了让Spring IoC容器正确的实例化Bean,需要对Bean实例化的相关信息进行配置。

4926.png

装配的三种方式

实例化及依赖注入的方法。

1.1 基于XML的显式装配

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="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>
    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions go here -->
</beans>

id代表bean的唯一标识,class代表bean的类型

1.1.1 实例化方法:构造方法(常用)、静态工厂和实例工厂

三种实例化方法的XML配置方法:

<!-- 一、使用默认构造函数创建,如果没有该默认构造函数,则创建失败。 -->
<bean id="userService1" class="com.smday.service.impl.UserServiceImpl"></bean>

<!-- 二、使用普通公章中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器 -->
<bean id="instanceFactory" class="com.smday.factory.InstanceFactory"></bean>
<bean id="userService2" factory-bean="instanceFactory" factory-method="getUserService"></bean>

<!-- 三、使用工厂中的静态方法创建对象 -->
<bean id="userService3" class="com.smday.factory.StaticFactory" factory-method="getUserService"></bean>

工厂方法需要对应的工厂类:

//实例工厂方法对应的工厂类
public class InstanceFactory {
    public BeanClass getUserService() {
        return new BeanClass("实例工厂方法");
    }
}
//静态工厂对应的类
public class StaticFactory {
    private static BeanClass beanInstance = new BeanClass("调用静态工厂方法实例化Bean");
    public static BeanClass createInstance() {
        return beanInstance;
    }
}

使用时,使用getBean("id")即可,如

BeanClass b = (BeanClass)appCon.getBean("staticFactoryInstance");

1.1.2 依赖注入方法:构造器注入、setter注入

构造器注入:<constructor-arg>标签

需要实例化的Bean定义:

public class ExampleBean {
    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;
    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}

在xml中使用构造器注入:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

value:用于提供基本类型和String类型的数据。

ref:用于提供其他的bean类型数据,在spring的ioc核心容器中出现过的bean对象

setter注入:<property>标签

需要实例化的Bean定义,需要提供 set方法

public class ExampleBean {
    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;
    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }
    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }
    public void setIntegerProperty(int i) {
        this.i = i;
    }
}

XML文件

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

name:指定注入时调用的set方法的属性名称。

value:提供基本类型和String类型的数据。

ref:提供其他的bean类型数据,在spring的ioc核心容器中出现过的bean对象。

集合类型的注入:list和map两类

<bean id="userService" class="com.smday.service.impl.UserServiceImpl">
    <property name="myStrs">
        <array>
            <value>AAA</value>
            <value>BBB</value>
            <value>BBB</value>
        </array>
    </property>
    <property name="myList">
        <list>
            <value>AAA</value>
            <value>BBB</value>
            <value>BBB</value>
        </list>
    </property>
    <property name="mySet">
        <set>
            <value>AAA</value>
            <value>BBB</value>
            <value>BBB</value>
        </set>
    </property>
    <property name="myMap">
        <map>
            <entry key="testA" value="AAA"></entry>
            <entry key="testB" >
                <value>BBB</value>
            </entry>
        </map>
    </property>
    <property name="myProp">
        <props>
            <prop key="testC">CCC</prop>
            <prop key="testD">DDD</prop>
        </props>
    </property>
</bean>

list结构:使用list、array和set标签

map结构:使用map和props标签

1.2 基于Java的显式装配

嘿嘿,老师没讲不写了

1.3基于注解的自动装配

Spring从以下两个角度实现自动装配:

  • 组件扫描:Spring自动发现应用上下文中所创建的bean,即扫描出项目中需要管理的bean并实例化放入缓存池。在bean类前使用注解,并在XML中设置<context:component-scan base-package="xxx"/>实现。
  • 自动装配:Spring自动满足bean之间的依赖,如果一个bean中有其他bean作为成员,可自动在Spring缓存池中寻找对应的bean。在成员属性前添加注解@Autowired实现。

1.3.1 基本结构

比如Controller层要使用Service层的bean:

定义一个Service层接口

public interface UserService {
    void add();
}

定义Service层接口实现类

@Component注解告知Spring创建这个bean,@Component相当于@Component(),或@Component("userServiceImpl"),或@Component(value = "userServiceImpl"),value为Bean的id,默认为首字母小写的类名,可自定义。

@Component
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("添加用户");
    }
}

定义Controller层

@Value注解将外部的值动态注入到Bean (相关博客)

@Autowired注解告诉Spring自动注入Userservice,默认按照Bean类型装配

@Component
public class UserController {
	@Value("注入普通字符串")
	private String s;
      
	@Autowired
        private UserService userservice;
        public String getS() {
                return s;
	}
	public void setS(String s) {
		this.s = s;
	}
	public void add(){
		System.out.println("s = "+s);
                userservice.add();
    }
}

在XML中配置注解,指定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"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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
        http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd">
        
    <context:component-scan base-package="com.service" />
	<context:component-scan base-package="com.controller" />
    
</beans>

测试类

public class Test {
	public static void main(String args[]) {
		@SuppressWarnings("resource")
		ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserController tmp = (UserController) appCon.getBean("userController");
		tmp.add();
	}
}

1.3.2 常用注解

@Component

  • 作用:将当前类对象存入spring容器中。

  • 属性:value,用于指定bean的id,不指定value时,默认值为当前类名首字母小写。

  • 在三层架构中,Spring框架提供了明确的三层注释,作用与@Component相同,但语义更加清晰,分别是:@Controller:控制层、@Service:业务层、@Respository:数据访问层

@Autowired

  • 作用:对类成员变量、方法及构造方法进行标注,完成自动装配的工作。自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。在使用注解注入时,set方法就不是必须的了。

  • 如果IoC容器中没有任何bean类型和要注入的变量类型匹配,则报错(可以设置required属性的值为false,如果没找到对应类型的bean,则会出于未装配状态)

  • 如果IoC容器中有多个类型匹配时出现歧义,也会报错,可以考虑使用@Qualifier和@Resource注解,参考:Spring解决自动装配歧义性的几种方案

@Resource

  • 与@Autowired功能一样。区别在于,该注解默认是按照名称来装配注入的,只有当找不到与名称匹配的Bean才会按照类型来装配注入;

  • @Resource注解有两个属性:name和type。name属性指定Bean实例名称,即按照名称来装配注入;type属性指定Bean类型,即按照Bean的类型进行装配。

@Qualifier

  • 当@Autowired注解需要按照名称来装配注入,则需要结合该注解一起使用,Bean的实例名称由@Qualifier注解的参数指定。

Bean的作用域

作用域名称描述
singleton默认的作用域,使用singleton定义的Bean在Spring容器中只有一个Bean实例。
prototypeSpring容器每次获取prototype定义的Bean,容器都将创建一个新的Bean实例。
request在一次HTTP请求中容器将返回一个Bean实例,不同的HTTP请求返回不同的Bean实例。仅在Web Spring应用程序上下文中使用。
session在一个HTTP Session中,容器将返回同一个Bean实例。仅在Web Spring应用程序上下文中使用。
application为每个ServletContext对象创建一个实例,即同一个应用共享一个Bean实例。仅在Web Spring应用程序上下文中使用。
websocket为每个WebSocket对象创建一个Bean实例。仅在Web Spring应用程序上下文中使用。

针对不同的装配方法,有不同的作用域定义方法:

  1. 使用scope="xxx"在XML文件中设置作用域
<bean id="constructorInstance" class="instance.BeanClass" scope="prototype"/>
  1. 在类定义时使用@Scope(xxx)注解
@Component
@Scope("prototype")
public class UserController {
	@Autowired
    private UserService userservice;
}

测试作用域,可以看到prototype作用域下,每次getBean都会得到新的实例

public class TestController {
	@Test
	public void testMethod(){
        ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserController tmp1 = (UserController) appCon.getBean("userController");
		System.out.println(tmp1);//com.controller.UserController@4d14b6c2
		
		UserController tmp2 = (UserController) appCon.getBean("userController");
		System.out.println(tmp2);//com.controller.UserController@c05fddc
    }
}

Bean的生命周期

6f5.jpg

调用定制的初始化方法、调用定制的销毁方法分别可以使用属性init-methoddestroy-method进行配置。

Bean

package life;
public class BeanLife {
    public void initMyself() {
        System.out.println(this.getClass().getName() + "执行自定义的初始化方法");
    }
    public void destroyMyself() {
        System.out.println(this.getClass().getName() + "执行自定义的销毁方法");
    }
}

XML配置文件

<bean id="beanLife" class="life.BeanLife" init-method="initMyself" destroy-method ="destroyMyself"/>

测试

public static void main(String[] args) {
   //初始化Spring容器,加载配置文件
   //为了方便演示销毁方法的执行,这里使ClassPathXmlApplicationContext实现类声明容器
   ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
   System.out.println("获得对象前");
   BeanLife blife = (BeanLife)ctx.getBean("beanLife");
   System.out.println("获得对象后" + blife);
   ctx.close();//关闭容器,销毁Bean对象
}

输出 image.png