一.什么是代理模式?
客户端不直接和目标对象(实际对象)发生交互,或不直接调用目标对象,而是通过调用代理对象来间接的调用目标对象的设计模式。
二.作用(为什么要使用代理模式)
当客户端不想直接访问目标对象或访问目标对象有困难时,需要通过代理对象进行间接访问在不改变目标对象源代码的情况下,通过代理对目标对象的功能进行增强。如添加日志、事务控制,权限控制,鉴权登录等
三.代理模式分类
1.静态代理(了解)
静态代理,代理类和i能代理某一特定的目标对象,并且对目标对象进行增强。
public interface Ticket {
void buyTicket();
}
/**
* 12306 直接购买
*/
public class By12306TicketImpl implements Ticket{
@Override
public void buyTicket() {
System.out.println("12306 买票");
}
}
/**
* 通过黄牛购买
*/
public class HuangNiuTicketImpl implements Ticket {
private Ticket ticket;
public void setTicket(Ticket ticket) {
this.ticket = ticket;
}
@Override
public void buyTicket() {
System.out.println("黄牛开始买票");
if (ticket!=null){
ticket.buyTicket();
}
System.out.println("黄牛买票成功");
}
}
测试类
public class Test {
public static void main(String[] args) {
// 自己买票
Ticket ticket = new By12306TicketImpl();
ticket.buyTicket();
// 通过黄牛买票,最终票的来源还是12306
HuangNiuTicketImpl huangNiu = new HuangNiuTicketImpl();
huangNiu.setTicket(ticket);
huangNiu.buyTicket();
}
}
2.动态代理(重要)
动态代理,代理对象可以代理很多类型的目标对象,并且对目标对象进行增强
java中动态代理有JDK和CGLIB两种方式
四、基于JDK接口的动态代理(面试必会)
1.JDK动态代理步骤:
实现InvocationHandler接口创造代理对象通过反射执行目标对象方法
2.注意点
应用场景:基于java jdk实现只能代理实现接口的目标对象,如果没有接口,则报错
搞清楚Proxy.newProxyInstance三个参数的意思返回的
代理对象是接口,目标对象实现的那个接口当代理对象调用对应的接口方法时 会回调传递第三个参数 InvocationHandler 对象内的 invoke 方法,通过反射执行
public class JdkProxyTest1 {
public static void main(String[] args) {
//12306 目标对象 因为作为了匿名内部类参数的缘故,所以用final修饰
final TicketBy12306Impl t12306 = new TicketBy12306Impl();
//创建一个代理对象
//ClassLoader loader, 类加载器
//Class<?>[] interfaces, 目标对象实现的接口,代理目标对象必须实现接口
// t12306.getClass().getInterfaces() 获取目标对象实现的接口
// InvocationHandler 当代理对象执行接口的方法时 会回调InvocationHandler的invoke
Ticket proxyTicket = (Ticket) Proxy.newProxyInstance(t12306.getClass().getClassLoader(), t12306.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理对象开始调用 目标对象");
//让目标对象执行方法 并且获取返回结果
Object result = method.invoke(t12306, args);
System.out.println("代理对象结束调用 目标对象");
return result;
}
});
String str = proxyTicket.bubTicket();
System.out.println("str:" + str);
}
}
3.封装工具类
/**
* 专门用于产生JDK代理对象
*/
public class JdkProxy implements InvocationHandler {
//目标对象
private Object target;
public Object createProxy(final Object target){
this.target = target;
//ClassLoader loader,指定当前目标对象使用类加载器,我们知道获取加载器的写法是固定的
//Class<?>[ ] interfaces,目标对象实现的接口的类型,使用泛型方式确认类型,我们知道要想知道一个类是否实现某个接口,可以使用 Object.getClass().getInterfaces() ,这个方法是获取类是否实现接口,如果此对象表示一个类,则返回值是一个数组,它包含了表示该类所实现的所有接口的对象。
//InvocationHandler h,事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
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;
}
}
五、cglib基于类的动态代理
cglib基于asm字节码生成框架,用于动态生成代理类。
重点:
应用场景:cglib可以代理类和接口,
返回的代理对象是其子类cglib 直接调用生成的类的方法,而jdkproxy是通过反射,返回效率差
1.cglib与jdk的区别(重点)?
1. jdk实现代理,目标对象必须实现接口
cglib是根据目标对象/目标类生成代理类的字节码文件,不要求实现接口
2.在生产代理类的效率上,cglib效率差 因为要生成字节码文件 jdk效率高
3.在代理方法执行效率上来说。 cglib直接执行class文件的方法,效率高。jdk通过反射实现,效率低
2.实现
public class CglibProxyTest1 {
public static void main(String[] args) {
//创建cglib代理对象需要工具类
//1.引入jar包
Enhancer enhancer = new Enhancer();
//传递目标类 cglib的原理:生成的代理类是目标类的子类
enhancer.setSuperclass(TicketBy12306Impl.class);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 代理开始调用 目标对象");
//让目标对象执行
//o 目标对象
//object 目标对象执行方法需要的参数
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("cglib-result:"+result);
System.out.println("cglib 代理结束调用 目标对象");
return null;
}
});
//创建代理对象 目标对象可以不创建 cglib会自己创建目标
TicketBy12306Impl ticket = (TicketBy12306Impl) enhancer.create();
ticket.bubTicket();
}
}
3.封装工具类
/**
* cglib 创建代理对象的工具类
*/
public class CglibProxy implements MethodInterceptor {
public Object createProxy(Class superClass){
Enhancer enhancer = new Enhancer();
// 传递目标类 cglib 的原理是生成的代理类是 目标类的子类
enhancer.setSuperclass(superClass);
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 代理开始调用 目标对象");
// 让目标 对象执行
// o 目标对象
// objects 目标对象执行方法需要的惨呼
Object result = methodProxy.invokeSuper(o,objects);
System.out.println("cglib-result:"+result);
System.out.println("cglib 代理结束调用 目标对象");
return result;
}
}
public class CglibProxyTest2 {
public static void main(String[] args) {
//生成动态代理的工具类
CglibProxy cglibProxy = new CglibProxy();
//12306代理对象
TicketBy12306Impl ticketBy12306Proxy = (TicketBy12306Impl) cglibProxy.createProxy(TicketBy12306Impl.class);
ticketBy12306Proxy.bubTicket();
System.out.println("*********************");
//第二个代理对象
RealSubject realSubjectProxy = (RealSubject) cglibProxy.createProxy(RealSubject.class);
realSubjectProxy.action();
}
}