Java的动态代理
功能增强的方式:
子类继承父类,重写父类方法
装饰者模式(包装类,静态代理),在包装类里进行功能增强
代理模式(动态代理),在代理类里进行功能增强
装饰者模式的使用步骤:
创建一个包装类,要求 和目标类实现相同的接口:保证有共同的方法
包装类里要有成员变量(目标对象),通过构造方法设置进去
包装类里不需要增强的方法:直接调用目标对象的方法
包装类里需要增强的方法:编写增强的功能代码
- 什么是代理
代理:如果不能、不方便直接调用目标对象的时候,可以通过代理对象,间接调用目标对象
两种代理方式:
静态代理:代理类是提前创建好的(装饰者模式)
动态代理:代理类不存在,我们代码在运行过程中,动态生成的代理类、代理对象,之后再调用代理对象 - Java的动态代理
2.1 API
注意:
哪怕没有目标对象,这个方法也可以给我们生成一个接口的实现类对象(没有被代理对象了,所有功能要由代理类来完成)
这个生成代理对象的方法,是基于接口的
如果没有接口,又想要某对象的代理对象,可以使用一个包:cglib
Object proxyInstance = Proxy.newProxyInstance(
ClassLoader loader,
Class[] interfaces,
InvocationHandler h
);
参数:
loader:类加载器,通常是从目标对象里得到:目标对象.getClass().getClassLoader()
interfaces:代理类要实现的接口,通常也可以从目标对象得到:目标对 象.getClass().getInterfaces()
h:代理类的方法里,要执行的内容(要做的事情)
通常是创建一个匿名内部类:InvocationHandler,接口里只有一个方法invoke
new InvocationHandler(){
/*
proxy:代理对象
method:执行的方法(我们调用代理对象时,所调用的方法Method对象)
args:方法需要的实参(我们调用代理对象的方法时,传递的实参)
*/
public Object invoke(Object proxy, Method method, Object[] args){
//可以调用目标对象的method方法
Object result = method.invoke(目标对象, args);
//我们调用代理对象方法时,代理对象给我们返回的结果
return result;
}
}
InvocationHandler匿名内部类,接口里的方法invoke中,是动态代理的功能部分,在方法里对目标对象的控制优先于代理对象在外边的控制,并且在方法中,相当于每个目标对象对应的方法是在invoke方法中循环出现一样(如此理解),要增强目标对象的方法,可以通过方法名来判断,来进行功能增强(分前后).
返回值:一定是我们提供的接口的一个实现类对象,是代理对象
动态代理常用的有两种方式
基于接口的动态代理
提供者:JDK官方的Proxy类。
要求:被代理类最少实现一个接口。
基于子类的动态代理
提供者:第三方的CGLib,如果报asmxxxx异常,需要导入asm.jar。
要求:被代理类不能用final修饰的类(最终类)。
1.使用JDK官方的Proxy类创建代理对象
创建ProxyProducer接口,代码如下:
public interface ProxyProducer {
/**
* 销售商品
* @param money
*/
public void saleProduct(Float money);
/**
* 售后服务
* @param money
*/
public void afterService(Float money) ;
}
创建Producer类实现ProxyProducer接口,代码如下:
public class Producer implements ProxyProducer {
/**
* 销售商品
* @param money
*/
public void saleProduct(Float money) {
System.out.println("销售商品,金额是:"+money);
}
/**
* 售后服务
* @param money
*/
public void afterService(Float money) {
System.out.println("提供售后服务,金额是:"+money);
}
}
创建JDK动态代理,代码如下:
public class Consumer {
public static void main(String[] args) {
//创建对象实例
Producer producer = new Producer();
producer.saleProduct(5000f);
producer.afterService(1000f);
/***
* 基于动态代理改造
* 动态代理:
* 特点:字节码随用随创建,随用随加载
* 分类:基于接口的动态代理,基于子类的动态代理
* 作用:不修改源码的基础上对方法增强
* 基于接口的动态代理:
* 提供者是:JDK官方
* 使用要求:被代理类最少实现一个接口。
* 涉及的类:Proxy
* 创建代理对象的方法:newProxyInstance
* 方法的参数:
* ClassLoader:类加载器。用于加载代理对象的字节码。和被代理对象使用相同的类加载器。固定写法。
* Class[ ]:字节码数组。用于给代理对象提供方法。和被代理对象具有相同的方法。和被代理对象实现相同的接口,就会具有相同的方法。固定写法
* InvocationHanlder:要增强的方法。此处是一个接口,我们需要提供它的实现类。通常写的是匿名内部类。
* 增强的代码随用随写。
*/
ProxyProducer proxyProducer = (ProxyProducer) Proxy.newProxyInstance(
Producer.class.getClassLoader(),
Producer.class.getInterfaces(),
new InvocationHandler() {
/**
* 执行被代理对象的任何方法都都会经过该方法,该方法有拦截的作用
* 参数的含义
* Object proxy:代理对象的引用。一般不用
* Method method:当前执行的方法
* Object[] args:当前方法所需的参数
* 返回值的含义
* 和被代理对象的方法有相同的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法参数
Float money = (Float) args[0];
//获取方法名字
String methodName = method.getName();
//定义一个对象,存储返回结果
Object result = null;
//销售提成25%
if(methodName.equals("saleProduct")){
result= method.invoke(producer,money*0.75f);
}else if(methodName.equals("saleProduct")){
//销售提成10%
result= method.invoke(producer,money*0.9f);
}
return result;
}
}
);
proxyProducer.saleProduct(1000f);
proxyProducer.afterService(1000f);
}
}
2.使用CGLib的Enhancer类创建代理对象
还是那个例子,只不过不让他实现接口,此时不能使用JDK的动态代理了,我们可以使用CGLib动态代理,首先我们需要引入依赖。
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
</dependencies>
修改Producer,不让它实现任何接口,代码如下:
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);
}
}
创建Consumer,使用CGLib动态代理,代码如下:
public class Consumer {
public static void main(String[] args) {
//创建对象实例
Producer producer = new Producer();
producer.saleProduct(5000f);
producer.afterService(1000f);
/**
* 动态代理:
* 特点:字节码随用随创建,随用随加载
* 分类:基于接口的动态代理,基于子类的动态代理
* 作用:不修改源码的基础上对方法增强
* 基于子类的动态代理
* 提供者是:第三方cglib包,在使用时需要先导包(maven工程导入坐标即可)
* 使用要求:被代理类不能是最终类,不能被final修饰
* 涉及的类:Enhancer
* 创建代理对象的方法:create
* 方法的参数:
* Class:字节码。被代理对象的字节码。可以创建被代理对象的子类,还可以获取被代理对象的类加载器。
* Callback:增强的代码。通常都是写一个接口的实现类或者匿名内部类。
* 我们在使用时一般都是使用Callback接口的子接口:MethodInterceptor
*/
Producer proxyProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//方法参数
Float money = (Float) args[0];
//获取方法名字
String methodName = method.getName();
//定义一个对象,存储返回结果
Object result = null;
//销售提成25%
if(methodName.equals("saleProduct")){
result= method.invoke(producer,money*0.75f);
}else if(methodName.equals("saleProduct")){
//销售提成10%
result= method.invoke(producer,money*0.9f);
}
return result;
}
});
proxyProducer.saleProduct(1000f);
proxyProducer.afterService(1000f);
}
}