之前參與一個項目,使用的技術框架是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