欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java內(nèi)省之Introspector解讀

 更新時間:2022年11月18日 09:51:11   作者:穿白襯衣的少年  
這篇文章主要介紹了Java內(nèi)省之Introspector解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Java內(nèi)省之Introspector

在JavaBean規(guī)范中有如下描述:

大意是java默認(rèn)情況下jdk使用低級的反射機制來分析Bean,為了方便其他人分析bean,java提供了一個內(nèi)省類Introspector,使用Introspector的getBeanInfo方法可以獲取一個封裝了bean信息(包括屬性和方法)的BeanInfo對象。

Introspector使用不當(dāng)導(dǎo)致內(nèi)存泄露的風(fēng)險

框架幾乎都使用了Introspector類來實現(xiàn)靈活性,但是Introspector在獲取beanInfo對象時,為了提高性能使用了緩存保存beanInfo:

public static BeanInfo getBeanInfo(Class<?> beanClass)
? ? ? ? throws IntrospectionException
? ? {
? ? ? ? if (!ReflectUtil.isPackageAccessible(beanClass)) {
? ? ? ? ? ? return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
? ? ? ? }
? ? ? ? ThreadGroupContext context = ThreadGroupContext.getContext();
? ? ? ? BeanInfo beanInfo;
? ? ? ? synchronized (declaredMethodCache) {
? ? ? ? ? ? beanInfo = context.getBeanInfo(beanClass);
? ? ? ? }
? ? ? ? if (beanInfo == null) {
? ? ? ? ? ? beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();
? ? ? ? ? ? synchronized (declaredMethodCache) {
? ? ? ? ? ? ? ? context.putBeanInfo(beanClass, beanInfo);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return beanInfo;
? ? }

緩存使用ThreadGroupContext——線程組級別共享,類似與ThreadLocal。

內(nèi)部使用WeakHashMap——key為弱引用來保存beanInfo,其中使用class作為key,beanInfo作為value。

同時使用WeakIdentityMap保存ThreadGroupContext對象(應(yīng)該是ThreadGroupContext對象的hash值)與WeakHashMap的映射關(guān)系,也就是說不同線程組相互隔離:

final class ThreadGroupContext {
//WeakIdentityMap 判斷key是否重復(fù)只判斷hash是否相等,不調(diào)用equals
? ? private static final WeakIdentityMap<ThreadGroupContext> contexts = new WeakIdentityMap<ThreadGroupContext>() {
? ? ? ? protected ThreadGroupContext create(Object key) {
? ? ? ? ? ? return new ThreadGroupContext();
? ? ? ? }
? ? };

? ? /**
? ? ?* Returns the appropriate {@code ThreadGroupContext} for the caller,
? ? ?* as determined by its {@code ThreadGroup}.
? ? ?*
? ? ?* @return ?the application-dependent context
? ? ?*/
? ? static ThreadGroupContext getContext() {
? ? ? ? return contexts.get(Thread.currentThread().getThreadGroup());
? ? }
。
。
。
}

但是beanInfo中持有class對象,因此WeakHashMap的弱引用失效,Introspector提供了清除緩存的方法flushCaches。

但有些框架在使用Introspector之后并沒有清除緩存

在spring中有如下描述:

/**
?* Listener that flushes the JDK's {@link java.beans.Introspector JavaBeans Introspector}
?* cache on web app shutdown. Register this listener in your {@code web.xml} to
?* guarantee proper release of the web application class loader and its loaded classes.
?*
?* <p><b>If the JavaBeans Introspector has been used to analyze application classes,
?* the system-level Introspector cache will hold a hard reference to those classes.
?* Consequently, those classes and the web application class loader will not be
?* garbage-collected on web app shutdown!</b> This listener performs proper cleanup,
?* to allow for garbage collection to take effect.
?*
?* <p>Unfortunately, the only way to clean up the Introspector is to flush
?* the entire cache, as there is no way to specifically determine the
?* application's classes referenced there. This will remove cached
?* introspection results for all other applications in the server too.
?*
?* <p>Note that this listener is <i>not</i> necessary when using Spring's beans
?* infrastructure within the application, as Spring's own introspection results
?* cache will immediately flush an analyzed class from the JavaBeans Introspector
?* cache and only hold a cache within the application's own ClassLoader.
?*
?* <b>Although Spring itself does not create JDK Introspector leaks, note that this
?* listener should nevertheless be used in scenarios where the Spring framework classes
?* themselves reside in a 'common' ClassLoader (such as the system ClassLoader).</b>
?* In such a scenario, this listener will properly clean up Spring's introspection cache.
?*
?* <p>Application classes hardly ever need to use the JavaBeans Introspector
?* directly, so are normally not the cause of Introspector resource leaks.
?* Rather, many libraries and frameworks do not clean up the Introspector:
?* e.g. Struts and Quartz.
?*
?* <p>Note that a single such Introspector leak will cause the entire web
?* app class loader to not get garbage collected! This has the consequence that
?* you will see all the application's static class resources (like singletons)
?* around after web app shutdown, which is not the fault of those classes!
?*
?* <p><b>This listener should be registered as the first one in {@code web.xml},
?* before any application listeners such as Spring's ContextLoaderListener.</b>
?* This allows the listener to take full effect at the right time of the lifecycle.

大意是在web應(yīng)用中使用Introspector分析bean,當(dāng)web應(yīng)用停止時(這里應(yīng)該指的是正常銷毀,而非殺死進(jìn)程暴力銷毀),由于Introspector持有被分析bean的強引用,導(dǎo)致bean以及加載bean的classload無法被gc,造成內(nèi)存泄露。

個人猜想,如果web應(yīng)用停止之后,main方法運行結(jié)束,jvm退出應(yīng)該不存在內(nèi)存泄露的情況。但是,當(dāng)web服務(wù)銷毀之后main方法還在執(zhí)行,那么就出現(xiàn)內(nèi)存泄露。例如在一個tomcat中部署多個應(yīng)用,在tomcat的manager App 頁面關(guān)閉應(yīng)用就會導(dǎo)致內(nèi)存泄露。

大部分框架在創(chuàng)建線程池的時候都繼承parentThreadGroup,因此即使使用WeakIdentityMap保存ThreadGroup對象的軟引用與WeakHashMap的映射關(guān)系,但其他未關(guān)閉的web應(yīng)用仍然持有ThreadGroup的強引用,因此WeakIdentityMap中的beanInfo緩存不會被回收——內(nèi)存泄露。

IntrospectorCleanupListener

為了解決其他框架如:

Struts和Quartz(大部分博客均指出這兩個框架使用Introspector后沒有flushCaches,但我沒有考證),一心為我們考慮的spring提供了解決方案 ——IntrospectorCleanupListener:

public class IntrospectorCleanupListener implements ServletContextListener {

? ? @Override
? ? public void contextInitialized(ServletContextEvent event) {
? ? ? ? CachedIntrospectionResults.acceptClassLoader(Thread.currentThread().getContextClassLoader());
? ? }

? ? @Override
? ? public void contextDestroyed(ServletContextEvent event) {
? ? ? ? CachedIntrospectionResults.clearClassLoader(Thread.currentThread().getContextClassLoader());
? ? ? ? Introspector.flushCaches();
? ? }
}

IntrospectorCleanupListener是servletContext的監(jiān)聽器,在servletContext銷毀時,會執(zhí)行contextDestroyed方法,調(diào)用Introspector.flushCaches(),防止內(nèi)存泄露。

spring同時說明spring框架沒有使用Introspector的緩存,而是使用Introspector分析bean之后,隨即清理了Introspector緩存,并使用自己的緩存邏輯進(jìn)行緩存,應(yīng)該就是

CachedIntrospectionResults.acceptClassLoader(Thread.currentThread().getContextClassLoader())

這行代碼實現(xiàn)——未考證,因此spring聲明在只使用spring框架時不需要考慮introspector導(dǎo)致內(nèi)存泄露的問題。

但個人認(rèn)為,如果某個框架在創(chuàng)建自己的線程池時,傳入了新的ThreadGroup對象,那么IntrospectorCleanupListener 可能也無法工作。

Java內(nèi)省Introspector應(yīng)用

IntroSpecor介紹

內(nèi)省(IntroSpector)是Java語言對JavaBean 類屬性、事件的一種缺省處理方法。

例如類A中有屬性name, 那我們可以通過getName,setName 來得到其值或者設(shè)置新的值。

通過getName/setName 來訪問name屬性,這就是默認(rèn)的規(guī)則。

Java中提供了一套API 用來訪問某個屬性的getter/setter方法,通過這些API 可以使你不需要了解這個規(guī)則,這些API存放于包java.beans 中。

Class Diagram

一般的做法是通過類Introspector的getBeanInfo方法獲取某個對象的BeanInfo 信息,然后通過BeanInfo來獲取屬性的描述器(PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應(yīng)的getter/setter方法,然后我們就可以通過反射機制來調(diào)用這些方法。

我們又通常把javabean的實例對象稱之為值對象(Value Object),因為這些bean中通常只有一些信息字段和存儲方法,沒有功能性方法。

一個JavaBean類可以不當(dāng)JavaBean用,而當(dāng)成普通類用。JavaBean實際就是一種規(guī)范,當(dāng)一個類滿足這個規(guī)范,這個類就能被其它特定的類調(diào)用。一個類被當(dāng)作javaBean使用時,JavaBean的屬性是根據(jù)方法名推斷出來的,它根本看不到j(luò)ava類內(nèi)部的成員變量。去掉set前綴,然后取剩余部分,如果剩余部分的第二個字母是小寫的,則把剩余部分的首字母改成小的。

 

除了反射用到的類需要引入外,內(nèi)省需要引入的類如下所示,它們都屬于java.beans包中的類,自己寫程序的時候也不能忘了引入相應(yīng)的包或者類。

簡單示例1

下面代碼片斷是設(shè)置某個JavaBean類某個屬性的關(guān)鍵代碼:

package com.jasun.test;
 
 import java.beans.BeanInfo;
 import java.beans.IntrospectionException;
 import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
 import org.apache.commons.beanutils.BeanUtils;
 
 
 publicclass IntrospectorTest {
 
publi static void main(String[] args) throws IllegalArgumentException, 
        IntrospectionException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        
        UserInfo userInfo=new UserInfo("zhangsan", "123456");
        String propertyName="userName";
        Object retVal=getProperty(userInfo, propertyName);
        System.out.println("retVal="+retVal); //retVal=zhangsan
         
        Object value="abc";
        setProperty(userInfo, propertyName, value);
        retVal=getProperty(userInfo, propertyName);
        System.out.println("retVal="+retVal); //retVal=abc
 
           //使用BeanUtils工具包操作JavaBean
        String userName=BeanUtils.getProperty(userInfo, propertyName);
        System.out.println("userName="+userName);
        BeanUtils.setProperty(userInfo, propertyName, "linjiqin");
        userName=BeanUtils.getProperty(userInfo, propertyName);
        System.out.println("userName="+userName);
    }
    
/**
     * 設(shè)置屬性
     * 
     * @param clazz 對象名
     * @param propertyName 屬性名
     * @param value 屬性值
*/
private static void setProperty(Object clazz, String propertyName, Object value) 
throws IntrospectionException,IllegalAccessException, InvocationTargetException{
//方法一
 /*PropertyDescriptor pd=new PropertyDescriptor(propertyName, clazz.getClass());
        Method methodSet=pd.getWriteMethod();
        methodSet.invoke(clazz, value);*/
        
//方法二
         BeanInfo beanInfo=Introspector.getBeanInfo(clazz.getClass());
        PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
for(PropertyDescriptor pd:pds){
if(propertyName.equals(pd.getName())){
                Method methodSet=pd.getWriteMethod();
                methodSet.invoke(clazz, value);
break;
            }
        }
    }
    
/**
     * 獲取屬性
     * 
     * @param clazz 對象名
     * @param propertyName 屬性名
     * @return
     * @throws IntrospectionException 
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
*/
private static Object getProperty(Object clazz, String propertyName) 
throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
//方法一
 /*PropertyDescriptor pd=new PropertyDescriptor(propertyName, clazz.getClass());
        Method methodGet=pd.getReadMethod();
        return methodGet.invoke(clazz);*/
        
//方法二
         Object retVal=null;
        BeanInfo beanInfo=Introspector.getBeanInfo(clazz.getClass());
        PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
for(PropertyDescriptor pd:pds){
if(propertyName.equals(pd.getName())){
                Method methodGet=pd.getReadMethod();
                retVal=methodGet.invoke(clazz);
break;
            }
        }
return retVal;
    }
    
}

UserInfo類

package com.ljq.test;
 
publicclass UserInfo {
private String userName;
private String pwd;
 
public UserInfo(String userName, String pwd) {
    super();
    this.userName = userName;
   this.pwd = pwd;
    }
 
public String getUserName() {
    return userName;
    }
 
publicvoid setUserName(String userName) {
    this.userName = userName;
    }
 
public String getPwd() {
    return pwd;
    }
 
publicvoid setPwd(String pwd) {
    this.pwd = pwd;
    }
 
}

簡單示例2::(僅作參考)

package com.siyuan.jdktest;
 
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
 
class Person {
 
 private String name;
 
 private int age;
 
 /**
  * @return the age
  */
 public int getAge() {
  return age;
 }
 
 /**
  * @param age the age to set
  */
 public void setAge(int age) {
  this.age = age;
 }
 
 /**
  * @return the name
  */
 public String getName() {
  return name;
 }
 
 /**
  * @param name the name to set
  */
 public void setName(String name) {
  this.name = name;
 }
 
}
 
public class IntrospectorTest {
 
 /**
  * @param args
  * @throws IntrospectionException
  */
 public static void main(String[] args) throws IntrospectionException {
  // TODO Auto-generated method stub
  BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
  
  System.out.println("BeanDescriptor===========================================");
  BeanDescriptor beanDesc = beanInfo.getBeanDescriptor();
  Class cls = beanDesc.getBeanClass();
  System.out.println(cls.getName());
  
  System.out.println("MethodDescriptor===========================================");
  MethodDescriptor[] methodDescs = beanInfo.getMethodDescriptors();
  for (int i = 0; i < methodDescs.length; i++) {
   Method method = methodDescs[i].getMethod();
   System.out.println(method.getName());
  }
  
  System.out.println("PropertyDescriptor===========================================");
  PropertyDescriptor[] propDescs = beanInfo.getPropertyDescriptors();
  for (int i = 0; i < propDescs.length; i++) {
   Method methodR = propDescs[i].getReadMethod();
   if (methodR != null) {
    System.out.println(methodR.getName());
   }
   Method methodW = propDescs[i].getWriteMethod();
   if (methodW != null) {
    System.out.println(methodW.getName());
   }
  }
 }
 
}

運行結(jié)果

BeanDescriptor===========================================
com.siyuan.jdktest.Person
MethodDescriptor===========================================
hashCode
setAge
equals
wait
wait
notify
getClass
toString
getAge
notifyAll
setName
wait
getName
PropertyDescriptor===========================================
getAge
setAge
getClass
getName
setName

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring的CorsFilter會失效的原因及解決方法

    Spring的CorsFilter會失效的原因及解決方法

    眾所周知CorsFilter是Spring提供的跨域過濾器,我們可能會做以下的配置,基本上就是允許任何跨域請求,我利用Spring的CorsFilter做跨域操作但是出現(xiàn)報錯,接下來小編就給大家介紹一Spring的CorsFilter會失效的原因及解決方法,需要的朋友可以參考下
    2023-09-09
  • 解決springboot?druid數(shù)據(jù)庫連接池連接失敗后一直重連問題

    解決springboot?druid數(shù)據(jù)庫連接池連接失敗后一直重連問題

    這篇文章主要介紹了解決springboot?druid數(shù)據(jù)庫連接池連接失敗后一直重連問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Jedis操作Redis數(shù)據(jù)庫的方法

    Jedis操作Redis數(shù)據(jù)庫的方法

    這篇文章主要為大家詳細(xì)介紹了Jedis操作Redis數(shù)據(jù)庫的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • 詳解springMVC容器加載源碼分析

    詳解springMVC容器加載源碼分析

    這篇文章主要介紹了詳解springMVC容器加載源碼分析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • SpringBoot集成Redisson實現(xiàn)延遲隊列的場景分析

    SpringBoot集成Redisson實現(xiàn)延遲隊列的場景分析

    這篇文章主要介紹了SpringBoot集成Redisson實現(xiàn)延遲隊列,本文通過場景分析實例代碼相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • 如何用idea編寫并運行第一個spark scala處理程序

    如何用idea編寫并運行第一個spark scala處理程序

    詳細(xì)介紹了如何使用IntelliJ IDEA創(chuàng)建Scala項目,包括配置JDK和Scala SDK,添加Maven支持,編輯pom.xml,并創(chuàng)建及運行Scala程序,這為Scala初學(xué)者提供了一個基礎(chǔ)的項目搭建和運行指南
    2024-09-09
  • SpringCloud Gateway的路由,過濾器和限流解讀

    SpringCloud Gateway的路由,過濾器和限流解讀

    這篇文章主要介紹了SpringCloud Gateway的路由,過濾器和限流解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Java double類型比較大小詳解

    Java double類型比較大小詳解

    這篇文章主要介紹了Java double類型比較大小,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • spring中的特殊注解@RequiredArgsConstructor詳解

    spring中的特殊注解@RequiredArgsConstructor詳解

    這篇文章主要介紹了spring中的特殊注解@RequiredArgsConstructor,包括注解注入,構(gòu)造器注入及setter注入,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • Mybatis generator自動生成代碼插件實例解析

    Mybatis generator自動生成代碼插件實例解析

    這篇文章主要介紹了Mybatis generator自動生成代碼插件實例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09

最新評論