0. 前言
代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
1. 静态代理
实现
第一步,写一个接口。
public interface Mobileable {
void updateMobile(String mobile) throws Exception;
}
复制代码
第二步,写一个我们要增强的类并实现Mobileable接口。
public class Person implements Mobileable{
private String name;
private String mobile;
@Override
public void updateMobile(String mobile) {
this.mobile = mobile;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMobile() {
return mobile;
}
}
复制代码
第三步,写一个代理类,也实现Mobileable接口并把要增强的对象作为成员变量。
public class PersonProxy implements Mobileable{
private Person person;
Pattern mobilePattern = Pattern.compile("1\\d{10}");
public PersonProxy(Person person) {
this.person = person;
}
@Override
public void updateMobile(String mobile) throws Exception {
System.out.println("执行目标方法之前,模拟开始事务 ...");
if(mobile == null || "".equals(mobile)){
throw new Exception("电话号码为空");
}
Matcher m = mobilePattern.matcher(mobile);
if (m.matches()){
System.out.println("电话号码格式正确");
}else{
throw new Exception("电话号码格式错误");
}
// 执行目标方法,并保存目标方法执行后的返回值
person.updateMobile(mobile);
System.out.println("执行目标方法之后,模拟结束事务 ...");
}
}
复制代码
第四步,测试。
public class TestCase {
public static void main(String[] args){
Person person = new Person();
PersonProxy personProxy = new PersonProxy(person);
person.setName("123");
try {
// personProxy.updateMobile(null);
// personProxy.updateMobile("123");
personProxy.updateMobile("12345678901");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(person.getName());
System.out.println(person.getMobile());
}
}
复制代码
优点
- 可以在不修改目标对象的前提下扩展目标对象的功能。
- 静态代理在编译时产生class字节码文件,可以直接使用,效率高。
缺点
- 冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
- 不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
2. JDK代理
JDK代理又称接口代理。JDK代理主要是通过JDK自带的API实现的。用的是reflect包下的Proxy类的newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法。
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
实现
第一步,写一个接口。
public interface IAnimal {
void eat(String food);
void speak(String language);
}
复制代码
第二步,实现这个接口。
public class Cat implements IAnimal{ @Override public void eat(String food) { System.out.println(this.getClass().getSimpleName() + " eats " + food); } @Override public void speak(String language) { System.out.println(this.getClass().getSimpleName() + " speaks " + language); } } 复制代码
public class Dog implements IAnimal{ @Override public void eat(String food) { System.out.println(this.getClass().getSimpleName() + " eats " + food); } @Override public void speak(String language) { System.out.println(this.getClass().getSimpleName() + " speaks " + language); } } 复制代码
第三步,实现一个自定义拦截器,实现InvocationHandler接口。
public class AnimalActionHandler implements InvocationHandler {
private Object object;
public AnimalActionHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(object.getClass().getSimpleName()+"."+method.getName()+"()执行之前");
Object invoke = method.invoke(object, args);
System.out.println(object.getClass().getSimpleName()+"."+method.getName()+"()执行之后");
return invoke;
}
}
复制代码
第四步,测试。
public class TestCase {
public static void main(String[] args) {
IAnimal cat = (IAnimal) Proxy.newProxyInstance(Cat.class.getClassLoader(),
new Class[]{IAnimal.class},
new AnimalActionHandler(new Cat()));
IAnimal dog = (IAnimal) Proxy.newProxyInstance(Dog.class.getClassLoader(),
new Class[]{IAnimal.class},
new AnimalActionHandler(new Dog()));
cat.eat("fish");
cat.speak("miao");
dog.eat("bone");
dog.speak("wang");
}
}
复制代码
优点
- 代理类只需实现InvocationHandler接口。。
缺点
- 被代理对象必须要实现接口,否则不能使用JDK代理。
3. CGLIB代理
CGLIB代理又称子类代理。利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
实现
第一步,写需要被代理的类。
public class Person {
private String name;
private String mobile;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
复制代码
第二步,写一个实现了MethodInterceptor接口的代理类。
public class PersonProxyFactory implements MethodInterceptor{
private final static String SET_MOBILE = "setMobile(Ljava/lang/String;)V";
Pattern mobilePattern = Pattern.compile("1\\d{10}");
private Object target;
public PersonProxyFactory(Object target) {
this.target = target;
}
> public Object newProxyInstance(){
Enhancer en = new Enhancer();
// 设置要代理的目标类
en.setSuperclass(target.getClass());
// 设置要代理的拦截器
en.setCallback(this);
// 生成代理类的实例
return (Person) en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
String name = methodProxy.getSignature().toString();
Object rvt;
if (SET_MOBILE.equals(name)){
System.out.println("执行目标方法之前,模拟开始事务 ...");
if(objects[0] == null || "".equals(objects[0])){
throw new Exception("电话号码为空");
}
Matcher m = mobilePattern.matcher(objects[0].toString());
if (m.matches()){
System.out.println("电话号码格式正确");
}else{
throw new Exception("电话号码格式错误");
}
// 执行目标方法,并保存目标方法执行后的返回值
rvt = methodProxy.invokeSuper(o, objects);
System.out.println("执行目标方法之后,模拟结束事务 ...");
}else {
// 执行目标方法,并保存目标方法执行后的返回值
rvt = methodProxy.invokeSuper(o, objects);
}
return rvt;
}
}
复制代码
第三步,测试。
public class TestCase {
public static void main(String[] args) {
Object o = new Person();
> PersonProxyFactory personProxyFactory = new PersonProxyFactory(o);
Person person = (Person) personProxyFactory.newProxyInstance();
> // person.setName("123");
// person.setName("yellowdoge");
person.setMobile(null);
// person.setMobile("12345678901");
// System.out.println(person.getName());
System.out.println(person.getMobile());
// System.out.println(person.getClass());
}
}
复制代码
优点
- cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题。
缺点
- cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。
- 使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。
参考文献: