jdk动态代理与cglib动态代理
Posted liqiliang1437
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdk动态代理与cglib动态代理相关的知识,希望对你有一定的参考价值。
动态代理
动态代理23种设计模式之一
动态代理的基于反射的
代理模式:
作用:
1)功能增强:在原有的功能上,增加了额外的功能,做了功能增强
2)控制访问:代理不让用户访问目标,比如4S店不让我直接找厂家
开发中,如果A类本来是调用C类的方法,完成某个功能
但是C不让A调用
A不能访问C,然后在A和C之间创建一个B代理类
C让B访问
B是代理类,既能访问C,同时又能增加新的业务功能,增强对C的访问
A通过访问B,B访问C,到了A访问C的结果,虽然是间接访问
有点类似用户和厂家的关系
比如说小李想买辆迈凯伦,但是小李不能直接联系到迈凯伦的厂家,厂家并不提供个人业务,那怎么办呢?只能通过4S店了,
我们去4S店买,4S店帮我们去和厂家订车,提车,并且还能给我们提供优质的服务。所以迈凯伦4S店就好比代理类,不仅帮我们去间接找到了厂家,
还对业务进行增强,为小李提供服务
实现代理的模式:
-
静态代理:
? 1)代理类是自己实现的,自己创建一个java类,表示代理类
? 2)并且要代理的目标类也是确定的
-
动态代理
? 1)程序执行过程中,使用JDK的反射机制,创建代理类对象,并动态指定要代理的目标类
动态代理的实现分类:
- JDK动态代理:使用jdk反射包中的类和接口实现动态代理的功能
- cglib动态代理:第三方的工具库,cglib的原理是继承,通过继承目标类,创建他的子类,在子类总重写父类中同名的方法,实现功能的修改
因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final的
cglib的要求目标类比较宽松,只要能继承就可以了
cglib在很多的框架中使用,比如mybatis,spring框架中都有使用
区别:
JDK动态代理要求目标类必须有接口
复习method方法
public interface Hello { void sayHello(String name); }
public class HelloImpl implements Hello {
@Override
public void sayHello(String name) {
System.out.println("你好!"+name);
}
}
public class Test01 {
@Test
public void test01(){
Hello hello = new HelloImpl();
hello.sayHello("小李");
/*
你好!小李
*/
}
public class Test01 {
@Test
public void test01(){
//通过反射机制来执行sayHello方法
Hello hello = new HelloImpl();
try {
Method method = Hello.class.getDeclaredMethod("sayHello", String.class);
method.invoke(hello,"启亮");
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
你好!启亮
*/
Object obj : 对象,要执行这个对象的方法
Object... args : 方法执行的参数值
Method是一个独立的方法对象,代表所有sayHello方法的对象,通过invoke就可以执行任意对象的sayHello方法,然后通过invoke中参数指定具体执行那个对象中的sayHello方法,和执行方法锁需要的参数
jdk动态代理的实现
反射包 java.lang.reflect;里面有三个类
InvocationHandler
Method
Proxy
jdk动态代理的使用
1)Proxy类:
? 核心的对象,创建代理对象。之前创建对象都是new类的构造方法
? 现在是使用Proxy类的方法,代替new的使用
方法:newProxyInstance
原型:
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h
){ ... }
参数:
ClassLoader loader,
类加载器(目标对象的类加载器),负责向内存中加载对象,使用反射获取对象的ClassLoader
Class<?>[] interfaces,
接口,目标对象实现的接口,也是反射获取的
InvocationHandler h
我们自己写的,代理类要完成的功能
返回值:就是代理的对象
2)newProxyInstance
InvocationHandler接口
源码:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable; }
InvocationHandler: 译为:调用处理程序
invoke:表示代理对象要执行的功能代码,代理类要完成的功能就写在这个方法中
完成的功能:1.调用目标方法,执行目标方法的功能
2.功能增强,在目标方法调用时,增加功能
参数说明:
proxy:jdk创建的代理对象,无需赋值
method:目标类中的方法 ,由jdk提供method对象的
args:目标类中方法的参数,由jdk提供
InvocationHandler接口:表示你的代理要干什么
怎么使用:
? 1.既然是接口,先实现接口InvocationHandler
? 2.重写invoke方法,原来静态代理中代理类完成的功能写在这里
3)Method类:
? 表示方法的,具体来说就是目标类中的方法
? 作用:通过Method可以执行某个目标类的方法,Method.invoke();
Method.invoke() ; 中的invoke只是Method类中的方法
public Object invoke(...,...)是InvocationHandler接口中的方法
Method.invoke(目标对象,方法的参数);
通过这个目标对象就可以执行对象的方法,而不需要知道这个方法的名称,因为method方法的值是jdk帮你确定的,这个对象本身就是jdk提供的
动态代理实现步骤
1.创建接口,定义目标类要完成的功能
2.创建目标类实现接口
3.创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
? ● 调用目标方法
? ● 增强功能
4.使用Proxy类的静态方法,创建代理对象,并把返回值转换为接口类型
代码实现:
User.java
public interface User {
void add();
void delete();
void update();
void find();
}
UserImpl.java
public class UserImpl implements User {
@Override
public void add() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("删");
}
@Override
public void update() {
System.out.println("改");
}
@Override
public void find() {
System.out.println("查");
}
}
UserHandler.java
//必须实现InvocationHandler接口,完成代理类要做的功能
//1.调用目标方法 2.功能增强
public class UserHandler implements InvocationHandler {
private Object target = null;//目标对象
//动态代理:目标对象是动态的,不是固定的
//传入的哪个对象,就给哪个对象创建代理
public UserHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
target:里面这个对象是动态的,需要传入
result:是方法执行的返回值,没有可以不加
*/
System.out.println("前增强");//增强方法
Object result = method.invoke(target,args);//传入的对象执行的每个方法,都会调用这个
System.out.println("后增强");//增强方法
//返回方法执行的返回值
return result;
}
}
MyProxy.java
public class MyProxy {
@Test
public void test() {
//1.创建目标对象
User user = new UserImpl();
//2.创建InvocationHandler对象
InvocationHandler handler = new UserHandler(user);
//3.创建代理对象
User userProxy = (User) Proxy.newProxyInstance(
user.getClass().getClassLoader(),//获得这个对象的类加载器才能操作这个对象
user.getClass().getInterfaces(),
handler
);
//4.把类型转换成代理对象的接口类型,因为目标对象实现了这个接口
//5.通过代理对象执行方法
userProxy.add();
System.out.println("=========");
userProxy.delete();
System.out.println("=========");
userProxy.update();
System.out.println("=========");
userProxy.find();
}
}
打印结果:
前增强
增
后增强
=========
前增强
删
后增强
=========
前增强
改
后增强
=========
前增强
查
后增强
匿名内部类实现:
User.java
public interface User {
void add();
void delete();
void update();
void select();
}
UserImpl.java
public class UserImpl implements User {
@Override
public void add() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("删");
}
@Override
public void update() {
System.out.println("改");
}
@Override
public void select() {
System.out.println("查");
}
}
MyProxyTest.java
public class MyProxyTest {
private User user = new UserImpl();
@Test
public void test01(){
user.add();
user.delete();
user.update();
user.select();
System.out.println("================");
User proxy = (User) Proxy.newProxyInstance(
user.getClass().getClassLoader(),//被代理对象的类加载器
user.getClass().getInterfaces(),//获取被代理对象所实现的接口,(有共同的特征)
new InvocationHandler() { //就是具体实现代理的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("A");
//所有代理对象调用的方法都会执行
Object invoke = method.invoke(user, args);
System.out.println("B");
return invoke;//返回给代理对象,有返回值就是此返回值
}
});
proxy.add();
System.out.println("*******");
proxy.delete();
System.out.println("*******");
proxy.update();
System.out.println("*******");
proxy.select();
}
}
*
cglib动态代理的实现
动态代理:
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
基于接口的动态代理 jdk
基于子类的动态代理 cglib
基于子类的动态代理:
涉及的类:Enhancer
提供者:第三方cglib库
如何创建代理对象:
使用Enhancer类中的create方法
创建代理对象的要求:
被代理类不能是最终类
create方法的参数:
Class:字节码
它是用于指定被代理对象的字节码。
Callback:用于提供增强的代码
它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
此接口的实现类都是谁用谁写。
我们一般写的都是该接口的子接口实现类:MethodInterceptor
依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
案例:
Producer.java
/**
* 一个生产者
*/
public class Producer {
/**
* 销售
* @param money
*/
public void saleProduct(float money){
System.out.println("销售产品,并拿到钱:"+money);
}
/**
* 售后
* @param money
*/
public void afterService(float money){
System.out.println("提供售后服务,并拿到钱:"+money);
}
}
测试类:
Client.java
/**
* 模拟一个消费者
*/
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 执行被代理对象的任何方法都会经过该方法
* @param proxy
* @param method
* @param args
* 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
* @param methodProxy :当前执行方法的代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
cglibProducer.saleProduct(10000f);
}
}
//销售产品,并拿到钱:8000.0
以上是关于jdk动态代理与cglib动态代理的主要内容,如果未能解决你的问题,请参考以下文章