Spring源码剖析4:其余方式获取Bean的过程分析

简介: 原型Bean加载过程 之前的文章,分析了非懒加载的单例Bean整个加载过程,除了非懒加载的单例Bean之外,Spring中还有一种Bean就是原型(Prototype)的Bean,看一下定义方式: 1 2 6 .

原型Bean加载过程

之前的文章,分析了非懒加载的单例Bean整个加载过程,除了非懒加载的单例Bean之外,Spring中还有一种Bean就是原型(Prototype)的Bean,看一下定义方式:

复制代码
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4     xsi:schemaLocation="http://www.springframework.org/schema/beans
5     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
6 
7     <bean id="prototypeBean" class="org.xrq.action.PrototypeBean" scope="prototype"  />
8     
9 </beans>
复制代码

原型Bean加载流程总得来说和单例Bean差不多,看一下不同之处,在AbstractBeanFactory的doGetBean的方法的这一步:

复制代码
 1 else if (mbd.isPrototype()) {
 2     // It's a prototype -> create a new instance.
 3     Object prototypeInstance = null;
 4     try {
 5         beforePrototypeCreation(beanName);
 6         prototypeInstance = createBean(beanName, mbd, args);
 7     }
 8     finally {
 9         afterPrototypeCreation(beanName);
10     }
11     bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
12 }
复制代码

第6行createBean是一样的,原型Bean实例化的主要区别就在于第6行,它是直接创建bean的,而单例bean我们再对比一下:

复制代码
 1 if (mbd.isSingleton()) {
 2     sharedInstance = getSingleton(beanName, new ObjectFactory() {
 3         public Object getObject() throws BeansException {
 4             try {
 5                 return createBean(beanName, mbd, args);
 6             }
 7             catch (BeansException ex) {
 8                 // Explicitly remove instance from singleton cache: It might have been put there
 9                 // eagerly by the creation process, to allow for circular reference resolution.
10                 // Also remove any beans that received a temporary reference to the bean.
11                 destroySingleton(beanName);
12                 throw ex;
13             }
14         }
15     });
16     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
17 }
复制代码

它优先会尝试getSington,即先尝试从singletonObjects中获取一下bean是否存在,如果存在直接返回singletonObjects中的bean对象。

接着,我们看到原型bean创建和单例bean创建的区别还在于第5行和第9行,先看第5行的代码:

复制代码
 1 protected void beforePrototypeCreation(String beanName) {
 2     Object curVal = this.prototypesCurrentlyInCreation.get();
 3     if (curVal == null) {
 4         this.prototypesCurrentlyInCreation.set(beanName);
 5     }
 6     else if (curVal instanceof String) {
 7         Set<String> beanNameSet = new HashSet<String>(2);
 8         beanNameSet.add((String) curVal);
 9         beanNameSet.add(beanName);
10         this.prototypesCurrentlyInCreation.set(beanNameSet);
11     }
12     else {
13         Set<String> beanNameSet = (Set<String>) curVal;
14         beanNameSet.add(beanName);
15     }
16 }
复制代码

这段主要是说bean在创建前要把当前beanName设置到ThreadLocal中去,其目的是保证多线程不会同时创建同一个bean。接着看第9行的代码实现,即bean创建之后做了什么:

复制代码
 1 protected void afterPrototypeCreation(String beanName) {
 2     Object curVal = this.prototypesCurrentlyInCreation.get();
 3     if (curVal instanceof String) {
 4         this.prototypesCurrentlyInCreation.remove();
 5     }
 6     else if (curVal instanceof Set) {
 7         Set<String> beanNameSet = (Set<String>) curVal;
 8         beanNameSet.remove(beanName);
 9         if (beanNameSet.isEmpty()) {
10             this.prototypesCurrentlyInCreation.remove();
11         }
12     }
13 }
复制代码

很好理解,就是把当前bean移除一下,这样其它线程就可以创建bean了。第11行的代码不看了,意思是如果bean是FactoryBean的实现类的话,调用getObject()方法获取真正的对象。

 

byName源码实现

Spring有为开发者提供Autowire(自动装配)的功能,自动装配最常用的就是byName和byType这两种属性。由于自动装配是为了解决对象注入导致的<property>过多的问题,因此很容易找到byName与byType的Spring源码实现应该在属性注入这一块,定位到属性注入的代码AbstractAutowireCapableBeanFactory的populateBean方法,直接截取重点:

复制代码
 1 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
 2         mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
 3     MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
 4 
 5     // Add property values based on autowire by name if applicable.
 6     if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
 7         autowireByName(beanName, mbd, bw, newPvs);
 8     }
 9 
10     // Add property values based on autowire by type if applicable.
11     if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
12         autowireByType(beanName, mbd, bw, newPvs);
13     }
14 
15     pvs = newPvs;
16 }
复制代码

看到第6行~第8行判断是否byName形式,是就执行byName自动装配代码;第11行~第13行判断是否byType形式,是就执行byType自动装配代码。那么首先看一下第7行的byName代码实现:

复制代码
 1 protected void autowireByName(
 2         String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
 3 
 4     String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
 5     for (String propertyName : propertyNames) {
 6         if (containsBean(propertyName)) {
 7             Object bean = getBean(propertyName);
 8             pvs.add(propertyName, bean);
 9             registerDependentBean(propertyName, beanName);
10             if (logger.isDebugEnabled()) {
11                 logger.debug("Added autowiring by name from bean name '" + beanName +
12                         "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
13             }
14         }
15         else {
16             if (logger.isTraceEnabled()) {
17                 logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
18                         "' by name: no matching bean found");
19             }
20         }
21     }
22 }
复制代码

篇幅问题,代码不一层层跟了,逻辑梳理一下:

  • 第4行,找到Bean中不是简单属性的属性,这句话有点绕,意思就是找到属性是对象类型的属性,但也不是所有的对象类型都会被找到,比如CharSequence类型、Number类型、Date类型、URL类型、URI类型、Locale类型、Class类型就会忽略,具体可见BeanUtils的isSimpleProperty方法
  • 第5行~第7行,遍历所有被找到的属性,如果bean定义中包含了属性名,那么先实例化该属性名对应的bean
  • 第9行registerDependentBean,注册一下当前bean的依赖bean,用于在某个bean被销毁前先将其依赖的bean销毁

其余代码都是一些打日志的,没什么好说的。

 

byType源码实现

上面说了byName的源码实现,接下来看一下byType源码实现:

复制代码
 1 protected void autowireByType(
 2         String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
 3 
 4     TypeConverter converter = getCustomTypeConverter();
 5     if (converter == null) {
 6         converter = bw;
 7     }
 8 
 9     Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);
10     String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
11     for (String propertyName : propertyNames) {
12         try {
13             PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
14             // Don't try autowiring by type for type Object: never makes sense,
15             // even if it technically is a unsatisfied, non-simple property.
16             if (!Object.class.equals(pd.getPropertyType())) {
17                 MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
18                 // Do not allow eager init for type matching in case of a prioritized post-processor.
19                 boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());
20                 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
21                 Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
22                 if (autowiredArgument != null) {
23                     pvs.add(propertyName, autowiredArgument);
24                 }
25                 for (String autowiredBeanName : autowiredBeanNames) {
26                     registerDependentBean(autowiredBeanName, beanName);
27                     if (logger.isDebugEnabled()) {
28                         logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
29                                 propertyName + "' to bean named '" + autowiredBeanName + "'");
30                     }
31                 }
32                 autowiredBeanNames.clear();
33             }
34         }
35         catch (BeansException ex) {
36             throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
37         }
38     }
39 }
复制代码

前面一样,到第10行都是找到Bean中属性是对象类型的属性。

接着就是遍历一下PropertyName,获取PropertyName对应的属性描述,注意一下16行的判断及其对应的注释:不要尝试自动装配Object类型,这没有任何意义,即使从技术角度看它是一个非简单的对象属性

第18行~第20行跳过(没有太明白是干什么的),byType实现的源码主要在第21行的方法resolveDependency中,这个方法是AbstractAutowireCapableBeanFactory类的实现类DefaultListableBeanFactory中的方法:

复制代码
 1 public Object resolveDependency(DependencyDescriptor descriptor, String beanName,
 2     Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException  {
 3 
 4     descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
 5     if (descriptor.getDependencyType().equals(ObjectFactory.class)) {
 6         return new DependencyObjectFactory(descriptor, beanName);
 7     }
 8     else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) {
 9         return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);
10     }
11     else {
12         return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter);
13     }
14 }
复制代码

这里判断一下要自动装配的属性是ObjectFactory.class还是javaxInjectProviderClass还是其他的,我们装配的是其他的,看一下12行的代码实现:

复制代码
 1 protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
 2     Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException  {
 3 
 4     Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
 5     if (value != null) {
 6         if (value instanceof String) {
 7             String strVal = resolveEmbeddedValue((String) value);
 8             BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
 9             value = evaluateBeanDefinitionString(strVal, bd);
10         }
11         TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
12         return converter.convertIfNecessary(value, type);
13     }
14 
15     if (type.isArray()) {
16         ...
17     }
18     else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
19         ...
20     }
21     else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
22         ...
23     }
24     else {
25         Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
26         if (matchingBeans.isEmpty()) {
27             if (descriptor.isRequired()) {
28                 raiseNoSuchBeanDefinitionException(type, "", descriptor);
29             }
30             return null;
31         }
32         if (matchingBeans.size() > 1) {
33             String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
34             if (primaryBeanName == null) {
35                 throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " +
36                         matchingBeans.size() + ": " + matchingBeans.keySet());
37             }
38             if (autowiredBeanNames != null) {
39                 autowiredBeanNames.add(primaryBeanName);
40             }
41             return matchingBeans.get(primaryBeanName);
42         }
43         // We have exactly one match.
44         Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
45         if (autowiredBeanNames != null) {
46             autowiredBeanNames.add(entry.getKey());
47         }
48         return entry.getValue();
49     }
50 }
复制代码

第四行结果是null不看了,为了简化代码Array装配、Collection装配、Map装配的代码都略去了,重点看一下普通属性的装配。首先是第25行获取一下自动装配的候选者:

复制代码
 1 protected Map<String, Object> findAutowireCandidates(
 2     String beanName, Class requiredType, DependencyDescriptor descriptor) {
 3 
 4     String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
 5             this, requiredType, true, descriptor.isEager());
 6     Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
 7     for (Class autowiringType : this.resolvableDependencies.keySet()) {
 8         if (autowiringType.isAssignableFrom(requiredType)) {
 9             Object autowiringValue = this.resolvableDependencies.get(autowiringType);
10             autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
11             if (requiredType.isInstance(autowiringValue)) {
12                     result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
13                 break;
14             }
15         }
16     }
17     for (String candidateName : candidateNames) {
18         if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, descriptor)) {
19             result.put(candidateName, getBean(candidateName));
20         }
21     }
22     return result;
23 }
复制代码

代码逻辑整理一下:

  • 首先获取候选者bean名称,通过DefaultListableBeanFactory的getBeanNamesForType方法,即找一下所有的Bean定义中指定Type的实现类或者子类
  • 接着第7行~第16行的判断要自动装配的类型是不是要自动装配的纠正类型,这个在【Spring源码分析】非懒加载的单例Bean初始化前后的一些操作一文讲PrepareBeanFactory方法的时候有讲过,如果要自动装配的类型是纠正类型,比如是一个ResourceLoader,那么就会为该类型生成一个代理实例,具体可以看一下第10行的AutowireUtils.resolveAutowiringValue方法的实现
  • 正常来说都是执行的第17行~第21行的代码,逐个判断查找一下beanName对应的BeanDefinition,判断一下是不是自动装配候选者,默认都是的,如果<bean>的autowire-candidate属性设置为false就不是

这样,拿到所有待装配对象的实现类或者子类的候选者,组成一个Map,Key为beanName,Value为具体的Bean。接着回看获取Bean之后的逻辑:

复制代码
 1 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
 2     if (matchingBeans.isEmpty()) {
 3         if (descriptor.isRequired()) {
 4             raiseNoSuchBeanDefinitionException(type, "", descriptor);
 5         }
 6         return null;
 7     }
 8     if (matchingBeans.size() > 1) {
 9         String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
10         if (primaryBeanName == null) {
11             throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " +
12                     matchingBeans.size() + ": " + matchingBeans.keySet());
13         }
14         if (autowiredBeanNames != null) {
15             autowiredBeanNames.add(primaryBeanName);
16         }
17         return matchingBeans.get(primaryBeanName);
18     }
19     // We have exactly one match.
20     Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
21     if (autowiredBeanNames != null) {
22         autowiredBeanNames.add(entry.getKey());
23     }
24     ...
25 }
复制代码

整理一下逻辑:

  • 如果拿到的Map是空的且属性必须注入,抛异常
  • 如果拿到的Map中有多个候选对象,判断其中是否有<bean>中属性配置为"primary=true"的,有就拿执行第13行~第15行的代码,没有就第8行的方法返回null,抛异常,这个异常的描述相信Spring用的比较多的应该比较熟悉
  • 如果拿到的Map中只有一个候选对象,直接拿到那个 

通过这样一整个流程,实现了byType自动装配,byType自动装配流程比较长,中间细节比较多,还需要多看看才能弄明白。

最后注意一点,即所有待注入的PropertyName-->PropertyValue映射拿到之后都只是放在MutablePropertyValues中,最后由AbstractPropertyAccessor类的setPropertyValues方法遍历并进行逐一注入。

 

通过FactoryBean获取Bean实例源码实现

我们知道可以通过实现FactoryBean接口,重写getObject()方法实现个性化定制Bean的过程,这部分我们就来看一下Spring源码是如何实现通过FactoryBean获取Bean实例的。代码直接定位到AbstractBeanFactory的doGetBean方法创建单例Bean这部分:

复制代码
 1 // Create bean instance.
 2 if (mbd.isSingleton()) {
 3     sharedInstance = getSingleton(beanName, new ObjectFactory() {
 4         public Object getObject() throws BeansException {
 5             try {
 6                 return createBean(beanName, mbd, args);
 7             }
 8             catch (BeansException ex) {
 9                 // Explicitly remove instance from singleton cache: It might have been put there
10                 // eagerly by the creation process, to allow for circular reference resolution.
11                 // Also remove any beans that received a temporary reference to the bean.
12                 destroySingleton(beanName);
13                 throw ex;
14             }
15         }
16     });
17     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
18 }
复制代码

FactoryBean首先是个Bean且被实例化出来成为一个对象之后才能调用getObject()方法,因此还是会执行第3行~第16行的代码,这段代码之前分析过了就不说了。之后执行第17行的方法:

复制代码
 1 protected Object getObjectForBeanInstance(
 2         Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
 3 
 4     // Don't let calling code try to dereference the factory if the bean isn't a factory.
 5     if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
 6         throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
 7     }
 8 
 9     // Now we have the bean instance, which may be a normal bean or a FactoryBean.
10     // If it's a FactoryBean, we use it to create a bean instance, unless the
11     // caller actually wants a reference to the factory.
12     if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
13         return beanInstance;
14     }
15 
16     Object object = null;
17     if (mbd == null) {
18         object = getCachedObjectForFactoryBean(beanName);
19     }
20     if (object == null) {
21         // Return bean instance from factory.
22         FactoryBean factory = (FactoryBean) beanInstance;
23         // Caches object obtained from FactoryBean if it is a singleton.
24         if (mbd == null && containsBeanDefinition(beanName)) {
25             mbd = getMergedLocalBeanDefinition(beanName);
26         }
27         boolean synthetic = (mbd != null && mbd.isSynthetic());
28         object = getObjectFromFactoryBean(factory, beanName, !synthetic);
29     }
30     return object;
31 }
复制代码

首先第5行~第7行判断一下是否beanName以"&"开头并且不是FactoryBean的实现类,不满足则抛异常,因为beanName以"&"开头是FactoryBean的实现类bean定义的一个特征。

接着判断第12行~第14行,如果:

  • bean不是FactoryBean的实现类
  • beanName以"&"开头

这两种情况,都直接把生成的bean对象返回出去,不会执行余下的流程。

最后流程走到第16行~第30行,最终调用getObject()方法实现个性化定制bean,先执行第28行的方法:

复制代码
 1 protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
 2     if (factory.isSingleton() && containsSingleton(beanName)) {
 3         synchronized (getSingletonMutex()) {
 4             Object object = this.factoryBeanObjectCache.get(beanName);
 5             if (object == null) {
 6                 object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
 7                 this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
 8             }
 9             return (object != NULL_OBJECT ? object : null);
10         }
11     }
12     else {
13         return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
14     }
15 }
复制代码

第1行~第11行的代码与第12行~第13行的代码最终都是一样的,调用了如下一段:

复制代码
 1 private Object doGetObjectFromFactoryBean(
 2         final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
 3         throws BeanCreationException {
 4 
 5     Object object;
 6     try {
 7         if (System.getSecurityManager() != null) {
 8             AccessControlContext acc = getAccessControlContext();
 9             try {
10                 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
11                     public Object run() throws Exception {
12                             return factory.getObject();
13                         }
14                     }, acc);
15             }
16             catch (PrivilegedActionException pae) {
17                 throw pae.getException();
18             }
19         }
20         else {
21             object = factory.getObject();
22         }
23     }
24     catch (FactoryBeanNotInitializedException ex) {
25         throw new BeanCurrentlyInCreationException(beanName, ex.toString());
26     }
27     catch (Throwable ex) {
28         throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
29     }
30         
31     // Do not accept a null value for a FactoryBean that's not fully
32     // initialized yet: Many FactoryBeans just return null then.
33     if (object == null && isSingletonCurrentlyInCreation(beanName)) {
34         throw new BeanCurrentlyInCreationException(
35                 beanName, "FactoryBean which is currently in creation returned null from getObject");
36     }
37 
38     if (object != null && shouldPostProcess) {
39         try {
40             object = postProcessObjectFromFactoryBean(object, beanName);
41         }
42         catch (Throwable ex) {
43             throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
44         }
45     }
46 
47     return object;
48 }
复制代码

第12行和第21行的代码,都一样,最终调用getObject()方法获取对象。回过头去看之前的getObjectFromFactoryBean方法,虽然if...else...逻辑最终都是调用了以上的方法,但是区别在于:

  • 如果FactoryBean接口实现类的isSington方法返回的是true,那么每次调用getObject方法的时候会优先尝试从FactoryBean对象缓存中取目标对象,有就直接拿,没有就创建并放入FactoryBean对象缓存,这样保证了每次单例的FactoryBean调用getObject()方法后最终拿到的目标对象一定是单例的,即在内存中都是同一份
  • 如果FactoryBean接口实现类的isSington方法返回的是false,那么每次调用getObject方法的时候都会新创建一个目标对象

微信公众号【黄小斜】大厂程序员,互联网行业新知,终身学习践行者。关注后回复「Java」、「Python」、「C++」、「大数据」、「机器学习」、「算法」、「AI」、「Android」、「前端」、「iOS」、「考研」、「BAT」、「校招」、「笔试」、「面试」、「面经」、「计算机基础」、「LeetCode」 等关键字可以获取对应的免费学习资料。 


                     

相关文章
|
16天前
|
XML 安全 Java
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
67 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
16天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
40 14
|
14天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
30 2
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
14天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
27 1
|
1月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
62 9
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
219 2