您的位置:首页 > 编程语言 > Java开发

Java学习之jdk与cglib动态代理

2017-02-23 17:32 169 查看
声明:参考部分博客做记录

1.jdk动态代理实现

接口:

public interface UserService {
String getName();
}


接口实现:

public class UserServiceImpl implements UserService {
public String getName() {
System.out.println("-----getName-----");
return "john";
}
}


代理类:

public class UserProxy implements InvocationHandler {// jdk动态代理需要实现InvocationHandler接口

// 传入目标类
private Object target;

public UserProxy() {
}

public UserProxy(Object target) {
this.target = target;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("getName".equals(method.getName())) {
System.out.println("-----before------" + method.getName()
+ "------------");
// 反射执行方法,参数1目标类,参数2是方法参数
Object result = method.invoke(target, args);
System.out.println("-----after------" + method.getName()
+ "------------");
retu
4000
rn result;

} else {
Object result = method.invoke(target, args);
return result;
}
}
}


执行类:

public class AppMain {

public static void main(String[] args) {
// 实例化
UserService userService = new UserServiceImpl();
// 创建代理
InvocationHandler invocationHandler = new UserProxy(userService);
// jdk动态代理通过newProxyInstance实现,参数1是目标类ClassLoader,参数2是目标类接口,参数3是代理类
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(), userService.getClass()
.getInterfaces(), invocationHandler);
System.out.println(userServiceProxy.getName());
}

}


运行结果:

-----before------getName------------
-----getName-----
-----after------getName------------
john


2.cglib动态代理实现

jdk与cglib动态代理主要是代理类的不同

代理类:

public class UserCglibProxy implements MethodInterceptor {// cglib动态代理要实现MethodInterceptor接口

// 参数1是目标类,参数2是方法,参数3是参数,参数4是为了得到接口的子类实例
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// getSuperName调用父类创建的合成方法的名称(非拦截)
System.out.println("-----before------" + proxy.getSuperName()
+ "------------");
System.out.println(method.getName());
// 执行父类方法
Object object = proxy.invokeSuper(obj, args);
System.out.println("-----after------" + proxy.getSuperName()
+ "------------");
return object;
}
}


执行类:

public class AppMain {

public static void main(String[] args) {
UserCglibProxy proxy = new UserCglibProxy();

// 主要的增强类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(UserServiceImpl.class);
// 设置回调,次回调为代理类
enhancer.setCallback(proxy);
// 创建代理
UserService userService = (UserService) enhancer.create();
userService.getName();
}

}


运行结果:

-----before------CGLIB$getName$0------------
getName
-----getName-----
-----after------CGLIB$getName$0------------


3.原理介绍

代理模式:

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

区别:

1. jdk动态代理要依靠接口实现,而cglib则不需要

2. cglib对指定目标类生产一个子类,并覆盖其中方法实现增强

动态代理:

java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象

Proxy 的静态方法:

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)


java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。

InvocationHandler 的核心方法

// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
Object invoke(Object proxy, Method method, Object[] args)


创建过程:

1. 通过实现 InvocationHandler 接口创建自己的调用处理器;

2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

动态生成的代理类本身的一些特点:

1)包:如果所代理的接口都是 public的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或

private,所以除 public 之外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理类所在的包就是com.ibm.developerworks),这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;

2)类修饰符:该代理类具有final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;

3)类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。

具体请参考:http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

cglib如何生产字节码:

//此函数会被AbstractClassGenerator.create间接调用,并会在create函数中将结果缓存.
Enhancer.generateClass(ClassVisitor v)

//AbstractClassGenerator.create(Object key)
protected Object create(Object key) {
try {
Class gen = null;

synchronized (source) {
ClassLoader loader = getClassLoader();
Map cache2 = null;
cache2 = (Map)source.cache.get(loader);
if (cache2 == null) {
cache2 = new HashMap();
cache2.put(NAME_KEY, new HashSet());
source.cache.put(loader, cache2);
} else if (useCache) {
Reference ref = (Reference)cache2.get(key);
gen = (Class) (( ref == null ) ? null : ref.get());
}
if (gen == null) {
Object save = CURRENT.get();
CURRENT.set(this);
try {
this.key = key;

if (attemptLoad) {
try {
gen = loader.loadClass(getClassName());
} catch (ClassNotFoundException e) {
// ignore
}
}
if (gen == null) {
//生成的Class的二进制存储
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
//将类名放入Cache中,Cache实际上是一个Hashset的实例
getClassNameCache(loader).add(className);
//将生成的类装载路JVM
gen = ReflectUtils.defineClass(className, b, loader);
}

if (useCache) {
//将装载的类放入cache
cache2.put(key, new WeakReference(gen));
}
return firstInstance(gen);
} finally {
CURRENT.set(save);
}
}
}
return firstInstance(gen);
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}


proxy如何生产字节码,请参源码:

private byte[] generateClassFile() {
/*
* Record that proxy methods are needed for the hashCode, equals,
* and toString methods of java.lang.Object.  This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
*/
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
/*
* Now record all of the methods from the proxy interfaces, giving
* earlier interfaces precedence over later ones with duplicate
* methods.
*/
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
/*
* For each set of proxy methods with the same signature,
* verify that the methods' return types are compatible.
*/
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* ============================================================
* Step 2: Assemble FieldInfo and MethodInfo structs for all of
* fields and methods in the class we are generating.
*/
try {
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// generate code for proxy method and add it
methods.add(pm.generateMethod());
}
}
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
/* ============================================================
* Step 3: Write the final class file.
*/
/*
* Make sure that constant pool indexes are reserved for the
* following items before starting to write the final class file.
*/
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
/*
* Disallow new constant pool additions beyond this point, since
* we are about to write the final constant pool table.
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
/*
* Write all the items of the "ClassFile" structure.
* See JVMS section 4.1.
*/
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout);   // (write constant pool)
// u2 access_flags;
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(
dotToSlash(interfaces[i].getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
return bout.toByteArray();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java jdk 动态代理