动态代理机制底层

动态代理机制作用和应用场景:
  1. 日志集中打印;
  2. 事务;
  3. 权限管理
  4. AOP
SpringAOP中可以用那些方式实现,区别?
  1. cglib
  2. java Proxy
  3. Aspectj
  4. instrumentation
区别:

cglib和java Proxy都是动态构建字节码工具的形式实现,会构造一个全新的类;其中JDK动态代理只能针对实现了接口的类生成代理。而cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。

如果目标对象没有实现接口,则默认会采用CGLIB代理;

如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入)。

Aspectj是Java支持的切面编程,原理:在编译期间,将字节码编译到代理对象中.
CGLIB动态生成class,
Aspectj静态的,在程序编译期间,将代理字节加入到目标对象中.(修改代理目标类的字节,织入代理的字节码);性能比CGLIB和java Proxy好一些.

instrumentation修改目标类的字节码,在类装载时,进行动态拦截去修改.

源代码位置:

instrument采用java agent(JDK1.5特性),使用时必须添加spring-instrument-4.3.8.RELEASE的jar包进行动态拦截.
若需要采用instrumentation实现AOP的话,可以在程序启动时,在JVM中加入-javaagent:spring-instrument-4.3.8.RELEASE.jar,动态拦截类的装载(拦截所有类的装载),之后根据Spring的配置进行拦截.

源代码:

1. 拿到Instrumentation动态的拦截字节码.

2. 通过transform修改,就能返回修改后的字节码.从而达到动态修改字节码,实现动态代理的目的.

动态代理:

a. 无论哪种方式实现动态代理,本质其实都是对字节码的修改,区别是从哪里进行切入修改字节码.
1. cglib–IOC beanFactory获取bean时,动态构建字节码,生成这个类
2. java Proxy– 获取或装载bean时,对字节码进行动态构建,装载,实例化
3. Aspectj– 代码编译时,进行织入修改字节码
4. instrumentation– 在类装载时.

动态代理技术栈图:


2018-1-25 23:46:47


总结:

1.动态代理可以由cglib/java Proxy/Aspectj/instrumentation等多种形式实现;
2.动态代理的本质是对class字节码进行动态构建或者修改;
a.修改的工具有ASM(= =比较难用,还是需要知道JVM指令)/javavssist(已经进行封装)
3.多种实现方式的区别在于对字节码切入方式不同.可选方式:
a.java代理/Cglib是基于动态构建接口实现类字节
b.AspectJ是借助eclipse工具在编译时织入代理字节
c.instrumentation是基于javaagent在类装载时,修改class织入代理字节
d.使用自定义classloader在装载时,织入代理字节

一个java动态代理的例子:

//java动态代理
public class JavaProxyTest {

    @Test
    public void proxyTest() {
        //接口
        UserService service = (UserService) Proxy.newProxyInstance(JavaProxyTest.class.getClassLoader(),new Class[]{UserService.class}, new InvocationHandler() {
            //代理目标
            UserServiceImpl target = new UserServiceImpl();
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("执行前----");
                Object result = method.invoke(target, args);
                System.out.println("执行后----");
                return result;
            }
        });
        //代理对象
        service.getName();
    }
}

他们的关系/结构:

PS:代理目标就是委托类

2018/1/30 19:22:20


代理对象如何代理目标对象?

关键在于invocationHandler接口,由该接口将代理对象,目标对象和代理逻辑连接在一起.下图是执行过程.

java proxy构建新的代理对象的过程:
1.proxy基于代理接口获取其代理class

a.从缓存接口中获取,若没有则继续下一步
b.使用proxyGenerator构建代理class字节
c.调用本地方法装载class字节至当前classLoader
d.使用class.forName()返回新的class对象

2.使用反射生成代理对象
3.调用代理对象class.newInstance()

javassis动态代理

(待完善,有bug)

package com.jd.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import org.omg.CORBA.DynAnyPackage.Invalid;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.bytecode.AccessFlag;
/**
 * 
* @ClassName: JavassisProxy
* @Description: 动态代理对象构建
* @author Helen
* @date 2018-2-11
*
 */
public class JavassisProxy {

    public <T>T newProxyInstance(ClassLoader loader, Class<T> proxyTarget,InvocationHandler invocationHandler) throws Exception{
        //构建class
        ClassPool pool = new ClassPool();
        pool.insertClassPath(new LoaderClassPath(proxyTarget.getClassLoader()));
        CtClass targetClass = pool.get(proxyTarget.getName());
        CtClass proxyClass = pool.makeClass(proxyTarget.getName()+"$proxy",targetClass);

        //添加invoker Hander字段
        CtField handlerField = new CtField(pool.get(invocationHandler.getClass().getName()), "h", proxyClass);
        proxyClass.addField(handlerField);

        //遍历并生成所有代理方法
        int methodIndex = 0;
        for (CtMethod ctMethod : targetClass.getDeclaredMethods()) {
            //屏蔽不能代理的方法
            if (!AccessFlag.isPublic(ctMethod.getModifiers())) {
                continue;
            }else if ((ctMethod.getModifiers() & AccessFlag.NATIVE) != 0) {
                continue;
            }else if ((ctMethod.getModifiers() & AccessFlag.STATIC) != 0) {
                continue;
            }else if ((ctMethod.getModifiers() & AccessFlag.FINAL) != 0) {
                continue;
            }
            String methodFname = ctMethod.getName() + methodIndex;
            CtField methodField = new CtField(pool.get(Method.class.getName()), methodFname, proxyClass);
            String paramTypeSrc = "new Class[]{";
            for (int i = 0; i < ctMethod.getParameterTypes().length; i++) {
                if (i != 0) {
                    paramTypeSrc += ",";
                }
                paramTypeSrc += ctMethod.getParameterTypes()[i].getName() + ".class";
            }
            paramTypeSrc += "}";
            String d = proxyTarget.getName() + ".class";
            //initSrc这句不知道咋写了..先留下
            String initSrc = "com.jd.proxy.JavassisProxy.getMethod(" + d + ",\""+ctMethod.getName() +"\")";

            proxyClass.addField(methodField,initSrc);

            CtMethod copyMethod = CtNewMethod.copy(ctMethod, proxyClass, null);

            String bodySrc = "{";
            bodySrc += "Object result=h.invoke($0," + methodFname +",$args);";

            if (!copyMethod.getReturnType().getName().equals("void")) {
                bodySrc += "return ($r)result;";
            }
            bodySrc += "}";
            copyMethod.setBody(bodySrc);
            proxyClass.addMethod(copyMethod);
            methodIndex++;
        }
        CtConstructor constructor =new CtConstructor(new CtClass[]{
            pool.get(InvocationHandler.class.getName())
        }, proxyClass);
        constructor.setBody("h=$1;");
        proxyClass.addConstructor(constructor);
        String wdir = System.getProperty("user.dir");
        System.out.println(wdir);

        proxyClass.debugWriteFile(wdir+"/target/");
        Class cla = proxyClass.toClass();

        UserServiceImpl i = (UserServiceImpl) cla.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
        return (T)i;
    }   
}


package com.jd.proxy.test;

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

import org.junit.Test;

import com.jd.proxy.JavassisProxy;
import com.jd.proxy.UserServiceImpl;

public class JavassisProxyTest {

    @Test
    public void proxyTest() throws Exception{
        UserServiceImpl userService = new JavassisProxy().newProxyInstance(JavassisProxy.class.getClassLoader(),
                UserServiceImpl.class, new InvocationHandler() {
                    UserServiceImpl target = new UserServiceImpl();

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        System.out.println("----代理前");
                        Object r = method.invoke(target, args);
                        System.out.println("----代理后");
                        return r;
                    }
                });

        userService.getName();
    }
}

Java应用协议代理(FTP、Http、Https、File)

URL的类结构

URL执行过程:
1.URL基于protocol构建对应UrlStreamHandler
2.UrlStreamHandler.openConnection()打开连接,返回URIConnection
3.返回URIConnection设置连接属性
4.URIConnection打开outPutStream
5.URIConnection打开inPutStream
6.关于连接

关键在于URLConnection的代理//使用静态代理

实现方法图解:

代码遗留…待我理解理解再补

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值