Java 23种设计模式之代理模式
一:简介
设计模式分为三大类:
创建型模式,共五种:工厂方法模式(已讲过)、抽象工厂模式(已讲过)、单例模式(已讲过)、建造者模式(已讲过)、原型模式(已讲过)。
结构型模式,共七种:适配器模式(已讲过)、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式(已讲过)、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
代理模式(Proxy)是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能
。
Java代理分为静态代理和动态代理和Cglib代理
二:静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
接口类AdminService 接口
这是一个目标
public interface AdminService {
//我定义一个用户服务权限接口
//定义了一个更新方法
void update();
//定义了一个查找方法,查找返回数据
Object find();
}
实现类AdminServiceImpl
/**静态接口的实现类*/
public class AdminServiceImpl implements AdminService{
@Override
public void update() {
System.out.println("我已经更新了系统");
}
@Override
public Object find() {
System.out.println("我已经查看了系统");
return new Object();
}
}
最关键的代理类AdminServiceProxy ,静态代理也要实现目标接口AdminService
public class AdminServiceProxy implements AdminService {
private AdminService adminService;
public AdminServiceProxy(AdminService adminService) {
this.adminService= adminService;
}
@Override
public void update() {
System.out.println("判断用户是否有权限进行update操作");
adminService.update();
System.out.println("记录用户执行update操作的用户信息、更改内容和时间等");
}
@Override
public Object find() {
System.out.println("判断用户是否有权限进行find操作");
System.out.println("记录用户执行find操作的用户信息、查看内容和时间等");
return adminService.find();
}
}
调用
//创建一个目标对象
AdminService adminService=new AdminServiceImpl();
//构建一个代理对象
AdminServiceProxy proxy= new AdminServiceProxy(adminService);
//调用更新
proxy.update();
System.out.println("=============================");
proxy.find()
//结果
System.out: 判断用户是否有权限进行update操作
System.out: 我已经更新了系统
System.out: 记录用户执行update操作的用户信息、更改内容和时间等
System.out: =============================
System.out: 判断用户是否有权限进行find操作
System.out: 记录用户执行find操作的用户信息、查看内容和时间等
System.out: 我已经查看了系统
总结:
静态代理模式在不改变目标对象的前提下,实现了对目标对象的功能扩展。
不足:静态代理实现了目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。
三:JDK 动态代理
为解决静态代理对象必须实现接口的所有方法的问题,Java给出了动态代理,动态代理具有如下特点:
1.Proxy对象不需要implements接口;
2.Proxy对象的生成利用JDK的Api,在JVM内存中动态的构建Proxy对象。需要使用java.lang.reflect.Proxy类的
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param 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
*/
static Object newProxyInstance(ClassLoader loader, Class>[] interfaces,InvocationHandler invocationHandler );
方法,方法参数说明:
a.ClassLoader loader:指定当前target对象使用类加载器,获取加载器的方法是固定的;
b.Class>[] interfaces:target对象实现的接口的类型,使用泛型方式确认类型
c.InvocationHandler invocationHandler:事件处理,执行target对象的方法时,会触发事件处理器的方法,会把当前执行target对象的方法作为参数传入。
接口类AdminService 接口
这是一个目标
public interface AdminService {
//我定义一个用户服务权限接口
//定义了一个更新方法
void update();
//定义了一个查找方法,查找返回数据
Object find();
}
实现类AdminServiceImpl ,这是一个目标对象的实现类
/**静态接口的实现类*/
public class AdminServiceImpl implements AdminService{
@Override
public void update() {
System.out.println("我已经更新了系统");
}
@Override
public Object find() {
System.out.println("我已经查看了系统");
return new Object();
}
}
动态代理和静态代理区别,动态代理不用实现目标接口,通过Jdk中的newProxyInstance,动态生成,动态代理
package com.ruan.mygitignore;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class AdminServiceDynamicProxy {
//维护一个目标对象
private Object target;
//维护一个事件处理,执行target对象的方法时,会触发事件处理器的方法,会把当前执行target对象的方法作为参数传入。
private InvocationHandler invocationHandler;
public AdminServiceDynamicProxy(Object target, InvocationHandler invocationHandler) {
this.target = target;
this.invocationHandler = invocationHandler;
}
public Object getPersonProxy(){
//target.getClass().getClassLoader():获取目标对象的使用类加载器
//target.getClass().getInterfaces():获取目标对象实现的接口的类型
//invocationHandler,是构造函数传入的,那我们就要想怎么创建这个InvocationHandler
Object obj= Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),invocationHandler);
return obj;
}
上面说了invocationHandler需要我们传入那我们如何生成它了AdminServiceInvocation 实现接口InvocationHandler
package com.ruan.mygitignore;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class AdminServiceInvocation implements InvocationHandler {
private Object target;
public AdminServiceInvocation(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("判断用户是否有权限进行操作");
//最关键的就是这个invoke(target):方法的注入
Object obj =method.invoke(target);
System.out.println("记录用户执行操作的用户信息、更改内容和时间等");
return obj;
}
}
调用
//方法一
System.out.println("============ 方法一 ==============");
//构建目标对象
AdminService adminService=new AdminServiceImpl();
System.out.println("代理的目标对象:" + adminService.getClass());
//创建事件处理
AdminServiceInvocation adminServiceInvocation=new AdminServiceInvocation(adminService);
//构建代理对象
AdminServiceDynamicProxy adminServiceDynamicProxy = new AdminServiceDynamicProxy(adminService,adminServiceInvocation);
//获取代理的目标对象
AdminService proxy= (AdminService) adminServiceDynamicProxy.getPersonProxy();
System.out.println("代理对象:" + proxy.getClass());
Object obj = proxy.find();
System.out.println("find 返回对象:" + obj.getClass());
System.out.println("----------------------------------");
proxy.update();
//结果
System.out: ============ 方法一 ==============
2021-07-26 16:43:24.602 12407-12407/com.ruan.mygitignore I/对象:class com.ruan.mygitignore.AdminServiceImpl
System.out: 代理对象:class $Proxy1
System.out: 判断用户是否有权限进行操作
System.out: 我已经查看了系统
System.out: 记录用户执行操作的用户信息、更改内容和时间等
System.out: find 返回对象:class java.lang.Object
System.out: ----------------------------------
System.out: 判断用户是否有权限进行操作
System.out: 我已经更新了系统
System.out: 记录用户执行操作的用户信息、更改内容和时间等
//方法二,是把getPersonProxy()方法拿到外面直接调用,不分装了
System.out.println("============ 方法二 ==============");
AdminService target = new AdminServiceImpl();
AdminServiceInvocation invocation = new AdminServiceInvocation(target);
AdminService proxy2 = (AdminService)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),invocation);
Object obj2 = proxy2.find();
System.out.println("find 返回对象:" + obj2.getClass());
System.out.println("----------------------------------");
proxy2.update();
//方法三,连InvocationHandler也不封装了,直接调用时候使用传递
System.out.println("============ 方法三 ==============");
final AdminService target3 = new AdminServiceImpl();
AdminService proxy3 = (AdminService) Proxy.newProxyInstance(target3.getClass().getClassLoader(), target3.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("判断用户是否有权限进行操作");
Object obj = method.invoke(target3, args);
System.out.println("记录用户执行操作的用户信息、更改内容和时间等");
return obj;
}
});
Object obj3 = proxy3.find();
System.out.println("find 返回对象:" + obj3.getClass());
System.out.println("----------------------------------");
proxy3.update();
四:Cglib代理
Cglib不能在Android中使用
JDK动态代理要求target对象是一个接口的实现对象,假如target对象只是一个单独的对象,并没有实现任何接口,这时候就会用到Cglib代理(Code Generation Library),即通过构建一个子类对象,从而实现对target对象的代理,因此目标对象不能是final类(报错),且目标对象的方法不能是final或static(不执行代理功能)。
Cglib依赖的jar包
cglib
cglib
3.2.10
Cglib对于Android来说,Android生成的.dex文件而不是.class文件需要另外实现方式
目标对象类AdminCglibService.java
public class AdminCglibService {
public void update() {
System.out.println("修改管理系统数据");
}
public Object find() {
System.out.println("查看管理系统数据");
return new Object();
}
}
代理类AdminServiceCglibProxy.java
public class AdminServiceCglibProxy implements MethodInterceptor {
private Object target;
public AdminServiceCglibProxy(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类代理对象
return en.create();
}
public Object intercept(Object object, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
System.out.println("判断用户是否有权限进行操作");
Object obj = method.invoke(target);
System.out.println("记录用户执行操作的用户信息、更改内容和时间等");
return obj;
}
}
调用
AdminCglibService target = new AdminCglibService();
AdminServiceCglibProxy proxyFactory = new AdminServiceCglibProxy(target);
AdminCglibService proxy = (AdminCglibService)proxyFactory.getProxyInstance();
System.out.println("代理对象:" + proxy.getClass());
Object obj = proxy.find();
System.out.println("find 返回对象:" + obj.getClass());
System.out.println("----------------------------------");
proxy.update();
//结果
代理对象:class com.lance.proxy.demo.service.AdminCglibService$$EnhancerByCGLIB$$41b156f9
判断用户是否有权限进行操作
查看管理系统数据
记录用户执行操作的用户信息、更改内容和时间等
find 返回对象:class java.lang.Object
----------------------------------
判断用户是否有权限进行操作
修改管理系统数据
记录用户执行操作的用户信息、更改内容和时间等
END:当你觉得你到达了顶峰,其实可能才到达另一座山峰的谷底