Java技术整理1---反射机制及动态代理详解
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---反射机制及动态代理详解的更多相关文章
- java.lang.Class<T> -- 反射机制及动态代理
Interface : Person package java_.lang_.component.bean; public interface Person { String area = " ...
- java反射机制与动态代理
在学习HadoopRPC时.用到了函数调用.函数调用都是採用的java的反射机制和动态代理来实现的,所以如今回想下java的反射和动态代理的相关知识. 一.反射 JAVA反射机制定义: JAVA反射机 ...
- Java反射机制以及动态代理
Java反射机制以及动态代理 Java反射机制 含义与功能 Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类 ...
- java学习笔记13--反射机制与动态代理
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...
- SpringBoot27 JDK动态代理详解、获取指定的类类型、动态注册Bean、接口调用框架
1 JDK动态代理详解 静态代理.JDK动态代理.Cglib动态代理的简单实现方式和区别请参见我的另外一篇博文. 1.1 JDK代理的基本步骤 >通过实现InvocationHandler接口来 ...
- Java中的反射机制和动态代理
一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...
- 【Java基础】java中的反射机制与动态代理
一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...
- Java的反射机制和动态代理
介绍Java注解的时候,多次提到了Java的反射API.与javax.lang.model不同的是,通过反射API可以获取程序在运行时刻的内部结构.反射API中提供的动态代理也是非常强大的功能,可以原 ...
- Java反射机制及Method.invoke详解
JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...
随机推荐
- 一行js弹窗代码就能设计漂亮的弹窗广告
接到一个设计需求,要求xmyanke在网站右侧挂一个弹窗广告宣传最近的活动,找了半天都没看到合适的,自己鼓捣了一行js弹窗代码就能设计漂亮的弹窗广告,来瞧一下,欢迎拍砖提意见,js弹窗广告代码如下: ...
- Cookie和Session的区别?
1.Cookie和Session都是会话技术,Cookie是运行在客户端,Session是运行在服务器端. 2.Cookie有大小限制以及浏览器在存cookie的个数也有限制,Session ...
- 006-优化web请求二-应用缓存、异步调用【Future、ListenableFuture、CompletableFuture】、ETag、WebSocket【SockJS、Stomp】
四.应用缓存 使用spring应用缓存.使用方式:使用@EnableCache注解激活Spring的缓存功能,需要创建一个CacheManager来处理缓存.如使用一个内存缓存示例 package c ...
- 晨枫U盘启动盘制作工具V4.0-安装原版Win7
第一类方法(32位64位系统通用): [1]找到Windows7系统的iso镜像,用UltraISO或者WinRAR打开iso镜像,然后提取/解压所有文件到你的U盘根目录. [2]在你的U盘里找到名为 ...
- sap gui 配置
1: sap gui 安全配置 ,在左下角搜索sap gui configuration.
- SOA架构大概思路
1.创建父工程(pom)管理jar包版本 2.创建子工程common工程(jar)管理通用工具类.通用pojo 3.创建服务层工程(聚合工程(pom)) 3.1.创建模块(dao.pojo.inter ...
- R安装package报ERROR: a 'NAMESPACE' file is required
R安装package报错: [root@Hadoop-NN-01 mysofts]# R CMD INSTALL trimcluster_0.1-1.tar.gz * installing to li ...
- GoWeb-Gin 文件上载
前些日子,我们Node.JS了一把. 如今,我们还是回到我们伟大的GO来吧 今天,带领大家继续Golang的啦,而且是个上传文件的例子 先给大家看结果 1. 如果是windows端,你需要安装post ...
- webpack使用雪碧图插件
1.先安装插件 npm install --save-dev webpack-spritesmith 2.配置webpack 配置之前 先引入var SpritesmithPlugin = requi ...
- java 之多线程
多线程基本概念_程序_线程 1.1程序.进程.线程 程序:Program是一个指令的集合 进程:Process(正在执行中的程序)是一个静态的概念.进程是程序的一次静态执行过程,占用特定的地址空间.每 ...