反射和动态代理【原】

       之前参与一个项目,使用的技术框架是struts2+ibatis,业余好奇探索了下,于是有幸接触到java的反射和动态代理。我知道在struts2的拦截器中使用了反射和动态代理,据说很多经典的框架,比如spring、hibernate、ibatis等也都大范围使用了。这两种技术大概意思如下:

反射:在程序运行的时候,动态的获取某个类中的属性和方法,并且能够调用(很多框架能自动识别你写的类,然后调用一些共同的方法,靠的就是它)。

代理:给类包装上一层壳,通过这个壳去操作这个类,使得你在操作这个类之前之后可以做一些你想做的事情。

 

反射介绍

反射比较简单,主要使用Class类,Class类提供了运行时获取或调用某个类具体内容的方法。如下代码作实例:

待调用的类MyClass:

public class MyClass {
    private int myInt;
    private String myString;

    public MyClass(){

    }

    public MyClass(int a){
        this.myInt = a;
    }

    public void Method2Void(){

    }

    public int Method2Int(){
        System.out.println("Method2Int has run");
        return 0;
    }

    public String Method2String(){
        return "";
    }

    public Object Method2Object() {
        return new Date();
    }

    public void Method3Param(int a, String b){
        System.out.println("Method3Param has run with param-a:" + a + " and param-b:" + b);
    }
}

Main函数
    public static void main(String[] args){
        MyClass myClass = new MyClass();
        Class cls = myClass.getClass();//获取一个类的Class

        System.out.println("1:" + cls.getName());//名字
        System.out.println("2:" + cls.getSimpleName());
        System.out.println("3:" + cls.getPackage());
        try{
            Method m = cls.getMethod("Method2Int", new Class[]{});//获取一个类的方法
            m.invoke(myClass, new Object[]{});//精髓所在,调用这个类的方法

            Method m2 = cls.getMethod("Method3Param", new Class[]{int.class, String.class});
            m2.invoke(myClass, new Object[]{5, "fake"});//调用这个类的方法,带参数的
        }catch(Exception e){
            e.printStackTrace();
        }
        Method[] ms = cls.getMethods();

        for(Method m:ms){
            System.out.println(m.getName());
        }


        try{
            Class cls2 = Class.forName("MyClass");//这种方法也能获取一个类的Class,同时能动态载入这个类
        }catch(Exception e){
            e.printStackTrace();
        }
    }


下面详细介绍下代理

      代理这种现象在生活中是非常常见的,比如你要买火车票,可以让你的朋友帮你买,也可以托代售点帮你买。你把钱给了他们,他们可能会做任何事情。也许你朋友忘记;或者代售点把你黑了。当然,程序是你的,你可以控制他们的行为。

      代理有普通代理和动态代理。上面说的你的朋友,可以看成是普通代理,代售点可以看成是动态代理。区别在于,你的朋友并不会帮每个人都买票,仅仅帮他认识少部分人买,而代售点是来者不拒的,具有通用性。

      使用代理模式,需要区分真实对象和代理对象。代理对象中含有真实对象的引用,可以对真实对象进行操作,调用真实对象的方法。可以通过调用代理对象的方法,间接的去调用真实对象的方法。

下面还是举例来说说这两种代理吧。

以下是这两种代理所需的类:

行为PersonAct.java

package proxy;

public interface PersonAct {
    void buyTicket();//买票
    void checkProperty();//查看财产
}

抽象类Person.java

import java.util.Map.Entry;

public abstract class Person implements PersonAct{
    //全部家当放在这个HashMap里
    HashMap<String, Object> property = new HashMap<String, Object>();
    
    String name;
    
    public Person(String name, int money){
        this.name= name;
        this.property.put("money", money);
    }
    
    //打印目前家当
    public void checkProperty(){
        System.out.println(this.name + "的家当如下:");
        Iterator<Entry<String, Object>> iter = property.entrySet().iterator();
        while(iter.hasNext()){
            Entry<String, Object> entry = (Entry<String, Object>)iter.next();
            System.out.println(entry.getKey() + "---" + entry.getValue());
        }        
    }           
}

真实类Boy.java

package proxy;

public class Boy extends Person{
    
    public Boy(String name, int money){
        super(name, money);
    }
    
    //买票
    @Override
    public void buyTicket() {
        int money = (Integer)property.get("money");
        
        property.put("money", money - 38);//扣钱
        property.put("ticket", "北京到上海机票");//拿票        
    }
}

真实类Girl.java

package proxy;

public class Girl extends Person{
    
    public Girl(String name, int money){
        super(name, money);
    }    
    
    @Override
    public void buyTicket() {
        int money = (Integer)property.get("money");
        
        property.put("money", money - 46);//扣钱
        property.put("ticket", "北京到深圳机票");//拿票    
    }        
}

上面的类Boy和Girl代表现实生活中的两个人,Boy自己去买票的过程如下。

        Boy boy = new Boy("西门庆", 100);        
        boy.checkProperty();
        boy.buyTicket();
        boy.checkProperty();

结果将会打印出boy购票前和投票后的家当。

1,普通代理

类FatherOfBoy.java

package proxy;

public class FatherOfBoy {
    private Boy boy = new Boy("西门庆", 100);
    
    public void buyTicket(){
        boy.buyTicket();
    }
    
    public void checkProperty(){
        boy.checkProperty();
    }
}

概念很好理解,普通代理买票可以这样测试。

        FatherOfBoy father = new FatherOfBoy();
        father.checkProperty();
        father.buyTicket();
        father.checkProperty();

2,  动态代理

动态代理主要通过继承InvocationHandler接口来实现。

ProxyAgency.java如下:

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxyAgency implements InvocationHandler{
    private Person target;//真实对象的引用
    
    public void setTarget(Person target) {
        this.target = target;
    }

    public ProxyAgency(){
        
    }
    
    public ProxyAgency(Person obj){
        this.setTarget(obj);
    }
    
    //obj,这个是代理对象
    //method,具体调用的方法
    //args,调用方法的时候传进来的参数,一般都直接传给真实对象即可
    @Override
    public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
        //target,真实对象,收取手续费10元
        if(method.getName().equalsIgnoreCase("buyTicket")){
            int money = Integer.parseInt(target.property.get("money").toString());
            target.property.put("money", money - 10);
        }
        //target,真实对象,下面一句属于java反射机制的东西,用反射机制获取真实对象的方法        
        return method.invoke(target, args);//调用真实对象的方法
    }
}

调用实例:


        //代理对象构建器
        ProxyAgency proxyAgency = new ProxyAgency();
        
        //真实对象
        Boy b = new Boy("西门庆", 100);
        Girl g = new Girl("潘金莲", 200);        
        
        //代理对象,获取方法1
        PersonAct p1 = (PersonAct) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{ PersonAct.class }, proxyAgency);
        
        //真实对象注入构建器,也可以在构建器的构造函数中注入
        proxyAgency.setTarget(b);        
        
        
        //代理对象获取方法2
//        Class<?> cls = g.getClass();
//        PersonAct p1 = (PersonAct) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), proxyAgency);
        
        p1.checkProperty();
        p1.buyTicket();
        p1.checkProperty();
        
        proxyAgency.setTarget(g);
        
        p1.checkProperty();
        p1.buyTicket();
        p1.checkProperty();

输出如下:

西门庆的家当如下:
money---100
西门庆的家当如下:
ticket---北京到上海机票
money---52
潘金莲的家当如下:
money---200
潘金莲的家当如下:
ticket---北京到深圳机票
money---144




  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值