介绍: 在不修改现有的功能到的代码的基础上,实现现有功能的增强(不修改核心功能的代码,但实现核心功能的增强),符合程序的开闭原则;原来功能进行了封装后不动,可以完成功能的升级
静态代理模式
实现了可以不对原来代理对象进行修改,且实现原有功能的增强
程序的开发阶段:分开实现,原有的核心功能和需要增强的功能分开实现
程序的运行阶段:需要将分开的功能进行整合
静态代理的构成:
代理接口
目标类(target)
代理类(proxy)拥有目标类对象的引用
客户端(client)需要使用代理类的对象,具备原来的核心功能和增强功能
注: 代理类和目标类,实现同一个接口
案例
代理接口:
public interface UserService {
/**
* 实现用户身份的验证
*/
Boolean checkUsers(String uname,String upwd);
}
目标类:
public class UserServiceImpl implements UserService {
/**
* 核心功能
* 实现用户的效验
*/
@Override
public Boolean checkUsers(String uname, String upwd) {
// TODO Auto-generated method stub
System.out.println("=====用户的身份验证 核心功能======");
if("admin".equals(uname) && "1234".equals(upwd)) {
return true;
}
return false;
}
代理类:
public class UserServiceImpl2 implements UserService {
/**
* 增强功能
*
* 实现用户登录的时候进行日志记录
*/
@Override
public Boolean checkUsers(String uname, String upwd) {
// TODO Auto-generated method stub
//声明UserService变量
UserService userService = new UserServiceImpl();
System.out.println(uname + "==在这个时间" + new Date() + " == 尝试登录系统");
return userService.checkUsers(uname, upwd);
}
}
客户端:
public class StaticTest {
public static void main(String[] args) {
//创建对象
UserService userService2 = new UserServiceImpl2();
userService2.checkUsers("admin", "1234");
}
}
静态代理的不足:
如果接口发生变化,需要维护两个实现类,维护目标类和代理类;
需要为每一个目标类,都创建一个代理类对象,导致程序的臃肿
动态代理模式
**解决问题:**频繁的创建代理类,JDK在内存中直接产生一个class文件,通过该class文件创建代理类对象,接口发生变化,需要修改代理类,在内存中产生class文件,是在程序运行的时候,代理类实现的接口对象class文件已经加载到了内存,产生的代理类class文件中已经重写了相应的方法。
动态代理实现的相关API
Proxy:创建动态代理和代理类的实例化
public static Object newProxyInstance(ClassLoader loader,
类<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
Proxy.newProxyInstance因为与IllegalArgumentException相同的原因而Proxy.getProxyClass 。
参数
loader - 类加载器来定义代理类
interfaces - 代理类实现的接口列表
h - 调度方法调用的调用处理函数。
InvocationHandler :需要改接口实现类对象传入newProxyInstance方法
Object invoke(Object proxy,
方法 method,
Object[] args)
throws Throwable
处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
参数
proxy - 调用该方法的代理实例
method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
ClassLoader:类加载器,将编译好的class文件拷贝到内存中,是有ClassLoader实现的,我们自定义的接口,类对应的ClassLoader都是一样的
案例:
通过动态代理实现权限的控制:模拟对数据库的CRUD操作,查询不需要进行控制(查询不需要修改数据库数据),如果进行crud方法的调用,调用者如果拥有权限,直接进行数据库的操作,否则提示权限不足
PetDao 接口
public interface PetDao {
/**
* 实现对宠物的crud
* 其中 对添加 修改 删除 进行登录控制的权限效验
* 只有查询不进行效验
*/
int add();
int update();
int delete();
List<?> select();
}
核心功能类:
public class PetDaoImpl implements PetDao {
@Override
public int add() {
// TODO Auto-generated method stub
System.out.println("这个对宠物的 添加方法");
return 1;
}
@Override
public int update() {
// TODO Auto-generated method stub
System.out.println("这个对宠物的 更新方法");
return 1;
}
@Override
public int delete() {
// TODO Auto-generated method stub
System.out.println("这个对宠物的 删除方法");
return 1;
}
@Override
public List<?> select() {
// TODO Auto-generated method stub
System.out.println("这个对宠物的 查询方法");
return new ArrayList<>();
}
}
增强功能类:
public class Privilege {
/**
* 实现用户的权限效验
*/
public Boolean checkPrivilege(Integer uid) {
System.out.println("=====进行角色的权限验证=====");
if(uid == 1 || uid == 2) {
System.out.println("验证成功");
return true;
}else {
System.out.println("验证失败");
return false;
}
}
}
产生代理对象进行封装:
public class ProxyTest {
private static PetDao pet;
private static Privilege pri;
/**
* 完成属性的初始化
*/
public static void init(PetDao petDao , Privilege privilege) {
pet = petDao;
pri = privilege;
}
/**
* 定义方法产生动态 代理类的对象
*/
public static <T> T newProxyObject(Class<T> clazz) {
//通过Proxy的静态方法获得代理对象
/**
* 参数一: 类的加载器
* 参数二:指定代理对象需要实现的接口
* 参数三:拦截器对象,拦截代理对象调用的方法
*/
T t = (T)Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class[] {clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object result = null;
//获得拦截的方法名
String name = method.getName();
//获得拦截方法的返回值
Type returnType = method.getGenericReturnType();
String type = returnType.getTypeName();
if(name.startsWith("add") || name.startsWith("update") || name.startsWith("delete")) {
//调用pri对的方法,实现 增强功能
Boolean b = pri.checkPrivilege(0);
//判断
if(b) {
//放行,权限效验通过
result = method.invoke(pet, args);
}
}else {
//直接放行,不需要进行权限的效验, 即不需要执行增强功能的方法
result = method.invoke(pet, args);
}
if(type.equals("int") && result== null) {
return 0;
}
if(type.equals("java.util.List") && result == null) {
return new ArrayList<>();
}
return result;
}
});
return t;
}
public static void main(String[] args) {
//进行属性的初始化
ProxyTest.init( new PetDaoImpl(), new Privilege());
//获得代理对象
PetDao petDao = ProxyTest.newProxyObject(PetDao.class);
//通过代理对象调用方法
petDao.add();
System.out.println("----------------------------");
petDao.select();
}
}
控制台显示: