动态代理机制作用和应用场景:
- 日志集中打印;
- 事务;
- 权限管理
- AOP
SpringAOP中可以用那些方式实现,区别?
- cglib
- java Proxy
- Aspectj
- 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的代理//使用静态代理
实现方法图解:
代码遗留…待我理解理解再补