Spring啟動(dòng)過程中實(shí)例化部分代碼的分析之Bean的推斷構(gòu)造方法
針對(duì)實(shí)例化過程中會(huì)做什么的分析,其中主要的是怎么推斷出構(gòu)造方法,怎么進(jìn)行匹配
【1】前言
實(shí)例化這一步便是在doCreateBean方法的 instanceWrapper = createBeanInstance(beanName, mbd, args);這段代碼中。
【2】對(duì)于實(shí)例化的疑問
對(duì)于Spring中的beanBeanDefinition,需要通過實(shí)例化得到一個(gè)bean對(duì)象才會(huì)被放入容器中,而實(shí)例化就需要用到構(gòu)造方法。
分析:一個(gè)類存在多個(gè)構(gòu)造方法,那么Spring進(jìn)行實(shí)例化時(shí),該如何去確定到底用哪個(gè)構(gòu)造方法呢?
1. 如果開發(fā)者指定了想要使用的構(gòu)造方法,那么就用這個(gè)構(gòu)造方法。
2. 如果開發(fā)者沒有指定想要使用的構(gòu)造方法,則看開發(fā)者有沒有讓Spring自動(dòng)去選擇構(gòu)造方法。
3. 如果開發(fā)者也沒有讓Spring自動(dòng)去選擇構(gòu)造方法,則Spring利用無參構(gòu)造方法,如果沒有無參構(gòu)造方法,則報(bào)錯(cuò)。
開發(fā)者可以通過什么方式來指定使用哪個(gè)構(gòu)造方法呢?
1.通過xml中的<constructor-arg>標(biāo)簽,這個(gè)標(biāo)簽表示構(gòu)造方法參數(shù),所以可以根據(jù)這個(gè)確定想要使用的構(gòu)造方法的參數(shù)個(gè)數(shù),從而確定想要使用的構(gòu)造方法
2.通過@Autowired注解,@Autowired注解可以寫在構(gòu)造方法上,所以哪個(gè)構(gòu)造方法上寫了@Autowired注解,表示開發(fā)者想使用哪個(gè)構(gòu)造方法,當(dāng)然,它和第一個(gè)方式的不同點(diǎn)是,通過xml的方式,我們直接指定了構(gòu)造方法的參數(shù)值,而通過@Autowired注解的方式,需要Spring通過byType+byName的方式去找到符合條件的bean作為構(gòu)造方法的參數(shù)值。
3.如果Bean為注解@Lazy修飾的或者非單例的,可以通過getBean方法設(shè)置構(gòu)造方法的入?yún)ⅲ_(dá)到指定構(gòu)造方法的效果。如,applicationContext.getBean("BeanDemo", new CircularRefA());【同理,獲取beanDefinition,使用beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(new CircularRefA());指定構(gòu)造方法參數(shù)】
【3】推斷構(gòu)造方法源碼分析
1.主體代碼邏輯
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { /** * ----------0,校驗(yàn)部分------------ */ Class<?> beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } /** * ----------1,通過Supplier實(shí)例化部分------------ */ // BeanDefinition中添加了Supplier,則調(diào)用Supplier來得到對(duì)象 Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } /** * ----------2,通過工廠方法實(shí)例化部分------------ */ // @Bean對(duì)應(yīng)的BeanDefinition if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } /** * ----------3,用合適的構(gòu)造函數(shù)實(shí)例化部分------------ * * 一個(gè)類可能有多個(gè)構(gòu)造器,所以Spring得根據(jù)參數(shù)個(gè)數(shù)、類型確定需要調(diào)用的構(gòu)造器。 * 原型的BeanDefinition,會(huì)多次來創(chuàng)建Bean。故在使用構(gòu)造器創(chuàng)建實(shí)例后,Spring會(huì)將解析過后確定下來的構(gòu)造器或工廠方法保存在緩存中,避免再次創(chuàng)建相同bean時(shí)再次解析(節(jié)約時(shí)間) */ boolean resolved = false; //對(duì)構(gòu)造器是否緩存了 boolean autowireNecessary = false; //緩存的構(gòu)造器是否有參數(shù) if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; // autowireNecessary表示有沒有必要要進(jìn)行注入,比如當(dāng)前BeanDefinition用的是無參構(gòu)造方法,那么autowireNecessary為false,否則為true,表示需要給構(gòu)造方法參數(shù)注入值 autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { // 如果確定了當(dāng)前BeanDefinition的構(gòu)造方法,那么看是否需要進(jìn)行對(duì)構(gòu)造方法進(jìn)行參數(shù)的依賴注入(構(gòu)造方法注入) if (autowireNecessary) { // 方法內(nèi)會(huì)拿到緩存好的構(gòu)造方法的入?yún)? return autowireConstructor(beanName, mbd, null, null); } else { // 構(gòu)造方法已經(jīng)找到了,但是沒有參數(shù),那就表示是無參,直接進(jìn)行實(shí)例化 return instantiateBean(beanName, mbd); } } //沒有緩存,從bean后置處理器中為自動(dòng)裝配尋找構(gòu)造方法 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 找出最合適的默認(rèn)構(gòu)造方法 ctors = mbd.getPreferredConstructors(); if (ctors != null) { // 構(gòu)造函數(shù)自動(dòng)注入 return autowireConstructor(beanName, mbd, ctors, null); } /** * ----------4,使用默認(rèn)構(gòu)造函數(shù)構(gòu)造部分------------ */ // 不匹配以上情況,則直接使用無參構(gòu)造方法 return instantiateBean(beanName, mbd); }
代碼說明
createBeanInstance() 方法是 spring 實(shí)例化 bean 的核心代碼,它根據(jù)不同的情況會(huì)調(diào)用四種實(shí)例化方法:
1)obtainFromSupplier() :通過 Supplier 實(shí)例化
2)instantiateUsingFactoryMethod():通過工廠方法實(shí)例化
3)autowireConstructor():用合適的構(gòu)造函數(shù)實(shí)例化
4)instantiateBean():用無參構(gòu)造函數(shù)實(shí)例化
2.局部分析代碼
1)通過Supplier實(shí)例化部分解析
代碼
// BeanDefinition中添加了Supplier,則調(diào)用Supplier來得到對(duì)象 Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); }
說明
Spring提供的一種機(jī)制,雖然不常用,通過設(shè)置實(shí)現(xiàn)Supplier接口的類,返回一個(gè)另一種的對(duì)象,類似于FactoryBean的感覺。示例:
//在beanDefinition階段設(shè)置好 beanDefinition.setBeanClass(Student.class); beanDefinition.setInstanceSupplier(new Supplier<Object>() { @Override public Object get() { return new UserServiceImpl1(); } }); //容器啟動(dòng)后嘗試獲取驗(yàn)證 applicationContext.getBean("student"); //返回new UserServiceImpl1()對(duì)象
2)通過工廠方法實(shí)例化部分針(同時(shí)也是@Bean注解的處理)解析【如果存疑可以查看注解@Bean解析】
代碼塊
// @Bean對(duì)應(yīng)的BeanDefinition if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); }
代碼深入部分:instantiateUsingFactoryMethod方法解析
protected BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) { // 使用factoryBean來實(shí)例化對(duì)象 return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs); } //ConstructorResolver類#instantiateUsingFactoryMethod方法 //省略日志與異常 public BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Object factoryBean; Class<?> factoryClass; boolean isStatic; //通過beanDefinition獲取到factoryBeanName ,實(shí)際就是@Bean注解的方法所在的configuration類 String factoryBeanName = mbd.getFactoryBeanName(); //表示非靜態(tài)方法 if (factoryBeanName != null) { if (factoryBeanName.equals(beanName)) { throw new BeanDefinitionStoreException(...); } factoryBean = this.beanFactory.getBean(factoryBeanName); // 該mbd已經(jīng)創(chuàng)建過了【代表這個(gè)邏輯已經(jīng)走過了】 if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) { throw new ImplicitlyAppearedSingletonException(); } factoryClass = factoryBean.getClass(); isStatic = false; } else {//表示靜態(tài)方法 // It's a static factory method on the bean class. if (!mbd.hasBeanClass()) { throw new BeanDefinitionStoreException(...); } factoryBean = null; factoryClass = mbd.getBeanClass(); isStatic = true; } Method factoryMethodToUse = null; ArgumentsHolder argsHolderToUse = null; Object[] argsToUse = null; //如果在調(diào)用getBean方法時(shí)有傳參,那就用傳的參作為@Bean注解的方法(工廠方法)的參數(shù), 一般懶加載的bean才會(huì)傳參,啟動(dòng)過程就實(shí)例化的實(shí)際上都沒有傳參 if (explicitArgs != null) { argsToUse = explicitArgs; } else { Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod; //不為空表示已經(jīng)使用過工廠方法,現(xiàn)在是再次使用工廠方法, 一般原型模式和Scope模式采用的上,直接使用該工廠方法和緩存的參數(shù) if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) { // Found a cached factory method... argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { argsToResolve = mbd.preparedConstructorArguments; } } } if (argsToResolve != null) { argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true); } } // 調(diào)用getBean方法沒有傳參,同時(shí)也是第一次使用工廠方法 if (factoryMethodToUse == null || argsToUse == null) { factoryClass = ClassUtils.getUserClass(factoryClass); // 獲取configuration類的所有候選方法 Method[] rawCandidates = getCandidateMethods(factoryClass, mbd); List<Method> candidateList = new ArrayList<>(); for (Method candidate : rawCandidates) { // 查找到與工廠方法同名的候選方法,即有@Bean的同名方法 if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) { candidateList.add(candidate); } } //當(dāng)與工廠方法同名的候選方法只有一個(gè),且調(diào)用getBean方法時(shí)沒有傳參,且沒有緩存過參數(shù),直接通過調(diào)用實(shí)例化方法執(zhí)行該候選方法 if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { Method uniqueCandidate = candidateList.get(0); if (uniqueCandidate.getParameterCount() == 0) { mbd.factoryMethodToIntrospect = uniqueCandidate; synchronized (mbd.constructorArgumentLock) { mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate; mbd.constructorArgumentsResolved = true; mbd.resolvedConstructorArguments = EMPTY_ARGS; } bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS)); return bw; } } Method[] candidates = candidateList.toArray(new Method[0]); // 有多個(gè)與工廠方法同名的候選方法時(shí),進(jìn)行排序。public的方法會(huì)往前排,然后參數(shù)個(gè)數(shù)多的方法往前排 AutowireUtils.sortFactoryMethods(candidates); ConstructorArgumentValues resolvedValues = null; boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); int minTypeDiffWeight = Integer.MAX_VALUE; Set<Method> ambiguousFactoryMethods = null; int minNrOfArgs; // 如果調(diào)用getBean方法時(shí)有傳參,那么工廠方法最少參數(shù)個(gè)數(shù)要等于傳參個(gè)數(shù) if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { //如果getBean的時(shí)候沒有傳參,如果BeanDefinition中有設(shè)置,則從BeanDefinition中獲取 if (mbd.hasConstructorArgumentValues()) { ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } else { minNrOfArgs = 0; } } LinkedList<UnsatisfiedDependencyException> causes = null; for (Method candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); if (paramTypes.length >= minNrOfArgs) { ArgumentsHolder argsHolder; if (explicitArgs != null) { // Explicit arguments given -> arguments length must match exactly. if (paramTypes.length != explicitArgs.length) { continue; } argsHolder = new ArgumentsHolder(explicitArgs); } else { // Resolved constructor arguments: type conversion and/or autowiring necessary. try { String[] paramNames = null; ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { paramNames = pnd.getParameterNames(candidate); } //當(dāng)傳入的參數(shù)為空,需要根據(jù)工廠方法的參數(shù)類型注入相應(yīng)的bean argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring, candidates.length == 1); } catch (UnsatisfiedDependencyException ex) { if (logger.isTraceEnabled()) { logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex); } // Swallow and try next overloaded factory method. if (causes == null) { causes = new LinkedList<>(); } causes.add(ex); continue; } } //計(jì)算工廠方法的權(quán)重 int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this factory method if it represents the closest match. if (typeDiffWeight < minTypeDiffWeight) { /* * 當(dāng)權(quán)重小時(shí),重新設(shè)置factoryMethodToUse 和argsHolderToUse ,argsToUse , * 并把當(dāng)前權(quán)重值設(shè)置為最小權(quán)重值,等待遍歷的下一個(gè)候選工廠方法比對(duì), * 并且將ambiguousFactoryMethods (表示有含糊同樣權(quán)重的候選方法)設(shè)置為空 * */ factoryMethodToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousFactoryMethods = null; } /* * 當(dāng)遍歷到下一個(gè)候選方法的時(shí)候,已經(jīng)設(shè)置了factoryMethodToUse 且權(quán)重值與上一次的最小權(quán)重值相等時(shí), * ambiguousFactoryMethods填值,這個(gè)ambiguousFactoryMethods不為空表示有兩個(gè)候選方法的最小權(quán)重相等, * spring無法匹配出最適合的工廠方法,如果再繼續(xù)往下遍歷候選器,有更小的權(quán)重值, * 那ambiguousFactoryMethods會(huì)再次被設(shè)置為空 * */ else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight && !mbd.isLenientConstructorResolution() && paramTypes.length == factoryMethodToUse.getParameterCount() && !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) { if (ambiguousFactoryMethods == null) { ambiguousFactoryMethods = new LinkedHashSet<>(); ambiguousFactoryMethods.add(factoryMethodToUse); } ambiguousFactoryMethods.add(candidate); } } } if (factoryMethodToUse == null) { if (causes != null) { UnsatisfiedDependencyException ex = causes.removeLast(); for (Exception cause : causes) { this.beanFactory.onSuppressedException(cause); } throw ex; } List<String> argTypes = new ArrayList<>(minNrOfArgs); if (explicitArgs != null) { for (Object arg : explicitArgs) { argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null"); } } else if (resolvedValues != null) { Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount()); valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values()); valueHolders.addAll(resolvedValues.getGenericArgumentValues()); for (ValueHolder value : valueHolders) { String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) : (value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null")); argTypes.add(argType); } } String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes); throw new BeanCreationException(...); } //返回類型不能為void else if (void.class == factoryMethodToUse.getReturnType()) { throw new BeanCreationException(...); } //存在含糊的兩個(gè)工廠方法,不知選哪個(gè) else if (ambiguousFactoryMethods != null) { throw new BeanCreationException(...); } if (explicitArgs == null && argsHolderToUse != null) { mbd.factoryMethodToIntrospect = factoryMethodToUse; argsHolderToUse.storeCache(mbd, factoryMethodToUse); } } //去實(shí)例化 bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse)); return bw; }
3)用合適的構(gòu)造函數(shù)實(shí)例化部分解析
boolean resolved = false; //對(duì)構(gòu)造器是否緩存了 boolean autowireNecessary = false; //緩存的構(gòu)造器是否有參數(shù) //判斷是否有傳參,因?yàn)間etBean方法可以通過傳參指定構(gòu)造方法參數(shù)類型來進(jìn)行匹配(故這種對(duì)于參數(shù)的不確定是不能緩存的) if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; // autowireNecessary表示有沒有必要要進(jìn)行注入,比如當(dāng)前BeanDefinition用的是無參構(gòu)造方法,那么autowireNecessary為false,否則為true,表示需要給構(gòu)造方法參數(shù)注入值 autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { // 如果確定了當(dāng)前BeanDefinition的構(gòu)造方法,那么看是否需要進(jìn)行對(duì)構(gòu)造方法進(jìn)行參數(shù)的依賴注入(構(gòu)造方法注入) if (autowireNecessary) { // 方法內(nèi)會(huì)拿到緩存好的構(gòu)造方法的入?yún)? return autowireConstructor(beanName, mbd, null, null); } else { // 構(gòu)造方法已經(jīng)找到了,但是沒有參數(shù),那就表示是無參,直接進(jìn)行實(shí)例化 return instantiateBean(beanName, mbd); } } //沒有緩存,從bean后置處理器中為自動(dòng)裝配尋找構(gòu)造方法 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); //存在推斷出來的構(gòu)造方法集合 //BeanDefinition被設(shè)置為AUTOWIRE_CONSTRUCTOR //BeanDefinition指定了構(gòu)造方法參數(shù)值 //getBean的時(shí)候傳入了構(gòu)造方法參數(shù)值 if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 找出最合適的默認(rèn)構(gòu)造方法 ctors = mbd.getPreferredConstructors(); if (ctors != null) { // 構(gòu)造函數(shù)自動(dòng)注入 return autowireConstructor(beanName, mbd, ctors, null); }
代碼說明
【1】針對(duì)determineConstructorsFromBeanPostProcessors方法分析
代碼展示
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName) throws BeansException { //如果類不為null,且存在InstantiationAwareBeanPostProcessor接口的處理器【其實(shí)用的不是它,而是他的子接口SmartInstantiationAwareBeanPostProcessor】 if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; //真正存在實(shí)現(xiàn)的類是AutowiredAnnotationBeanPostProcessor類 Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName); if (ctors != null) { return ctors; } } } } return null; } //AutowiredAnnotationBeanPostProcessor類#determineCandidateConstructors方法 //日志和異常的輸出會(huì)被省略 public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException { // 判斷是否檢測(cè)過 if (!this.lookupMethodsChecked.contains(beanName)) { try { // 遍歷目標(biāo)類中的method,查看是否寫了@Lookup方法 ReflectionUtils.doWithMethods(beanClass, method -> { Lookup lookup = method.getAnnotation(Lookup.class); if (lookup != null) { Assert.state(this.beanFactory != null, "No BeanFactory available"); // 將當(dāng)前method封裝成LookupOverride并設(shè)置到BeanDefinition的methodOverrides中 LookupOverride override = new LookupOverride(method, lookup.value()); try { RootBeanDefinition mbd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName); mbd.getMethodOverrides().addOverride(override); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(beanName,"..."); } } }); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "...", ex); } this.lookupMethodsChecked.add(beanName); } //先從緩存中獲取,看之前有沒有找過 Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass); //緩存中沒有 if (candidateConstructors == null) { // 加鎖,開始尋找 synchronized (this.candidateConstructorsCache) { //再次從緩存中獲取,看被阻塞的時(shí)候沒有有線程已經(jīng)找好了存在緩存中(加鎖并雙重判斷的常規(guī)做法) candidateConstructors = this.candidateConstructorsCache.get(beanClass); if (candidateConstructors == null) { Constructor<?>[] rawCandidates; try { // 拿到所有的構(gòu)造方法 rawCandidates = beanClass.getDeclaredConstructors(); } catch (Throwable ex) { throw new BeanCreationException(beanName, "...", ex); } List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length); // 用來記錄required為true的構(gòu)造方法,一個(gè)類中只能有一個(gè)required為true的構(gòu)造方法 Constructor<?> requiredConstructor = null; // 用來記錄默認(rèn)無參的構(gòu)造方法 Constructor<?> defaultConstructor = null; // kotlin相關(guān),不用管 Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass); int nonSyntheticConstructors = 0; // 遍歷每個(gè)構(gòu)造方法 for (Constructor<?> candidate : rawCandidates) { if (!candidate.isSynthetic()) { // 記錄一下普通的構(gòu)造方法 nonSyntheticConstructors++; } else if (primaryConstructor != null) { continue; } // 當(dāng)前遍歷的構(gòu)造方法是否寫了@Autowired AnnotationAttributes ann = findAutowiredAnnotation(candidate); //沒有@Autowired注解的情況 if (ann == null) { // 如果beanClass是代理類,則得到被代理的類的類型 Class<?> userClass = ClassUtils.getUserClass(beanClass); if (userClass != beanClass) { try { //尋找被代理的類上的構(gòu)造方法有沒有@Autowired注解 Constructor<?> superCtor = userClass.getDeclaredConstructor(candidate.getParameterTypes()); ann = findAutowiredAnnotation(superCtor); } catch (NoSuchMethodException ex) {} } } /** * 構(gòu)造方法如果有@Autowired注解,最后的操作是candidates.add(candidate),但如果出現(xiàn)兩種情況會(huì)拋出異常 * a、如果之前有遍歷過@Autowired的構(gòu)造器,即requiredConstructor不為空 * b、如果當(dāng)前的@Autowired的required屬性為true,不管之前遍歷過的@Autowired屬性是true還是false */ if (ann != null) { // 整個(gè)類中如果有一個(gè)required為true的構(gòu)造方法,那就不能有其他的加了@Autowired的構(gòu)造方法 if (requiredConstructor != null) { throw new BeanCreationException(...); } //獲取該方法上的@Autowired注解的required屬性 boolean required = determineRequiredStatus(ann); if (required) { if (!candidates.isEmpty()) { throw new BeanCreationException(...); } requiredConstructor = candidate; } // 記錄所有加了@Autowired的構(gòu)造方法,不管required是true還是false,如果默認(rèn)無參的構(gòu)造方法上也加了@Autowired,那么也會(huì)加到candidates中 candidates.add(candidate); } else if (candidate.getParameterCount() == 0) { //沒有注解且參數(shù)個(gè)數(shù)為0【即無參】 // 記錄唯一一個(gè)無參的構(gòu)造方法 defaultConstructor = candidate; } //但是這里沒有處理:有可能存在有參、并且沒有添加@Autowired的構(gòu)造方法。 } //存儲(chǔ)有@Autowired注解的構(gòu)造方法的集合不為空 if (!candidates.isEmpty()) { // 如果不存在一個(gè)required為true的構(gòu)造方法,則所有required為false的構(gòu)造方法和無參構(gòu)造方法都是合格的 if (requiredConstructor == null) { if (defaultConstructor != null) { //無參構(gòu)造方法也添加到集合中 candidates.add(defaultConstructor); } else if (candidates.size() == 1 && logger.isInfoEnabled()) { logger.info(...); } } // 轉(zhuǎn)為數(shù)組 candidateConstructors = candidates.toArray(new Constructor<?>[0]); } // 沒有添加了@Autowired注解的構(gòu)造方法,并且類中只有一個(gè)構(gòu)造方法,并且是有參的 else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) { candidateConstructors = new Constructor<?>[] {rawCandidates[0]}; } // primaryConstructor不用管 else if (nonSyntheticConstructors == 2 && primaryConstructor != null && defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) { candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor}; } // primaryConstructor不用管 else if (nonSyntheticConstructors == 1 && primaryConstructor != null) { candidateConstructors = new Constructor<?>[] {primaryConstructor}; } else { // 如果有多個(gè)有參、并且沒有添加@Autowired的構(gòu)造方法,是會(huì)返回空的 candidateConstructors = new Constructor<?>[0]; } //將結(jié)果存入緩存 this.candidateConstructorsCache.put(beanClass, candidateConstructors); } } } //有候選構(gòu)造器則返回,沒有返回null return (candidateConstructors.length > 0 ? candidateConstructors : null); } 返回結(jié)果預(yù)測(cè): 沒有加了@Autowired注解的構(gòu)造方法 有多個(gè)構(gòu)造方法 --》 返回null 只有一個(gè)有參的構(gòu)造方法 --》 返回此構(gòu)造方法 只有一個(gè)無參的構(gòu)造方法 --》 返回null 存在加了@Autowired注解的構(gòu)造方法 只有一個(gè)required為true的構(gòu)造方法 --》 返回此構(gòu)造方法 有多個(gè)required為true的構(gòu)造方法 --》 拋異常 有一個(gè)required為true和其他的required為false的構(gòu)造方法 --》 視情況而定,可能會(huì)拋異常,可能會(huì)返回帶注解的構(gòu)造方法 沒有required為true的構(gòu)造方法 --》 返回所有required為false的構(gòu)造方法以及無參構(gòu)造方法
發(fā)現(xiàn)說明
1.在多個(gè)構(gòu)造方法上添加@Autowired注解會(huì)發(fā)生什么?
(1)因?yàn)锧Autowired注解的required屬性默認(rèn)為true,而根據(jù)源碼展示,如果有一個(gè)構(gòu)造方法上的@Autowired注解的required為true,那么后面的其他的構(gòu)造方法不管@Autowired注解的required屬性是什么都會(huì)報(bào)錯(cuò)。
(2)故如果要在多個(gè)構(gòu)造方法上添加@Autowired注解,那么必須將他們的required屬性設(shè)置為false。(這種更安全)
(3)要么就是將@Autowired注解的required屬性設(shè)置為true的構(gòu)造方法放到最后【僅限一個(gè)】
4)autowireConstructor方法分析
代碼展示
protected BeanWrapper autowireConstructor( String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) { return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs); } public ConstructorResolver(AbstractAutowireCapableBeanFactory beanFactory) { this.beanFactory = beanFactory; this.logger = beanFactory.getLogger(); } //ConstructorResolver類#autowireConstructor方法 //省略一些異常和日志 public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) { // 實(shí)例化BeanWrapper,是包裝bean的容器 BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Constructor<?> constructorToUse = null; ArgumentsHolder argsHolderToUse = null; Object[] argsToUse = null; // 如果getBean()傳入了args,那構(gòu)造方法要用的入?yún)⒕椭苯哟_定好了 if (explicitArgs != null) { argsToUse = explicitArgs; } else { // 如果getBean()沒有傳入了參數(shù) Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { // 嘗試從緩存中獲取構(gòu)造方法 constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod; if (constructorToUse != null && mbd.constructorArgumentsResolved) { // 從緩存中找到了構(gòu)造器,那么繼續(xù)從緩存中尋找緩存的構(gòu)造器參數(shù) argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { // 緩存沒有的話,那就獲取BeanDefinition指定的那些參數(shù)【可以沒有指定】 argsToResolve = mbd.preparedConstructorArguments; } } } //argsToResolve不為空,即之前獲取到了參數(shù)【是從BeanDefinition指定的】要解析配置的參數(shù) if (argsToResolve != null) { // 解析參數(shù)類型,比如將配置的String類型轉(zhuǎn)換為list、boolean等類型 argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true); } } // 如果沒有確定要使用的構(gòu)造方法,或者確定了構(gòu)造方法但是所要傳入的參數(shù)值沒有確定 if (constructorToUse == null || argsToUse == null) { // 拿到傳入的構(gòu)造器數(shù)組 Constructor<?>[] candidates = chosenCtors; //數(shù)組為空 if (candidates == null) { Class<?> beanClass = mbd.getBeanClass(); try { //使用public的構(gòu)造器(默認(rèn))或者所有構(gòu)造器 candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors()); } catch (Throwable ex) { throw new BeanCreationException(...); } } // 如果只有一個(gè)候選構(gòu)造方法,并且沒有指定所要使用的構(gòu)造方法參數(shù)值,并且該構(gòu)造方法是無參的,那就直接用這個(gè)無參構(gòu)造方法進(jìn)行實(shí)例化了 if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { Constructor<?> uniqueCandidate = candidates[0]; if (uniqueCandidate.getParameterCount() == 0) { synchronized (mbd.constructorArgumentLock) { mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate; mbd.constructorArgumentsResolved = true; mbd.resolvedConstructorArguments = EMPTY_ARGS; } bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS)); return bw; } } //需要解析構(gòu)造函數(shù)標(biāo)志(當(dāng)構(gòu)造方法數(shù)組不為空,或是自動(dòng)裝配構(gòu)造函數(shù)時(shí)) boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; // 確定要選擇的構(gòu)造方法的參數(shù)個(gè)數(shù)的最小值,后續(xù)判斷候選構(gòu)造方法的參數(shù)個(gè)數(shù)如果小于minNrOfArgs,則直接pass掉 int minNrOfArgs; if (explicitArgs != null) { //如果直接傳了構(gòu)造方法參數(shù)值,那么所用的構(gòu)造方法的參數(shù)個(gè)數(shù)肯定不能少于 minNrOfArgs = explicitArgs.length; } else { // 提取BeanDefinition中的配置的構(gòu)造函數(shù)參數(shù) ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); // 用于承載解析后的構(gòu)造函數(shù)參數(shù)的值 resolvedValues = new ConstructorArgumentValues(); // 能解析到的參數(shù)個(gè)數(shù) minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } // 排序給定的構(gòu)造函數(shù),public的構(gòu)造函數(shù)優(yōu)先,參數(shù)數(shù)量降序 AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; Set<Constructor<?>> ambiguousConstructors = null; LinkedList<UnsatisfiedDependencyException> causes = null; for (Constructor<?> candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); // 如果已經(jīng)找到選用的構(gòu)造函數(shù)或者需要的參數(shù)個(gè)數(shù)小于當(dāng)前的構(gòu)造函數(shù)參數(shù)個(gè)數(shù)則終止,前面已經(jīng)經(jīng)過了排序操作 if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) { break; } if (paramTypes.length < minNrOfArgs) { // 參數(shù)個(gè)數(shù)不相等 continue; } ArgumentsHolder argsHolder; // 沒有通過getBean()指定構(gòu)造方法參數(shù)值 if (resolvedValues != null) { try { // 有參數(shù)則根據(jù)值構(gòu)造對(duì)應(yīng)參數(shù)類型的參數(shù) String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length); if (paramNames == null) { // 獲取參數(shù)名稱探索器 ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { // 獲取指定構(gòu)造函數(shù)的參數(shù)名稱 paramNames = pnd.getParameterNames(candidate); } } // 根據(jù)名稱和數(shù)據(jù)類型創(chuàng)建參數(shù)持有者 argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1); } catch (UnsatisfiedDependencyException ex) { // 當(dāng)前正在遍歷的構(gòu)造方法找不到可用的入?yún)?duì)象,記錄一下 if (causes == null) { causes = new LinkedList<>(); } causes.add(ex); continue; } } else { // 在調(diào)getBean方法是傳入了參數(shù)值,那就表示只能用對(duì)應(yīng)參數(shù)個(gè)數(shù)的構(gòu)造方法 if (paramTypes.length != explicitArgs.length) { continue; } // 構(gòu)造函數(shù)沒有參數(shù)的情況 argsHolder = new ArgumentsHolder(explicitArgs); } // 當(dāng)前遍歷的構(gòu)造方法所需要的入?yún)?duì)象都找到了,根據(jù)參數(shù)類型和找到的參數(shù)對(duì)象計(jì)算出來一個(gè)匹配值,值越小越匹配(Lenient表示寬松模式) int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // 值越小越匹配 if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } // 值相等的情況下,記錄一下匹配值相同的構(gòu)造方法 else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { if (ambiguousConstructors == null) { ambiguousConstructors = new LinkedHashSet<>(); ambiguousConstructors.add(constructorToUse); } ambiguousConstructors.add(candidate); } } // 遍歷結(jié)束 // 如果沒有可用的構(gòu)造方法,就取記錄的最后一個(gè)異常并拋出 if (constructorToUse == null) { if (causes != null) { UnsatisfiedDependencyException ex = causes.removeLast(); for (Exception cause : causes) { this.beanFactory.onSuppressedException(cause); } throw ex; } throw new BeanCreationException(...); } // 如果有可用的構(gòu)造方法,但是有多個(gè) else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { throw new BeanCreationException(...); } if (explicitArgs == null && argsHolderToUse != null) { // 將解析的構(gòu)造函數(shù)加入緩存 argsHolderToUse.storeCache(mbd, constructorToUse); } } // 將構(gòu)造的實(shí)例加入BeanWrapper中 bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse)); return bw; }
發(fā)現(xiàn)說明
1.autowireConstructor()大體流程
?。?)先檢查是否指定了具體的構(gòu)造方法和構(gòu)造方法參數(shù)值,或者在BeanDefinition中緩存了具體的構(gòu)造方法或構(gòu)造方法參數(shù)值,如果存在那么則直接使用該構(gòu)造方法進(jìn)行實(shí)例化
?。?)如果沒有確定的構(gòu)造方法或構(gòu)造方法參數(shù)值,那么
1)如果沒有確定的構(gòu)造方法,那么則找出類中所有的構(gòu)造方法
2)如果只有一個(gè)無參的構(gòu)造方法,那么直接使用無參的構(gòu)造方法進(jìn)行實(shí)例化
3)如果有多個(gè)可用的構(gòu)造方法或者當(dāng)前Bean需要自動(dòng)通過構(gòu)造方法注入
4)根據(jù)所指定的構(gòu)造方法參數(shù)值,確定所需要的最少的構(gòu)造方法參數(shù)值的個(gè)數(shù)
5)對(duì)所有的構(gòu)造方法進(jìn)行排序,參數(shù)個(gè)數(shù)多的在前面
6)遍歷每個(gè)構(gòu)造方法
7)如果不是調(diào)用getBean方法時(shí)所指定的構(gòu)造方法參數(shù)值,那么則根據(jù)構(gòu)造方法參數(shù)類型找值
8)如果時(shí)調(diào)用getBean方法時(shí)所指定的構(gòu)造方法參數(shù)值,就直接利用這些值
9)如果根據(jù)當(dāng)前構(gòu)造方法找到了對(duì)應(yīng)的構(gòu)造方法參數(shù)值,那么這個(gè)構(gòu)造方法就是可用的,但是不一定這個(gè)構(gòu)造方法就是最佳的,所以這里會(huì)涉及到是否有多個(gè)構(gòu)造方法匹配了同樣的值,這個(gè)時(shí)候就會(huì)用值和構(gòu)造方法類型進(jìn)行匹配程度的打分,找到一個(gè)最匹配的
2.為什么分越少優(yōu)先級(jí)越高?
(1)主要是計(jì)算找到的bean和構(gòu)造方法參數(shù)類型匹配程度有多高。
(2)示例:假設(shè)bean的類型為A,A的父類是B,B的父類是C,同時(shí)A實(shí)現(xiàn)了接口D 如果構(gòu)造方法的參數(shù)類型為A,那么完全匹配,得分為0 如果構(gòu)造方法的參數(shù)類型為B,那么得分為2 如果構(gòu)造方法的參數(shù)類型為C,那么得分為4 如果構(gòu)造方法的參數(shù)類型為D,那么得分為
Object[] objects = new Object[]{new A()}; // 0 System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{A.class}, objects)); // 2 System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{B.class}, objects)); // 4 System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{C.class}, objects)); // 1 System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{D.class}, objects));
?。?)所以,我們可以發(fā)現(xiàn),越匹配分?jǐn)?shù)越低
5)無參構(gòu)造方法instantiateBean方法分析
//AbstractAutowireCapableBeanFactory類#instantiateBean方法 protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { try { Object beanInstance; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged( (PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this), getAccessControlContext()); } else { //獲取實(shí)例化策略并且進(jìn)行實(shí)例化操作 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this); } // 包裝成BeanWrapper BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } } //SimpleInstantiationStrategy類#instantiate方法 public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // bd對(duì)象定義中,是否包含MethodOverride列表,spring中有兩個(gè)標(biāo)簽參數(shù)會(huì)產(chǎn)生MethodOverrides,分別是lookup-method,replaced-method // 沒有MethodOverrides對(duì)象,可以直接實(shí)例化 if (!bd.hasMethodOverrides()) { // 實(shí)例化對(duì)象的構(gòu)造方法 Constructor<?> constructorToUse; // 鎖定對(duì)象,使獲得實(shí)例化構(gòu)造方法線程安全 synchronized (bd.constructorArgumentLock) { // 查看bd對(duì)象里使用否含有構(gòu)造方法 constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; // 如果沒有 if (constructorToUse == null) { // 從bd中獲取beanClass final Class<?> clazz = bd.getBeanClass(); // 如果要實(shí)例化的beanDefinition是一個(gè)接口,則直接拋出異常 if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { // 獲取系統(tǒng)安全管理器 if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor); } else { // 獲取默認(rèn)的無參構(gòu)造器 constructorToUse = clazz.getDeclaredConstructor(); } // 獲取到構(gòu)造器之后將構(gòu)造器賦值給bd中的屬性 bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } // 通過反射生成具體的實(shí)例化對(duì)象 return BeanUtils.instantiateClass(constructorToUse); } else { // 必須生成cglib子類 return instantiateWithMethodInjection(bd, beanName, owner); } } public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); return getConstructor0(parameterTypes, Member.DECLARED); } private Constructor<T> getConstructor0(Class<?>[] parameterTypes, int which) throws NoSuchMethodException { //獲取所有的構(gòu)造器 Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); //遍歷構(gòu)造器,匹配的返回 for (Constructor<T> constructor : constructors) { if (arrayContentsEq(parameterTypes, constructor.getParameterTypes())) { return getReflectionFactory().copyConstructor(constructor); } } throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes)); }
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(new CircularRefA());
到此這篇關(guān)于Spring啟動(dòng)過程中實(shí)例化部分代碼的分析之Bean的推斷構(gòu)造方法的文章就介紹到這了,更多相關(guān)Spring Bean的推斷構(gòu)造方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Bean實(shí)例化之前修改BeanDefinition示例詳解
- IOC?容器啟動(dòng)和Bean實(shí)例化兩個(gè)階段詳解
- Spring中Bean的三種實(shí)例化方式詳解
- 詳解Spring?Bean的配置方式與實(shí)例化
- Spring實(shí)例化bean的四種方式詳解
- SpringBoot借助spring.factories文件跨模塊實(shí)例化Bean
- Spring Bean生命周期之Bean的實(shí)例化詳解
- 在spring中實(shí)例化bean無效的問題
- 基于springboot bean的實(shí)例化過程和屬性注入過程
- Spring之詳解bean的實(shí)例化
相關(guān)文章
Java深入學(xué)習(xí)圖形用戶界面GUI之布局管理器
本文章向大家介紹Java GUI布局管理器,主要包括布局管理器使用實(shí)例、應(yīng)用技巧、基本知識(shí)點(diǎn)總結(jié)和需要注意事項(xiàng),具有一定的參考價(jià)值,需要的朋友可以參考一下2022-05-05Java設(shè)計(jì)模式之命令模式詳細(xì)解析
這篇文章主要介紹了Java設(shè)計(jì)模式之命令模式詳細(xì)解析,命令模式將請(qǐng)求封裝成對(duì)象,以便使用不同的請(qǐng)求、隊(duì)列或者日志來參數(shù)化其他對(duì)象,同時(shí)也支持可撤銷的操作,需要的朋友可以參考下2024-01-01Java過濾器與監(jiān)聽器間區(qū)別與聯(lián)系
監(jiān)聽器是一個(gè)接口內(nèi)容由我們實(shí)現(xiàn),會(huì)在特定時(shí)間被調(diào)用,監(jiān)聽器用于監(jiān)聽web應(yīng)用中三大域?qū)ο?request,session,application),信息的創(chuàng)建,銷毀,增加,修改,刪除等動(dòng)作的發(fā)生,然后做出相應(yīng)的響應(yīng)處理2023-01-01SpringBoot打印系統(tǒng)執(zhí)行的sql語(yǔ)句及日志配置指南
這篇文章主要給大家介紹了關(guān)于SpringBoot打印系統(tǒng)執(zhí)行的sql語(yǔ)句及日志配置的相關(guān)資料,在Java SpringBoot項(xiàng)目中如果使用了Mybatis框架,默認(rèn)情況下執(zhí)行的所有SQL操作都不會(huì)打印日志,需要的朋友可以參考下2023-10-10