这篇文章主要介绍“java怎么实现从静态代理到动态代理”,在日常操作中,相信很多人在java怎么实现从静态代理到动态代理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java怎么实现从静态代理到动态代理”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
1.什么是代理
引用网上的一段话 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下: 
2.java中如何实现代理
①. 代理的名词
代理对象:增强后的对象 目标对象:被增强的对象
②. 静态代理
先来假设一个场景,查询用户,在查询之前要写日志记录。
public class UserDaoImpl {
public void query(){
System.out.println("查询用户信息");
}
}
上面代码我们要实现对其功能的增强,可以通过修改代码来实现。
public class UserDaoImpl {
public void query(){
System.out.println("log");
System.out.println("查询用户信息");
}
}
这样实现可以达到效果,但是破坏了面向对象的开闭原则。况且有的时候,我们是不能拿到该Dao的源码,也没有办法对其修改。所以可以考虑代理模式来实现。
继承
编写UserDaoImpl的一个子类,复写query方法.
public class ProxyUserDao extends UserDaoImpl{
@Override
public void query() {
System.out.println("log");
super.query();
}
}
编写测试,执行测试。
public static void main(String[] args) {
ProxyUserDao proxyUserDao = new ProxyUserDao();
proxyUserDao.query();
}

聚合
目标对象和代理对象都要实现同一接口。 编写UserDao接口
public interface UserDao {
public void query();
}
UserDaoImpl实现
public class UserDaoImpl implements UserDao{
public void query(){
System.out.println("查询用户信息");
}
}
编写代理类
public class ProxyUserDao implements UserDao{
//目标对象
UserDao userDao;
//通过构造函数传入目标对象
public ProxyUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
public void query() {
System.out.println("log");
userDao.query();
}
}
改写测试
ProxyUserDao proxyUserDao = new ProxyUserDao(new UserDaoImpl());
proxyUserDao.query();
执行后,也可以完成代理。上面实现的都是先打印log,在执行查询,那么要实现先执行查询,再打印log,我们就要再写一个代理类,岂不蛋疼。
现在我们来假设要实现多个功能的增强,以前是日志记录,log,我们还要家人时间记录time,那么这里就有多种组合方式。 
那么有不同的需求就要有不同的实现方式,每一种都要编写不同的代理实现逻辑。就会写大量的java代理类。我们回头看一下继承的方式来实现的话,还更加麻烦,代理类会更多。
两种方式的缺点与总结
继承:代理类过多,比较复杂 聚合:也会有大量的代理类,不过相比继承要好很多。
所以,在不确定的情况下,就不要去使用静态代理,因为会编写大量的代理类来满足不同的需求。比较麻烦。这里模拟的还仅仅是一个UserDao,若有多个目标代理对象,那么通过硬编码的方式,就真的很不理智了。
③. 动态代理
我们可以通过动态代理来避免编写大量的代理对象的困扰。
手动实现动态代理
我们先来分析一下,生成一个对象的途径。 第一步:编写.java文件
第二步:编译.java文件,获得.class文件 第三步:类记载机制加载.class文件,new出对象。
先来分析第一步:我们编写java文件以外,我们就是要为了不编写大量的类代码,所以这种方式无效,此外还可以通过编写程序实现输出一个java文件。 下面直接贴源码:
public class ProxyUtil {
/**
* content --->string
* .java io
* .class
* .new 反射----》class
* @return
*/
public static Object newInstance(Object target){
Object proxy=null;
Class targetInf = target.getClass().getInterfaces()[0];
Method methods[] =targetInf.getDeclaredMethods();
String line="\n";
String tab ="\t";
String infName = targetInf.getSimpleName();
String content ="";
//进行拼装代理类代码
String packageContent = "package com.google;"+line;
String importContent = "import "+targetInf.getName()+";"+line;
String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
String filedContent =tab+"private "+infName+" target;"+line;
String constructorContent =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();
// Sting.class String.class
Class args[] = method.getParameterTypes();
String argsContent = "";
String paramsContent="";
int flag =0;
for (Class arg : args) {
String temp = arg.getSimpleName();
//String
//String p0,Sting p1,
argsContent+=temp+" p"+flag+",";
paramsContent+="p"+flag+",";
flag++;
}
if (argsContent.length()>0){
argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);
}
//代理打印log日志功能(这里是写死的)
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") {"+line
+tab+tab+"System.out.println(\"log\");"+line
+tab+tab+"target."+methodName+"("+paramsContent+");"+line
+tab+"}"+line;
}
//类中的代码字符串
content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
File file =new File("//$Proxy.java");
try {
if (!file.exists()) {
file.createNewFile();
}
//将content写入java文件
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
//进行java文件编译 java--->class
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();
// 类加载
URL[] urls = new URL[]{new URL("file:D:\\\\")};
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;
}
}
上面是编写好的代理工具类,会实现java代理文件的生成,java的编译,加载,反射创建实例。调用newInstance就返回了代理对象。不过这里将代理功能写死的。
编写测试类。并执行
UserDao o = (UserDao) ProxyUtil.newInstance(new UserDaoImpl());
o.query();
目录下已经生成了对应的文件

查看生成的代理类代码

控制台打印的日志,实现了对方法加上日志打印 
我们要实现对其他目标对象的代理,只需要调用newInstance传入目标对象即可。
如:我们要实现对订单OrderDao查询订单增加日志打印
//订单Dao接口
public interface OrderDao {
public void query();
}
//订单Dao接口实现 目标对象
public class OrderDaoImpl implements OrderDao {
public void query(){
System.out.println("查询订单");
}
}
//测试函数
public static void main(String[] args) {
OrderDao o = (OrderDao) ProxyUtil.newInstance(new OrderDaoImpl());
o.query();
}
只需测试,查询日志,达到了对订单接口的代理

通过动态代理我们可以在不手动编写代理对象的方式,实现对不同目标对象的代理。增强了代码的可扩展。ps:java底层也有动态代理的工具类proxy。他的实现原理也是相同的。
到此,关于“java怎么实现从静态代理到动态代理”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注天达云网站,小编会继续努力为大家带来更多实用的文章!