东莞建站网站,石家庄网站建设解决方案,广州网站制作功能,网页 调用 wordpressSpring Configuration 和 Component 区别 一句话概括就是 Configuration 中所有带 Bean 注解的方法都会被动态代理#xff0c;因此调用该方法返回的都是同一个实例。 下面看看实现的细节。
Configuration 注解#xff1a;
Target(ElementType.TYPE)
Retention(RetentionPol…Spring Configuration 和 Component 区别 一句话概括就是 Configuration 中所有带 Bean 注解的方法都会被动态代理因此调用该方法返回的都是同一个实例。 下面看看实现的细节。
Configuration 注解
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
Documented
Component
public interface Configuration {String value() default ;
}从定义来看 Configuration 注解本质上还是 Component因此 或者 ComponentScan 都能处理Configuration 注解的类。
Configuration 标记的类必须符合下面的要求
配置类必须以类的形式提供不能是工厂方法返回的实例允许通过生成子类在运行时增强cglib 动态代理。配置类不能是 final 类没法动态代理。配置注解通常为了通过 Bean 注解生成 Spring 容器管理的类配置类必须是非本地的即不能在方法中声明不能是 private。任何嵌套配置类都必须声明为static。Bean 方法可能不会反过来创建进一步的配置类也就是返回的 bean 如果带有 Configuration也不会被特殊处理只会作为普通的 bean。
加载过程
Spring 容器在启动时会加载默认的一些 PostPRocessor其中就有 ConfigurationClassPostProcessor这个后置处理程序专门处理带有 Configuration 注解的类这个程序会在 bean 定义加载完成后在 bean 初始化前进行处理。主要处理的过程就是使用 cglib 动态代理增强类而且是对其中带有 Bean 注解的方法进行处理。
在 ConfigurationClassPostProcessor 中的 postProcessBeanFactory 方法中调用了下面的方法
/*** Post-processes a BeanFactory in search of Configuration class BeanDefinitions;* any candidates are then enhanced by a {link ConfigurationClassEnhancer}.* Candidate status is determined by BeanDefinition attribute metadata.* see ConfigurationClassEnhancer*/
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {MapString, AbstractBeanDefinition configBeanDefs new LinkedHashMapString, AbstractBeanDefinition();for (String beanName : beanFactory.getBeanDefinitionNames()) {BeanDefinition beanDef beanFactory.getBeanDefinition(beanName);if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {//省略部分代码configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);}}if (configBeanDefs.isEmpty()) {// nothing to enhance - return immediatelyreturn;}ConfigurationClassEnhancer enhancer new ConfigurationClassEnhancer();for (Map.EntryString, AbstractBeanDefinition entry : configBeanDefs.entrySet()) {AbstractBeanDefinition beanDef entry.getValue();// If a Configuration class gets proxied, always proxy the target classbeanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);try {// Set enhanced subclass of the user-specified bean classClass? configClass beanDef.resolveBeanClass(this.beanClassLoader);Class? enhancedClass enhancer.enhance(configClass, this.beanClassLoader);if (configClass ! enhancedClass) {//省略部分代码beanDef.setBeanClass(enhancedClass);}}catch (Throwable ex) {throw new IllegalStateException(Cannot load configuration class: beanDef.getBeanClassName(), ex);}}
}在方法的第一次循环中查找到所有带有 Configuration 注解的 bean 定义然后在第二个 for 循环中通过下面的方法对类进行增强
Class? enhancedClass enhancer.enhance(configClass, this.beanClassLoader);然后使用增强后的类替换了原有的 beanClass
beanDef.setBeanClass(enhancedClass);所以到此时所有带有 Configuration 注解的 bean 都已经变成了增强的类。
下面关注上面的 enhance 增强方法多跟一步就能看到下面的方法
/*** Creates a new CGLIB {link Enhancer} instance.*/
private Enhancer newEnhancer(Class? superclass, ClassLoader classLoader) {Enhancer enhancer new Enhancer();enhancer.setSuperclass(superclass);enhancer.setInterfaces(new Class?[] {EnhancedConfiguration.class});enhancer.setUseFactory(false);enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));enhancer.setCallbackFilter(CALLBACK_FILTER);enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());return enhancer;
}通过 cglib 代理的类在调用方法时会通过 CallbackFilter 调用这里的 CALLBACK_FILTER 如下
// The callbacks to use. Note that these callbacks must be stateless.
private static final Callback[] CALLBACKS new Callback[] {new BeanMethodInterceptor(),new BeanFactoryAwareMethodInterceptor(),NoOp.INSTANCE
};private static final ConditionalCallbackFilter CALLBACK_FILTER new ConditionalCallbackFilter(CALLBACKS);其中 BeanMethodInterceptor 匹配方法如下
Override
public boolean isMatch(Method candidateMethod) {return BeanAnnotationHelper.isBeanAnnotated(candidateMethod);
}//BeanAnnotationHelper
public static boolean isBeanAnnotated(Method method) {return AnnotatedElementUtils.hasAnnotation(method, Bean.class);
}也就是当方法有 Bean 注解的时候就会执行这个回调方法。
另一个 BeanFactoryAwareMethodInterceptor 匹配的方法如下
Override
public boolean isMatch(Method candidateMethod) {return (candidateMethod.getName().equals(setBeanFactory) candidateMethod.getParameterTypes().length 1 BeanFactory.class candidateMethod.getParameterTypes()[0] BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
}当前类还需要实现 BeanFactoryAware 接口上面的 isMatch 就是匹配的这个接口的方法。
Bean 注解方法执行策略
先给一个简单的示例代码
Configuration
public class MyBeanConfig {Beanpublic Country country(){return new Country();}Beanpublic UserInfo userInfo(){return new UserInfo(country());}} 相信大多数人第一次看到上面 userInfo() 中调用 country() 时会认为这里的 Country 和上面 Bean 方法返回的 Country 可能不是同一个对象因此可能会通过下面的方式来替代这种方式 Autowired
private Country country;实际上不需要这么做后面会给出需要这样做的场景直接调用 country() 方法返回的是同一个实例。 下面看调用 country() 和 userInfo() 方法时的逻辑。 现在我们已经知道 Configuration 注解的类是如何被处理的了现在关注上面的 BeanMethodInterceptor看看带有 Bean 注解的方法执行的逻辑。下面分解来看 intercept 方法。
//首先通过反射从增强的 Configuration 注解类中获取 beanFactory
ConfigurableBeanFactory beanFactory getBeanFactory(enhancedConfigInstance);//然后通过方法获取 beanName默认为方法名可以通过 Bean 注解指定
String beanName BeanAnnotationHelper.determineBeanNameFor(beanMethod);//确定这个 bean 是否指定了代理的范围
//默认下面 if 条件 false 不会执行
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;}
}//中间跳过一段 Factorybean 相关代码//判断当前执行的方法是否为正在执行的 Bean 方法
//因为存在在 userInfo() 方法中调用 country() 方法
//如果 country() 也有 Bean 注解那么这个返回值就是 false.
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {// 判断返回值类型如果是 BeanFactoryPostProcessor 就写警告日志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 Springs BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as Autowired, Resource and PostConstruct within the methods 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()));}//直接调用原方法创建 beanreturn cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
//如果不满足上面 if也就是在 userInfo() 中调用的 country() 方法
return obtainBeanInstanceFromFactory(beanMethod, beanMethodArgs, beanFactory, beanName); 关于 isCurrentlyInvokedFactoryMethod 方法 可以参考 SimpleInstantiationStrategy 中的 instantiate 方法这里先设置的调用方法 currentlyInvokedFactoryMethod.set(factoryMethod);
return factoryMethod.invoke(factoryBean, args);12 而通过方法内部直接调用 country() 方法时不走上面的逻辑直接进的代理方法也就是当前的 intercept方法因此当前的工厂方法和执行的方法就不相同了。 obtainBeanInstanceFromFactory 方法比较简单就是通过 beanFactory.getBean 获取 Country如果已经创建了就会直接返回如果没有执行过就会通过 invokeSuper 首次执行。
因此我们在 Configuration 注解定义的 bean 方法中可以直接调用方法不需要 Autowired 注入后使用。
Component 注意
Component 注解并没有通过 cglib 来代理Bean 方法的调用因此像下面这样配置时就是两个不同的 country。
Component
public class MyBeanConfig {Beanpublic Country country(){return new Country();}Beanpublic UserInfo userInfo(){return new UserInfo(country());}}有些特殊情况下我们不希望 MyBeanConfig 被代理代理后会变成WebMvcConfig$$EnhancerBySpringCGLIB$$8bef3235293时就得用 Component这种情况下上面的写法就需要改成下面这样
Component
public class MyBeanConfig {Autowiredprivate Country country;Beanpublic Country country(){return new Country();}Beanpublic UserInfo userInfo(){return new UserInfo(country);}}这种方式可以保证使用的同一个 Country 实例。