先看一个案例:
package accelerate1.bean;
public class Test1 {
}
package accelerate1.bean;
public class Test2 {
}
package accelerate1.app;
import accelerate1.bean.Test1;
import accelerate1.bean.Test2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("accelerate1")
public class AppConfig {
@Bean
public Test1 test1(){
System.out.println("create test1");
return new Test1();
}
@Bean
public Test2 test2(){
test1();
System.out.println("create test2");
return new Test2();
}
}
package accelerate1.test;
import accelerate1.app.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo4 {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(annotationConfigApplicationContext.getBean(AppConfig.class));
}
}
运行结果:
我们看到打印了两次“create test1” ,就表示该Test1对象被实例化两次,不符合Spring 单例原则。
为了让Spring只能初始化一次,我们需要增加一个注解在AppConfig类上。
package accelerate1.app;
import accelerate1.bean.Test1;
import accelerate1.bean.Test2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("accelerate1")
@Configuration
public class AppConfig {
@Bean
public Test1 test1(){
System.out.println("create test1");
return new Test1();
}
@Bean
public Test2 test2(){
test1();
System.out.println("create test2");
return new Test2();
}
}
就加一个注解@Configuration 其他代码不变。
运行结果:
发现的变化为:create test1 打印了一次,保证了Spring单例原则。
并且发现我们AppConfig对象也编程代理对象了。由此可以却选Spring修改我们的AppConfig类内容将他转变成代理类来控制实现单例只实例化一次。
下面我们看看源码:Spring是如何实现的。
在 ConfigurationClassEnhancer 对象的 newEnhancer 对象中创建一个代理类(代理类有CGLib技术实现:)
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
//增强父类,地球人都知道cglib是基于继承来的
enhancer.setSuperclass(configSuperClass);
//增强接口,为什么要增强接口?
//便于判断,表示一个类以及被增强了
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
//不继承Factory接口
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// BeanFactoryAwareGeneratorStrategy是一个生成策略
// 主要为生成的CGLIB类中添加成员变量$$beanFactory
// 同时基于接口EnhancedConfiguration的父接口BeanFactoryAware中的setBeanFactory方法,
// 设置此变量的值为当前Context中的beanFactory,这样一来我们这个cglib代理的对象就有了beanFactory
//有了factory就能获得对象,而不用去通过方法获得对象了,因为通过方法获得对象不能控制器过程
//该BeanFactory的作用是在this调用时拦截该调用,并直接在beanFactory中获得目标bean
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
//过滤方法,不能每次都去new
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
将AppConfig 变成代理类之后。
后面Spring获取Bean的时候就执行如下核心方法。如何防止重复创建对象的原理已经写在注释当中。
/**
* @author hubt 用于拦截@Bean方法的调用,并直接从BeanFactory中获取目标bean,而不是通过执行方法。
* Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper
* handling of bean semantics such as scoping and AOP proxying.
* @see Bean
* @see ConfigurationClassEnhancer
*/
private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
/**
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
* existence of this bean object.
* @throws Throwable as a catch-all for any exception that may be thrown when invoking the
* super implementation of the proxied method i.e., the actual {@code @Bean} method
*/
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
//enhancedConfigInstance 代理
// 通过enhancedConfigInstance中cglib生成的成员变量$$beanFactory获得beanFactory。
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// To handle the case of an inter-bean method reference, we must explicitly check the
// container for already cached instances.
// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// This ensures that the semantics of calling a FactoryBean from within @Bean methods
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
/**
* @author hubt 判断当前执行方法是否是本次被调用方法。
* 相同则调用父类(我们的全注解类)方法,将对象实例化出来。
* 否则就不调用该方法,不再实例化对象,保证了Spring 单例只是实例化一次。
* A 方法 ( 调用B方法 , return new A )
* B 方法 ( return new )
* Spring 先执行A方法(调用和执行都是A方法故执行父类方法),父类方法执行到B方法时,变成执行B方法了:
* 由于这个判断(当前执行B方法不等于,调用方法,因为调用方法还是A方法),就不会执行B方法了。
* A方法继续往下执行,创建A。
*/
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
if (logger.isWarnEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
}
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
总结:Spring 源码各个环节之间都有紧密相连,单单看一个逻辑并不好理解,建议还是从整体看Spring源码。
This moment will nap, you will have a dream; But this moment study,you will interpret a dream.