定义
为另一个对象提供一个替身或占位符以控制对这个对象的访问。
代理模式可以分为静态代理和动态代理。
- 静态代理:由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了
- 动态代理:在程序运行时通过反射机制动态创建
主要角色
- Subject 抽象主题:声明了目标对象和代理对象的共同接口
- RealSubject 具体主题:也叫做被委托角色、被代理角色。是业务逻辑的具体执行者。
- Proxy 代理主题角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。
静态代理
Subject 抽象主题:
/**
* 学生服务接口
* 提供学生查询功能
*/
public interface StudentService {
String getStudentById(long id);
}
RealSubject 具体主题,即实际登录逻辑
/**
*/
public class StudentServiceImpl implements StudentService{
// 查询用户名
Map<long,String> map = new HashMap<>();
@Override
public String getStudentById(long id) {
return map.get(id);
}
}
Proxy 代理主题,操作目标对象
public class StudentServiceProxy implements StudentService{
private StudentService studentService;
public StudentServiceProxy(StudentService studentService) {
this.studentService = studentService;
}
@Override
public void userLogin() {
System.out.println("查询前业务逻辑...");
studentService.getStudentById();
System.out.println("查询后业务逻辑...");
}
}
可以看出,这个代理是由程序员手动指定创建的,灵活性较差,可以使用动态代理来进行调用。
在使用代理时u,可以直接:
public class Client {
public static void main(String[] args) {
StudentService studentService = new StudentServiceImpl();
StudentServiceProxy studentServiceProxy = new StudentServiceProxy(studentService);
studentServiceProxy.getStudentById(1L);
}
}
动态代理
JDK的Proxy
JDK自带的动态代理InvocationHandler位于JDK反射包下,其作用是在实现JDK动态代理的时候提供了动态执行增强逻辑的方法。
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(Object object) {
this.object = object;
}
/**
*
* @param proxy 代理对象
* @param method 目标对象方法
* @param args 目标对象参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("查询前业务逻辑...");
Object result = method.invoke(object, args);
System.out.println("查询后业务逻辑...");
return result;
}
}
在调用的时候:
public class Client {
public static void main(String[] args) {
StudentService studentService = new StudentServiceImpl();
StudentService dynamicProxy = (StudentService) Proxy.newProxyInstance(StudentService.class.getClassLoader(), new Class[]{StudentService.class}, new DynamicProxyHandler(studentService));
dynamicProxy.getStudentById(1L);
}
}
这样,InvocationHandler实现了无侵入式的增加被代理类的功能,Proxy.newProxyInstance利用传入参数,运行时动态生成被代理类的实际对象。
用到了Proxy.newProxyInstance()函数,官方文档其参数为:
/**
*
* loader - the class loader to define the proxy class
* interfaces - the list of interfaces for the proxy class to implement
* h - the invocation handler to dispatch method invocations to
* @return
* a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* 具有代理类的指定调用处理程序的代理实例,该代理类由指定的类装入器定义并实现指定的接口
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
Cglib代理
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(final Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("查询前业务逻辑...");
Object result = methodProxy.invokeSuper(object, args);
System.out.println("查询后业务逻辑...");
return result;
}
}
调用的时候:
public class Client {
public static void main(String[] args) {
StudentService studentService = new StudentServiceImpl();
CglibProxy cglibProxy = new CglibProxy();
StudentServiceImpl instance = (StudentServiceImpl) cglibProxy.getInstance(studentService);
instance.getStudentById(1L);
}
}
优缺点
JDK自带的Proxy优点:
- InvocationHandler接口的实现类可以复用
- 可以在不修改原来代码的基础上,就在原来代码的基础上做操作,即AOP,面向切面编程
缺点:
- 只能针对接口生成代理,不能只针对某一个类生成代理
使用Cglib代理,创建代理对象时所花费的时间却比JDK多得多,但是Cglib创建的动态代理对象比JDK创建的动态代理对象的性能更高。
参考
《Head First 设计模式》(十三):代理模式
07 代理模式:帮我拿一下快递
官方文档 java.lang.reflect-Class Proxy
Java动态代理之InvocationHandler最简单的入门教程