代理模式是一个非常常见的模式,他有非常多的变种。
比如说远程代理,他可以让操作远程对象像操作本地对象一样的容易
还有虚拟代理,他主要是代理一些加载特别缓慢的对象,先展示给用户的是一个代理对象,然后异步加载真正的对象,来提高系统的响应速度
代理模式的变种是在是太多了,今天我主要给大家介绍的是保护代理,我分别用静态代理和动态代理两种方式去实现保护代理
我们都知道,对于系统的升级最好要对原先的代码零入侵,也就是说不修改原来的代码,代理模式就做到了这一点。
我们先来看看以下场景:假如有一个Person类,我们实现了他的get/set方法,但是呢,有一些时候,我们需要为这些接口设置一些权限,不是每个人都能去修改访问的,这种情况下,我们就可以用到保护代理来进行权限控制了。
我们假设有三个属性,姓名,年龄,成绩,看自己没有修改成绩的权限,看别人没有所有修改的权限
我们先看看静态代理是如何实现的:
我们实现了一个自己的代理,一个别人的代理,来看看代码
public interface Person {
String getName();
void setName(String name) throws IllegalAccessException;
int getAge();
void setAge(int age) throws IllegalAccessException;
float getGrade();
void setGrade(float grade) throws IllegalAccessException;
}
public class PersonImp implements Person {
private String name;
private int age;
private float grade;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getGrade() {
return grade;
}
public void setGrade(float grade) {
this.grade = grade;
}
}
public class OwnerProxy implements Person{
Person person;
public OwnerProxy(Person person) {
this.person = person;
}
@Override
public String getName() {
return person.getName();
}
@Override
public void setName(String name) throws IllegalAccessException {
person.setName(name);
}
@Override
public int getAge() {
return person.getAge();
}
@Override
public void setAge(int age) throws IllegalAccessException {
person.setAge(age);
}
@Override
public float getGrade() {
return person.getGrade();
}
@Override
public void setGrade(float grade) throws IllegalAccessException {
throw new IllegalAccessException("没有修改权限");
}
}
public class NonOwnerProxy implements Person{
Person person;
public NonOwnerProxy(Person person) {
this.person = person;
}
@Override
public String getName() {
return person.getName();
}
@Override
public void setName(String name) throws IllegalAccessException {
throw new IllegalAccessException("没有修改权限");
}
@Override
public int getAge() {
return person.getAge();
}
@Override
public void setAge(int age) throws IllegalAccessException {
throw new IllegalAccessException("没有修改权限");
}
@Override
public float getGrade() {
return person.getAge();
}
@Override
public void setGrade(float grade) throws IllegalAccessException {
throw new IllegalAccessException("没有修改权限");
}
}
写一个测试方法
public static void main(String[] args) throws IllegalAccessException {
Person person=new PersonImp();
person.setName("张三");
person.setAge(18);
person.setGrade(99);
Person ownerProxy=new OwnerProxy(person);
System.out.println(ownerProxy.getGrade());
//ownerProxy.setGrade(11);
Person nonOwnerProxy=new NonOwnerProxy(person);
nonOwnerProxy.setAge(11);
}
运行结果:
99.0
Exception in thread "main" java.lang.IllegalAccessException: 没有修改权限
at proxypattern.staticproxy.NonOwnerProxy.setAge(NonOwnerProxy.java:33)
at proxypattern.staticproxy.Main.main(Main.java:21)
其实静态代理所做的事情非常简单,就是拿到要代理的对象,然后把他封装进代理类中,然后重写其所有方法,有权限的就委托给被代理对象,没权限就抛出异常。
我们来看看动态代理是怎么做的(JDK的动态代理)
其实动态代理是在运行时生成一个代理类,而静态代理的代理类是实现编译好的,因此动态代理更具有弹性。
我们来看看代码,Person 和 PersonImp几乎和静态代理的一样,我就不赘述了
public class NonOwnerHandler implements InvocationHandler{
Person person;
//注入被代理对象
public NonOwnerHandler(Person person) {
this.person = person;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过反射,得到方法名称,看看是否有操作权限,没有就抛出异常,有就通过反射调用
if (method.getName().startsWith("set"))
throw new IllegalAccessException("没有修改权限");
else
return method.invoke(person,args);
}
}
public class OwnerHandler implements InvocationHandler{
Person person;
public OwnerHandler(Person person) {
this.person = person;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("setGrade"))
throw new IllegalAccessException("没有修改权限");
else
return method.invoke(person,args);
}
}
//用简答工厂封装一下,方便使用
public class NonOwnerProxyFactory {
public static Person getNonOwnerProxy(Person person){
//返回代理对象(实质是通过字节码的重组生成新的类)
return (Person) Proxy.newProxyInstance(person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new NonOwnerHandler(person));
}
}
public class OwnerProxyFactory {
public static Person getNonOwnerProxy(Person person){
return (Person) Proxy.newProxyInstance(person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerHandler(person));
}
}
写个测试类
public static void main(String[] args) {
Person person=new PersonImp();
person.setName("张三");
person.setAge(18);
person.setGrade(99);
Person nonOwner=NonOwnerProxyFactory.getNonOwnerProxy(person);
System.out.println(nonOwner.getAge());
nonOwner.setAge(1);
运行结果:
18
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.setAge(Unknown Source)
at proxypattern.dynamicproxy.Main.main(Main.java:18)
Caused by: java.lang.IllegalAccessException: 没有修改权限
at proxypattern.dynamicproxy.NonOwnerHandler.invoke(NonOwnerHandler.java:22)
... 2 more
cglib的动态代理和JDK的原理都是一样的,都是运行时通过字节码的重组生成新的类,但是cglib是通过继承被代理对象的类生成新的代理类,而JDK则是通过与被代理对象实现相同的接口实现代理类。
代理模式的用途非常的广泛,总的来说就是控制访问,然后基于你的访问来制订访问策略。
PS:源码地址