深入解析java中的静态代理与 *
发布时间:2023-08-23 13:10:56
java编码中经常用到代理,代理分为静态代理和 * 。其中 * 可以实现spring中的aop。
一、静态代理:程序运行之前,程序员就要编写proxy,然后进行编译,即在程序运行之前,代理类的字节码文件就已经生成了
被代理类的公共父类
package staticproxy;
public abstract class BaseClass {
public abstract void add();
}
被代理类
package staticproxy;
public class A extends BaseClass {
public void add() {
System.out.println("A add !");
}
}
代理类
package staticproxy;
public class Proxy {
BaseClass baseClass;
public void add() {
baseClass.add();
}
public void setBaseClass(BaseClass baseClass) {
this.baseClass = baseClass;
}
public static void main(String[] args) {
BaseClass baseClass = new A();
Proxy proxy = new Proxy();
proxy.setBaseClass(baseClass);
proxy.add();
}
}
二、 * :实际的代码在编译期间并没有生成,而是在运行期间运用反射机制动态的生成
被代理类接口
package jdkproxy;
public interface Service {
public void add();
public void update();
}
被代理类A
package jdkproxy;
public class AService implements Service {
public void add() {
System.out.println("AService add>>>>>>>>>>>>>>>>>>");
}
public void update() {
System.out.println("AService update>>>>>>>>>>>>>>>");
}
}
被代理类B
package jdkproxy;
public class BService implements Service {
public void add() {
System.out.println("BService add---------------");
}
public void update() {
System.out.println("BService update---------------");
}
}
代理类
package jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
MyInvocationHandler() {
super();
}
MyInvocationHandler(Object target) {
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 程序执行前加入逻辑
System.out.println("before-----------------------------");
// 程序执行
Object result = method.invoke(target, args);
//程序执行后加入逻辑
System.out.println("after------------------------------");
return result;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
测试类
package jdkproxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Service aService = new AService();
MyInvocationHandler handler = new MyInvocationHandler(aService);
// Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
.getClass().getClassLoader(), aService.getClass()
.getInterfaces(), handler);
//由动态生成的代理对象来aServiceProxy 代理执行程序,其中aServiceProxy 符合Service接口
aServiceProxy.add();
System.out.println();
aServiceProxy.update();
// 以下是对B的代理
// Service bService = new BService();
// MyInvocationHandler handler = new MyInvocationHandler(bService);
// Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
// .getClass().getClassLoader(), bService.getClass()
// .getInterfaces(), handler);
// bServiceProxy.add();
// System.out.println();
// bServiceProxy.update();
}
}
输出结果:
before-----------------------------
AService add>>>>>>>>>>>>>>>>>>
after------------------------------
before-----------------------------
AService update>>>>>>>>>>>>>>>
after------------------------------
其中上述标红的语句是产生代理类的关键代码,可以产生一个符合Service接口的代理对象,newProxyInstance这个方法会做这样一件事情,他将把你要代理的全部接口,用一个由代码动态生成的类来实现,该类中所有的接口中的方法都重写为调用InvocationHandler.invoke()方法。
下面详细介绍是如何实现代理对象的生成的
Proxy的newProxyInstance方法,其中,为了看起来方便,已经将该方法中的异常处理语句删减
下下面public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
//生成指定的代理类
Class cl = getProxyClass(loader, interfaces);
Constructor cons = cl.getConstructor(constructorParams);
// 生成代理类的实例,并把MyInvocationHandler的实例传给它的构造方法,代理类对象实际执行都会调用MyInvocationHandler的invoke方法,所以代理类对象中维持一个MyInvocationHandler引用
return (Object) cons.newInstance(new Object[] { h });
} 其中getProxyClass方法返回代理类的实例
Proxy的getProxyClass方法
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
{
//前面省略很多缓存、异常处理、判断逻辑代码,为了使程序更加突出
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
proxyClasses.put(proxyClass, null);
return proxyClass;
}
下面看ProxyGenerator的generateProxyClass方法,该方法最终产生代理类的字节码文件:
public static byte[] generateProxyClass(final String name, Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
// 这里动态生成代理类的字节码
final byte[] classFile = gen.generateClassFile();
// 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
// 返回代理类的字节码
return classFile;
}
那么最终生成的代理类到底是什么样子呢,如下(省略了一下equals,hashcode,toString等方法,只展示构造函数和add方法):
public final class $Proxy11 extends Proxy implements Service
{ // 构造方法,参数就是刚才传过来的MyInvocationHandler类的实例
public $Proxy11(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
/**
* 继承的add方法,重写,调用MyInvocationHandler中的invoke方法
*/
public final void add()
{
try
{
// 实际上就是调用MyInvocationHandler中的invoke方法
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
}


猜你喜欢
- 一、why(为什么要用Hibernate缓存?)Hibernate是一个持久层框架,经常访问物理数据库。为了降低应用程序对物理数据源访问的频
- 本文为大家分享了JSplitPane的使用方法,供大家参考,具体内容如下1、swing分割窗口控件JSplitPane,用来将窗口分割成两个
- 今天收到了Android Studio3.0更新推送,在升级过程中遇到几个问题,在这里把问题和解决方法记录下,方便要升级的童鞋。如果还有童
- BeanUtils.copyProperties无法封装使用BeanUtils.copyProperties(user, memeber);
- 现阶段,我们创建了最简单的Android项目,现在在此公布github链接https://github.com/neuyu/android-
- 鉴于谷歌最新推出的Android Studio备受开发者的推崇,所以也跟着体验一下。一、介绍Android Studio Andr
- public interface ArraySQL 类型 ARRAY 在 java 编程语言中的映射关系。默认情况下,Array 值是对 S
- 虹软免费,高级版本试用支持在线、离线有 Java SDK,C++ SDK一、注册虹软开发者平台点击注册注册完成后可在“我
- (注意:本文基于JDK1.8) 前言包括迭代器中的remove()方法,以及删除单个元素、删除多个元素、删除所有元素、删除不包含的
- 一. spring配置文件:application.xml<?xml version="1.0" encoding
- 需要读取excel数据转换成json数据,写了个测试功能,转换正常:JSON转换:org.json.jar 测试类:
- 1、前言WorkManager 是适合用于持久性工作的推荐解决方案。如果工作始终要通过应用重启和系统重新启动来调度,便是持久性的工
- spring中实例化bean无效在做Struts2和Spring整合时遇到Spring实例化无效的情况,Action中代码如下public
- 文件复制和文件上传最近在看文件和IO流相关的东西,写了一些代码,发现这个有很多很有趣的地方。特别是对 File 和 IO 流的使用之后,我对
- AlertDialog的几种用法xml代码:<?xml version="1.0" encoding="
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- 本文介绍的是关于Mybatis中用OGNL表达式处理动态sql的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍:常用的Mybat
- package com.test;import org.htmlparser.Node;import org.htmlparser.Node
- 前言最近在学习spring,抽空会将学习的知识总结下面,本文我们会接触spring 4的新功能:@Conditional注解。在之前的spr
- 当使用struts2的validation.xml进行验证时,需要注意如下几个方面:I.首先应该注意validation.xml的名字,一定