目录
1.场景模拟
1.1 声明接口
package com.spring.proxy;
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
1.2创建带日志功能的实现类
package com.spring.proxy;
public class CalculatorImpl implements Calculator{
public int add(int i, int j) {
System.out.println("[日志] add 方法开始了,参数是: " + i + ", " + j);
int result=i+j;
System.out.println("方法内部:result="+result);
System.out.println("[日志] add 结果是: " + result);
return result;
}
public int sub(int i, int j) {
System.out.println("[日志] sub 方法开始了,参数是: " + i + ", " + j);
int result=i-j;
System.out.println("方法内部:result="+result);
System.out.println("[日志] sub 结果是: " + result);
return result;
}
public int mul(int i, int j) {
System.out.println("[日志] mul 方法开始了,参数是: " + i + ", " + j);
int result=i*j;
System.out.println("方法内部:result="+result);
System.out.println("[日志] mul 结果是: " + result);
return result;
}
public int div(int i, int j) {
System.out.println("[日志] div 方法开始了,参数是: " + i + ", " + j);
int result=i/j;
System.out.println("方法内部:result="+result);
System.out.println("[日志] div 结果是: " + result);
return result;
}
}
1.3提出问题
①现有代码缺陷
针对带日志功能的实现类,我们发现有如下缺陷:
- 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
- 附加功能分散在各个业务功能方法中,不利于统一维护
②解决思路
解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。
③困难
解决问题的困难:要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。所以需要引入新的技术。
2.代理模式
2.1概念
①介绍
二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。
- 代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。
- 目标:被代理“套用”了非核心逻辑代码的类、对象、方法。


3.静态代理
package com.spring.proxy;
public class CalculatorImpl implements Calculator{
public int add(int i, int j) {
int result=i+j;
System.out.println("方法内部:result="+result);
return result;
}
public int sub(int i, int j) {
int result=i-j;
System.out.println("方法内部:result="+result);
return result;
}
public int mul(int i, int j) {
int result=i*j;
System.out.println("方法内部:result="+result);
return result;
}
public int div(int i, int j) {
int result=i/j;
System.out.println("方法内部:result="+result);
return result;
}
}
package com.spring.proxy;
public class CalculatorStaticProxy implements Calculator{
private CalculatorImpl target;
public CalculatorStaticProxy(CalculatorImpl target) {
this.target = target;
}
public int add(int i, int j) {
System.out.println("[日志] add 方法开始了,参数是: " + i + ", " + j);
int result = target.add(i, j);
System.out.println("[日志] add 结果是: " + result);
return result;
}
public int sub(int i, int j) {
System.out.println("[日志] sub 方法开始了,参数是: " + i + ", " + j);
int result = target.sub(i, j);
System.out.println("[日志] sub 结果是: " + result);
return result;
}
public int mul(int i, int j) {
System.out.println("[日志] mul 方法开始了,参数是: " + i + ", " + j);
int result = target.mul(i, j);
System.out.println("[日志] mul 结果是: " + result);
return result;
}
public int div(int i, int j) {
System.out.println("[日志] div 方法开始了,参数是: " + i + ", " + j);
int result = target.div(i, j);
System.out.println("[日志] div 结果是: " + result);
return result;
}
}
静态代理后,目标类中无日志功能,但将日志功能实现在了加减乘除中,实现了解耦。
静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来
说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代
码,日志功能还是分散的,没有统一管理。
提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理
类来实现。这就需要使用动态代理技术了。
4.动态代理
package com.spring.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
//此类不是真正的代理类,它可以帮助生成目标类的代理类,就是一个工具类
//可生成任意目标类所对应的代理类
public class ProxyFactory {
//目标类型不确定
private Object target;
//有参构造
public ProxyFactory(Object target) {
this.target = target;
}
//jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口
public Object getProxy(){
/**
* newProxyInstance():创建一个代理实例
* 其中有三个参数:
* ClassLoader loader:加载动态生成的代理类的类加载器
* Class[] interfaces:目标对象实现的所有接口的class对象所组成的数组
* InvocationHandler h:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法
*/
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler h=new InvocationHandler() {
//这个invoke是代理对象如何执行方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy表示代理对象;method表示代理对象需要实现的方法,即其中需要重写的方法;args表示要执行的方法到的参数列表
System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
//这个invoke是目标对象如何执行
Object result = method.invoke(target, args);
System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
return result;
}
};
//Proxy.newProxyInstance()里有三个参数
return Proxy.newProxyInstance(classLoader,interfaces,h);
}
}
可进一步优化:
package com.spring.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler h=new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
result = method.invoke(target, args);
System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
} catch (Exception e) {
System.out.println("[动态代理][日志] "+method.getName()+",异常:"+ e);
e.printStackTrace();
} finally {
System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
}
return result;
}
};
return Proxy.newProxyInstance(classLoader,interfaces,h);
}
}
测试类:
import com.spring.proxy.Calculator;
import com.spring.proxy.CalculatorImpl;
import com.spring.proxy.CalculatorStaticProxy;
import com.spring.proxy.ProxyFactory;
import org.junit.Test;
public class ProxyTest {
/**
* 动态代理有两种:
* 1、jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口
* 在com.sun.proxy包下,类名为$proxy2
* 2、cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下
*/
@Test
public void testProxy(){
/*CalculatorStaticProxy proxy=new CalculatorStaticProxy(new CalculatorImpl());
proxy.add(1,2);*/
ProxyFactory proxyFactory=new ProxyFactory(new CalculatorImpl());
//不知道代理类proxy的类型,但是可用它的接口Calculator表示
Calculator proxy = (Calculator) proxyFactory.getProxy();
proxy.add(1,2);
}
}
可以点个免费的赞吗!!!
本文含有隐藏内容,请 开通VIP 后查看