Spring注解 @Configuration
Spring注解 @Configuration
一.注解作用.
标注在类上,该类会被CGLIB动态代理生成子类,可以达到这样的效果:在某@Bean方法下调用另一个标注了@Bean的方法,得到的会是同一个Bean对象;
@Configuration注解注意点:
1.可以作为Component标签使用;
2.标注的类不能是final类型的(final类无法动态代理生成子类);
3.注解类里的@Bean对象的id默认是方法名,如果设置了@Bean的name或者value属性,取第一个作为beanId,name中其他的作为别名使用;
4. 标注了@Configuration的类不能是普通内部类,如果非要是个内部类,那就静态内部类也是可以的; 因为普通内部类依赖于外部类的存在;
达到的效果就是这样: 回到解析@Configuration的地方四
获取bean会发现getMan和getMan2对象是同一个对象,去掉Configuration的话就是两个不同的对象
二. 注解形式的Spring容器的启动方式(非Web项目)
方式1. 启动时候将配置类作为参数传入容器,多个配置类也可以一起传入,参数是可变参数类型可以接收多个;
public class AppConfig1 {
@Bean
public Man getMan() {
Man man = new Man();
man.setName("吕彬彬");
man.setAge(23);
return man;
} @Bean
public Man getMan2() {
return getMan();
} public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig1.class);
//传入的AppConfig1就是配置类,可以不标注@Configuration也能使用
String[] names = ac.getBeanDefinitionNames();
for (String string : names) {
System.out.println(string+"==="+ac.getBean(string));
}
ac.close();
}
方式2. 空的构造器,之后手动注册配置类,但是记得要调用其refresh方法启动容器;
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(AppConfig1.class);
ac.refresh();
String[] names = ac.getBeanDefinitionNames();
for (String string : names) {
System.out.println(string+"==="+ac.getBean(string));
}
ac.close();
三. 分析不加@Configuration 只是一个启动类就可以解析@Bean注解
3.1 简单绘制下我理解的Spring容器bean的初始化流程:1-2是Spring容器初始化经历的过程,而3-9则是每一个bean创建必经的过程;InstantiationAwareBeanPostProcessor这些特殊的bean处理器如果有就会执行相应的方法;
如果没有 也不影响Bean初始化流程 ;这也是Spring可以丰富扩展的一个点,Spring很多功能Aop、Tx底层就为我们添加了很多这种BFPP、BPP;
AnnotationConfigApplicationContext就为我们添加了这样一个BFPP ConfigurationClassPostProcessor;同样还有很多其他的BFPP、BPP,方法位于AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
ConfigurationClassPostProcessor的类结构图如下,我们只需要看生命周期1、2中的方法即可;
3.2.ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法干了什么呢?
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { //registry就是传入的Spring的基本容器BeanFactory对象,最常见的是DefaultListableBeanFactory
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId); processConfigBeanDefinitions(registry);
//Spring给的解释该方法是Build and validate a configuration model based on the registry of Configuration classes.
}
查看processConfigBeanDefinitions方法
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames(); //遍历现有注册的所有bean, 包括了之前的配置类AppConfig1,类型是AnnotatedGenericBeanDefinition for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
//判断beanDef有没有CONFIGURATION_CLASS_FULL属性(代表有Configuration注解) CONFIGURATION_CLASS_LITE属性代表有@Bean注解
//只有解析过的beanDef才会有这两种属性
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//checkConfigurationClassCandidate方法做了以下操作:
// 有@Configuration注解我就给beanDef添加属性CONFIGURATION_CLASS_FULL
// 有@Bean注解我就给beanDef添加属性CONFIGURATION_CLASS_LITE
// 如果两种注解都没有直接返回false,相反有一种都能为true, 就会添加到config候选集合中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
} // config候选集合为空直接返回
if (configCandidates.isEmpty()) {
return;
} ....省略代码
// Configuration类解析器
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); // config候选集合candidates
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
// 开始解析Configuration类,解析过程较为复杂, 简单的针对@Bean对象, parser的configurationClasses集合中添加的ConfigurationClass中持有BeanMethod对象,就是含有@Bean标签的方法
parser.validate(); //验证config配置类不能为final类型,还有@Bean方法如果是static的也无需验证 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed); if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
//读取ConfigurationClass的集合,根据BeanMethod来创建ConfigurationClassBeanDefinition,也是一种BeanDefinition对象,不同之处是创建的使用的是factory-method工厂方式创建的
//每个ConfigurationClassBeanDefinition的工厂名就是配置类的ID,工厂方法就是@Bean得方法名;
//同样还有很多处理,比如@Bean的属性设置、init-Method、destroy-Method ; Lazy 、DependsOn等注解的解析 , 还有很多额外的注解的解析就不介绍了;最后解析完成会注册到registry中
alreadyParsed.addAll(configClasses); candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
到这里postProcessBeanDefinitionRegistry方法就解析完毕,可以看到没有@Configuration注解的AppConfig1类的@Bean注解的@Bean也注册到Spring容器中了;
结束postProcessBeanDefinitionRegistry 方法时候打印下已经注册的BeanDefinition,可以看到最后两个Bean定义 主要是factoryBeanName以及factoryMethodName属性设置上了
3.3 ConfigurationClassPostProcessor的postProcessBeanFactory方法干了什么呢?
Spring初始化流程图步骤2执行postProcessBeanFactory方法: 其中enhanceConfigurationClasses方法会遍历所有的bean发现没有Configuration注解的bean就结束方法了,所以在这里不分析该方法,后面也会分析的 :)
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
} enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
下面叙述下这种@Bean转换的ConfigurationClassBeanDefinition怎么实例化: AbstractAutowireCapableBeanFactory的doCreateBean方法 ===> 调用createBeanInstance ===> 发现factoryMethodName不为空,调用instantiateUsingFactoryMethod ===> 最后调用SimpleInstantiationStrategy的instantiate方法;
3.4 没有@Configuration注解下@Bean的实例化
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, Object... args) { try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(factoryMethod);
return null;
});
}
else {
ReflectionUtils.makeAccessible(factoryMethod);
} Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get(); //currentlyInvokedFactoryMethod为ThreadLocal对象,实例化Bean的时候会记录当前的factoryMethod
try {
currentlyInvokedFactoryMethod.set(factoryMethod);
Object result = factoryMethod.invoke(factoryBean, args); //调用反射实例化该@Bean对象
if (result == null) {
result = new NullBean();
}
return result;
}
finally { //实例化完成后恢复currentlyInvokedFactoryMethod为之前的值
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
else {
currentlyInvokedFactoryMethod.remove();
}
}
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(factoryMethod,
"Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
"args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(factoryMethod,
"Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex);
}
catch (InvocationTargetException ex) {
String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";
if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory &&
((ConfigurableBeanFactory) owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {
msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " +
"declaring the factory method as static for independence from its containing instance. " + msg;
}
throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException());
}
}
到这里@Bean最简单的创建过程已经分析完成。
四.@Configuration注解下为啥 一 里面得到的@Bean就是同一个对象呢? 回到效果图地方
前面帮助:其中3.processConfigBeanDefinitions给标注了@Configuration的配置类设置了属性CONFIGURATION_CLASS_FULL
4.1 查看3.3中没有解析的postProcessBeanFactory的enhanceConfigurationClasses方法
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
//遍历了所有的BeanDefinition对象,没有CONFIGURATION_CLASS_FULL就是空的configBeanDefs,方法之前直接返回了;
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); //存放标注了Configuration注解的beanDefinition
}
}
if (configBeanDefs.isEmpty()) {
return;
} ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
try {
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
if (configClass != null) {
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
//生成AppConfig1的子类CGLIB代理Class 并且在下面将beanDef类型更改为了该CGLIB class
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
}
catch (Throwable ex) {
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
4.2 既然知道了是采用CGLIB动态代理,那有很多属性需要设置,代理哪些接口,代理的父类类型已经知道了,回调函数、回调函数过滤器设置了哪些?
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass); //被代理的父类类型设置上去就是AppConfig1
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
//被代理的接口只设置了EnhancedConfiguration,只是为了给CGLIB子类能够设置上BeanFactory属性
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
CALLBACK_FILTER对象如下:
接着利用enhancer对象生成代理子类
private Class<?> createClass(Enhancer enhancer) {
Class<?> subclass = enhancer.createClass();
// Registering callbacks statically (as opposed to thread-local)
// is critical for usage in an OSGi environment (SPR-5932)...
Enhancer.registerStaticCallbacks(subclass, CALLBACKS); //CALLBACKS对象在上面图片里
return subclass;
}
4.3 这样就生成的CGLIB代理的AppConfig对象,相当于AOP增强了该对象,本来AOP增强内的方法调用自身的方法是不能直接增强自身的,那Spring是怎么做的呢?
简单介绍下,Spring CGLIB CallBackFilter的作用; Callback我们都知道是回调方法,CGLIB对象调用方法就会调用回调方法,但是添加了CallBackFilter,他有个方法accpet(Method method)方法用来判断调用的方法,返回值为int类型,代表着走哪个Callback的下标,传入的是个Callback的数组嘛 :)
1 public int accept(Method method) {
2 for (int i = 0; i < this.callbacks.length; i++) {
3 Callback callback = this.callbacks[i];
4 if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
5 return i;
6 }
7 }
8 throw new IllegalStateException("No callback available for method " + method.getName());
9 }
4.3.1先查看第一个Callback BeanMethodInterceptor
查看其isMatch方法
@Override
public boolean isMatch(Method candidateMethod) {
return (candidateMethod.getDeclaringClass() != Object.class &&
!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
//方法不是Object中定义的,且不是setBeanFactory方法,且该方法包含@Bean注解就返回true
}
可以发现,只要调用自身的@Bean注解的方法都会走这个BeanMethodInterceptor回调,那我们就不看剩下两个回调函数了,另外一个不做任何操作,一个只是负责给CGLIB对象设置上BeanFactory对象,你说怎么设置,之前CGLIB中就添加了一个实现的接口EnhancedConfiguration,这个接口实现了BeanFactoryAware接口,可以注入BeanFactory对象;
4.3.2 查看 BeanMethodInterceptor的intercept方法
什么时候调用getMan、getMan2方法呢?看到3.4 这样一行 Object result = factoryMethod.invoke(factoryBean, args) 调用反射实例化Bean对象,这个时候不就会走回调方法了吗 :)
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable { ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
// 通过反射从CGLIB增强的对象获取beanFactory对象
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); // 得到beanName值,默认为方法名字,可以通过@Bean注解指定 if (BeanAnnotationHelper.isScopedProxy(beanMethod)) { // 解析Scope注解
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
//FactoryBean类型的Bean解析方式,暂不分析
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);
}
} if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
//判断当前执行的方法是否是正在执行的@Bean的方法,getMan2中调用getMan方法,getMan含有@Bean 这时候就返回false
if (logger.isInfoEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.info(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); //getMan方法直接反射可以得到对象
} return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName); //在getMan2中调用getMan方法就会执行这段逻辑
通常情况下,比如getMan方法下,会返回true,然后调用反射直接得到Bean对象;而getMan2方法执行的时候调用getMan方法,this对象就是CGLIB对象,就会在走一次这个方法,ThreadLocal对象里存储的是getMan2,当前方法是getMan,就会返回false了,执行resolveBeanReference方法;
private boolean isCurrentlyInvokedFactoryMethod(Method method) {
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
//获取ThreadLocalcurrentlyInvokedFactoryMethod对象currentlyInvokedFactoryMethod中当前的Method
return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}
4.3.3 查看resolveBeanReference方法
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) { boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName); //getMan并不是正在创建的bean,false
try {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, false);
}
boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
if (useArgs && beanFactory.isSingleton(beanName)) {
for (Object arg : beanMethodArgs) {
if (arg == null) {
useArgs = false;
break;
}
}
}
Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
beanFactory.getBean(beanName)); //没有参数的情况下,直接getBean获取就可以了
if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
if (beanInstance.equals(null)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("@Bean method %s.%s called as bean reference " +
"for type [%s] returned null bean; resolving to null value.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
beanMethod.getReturnType().getName()));
}
beanInstance = null;
}
else {
String msg = String.format("@Bean method %s.%s called as bean reference " +
"for type [%s] but overridden by non-compatible bean instance of type [%s].",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
beanMethod.getReturnType().getName(), beanInstance.getClass().getName());
try {
BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore - simply no detailed message then.
}
throw new IllegalStateException(msg);
}
}
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod(); //当前ThreadLocal中的是getMan2
if (currentlyInvoked != null) {
String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
beanFactory.registerDependentBean(beanName, outerBeanName); //设置依赖关系
}
return beanInstance;
}
finally {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, true);
}
}
}
五.总结与疑问
查看CGLIB代理的AppConfig1对象
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig1.class);
AppConfig1 config = ac.getBean(AppConfig1.class);
Field[] fs = config.getClass().getFields();
for (Field field : fs) {
System.out.println(field.getName());
}
查看输出:)
总结:@Configuration 可以使 该配置类中 @Bean下方法中如果调用同类的方法 返回的是同一个对象!
疑问? 突然之间懵逼了,测试一下,会发现@Configuration标注的情况下,this对象指代的是CGLIB代理对象, 我记得Spring Aop的代理对象的this对象不是CGLIB代理对象啊?
所以望知悉的人告知,是this就是CGLIB代理对象还是 SpringAop 作了不透明的封装,this方法调用的时候走父类的方法呢?
解决方法,也算找到问题出在哪里,搞明白其中的道道了 ; 点我查看解决
Spring注解 @Configuration的更多相关文章
- [转]Spring注解-@Configuration注解、@Bean注解以及配置自动扫描、bean作用域
1.@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文) package com.test.s ...
- Spring注解-@Configuration注解、@Bean注解以及配置自动扫描、bean作用域
1.@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文) package com.test.s ...
- Spring注解@Configuration和Java Config
1.从Spring 3起,JavaConfig功能已经包含在Spring核心模块,它允许开发者将bean定义和在Spring配置XML文件到Java类中.但是,仍然允许使用经典的XML方式来定义bea ...
- Spring注解@Configuration是如何被处理的?
从SpringApplication开始 一般情况下启动SpringBoot都是新建一个类包含main方法,然后使用SpringApplication.run来启动程序: @SpringBootApp ...
- 【Spring注解开发】组件注册-使用@Configuration和@Bean给容器中注册组件
写在前面 在之前的Spring版本中,我们只能通过写XML配置文件来定义我们的Bean,XML配置不仅繁琐,而且很容易出错,稍有不慎就会导致编写的应用程序各种报错,排查半天,发现是XML文件配置不对! ...
- spring注解源码分析--how does autowired works?
1. 背景 注解可以减少代码的开发量,spring提供了丰富的注解功能.我们可能会被问到,spring的注解到底是什么触发的呢?今天以spring最常使用的一个注解autowired来跟踪代码,进行d ...
- Spring之@Configuration配置解析
1.简单的示例: @Configuration @EnableConfigurationProperties({DemoProperties.class}) public class DemoConf ...
- Spring 注解学习手札(七) 补遗——@ResponseBody,@RequestBody,@PathVariable (转)
最近需要做些接口服务,服务协议定为JSON,为了整合在Spring中,一开始确实费了很大的劲,经朋友提醒才发现,SpringMVC已经强悍到如此地步,佩服! 相关参考: Spring 注解学习手札(一 ...
- Spring 注解驱动(一)基本使用规则
Spring 注解驱动(一)基本使用规则 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 一.基本使用 @Configur ...
随机推荐
- iOS笔记之UIKit_UITextField
- (void)viewDidLoad { [super viewDidLoad]; //建立在你已经遵守了<协议UITextFieldDelegate> self.numTF.deleg ...
- cpu信息查看
# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 # 查看物理CPU个数 cat /proc/cpuinfo| ...
- delphi CopyFileProgressBar 拷贝文件显示进度条
CopyFileProgressBar(pwidechar(ListBox1.Items.Strings[I]),pwidechar(NewDir+'\'+ExtractFileName(ListBo ...
- Android-Kotlin-区间与for&List&Map简单使用
区间与for: package cn.kotlin.kotlin_base04 /** * 区间与for */ fun main(args: Array<String>) { /** * ...
- .net图表之ECharts随笔02-字符云
后续每一类图表,若无特殊说明,都将建立在01的基础上,修改参数option,且参数均以json的格式 要形成如图所示的字符云,一般需要设置两个大参数——title和series 其中,title就是图 ...
- 未能加载文件或程序集“System.Web.Http.WebHost, Version=4.0.0.0, ”或它的某一个依赖项。系统找不到指定的文件。
第一次发布MVC项目,打开网站 未能加载文件或程序集“System.Web.Http.WebHost, Version=4.0.0.0, ”或它的某一个依赖项.系统找不到指定的文件. 问题原因:缺少配 ...
- MVC和WEBAPI(一)
什么是MVC (模型 视图 控制器)? MVC是一个架构模式,它分离了表现与交互.它被分为三个核心部件:模型.视图.控制器.下面是每一个部件的分工: 视图是用户看到并与之交互的界面. 模型表示业务数据 ...
- Linux下安装MySQL以及一些小坑
第一次写博客,各位凑合着看吧(假装有人看). 我这里使用的是centos7. 1.首先打开终端,查看有没有安装过MySQL: [root@localhost lyp]# rpm -qa | grep ...
- AJPFX平台讲述买卖、点差、单位,外汇的交易时间以及外汇交易者的参与者
AJPFX平台讲解:买(多).卖(空).点差.单位 外汇保交易也就是通过外汇的升值和贬值来赚取利润.以EURUSD(欧元/美元)为例.假设目前价格为1.3820左右,即1欧元兑换1.3820美元.这个 ...
- Git入门--创建版本库,关联远程库,从远程库下载
1.(先进入项目文件夹)通过命令 git init 把这个目录变成git可以管理的仓库 git init 2.把文件添加到版本库中,使用命令 git add .添加到暂存区里面去,不要忘记后面的小数点 ...