什么是代理
代理就是增强一个对象的功能
java实现的代理的两种办法
代理的名词
代理对象 增强后的对象
目标对象 被增强的对象
他们不是绝对的,会根据情况发生变化
静态代理
继承
代理对象继承目标对象,重写需要增强的方法;
缺点:会代理类过多,非常复杂
- 基本类
public class UserDaoImp {
public void query(){
System.out.println("业务逻辑");
}
}
- 对类进行增强,想要输出日志
public class UserDaoLogImp extends UserDaoImp {
@Override
public void query() {
System.out.println("-----Log-------");
super.query();
}
}
- Test
public class Test {
public static void main(String[] args) {
UserDaoImp dao = new UserDaoLogImp();
dao.query();
}
}
- 对类进行增强,想要输出时间
public class UserDaoTimeImp extends UserDaoImp {
@Override
public void query() {
System.out.println("-----------Time-------------");
super.query();
}
}
- 若想对基本类进行增强,输入时间和日志
解决方式是:单链继承
若想先输出日志,那就让time继承基础类,再让log继承time.(我是先执行增强的事务,在执行业务)。至于怎么输出看逻辑
若是想输出时间,地点,日志,且输出的顺序不一样,那就需要创建大量的类来实现增强。(排列组合)
聚合(接口)
目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。
缺点:也会产生类爆炸,只不过比继承少一点点。(有多少增强方法就需要实现多少次接口类)
- 基础接口
public interface UserDao {
void query();
}
- 实现接口
public class UserDaoImp implements UserDao{
public void query(){
System.out.println("业务逻辑");
}
}
- 日志代理对象
public class UserDaoLog implements UserDao {
UserDao dao;
public UserDaoLog(UserDao dao){
this.dao = dao;
}
public void query() {
System.out.println("-----Log-------");
dao.query();
}
}
- 时间代理对象
public class UserDaoTime implements UserDao {
UserDao dao;
public UserDaoTime(UserDao dao){
this.dao = dao;
}
public void query() {
System.out.println("-----------Time-----------");
dao.query();
}
}
public class Test {
public static void main(String[] args) {
/**
* 就相当于
* UserDao target = new UserDaoImp();
*
* UserDao proxy = new UserDaoLog(target);
*/
UserDao dao = new UserDaoLog(new UserDaoImp());
dao.query();
System.out.println("#####################");
/**
* UserDao target = new UserDaoImp();
*
* UserDao proxy = new UserDaoTime(target);
*/
UserDao dao1 = new UserDaoTime(new UserDaoImp());
dao1.query();
System.out.println("#########################");
/**
* UserDao target = new UserDaoLog(new UserDaoImp());
*
* UserDao proxy = new UserDaoTime(target);
*/
UserDao dao2 = new UserDaoTime(new UserDaoLog(new UserDaoImp()));
dao2.query();
}
}
总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。
动态代理(不能new)
手写模拟动态代理
不需要手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。
- 任意接口,任意参数,任意返回值的动态代理
public class ProxyUtil {
public static Object newInstance(Object target){ //传递一个类对象其中有接口
Object proxy = null;
Class targetInf = target.getClass().getInterfaces()[0]; //获取类接口(它是一个类)
//============================以下是动态代理的字符串编写=================================
String line= "\n";
String tab = "\t";
String infName = targetInf.getSimpleName(); //只获取类名
Method[] methods = targetInf.getDeclaredMethods();
String content = "";
String packageContent = "package com.google;" + line;
String importContent = "import " + targetInf.getName() + ";" + line; //获取全类名
String classFirstLineContent = "public class $Proxy implements " + infName + "{" + line;
String filedContent = tab + "private " + infName + " target;" + line;
String constructContent = tab + "public $Proxy(" + infName +" target) { " + line
+ tab + tab + "this.target = target;" + line
+ tab + "}" + line;
String methodContent = "";
for (Method method : methods) {
String returnTypeName = method.getReturnType().getSimpleName();
String methodName = method.getName();
Class args[] = method.getParameterTypes(); //不是对象,就是类
String argsContent = "";
String parameContent = "";
int flag = 0;
for (Class arg : args) {
String temp = arg.getSimpleName();
argsContent += temp + " i" + flag + ",";
parameContent += "i" + flag + ",";
flag ++;
}
if(argsContent.length() > 0){
argsContent = argsContent.substring(0, argsContent.lastIndexOf(',')-1);
parameContent = parameContent.substring(0,parameContent.lastIndexOf(',')-1 );
}
methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent +") {" + line
+ tab + tab + "System.out.println(\"log\");" + line;
if(returnTypeName.equals("void")) {
methodContent += tab + tab + "target." + methodName + "(" + parameContent + ");" + line;
} else {
methodContent += tab + tab + "return (" + returnTypeName + ") target." + methodName + "(" + parameContent + ");" + line;
}
methodContent += tab + "}" + line;
}
content = packageContent + importContent + classFirstLineContent + filedContent + constructContent + methodContent + "}";
//============================编写完成=================================
// System.out.println(content);
//===============================写入文件==========================
File file =new File("c:\\com\\google\\$Proxy.java");
try {
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
// 编写.class文件
JavaCompiler compiler = getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//加载.class进内存
URL[] urls = new URL[]{new URL("file:c:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass("com.google.$Proxy");
//构造器创建类
Constructor constructor = clazz.getConstructor(targetInf); //传递接口
proxy = constructor.newInstance(target);
} catch (Exception e){
e.printStackTrace();
}
return proxy;
}
}
代码解读:
由对象可以获取类,类在获取接口列表: 对象。getClass().getInterfaces()[0]
,获取第一个接口(Class,注:接口也是Class)
只实现了第一个接口的代理。
获取接口(Class)中的所有方法。
获取接口(Class)的名字(全类名,包含包名)
- Method反射里面的内容
类对象.getDeclaredMethods()
/**
* A {@code Method} provides information about, and access to, a single method
* on a class or interface. The reflected method may be a class method
* or an instance method (including an abstract method).
*
* <p>A {@code Method} permits widening conversions to occur when matching the
* actual parameters to invoke with the underlying method's formal
* parameters, but it throws an {@code IllegalArgumentException} if a
* narrowing conversion would occur.
*
* @see Member
* @see java.lang.Class
* @see java.lang.Class#getMethods()
* @see java.lang.Class#getMethod(String, Class[])
* @see java.lang.Class#getDeclaredMethods()
* @see java.lang.Class#getDeclaredMethod(String, Class[])
*
* @author Kenneth Russell
* @author Nakul Saraiya
*/
类对象= 对象.getClass() ==> 类对象.getClassName()
- 缺点
- 首先要生成文件
- 动态编译文件 class
- 需要一个URLclassloader
软件性能的最终体现在IO操作(自己写的动态代理中间IO操作)
- 需要代理的字符串内容
package com.google;
import com.luban.dao.UserDao;
public class $Proxy implements UserDao{
private UserDao target;
public $Proxy(UserDao target) {
this.target = target;
}
public String print(String i0,int i) {
System.out.println("log");
return (String) target.print(i0,i);
}
public void query(String i) {
System.out.println("log");
target.query(i);
}
}
上面的代理虽然不用重写类,但是不能实现动态代理
动态逻辑的动态代理如下
package com.luban.dao;
import java.lang.reflect.Method;
public interface CustomeInvocationHandler {
public Object invoke(Method method);
}
package com.google;
import com.luban.dao.CustomeInvocationHandler;
import com.luban.dao.UserDao;
import java.lang.reflect.Method;
public class $Proxy implements UserDao {
private CustomeInvocationHandler h;
public $Proxy(CustomeInvocationHandler h){
this.h = h;
}
public String proxy() throws Exception{
Method method = Class.forName("com.luban.dao.UserDao").getDeclaredMethod("proxy");
return (String)h.invoke(method);
}
}
JDK动态代理
通过接口反射得到字节码,然后把字节码转成class native openJDK c++