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 ...
随机推荐
- java 并发包runnable 与 callable
1.runnable 与 callable区别 2.避免callable执行完任务,获取返回结果时,阻塞其他子线程 下面固定线程池,设置4个,表明同时只有4个线程在执行任务,当某个线程执行完一个任务, ...
- RN animated帧动画
效果图: 代码: export default class AnimationFrameScene extends Component { constructor () { super() this. ...
- Mysql常用的存储引擎
存储引擎 存储引擎是表级别的概念,不同的存储引擎保存数据和索引的方式是不相同的. MyISAM存储引擎 MyISAM最典型的性能问题就是表锁的问题. MyISAM只将数据写到内存中,然后等待操作系统 ...
- 简明 ASP.NET Core 手册2018
https://windsting.github.io/little-aspnetcore-book/book/ 中文版 https://nbarbettini.gitbooks.io/little- ...
- 技嘉主板BIOS恢复方法
技嘉的dual bios技术的原理很简单,在main bios能启动的情况下,backup bios不会对bios进行检测.只有当main bios出现故障(这个故障一般是软件故障)才会从backup ...
- 快学Scala 2
控制结构和函数 1.在Scala中,几乎所有构造出来的语法结构都有值.这个特性是为了使得程序更加精简,也更易读. (1)if表达式有值 (2)块也有值——是它最后一个表达式的值 (3)Scala的fo ...
- 微信小程序使用阿里图标-iconfont
步骤一:下载项目图标 步骤二:解压文件,重命名 iconfont.css为 iconfont.wxss ,并复制 到项目 static文件夹 icon文件夹下 ...
- sap 类的左侧导航栏
- [LeetCode] 98. Validate Binary Search Tree_Medium
Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as ...
- [LeetCode] 603. Consecutive Available Seats_Easy tag: SQL
Several friends at a cinema ticket office would like to reserve consecutive available seats.Can you ...