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

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

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

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

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

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

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

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

代码示例如下:

定义一个买票的接口BuyTicketService

 package com.luck.codehelp.proxy;

 //定义一个serivce
public interface BuyTicketService {
// 买票的接口
public void buyTicket();
}

PersonA实现买票接口

 public class PersonAServiceImpl implements BuyTicketService {

     @Override
public void buyTicket() {
System.out.println("我是personA,我买到车票啦");
} }

PersonB实现买票接口

 public class PersonBServiceImpl implements BuyTicketService {

     @Override
public void buyTicket() {
System.out.println("我是personB,我是帮personA去买票的");
System.out.println("打车去车站");
new PersonAServiceImpl().buyTicket();
System.out.println("打车回去");
} }

Main方法测试代码如下:

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

结果为:

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

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

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

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

这个是Proxy类的静态方法,方法的三个参数为
ClassLoader loader:类加载器,目标对象类的类加载器
Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

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

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* 代理工厂
*/
public class ProxyFactory { /**
* 需要被代理的目标对象
*/
private Object target; public ProxyFactory(Object object){
target = object;
} /**
* 动态给目标对象创建一个代理对象
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是由代理工厂创建来代理target的");
Object result = method.invoke(target, args);
System.out.println("代理处理完成");
return result;
}
});
}
}

测试main方法如下:

 public static void main(String[] args) throws ClassNotFoundException {

         PersonAServiceImpl personA = new PersonAServiceImpl();
BuyTicketService ticketService = (BuyTicketService)new ProxyFactory(personA).getProxyInstance();
ticketService.buyTicket();
}

结果如下:

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

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

 public class PersonCServiceImpl implements BuyTicketService {

     @Override
public void buyTicket() {
System.out.println("我是personC,我买到车票啦");
}
}

修改测试的main方法如下:

 public static void main(String[] args) throws ClassNotFoundException {

         PersonAServiceImpl personA = new PersonAServiceImpl();
PersonCServiceImpl personC = new PersonCServiceImpl();
BuyTicketService ticketServiceA = (BuyTicketService)new ProxyFactory(personA).getProxyInstance();
ticketServiceA.buyTicket();
BuyTicketService ticketServiceC = (BuyTicketService)new ProxyFactory(personC).getProxyInstance();
ticketServiceC.buyTicket();
}

结果为:

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

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

 public class ProxyFactory {

     /**
* 需要被代理的目标对象
*/
private PersonAServiceImpl target = new PersonAServiceImpl(); /**
* 动态给目标对象创建一个代理对象
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是由代理工厂创建来代理target的");
Object result = method.invoke(target, args);
System.out.println("代理处理完成");
return result;
}
});
}
}

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

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

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

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

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

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

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

 package com.luck.codehelp.proxy;

 //定义一个serivce
public interface BuyTicketService {
// 买票
public void buyTicket();
// 退票
public void refundTicket();
}

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

 package com.luck.codehelp.proxy;

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* BuyTicketService的动态代理实现
* */
public class BuyTicketProxy implements InvocationHandler{ @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是代理接口类型的");
System.out.println("我现在代理实现接口的方法method:"+method.getName());
System.out.println("代理执行完成");
return null;
}
}

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

再定义一个代理工厂ProxyFactory

 package com.luck.codehelp.proxy;

 import java.lang.reflect.Proxy;

 /**
* 代理工厂
*/
public class ProxyFactory { /**
* 动态给目标对象创建一个代理对象
*/
@SuppressWarnings("unchecked")
public static <T> T getProxyInstance(Class<T> proxyInterface) { Class<T>[] interfaces = new Class[]{proxyInterface}; return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(), interfaces,
new BuyTicketProxy());
}
}

测试main方法如下:

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

结果为:

 我是代理接口类型的
我现在代理实现接口的方法method:buyTicket
代理执行完成
我是代理接口类型的
我现在代理实现接口的方法method:refundTicket
代理执行完成

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

首先:

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

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

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

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

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

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

 package org.apache.ibatis.binding;

 import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map; import org.apache.ibatis.session.SqlSession; public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
} private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
} }

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

 package org.apache.ibatis.binding;

 import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import org.apache.ibatis.session.SqlSession; public class MapperProxyFactory<T> { private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
} public Class<T> getMapperInterface() {
return mapperInterface;
} public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
} @SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
} public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
} }

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

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

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

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

则执行第五行,cachedMapperMethod(method)

Java技术整理1---反射机制及动态代理详解的更多相关文章

  1. java.lang.Class<T> -- 反射机制及动态代理

    Interface : Person package java_.lang_.component.bean; public interface Person { String area = " ...

  2. java反射机制与动态代理

    在学习HadoopRPC时.用到了函数调用.函数调用都是採用的java的反射机制和动态代理来实现的,所以如今回想下java的反射和动态代理的相关知识. 一.反射 JAVA反射机制定义: JAVA反射机 ...

  3. Java反射机制以及动态代理

    Java反射机制以及动态代理 Java反射机制 含义与功能 Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类 ...

  4. java学习笔记13--反射机制与动态代理

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...

  5. SpringBoot27 JDK动态代理详解、获取指定的类类型、动态注册Bean、接口调用框架

    1 JDK动态代理详解 静态代理.JDK动态代理.Cglib动态代理的简单实现方式和区别请参见我的另外一篇博文. 1.1 JDK代理的基本步骤 >通过实现InvocationHandler接口来 ...

  6. Java中的反射机制和动态代理

    一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...

  7. 【Java基础】java中的反射机制与动态代理

    一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...

  8. Java的反射机制和动态代理

    介绍Java注解的时候,多次提到了Java的反射API.与javax.lang.model不同的是,通过反射API可以获取程序在运行时刻的内部结构.反射API中提供的动态代理也是非常强大的功能,可以原 ...

  9. Java反射机制及Method.invoke详解

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...

随机推荐

  1. Springmvc 使用 AbstractExcelView 导出excel

    $("#exportBtn").click(function () { location.href = "${pageContext.request.contextPat ...

  2. localstorage 和 sessionstorage 是什么?区别是什么?

    localstorage 和 sessionstorage 一样都是用来存储客户端临时信息的对象,他们均只能存储字符串类型对象: localstorage生命周期是永久的,这意味着除非用户在浏览器提供 ...

  3. 关于eclipse调试时程序控制台不能自动打开

      对于这个程序,在刚开始的时候,没有敲上22,29,33行的打印语句时,在调试的时候不会自动弹开控制台,所以一直在怀疑代码可能出错了.因此可以自己手动打开,但是如果敲上那些代码,系统可以自动弹开控制 ...

  4. JavaScript substr() 字符串截取函数使用详解

    substr 定义和用法 substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符. 语法 stringObject.substr(start,length) 如果 length ...

  5. Object类(API文档)

    java.lang Class Object java.lang.Object public class Object Class Object is the root of the class hi ...

  6. 机器人meta标签和X-Robots-Tag HTTP标头规格

    抽象 本文档详细介绍了页级索引设置如何让您控制Google如何通过搜索结果提供内容.您可以通过在(X)HTML页面或HTTP标头中包含元标记来指定这些标记. 笔记 请注意,只有当抓取工具被允许访问包含 ...

  7. chrome debug 服务端性能

    设置 http header 在 chrome 查看服务端性能 \Yii::$app->getResponse()->headers->set('Server-Timing', 'c ...

  8. 文档设计也需要坚持DRY原则--支付中心应用部署结构图完善

    今天上午,我拿着支付中心的设计文档,给入职不久的同事讲解目前支付中心系统的应用部署情况.当时同事嗯嗯地点头反应. 下午呢,发现自己设计的有问题,赶紧给予完善. 代码重构方面讲究DRY编程原则.我们在设 ...

  9. spring注解式开发之视图解析器

    http://localhost:8089/springmvc-04-viewResovler/springmvc/hello

  10. PHP开启mysqli扩展

    Call to undefined function mysqli_connect()解决这个问题需要开启mysqli扩展开启mysqli扩展需要这两个步骤缺一不可1.在php.ini中搜索php_m ...