基礎代碼
先在此處聲明接口與實現類,後續會用到。
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);
}
}
本文只是寫了簡易版的動態代理,目的在於瞭解大致原理,還有許多待改進的地方比如異常處理、動態傳入增強的邏輯等。