基础代码
先在此处声明接口与实现类,后续会用到。
Dao接口:
package dao;
/**
* @author Livingdd
* 2020/4/30 10:47
**/
public interface Dao {
void queryDataBase();
String queryDataBase(String param);
}
Dao实现类:
package dao;
/**
* @author Livingdd
* 2020/4/30 11:04
**/
public class RoleDaoImpl implements Dao{
public void queryDataBase() {
System.out.println("Query Role Database No Param");
}
public String queryDataBase(String param) {
System.out.println("Query Role Database Param : "+param);
return param;
}
}
静态代理
静态代理分为两种,聚合以及继承。
继承
顾名思义,继承要代理的目标对象,对目标对象中的方法进行代理。
假如变态的老板让你用静态代理的方式对Dao层实现类加入日志功能。你的代码可能会这样:
package staticProxy.inherit;
import dao.RoleDaoImpl;
/**
* @author Livingdd
* 2020/4/30 11:06
**/
public class RoleRecordLog extends RoleDaoImpl {
@Override
public void queryDataBase() {
System.out.println("Record Role Log");
super.queryDataBase();
}
@Override
public String queryDataBase(String param) {
System.out.println("Record Role Log");
return super.queryDataBase(param);
}
}
主方法调用:
Dao dao = new RoleRecordLog();
dao.queryDataBase();
System.out.println("===============");
System.out.println("Param Return :"+dao.queryDataBase("test"));
假如你的变态老板心情不好,和你说Dao层实现类不需要日志了,要加上事务,你的代码可能会这样:
package staticProxy.inherit;
import dao.RoleDaoImpl;
/**
* @author Livingdd
* 2020/4/30 11:06
**/
public class RoleTransaction extends RoleDaoImpl {
@Override
public void queryDataBase() {
System.out.println("Make Role Transaction");
super.queryDataBase();
}
@Override
public String queryDataBase(String param) {
System.out.println("Make Role Transaction");
return super.queryDataBase(param);
}
}
主方法调用:
Dao dao = new RoleTransaction();
dao.queryDataBase();
System.out.println("===============");
System.out.println("Param Return :"+dao.queryDataBase("test"));
假如你的变态老板更加变态了,他可能会让你先做事务再加日志,那么你的代码可能会这样:
package staticProxy.inherit;
/**
* @author Livingdd
* 2020/4/30 11:10
**/
public class TransactionAndRoleRecordLog extends RoleRecordLog{
@Override
public void queryDataBase() {
System.out.println("Make Role Transaction");
super.queryDataBase();
}
@Override
public String queryDataBase(String param) {
System.out.println("Make Role Transaction");
return super.queryDataBase(param);
}
}
主方法调用:
Dao dao = new TransactionAndRoleRecordLog();
dao.queryDataBase();
System.out.println("===============");
System.out.println("Param Return :" + dao.queryDataBase("test"));
但是如果你的变态老板让你先写日志再做事务呢,难道要再添加一个代理类去继承写日志的代理类吗?
所以此方法的缺点为会产生类爆炸。
聚合
目标对象与代理对象实现同一个接口,并且传入代理对象作为私有属性。
同一个例子,假如变态的老板让你对Dao层实现类加入日志功能。你的代码可能会这样:
package staticProxy.aggregation;
import dao.Dao;
/**
* @author Livingdd
* 2020/4/30 11:19
**/
public class RoleRecordLog implements Dao {
private Dao dao;
public RoleRecordLog(Dao dao){
this.dao =dao;
}
@Override
public void queryDataBase() {
System.out.println("Record Role Log");
this.dao.queryDataBase();
}
@Override
public String queryDataBase(String param) {
System.out.println("Record Role Log");
return this.dao.queryDataBase(param);
}
}
主方法调用:
RoleDaoImpl roleDao = new RoleDaoImpl();
Dao dao = new RoleRecordLog(roleDao);
dao.queryDataBase();
System.out.println("===============");
System.out.println("Param Return :" + dao.queryDataBase("test"));
要是让你加入事务呢?
package staticProxy.aggregation;
import dao.Dao;
/**
* @author Livingdd
* 2020/4/30 11:19
**/
public class RoleTransaction implements Dao {
private Dao dao;
public RoleTransaction(Dao dao){
this.dao =dao;
}
@Override
public void queryDataBase() {
System.out.println("Make Role Transaction");
this.dao.queryDataBase();
}
@Override
public String queryDataBase(String param) {
System.out.println("Make Role Transaction");
return this.dao.queryDataBase(param);
}
}
此时你的老板又变态了,让你又加事务,又加日志,聚合的方式能够比继承的方式灵活一些,你可以这样:
Dao roleRecordLog = new RoleRecordLog(new RoleDaoImpl());
Dao dao = new RoleTransaction(roleRecordLog);
dao.queryDataBase();
System.out.println("===============");
System.out.println("Param Return :" + dao.queryDataBase("test"));
可以看出聚合的方式相对于继承的方式,会少产生一些类,但是还是会产生很多类。
动态代理
动态代理原理为,用代码写出代理类,而后编译再获取代理对象,对目标对象进行增强。相比较与动态代理更加方便。。。
生成代理对象代码:
package dynamicProxy;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* @author Livingdd
* 2020/4/30 11:28
**/
public class ProxyUtil {
private static final String lineFeed = "\n";
private static final String tab = "\t";
private static final String noReturn ="void";
public static Object getJavaFile(Object targetObject) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class clazz= targetObject.getClass().getInterfaces()[0];
//拼接java文件
String interfaceName = clazz.getSimpleName();
String content = "";
//包名
String packageContent = "package com.livingdd;" + lineFeed + lineFeed;
//import
String importContent = "import dao.Dao;" + lineFeed + lineFeed;
//类描述
String classDescription = "public class $Proxy implements " + interfaceName + " {" + lineFeed;
//成员变量
String paramContent = tab + "private " + interfaceName + " target;" + lineFeed;
//构造方法
String constructContent = tab + "public $Proxy(" + interfaceName + " target){" + lineFeed
+ tab + tab + "this.target = target;" + lineFeed
+ tab + "}" + lineFeed;
//方法,可能有多个,for循环拼接
String methodContent = "";
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String returnTypeName = method.getReturnType().getSimpleName();
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
methodContent += tab + "public " + returnTypeName + " " + methodName + "(";
//方法中的参数,可能有多个,for循环拼接
String argsContent ="";
//目标对象调用的参数,可能有多个,for循环拼接
String useArgasContent="";
int paramCount = 1;
for (Class parameterType : parameterTypes){
String parameterTypeSimpleName = parameterType.getSimpleName();
argsContent += parameterTypeSimpleName+" var"+paramCount+",";
useArgasContent += "var"+paramCount+",";
}
if(argsContent.length()>0){
argsContent = argsContent.substring(0,argsContent.lastIndexOf(",")-1);
useArgasContent = useArgasContent.substring(0,useArgasContent.lastIndexOf(",")-1);
}
methodContent += argsContent+"){"+lineFeed
+tab+tab+"System.out.println(\"Record Log\");"+lineFeed;
methodContent += tab+tab;
if(!noReturn.equals(returnTypeName)){
methodContent += "return ";
}
methodContent += "this.target."+methodName+"("+useArgasContent+");"+lineFeed;
methodContent += tab+"}"+lineFeed;
}
content+= packageContent+importContent+classDescription+paramContent+constructContent+methodContent+"}";
FileWriter fileWriter = null;
File file = new File("D:\\com\\livingdd\\$Proxy.java");
try {
if (!file.exists()) {
file.createNewFile();
}
fileWriter = new FileWriter(file);
fileWriter.write(content);
fileWriter.flush();
}finally {
if(fileWriter!=null){
fileWriter.close();
}
}
//编译java文佳
JavaCompiler compiler = ToolProvider.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();
//反射获取java对象
//这里只需要填写d盘即可,因为反射出来的类包名为com.livingdd,并且在D盘下
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader classLoader = new URLClassLoader(urls);
Class<?> loadClass = classLoader.loadClass("com.livingdd.$Proxy");
Constructor constructor = loadClass.getConstructor(clazz);
Object o = constructor.newInstance(targetObject);
return o;
}
}
主方法调用:
Dao dao = (Dao) ProxyUtil.getJavaFile(new RoleDaoImpl());
附 生成的java文件代码:
package com.livingdd;
import dao.Dao;
public class $Proxy implements Dao {
private Dao target;
public $Proxy(Dao target){
this.target = target;
}
public void queryDataBase(){
System.out.println("Record Log");
this.target.queryDataBase();
}
public String queryDataBase(String var){
System.out.println("Record Log");
return this.target.queryDataBase(var);
}
}
本文只是写了简易版的动态代理,目的在于了解大致原理,还有许多待改进的地方比如异常处理、动态传入增强的逻辑等。