Java动态代理简单应用

Java动态代理简单应用

http://www.cnblogs.com/coderworld/p/java-dynamic-proxy.html
回到顶部

概念

  代理模式是基本的设计模式之一,它是开发者为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。

  Java动态代理比代理的思想更进一步,因为它可以动态地创建代理并动态地处理对代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器(InvocationHandler)上,调用处理器的工作是揭示调用的类型并确定相应的策略。

  Java动态代理实现机制采用了反射的思想,有关于反射的基础知识,可以参见博客Java发射机制浅析。

回到顶部

原理

  Spring核心AOP实现技术之一就是采用Java动态代理机制,权限认证、日志以及事务管理都可以采用动态代理,使用动态代理机制可以减少代码重复,降低系统耦合度。Java动态代理API位于java.lang.reflect包下,实现动态代理主要涉及以下两个关键步骤:

1.实现InvocationHandler接口

InvocationHandler接口仅定义了一个方法。

1
public  Object invoke(Object proxy, Method method, Object[] args)  throws  Throwable;

proxy:代理对象

method:被代理类方法对象

args:被代理方法参数数组

proxy参数表示代理对象,以防开发者需要区分请求的来源,但是在许多情况下,开发者并不关心这一点。InvocationHandler通常需要持有被代理对象的引用,在动态代理上所做的所有调用都会被重定向到InvocationHandler接口的invoke方法上。在invoke方法内部调用被代理对象的method方法,开发者可以在方法调用前后加上需要的逻辑。如果只是想对某些方法进行代理,开发者可以在invoke方法内添加相应的过滤条件,通过method参数可以获取方法本身的很多信息。

2.通过Proxy创建动态代理类实例

Proxy是所有动态代理类的父类,并提供一个静态方法newProxyInstance负责创建动态代理类的实例。 

1
2
public  static  Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
      throws  IllegalArgumentException

loader:类加载器(通常可以从已经被加载的对象中获取其类加载器)

interfaces:代理类需要实现的接口列表(通常使用被代理类实现的接口列表)

hInvocationHandler接口的一个实现

newProxyInstance方法会根据传入的接口列表interfaces在虚拟机中动态的生成一个Proxy的子类,并实现了传入的接口列表interfaces的所有方法,然后返回该类的一个实例。因此返回的对象能够转型到传入的任意一个接口,通过该对象对接口内所有方法的调用都会重定向到第三个参数hinvoke方法。

Java动态代理底层原理实现比较复杂。总而言之,是根据newProxyInstance方法传入的类加载器和接口,生成代理类的二进制字节码,再利用反射机制创建代理类的实例,在代理类的内部对被代理类方法的调用都会重定向到InvocationHandler中的invoke方法,在invoke方法内再对被代理方法的调用进行封装预处理,加上开发者自己的逻辑。

回到顶部

应用

下面演示两个简单的示例:方法性能监测和日志管理

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  class  User {
     
     private  String name;
 
     public  User(String name) {
         super ();
         this .name = name;
     }
 
     public  String getName() {
         return  name;
     }
 
     public  void  setName(String name) {
         this .name = name;
     }
     
}

 接口

1
2
3
4
5
6
7
8
9
public  interface  UserDao {
     
     void  addUser(User user);
     
     void  deleteUser(String name);
     
     void  updateUser(String name);
 
}

 实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public  class  UserDaoImpl  implements  UserDao {
 
     @Override
     public  void  addUser(User user) {
         System.out.println( "add user named "  + user.getName() +  "..." );
         try  {
             Thread.sleep( 1000 );
         catch  (InterruptedException e) {
             e.printStackTrace();
         }
     }
 
     @Override
     public  void  deleteUser(String name) {
         System.out.println( "delete user named "  + name +  "..." );
         try  {
             Thread.sleep( 1500 );
         catch  (InterruptedException e) {
             e.printStackTrace();
         }
     }
 
     @Override
     public  void  updateUser(String name) {
         System.out.println( "update user named "  + name +  "..." );
         try  {
             Thread.sleep( 2000 );
         catch  (InterruptedException e) {
             e.printStackTrace();
         }
     }
 
}

方法性能监测

监测方法运行性能效率,此处简单以方法运行时间作为评价标准。传统的做法是在每个方法执行前后各自记录时间,然后计算运行时间。采用动态代理机制,可以将以上操作统一到InvocationHandler中,并创建代理类监测所有的方法。

InvocationHandler实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import  java.lang.reflect.InvocationHandler;
import  java.lang.reflect.Method;
 
public  class  PerformanceInterceptor  implements  InvocationHandler {
     
     private  Object proxied;
     
     public  PerformanceInterceptor(Object proxied) {
         this .proxied = proxied;
     }
 
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)  throws  Throwable {
         long  startTime = System.currentTimeMillis();
         Object obj = method.invoke(proxied, args);
         long  endTime = System.currentTimeMillis();
         System.out.println( "Method "  + method.getName() +  " execution time: "  + (endTime - startTime) *  1.0  1000  "s" );
         return  obj;
     }
 
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import  java.lang.reflect.InvocationHandler;
import  java.lang.reflect.Proxy;
 
import  org.junit.Test;
 
public  class  PerformanceInterceptorTest {
     
     @Test
     public  void  testInvoke() {
         UserDao userDao =  new  UserDaoImpl();
         Class<?> cls = userDao.getClass();
         InvocationHandler handler =  new  PerformanceInterceptor(userDao);
         UserDao proxy = (UserDao) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);
         proxy.addUser( new  User( "tom" ));
         proxy.deleteUser( "tom" );
         proxy.updateUser( "tom" );
     }
     
}

测试结果

1
2
3
4
5
6
add user named Tom...
Method addUser execution time:  1 .0s
delete user named tom...
Method deleteUser execution time:  1 .5s
update user named tom...
Method updateUser execution time:  2 .0s

日志管理

有时候开发者需要在方法开始执行和执行结束时打印一些信息,使用动态代理就可以对所有方法或者某些方法进行简单日志管理,并写入到指定文件。

InvocationHandler实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import  java.io.File;
import  java.io.FileWriter;
import  java.io.IOException;
import  java.lang.reflect.InvocationHandler;
import  java.lang.reflect.Method;
import  java.text.DateFormat;
import  java.text.SimpleDateFormat;
 
public  class  LogInterceptor  implements  InvocationHandler {
     
     private  Object proxied;
     
     public  static  final  String path =  "run.log" ;
     
     public  LogInterceptor(Object proxied) {
         this .proxied = proxied;
     }
     
     public  String beforeMethod(Method method) {
         return  getFormatedTime() +  " Method:"  + method.getName() +  " start running\r\n" ;
     }
     
     public  String afterMethod(Method method) {
         return  getFormatedTime() +  " Method:"  + method.getName() +  " end running\r\n" ;
     }
 
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)  throws  Throwable {
         write(path, beforeMethod(method));
         Object object = method.invoke(proxied, args);
         write(path, afterMethod(method));
         return  object;
     }
     
     public  String getFormatedTime() {
         DateFormat formater =  new  SimpleDateFormat( "yyyy-MM-dd hh:mm:ss" );
         return  formater.format(System.currentTimeMillis());
     }
     
     public  void  write(String path, String content) {
         FileWriter writer =  null ;
         try  {
             writer =  new  FileWriter( new  File(path),  true );
             writer.write(content);
             writer.flush();
         catch  (IOException e) {
             e.printStackTrace();
         finally  {
             if ( null  != writer) {
                 try  {
                     writer.close();
                 catch  (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     }
 
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import  java.lang.reflect.InvocationHandler;
import  java.lang.reflect.Proxy;
 
import  org.junit.Test;
 
public  class  LogInterceptorTest {
     
     @Test
     public  void  testInvoke() {
         UserDao userDao =  new  UserDaoImpl();
         Class<?> cls = userDao.getClass();
         InvocationHandler handler =  new  LogInterceptor(userDao);
         UserDao proxy = (UserDao) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);
         proxy.addUser( new  User( "tom" ));
         proxy.deleteUser( "tom" );
         proxy.updateUser( "tom" );
     }
     
}

测试结果

在工程根目录下生成run.log文件,内容如下:

1
2
3
4
5
6
2015 - 10 - 07  05 : 41 : 02  Method:addUser start running
2015 - 10 - 07  05 : 41 : 03  Method:addUser end running
2015 - 10 - 07  05 : 41 : 03  Method:deleteUser start running
2015 - 10 - 07  05 : 41 : 05  Method:deleteUser end running
2015 - 10 - 07  05 : 41 : 05  Method:updateUser start running
2015 - 10 - 07  05 : 41 : 07  Method:updateUser end running
回到顶部

总结

  尽管Java动态代理机制设计的已经非常出色,美中不足之处的是,Java动态代理只能代理实现接口的类。若想要对没有实现任何接口的类进行代理,这种情况确实罕见,就需要使用cdlibJava动态代理机制是实现AOP编程的重要技术之一,动态代理并非开发者日常使用的工具,但是在某些特殊的场景,使用动态代理可以非常好地解决某些类型的问题。

你可能感兴趣的:(android)