Java反射及代理模式

反射

反射是Java語言的重要特性,它允許程序運行時進行自我檢查,也允許對內部的成員
進行操作,能夠實現在運行時對類進行裝載,使程序運行時更加靈活,但是也有注意正確使用
否則會對性能有影響。

案例1 基本的反射

父類

public class Parents {
    public void function(){
        System.out.println("I'm parents!");
    }
}

子類

public class Children extends Parents{
    @Override
    public void function() {
        System.out.println("I'm children!");;
    }
}

測試類

import one.Parents;public class TestReflection {
    @org.junit.Test
    public void testFunction() throws Exception{
        Class cls = Class.forName("one.Children");// 包名.類名
        Parents parents = (Parents)cls.newInstance();
        parents.function();
    }
}

這時候會調用子類的function方法輸出

I'm children!

我們可以寫一個父類的工廠方法,傳入子類的名稱,自動調用子類的方法,一些RPC框架就是基于這種動態(tài)代理的思想

這里我們增加一個實現類Student

public class Student extends Parents{
    @Override
    public void function() {
        System.out.println("I'm children!");;
    }
}

增加一個工廠,模擬注冊中心,調用子類的方法

public class Factory {
    private static Map<String, String> map = new HashMap<String, String>();

    static{
        map.clear();
        // 模擬注冊中心,存放實現類
        map.put("one.Children", "one.Children");
        map.put("one.Student", "one.Student");
    }

    public static void main(String[] args) {
        run("one.Student");
    }

    private static
    void run(String clzName){
        if(null==clzName || clzName.length()==0 || "".equals(clzName)){
            System.out.println("參數不能為空");
            return;
        }
        if(!map.containsKey(clzName)){
            System.out.println("參數不合法");
            return;
        }
        try{
            Class cls = Class.forName(map.get(clzName));
            Parents parents = (Parents)cls.newInstance();
            System.out.println("執(zhí)行方法"+clzName);
            parents.function();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

運行Factory會出現

執(zhí)行方法Student
I'm children!

案例2 動態(tài)代理實現反射

父類接口

public interface Parents {
    public void function();
}

子類實現

public class Children implements Parents{
    @Override
    public void function() {
        System.out.println("I'm children!");
    }
}

基本的代理類,繼承InvocationHandler接口,實現invoke方法。

public class Intermediary implements InvocationHandler {
    private Object post;
    public Intermediary(Object post) {
        this.post = post;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(post, args);
        System.out.println("代理類:打印日志");
        return invoke;
    }
}

優(yōu)雅的代理類,直接返回對象。采用內部類的方式,將反射的方法當作參數傳遞給InvocationHandler

public class GetObject {
    /**
     * 反射+動態(tài)代理的方式調用Parents里面的方法
     * @return
     */
    public static void runObject(final Parents post){
        // 調用方法時的處理器,本質都是在調用invoke()方法
        InvocationHandler h = new InvocationHandler() {
            /**
             * 調用方法的處理內容放在invoke里面
             * @param proxy 代理對象
             * @param method 調用的方法
             * @param args 傳遞的參數
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = method.invoke(post, args);
                System.out.println("代理類:打印日志");
                return invoke;
            }
        };
        // 參數:產生這個代理類的classLoader, 實現了這個代理類的接口,h
        Object o = Proxy.newProxyInstance(Parents.class.getClassLoader(), new Class[]{Parents.class}, h);
        System.out.println(o.getClass().getName());
        System.out.println(o.getClass().getInterfaces()[0]);
        Parents parents = (Parents) o;
        parents.function();
        return;
    }
}

測試下上述兩個類的效果

public class TestIntermediary {
    public static void main(String[] args) {

        // 基本的代理類
        Parents child = new Children();
        Intermediary intermediary = new Intermediary(child);
        Parents proxy = (Parents) Proxy.newProxyInstance(child.getClass().getClassLoader(), child.getClass().getInterfaces(), intermediary);
        proxy.function();

        // 優(yōu)雅的代理工具類
        GetObject.runObject(new Children());
    }
}

通過代理類可以在代理類中包裝一些方法進去,以下是運行結果

I'm children!
代理類:打印日志
com.sun.proxy.$Proxy0
interface two.Parents
I'm children!
代理類:打印日志

案例3 通過代理類設置方法攔截器

自定義一個服務

public class MyService {
    public String run(String something){
        // int i = 1/0;
        return "服務正在運行...." + something;
    }
}

定義一個其他服務

public class OtherService {
    public String runOther(String something){
        // int i = 1/0;
        return "服務正在運行...." + something;
    }
}

寫一個服務攔截器,對不是“run”的方法進行攔截

public class ServiceInterceptor implements MethodInterceptor {

    private static final String INTERCEPTOR = "run";

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object result = null;
        if(!INTERCEPTOR.equals(methodInvocation.getMethod().getName())){
            System.out.println("不能執(zhí)行該方法" + methodInvocation.getMethod().getName());
            return null;
        }
        try {
            System.out.println("方法執(zhí)行之前:" + methodInvocation.getMethod().toString());

            result = methodInvocation.proceed();
            System.out.println("方法正常運行結果:" + result);

            System.out.println("方法執(zhí)行之后:" + methodInvocation.getMethod().toString());
            return result;

        } catch (Exception e) {
            System.out.println("方法出現異常:" + e.toString());
            System.out.println("方法運行Exception結果:" + result);
            return result;
        }
    }
}
public class TestMethodInterceptor {
    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(new MyService());
        proxyFactory.addAdvice(new ServiceInterceptor());

        Object proxy = proxyFactory.getProxy();
        MyService myService = (MyService) proxy;
        myService.run("通過代理工廠設置代理對象,攔截代理方法");

        proxyFactory.setTarget(new OtherService());
        proxy = proxyFactory.getProxy();
        OtherService otherService = (OtherService) proxy;
        otherService.runOther("通過代理工廠設置代理對象,攔截代理方法");
    }
}

可以看到otherService.runOther方法被攔截了

方法執(zhí)行之前:public java.lang.String three.MyService.run(java.lang.String)
方法正常運行結果:服務正在運行....通過代理工廠設置代理對象,攔截代理方法
方法執(zhí)行之后:public java.lang.String three.MyService.run(java.lang.String)
不能執(zhí)行該方法runOther

案例4 抽象服務的接口

定義一個父類實現接口打印

public class BaseService {
    /**
     *
     * @param something
     * @return
     */
    String run(String something){
        return null;
    }
    void printLog(){
        System.out.println("統(tǒng)一打印日志");
    }
}

ServiceOne實現上述接口

public class ServiceOne extends BaseService {
    @Override
    String run(String something) {
        return "ServiceOne "+ something;
    }
}

ServiceTwo實現上述接口

public class ServiceTwo extends BaseService{

    @Override
    String run(String something) {
        return "ServiceTwo "+ something;
    }
}

借助Spring攔截器,實現反射

public class ServiceInterceptor implements MethodInterceptor {

    private static final String INTERCEPTOR = "printLog";

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object result = null;
        try {
            if(INTERCEPTOR.equals(methodInvocation.getMethod().getName())){
                //日志直接打印
                result = methodInvocation.proceed();
                return result;
            }

            System.out.println("方法執(zhí)行之前:" + methodInvocation.getMethod().toString());

            result = methodInvocation.proceed();
            System.out.println("方法正常運行結果:" + result);

            System.out.println("方法執(zhí)行之后:" + methodInvocation.getMethod().toString());
            return result;

        } catch (Exception e) {
            System.out.println("方法出現異常:" + e.toString());
            System.out.println("方法運行Exception結果:" + result);
            return result;
        }
    }
}

定義測試類,調用ServiceOne和ServiceTwo, 把二者的公共方法printLog直接放在test里面調用,反射不會對其執(zhí)行環(huán)繞方法。

public class TestMethodInterceptor {
    public static void main(String[] args) {
        ServiceOne serviceOne = (ServiceOne) test(new ServiceOne());
        serviceOne.run("通過代理工廠設置代理對象,攔截代理方法");

        ServiceTwo serviceTwo = (ServiceTwo) test(new ServiceTwo());
        serviceTwo.run("通過代理工廠設置代理對象,攔截代理方法");
    }

    public static BaseService test(BaseService baseService){
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(baseService);
        proxyFactory.addAdvice(new ServiceInterceptor());

        Object proxy = proxyFactory.getProxy();
        BaseService baseProxy = (BaseService) proxy;
        baseProxy.printLog();
        return baseProxy;
    }
}

查看執(zhí)行結果

統(tǒng)一打印日志
方法執(zhí)行之前:java.lang.String four.ServiceOne.run(java.lang.String)
方法正常運行結果:ServiceOne 通過代理工廠設置代理對象,攔截代理方法
方法執(zhí)行之后:java.lang.String four.ServiceOne.run(java.lang.String)
統(tǒng)一打印日志
方法執(zhí)行之前:java.lang.String four.ServiceTwo.run(java.lang.String)
方法正常運行結果:ServiceTwo 通過代理工廠設置代理對象,攔截代理方法
方法執(zhí)行之后:java.lang.String four.ServiceTwo.run(java.lang.String)

案例5 使用FastClass完成反射,執(zhí)行類中的所有方法

父接口與原來保持一致

public interface Parents {
    String hello(String name);
}

子類實現接口

public class Children implements Parents {

    @Override
    public String hello(String name) {
        return "I'm " + name;
    }
}

實現基本的代理類

public class Intermediary implements InvocationHandler {

    private Object post;

    public Intermediary(Object post) {
        this.post = post;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(post, args);
        System.out.println("代理類:打印日志");
        return invoke;
    }
}

調用FastClass實現反射,循環(huán)遍歷類中的方法執(zhí)行

public class GetObject {

    /**
     * 反射+動態(tài)代理的方式調用Parents里面的方法
     * @return
     */
    public static Object runObject(final Parents post, String methodName, Object[] parameters){
        try {
            Class<?> clazz = Class.forName(post.getClass().getName());
            //獲取本類的所有方法,存放入數組
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if(methodName.equals(method.getName())){
                    System.out.println("方法名:"+method.getName());
                    //獲取本方法所有參數類型,存入數組
                    Class<?>[] getTypeParameters = method.getParameterTypes();
                    if(getTypeParameters.length==0){
                        System.out.println("此方法無參數");
                    }
                    for (Class<?> class1 : getTypeParameters) {
                        String parameterName = class1.getName();
                        System.out.println("參數類型:"+parameterName);
                    }
                    FastClass fastClazz = FastClass.create(Parents.class);
                    int methodIndex = fastClazz.getIndex(method.getName(), getTypeParameters);
                    return fastClazz.invoke(methodIndex, post, parameters);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

執(zhí)行代理類

public class TestIntermediary {
    public static void main(String[] args) {

        // 基本的代理類
        Parents child = new Children();
        Intermediary intermediary = new Intermediary(child);
        Parents proxy = (Parents) Proxy.newProxyInstance(child.getClass().getClassLoader(), child.getClass().getInterfaces(), intermediary);
        System.out.println(proxy.hello("shgx"));

        // 優(yōu)雅的代理工具類
        System.out.println(GetObject.runObject(new Children(), "hello", new String[]{"shgx"}));
    }
}

執(zhí)行結果如下:

代理類:打印日志
I'm shgx
方法名:hello
參數類型:java.lang.String
I'm shgx

案例6 使用PropertyUtil和BeanUtil拷貝參數

父類接口

public interface Parents {
    void function();
}

子類-外部類

public class OuterClass implements Parents{
    private Long id;
    private String name;
    private Integer sex;
    private Double age;
    private Date birthDay;
    // 省略get set toString方法

    @Override
    public void function() {
        System.out.println("OuterClass{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex=" + sex +
                ", age=" + age +
                ", birthDay=" + birthDay +
                '}');
    }
}

子類-內部類

public class InnerClass implements Parents {
    private Long id;
    private String name;
    private Integer sex;
    private Double age;
    private Date birthDay;
    // 新增, 與上面的類形成對比
    private String address;
    // 省略get set toString方法

    @Override
    public void function() {
        System.out.println("反射實現 InnerClass{" +
                "id=" + getId() +
                ", name='" + getName() + '\'' +
                ", sex=" + getSex() +
                ", age=" + getAge() +
                ", birthDay=" + getBirthDay() +
                ", address='" + address + '\'' +
                '}');
    }
}

優(yōu)雅的代理類實現反射

public class GetObject {

    /**
     * 反射+動態(tài)代理的方式調用Parents里面的方法
     * @return
     */
    public static void runObject(final Parents post){
        // 調用方法時的處理器,本質都是在調用invoke()方法
        InvocationHandler h = new InvocationHandler() {
            /**
             * 調用方法的處理內容放在invoke里面
             * @param proxy 代理對象
             * @param method 調用的方法
             * @param args 傳遞的參數
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = method.invoke(post, args);
                return invoke;
            }
        };
        // 參數:產生這個代理類的classLoader, 實現了這個代理類的接口,h
        Object o = Proxy.newProxyInstance(Parents.class.getClassLoader(), new Class[]{Parents.class}, h);
        Parents parents = (Parents) o;
        parents.function();
    }
}

實現對象之間的參數拷貝

public class CopyUtil {

    @SuppressWarnings("unchecked")
    public static void getClassByBeanUtil(InnerClass dest, OuterClass source) {
        try {
            BeanUtils.copyProperties(dest, source);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println("BeanUtils result:" + dest);
    }

    public static void getClassByPropertyUtil(InnerClass dest, OuterClass source) {
        try {
            PropertyUtils.copyProperties(dest, source);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        System.out.println("PropertyUtils result:" + dest);
    }
}
public class TestIntermediary {
    public static void main(String[] args) {

        // 基本的代理類
        OuterClass outerClass = new OuterClass(1L, "shgx", 1, 23.5, new Date());
        // BeanUtil 完成對象拷貝
        InnerClass innerClassOne = new InnerClass();
        CopyUtil.getClassByBeanUtil(innerClassOne, outerClass);
        GetObject.runObject(innerClassOne);

        // PropertyUtil 完成對象拷貝
        InnerClass innerClassTwo = new InnerClass();
        CopyUtil.getClassByPropertyUtil(innerClassTwo, outerClass);
        GetObject.runObject(innerClassTwo);

        // 置空
        outerClass.setId(null);
        outerClass.setName(null);
        outerClass.setAge(null);
        outerClass.setSex(null);
        outerClass.setBirthDay(null);
        // BeanUtil
        CopyUtil.getClassByBeanUtil(innerClassOne, outerClass);
        GetObject.runObject(innerClassOne);

        // PropertyUtil
        CopyUtil.getClassByPropertyUtil(innerClassTwo, outerClass);
        GetObject.runObject(innerClassTwo);

    }
}

執(zhí)行結果

BeanUtils result:InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
反射實現 InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
PropertyUtils result:InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
反射實現 InnerClass{id=1, name='shgx', sex=1, age=23.5, birthDay=Sat Jul 04 18:23:51 CST 2020, address='null'}
BeanUtils result:InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
反射實現 InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
PropertyUtils result:InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}
反射實現 InnerClass{id=null, name='null', sex=null, age=null, birthDay=null, address='null'}

上述方法可以在不同系統(tǒng)之間完成參數傳輸,實現RPC調用

置空之后在執(zhí)行發(fā)現對象拷貝完成也是null,不會賦予默認值

多余的參數會賦予null值,不會拋出異常

案例7 使用注解和反射實現日志打印

使用注解的方式配置日志

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogAnnotation {
    /** 日志類型 */
    LogTypeEnum logType() default LogTypeEnum.SERVICE_LOG;

    /** 業(yè)務名 */
    String bizName() default "";

    /** 自定義日志打印*/
    Class<? extends LogInfo> customerLogType() default LogInfo.class;

    /** 是否打印日志*/
    boolean recordMonitorData() default false;
}

不在展開描述,見項目空間

源碼參考

項目代碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容