大纲

  1. 代理
  2. proxy
  3. cglib
  4. 小结

一、代理

为什么要用代理?其实就是希望不修改对象的情况下,增强对象。

静态代理:

  • 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法,调用方法的前后实现需要增强的逻辑。
  • 静态代理有一个问题就是,每个代理类和目标类一一对应,需要代理的类多的情况,需要大量的代理类,难以维护。

动态代理:

  • 动态代理就是运行时动态生成的类,并不是在编译时期。
  • 动态代理有两种不同的方式,一种是jdk反射包下的的Prxoy,一种是cglib。

二、Proxy

Proxy生成代理对象需要目标对象实现一至少一个接口。

Proxy通过反射实现动态代理。

生成代理对象需要调用Proxy中newProxyInstance方法。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

loader-目标对象的classloader

interfaces-目标对象实现的接口

InvocationHandler-处理器,当目标对象接口中的方法被调用时处理器中invoke方法会被调用从而实现动态代理

在看下InvocationHandler

public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

proxy-代理对象
method-被调用的方法
args-调用方法时传入的参数
invoke返回值为代理方法的返回值

测试:

接口:

public interface Speak {
String say(String content);
}

目标类:


import lombok.Data;
@Data
public class Person implements Speak{
private String name;
private int age;
public String say(String content){
System.out.println("hi"+name+age+"content:"+content);
return "say return";
}
}

代理工厂:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class ProxyFactory{
//维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//生成代理对象
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("before method:"+method.getName());
//执行目标对象方法,返回return值。
Object returnValue = method.invoke(target, args);
System.out.println("end method:"+method.getName());
return returnValue;
}
});
}
}

代理对象在代理方法调用前后打了一行字。

public static void main(String[] args) {
Person p = new Person();
p.setAge(11);
p.setName("xx");
ProxyFactory factory = new ProxyFactory(p);
Object proxyInstance = factory.getProxyInstance();
Speak speak = (Speak) proxyInstance;
String returnValue = speak.say("haha");
System.out.println("returnValue:"+returnValue);
}

三、cglib

静态代理和都必须实现接口,而cglib没有这个限制,cglib通过字节码操作动态生成子类,因此目标类不能被final修饰。

与proxy类似的我们也需要复写一个处理器

public interface MethodInterceptor extends Callback {
Object  (Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable;
}

proxy-代理对象
method-被调用的方法
methodProxy-代理方法(具体本人不是特别清楚)
intercept返回值为代理方法的返回值

测试:

重写Person不需要实现接口

import lombok.Data;

@Data
public class Person{
private String name;
private int age;
public String say(String content){
System.out.println("hi"+name+age+"content:"+content);
return "say return";
}
}

代理工厂:

import org.assertj.core.internal.cglib.proxy.Enhancer;
import org.assertj.core.internal.cglib.proxy.MethodInterceptor;
import org.assertj.core.internal.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CgProxyFactory<T> {
//维护目标对象
private T target;
public CgProxyFactory(T target) {
this.target = target;
} //获取代理
public T getProxyInstance() {
Enhancer en = new Enhancer();
en.setSuperclass(this.target.getClass());
//设置拦截器
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before method:"+method.getName());
Object returnValue = method.invoke(target, objects);
System.out.println("end method:"+method.getName());
return returnValue;
}
});
return (T) en.create();
}
}

和之前的代理对象一样,在代理方法调用前后打了一行字。

public static void main(String[] args) {
Person p = new Person();
p.setAge(11);
p.setName("xx");
CgProxyFactory<Person> factory = new CgProxyFactory(p);
Person proxyInstance = factory.getProxyInstance();
String returnValue = proxyInstance.say("cg");
System.out.println("returnValue:" + returnValue);
}

四、小结:

  1. Proxy需要代理类实现接口,底层为反射。
  2. Cglib代理对象不能被final修饰,底层是字节码操作。
  3. spring会根据目标类是否实现接口的情况,切换动态代理的模式,也可以通过配置强制使用cglib。

java动态代理--proxy&cglib的更多相关文章

  1. java动态代理之CGLIB实现

    动态代理(CGlib 与连接池的案例) Cglib代理: 针对类来实现代理,对指定目标 产生一个子类 通过方法拦截技术拦截所有父类方法的调用. 我们要使用cglib代理必须引入 cglib的jar包 ...

  2. Java动态代理与CGLib

    Java帝国之动态代理 CGLib:从兄弟到父子-动态代理在民间是怎么玩的? 以上两篇文章引用自微信公众号: 码农翻身 Java动态代理 深度详解 以上文章引用博客园:陈树义

  3. Java动态代理与Cglib库

    JDK动态代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在 ...

  4. (转)Java动态代理与CGLib代理

    <br>public class UserDAOImpl{ <br><br>    public void save() { <br>        / ...

  5. Java动态代理机制——Cglib

    上一篇说过JDK动态代理机制,只能代理实现了接口的类,这就造成了限制.对于没有实现接口的类,我们可以用Cglib动态代理机制来实现. Cglib是针对类生成代理,主要是对用户类生成一个子类.因为有继承 ...

  6. IT忍者神龟之Java动态代理与CGLib代理

    <br>public class UserDAOImpl{ <br><br>    public void save() { <br>        / ...

  7. java动态代理Proxy

    package com.gz_06; public interface StudentDao { public void login(); public void regist(); } packag ...

  8. java 动态代理 Proxy.newProxyInstance 使用心法

    使用JDk的Proxy类的静态方法newProxyInstance ,让JVM自动生成一个新的类,类中包含了inerfaces参数中的所有方法,每个方法都调用h.invoke 方法       AOP ...

  9. Proxy Pattern(Java动态代理和cglib的实现)

    代理模式:给某一个对象提供代理对象,由代理对象控制具体对象的引用. 代理,指的就是一个角色对表另一个角色采取行动,就生活中,一个红酒厂商,是不会直接把红酒零销给客户的,都是通过代理完成他的销售业务.而 ...

随机推荐

  1. Android开发环境部署:JDK+Android Studio

    1. 刚开始接触Android开发,首先需要为你的电脑安装java JDK(Java开发工具包),不管是用Eclipse还是Android Studio都需要只吃Java语言运行吧. 官网:Oracl ...

  2. shell位置参数和专用参数举例

  3. 安装python及编辑工具PyCharm

    win10下安装python环境,安装编辑工具PyCharm 1.安装 pythonpython安装包下载地址https://www.python.org/ftp/python/3.8.0/pytho ...

  4. Ansible 和 Playbook 暂存

    Ansible  和  Playbook 暂存 , 也是一个批量管理工具 自动化的批量管理工具 主机清单  HOST Inventory 模块插件  Playbooks 查看ansible的目录结构 ...

  5. SNAT 和 DNAT

    SNAT是原地址转换,DNAT是目标地址转换. SNAT 内部地址要访问公网上的服务时,内部地址会主动发起连接,将内部地址转换成公有ip.网关这个地址转换称为SNAT 企业内部的主机A想访问互联网上的 ...

  6. 安卓8.0真机运行appium1.4遇到的问题:运行自动化脚本,手机自动安装 settings.apk和unclock.apk,执行脚本时提示安装UnicodeIME-debug.apk失败,怎么关掉自动安装?

    运行自动化脚本,手机自动安装 settings.apk和unclock.apk,执行脚本时提示安装UnicodeIME-debug.apk失败,怎么关掉自动安装? 这3个apk的目录分别是: D:\P ...

  7. Unity NGUI Download

    { 链接:https://pan.baidu.com/s/1hPf4brhN8RvcKP7HSwphHw  提取码:0iim }

  8. 运行go代码

    go运行go代码 现在,让我们通过创建一个简单的示例,开启我们的go学习旅程,并学习如何编译和执行go程序.打开你最喜欢的文本编辑器,输入以下代码: package main func main() ...

  9. 在使用element-ui搭建的表格中,实现点击"定位"按钮后,屏幕滚动到对应行的位置

    背景: 一个后台管理系统,当管理员登录之后,会存在一个自己的id值, 在一个表格中,当点击"定位"按钮后,屏幕滚动到拥有管理员id的这一行,并且给设置一个高亮的背景 相关知识点: ...

  10. API函数ShellExecute与ShellExecuteEx用法

    ShellExecute: 1.函数功能:你可以给它任何文件的名字,它都能识别出来并打开它.2.函数原型: HINSTANCE ShellExecute( HWND hwnd, LPCTSTR lpO ...