传送门java
//面试
public interface InterView {
void chatting();
}
复制代码
public class Persion implements InterView{
@Override
public void chatting() {
System.out.println("is chatting ...");
try {
//模拟面试时间
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
public class Persion implements InterView{
@Override
public void chatting() {
Long start = System.currentTimeMillis();
System.out.println("is chatting ...");
try {
//模拟面试时间
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
Long end = System.currentTimeMillis();
System.out.println("chatting time: " + (end - start) );
}
}
复制代码
若是这个方法来自三方库没有改动源码的权限,怎么办
public class Persion2 extends Persion {
@Override
public void chatting() {
long start = System.currentTimeMillis();
super.chatting();
long end = System.currentTimeMillis();
System.out.println("chatting time : " + (end - start));
}
}
复制代码
public class Person3 implements InterView {
private Person person;
public Person3(Person person){
this.person = person;
}
@Override
public void chatting() {
long start = System.currentTimeMillis();
person.chatting();
long end = System.currentTimeMillis();
System.out.println("chatting time : " + (end - start));
}
}
复制代码
咱们继续增长需求,若是我还要在面试先后作记录(打印日志)呢,很简单,继承person2,并在先后添加打印日志便可
若是我要改变执行顺序呢,先获取时间,再打印日志呢,能够再用一个继承类,重写方法,这样会致使无限扩增
public class Person3 implements InterView {
private InterView interView;
public Person3(InterView interView){
this.interView = interView;
}
@Override
public void chatting() {
long start = System.currentTimeMillis();
interView.chatting();
long end = System.currentTimeMillis();
System.out.println("chatting time : " + (end - start));
}
}
复制代码
public class PersonLogProxy implements Interview {
private Interview interview;
public PersonLogProxy(Interview interview) {
this.interview = interview;
}
@Override
public void chatting() {
System.out.println("chatting start...");
interview.chatting();
System.out.println("chatting end...");
}
}
复制代码
public class Test {
public static void main(String[] args) {
Person person = new Person();
PersonLogProxy p1 = new PersonLogProxy(person);
PersonProxy p2 = new PersonProxy(p1);
p2.chatting();
}
}
复制代码
public class Test {
public static void main(String[] args) {
PersonProxy p3 = new PersonProxy(person);
PersonLogProxy p4 = new PersonLogProxy(p3);
p4.chatting();
}
}
复制代码
这里会出现一个问题,从表现来看,聚合能够实现灵活的执行顺序,而继承不能够,为何
面试
在上面的PersonTimeProxy的chatting方法,咱们直接调用了interview().chatting,换而言之,PersonProxy代理传入的interview对象,这就是典型的静态代理实现
设计模式
其实就是经过jdk自带的Proxy.newProxyInstance()方法动态生成代理类,动态编译,在经过反射建立对象并加载到内存中bash
能够查看一下Proxy->newProxyInstance()的源码,代理类的动态建立经过InvocationHandler自定义dom
咱们能够看一下源码中的InvocationHandler接口
jvm
import java.lang.reflect.Method;
/**
* proxy:指定动态生成的代理类
* method:接口中传入的全部方法对象
* args:当前传入方法的参数
*/
public interface InvocationHandler {
void invoke(Object proxy, Method method,Object[] args);
}
复制代码
使用:ide
public class MyInvocationHandler implements InvocationHandler {
//被代理对象,object类型
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
/**
*proxy:指定动态生成的代理类,
*method:接口中传入的对象的全部方法
*当前传入方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("chatting start ...");
Object returnValue = method.invoke(target, args);
System.out.println("chatting end");
return returnValue;
}
}
复制代码
public class DynamicProxyTest {
public static void main(String[] args) {
Person person = new Person();
MyInvocationHandler handler = new MyInvocationHandler(person);
/**
* 第一个参数指定代理类的类加载器,这里传入当前测试类加载器
* 第二个参数是代理类须要实现的接口的实例类
* 第三个参数是invocation handler,用来处理方法的调用,这里传入本身实现的handler
*/
InterView proxyObject = (InterView) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader()
, person.getClass().getInterfaces(),
handler);
proxyObject.chatting();
}
}
复制代码
此时,整个方法的调用栈变成了这样 测试
简而言之就是:ui
真实的业务类须要实现业务接口,代理类经过反射获取真实业务对象的类加载器,从而在内存中建立一个真实的代理对象(静态代理手动扩展那个),代理类须要实现invocationhandler接口,重写invoke方法,invoke方法三个参数,分别是代理的接口,代理的方法,和方法参数,在该方法里对真实的方法进行包装,实现动态代理
和静态代理的区别:静态代理是本身去建立代理类对象,动态代理本质是在内存中根据传入的参数,动态的生成一个代理类对象,经过生成的class文件反编译能够看到this
cglib是针对类来实现代理的,原理是针对指定的业务类生成一个子类,并覆盖其中的业务方法,实现代理,由于用的是继承,因此不能代理final修饰的类
public class PersonCglib implements MethodInterceptor {
//业务对象
private Object target;
public Object getInstance(Object target){
this.target = target;
//建立加强器
Enhancer enhancer = new Enhancer();
//指定要代理的类(指定生成子类的父类)
enhancer.setSuperclass(this.target.getClass());
//设置回调,对于代理类上全部方法的调用,都会调用callback,因此callback则须要实现intercept()方法进行拦截
enhancer.setCallback(this);
//包含三个步骤
//生成源代码
//编译成class文件
//加载到jvm中,建立实例并返回
return enhancer.create();
}
//实现方法回调
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("chatting start");
//这个obj对象是cglib给咱们new出来的
//cglib new出来的对象是被代理对象的子类(继承了被代理类),继承至关于间接持有父类的引用
methodProxy.invokeSuper(o,objects);
System.out.println("chatting end");
return null;
}
}
复制代码
public static void main(String[] args) {
Person person = new Person();
PersonCglib cglib = new PersonCglib();
InterView interView = (InterView) cglib.getInstance(person);
interView.chatting();
}
复制代码