@Bean在@Configuration和在@Component中的区别

疑问由来

首先看一下Spring官方文档是怎么说的:


The @Bean methods in a Spring component are processed differently than their counterparts inside a Spring @Configuration class. The difference is that @Component classes are not enhanced with CGLIB to intercept the invocation of methods and fields. CGLIB proxying is the means by which invoking methods or fields within @Bean methods in @Configuration classes creates bean metadata references to collaborating objects; such methods are not invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring beans even when referring to other beans via programmatic calls to @Bean methods. In contrast, invoking a method or field in an @Bean method within a plain @Component class has standard Java semantics, with no special CGLIB processing or other constraints applying.


我英文不是很好,理解起来比较困难,后来做了个测试才理解的。大致翻译下,括号里内容是我自己补充的:


在Component中(@Component标注的类,包括@Service,@Repository, @Controller)使用@Bean注解和在@Configuration中使用是不同的。在@Component类中使用方法或字段时不会使用CGLIB增强(及不使用代理类:调用任何方法,使用任何变量,拿到的是原始对象,后面会有例子解释)。而在@Configuration类中使用方法或字段时则使用CGLIB创造协作对象(及使用代理:拿到的是代理对象);当调用@Bean注解的方法时它不是普通的Java语义,而是从容器中拿到由Spring生命周期管理、被Spring代理甚至依赖于其他Bean的对象引用。在@Component中调用@Bean注解的方法和字段则是普通的Java语义,不经过CGLIB处理。


不知道上面写的是不是很拗口,可能还是不好理解,来个示例吧。


概念和示例

首先介绍下Spring中Bean的生命周期,后面好解释运行结果。很熟悉的请跳过,由于我刚读Spring的手册,记录下加深印象。


Spring中Bean生命周期

Bean简单来说就是Spring管理的对象,最常用的应该是Scope=Singleton的Bean。

一个最简单Singleton Bean它在Spring中的创建过程大致是,Singleton Bean完成这些在容器启动时:读取类定义 -> 调用构造函数实例化 -> 填充属性值 -> 初始化
 消亡过程 ,一般是在容器关闭时: 销毁(析构前处理)  -> 后面的事就不太清楚了,应该是Spring断开引用然后交由Java虚拟机自己处理。

不同Scope的Bean生命周期大致一样,完成的时机不同,例如prototype在使用时才做实例化之后的动作。

生命周期接口示例

1.首先写一个Bean的配置类,在singleton的MyService中注入prototype的MyMode类。这里使用接口为了能自由选择JDK动态代理或CGLIB


Mode接口

package com.bean;


import java.util.concurrent.atomic.AtomicInteger;


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


import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;


public class MyMode implements IMode, InitializingBean, DisposableBean {


private static AtomicInteger i = new AtomicInteger(0);


private int value;


public MyMode() {
System.out.println("MyMode constructor call");
this.value = i.incrementAndGet();
}


public void print() {
System.out.println("MyMode print called");
System.out.println(value);
}


public void destroy() throws Exception {
System.out.println("Mode destroy called");
}


	public void afterPropertiesSet() throws Exception {
		System.out.println("Mode afterPropertiesSet called");


	}


	public void beanInit() {
		System.out.println("Mode @Bean anno Init called");
	}


	public void beanDestory() {
		System.out.println("Mode @Bean anno destory called");
	}


	@PostConstruct
	public void annotationInit() {
		System.out.println("Mode @PostConstruct anno init called");
	}


	@PreDestroy
	public void annotationDestory() {
		System.out.println("Mode @PreDestroy anno destory called");
	}
}




Service接口

package com.bean;

public interface IService {

	public IMode getMode();
}

Service实现

package com.bean;

public class MyService implements IService{

	private IMode mode;
	
	public MyService() {

	}

	public MyService(IMode mode) {
		this.mode = mode;
	}
	
	public IMode getMode() {
		return mode;
	}

	public String toString() {
		if(mode==null){
			return "none mode in service";
		}
		return  "bingo";
	}
}


Bean配置类

package com;


import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;


import com.bean.IMode;
import com.bean.IService;
import com.bean.MyMode;
import com.bean.MyService;


@Configuration
public class TestBeanConfigration {


@Bean
public IService myService() {
return new MyService(myMode());
}

@Bean(initMethod = "beanInit", destroyMethod = "beanDestory")
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public IMode myMode() {
return new MyMode();
}
}

上面的MyMode Bean使用了三个接口:
①注解@Bean的属性initMethod, destroyMethod 
②接口InitializingBean, DisposableBean
③注解@PostConstruct,@PreDestroy
都作用于同样的两个过程——初始化阶段和销毁阶段

2、BeanPostProcessor
package com.processor;

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

import com.bean.MyMode;

@Component
public class TestBeanPostProcessor implements BeanPostProcessor {

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof MyMode) {
			System.out
					.println("BeanPostProcessor postProcessBeforeInitialization called");
		}
		return bean;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof MyMode) {
			System.out
					.println("BeanPostProcessor postProcessAfterInitialization called");
		}
		return bean;
	}
}

实现了BeanPostProcessor接口,该接口作用于Bean初始化前后

3、InstantiationAwareBeanPostProcessor

package com.processor;

import java.beans.PropertyDescriptor;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.stereotype.Component;

import com.bean.MyMode;

@Component
public class TestInstantiationBeanPostProcessor extends
		InstantiationAwareBeanPostProcessorAdapter {

	public Object postProcessBeforeInstantiation(Class beanClass,
			String beanName) throws BeansException {
		if (beanClass.isAssignableFrom(MyMode.class)) {
			System.out
					.println("InstantiationBeanPostProcessor postProcessBeforeInstantiation called");
		}
		return null;
	}

	public boolean postProcessAfterInstantiation(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof MyMode) {
			System.out
					.println("InstantiationBeanPostProcessor postProcessAfterInstantiation called");
		}
		return true;
	}

	public PropertyValues postProcessPropertyValues(PropertyValues pvs,
			PropertyDescriptor[] pds, Object bean, String beanName)
			throws BeansException {
		if (bean instanceof MyMode) {
			System.out
					.println("InstantiationBeanPostProcessor postProcessPropertyValues called");
		}
		return pvs;
	}

}

继承自一个抽象类,主要实现的还是InstantiationAwareBeanPostProcessor接口的方法,该接口作用于实例化阶段,完成实例化,属性描述修改以及实例化后处理逻辑

4、以下是Spring Boot的启动类,以及一个简单的Web Controller

package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

package com;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.bean.IMode;
import com.bean.IService;

@RestController
public class GreetingController {

	private static final String template = "Hello, %s!";
	private final AtomicLong counter = new AtomicLong();

	@Autowired
	private IService myService;

	@Autowired
	private IMode mode;

	@RequestMapping("/greeting")
	public Greeting greeting(
			@RequestParam(value = "name", defaultValue = "World") String name) {
		if (mode == myService.getMode()) {
			System.out
					.println("The Mode Bean injected in different Bean is the same object, class type is "
							+ mode.getClass().getName());
		} else {
			System.out
					.println("The Mode Bean injected in different Bean is different object.");
			if (mode.getClass() == myService.getMode().getClass()) {
				System.out.println(" but they are the same type: "
						+ mode.getClass().getName());
			} else {
				System.out.println(" and they are different type : "
						+ mode.getClass().getName() + ","
						+ myService.getMode().getClass().getName());
			}
		}
mode.print();


myService.getMode().print();

mode.print();
		return new Greeting(counter.incrementAndGet(), String.format(template,
				name));
	}

	private static class Greeting {

		private final long id;
		private final String content;

		public Greeting(long id, String content) {
			this.id = id;
			this.content = content;
		}

		public long getId() {
			return id;
		}

		public String getContent() {
			return content;
		}
	}
}


5、启动应用,调用Controller打印结果:

The Mode Bean injected in different Bean is the same object, class type is com.sun.proxy.$Proxy47
InstantiationBeanPostProcessor postProcessBeforeInstantiation called
MyMode constructor call
InstantiationBeanPostProcessor postProcessAfterInstantiation called
InstantiationBeanPostProcessor postProcessPropertyValues called
BeanPostProcessor postProcessBeforeInitialization called
Mode @PostConstruct anno init called
Mode afterPropertiesSet called
Mode @Bean anno Init called
BeanPostProcessor postProcessAfterInitialization called
MyMode print called
1
MyMode constructor call
InstantiationBeanPostProcessor postProcessAfterInstantiation called
InstantiationBeanPostProcessor postProcessPropertyValues called
BeanPostProcessor postProcessBeforeInitialization called
Mode @PostConstruct anno init called
Mode afterPropertiesSet called
Mode @Bean anno Init called
BeanPostProcessor postProcessAfterInitialization called
MyMode print called
2
MyMode constructor call
InstantiationBeanPostProcessor postProcessAfterInstantiation called
InstantiationBeanPostProcessor postProcessPropertyValues called
BeanPostProcessor postProcessBeforeInitialization called
Mode @PostConstruct anno init called
Mode afterPropertiesSet called
Mode @Bean anno Init called
BeanPostProcessor postProcessAfterInitialization called
MyMode print called
3

由于MyMode是prototype的,每次调用不同实例,生命周期接口调用顺序应该是:
postProcessBeforeInstantiation->constructor->postProcessAfterInstantiation->postProcessPropertyValues 
->postProcessBeforeInitialization->( @PostConstruct->afterPropertiesSet->@Bean init)->postProcessAfterInitialization
->( @PostDestroy>destroy->@Bean destroy)

postProcessBeforeInstantiation只调用一次,说明某个类不管在Spring中有几个相应的对象,其原始实例只有一个。

但是为什么销毁阶段的函数没调用,因为Spring文档就这样写的,prototype资源当做普通对象自己代码中释放就好。暂时将MyMode的Scope改为WebApplicationContext.SCOPE_REQUEST。输出如下:

The Mode Bean injected in different Bean is the same object, class type is com.sun.proxy.$Proxy47
InstantiationBeanPostProcessor postProcessBeforeInstantiation called
MyMode constructor call
InstantiationBeanPostProcessor postProcessAfterInstantiation called
InstantiationBeanPostProcessor postProcessPropertyValues called
BeanPostProcessor postProcessBeforeInitialization called
Mode @PostConstruct anno init called
Mode afterPropertiesSet called
Mode @Bean anno Init called
BeanPostProcessor postProcessAfterInitialization called
MyMode print called
1
MyMode print called
1
MyMode print called
1
Mode @PreDestroy anno destory called
Mode destroy called
Mode @Bean anno destory called


@Configuration与@Component


上面例子不仅描述了Spring生命周期,注意TestBeanConfigration中那句 new MyService(myMode())
它是调用本类中的方法myMode(),当Scope=prototype时,根据上面第二次print的结果,实际上得到的却是拥有Spring生命周期Bean。
Spring官方文档所要表达的就是这个。

接下来试试TestBeanConfigration的@Configuration改为@Component,MyMode的Scope仍然是prototype。看看结果:

在启动时打印了一次:MyMode constructor call

调用Controller时:

The Mode Bean injected in different Bean is different object.
 and they are different type : com.sun.proxy.$Proxy46,com.bean.MyMode
InstantiationBeanPostProcessor postProcessBeforeInstantiation called
MyMode constructor call
InstantiationBeanPostProcessor postProcessAfterInstantiation called
InstantiationBeanPostProcessor postProcessPropertyValues called
BeanPostProcessor postProcessBeforeInitialization called
Mode @PostConstruct anno init called
Mode afterPropertiesSet called
Mode @Bean anno Init called
BeanPostProcessor postProcessAfterInitialization called
MyMode print called
2
MyMode print called
1
MyMode constructor call
InstantiationBeanPostProcessor postProcessAfterInstantiation called
InstantiationBeanPostProcessor postProcessPropertyValues called
BeanPostProcessor postProcessBeforeInitialization called
Mode @PostConstruct anno init called
Mode afterPropertiesSet called
Mode @Bean anno Init called
BeanPostProcessor postProcessAfterInitialization called
MyMode print called
3

由以上结果可以看出使用@Component时,TestBeanConfigration调用本类中的方法myMode(),立马返回一个原始的MyMode对象,该对象就是简单的Java对象。


你可能感兴趣的:(Spring)