java动态代理
概念:JDK提供的动态创建接口对象的方式,就叫动态代理
问题场景假设:
电脑上有浏览器Browser这种工具,而市面上实现了这种工具的产品主要有Chrome,现在,我也想做一个类似于Chrome的浏览器,并且实现我自己的功能.但是我不知道他是怎么做的,此时可以使用代理模式.
我们已知Browser是Chrome实现的接口,Chrome就是被代理的对象.既然Chrome实现了Browser接口,那就实现了Browser的所有方法,我们可以也实现Browser接口,来获取里面的方法,并传入一个Chrome对象,借用Chrome对象来实现方法
package Aop;
public interface Browser {
public void open(String str);
public void close();
}
package Aop.impl;
import Aop.Browser;
//这个类的内部实现应该是我们不知道的
public class Chrome implements Browser {
@Override
public void open(String str) {
System.out.println("打开浏览器进行搜索:"+str);
}
@Override
public void close() {
System.out.println("关闭浏览器...");
}
}
package Aop.impl;
import Aop.Browser;
public class QQBrowser implements Browser {
private Browser browser;
public QQBrowser(Browser browser){
this.browser=browser;
}
@Override
public void open(String str) {
//这里加入了自己的功能
System.out.println("搜集用户信息,回传华强北");
//通过browser对象来具体实现方法.
browser.open(str);
System.out.println("准备开始弹窗");
}
@Override
public void close() {
browser.close();
}
}
public static void main(String[] args) {
Browser browser=new Chrome();
QQBrowser qqBrowser = new QQBrowser(browser);
qqBrowser.open("百度百科");
qqBrowser.close();
}
这种方法是静态代理,主要有两个缺点:
1.必须继承和盗窃类相同的接口,才能知道被盗取的类有什么功能
2.必须创建一个实体类文件
这样也太麻烦了,每一次创建一个实体类,有什么方法可以不创建实体类就获取到Browser的所有东西吗?
反射!动态代理就是基于反射机制完成的
public static void main(String[] args) {
Browser browser=new Chrome();
/**
* Proxy 动态代理类 属于java.lang.reflect包下,也就是反射包下
* newProxyInstance常用的Proxy实例化方法
* 参数: ClassLoader 被代理类的加载器
* Interfaces 被代理类实现的接口
* InvocationHandler 调用处理器,具体的功能在这里实现
*/
Browser ahou =(Browser) Proxy.newProxyInstance(browser.getClass().getClassLoader()
, browser.getClass().getInterfaces(), new InvocationHandler() {
/**
*这里要做的是对被代理类进行功能扩展,每一次调用代理类ahou的方法时,都会调用一次invoke方法
* @param proxy 代理对象 Proxy
* @param method 代理方法
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
if (method.getName()=="open"){
System.out.println("搜集用户信息,回传华强北");
invoke = method.invoke(browser, args);
System.out.println("准备开始弹窗");
}else{
invoke = method.invoke(browser, args);
}
return invoke;
}
});
ahou.open("百度百科");
// ahou.close();
}
这里可能会有一个疑惑,那就是proxy这个参数传进来有什么用呢?
我们看一下下面的动态代理的代码
package Aop.WhatisProxy;
public interface People {
public People work(String workName);
}
package Aop.WhatisProxy;
public class Student implements People{
@Override
public People work(String workName) {
System.out.println("工作内容是"+workName);
return this;
}
}
package Aop.WhatisProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
People people = new Student();
People proxy = (People)Proxy.newProxyInstance(people.getClass().getClassLoader(),
people.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
System.out.println("before 动态代理...");
invoke = method.invoke(people, args);
System.out.println("after 动态代理...");
return invoke;
}
});
proxy.work("写代码").work("开会").work("上课");
}
}
输出结果:
before 动态代理...
工作内容是写代码
after 动态代理...
工作内容是开会
工作内容是上课
这个例子中,我们同上面一样,实际上并没有用到proxy这个参数,我们可以看到,实际上进入invoke方法只有第一次调用work()方法的时候
我们把上面的代码改一下
package Aop.WhatisProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
People people = new Student();
People proxy = (People)Proxy.newProxyInstance(people.getClass().getClassLoader(),
people.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
System.out.println("before 动态代理...");
invoke = method.invoke(people, args);
System.out.println("after 动态代理...");
return proxy;
}
});
proxy.work("写代码").work("开会").work("上课");
}
}
这里将返回值改成了proxy
看一下输出:
before 动态代理...
工作内容是写代码
after 动态代理...
before 动态代理...
工作内容是开会
after 动态代理...
before 动态代理...
工作内容是上课
after 动态代理...
可以看到,现在每次都进到了invoke方法中.
产生不同情况的原因,我们可以通过proxy,people和invoke表示的类来理解一下
package Aop.WhatisProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
People people = new Student();
People proxy = (People)Proxy.newProxyInstance(people.getClass().getClassLoader(),
people.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
System.out.println("before 动态代理...");
System.out.println(proxy.getClass().getName());
System.out.println(people.getClass().getName());
invoke = method.invoke(people, args);
System.out.println(invoke.getClass().getName());
System.out.println("after 动态代理...");
return proxy;
}
});
proxy.work("写代码").work("开会").work("上课");
}
}
before 动态代理...
com.sun.proxy.$Proxy0
Aop.WhatisProxy.Student
工作内容是写代码
Aop.WhatisProxy.Student
after 动态代理...
before 动态代理...
com.sun.proxy.$Proxy0
Aop.WhatisProxy.Student
工作内容是开会
Aop.WhatisProxy.Student
after 动态代理...
before 动态代理...
com.sun.proxy.$Proxy0
Aop.WhatisProxy.Student
工作内容是上课
Aop.WhatisProxy.Student
after 动态代理...
从上面的输出代码可以明显看出,proxy是Proxy这个代理类,people自然就是被代理类Aop.WhatisProxy.Student,而invoke也是Aop.WhatisProxy.Student,是因为work方法返回的this就是Student类.
所以,如果在invoke()方法中返回的是invoke,那么只有 proxy.work(“写代码”)是用了代理类的方法,而proxy.work(“写代码”)返回了Aop.WhatisProxy.Student类,所以后面的.work(“开会”)和.work(“上课”)自然就调用了Aop.WhatisProxy.Student类中的work()方法.
如果返回的是proxy,那么就可以确保在这个链式调用中每次返回的都是代理类,既然是代理类那就是用的代理类中的invoke方法来实现Aop.WhatisProxy.Student类中的work()方法,而不是 直接使用Aop.WhatisProxy.Student类中的work()方法.