Static agents, JDK proxies, CGLIB agent study notes

0. Introduction

  Proxy pattern is a design pattern that provides additional access to the target object is a way that access to the target object through a proxy object, so you can without modifying the original target object is the premise of providing additional functionality operation, the expansion of the target object features.

1. Static Proxy

achieve

  The first step, write an interface.

public interface Mobileable {
    void updateMobile(String mobile) throws Exception;
}
复制代码

  The second step, we want to write a class and implement enhanced Mobileable interface.

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;
    }
}
复制代码

  The third step is to write a proxy class, but also to achieve Mobileable the interface and the enhanced object as a member variable.

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("执行目标方法之后,模拟结束事务 ...");
    }
}
复制代码

  The fourth step is to test.

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());
    }
}
复制代码

advantage

  1. The target object can be extended functionality without modifying the target object premise.
  2. Static proxy class generated at compile time bytecode files can be used directly, and high efficiency.

Shortcoming

  1. redundancy. Because the proxy object to be consistent with the target object's interface, will have excessive proxy class.
  2. Difficult to maintain. Once the interface method for increasing the target object and proxy object must be modified.

2. JDK agent

  JDK, also known as proxy interface agent. JDK agent is mainly achieved through the built-in JDK API. NewProxyInstance Proxy class is used under reflect package (ClassLoader loader, Class <?> [] Interfaces, InvocationHandler h) method.
  Using interceptors (interceptor must implement InvocationHanlder) plus reflection Generates a proxy class interfaces anonymous call InvokeHandler before calling a specific method to process.

achieve

  The first step, write an interface.

public interface IAnimal {
    void eat(String food);
    void speak(String language);
}
复制代码

  The second step, to implement this interface.

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);
    }
}
复制代码

  The third step is to implement a custom interceptor to achieve InvocationHandler interface.

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;
    }
}
复制代码

  The fourth step is to test.

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");
    }
}
复制代码

advantage

  1. Just InvocationHandler proxy class implement the interface. .

Shortcoming

  1. The proxy object must implement the interface, or can not use JDK proxy.

3. CGLIB agent

  CGLIB proxy, also known as sub-agent class. ASM using open source packages, class files proxy object classes loaded in, processed by modifying a subclass bytecode.

achieve

  The first step that needs to be written proxy 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;
    }
}
复制代码

  The second step, to write a proxy class implements MethodInterceptor interface.

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;
    }
}
复制代码

  The third step is to test.

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());
    }
}
复制代码

advantage

  1. cglib agents need to implement interface, by generating the proxy class bytecode, slightly faster than the reflective, performance issues.

Shortcoming

  1. cglib will inherit the target object, the method needs to be rewritten, so the audience can not be a final class.
  2. Use cglib need to introduce cglib jar package, if you already have a spring-core jar package, you do not need to introduce, because spring is included cglib.

references:

Guess you like

Origin juejin.im/post/5d595249518825240252d535