Java中代理
普通代理(最简单的代理)
需要有两个实现同一个接口的类,一个是被代理的类,一个是代理类
被代理类中我们按照自己想实现的功能重写接口中的方法
代理类中因为需要代理被代理类,所以在代理类中需要有个被代理类的实例,这样在重写接口中的方法的时候,直接调用代理类中的实例中对应的方法直接实现代理功能
具体代码的实现
interface Interface{
void dosomething();
void dosomethingelse(String args);
}
class RealObject implements Interface{ //被代理的类
@Override
public void dosomething() {
System.out.println("do something!!!");
}
@Override
public void dosomethingelse(String args) {
System.out.println("do something else:"+args);
}
}
class CommonProxy implements Interface{ //代理类
private Interface proxied; //这个接口应该是指向被代理对象的实例
public CommonProxy(Interface proxied) {
this.proxied = proxied;
}
@Override
public void dosomething() {
proxied.dosomething(); //直接调用被代理对象中的方法实现代理功能
}
@Override
public void dosomethingelse(String args) {
proxied.dosomethingelse(args);
}
}
public class SimpleProxyDemo{
public static void consumer(Interface inter){
inter.dosomethingelse("booo");
inter.dosomething();
}
public static void main(String[] args) {
consumer(new CommonProxy(new RealObject()));
}
}
动态代理技术
上面的代理对象都是我们手动写的,Java动态代理技术可以实现动态的创建代理对象,并且动态的处理对代理方法的调用
简单的代码实现(有一部分使用的是上面的接口和实现类)
class DynamicProxyHandler implements InvocationHandler {
Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxied,args);
}
}
public class DynamicProxyDemo{
public static void main(String[] args) {
RealObject realObject=new RealObject();
Interface proxy = (Interface) Proxy
.newProxyInstance(Interface.class.getClassLoader(), new Class[]{Interface.class}, new DynamicProxyHandler(new RealObject()));
proxy.dosomethingelse("shei");
proxy.dosomething();
}
}
使用步骤:
定义一个实现InvocationHandler接口的调用处理器,重写里面的invoke方法
直接使用Proxy中的静态方法创建代理类,创建的过程中需要一个类加载器,一个被代理的接口,还有一个上面定义的调用处理器。
注意最后将生成的代理对象转换成我们需要的被代理类实现的接口,在上面就是Interface接口,注意这里的Interface是首字母大写,不是Java中的关键字。
动态代理的原理
Proxy.newProxyInstance()函数
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h); //首先判断传递的调用处理器是不是空,否则就抛出空指针异常 final Class<?>[] intfs = interfaces.clone(); //使用clone()函数实现初始化接口的Class对象数组。
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
} /*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs); //生成代理对象的Class对象实例。 /*
* 借助调用处理器生成对应的代理类
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
} final Constructor<?> cons = cl.getConstructor(constructorParams); //获得代理类的构造函数
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h}); //使用代理类的构造函数和InvocationHandler实例化一个代理对象,最后返回代理对象
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
下面函数是二级缓存类WeakCache类中的get()函数,其实例对象就是cacheProxyClass,下面函数中如果根据提供的类加载器和接口数组能在缓存中找到代理类就直接返回该代理类,否则会调用ProxyClassFactory工厂去生成代理类。这里用到的缓存是二级缓存,它的一级缓存key是根据类加载器生成的,二级缓存key是根据接口数组生成的。
代理类Class对象的生成(上面黄色的部分)最终会定位到WeakCache类的实例proxyClassCache中的get()函数。ProxyClassCache对象有两个变量,KeyFactory和ProxyClassFactory,前者实现生成二级缓存的subkey,根据subkey在Concurrentmap中取出对应的factory(代码中使用父接口Supplier引用supplier指向),由supplier的get()函数得到对应的value(代理类class对象)。
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 根据由类加载器生成的一级缓存的key向缓存中取对应的concurrentmap值
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) { //如果没有valuesmap就创建一对放进去,
//只不过里面的concurrentmap还没有什么东西(二次缓存的key和工厂)
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// 运行到这里,生成二次缓存的key(使用classloader和interface生成),
// 取出里面对应的代理类supplier
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);//注意缓存的只是Supplier指向的工厂,没有缓存代理类Class对象
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier可能是个工厂也可能是缓存,由他提供对应的代理类Class对象
V value = supplier.get(); //注意这一步是生成代理Class对象的关键,
//由缓存或者新建的supplier创建代理的Class对象
if (value != null) {
return value;
}
}
// 否则没有supplier,由下面的语句生成对应的supplier
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap); //这就是supplier
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory); //将生成的factory放进缓存,方便下次使用。
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else { //supplier只是Factory的父接口
if (valuesMap.replace(subKey, supplier, factory)) { //有supplier但是经过get()方法得到的是空,
//及所以这里使用新的factory替换之前的supplier
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// 否则就使用现在的supplier重试
supplier = valuesMap.get(subKey);
}
}
}
}
Factory类中的get()函数(上面的黄色部分),其实现了supplier接口重写了里面的get()方法
public synchronized V get() { // serialize access
// 根据二级的key查询得到factory对象,检查拿到的和现在的相比是否一致,
//factory中有点像循环引用,因为factory对象中的ConCurrentMap中含有当前对象的引用
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// 检查缓存是否一致,不一致就设为null,返回上一个函数继续生成对应的Factory对象
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter)); //生成对应的代理class对象,
//valueFactory就是上面ProxyClassCache的实现类
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value); //CacheValue继承自Supplier,
//同样也含有get()(是父类reference中的函数)函数返回对应的值。
// put into reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
// try replacing us with CacheValue (this should always succeed)
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
}
上面黄色部分最终会执行下面proxyclassfactory中的apply()函数
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 代理类的名字前缀
private static final String proxyClassNamePrefix = "$Proxy"; // 为下一个代理类产生独一无二的标志数,放在类的名字中
private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* 判断是不是代表一个接口
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* 判断是不是重复.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
} String proxyPkg = null; //
int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /*
* 判断是不是所有的非公有接口是不是在同一个包下,如果没有就会抛出异常
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
} if (proxyPkg == null) {
// 如果没有非公有的interface就使用sun公司自己默认的包
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
} /*
* 为代理类生成名字 类似 $proxy0
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num; /*
* 由proxygenerator生成对应的Class类的字节形式,使用我们给的类加载器加载对应的类。
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName, //native 方法
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
最后我们看看上面的红色部分,ProxyGenerator类的generatorProxyClass()函数
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);//46在ASCII码中就是 “.”
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3); //和上面一起用来创建代理类的存放目录
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");//这个就是代理类的名字 $proxy0.class
} else {
var2 = Paths.get(var0 + ".class");//var2是代理类的全路径名
}
Files.write(var2, var4, new OpenOption[0]);//最后写进对应的文件。
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
上面的黄色部分里面使用了很多反射生成对应的byte数组,里面写的就应该是class文件的内容,
动态代理中最重要的就是Proxy类,里面含有大量的静态变量和静态函数
静态变量:WeakCache类型的实例 proxyClassCache
InnovationHandler实例h
所含的内部类:
内部类keyFactory(在二级缓存中用于生成二级缓存的subkey)
内部类ProxyClassFactory(和上面一个内部类一起实例化上面的代理Class类的二级缓存WeakCache,在缓存中这个用于获得或者生成代理类Class对象)
Factory(实例存储在二级缓存中,最终调用它的get()函数返回对应的代理类Class文件)
静态函数:
newProxyInstance(classloader,classes[],InvocationHandler),(这个函数的会调用下面的函数,返回代理类Class对象实例,最终使用反射生成代理类实例)
getproxyclass0(classloader,classes)(这个函数会直接调用WeakCache的get()函数查询proxyClassCache缓存)
根据classloader生成一级缓存的key,取出concurrentmap,在根据classloader和interfaces使用keyFactory中apply函数生成subkey,根据subkey取出对应的supplier,有下面两 种情况,
一种:supplier是一个缓存,这时候supplier指向的是一个CacheValue的对象,调用get()函数是其父类weakreference的父类reference中的get()函数,函数返回缓存中的值
另一种:当前supplier中没有值,这时候使用ProxyClassFactory中的apply()函数生成对应的代理类的Class对象,并且存进CacheValue中。
Java中代理的更多相关文章
- Java中代理对象的使用小结
在某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到了中介作用,这不仅仅使用代理模式,还可以实现适配器模式.装饰模式等. 代理对象内部含有对真实对象的引用,从而 ...
- JAVA中代理模式
代理模式 在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用.代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能 ...
- java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. ...
- 所有和Java中代理有关的知识点都在这了。
对于每一个Java开发来说,代理这个词或多或少都会听说过.你可能听到过的有代理模式.动态代理.反向代理等.那么,到底什么是代理,这么多代理又有什么区别呢.本文就来简要分析一下. 代理技术,其实不只是J ...
- Java中代理和装饰者模式的区别
装饰模式:以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案: 代理模式:给一个对象提供一个代理对象,并有代理对象来控制对原有对象的引用: 装饰模式为所装饰的对象增强功能:代理模式对代理的对 ...
- 谈谈Java中的代理模式
首先来看一下代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用, 其特征是代理类与 ...
- Java中jdk代理和cglib代理
代理模式 给某一个对象提供一个代理,并由代理对象控制对原对象的引用.在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 在Java中代理模式从实 ...
- java 动态代理—— Mybaties 拦截器链基本原理实现
1.摘要 Mybaties 中有个分页插件,之前有特意的去了解了一下原理 :https://www.cnblogs.com/jonrain0625/p/11168247.html,从了解中得知分页插件 ...
- Java中的JDK动态代理
所谓代理,其实就是相当于一个中间人,当客户端需要服务端的服务时,不是客户直接去找服务,而是客户先去找代理,告诉代理需要什么服务,然后代理再去服务端找服务,最后将结果返回给客户. 在日常生活中,就拿买火 ...
随机推荐
- Realm 处理List<String> 问题 Type parameter 'java.lang.String' is not within its bound; should implement 'io.realm.RealmModel
public class InitAppBean extends RealmObject { private String sapling; private String logistics; pri ...
- python之路——18
王二学习python的笔记以及记录,如有雷同,那也没事,欢迎交流,wx:wyb199594 有需要<正则指引>这本书的可以联系我,仅供学习交流使用 复习 1.递归函数,必须有结束条件2.返 ...
- Spring核心模块:IoC容器介绍
1.IoC容器运用的是控制反转模式. 2.IoC容器负责管理对象之间的依赖关系,并完成对象的注入. 3.在IoC设计中,会将依赖关系注入到特定组件中,其中setter注入和构造器注入是主要的注入方式. ...
- VS2017离线版的SSDT安装包(包括SSIS)
VS2017离线版的SSDT安装包(包括SSIS) 装好SQL2016和VS2017后发现没有创建SSIS项目的接口,原来VS2017里是没有包含SSDT的安装包的. 下面是我整理好的包含中英文的VS ...
- eclipse连接mysql数据库
我这里在eclipse新建一个maven 项目做测试 首先我们要在本地电脑安装了mysql数据库和mysql驱动包 我的mysql数据库是通过phpstudy自带的 这个是驱动包 window–> ...
- 一文搞清到底什么是 .NET?
现在各种 .NET 满天飞,别说新手了,连我这样的老手都差点被绕进去.到底什么是 .NET 呢?通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 这篇文章好长呀 ...
- mysql索引总结(转)
mysql索引总结----mysql 索引类型以及创建 文章归属:http://feiyan.info/16.html,我想自己去写了,但是发现此君总结的非常详细.直接搬过来了 关于MySQL索引的好 ...
- (二)获取Access_token
获取access_token access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token.开发者需要进行妥善保存.access_token的存储至少要保 ...
- koa-passport实现本地验证
安装 yarn add koa-passport passport-local 先看下passport.js登录策略,判断用户和密码 const passport = require('koa-pas ...
- DevExpress 折线图和柱状图的绘制与数据绑定
DevExpress 组件是一个非常丰富和强大的组件,适合各种可视化图形的绘制与展示,在数据分析展示中是个很有帮助的,网上也有很多关于这方面的文章,关于折线图或柱状图的画法,以下是自己在工作中接触到的 ...