代理模式是指对原有的类上增加新功能时增加一个代理类完成新的功能,以保证程序的开闭原则。代理模式分为静态代理和动态代理。动态代理又有两种实现方式:jdk的动态代理(Spring的Aop默认实现)和Cglib。
- 静态代理
假如你周末早上起来要去吃午饭,室友见你要去吃午饭,想让你帮他打包。这里室友就是被代理类,你就是代理类。室友和你都是人都要吃饭(person)
// 你和室友共同实现Person接口
public interface Person {
public void lunch();
}
public class Roomie implements Person{
@Override
public void lunch() {
System.out.println("室友中午想吃午饭了,让你给打包");
}
}
当你在打包回来的路上,室友给你发微信在楼下买瓶可乐
public class RoomieProxy implements Person{
private Person roomie;
public void setRoomie(Person roomie) {
this.roomie = roomie;
}
@Override
public void lunch() {
roomie.lunch();
System.out.println("室友打电话说顺便买个快乐水");
}
}
测试
public class Test {
public static void main(String[] args) {
RoomieProxy roomieProxy = new RoomieProxy();
Person roomie = new Roomie();
roomieProxy.setRoomie(roomie);
roomieProxy.lunch();
}
}
控制台输出
这样就可以在不改变原有的代码上,使用静态代理完成了新的需求。但是,每当有新的类需要被代理时就需要新建一个代理类出来,比如另外一个也需求被代理。这一就会显得很繁琐。
private Person xxx;
public void setRoomie(Person xxx) {
this.xxx= xxx;
}
- 动态代理(Jdk)
还是刚才的那个例子
用Jdk动态代理模式主要有两个类:InvocationHandler 、Proxy,实现InvocationHandler 这个接口,这个类中有一个被代理的对象target,InvocationHandler 中有一个invoke方法,invoke方法执行被代理对象(target)相应的方法。
public interface Person {
public void lunch();
}
public class RomInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target){
this.target = target;
}
public Object getTarget(Object target){
this.target = target;
Object proxyObject = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
return proxyObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理");
Object result = method.invoke(target, args);
System.out.println("动态代理结束");
return result;
}
}
public class Test2 {
public static void main(String[] args) {
Roomie roomie = new Roomie();
RomInvocationHandler romInvocationHandler = new RomInvocationHandler();
romInvocationHandler.setTarget(roomie);
Person roomieProxy = (Person)romInvocationHandler.getTarget(roomie);
// 跳到romInvocationHandler所在的involve方法
roomieProxy.lunch();
}
}
控制台输出
- Clig实现动态代理
使用Clig来实现动态代理要先导入asm包
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>9.0</version>
</dependency>
创建一个要被代理的类
public class PersonT {
public void eat(){
System.out.println("人要吃饭");
}
}
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Object target;
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib代理钱");
Object result = method.invoke(target, args); // target:目标对象, args 参数
System.out.println("cglib代理结束");
return result;
}
public PersonT getCglibProxy(Object target){
this.target = target; // 为目标对象赋值
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(target.getClass());
// 设置回调
enhancer.setCallback(this);
// 创建并返回代理对象
Object result = enhancer.create();
return (PersonT)result;
}
}
public class Test {
public static void main(String[] args) {
// 实例化代理对象
CglibProxy cglibProxy = new CglibProxy();
// 获取代理对象
PersonT cglibProxy1 = cglibProxy.getCglibProxy(new PersonT());
cglibProxy1.eat();
}
}
控制台输出
在Spring的Aop中:
- 如果目标对象实现了接口,默认采用jdk动态代理实现Aop
- 如果目标对象没有实现接口,必须使用Cglib,Spring会自动在JDK的动态代理和Cglib中自动转换。