avatar
hqinglau的网络日志

记录Linux, 编程, 操作系统...


动态代理、IoC、AOP


动态代理

JDK动态代理

利用反射机制生成一个实现代理接口的类,在调用具体方法前用invokeHandler来处理。JDK代理只能对实现接口的类生成代理。

IWork.java

package dynamicProxy;

public interface IWork {
    void meeting();

    int evaluate(String name);
}

Leader.java

package dynamicProxy;

import java.util.Random;

public class Leader implements IWork{
    @Override
    public void meeting() {
        System.out.println("领导早上要组织会议");
    }

    @Override
    public int evaluate(String name) {
        int score = new Random(System.currentTimeMillis()).nextInt(20) + 80;
        System.out.println(String.format("领导给%s的考评为%s分", name, score));
        return score;
    }
}

Test.java

package dynamicProxy;

import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        Leader leader = new Leader();

        //proxy是被代理后生成的对象,是通过IWork接口的字节码增强方式创建的类而构造出来的,是一个临时构造的类的对象
        IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
                new Class[]{IWork.class},new WorkInvocationHandler(leader));
        proxy.meeting();
        proxy.evaluate("Joy");
        proxy.evaluate("James");
    }
}

WorkInvocationHandler.java

package dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class WorkInvocationHandler implements InvocationHandler {
    private Object object;

    public WorkInvocationHandler(Object o) {
        this.object = o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("object: "+object.getClass().getSimpleName());
        System.out.println("proxy: "+proxy.getClass().getSimpleName());

        if("meeting".equals(method.getName())) {
            System.out.println("准备材料。。");
            return method.invoke(object,args);
        } else if("evaluate".equals(method.getName())) {
            if(args[0] instanceof String) {
                if("James".equals(args[0])) {
                    System.out.println("James 不好");
                    return 70;
                }
            }
            return method.invoke(object,args);
        }
        return null;
    }
}

输出结果:

object: Leader
proxy: $Proxy0
准备材料。。
领导早上要组织会议
object: Leader
proxy: $Proxy0
领导给Joy的考评为97分
object: Leader
proxy: $Proxy0
James 不好

从main函数看起:

Leader leader = new Leader();
//proxy是被代理后生成的对象,是通过IWork接口的字节码增强方式创建的类而构造出来的,是一个临时构造的类的对象
IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
new Class[]{IWork.class},new WorkInvocationHandler(leader));
proxy.meeting();

通过类WorkInvocationHandler,我们可以动态代理Leader类方法的实现。通过Proxy的静态方法newProxyInstance来生成了一个实例对象。看下源码,解释放在注释里面:

// loader: 选用的类加载器,用哪个类加载器去加载代理对象,本文中是Leader的类加载器
// interfaces:被代理的类实现的接口,可以是多个
// h:绑定代理类的一个方法
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    // 一些判断和验证
    Objects.requireNonNull(h);
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    // 查找或生成特定的代理类,也就是会h要处理的类
    // 内部实现:如果有实现给定接口的代理类,就返回cache,
    /*
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        // 验证
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        // /* parameter types of a proxy class constructor */
        //private static final Class<?>[] constructorParams =
        //       { InvocationHandler.class };
        // getConstructor可以无参也可以有参
        // 获得代理类构造器 (接收一个 InvocationHandler 参数)
        // 获取Constructor类对象
        // 一共有4种方法,全部都在Class类中:
        // 1. getConstructors():获取类中的公共方法
        // 2. getConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型
        // 3. getDeclaredConstructors(): 获取类中所有的构造方法(public、protected、default、private)
        // 4. getDeclaredConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        // Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
        // Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

查看代理类的内容,例如这篇文章:java怎么查看jvm中动态代理class类内容

生成代理类的内容具体如下,可以看到,除了hashCodetoStringequals之外,还实现了IWork接口的两个方法。:

public final class $Proxy0 extends Proxy implements IWork {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) {
        super(var1);
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("cn.orzlinux.Bean.dynamicProxy.IWork").getMethod("meeting");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("cn.orzlinux.Bean.dynamicProxy.IWork").getMethod("evaluate", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    public final boolean equals(Object var1) {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int evaluate(String var1) {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void meeting() {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

调用则都用了InvocationHandler的invoke方法。我们已经实现了这个方法:

image-20211001144907164

invoke的参数:

  • proxy就是我们生成的$proxy0类:
IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
                new Class[]{IWork.class},new WorkInvocationHandler(leader));
  • method是$proxy类中实现的那几个方法
  • args是参数数组

这样就一目了然了:

在main方法中:

Leader leader = new Leader();
IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
new Class[]{IWork.class},new WorkInvocationHandler(leader));
proxy.meeting();

先生成代理类,代理类中除了hashCodetoStringequals之外,还实现了IWork接口的两个方法。给WorkInvocationHandler传递了一个Leader对象。例如调用proxy.meeting()

去代理类中找meeting方法,发现要到handlerinvoke方法,而invoke方法可以在leader对象的基础之上加一些准备工作。也就是输出:

准备材料。。
领导早上要组织会议

至此,整体脉络就梳理完了。

CGlib动态代理

JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口。

CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法,这样可以保证代理类拥有目标类的同名方法。

image-20211001151404831

示例

Dog.java

public class Dog {
    final public void run(String name) {
        System.out.println("dog "+name+"--- run");
    }
    public void eat() {
        System.out.println("dog --- eat");
    }
}

MyMethodInterceptro.java

public class MyMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("对目标类进行增强");
        // 方法调用,非反射
        Object Object = methodProxy.invokeSuper(o,objects);
        return objects;
    }
}

CGlibProxy.java

public class CGlibProxy {
    public static void main(String[] args) {
        // 导出cglib动态类
                    
        System.setProperty( DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\java_classes");

        // Enhancer,类似于JDK动态代理的Proxy
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Dog.class);
        enhancer.setCallback(new MyMethodInterceptor());
        //这里的creat方法就是正式创建代理类
        Dog proxyDog = (Dog) enhancer.create();
        proxyDog.eat();
    }
}

image-20211001153423814

代理类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

// 代理类继承了Dog,并且实现了Factory接口(设置回调函数和返回实例化对象的方法)
public class Dog$$EnhancerByCGLIB$$b46ed1e9 extends Dog implements Factory {
    // method 和 methodProxy
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$eat$0$Method;
    private static final MethodProxy CGLIB$eat$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    // 为目标类的每一个方法都建立索引
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("cn.orzlinux.Bean.cglib.Dog$$EnhancerByCGLIB$$b46ed1e9");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$eat$0$Method = ReflectUtils.findMethods(new String[]{"eat", "()V"}, (var1 = Class.forName("cn.orzlinux.Bean.cglib.Dog")).getDeclaredMethods())[0];
        CGLIB$eat$0$Proxy = MethodProxy.create(var1, var0, "()V", "eat", "CGLIB$eat$0");
    }

    // 调用目标类的eat方法,
    final void CGLIB$eat$0() {
        super.eat();
    }

    public final void eat() {
        // 传入的方法拦截器
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$eat$0$Method, CGLIB$emptyArgs, CGLIB$eat$0$Proxy);
        } else {
            super.eat();
        }
    }

    final void CGLIB$finalize$1() throws Throwable {
        super.finalize();
    }

    // ...

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -1574182249:
            if (var10000.equals("finalize()V")) {
                return CGLIB$finalize$1$Proxy;
            }
            break;
        case -1310345955:
            if (var10000.equals("eat()V")) {
                return CGLIB$eat$0$Proxy;
            }
            break;
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$5$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return CGLIB$equals$2$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$3$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$4$Proxy;
            }
        }

        return null;
    }

    public Dog$$EnhancerByCGLIB$$b46ed1e9() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        Dog$$EnhancerByCGLIB$$b46ed1e9 var1 = (Dog$$EnhancerByCGLIB$$b46ed1e9)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    public Object newInstance(Callback[] var1) {
        CGLIB$SET_THREAD_CALLBACKS(var1);
        Dog$$EnhancerByCGLIB$$b46ed1e9 var10000 = new Dog$$EnhancerByCGLIB$$b46ed1e9();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
        Dog$$EnhancerByCGLIB$$b46ed1e9 var10000 = new Dog$$EnhancerByCGLIB$$b46ed1e9();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        Dog$$EnhancerByCGLIB$$b46ed1e9 var10000 = new Dog$$EnhancerByCGLIB$$b46ed1e9;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        default:
            throw new IllegalArgumentException("Constructor not found");
        }
    }

    public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

调用方法一的时候,在代理类中会先判断是否实现了方法拦截的接口,没实现的话直接调用目标类的方法一;如果实现了那就会被方法拦截器拦截,在方法拦截器中会对目标类中所有的方法建立索引,其实大概就是将每个方法的引用保存在数组中,我们就可以根据数组的下标直接调用方法,而不是用反射;索引建立完成之后,方法拦截器内部就会调用invoke方法(这个方法在生成的FastClass中实现),在invoke方法内就是调用CGLIB$方法一$这种方法,也就是调用对应的目标类的方法一。一般我们要添加自己的逻辑就是在方法拦截器那里。

image-20211001154904445

什么时候用cglib什么时候用jdk动态代理?

  • 目标对象生成了接口 默认用JDK动态代理
  • 如果目标对象使用了接口,可以强制使用cglib

JDK动态代理只能对实现了接口的类生成代理,而不能针对类。Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的。

IOC

百科解释(意会一下):

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

Java类和类之间的引用错综复杂:

image-20211001162039860

引入IOC框架来解耦,维护类的声明周期和类的引用:

image-20211001162246775

IoC框架负责维护,类的使用者只管使用。实现IoC框架和代码详见:徒手撸框架--实现IoC

image-20211001173909009

BeanFactoryImpl.java

public class BeanFactoryImpl implements BeanFactory {
    private static final ConcurrentHashMap<String,Object> beanMap = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap<String,BeanDefinition> beanDefineMap= new ConcurrentHashMap<>();

    private static final Set<String> beanNameSet = Collections.synchronizedSet(new HashSet<>());

    @Override
    public Object getBean(String name) throws Exception {
        //查找对象是否已经实例化过
        Object bean = beanMap.get(name);
        if(bean != null){
            return bean;
        }
        //如果没有实例化,那就需要调用createBean来创建对象
        bean =  createBean(beanDefineMap.get(name));

        if(bean != null) {

            //对象创建成功以后,注入对象需要的参数
            populatebean(bean);

            //再吧对象存入Map中方便下次使用。
            beanMap.put(name,bean);
        }

        //结束返回
        return bean;
    }

    protected void registerBean(String name, BeanDefinition bd){
        beanDefineMap.put(name,bd);
        beanNameSet.add(name);
    }

    private Object createBean(BeanDefinition beanDefinition) throws Exception {
        String beanName = beanDefinition.getClassName();
        Class clz = ClassUtils.loadClass(beanName);
        if(clz == null) {
            throw new Exception("can not find bean by beanName");
        }
        List<ConstructorArg> constructorArgs = beanDefinition.getConstructorArgs();
        if(constructorArgs != null && !constructorArgs.isEmpty()){
            List<Object> objects = new ArrayList<>();
            for (ConstructorArg constructorArg : constructorArgs) {
                if (constructorArg.getValue() != null) {
                    objects.add(constructorArg.getValue());
                } else {
                    objects.add(getBean(constructorArg.getRef()));
                }
            }
            Class[] constructorArgTypes = objects.stream().map(it -> it.getClass()).collect(Collectors.toList()).toArray(new Class[]{});
            Constructor constructor = clz.getConstructor(constructorArgTypes);
            return BeanUtils.instanceByCglib(clz, constructor, objects.toArray());
        } else {
            return BeanUtils.instanceByCglib(clz, null, null);
        }
    }

    private void populatebean(Object bean) throws Exception {
        Field[] fields = bean.getClass().getSuperclass().getDeclaredFields();
        if (fields != null && fields.length > 0) {
            for (Field field : fields) {
                String beanName = field.getName();
                beanName = StringUtils.uncapitalize(beanName);
                if (beanNameSet.contains(field.getName())) {
                    Object fieldBean = getBean(beanName);
                    if (fieldBean != null) {
                        ReflectionUtils.injectField(field,bean,fieldBean);
                    }
                }
            }
        }
    }
}

BeanUtils.java

// 处理对象的实例化
public class BeanUtils {
    public static <T> T instanceByCglib(Class<T> clz, Constructor ctr,
                                        Object[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clz);
        //NoOp.INSTANCE,这表示一个空Callback,即如果不想对某个方法进行拦截,
        // 可以在DaoFilter中返回
        enhancer.setCallback(NoOp.INSTANCE);
        if(ctr == null) {
            return (T) enhancer.create();
        } else {
            return (T) enhancer.create(ctr.getParameterTypes(),args);
        }
    }
}

JsonApplicationContext.java

public class JsonApplicationContext extends BeanFactoryImpl {
    private String fileName;

    public JsonApplicationContext(String fileName) {
        this.fileName = fileName;
    }

    public void init(){
        loadFile();
    }

    private void loadFile(){

        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);

        // ObjectMapper提供了readValue(String content, TypeReference valueTypeRef)接口,
        // 第二个参数为new一个TypeReference的子类实例:
        // new TypeReference<List<UserResource>>(){}。
        // 泛型抽象类TypeReference用于通过子类获取完整的泛型类型信息。
        List<BeanDefinition> beanDefinitions = JsonUtils.readValue(is,new TypeReference<List<BeanDefinition>>(){});

        if(beanDefinitions != null && !beanDefinitions.isEmpty()) {

            for (BeanDefinition beanDefinition : beanDefinitions) {
                registerBean(beanDefinition.getName(), beanDefinition);
            }
        }

    }
}

测试类:

JsonApplicationContext applicationContext = new JsonApplicationContext( "application.json" );
applicationContext.init();
Robot aiRobot = (Robot) applicationContext.getBean("robot");
aiRobot.show();

运行图

image-20211001183247038

AOP

AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角。

在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)

本节参考自永顺,链接 here

术语

假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来。

  • join point --> 爪哇的小县城里的百姓: 因为根据定义, join point 是所有可能被织入 advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 join point. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人。
  • point cut --> 男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 advice, 但是我们并不希望在所有方法上都织入 advice, 而 point cut 的作用就是提供一组规则来匹配join point, 给满足规则的 join point 添加 advice. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来. 在这里 凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问。
  • advice --> 抓过来审问, advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 join point 上的. 同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸爪哇的小县城里的百姓
  • aspect: aspect 是 point cut 与 advice 的组合, 因此在这里我们就可以类比: "根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问" 这一整个动作可以被认为是一个 aspect。

AOP的工作重心在于如何将增强织入目标对象的连接点上, 这里包含两个工作:如何通过 pointcut 和 advice 定位到特定的 joinpoint 上;如何在 advice 中编写切面代码。

可以简单地认为, 使用 @Aspect 注解的类就是切面。

在 Spring 中, 所有的方法都可以认为是 joinpoint, 但是我们并不希望在所有的方法上都添加 Advice, 而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice。

例子感觉挺不错的,一堆术语放到具体的例子上就大致知道是想说什么了。

AOP的实现方式有两种:

  • AOP框架在编译阶段,就对目标类进行修改,得到的class文件已经是被修改过的。生成静态的AOP代理类(生成*.class文件已经被改掉了,需要使用特定的编译器)。以AspectJ为代表 —— 静态AOP框架。
  • AOP框架在运行阶段,动态生成AOP代理(在内存中动态地生成AOP代理类),以实现对目标对象的增强。它不需要特殊的编译器。以Spring AOP为代表。—— 动态AOP框架。

例子:http接口鉴权

需求:为某些http restful api提供权限验证功能,权限不符合时,返回报错。

设计:

  • 提供一个特殊的注解 AuthChecker,方法注解,有此注解标注的Controller需要进行权限验证。

  • 利用Spring AOP,以@annotation切点标志符来匹配有注解 AuthChecker所标注的 joinpoint。

  • 在 advice 中, 简单地检查调用者请求中的 Cookie 中是否有我们指定的 token, 如果有, 则认为此调用者权限合法, 允许调用, 反之权限不合法, 范围错误。

AuthChecker.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthChecker {

}

HttpAOPAdviseDefine.java

@Component
@Aspect
public class HttpAOPAdviseDefine {
    // 定义一个 Pointcut, 使用 切点表达式函数 来描述对哪些 Join point 使用 advise.
    // 之前例子中的找出七尺五寸的男子, 这里是找出AuthChecker标注的方法
    @Pointcut("@annotation(cn.orzlinux.AuthChecker)")
    public void pointcut() {

    }
    
    // 要把这些男子抓起来审问(对符合标注方法的动作,验证cookie)
    // 定义advise
    @Around("pointcut()")
    public Object checkAuth(ProceedingJoinPoint joinPoint) throws Throwable {
        // 在Web开发中,service层或者某个工具类中需要获取到HttpServletRequest对象还是比较常见的。
        // 一种方式是将HttpServletRequest作为方法的参数从controller层一直放下传递,
        // 不过这种有点费劲,且做起来不是优雅;
        //还有另一种则是RequestContextHolder,直接在需要用的地方使用如下方式
        //HttpServletRequest即可,使用代码如下:
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();

        // 检查用户所传递的 token 是否合法
        String token = getUserToken(request);
        if (!token.equalsIgnoreCase("123456")) {
            return "错误, 权限不合法!";
        }
        // 如果授权是 true 则 调用 方法( joinPoint.proceed(); )
        return joinPoint.proceed();
    }
    private String getUserToken(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return "";
        }
        for (Cookie cookie : cookies) {
            if (cookie.getName().equalsIgnoreCase("user_token")) {
                return cookie.getValue();
            }
        }
        return "";
    }
}

测试:

image-20211001231427282

加上cookie之后:

image-20211001232034785

解决中文???问题

在配置文件中的mvc:annotation-driven中添加如下代码:

<mvc:annotation-driven >
    <!-- 消息转换器 -->
    <mvc:message-converters register-defaults="true">
      <bean class="org.springframework.http.converter.StringHttpMessageConverter">
        <property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
      </bean>
    </mvc:message-converters>
  </mvc:annotation-driven>

有什么乱七八糟的地方找不到类,试试这个:

image-20211001230041534

参考

静态代理和动态代理

CGLib动态代理

你真的完全了解Java动态代理吗?看这篇就够了

彻底征服 Spring AOP 之 理论篇

彻底征服 Spring AOP 之 实战篇

徒手撸框架--实现IoC