首先看一下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的手册,记录下加深印象。
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();
}
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;
}
}
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;
}
}
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;
}
}
}