Java技术整理1---反射机制及动态代理详解

1.反射是指在程序运行过程中动态获取类的相关信息,包括类是通过哪个加载器进行加载,类的方法和成员变量、构造方法等。

如下示例可以通过三种方法根据类的实例来获取该类的相关信息

 1 public static void getClassTest(User user) throws ClassNotFoundException{
 2         //方法一:
 3         Class c1 = user.getClass();
 4         //方法二:
 5         Class c2 = User.class;
 6         //方法三:
 7         Class c3 = Class.forName("com.luck.codehelp.entity.User");
 8         System.out.println(c1==c2);//结果为true
 9         System.out.println(c1==c3);//结果为true
10     }

这里虽然c1、c2和c3是三个不同的对象,但是都是指向User类的Class对象,而每个User对象的实例的Class对象都是同一个,存在JVM的共享的方法区,所以c1=c2=c3=User.Class

这里通过类的实例来获取类的Class对象的过程就叫做反射;

2.代理是指为某个对象提供一个代理来控制这个对象的访问,以代买火车票为例,定义一个买车票的接口BuyTicketService

personA需要买票,但是想让personB给他代买一下,B先打车去车站,最终购票成功了,然后打车回来,车票上的信息还是personA的信息,在这里

personA是被代理角色,也就是业务逻辑的具体执行者;personB是代理角色,把抽象类或接口定义的方法限制委托给真实主题角色实现(买车票),并在主题角色处理完毕前后做预处理(打车去车站)和善后处理(打车回来)

代码示例如下:

定义一个买票的接口BuyTicketService

1 package com.luck.codehelp.proxy;
2 
3 //定义一个serivce
4 public interface BuyTicketService {
5     // 买票的接口
6     public void buyTicket();
7 }

PersonA实现买票接口

1 public class PersonAServiceImpl implements BuyTicketService {
2 
3     @Override
4     public void buyTicket() {
5         System.out.println("我是personA,我买到车票啦");
6     }
7 
8 }

PersonB实现买票接口

 1 public class PersonBServiceImpl implements BuyTicketService {
 2 
 3     @Override
 4     public void buyTicket() {
 5         System.out.println("我是personB,我是帮personA去买票的");
 6         System.out.println("打车去车站");
 7         new PersonAServiceImpl().buyTicket();
 8         System.out.println("打车回去");
 9     }
10 
11 }

Main方法测试代码如下:

1 public static void main(String[] args) throws ClassNotFoundException{
2         //新建personB的实例调用方法
3         buyTicket(new PersonBServiceImpl());
4     }
5     
6     //定义买票方法,参数是买票的接口BuyTicketService
7     public static void buyTicket(BuyTicketService person){
8         person.buyTicket();
9     }

结果为:

1 我是personB,我是帮personA去买票的
2 打车去车站
3 我是personA,我买到车票啦
4 打车回去

这是一个最简单的静态代理模式,相当于定义一个接口,代理者和被代理者都实现了该接口,代理者除了实现自身的业务逻辑之外,还将被代理者的实现也一并完成了。

接下来再看看动态代理模式。

动态代理需要调用:java.lang.reflect.Proxy的newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法

这个是Proxy类的静态方法,方法的三个参数为

ClassLoader loader:类加载器,目标对象类的类加载器

Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型

InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

而一般使用动态代理的时候会为需要代理的目标对象创建一个代理工厂来为其代理,示例如下:

 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Method;
 3 import java.lang.reflect.Proxy;
 4 
 5 /**
 6  * 代理工厂
 7  */
 8 public class ProxyFactory {
 9 
10     /**
11      * 需要被代理的目标对象
12      */
13     private Object target;
14 
15     public ProxyFactory(Object object){
16         target = object;
17     }
18     
19     /**
20      * 动态给目标对象创建一个代理对象
21      */
22     public Object getProxyInstance() {
23         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
24             
25             @Override
26             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
27                 System.out.println("我是由代理工厂创建来代理target的");
28                 Object result = method.invoke(target, args);
29                 System.out.println("代理处理完成");
30                 return result;
31             }
32         });
33     }
34 }

测试main方法如下:

1 public static void main(String[] args) throws ClassNotFoundException {
2 
3         PersonAServiceImpl personA = new PersonAServiceImpl();
4         BuyTicketService ticketService =  (BuyTicketService)new ProxyFactory(personA).getProxyInstance();
5         ticketService.buyTicket();
6     }

结果如下:

1 我是由代理工厂创建来代理target的
2 我是personA,我买到车票啦
3 代理处理完成

在这里这个ProxyFactory的目标对象是个Object类型的,所以不仅可以代理PersonAServiceImpl,还可以代理其他的类型的目标对象。比如现在新增一个PersonCServiceImpl如下:

1 public class PersonCServiceImpl implements BuyTicketService {
2 
3     @Override
4     public void buyTicket() {
5         System.out.println("我是personC,我买到车票啦");
6     }
7 }

修改测试的main方法如下:

1 public static void main(String[] args) throws ClassNotFoundException {
2 
3         PersonAServiceImpl personA = new PersonAServiceImpl();
4         PersonCServiceImpl personC = new PersonCServiceImpl();
5         BuyTicketService ticketServiceA =  (BuyTicketService)new ProxyFactory(personA).getProxyInstance();
6         ticketServiceA.buyTicket();
7         BuyTicketService ticketServiceC =  (BuyTicketService)new ProxyFactory(personC).getProxyInstance();
8         ticketServiceC.buyTicket();
9     }

结果为:

1 我是由代理工厂创建来代理target的
2 我是personA,我买到车票啦
3 代理处理完成
4 我是由代理工厂创建来代理target的
5 我是personC,我买到车票啦
6 代理处理完成

但是如过想让这个ProxyFactory只代理PersonAServiceImpl的话,就可以将ProxyFactory的目标对象定义成PersonAServiceImpl如下示例:

 1 public class ProxyFactory {
 2 
 3     /**
 4      * 需要被代理的目标对象
 5      */
 6     private PersonAServiceImpl target = new PersonAServiceImpl();
 7     
 8     /**
 9      * 动态给目标对象创建一个代理对象
10      */
11     public Object getProxyInstance() {
12         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
13             
14             @Override
15             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
16                 System.out.println("我是由代理工厂创建来代理target的");
17                 Object result = method.invoke(target, args);
18                 System.out.println("代理处理完成");
19                 return result;
20             }
21         });
22     }
23 }

则再次执行测试的main方法,结果就变成:

1 我是由代理工厂创建来代理target的
2 我是personA,我买到车票啦
3 代理处理完成
4 我是由代理工厂创建来代理target的
5 我是personA,我买到车票啦
6 代理处理完成

那么使用代理模式有什么好处呢?

1.首先可以将需要代理的目标对象personAServiceImpl封装起来,而对外服务是代理对象;

2.可以对目标对象进行扩展而不需要改personAServiceImpl本身的业务

之前的案例被代理的personAServiceImpl是接口的实现类,那么如果想直接代理接口咋办呢,如mybatis的mapper,只定义了接口而没有具体的实现类,需要怎么代理呢?案例如下:

需要被代理的目标接口为BuyTicketService

1 package com.luck.codehelp.proxy;
2 
3 //定义一个serivce
4 public interface BuyTicketService {
5     // 买票
6     public void buyTicket();
7     // 退票
8     public void refundTicket();
9 }

然后给这个接口创建一个代理者:

 1 package com.luck.codehelp.proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 /**
 7  * BuyTicketService的动态代理实现
 8  * */
 9 public class BuyTicketProxy implements InvocationHandler{
10 
11     @Override
12     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
13         System.out.println("我是代理接口类型的");
14         System.out.println("我现在代理实现接口的方法method:"+method.getName());
15         System.out.println("代理执行完成");
16         return null;
17     }
18 }

和之前一样,代理者只需要实现InvocationHandler和重写invoke方法即可

再定义一个代理工厂ProxyFactory

 1 package com.luck.codehelp.proxy;
 2 
 3 import java.lang.reflect.Proxy;
 4 
 5 /**
 6  * 代理工厂
 7  */
 8 public class ProxyFactory {
 9 
10     /**
11      * 动态给目标对象创建一个代理对象
12      */
13     @SuppressWarnings("unchecked")
14     public static <T> T getProxyInstance(Class<T> proxyInterface) {
15         
16         Class<T>[] interfaces = new Class[]{proxyInterface};
17         
18         return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(), interfaces,
19                 new BuyTicketProxy());
20     }
21 }

测试main方法如下:

1 public static void main(String[] args) throws ClassNotFoundException {
2         BuyTicketService buyTicket = ProxyFactory.getProxyInstance(BuyTicketService.class);
3         buyTicket.buyTicket();
4         buyTicket.refundTicket();
5     }

结果为:

1 我是代理接口类型的
2 我现在代理实现接口的方法method:buyTicket
3 代理执行完成
4 我是代理接口类型的
5 我现在代理实现接口的方法method:refundTicket
6 代理执行完成

可以看出最后BuyTicketService的方法最终都是调用了代理者的invoke方法,可见实现动态代理过程不复杂

首先:

1、需要一个代理者,代理者需要实现InvocationHandler接口并重写invoke方法

2、有一个代理工厂,为目标对象new出一个代理者即可

3、最终被代理的接口的方法的调用都是执行代理者的invoke方法

mybatis的mapper就是使用了这样的技术来实现mapper接口的动态代理,过程如下:

首先需要定义接口,mybatis的所有mapper都是

然后需要有一个代理者,mybatis的代理者是MapperProxy,源码如下:

 1 package org.apache.ibatis.binding;
 2 
 3 import java.io.Serializable;
 4 import java.lang.reflect.InvocationHandler;
 5 import java.lang.reflect.Method;
 6 import java.util.Map;
 7 
 8 import org.apache.ibatis.session.SqlSession;
 9 
10 public class MapperProxy<T> implements InvocationHandler, Serializable {
11 
12   private static final long serialVersionUID = -6424540398559729838L;
13   private final SqlSession sqlSession;
14   private final Class<T> mapperInterface;
15   private final Map<Method, MapperMethod> methodCache;
16 
17   public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
18     this.sqlSession = sqlSession;
19     this.mapperInterface = mapperInterface;
20     this.methodCache = methodCache;
21   }
22 
23   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
24     if (Object.class.equals(method.getDeclaringClass())) {
25       return method.invoke(this, args);
26     }
27     final MapperMethod mapperMethod = cachedMapperMethod(method);
28     return mapperMethod.execute(sqlSession, args);
29   }
30 
31   private MapperMethod cachedMapperMethod(Method method) {
32     MapperMethod mapperMethod = methodCache.get(method);
33     if (mapperMethod == null) {
34       mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
35       methodCache.put(method, mapperMethod);
36     }
37     return mapperMethod;
38   }
39 
40 }

实现了InvocationHandler接口并且重写了invoke方法,最终被代理的mapper的方法调用都是执行了这个invoke方法,待会再具体分析,先看下代理工厂,mybatis的代理工厂是MapperProxyFactory,源码如下:

 1 package org.apache.ibatis.binding;
 2 
 3 import java.lang.reflect.Method;
 4 import java.lang.reflect.Proxy;
 5 import java.util.Map;
 6 import java.util.concurrent.ConcurrentHashMap;
 7 
 8 import org.apache.ibatis.session.SqlSession;
 9 
10 public class MapperProxyFactory<T> {
11 
12   private final Class<T> mapperInterface;
13   private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
14 
15   public MapperProxyFactory(Class<T> mapperInterface) {
16     this.mapperInterface = mapperInterface;
17   }
18 
19   public Class<T> getMapperInterface() {
20     return mapperInterface;
21   }
22 
23   public Map<Method, MapperMethod> getMethodCache() {
24     return methodCache;
25   }
26 
27   @SuppressWarnings("unchecked")
28   protected T newInstance(MapperProxy<T> mapperProxy) {
29     return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
30   }
31 
32   public T newInstance(SqlSession sqlSession) {
33     final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
34     return newInstance(mapperProxy);
35   }
36 
37 }

代理工厂的作用是返回一个目标对象的代理对象,这里方法newInstance就是这样的功能,可以看出protected T newInstance的方法和上一个案例的getProxyInstance方法效果一样。都是创建一个代理实现。

那么接下来调用了mapper中的方法都会执行代理者MapperProxy的invoke方法

1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
2     if (Object.class.equals(method.getDeclaringClass())) {
3       return method.invoke(this, args);
4     }
5     final MapperMethod mapperMethod = cachedMapperMethod(method);
6     return mapperMethod.execute(sqlSession, args);
7   }

第二行method.getDeclaringClass返回Class信息,由于接口是被代理实现的,所以返回的结果是:com.sun.proxy.$Proxy0

则执行第五行,cachedMapperMethod(method)