1.类内部的注解,如:@Autowire、@Value、@Required、@Resource以及EJB和WebSerivce相关的注解,是容器对Bean对象实例化和依赖注入时,通过容器中注册的Bean后置处理器处理这些注解的。
2.Spring中处理注解的Bean后置处理器:
当使用Spring的注解功能时,在Spring配置文件中添加如下配置开启Spring的注解处理器:
上面的配置将隐式地向Spring容器注册、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor以及这4个专门用于处理注解的Bean后置处理器。
下面将具体介绍这4个注解后置处理器。
:
是Spring容器专门处理配置了自动依赖注入装配相关注解(@Autowire、@Value以及其他JSR-330注解)的Bean后置处理器,其主要功能源码如下:
(1).的构造方法:
AutowiredAnnotationBeanPostProcessor只有一个的构造方法,其源码如下:
public AutowiredAnnotationBeanPostProcessor() { //后置处理器将处理@Autowire注解 this.autowiredAnnotationTypes.add(Autowired.class); //后置处理器将处理@Value注解 this.autowiredAnnotationTypes.add(Value.class); //获取当前类的类加载器 ClassLoader cl = AutowiredAnnotationBeanPostProcessor.class.getClassLoader(); try { //后置处理器将处理javax.inject.Inject JSR-330注解 this.autowiredAnnotationTypes.add((Class extends Annotation>) cl.loadClass("javax.inject.Inject")); logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
(2).为指定类选择其合适的构造方法:
容器对指定类进行自动依赖注入装配(autowiring)时,容器需要对Bean调用合适的构造方法创建实例对象,AutowiredAnnotationBeanPostProcessor为指定类选择相应的构造方法,源码如下:
//为自动依赖注入装配Bean选择合适的构造方法 public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException { //首先从容器的缓存中查找是否有指定Bean的构造方法 Constructor[] candidateConstructors = this.candidateConstructorsCache.get(beanClass); //容器缓存中没有给定类的构造方法 if (candidateConstructors == null) { //线程同步以确保容器中数据一致性 synchronized (this.candidateConstructorsCache) { candidateConstructors = this.candidateConstructorsCache.get(beanClass); if (candidateConstructors == null) { //通过JDK反射机制,获取指定类的中所有声明的构造方法 Constructor[] rawCandidates = beanClass.getDeclaredConstructors(); //存放候选构造方法的集合 List
(3).AutowiredAnnotationBeanPostProcessor对方法和属性的依赖注入:
当Spring容器对配置了autowire相关注解的Bean进行依赖注入时,后置处理器对属性和对象进行自动注入处理,源码如下:
//处理类中的属性 public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { //获取指定类中autowire相关注解的元信息 InjectionMetadata metadata = findAutowiringMetadata(bean.getClass()); try { //对Bean的属性进行自动注入 metadata.inject(bean, beanName, pvs); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; } //处理对象的注入 public void processInjection(Object bean) throws BeansException { //获取给定Bean的Class对象 Class> clazz = bean.getClass(); //获取给定类中autowire相关注解元信息 InjectionMetadata metadata = findAutowiringMetadata(clazz); try { //对Bean对象进行自动注入 metadata.inject(bean, null, null); } catch (Throwable ex) { throw new BeanCreationException("Injection of autowired dependencies failed for class [" + clazz + "]", ex); } } //获取给定类的autowire相关注解元信息 private InjectionMetadata findAutowiringMetadata(Class clazz) { //首先从容器中查找是否有给定类的autowire相关注解元信息 InjectionMetadata metadata = this.injectionMetadataCache.get(clazz); if (metadata == null) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(clazz); if (metadata == null) { //解析给定类autowire相关注解元信息 metadata = buildAutowiringMetadata(clazz); //将得到的给定类autowire相关注解元信息存储在容器缓存中 this.injectionMetadataCache.put(clazz, metadata); } } } return metadata; } //解析给定类autowire相关注解元信息 private InjectionMetadata buildAutowiringMetadata(Class clazz) { //创建一个存放注解元信息的集合 LinkedList
(4).AutowiredAnnotationBeanPostProcessor对字段和方法的注入:
a.AutowiredAnnotationBeanPostProcessor对字段的注入是通过AutowiredFieldElement类的inject方法实现的,源码如下:
//对字段进行注入 protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { //获取注入元素对象 Field field = (Field) this.member; try { Object value; //如果当前对象在容器中被缓存 if (this.cached) { //根据Bean名称解析缓存中的字段值 value = resolvedCachedArgument(beanName, this.cachedFieldValue); } //如果当前对象没有被容器缓存 else { //创建一个字段依赖描述符 DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required); Set
b.AutowiredAnnotationBeanPostProcessor对字段的注入是通过AutowiredMethodElement类的inject方法实现的,源码如下:
//对方法进行注入 protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { //如果属性被显式设置为skip,则不进行注入 if (checkPropertySkipping(pvs)) { return; } //获取注入元素对象 Method method = (Method) this.member; try { Object[] arguments; //如果容器对当前方法缓存 if (this.cached) { //获取缓存中指定Bean名称的方法参数 arguments = resolveCachedArguments(beanName); } //如果没有缓存 else { //获取方法的参数列表 Class[] paramTypes = method.getParameterTypes(); //创建一个存放方法参数的数组 arguments = new Object[paramTypes.length]; DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length]; Set
beanFactory.resolveDependency和registerDependentBeans方法我们在Spring容器依赖注入源码分析中已经分析过,这里就不再具体分析。
后置处理器主要解析autowire相关的注解,即@Autowire、@Value等。
4.:
CommonAnnotationBeanPostProcessor是Spring中用于处理JavaEE5中常用注解(主要是EJB相关的注解)和Java6中关于JAX-WS相关的注解,可以处理PostConstruct、@PreDestroy等Bean生命周期相关事件的注解,该后置处理最核心的是处理@Resource注解,同时还可以处理JAX-WS相关的注解,按照其主要功能分析其主要实现源码:
(1).静态初始化块和构造函数:
//WebService关于JAX-WS的相关注解 private static Class extends Annotation> webServiceRefClass = null; //EJB相关的注解 private static Class extends Annotation> ejbRefClass = null; //静态初始化块 static { //获取当前类的类加载器 ClassLoader cl = CommonAnnotationBeanPostProcessor.class.getClassLoader(); try { //使用类加载器加载WebService相关的类 webServiceRefClass = (Class) cl.loadClass("javax.xml.ws.WebServiceRef"); } catch (ClassNotFoundException ex) { webServiceRefClass = null; } try { //使用类加载器加载EJB相关的类 ejbRefClass = (Class) cl.loadClass("javax.ejb.EJB"); } catch (ClassNotFoundException ex) { ejbRefClass = null; } } //构造方法 public CommonAnnotationBeanPostProcessor() { setOrder(Ordered.LOWEST_PRECEDENCE - 3); //设置初始的注解类型为@PostConstruct setInitAnnotationType(PostConstruct.class); //设置消耗的注解为@ PreDestroy setDestroyAnnotationType(PreDestroy.class); //当使用@Resource注解时,忽略JAX-WS的资源类型 ignoreResourceType("javax.xml.ws.WebServiceContext"); }
从CommonAnnotationBeanPostProcessor的静态初始化块和构造方法可以看出该后置处理器主要处理EJB和WebService相关的注解,以及Bean生命周期事件的相关注解。
(2).CommonAnnotationBeanPostProcessor对属性值的查找:
CommonAnnotationBeanPostProcessor对普通属性的处理与AutowiredAnnotationBeanPostProcessor的处理基本相同,不同在于查找属性值的方法不同,通过autowire相关注解的required配置获取依赖的属性值,则通过对@Resource注解的解析获取属性的值,CommonAnnotationBeanPostProcessor获取属性值的主要源码:
//处理属性值 public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { //获取@Resource注解中配置的属性值元数据 InjectionMetadata metadata = findResourceMetadata(bean.getClass()); try { //注入属性值,与AutowiredAnnotationBeanPostProcessor中处理相同 metadata.inject(bean, beanName, pvs); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex); } return pvs; } //获取@Resource注解中配置的属性值元数据 private InjectionMetadata findResourceMetadata(final Class clazz) { //首先从容器缓存中查找 InjectionMetadata metadata = this.injectionMetadataCache.get(clazz); //缓存中没有给定类的注解元信息数据 if (metadata == null) { //线程同步,以确保容器中数据一致 synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(clazz); if (metadata == null) { //创建一个用于存放注解元数据的集合 LinkedList
从上面的源码中,我们可以看到,对于方法上面的注解,EJB和WebService相关注解以及@Resource只能在单个参数的方法上配置,否则会有异常抛出。
(3).根据给定名称或者类型获取资源对象:
WebService和EJB相关注解以及@Resource主要是为所添加的字段或者方法注入所需要的资源,CommonAnnotationBeanPostProcessor类中获取资源的源码如下:
//根据给定名称或者类型获取资源对象 protected Object getResource(LookupElement element, String requestingBeanName) throws BeansException { //如果注解对象元素的mappedName属性不为空 if (StringUtils.hasLength(element.mappedName)) { //根据JNDI名称和类型去Spring的JNDI容器中获取Bean return this.jndiFactory.getBean(element.mappedName, element.lookupType); } //如果该后置处理器的alwaysUseJndiLookup属性值为true if (this.alwaysUseJndiLookup) { //从Spring的JNDI容器中查找指定JDNI名称和类型的Bean return this.jndiFactory.getBean(element.name, element.lookupType); } if (this.resourceFactory == null) { throw new NoSuchBeanDefinitionException(element.lookupType, "No resource factory configured - specify the 'resourceFactory' property"); } //使用autowiring自动依赖注入装配,通过给定的名称和类型从资源容器获取Bean对象 return autowireResource(this.resourceFactory, element, requestingBeanName); } //自动依赖注入装配资源对象 protected Object autowireResource(BeanFactory factory, LookupElement element, String requestingBeanName) throws BeansException { Object resource; Set
(4).@Resource、WebService和EJB相关注解的解析和属性注入:
Spring中,@Resource注解是由类解析的,WebService相关注解是由类解析的,EJB相关注解是由EjbRefElement类解析的,下面就具体分析其解析的实现
a.ResourceElement解析@Resource注解和属性注入:
ResourceElement是Spring中用于解析@Resource注解和属性注入的类,源码如下:
//解析@Resource注解 protected void initAnnotation(AnnotatedElement ae) { //获取元素对象的@Resource注解 Resource resource = ae.getAnnotation(Resource.class); //获取@Resource注解的name属性值作为资源名称 String resourceName = resource.name(); //获取@Resource注解的type属性值作为资源类型 Class resourceType = resource.type(); //根据注解配置的资源名称是否为空,判断元素是否有默认名称 this.isDefaultName = !StringUtils.hasLength(resourceName); //如果元素有默认名称 if (this.isDefaultName) { //获取元素对象名称作为资源名称 resourceName = this.member.getName(); //如果当前添加注解的对象是方法,且方法名称以set开头,且属性名称 //不为空(方法名长度大于3,即除了set之外还有其他字符) if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { //利用JDK的内省机制格式化对象名称,转换为Java标准的驼峰命名法 resourceName = Introspector.decapitalize(resourceName.substring(3)); } } //如果当前的容器是ConfigurableBeanFactory类型容器 else if (beanFactory instanceof ConfigurableBeanFactory){ //解析给定的对象名的内嵌值做为资源名称 resourceName = ((ConfigurableBeanFactory) beanFactory).resolveEmbeddedValue(resourceName); } //如果注解配置的资源类型不为空,且不说Object类型 if (resourceType != null && !Object.class.equals(resourceType)) { //检查资源类型是字段还是方法 checkResourceType(resourceType); } else { //如果注解中没有配置资源类型,则利用JDK反射机制判断是字段还是方法 resourceType = getResourceType(); } //设置当前注解元素的资源名称 this.name = resourceName; //设置当前注解元素的JNDI类型 this.lookupType = resourceType; //获取@Resource注解的mappedName属性值,设置当前注解元素的JNDI名称 this.mappedName = resource.mappedName(); //获取@Resource注解的shareable属性值,设置当前注解元素是否在容器中共享 this.shareable = resource.shareable(); } //注入属性 protected Object getResourceToInject(Object target, String requestingBeanName) { //调用我们在(3)中分析的源码,注入依赖的资源对象 return getResource(this, requestingBeanName); }
解析WebService相关的注解和属性注入:
WebServiceRefElement是Spring中用于解析WebService相关注解和属性注入的类,源码如下:
//解析WebService相关的注解 protected void initAnnotation(AnnotatedElement ae) { //获取元素对象上的@WebServiceRef注解 WebServiceRef resource = ae.getAnnotation(WebServiceRef.class); //获取@WebServiceRef注解的name属性值作为资源名称 String resourceName = resource.name(); //获取@WebServiceRef注解的type属性值作为资源类型 Class resourceType = resource.type(); //根据资源名称是否为空判断当前元素是否有默认名称 this.isDefaultName = !StringUtils.hasLength(resourceName); //如果当前元素有默认名称 if (this.isDefaultName) { //获取当前元素对象的名称作用资源名称 resourceName = this.member.getName(); //如果添加注解的当前元素是方法,且方法名以set开头,且资源对象名称不为空 if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { //利用JDK的内省机制,将格式化资源名称(将去掉set之后的名称首字//母小写),是资源名称符合java变量的驼峰命名规则 resourceName = Introspector.decapitalize(resourceName.substring(3)); } } //如果@WebServiceRef注解的type属性值不为空 if (resourceType != null && !Object.class.equals(resourceType)) { //根据注解配置的资源类型值检查资源对象是字段还是方法 checkResourceType(resourceType); } //如果@WebServiceRef注解没有配置type属性 else { //容器通过JDK的反射机制检查资源对象是字段还是方法类型 resourceType = getResourceType(); } //将资源名称赋值给元素的名称 this.name = resourceName; //为元素设置资源类型 this.elementType = resourceType; //指定类型的资源可以Service被访问 if (Service.class.isAssignableFrom(resourceType)) { //设置JNDI的类型 this.lookupType = resourceType; } //如果指定类型的资源不能被Service访问 else { //根据@WebServiceRef注解的value属性值是否是Object,设置JNDI类型 this.lookupType = (!Object.class.equals(resource.value()) ? resource.value() : Service.class); } //获取@WebServiceRef注解的mappedName属性值,设置JNDI名称 this.mappedName = resource.mappedName(); //获取@WebServiceRef注解的wsdlLocation属性值,设置元素的wsdl路径 this.wsdlLocation = resource.wsdlLocation(); } //属性注入 protected Object getResourceToInject(Object target, String requestingBeanName) { Service service; try { //根据JNDI名称和类型获取指定资源对象 service = (Service) getResource(this, requestingBeanName); } catch (NoSuchBeanDefinitionException notFound) { //如果JNDI类型是Service if (Service.class.equals(this.lookupType)) { throw new IllegalStateException("No resource with name '" + this.name + "' found in context, " + "and no specific JAX-WS Service subclass specified. The typical solution is to either specify " + "a LocalJaxWsServiceFactoryBean with the given name or to specify the (generated) Service " + "subclass as @WebServiceRef(...) value."); } //如果元素的wsdl路径不为空 if (StringUtils.hasLength(this.wsdlLocation)) { try { //根据wsdl获取构造方法 Constructor ctor = this.lookupType.getConstructor(new Class[] {URL.class, QName.class}); //获取元素JDNI类型的@WebServiceClient注解,创建WebService客户端对象 WebServiceClient clientAnn = this.lookupType.getAnnotation(WebServiceClient.class); if (clientAnn == null) { throw new IllegalStateException("JAX-WS Service class [" + this.lookupType.getName() + "] does not carry a WebServiceClient annotation"); } //根据构造方法和wsdl文件WebService实例对象 service = (Service) BeanUtils.instantiateClass(ctor, new URL(this.wsdlLocation), new QName(clientAnn.targetNamespace(), clientAnn.name())); } catch (NoSuchMethodException ex) { throw new IllegalStateException("JAX-WS Service class [" + this.lookupType.getName() + "] does not have a (URL, QName) constructor. Cannot apply specified WSDL location [" + this.wsdlLocation + "]."); } catch (MalformedURLException ex) { throw new IllegalArgumentException( "Specified WSDL location [" + this.wsdlLocation + "] isn't a valid URL"); } } //如果元素没有配置wsdl文件路径,则根据JNDI类型创建WebService实例 //通过JDK的反射机制,调用合适的构造方法创建实例对象 else { service = (Service) BeanUtils.instantiateClass(this.lookupType); } } //根据资源类型创建WebService提供服务的代理对象 return service.getPort(this.elementType); }
通过上面的源码分析,我们知道Spring容器在对WebSerice进行注入时,首先通过JNDI查找容器中的实例对象,如果没有找到,则根据wsdl文件实例化WebService对象,如果没有指定wsdl文件的路径,则根据类型利用JDK的反射机制生成WebService实例对象,完成注入。
c.EjbRefElement解析EJB相关的注解和属性注入:
EjbRefElement是Spring中用于解析EJB相关注解和属性注入的类,源码如下:
//解析EJB相关的注解 protected void initAnnotation(AnnotatedElement ae) { //获取元素上的@EJB注解 EJB resource = ae.getAnnotation(EJB.class); //获取@EJB注解的beanName属性值,作为资源Bean名称 String resourceBeanName = resource.beanName(); //获取@EJB注解的name属性值,作为资源名称 String resourceName = resource.name(); //根据资源名称是否为空判断元素是否有默认名称 this.isDefaultName = !StringUtils.hasLength(resourceName); //如果元素有默认名称 if (this.isDefaultName) { //获取元素对象名称作用资源名称 resourceName = this.member.getName(); //如果元素是方法,且是以set开头的方法,并且元素名称不为空 if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { //利用JDK内省机制格式化资源名称 resourceName = Introspector.decapitalize(resourceName.substring(3)); } } //获取@EJB注解中的beanInterface属性值,作用资源类型 Class resourceType = resource.beanInterface(); //如果资源类型不为空,且不是Object if (resourceType != null && !Object.class.equals(resourceType)) { //根据资源类型判断资源是字段还是方法 checkResourceType(resourceType); } //如果@EJB注解中没有指定资源类型 else { //利用JDK反射机制判断资源是字段还是方法类型 resourceType = getResourceType(); } //设置元素的Bean名称为@EJB注解中配置的resourceBeanName属性值 this.beanName = resourceBeanName; //设置元素名称为资源名称 this.name = resourceName; //设置JNDI类型为资源类型 this.lookupType = resourceType; //获取@EJB注解中的mappedName属性值,作为JDNI名称 this.mappedName = resource.mappedName(); } //属性注入 protected Object getResourceToInject(Object target, String requestingBeanName) { //如果当前Bean名称不为空 if (StringUtils.hasLength(this.beanName)) { //如果容器不为null,并且容器中存在指定名称的Bean if (beanFactory != null && beanFactory.containsBean(this.beanName)) { //直接从本地容器中获取指定名称的Bean Object bean = beanFactory.getBean(this.beanName, this.lookupType); //向容器注册Bean名称 if (beanFactory instanceof ConfigurableBeanFactory) { ((ConfigurableBeanFactory) beanFactory).registerDependentBean(this.beanName, requestingBeanName); } //返回从本地容器中获取到的Bean对象 return bean; } //如果元素有默认名称,且元素的JNDI名称为空 else if (this.isDefaultName && !StringUtils.hasLength(this.mappedName)) { throw new NoSuchBeanDefinitionException(this.beanName, "Cannot resolve 'beanName' in local BeanFactory. Consider specifying a general 'name' value instead."); } } //通过JNDI查找指定名称资源 return getResource(this, requestingBeanName); } }
从上面对@Resource、WebService和EJB相关注解解析源码的分析中,我们可以看出,Spring主要使用JDNI查找方式获取这三类注解资源,另外,由源码的“if (this.member instanceof Method&& resourceName.startsWith("set") && resourceName.length() > 3)”判断条件,我们可以看出这三类注解只能添加在字段上,或者set属性方法上,在get属性方法上添加这三类注解将是无法被解析的。
5.RequiredAnnotationBeanPostProcessor:
是Spring中用于处理@Required注解的,@Required注解强制要求Bean属性必须被配置,当Spring容器对Bean的属性进行依赖注入时,配置了@Required注解的属性,Spring容器会检查依赖关系是否设置,按照其主要功能分析其主要实现源码:
//注入属性 public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { //如果容器缓存中没有指定Bean名称 if (!this.validatedBeanNames.contains(beanName)) { //如果指定Bean定义中没有设置skipRequiredCheck属性 if (!shouldSkip(this.beanFactory, beanName)) { List
6.:
PersistenceAnnotationBeanPostProcessor是Spring中用于处理JPA相关注解的Bean后置处理器,主要解析和处理@PersistenceUnit、@PersistenceContext注解,其主要作用是为JPA的实体管理器工厂(EntityManagerFactory)和实体管理器(EntityManager)注入相应的持久化单元(PersistenceUnit)或持久化上下文(PersistenceContext)。按照其主要功能分析其主要实现源码:
(1).处理和查找持久化元信息:
//处理持久化相关属性 public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { //查找给定类中持久化元信息 InjectionMetadata metadata = findPersistenceMetadata(bean.getClass()); try { //为Bean注入持久化属性 metadata.inject(bean, beanName, pvs); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of persistence dependencies failed", ex); } return pvs; } //查找给定类中的持久化元信息 private InjectionMetadata findPersistenceMetadata(final Class clazz) { //首先从容器缓存中查找给定类的元信息 InjectionMetadata metadata = this.injectionMetadataCache.get(clazz); //容器缓存中没有给定类的元信息 if (metadata == null) { //线程同步,以确保容器中数据的一致性 synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(clazz); if (metadata == null) { //创建一个存储所有注入元信息的集合 LinkedList
(2).根据持久化单元名称获取持久化上下文、持久化单元和实体管理器工厂:
PersistenceAnnotationBeanPostProcessor最重要的核心功能就是根据持久化单元名称获取相应的持久化上下文,持久化单元或者实体管理器工厂,源码如下:
//根据持久化单元名称获取实体管理器工厂 protected EntityManagerFactory getPersistenceUnit(String unitName) { //如果持久化单元不为null if (this.persistenceUnits != null) { //获取持久化单元名称 String unitNameForLookup = (unitName != null ? unitName : ""); //如果持久化单元名称为空 if ("".equals(unitNameForLookup)) { //将默认的持久化单元名称作为持久化单元名称 unitNameForLookup = this.defaultPersistenceUnitName; } //获取持久化单元的JNDI名称 String jndiName = this.persistenceUnits.get(unitNameForLookup); if (jndiName == null && "".equals(unitNameForLookup) && this.persistenceUnits.size() == 1) { //从持久化单元中获取JNDI名称 jndiName = this.persistenceUnits.values().iterator().next(); } if (jndiName != null) { try { //查找指定JNDI名称的实体管理器工厂 return lookup(jndiName, EntityManagerFactory.class); } catch (Exception ex) { throw new IllegalStateException("Could not obtain EntityManagerFactory [" + jndiName + "] from JNDI", ex); } } } return null; } //查找给定持久化单元名称的实体管理器 protected EntityManager getPersistenceContext(String unitName, boolean extended) { //获取持久化上下文 Map
(3).:
类是Spring中用于解析持久化相关注解,并提供相应的实体管理器工厂或者实体管理器,PersistenceElement解析持久化相关注解的源码如下:
//解析持久化注解 public PersistenceElement(Member member, PropertyDescriptor pd) { super(member, pd); //获取当前元素对象 AnnotatedElement ae = (AnnotatedElement) member; //获取当前元素对象的@PersistenceContext注解 PersistenceContext pc = ae.getAnnotation(PersistenceContext.class); //获取当前元素对象的@PersistenceUnit注解 PersistenceUnit pu = ae.getAnnotation(PersistenceUnit.class); //资源类型为实体管理器 Class resourceType = EntityManager.class; //如果元素配置的持久化上下文不为null if (pc != null) { //如果元素配置的持久化单元也不为null if (pu != null) { //持久化上下文和持久化单元只能二选一,不能同时配置 throw new IllegalStateException("Member may only be annotated with either " + "@PersistenceContext or @PersistenceUnit, not both: " + member); } Properties properties = null; //获取持久化上下文的属性 PersistenceProperty[] pps = pc.properties(); //如果持久化上下文属性不为空 if (!ObjectUtils.isEmpty(pps)) { properties = new Properties(); //将持久化上下文的属性名称和属性值保存到属性集合中 for (PersistenceProperty pp : pps) { properties.setProperty(pp.name(), pp.value()); } } //设置元素的持久化单元名称 this.unitName = pc.unitName(); //设置元素的持久化类型为实体管理器(EntityManager) this.type = pc.type(); //设置元素的持久化属性 this.properties = properties; } //如果持久化上下文为null else { //持久化资源类型为实体管理器工厂 resourceType = EntityManagerFactory.class; this.unitName = pu.unitName(); } //检查给定的持久化资源类型是字段还是方法类型 checkResourceType(resourceType); }
从上面的源码我们可以看到,当配置了持久化上下文时,容器会自动选择实体管理器作用持久化资源类型,如果配置了持久化单元时,选择实体管理器工厂作为持久化资源类型。
(4).获取并注入持久化资源:
PersistenceElement类对持久化注解解析获取到基本的持久化信息之后,就可以根据注解配置的持久化信息获取并注入相应的持久化资源,源码如下:
//获取并注入持久化资源 protected Object getResourceToInject(Object target, String requestingBeanName) { //持久化资源类型不为null,创建实体管理器 if (this.type != null) { return (this.type == PersistenceContextType.EXTENDED ? resolveExtendedEntityManager(target, requestingBeanName) : resolveEntityManager(requestingBeanName)); } //持久化资源类型为null,创建实体管理器工厂 else { return resolveEntityManagerFactory(requestingBeanName); } } //创建扩展的实体管理器 private EntityManager resolveExtendedEntityManager(Object target, String requestingBeanName) { //以持久化单元名称作为JNDI名称获取持久化上下文作为实体管理器 EntityManager em = getPersistenceContext(this.unitName, true); //如果根据JNDI获取到的持久化上下文为null if (em == null) { //以持久化单元名称作为JNDI名称获取实体管理器工厂 EntityManagerFactory emf = getPersistenceUnit(this.unitName); //没有获取到指定JNDI名称的实体管理器工厂 if (emf == null) { //根据持久化单元名称向Spring容器查找指定名称的实体管理器工厂 emf = findEntityManagerFactory(this.unitName, requestingBeanName); } //根据实体管理器工厂和持久化属性创建一个容器管理的实体管理器 em = ExtendedEntityManagerCreator.createContainerManagedEntityManager(emf, this.properties); } //创建实体管理器代理委派调用的实体管理器 if (em instanceof EntityManagerProxy && beanFactory != null && !beanFactory.isPrototype(requestingBeanName)) { extendedEntityManagersToClose.put(target, ((EntityManagerProxy) em).getTargetEntityManager()); } return em; } } //创建实体管理器 private EntityManager resolveEntityManager(String requestingBeanName) { //以持久化单元名称作为JNDI名称获取持久化上下文作为实体管理器 EntityManager em = getPersistenceContext(this.unitName, false); //如果根据JNDI获取到的持久化上下文为null if (em == null) { //以持久化单元名称作为JNDI名称获取实体管理器工厂 EntityManagerFactory emf = getPersistenceUnit(this.unitName); //没有获取到指定JNDI名称的实体管理器工厂 if (emf == null) { //根据持久化单元名称向Spring容器查找指定名称的实体管理器工厂 emf = findEntityManagerFactory(this.unitName, requestingBeanName); } //创建一个共享事务管理的实体管理器代理 if (emf instanceof EntityManagerFactoryInfo && ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) { //根据JPA实现提供商类型创建相应的实体管理器 em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties); } else { //根据持久化注解配置的持久化资源类型创建实体管理器 em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties, getResourceType()); } } return em; } //创建实体管理器工厂 private EntityManagerFactory resolveEntityManagerFactory(String requestingBeanName) { //以持久化单元名称作为JNDI名称获取实体管理器工厂 EntityManagerFactory emf = getPersistenceUnit(this.unitName); //没有获取到指定JNDI名称的实体管理器工厂 if (emf == null) { //根据持久化单元名称向Spring容器查找指定名称的实体管理器工厂 emf = findEntityManagerFactory(this.unitName, requestingBeanName); } return emf; }
Spring中还有其他处理注解的Bean后置处理器,其基本的实现原理与上面分析的这4个最常用的注解Bean后置处理器类似,注解其实也没什么神秘的,和XML配置文件类似都是一种配置的方式而已,只不过利用JDK的反射机制,在编译时或者运行时动态获取所配置的信息而已,注解本身只是个标识,注解的真正意义在于通过注解标识获取注解所在对象的信息以及注解中配置的信息。
Spring的注解方式只是简化了XML配置文件,可以在读入Bean定义资源时可以动态扫描给定的路径,在解析和依赖注入时,XML方式配置的Bean,Spring需要解析XML文件,注解方式配置的Bean,Spring需要通过JDK的反射机制获取注解配置的信息。