Spring学习(3)IoC之Bean的装配

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SDDDLLL/article/details/86598987

Bean的装配,也就是bean对象在容器中的创建。然后容器根据我们的代码需求,将Bean对象传递过来。

这里有三种装配方式:

  • 默认装配方式
  • 动态工厂方式
  • 静态工厂方式

先看项目结构:

先给出applicationContext

<?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">

    <!-- 注册Service-->  
    <bean id="myService" class="com.fdd.service.SomeServiceImpl"/>

</beans>

第一步创建接口:ISomeService

package com.fdd.service;

public interface ISomeService {
	public void doSome();
	public void doOther();
}

 第二步:创建实现类

public class SomeServiceImpl implements ISomeService {
	//添加无参构造器
	public SomeServiceImpl() {
		System.out.println("无参构造器:SomeServiceImpl实现类");
	}

	public void doSome() {
		System.out.println("doSome实现");
	}

	public void doOther() {
		System.out.println("doOther实现");
	}
}

第三步:测试

public class MyTest {
	
	@Test
	public void test02(){
		//创建容器对象,记载Spring配置文件,会从类路径下面查找文件也就是src下面
		ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
		

		ISomeService service = (ISomeService) ac.getBean("myService");
		service.doSome();
	}
	
}

一、默认装配方式

代码通过getBean方式从容器中获取指定的Bean实例,容器首先会调用Bean类的无参构造器,创建空值的实例对象。

z在上面代码的基础之上直接运行:

二、动态工厂方式

有些时候,项目需要使用工厂来创建Bean实例,而不能像前面例子中似的,直接由Spring容器来装配。

1、首先创建一个工厂类

public class ServiceFactory {
	
	public ISomeService getISomeService(){
		return new SomeServiceImpl();
	}

}

2、在配置文件中配置该工厂

<?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="myService" class="com.fdd.service.ServiceFactory"/>

</beans>

 注意到这里我们配置的是工厂,而不是bean。这是因为我们的bean是通过工厂来创建的。

3、修改测试类

        @Test
	public void test01(){
		//创建容器对象,记载Spring配置文件,会从类路径下面查找文件也就是src下面
		ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
		//从Spring容器中获取该工厂
		ServiceFactory factory=(ServiceFactory) ac.getBean("myFactory");
		//通过factory获取service
		ISomeService service=factory.getISomeService();
		service.doSome();
	}

4、改进

我们可以看到,我们在工厂中需要去new一个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 id="myFactory" class="com.fdd.service.ServiceFactory"/>
	<bean id="someService" factory-bean="myFactory" factory-method="getISomeService"></bean>
</beans>

此时我们就不需要获取Factory了,我们直接从容器中获取Service

	@Test
	public void test01(){
		//创建容器对象,记载Spring配置文件,会从类路径下面查找文件也就是src下面
		ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
		//通过factory获取service
		ISomeService service=(ISomeService) ac.getBean("someService");
		service.doSome();
		service.doOther();
	}

三、静态工厂方式

使用静态工厂创建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">

    <!-- 注册service-->  
	<bean id="someService" class="com.fdd.service.ServiceFactory" 
			factory-method="getISomeService">
	</bean>
</beans>

四、容器中bean的作用域

当通过Spring容器来创建一个bean实例的时候,不仅可以完成bean的实例化,还可以通过scope属性来制定bean的作用域。Spring支持5中作用域:

  • singleton:单态模式。也就是说在整个Spring容器中,使用singleton定义的bean将是单例的,只有一个实例。这也是默认的。
  • prototype:原型模式。也就是说每次使用getBean方法获取的同一个<bean/>对象都是一个新的实例。
  • request:对于每次HTTP请求,都会产生一个不同的bean实例
  • session:对于不同的session,都会产生一个新的实例、
  • global session:每一个全局的session对应一个bean实例。这是在集群中使用的。

需要注意的是:

  1. singleton:表示该bean是在容器中被创建时候就已经被装配好了
  2. prototype: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">

    <!-- 注册service-->  
	<bean id="someService1" class="com.fdd.service.SomeServiceImpl"  scope="singleton"/>
	<bean id="someService2" class="com.fdd.service.SomeServiceImpl" scope="prototype"/>
	
	
</beans>

然后修改测试代码:

	@Test
	public void test01(){
		//创建容器对象,记载Spring配置文件,会从类路径下面查找文件也就是src下面
		ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
		
		//service使用的是singleton
		ISomeService service1=(ISomeService) ac.getBean("someService1");
		ISomeService service2=(ISomeService) ac.getBean("someService1");
		System.out.println("service1=service2 ?  "+(service1==service2));
		
		//service使用的是prototype
		ISomeService service3=(ISomeService) ac.getBean("someService2");
		ISomeService service4=(ISomeService) ac.getBean("someService2");
		System.out.println("service3=service4 ?  "+(service3==service4));
		
	}

接下来看结果:

五、bean后处理器

bean后处理器是一种特殊的bean,容器中所有的bean在初始化时候,均会自动执行该类的两个方法。由于该bean是由其他bean自动调用执行,不是自己手动调用,因此这个bean不需要id属性

如果我们需要自定义bean,我们就需要实现BeanPostProcessor接口,这个接口中含有两个方法,分别在bean初始化完毕之前和之后执行。最后返回一个增强的bean,也就是添加了自己处理的bean

看一个例子,我们在之前的例子上进行修改、现在我们有两个bean,StudentServiceImpl和TeacherServiceImpl都实现了ISomeService接口。现在我们队StudentServiceImpl进行增强。

package com.fdd.service;

//这个是需要增强的类
public class StudentServiceImpl implements ISomeService {

	public void doSome() {
		System.out.println(this.getClass().getSimpleName()+",执行了doSome方法");
	}

	public void doOther() {
		System.out.println(this.getClass().getSimpleName()+",执行了doOther方法");
	}

}

然后是TeacherServiceImpl

package com.fdd.service;

public class TeacherServiceImpl implements ISomeService {

	public void doSome() {
		System.out.println(this.getClass().getSimpleName()+",执行了doSome方法");
	}

	public void doOther() {
		System.out.println(this.getClass().getSimpleName()+",执行了doOther方法");
	}

}

然后我们创建自己的bean后置器


public class MyBeanPostProcessor implements BeanPostProcessor {
	
	
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		//即使不对bean进行增强,也要是方法返回bean,不能为默认的null
		//否则将抛出空指针异常
		return bean;
	}

	public Object postProcessAfterInitialization(final Object bean, String beanName)
			throws BeansException {
		//在这里我们增强的是StudentServiceImpl
		if("studentService".equals(beanName)){
			Object proxy=Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), 
					new InvocationHandler() {
						
						public Object invoke(Object proxy, Method method, Object[] args)
								throws Throwable {
							if("doSome".equals(method.getName())){
								System.out.println("目标方法开始执行的时间"+System.currentTimeMillis());
								//执行目标方法
								Object result=method.invoke(bean, args);
								System.out.println("目标方法结束执行的时间"+System.currentTimeMillis());
								return result;
							}
							return method.invoke(bean, args);
						}
						
					});
			//将增强之后的代理返回
			return proxy;
		}
		return bean;
	}
}

最后在我们的配置文件中声明后处理器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 注册service-->  
	<bean id="studentService" class="com.fdd.service.StudentServiceImpl"  />
	<bean id="teacherService" class="com.fdd.service.TeacherServiceImpl"/>
	<bean class="com.fdd.service.MyBeanPostProcessor"></bean>
	
</beans>

修改测试类:

	@Test
	public void test01(){
		//创建容器对象,记载Spring配置文件,会从类路径下面查找文件也就是src下面
		ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
		
		//service使用的是singleton
		ISomeService studentService=(ISomeService) ac.getBean("studentService");
		//这个是增强的方法
		studentService.doSome();
		studentService.doOther();
		System.out.println("===============================");
		ISomeService teacherService=(ISomeService) ac.getBean("teacherService");
		teacherService.doSome();
		teacherService.doOther();
	}

看运行结果:

六、定制bean的生命始末

我们可以在配置文件中声明一个bean,在刚刚初始化的时候的行为,也可以声明销毁前的行为

比如我们在上面的TeacherServiceImpl实现类中声明

<?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">

    <!-- 注册service-->  
	<bean id="studentService" class="com.fdd.service.StudentServiceImpl"  />
	<!-- 在TeacherServiceImpl -->
	<bean id="teacherService" class="com.fdd.service.TeacherServiceImpl"
	init-method="setUp" destroy-method="setDown"/>
	<bean class="com.fdd.service.MyBeanPostProcessor"></bean>
	
</beans>

需要注意的是,我们如果想要看到销毁方法,需要在调用方法之后,关闭ApplicationContext:

	@Test
	public void test01(){
		//创建容器对象,记载Spring配置文件,会从类路径下面查找文件也就是src下面
		ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
		
		//service使用的是singleton
		ISomeService teacherService=(ISomeService) ac.getBean("teacherService");
		teacherService.doSome();
		teacherService.doOther();
		((ClassPathXmlApplicationContext)ac).close();
	}

查看运行结果:

七、bean的生命周期

  1. 调用无参构造器,创建实例对象
  2. 调用set方法
  3. 若bean实现了BeanNameAware接口,则会执行接口方法setBeanName(String beanid),使得bean获取其在容器中的id
  4. 若bean实现了BeanFactoryAware接口,则会执行setBeanFactory(BeanFactory factory),使得bean获取到BeanFactory对象
  5. 如果注册了Bean后处理器,那么会执行该接口方法postProcessorBeforeIntialization()。
  6. 如果实现了InitialzingBean接口,就会执行afterPropertiesSet()。这个方法是bean初始化结束的标志
  7. 若设置了init-method方法,则执行
  8. 如果注册了Bean后处理器,执行postProcessorAfterIntialization()
  9. 执行业务方法
  10. 如果设置了DisposableBean接口,则执行改接口方法destory().
  11. 如果设置了destory-method方法,则执行

猜你喜欢

转载自blog.csdn.net/SDDDLLL/article/details/86598987
今日推荐