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 ...
随机推荐
- oracle按照指定列排序操作
按照...分组排序后,得到行编号: row_number() over(partition by ... order by ...) 按照...分组排序后,得到相应的列的第一个数据: first_va ...
- 20170718 关于Mysql 安装于虚拟机Ubuntu中,内网中Windows系统无法访问
-- 1. 前提Mysql 已经安装在Ubuntu中 -- 2. 防火墙已经关闭 命令确认防护墙状态 -- 3.问题如果Ubuntu是基于Docker容器的环境,是否需要把Docker做端口映射? 解 ...
- linux相关介绍
1.linux的简介 (1)linux是一个开源.免费的操作系统,其稳定性.安全性.处理多并发(基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU) 的操作系统.linux是一个Unix类 ...
- WINDOWS SERVER 2008 R2安装指南
说明:适用于以下几种操作系统: 1.Windows Server 2008 Standard Endition R2 2.Windows Server 2008 Enterprise Endition ...
- url映射
#include<iostream> #include<algorithm> #include<ctype.h> #include<string> #i ...
- HTML中--定义header和footer高度中间自适应
<html> <head> <meta charset="utf-8" /> <title></title> <s ...
- golang fmt格式“占位符”
# 定义示例类型和变量 type Human struct { Name string } var people = Human{Name:"zhangsan"} 普通占位符 占位 ...
- C 语言boolean 值判断
printf("%d\n", !0); 1 1 printf("%d\n", !0); #include <std ...
- 112A
#include <iostream> #include <string> #include <cctype> using namespace std; int m ...
- 新发现:排序算法时间复杂度只有O(3n),命名为"wgw"排序法
思路:首先在待排序数组i[]中找出最大的值,以(最大值+1)的大小创建一个空数组kk[],然后遍历待排序数组i[]中的值n,其值n对应数组kk[]中的第n个元素加1.最后再把数组kk[]排好序的值赋回 ...