疑问由来
首先看一下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生命周期
生命周期接口示例
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");
}
}
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";
}
}
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();
}
}
都作用于同样的两个过程——初始化阶段和销毁阶段
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初始化前后
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接口的方法,该接口作用于实例化阶段,完成实例化,属性描述修改以及实例化后处理逻辑
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打印结果:
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
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
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