代理模式
定义:是一种结构型的代理模式。给某一个对象提供一个代理对象,由代理对象控制对原对象的引用。用户可通过远程代理对象来实现对远程原对象的操作。
模式角色:
- 抽象主题角色:声明了真实主题和代理主题的共同接口,保证了任何使用真实主题的地方都可以使用代理主题。客户端针对抽象主题进行编程。
- 代理主题角色:内部包含着真实主题对象作为属性成员,用于调用真实主题对象中的操作。不仅如此,还会在此基础上执行一些代理主题独有的操作。
- 真实主题角色:实现真实的业务操作。
优点:
- 保护代理可以控制客户操作权限
- 虚拟代理使用小对象代表大对象,减少系统资源的消耗,提升运行速度。
缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
代理模式类型:
根据使用目的进行划分
列举常用几种
- 远程代理,实现本地调用者与远程被调用者之间的正常交互。
- 虚拟代理,先用小对象来代替资源消耗巨大的对象,真实对象只在需要的时候才被创建。
- 保护代理,给被调用者提供访问控制,确认调用者的权限。
- 智能引用代理,代理对象引用真实对象中的操作时,同时包含着一些代理独有的额外的操作。
- 同步化代理:使得几个用户同时使用一个真实对象不会发生冲突。
根据实现方式进行划分
- 静态代理
- 动态代理
静态代理
实例说明:
- 保护代理的静态实现
这里演示某一网站注册用户和未注册用户的权限管理。用户必须注册后才能进行一些功能的操作,否则只有查看功能权限。
AbstractPermission
/**
* 抽象主题角色
*/
public interface AbstractPermission {
/**
* 查看帖子功能
* 所有用户功能
*/
void look();
/**
* 评论功能
* 注册用户功能
*/
void comment();
/**
* 发布帖子功能
* 注册用户功能
*/
void publish();
/**
* 注册功能
* 未注册用户实现
*/
void register(boolean flag);
}
RegisteredUser
public class RegisteredUser implements AbstractPermission {
@Override
public void look() {
System.out.println("查看帖子");
}
@Override
public void comment() {
System.out.println("发出评论");
}
@Override
public void publish() {
System.out.println("发出帖子");
}
@Override
public void register(boolean flag) {
}
}
UnRegisteredUser
public class UnRegisteredUser implements AbstractPermission{
private boolean flag=false;
private AbstractPermission registerUser=new RegisterUser();
@Override
public void look() {
registerUser.look();
}
@Override
public void comment() {
if(!flag)
{
System.out.println("无账号不能评论,是否立即注册!");
}else
{
registerUser.comment();
}
}
@Override
public void publish() {
if(!flag)
{
System.out.println("无账号不能发布帖子,是否立即注册!");
}else
{
registerUser.publish();
}
}
@Override
public void register(boolean flag) {
this.flag=flag;
}
}
TestUser
public class TestUser {
public static void main(String[] args) {
AbstractPermission user=new UnRegisteredUser();
user.look();
user.publish();
user.comment();
System.out.println("注册一个账号");
user.register(true);
user.look();
user.publish();
user.comment();
/*结果如下:
查看帖子
无账号不能发布帖子,是否立即注册!
无账号不能评论,是否立即注册!
注册一个账号
查看帖子
发出帖子
发出评论
*
*/
}
}
动态代理
如果是静态代理的话,真实角色必须事先存在,一个真实主题角色必须对应一个代理主题角色,且真实主题角色作为代理主题角色的内部成员属性,这将导致系统中类的个数急剧增加。所以动态代理解决了事先不知道真实主题角色的情况下,如何使用代理主题角色的问题。
JDK
使用java.lang.reflect包中的类和接口实现动态代理功能
1、 InvocationHander
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
proxy表示目标类
method表示目标类中的目标方法
args表示目标方法中的入参
其中invoke()
方法中表示代理对象需要执行的功能代码,包括
(1)执行目标类中的方法
(2)功能增强,在目标方法调用时,增加一些额外的功能
2、 Method
目标类中的方法对象,可通过反射机制获得
Class clazz = A.class;
// 方法名 方法参数类型
Method method = clazz.getMethod("ok",String.class);
getMethod(String name, Class<?>... parameterTypes)
作用:获取类对象中的某个方法
参数:(1)目标方法名称 (2)目标方法的参数类型,可变长度类型
得到目标方法对象后,执行该目标方法
method.invoke(new A(),"bai");
invoke(Object obj, Object... args)
参数:(1)目标对象 (2)目标方法的参数值,可变长度类型
3、 Proxy
内部提供了 newProxyInstance()
用于创建代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
loader:目标对象的类的类加载器
interfaces:目标对象的类实现的接口
h:代理对象完成的功能,自行定义
实例讲解
步骤如下
- 创建接口,定义抽象方法
- 创建目标类,实现接口
- 创建InvocationHandler接口的实现类,在invoke方法中实现代理类的功能
(1)调用目标方法
(2)增强功能 - 使用Proxy获得代理对象
public interface AbstractPermission {
/**
* 查看帖子功能
* 所有人权限
*/
void look();
/**
* 评论功能
* VIP可用
*/
void comment();
/**
* 发布帖子功能
* VIP可用
*/
void publish();
}
public class RegisteredUser implements AbstractPermission {
@Override
public void look() {
System.out.println("查看帖子");
}
@Override
public void comment() {
System.out.println("发出评论");
}
@Override
public void publish() {
System.out.println("发出帖子");
}
}
public class ProxyHandler implements InvocationHandler {
private boolean isRegister = false;
private Object targetObj;
// 动态传入目标对象
public ProxyHandler(Object obj){
this.targetObj = obj;
}
public void register(){
this.isRegister = true;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(!isRegister){
System.out.println("未注册,无权限执行");
return null;
}
return method.invoke(targetObj,args);
}
}
public class TestUser {
public static void main(String[] args) {
// 1、创建目标对象
AbstractPermission user = new RegisteredUser();
// 2、InvocationHandler 实现代理对象增强功能
InvocationHandler h = new ProxyHandler(user);
// 3、创建代理对象
AbstractPermission proxyObj = (AbstractPermission)Proxy.newProxyInstance(user.getClass().getClassLoader(),
user.getClass().getInterfaces(),h);
proxyObj.publish();
System.out.println("----------------进行注册---------------");
((ProxyHandler) h).register();
proxyObj.publish();
}
}
思考:如何对目标对象中的不同方法做不同的代理增强?
CGLib
cglib是第三方工具库,创建代理对象
实例讲解
参考书籍:
设计模式.刘伟.胡志刚.郭克华.清华大学出版社