代理模式
一、概述
代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求,同时代理模式便于扩展目标对象功能的特点也为多人所用。
二、图形描述
三、代码实现
静态代理,由于比较简单,所有把代码都合到一起了:
// 需要被代理的接口
public interface BussinessInterface {
void execute();
}
// 基础实现类
public class Bussiness implements BussinessInterface {
@Override
public void execute() {
System.out.println("柯贤铭在做生意~~~");
}
}
// 在基础实现类的基础之上,封装一层方法
public class BussinessProxy implements BussinessInterface {
private BussinessInterface bussinessImpl;
public BussinessProxy(BussinessInterface bussinessImpl) {
this.bussinessImpl = bussinessImpl;
}
@Override
public void execute() {
System.out.println("前拦截...");
bussinessImpl.execute();
System.out.println("后拦截...");
}
}
// 测试类,真正使用的时候,我们采用增强之后的实现类
public class TestAgent {
public static void main(String[] args) {
BussinessInterface bussinessInterface = new Bussiness();
BussinessInterface newBuss = new BussinessProxy(bussinessInterface);
newBuss.execute();
}
}
效果截图:
静态总结:
优点:可以做到不对目标对象进行修改的前提下,对目标对象进行功能的扩展和拦截。
缺点:因为代理对象,需要实现与目标对象一样的接口,会导致代理类十分繁多,不易维护,同时一旦接口增加方法,则目标对象和代理类都需要维护。
动态代理模式,由于代码量较少,我也融合到一起:
// 定义接口
public interface UserService {
void saveUser();
}
// 定义接口实现类及方法
public class UserServiceImpl implements UserService {
@Override
public void saveUser() {
System.out.println("调用 saveUser() 方法");
}
}
// 定义代理工具类
public class MyProxyUtil {
public static UserService getProxyByJDK(UserService service) {
// 参数:目标对象的类加载器,目标对象的接口,代理对象的执行处理器
UserService userService = (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("记录日志-开始");
Object obj = method.invoke(service, args);
System.out.println("记录日志-结束");
return obj;
}
});
return userService;
}
}
// 测试类
public class Test {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 生成代理对象
UserService proxy = MyProxyUtil.getProxyByJDK(userService);
// 调用目标对象方法
userService.saveUser();
System.out.println("===================================");
// 调用代理对象方法
proxy.saveUser();
}
}
测试截图:
动态代理的总结
优点:代理对象无需实现接口,免去了编写很多代理类的烦恼,同时接口增加方法也无需再维护目标对象和代理对象,只需在事件处理器中添加对方法的判断即可。
缺点:代理对象不需要实现接口,但是目标对象一定要实现接口,否则无法使用JDK动态代理。
CGLib 动态代理
CGLib 动态代理相对于 JDK 动态代理局限性就小了很多,目标对象不需要实现接口,底层是通过继承目标对象产生代理子对象
代码,只是工具类方法多了一个:
public static UserService getProxyByCglib(UserService service) {
// 创建增强器
Enhancer enhancer = new Enhancer();
// 设置需要增强的类的对象
enhancer.setSuperclass(UserServiceImpl.class);
// 设置回调方法
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long begin = System.currentTimeMillis();
System.out.println("记录程序开始");
Object object = methodProxy.invokeSuper(o, args);
long end = System.currentTimeMillis();
System.out.println("记录程序结束");
return object;
}
});
UserService userService = (UserService) enhancer.create();
return userService;
}
测试代码:
public class TestCglib {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 生成代理对象
UserService proxy = MyProxyUtil.getProxyByCglib(userService);
// 调用目标对象方法
userService.saveUser();
System.out.println("===================================");
// 调用代理对象方法
proxy.saveUser();
}
}
测试结果:
总结:
Cglib代理: 针对类来实现代理,对指定目标 产生一个子类 通过方法拦截技术拦截所有父类方法的调用。 我们要使用cglib代理必须引入 cglib的jar包
三种代理模式进行总的分析概括:
代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。
而AOP,是通过动态代理实现的。
一、简单来说:
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
二、Spring在选择用JDK还是CGLiB的依据:
(1)当Bean实现接口时,Spring就会用JDK的动态代理
(2)当Bean没有实现接口时,Spring使用CGlib是实现
(3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
三、CGlib比JDK快?
(1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
(2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。