静态代理(需要一个目标类写一个代理类)
使用代理模式可以在不影响原有代码(目标类方法)的情况下,增强功能逻辑
实现步骤
1. 相同接口
public interface Caculator {
int add(int x,int y);
}
2. 目标类,实现接口
public class TargetCaculatorImpl implements Caculator{
@Override
public int add(int x, int y) {
System.out.println("目标类方法执行了!");
return x+y;
}
}
3. 代理类,实现接口
public class ProxyCaculatorImpl implements Caculator{
//持有目标类
private TargetCaculatorImpl target;
public ProxyCaculatorImpl(TargetCaculatorImpl target){
this.target = target;
}
@Override
public int add(int x, int y) {
//目标类方法之前
System.out.println("目标类方法之前:增强功能");
//执行目标类方法
int sum = targetCaculator.add(x, y);
//目标类方法之后
System.out.println("目标类方法之后:增强功能");
return sum;
}
}
4. 创建代理对象,传入目标对象
Caculator caculator = new ProxyCaculatorImpl(new TargetCaculatorImpl);
System.out.println(caculator.add(1,3));
动态代理(适用给不同类方法添加相同功能逻辑)
JDK动态代理
实现步骤
1.接口
public interface Caculator {
int add(int x,int y);
int multiply(int x,int y);
}
2.目标类,实现接口
public class TargetCaculatorImpl implements Caculator {
//方法只返回两数之和
@Override
public int add(int x, int y) {
System.out.println("目标类方法执行了");
return x+y;
}
@Override
public int multiply(int x, int y) {
System.out.println("目标类multiply方法执行了");
return x*y;
}
}
3.创建包装类,实现InvocationHandler接口,重写方法
//包装类
public class MyProxy implements InvocationHandler {
private Object target; //目标对象,任意类型
public MyProxy(Object target){
this.target = target;
}
//自定义方法:返回目标类的代理类实例对象
public Object getProxyInsatnce(){
//JDK提供的newProxyInstance(ClassLoader,Interface,InvocationHandler)方法
//获取目标类的接口信息,就可以获得目标类信息,主要是目标类方法
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
//所有Target的所有方法的调用入口(invoke)
//执行目标类的方法,在方法前后可以添加功能,不影响原有方法执行
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法前操作:");
//获得目标类方法,执行结果
Object result = method.invoke(target,args);
System.out.println("方法后操作:");
return result;
}
}
4.创建包装类,生产代理类,调用方法
//创建包装类对象
MyProxy myProxy = new MyProxy(new TargetCaculatorImpl());
//获得目标类的代理类对象
Caculator caculator = (Caculator) myProxy.getProxyInsatnce();
//执行接口方法(目标类和代理类的方法都执行)
System.out.println(caculator.add(11,3));
method.getName()
可以获取方法名最当前方法做判断,是否添加逻辑,调用时加try,catch捕获空指针异常
CGLib动态代理(无需接口)
通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。
1. 创建目标类
public class TargetCaculator {
public int add(int x,int y){
return x+y;
}
public int multiply(int x,int y){
return x*y;
}
}
2. 创建Cglib动态代理类,实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {
public Object createProxyInstance(Class<?> clazz){
//字节码增强器,可以用来为无接口的类创建代理。
//它的功能与java自带的Proxy类挺相似的。
//它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("增强");
return methodProxy.invokeSuper(o,objects);
}
}
区别
经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。
cglib执行速度略大于jdk,所以比较适合单例模式。
另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。
如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。
spring默认使用jdk动态代理,如果类没有接口,则使用cglib。