1. 简介
1.1 定义
为另一个对象提供一个替身或占位符以访问这个对象。
1.2 模式角色
- Subject 抽象主题角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
- RealSubject 具体主题角色:也叫做被委托角色、被代理角色。是业务逻辑的具体执行者。
- Proxy 代理主题角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。
1.3 角色关系图
1.4 模式分类
- 静态代理:由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
- 动态代理:在程序运行时通过反射机制动态创建的。
2. 代码示例
2.1 静态代理
Subject 抽象主题:
package com.jbp.designpattern.proxy;
/**
* @ClassName: UserService
* @description: 用户服务
* @author: JiangBeiPing
* @create: 2021-09-09 16:04
* @Version: 1.0
**/
public interface UserService {
void userLogin();
}
RealSubject 具体主题:
package com.jbp.designpattern.proxy;
/**
* @ClassName: UserServiceImpl
* @description: 用户服务实现类
* @author: JiangBeiPing
* @create: 2021-09-09 16:06
* @Version: 1.0
**/
public class UserServiceImpl implements UserService{
@Override
public void userLogin() {
System.out.println("用户开始登陆...");
}
}
Proxy 代理主题:
package com.jbp.designpattern.proxy;
/**
* @ClassName: UserServiceProxy
* @description: 用户服务代理类
* @author: JiangBeiPing
* @create: 2021-09-09 16:07
* @Version: 1.0
**/
public class UserServiceProxy implements UserService{
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public void userLogin() {
System.out.println("登陆之前...");
userService.userLogin();
System.out.println("登陆之后...");
}
}
2.2 动态代理
Proxy 代理主题:
package com.jbp.designpattern.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @ClassName: DynamicProxyHandler
* @description: 动态代理处理器
* @author: JiangBeiPing
* @create: 2021-09-09 16:17
* @Version: 1.0
**/
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(Object object) {
this.object = object;
}
@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;
}
}
2.3 Cglib代理代理
Proxy 代理主题:
package com.jbp.designpattern.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @ClassName: CglibProxy
* @description: Cglib代理
* @author: JiangBeiPing
* @create: 2021-09-09 16:25
* @Version: 1.0
**/
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;
}
}
测试:
package com.jbp.designpattern.proxy;
import java.lang.reflect.Proxy;
/**
* @ClassName: Client
* @description: 测试
* @author: JiangBeiPing
* @create: 2021-09-09 16:09
* @Version: 1.0
**/
public class Client {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
// 无代理
userService.userLogin();
System.out.println("代理之后...");
// 静态代理
UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
userServiceProxy.userLogin();
// 动态代理
UserService dynamicProxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserService.class}, new DynamicProxyHandler(userService));
dynamicProxy.userLogin();
// Cglib代理
CglibProxy cglibProxy = new CglibProxy();
UserServiceImpl instance = (UserServiceImpl) cglibProxy.getInstance(userService);
instance.userLogin();
}
}
3. 总结
3.1 静态代理
优点:
- 可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:
- 要为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
3.2 动态代理
优点:
- 需要的类少了。
- 代理内容也就是InvocationHandler接口的实现类可以复用。
- 可以在不修改原来代码的基础上,就在原来代码的基础上做操作,即AOP,面向切面编程。
缺点:
- 只能针对接口生成代理,不能只针对某一个类生成代理。
3.3 CGLIB代理
优点:
- CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高。
缺点:
- CGLIB创建代理对象时所花费的时间却比JDK多得多。
- CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。