销售平台网站建设,浏览网址大全,CQ网络科技网站建设,网络空间安全考研学校排名前言
回想起五年前的一次面试#xff0c;面试官问Configuration注解和Component注解有什么区别#xff1f;记得当时的回答是#xff1a; 相同点#xff1a;Configuration注解继承于Component注解#xff0c;都可以用来通过ClassPathBeanDefinitionScanner装载Spring bean…前言
回想起五年前的一次面试面试官问Configuration注解和Component注解有什么区别记得当时的回答是 相同点Configuration注解继承于Component注解都可以用来通过ClassPathBeanDefinitionScanner装载Spring bean的配置信息。 不同点Component注解为通用组件类模式注解Configuration注解为配置类模式注解主要是在做代码分层上的有差别当然也是从字面意思上理解。
很显然不是面试官想要到答案最后还是挂了。回去看了一下注解本身定Configuration继承于Component多了个proxyBeanMethods 属性注释中提到在运行中可以生成子类进行增强但是类类型必须不是final的,当proxyBeanMethods配置为false的时候不会进行增强。当时也就草率的下了定义Configuration可以选择是否通过生成代理类进行增强。
进阶
多余的属性是proxyBeanMethods字面的意思是代理Bean的方法代理了个什么东西是如何增强带着问题写了一段测试代码
Component
public class Demo {public static void main(String[] args) {AnnotationConfigApplicationContext annotationConfigApplicationContext new AnnotationConfigApplicationContext();annotationConfigApplicationContext.register(Demo.class);annotationConfigApplicationContext.refresh();Test test annotationConfigApplicationContext.getBean(Test.class);System.out.println(spring ioc容器中管理的person对象 annotationConfigApplicationContext.getBean(Person.class));for (int i 0; i 2; i) {System.out.println(通过bean作用的方法创建对象 test.createUser());}for (int i 0; i 2; i) {System.out.println(通过没用bean作用的方法创建对象 test.createUserNoMethodBean());}annotationConfigApplicationContext.close();
}Configurationpublic static class Test {Beanpublic Person createUser() {return createUserNoMethodBean();}public Person createUserNoMethodBean(){Person person new Person();person.setName(UUID.randomUUID().toString());return person;}}public static class Person {private String name;//get set 省略Overridepublic String toString() {return Person{ name name \ };}}
}代码输出
spring ioc容器中管理的person对象Person{name2a8c8844-c979-4784-942b-af4299d32866}
通过bean作用的方法创建对象Person{name2a8c8844-c979-4784-942b-af4299d32866}
通过bean作用的方法创建对象Person{name2a8c8844-c979-4784-942b-af4299d32866}
通过没用bean作用的方法创建对象Person{name67707608-79bf-44c0-a83a-9ebaa7c17bb1}
通过没用bean作用的方法创建对象Person{name27028430-9f82-4379-993a-6edd884ce145}我们调用Test对象创建用户方法带有bean注解的返回的是同一个对象并且与注入到spring ioc容器中的person对象是同一个而且没有通过Bean注解作用的方法真正执行了。不得不说好神奇。
猜想
我们先做一个大胆的猜想~ 注解Configuration注解proxyBeanMethods默认为true也就是说默认会进行代理增强。调用通过bean注解的方法时会进行拦截并且会舍弃调用真正的目标方法。其中会对带有bean的方法进行代理对不带有bean的方法进行过滤。拦截带有bean方法返回对象时会从spring ioc容器中进行依赖查找并返回该对象。 如图
带有bean方法不需要进行执行目标方法也就是我们的原始方法我们需要注入我们的BeanFactory对象来完成我们带有Bean方法依赖查找需要对我们的方法进行过滤需要对特定方法进行回调。
源码
由上一章我们熟悉了Spring ioc容器解析注册的流程ConfigurationClassPostProcessor.class类比较重要前半部分为BeanDefinitionRegistry逻辑后半部为配置类的增强。 接下来开始查看对给定BeanFactoryPostProcessor的处理。跟踪源码到ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(beanfactory);
/*** Prepare the Configuration classes for servicing bean requests at runtime* by replacing them with CGLIB-enhanced subclasses.*///通过 CGLIB 增强的子类来代替配置类来为 bean 请求提供支持
Override
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));
}继续跟踪到ConfigurationClassPostProcessor#enhanceConfigurationClasses(beanFactory)方法中 public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {StartupStep enhanceConfigClasses this.applicationStartup.start(spring.context.config-classes.enhance);MapString, AbstractBeanDefinition configBeanDefs new LinkedHashMap();for (String beanName : beanFactory.getBeanDefinitionNames()) {BeanDefinition beanDef beanFactory.getBeanDefinition(beanName);Object configClassAttr beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);AnnotationMetadata annotationMetadata null;MethodMetadata methodMetadata null;if (beanDef instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition annotatedBeanDefinition (AnnotatedBeanDefinition) beanDef;annotationMetadata annotatedBeanDefinition.getMetadata();methodMetadata annotatedBeanDefinition.getFactoryMethodMetadata();}if ((configClassAttr ! null || methodMetadata ! null) beanDef instanceof AbstractBeanDefinition) {// Configuration class (full or lite) or a configuration-derived Bean method// - eagerly resolve bean class at this point, unless its a lite configuration// or component class without Bean methods.AbstractBeanDefinition abd (AbstractBeanDefinition) beanDef;if (!abd.hasBeanClass()) {boolean liteConfigurationCandidateWithoutBeanMethods (ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) annotationMetadata ! null !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));if (!liteConfigurationCandidateWithoutBeanMethods) {try {abd.resolveBeanClass(this.beanClassLoader);}catch (Throwable ex) {throw new IllegalStateException(Cannot load configuration class: beanDef.getBeanClassName(), ex);}}}}//判断BeanDefinition的configurationClass是否为full然后加入集合后续进行特殊处理if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {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);}}if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {// nothing to enhance - return immediatelyenhanceConfigClasses.end();return;}ConfigurationClassEnhancer enhancer new ConfigurationClassEnhancer();//遍历进行cglib增强子类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);// Set enhanced subclass of the user-specified bean classClass? configClass beanDef.getBeanClass();Class? enhancedClass enhancer.enhance(configClass, this.beanClassLoader);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);}}enhanceConfigClasses.tag(classCount, () - String.valueOf(configBeanDefs.keySet().size())).end();
在ConfigurationClassEnhancer类中我们看到增强的具体实现 /*** Creates a new CGLIB {link Enhancer} instance.*///创建cglib的实例。
private Enhancer newEnhancer(Class? configSuperClass, Nullable ClassLoader classLoader) {Enhancer enhancer new Enhancer();//为增强类设置父类enhancer.setSuperclass(configSuperClass);//为增强类设置接口该接口继承BeanFactoryAware在实例化非lazy对象处理中接口回调阶段initializeBean 进行BeanNameAware ClassLoaderAware BeanFactoryAware 回调我们能通过BeanFactoryAware 获取我们beanfactory类。enhancer.setInterfaces(new Class?[] {EnhancedConfiguration.class});enhancer.setUseFactory(false);//设置beanfactory字段。方便进行依赖查找enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));//具体我们可以看到此处配置的filter 也就是特殊方法才会执行回调否则调用父类目标方法enhancer.setCallbackFilter(CALLBACK_FILTER);enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());return enhancer;
}CALLBACK_FILTER:并不是所有方法进行拦截 首先需要拦截的是内部调用 Bean 注解的方法时进行ioc依赖查找返回其次是依赖查找依赖的beanfactory字段的赋值其他方法不进行拦截当然也可以拦截直接在调用父类的方法proxy.invokeSuper(obj, args);这样多实现不如不实现没用。
因为我们多个拦截器所以我们需要进行组合选出符合条件的拦截器下标。
/*** A {link CallbackFilter} that works by interrogating {link Callback Callbacks} in the order* that they are defined via {link ConditionalCallback}.*/private static class ConditionalCallbackFilter implements CallbackFilter {private final Callback[] callbacks;private final Class?[] callbackTypes;//初始化的数据为固定顺序public ConditionalCallbackFilter(Callback[] callbacks) {this.callbacks callbacks;this.callbackTypes new Class?[callbacks.length];for (int i 0; i callbacks.length; i) {this.callbackTypes[i] callbacks[i].getClass();}}Overridepublic int accept(Method method) {//遍历callbacks首先判断是否符合for (int i 0; i this.callbacks.length; i) {Callback callback this.callbacks[i];if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {return i;}}throw new IllegalStateException(No callback available for method method.getName());}public Class?[] getCallbackTypes() {return this.callbackTypes;}}// 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);CALLBACKS 根据ConditionalCallbackFilter#accept(method)方法逻辑首先会判断是是继承了ConditionalCallback然后调用isMatch(method)的方法组合条件中 NoOp.INSTANCE非ConditionalCallback子类与BeanMethodInterceptor和BeanFactoryAwareMethodInterceptor互斥BeanMethodInterceptor和BeanFactoryAwareMethodInterceptor互斥。
BeanMethodInterceptor#isMatch(method);源码
Override
public boolean isMatch(Method candidateMethod) {return (candidateMethod.getDeclaringClass() ! Object.class !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}接下来创建我们目标类的子类注册子类的回调: /**
*为派生子类的设置回调。
*/
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)...//CALLBACKS为Enhancer.registerStaticCallbacks(subclass, CALLBACKS);return subclass;
}CALLBACKS 为数组
BeanMethodInterceptor对于配置类中内部 Bean 注解的方法的调用将会被拦截器拦截。拦截器的逻辑是判断声明的 Spring bean 在容器中是否已经存在如果存在则直接返回容器中的 Spring bean。否则真正的配置类的方法创建 Spring bean 实例避免了多例的出现。主要是解析获取我们beanfactory字段该字段受益于CALLBACKS第二个元素的赋值进行依赖查找。
private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {//解析BEAN_FACTORY_FIELD beanfactory字段~Field field ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);Assert.state(field ! null, Unable to find generated bean factory field);Object beanFactory ReflectionUtils.getField(field, enhancedConfigInstance);Assert.state(beanFactory ! null, BeanFactory has not been injected into Configuration class);Assert.state(beanFactory instanceof ConfigurableBeanFactory,Injected BeanFactory is not a ConfigurableBeanFactory);return (ConfigurableBeanFactory) beanFactory;
}BeanFactoryAwareMethodInterceptor为我们代理类新增的BEAN_FACTORY_FIELD字段进行赋值。源码如下
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {//查找我们的BEAN_FACTORY_FIELD字段Field field ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);Assert.state(field ! null, Unable to find generated BeanFactory field);//为我们字段进行赋值args[0]-因为我们BeanFactoryAware接口回调方法为 void setBeanFactory(BeanFactory beanFactory) throws BeansException;只有一个参数~field.set(obj, args[0]);// Does the actual (non-CGLIB) superclass implement BeanFactoryAware?// If so, call its setBeanFactory() method. If not, just exit.//如果父类实现了BeanFactoryAware接口改方法直接调用父类方法。if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {return proxy.invokeSuper(obj, args);}return null;
}NoOp.INSTANCE:这个NoOp表示no operator即什么操作也不做代理类直接调用被代理的方法不进行拦截。 配置中当然满足内部bean方法调用时走BeanMethodInterceptor调用setBeanFactory(BeanFactory beanFactory)时走BeanFactoryAwareMethodInterceptor其他方法走NoOp.INSTANCE。 回到我们ConfigurationClassPostProcessor#enhanceConfigurationClasses(beanFactory)方法中可以看到BeanDefinition的beanClass属性被赋值我们生成增强的代理子类 最后来到我们bean的实例化处理方法DefaultListableBeanFactory#preInstantiateSingletons。至此Configuration的实现原理和我们猜想大致相同。