猿思考系列5——一文明白java和微商那点儿事儿
看完上一个章节,相信你已经掌握了一些编写并发代码编写的要领了。今天我们来聊一个新的话题。另外真的很感谢大家的支持,和巨兽的斗争暂时进入僵持阶段,猿人工厂君已经说了,虽千万人,吾往矣。中间细节,猿人工厂君,会在方便的时候公开,程序猿鸭,且行且珍惜。
开动你的小脑筋,你怎样才能代替我去卖面膜?先看下面的代码,卖面膜是一种行为,定义成一个接口,MaiMianMo,还有一个实现类MaiMianMoImpl,具体怎么卖面膜由MaiMianMoImpl去做。
package com.pz.se.demo;
public interface MaiMianMo {
public Object maiMianMo(Object obj);
}
packagecom.pz.se.demo;
public class MaiMianMoImpl implements MaiMianMo {
@Override
public Object maiMianMo(Object obj) {
System.out.println("我正在售卖 面米:"+obj);
return obj;
}
}
嗯,你的意思是代替你去卖面膜,这还不简单,编写一个类实现MaiMianMo接口,代替你嘛,我就是你的代理,我直接在新的类中使用MaiMianMoImpl的方法就好了,名字嘛就叫MaiMianMoProxy好了,talk is cheap show me your code…
package com.pz.se.demo;
public class MaiMianMoProxyimplements MaiMianMo {
private MaiMianMo maiMianMoImpl = new MaiMianMoImpl();
@Override
public Object maiMianMo(Object obj) {
System.out.println("我是微商,我卖面膜");
return maiMianMoImpl.maiMianMo(obj);
}
public static void main(String args[]){
MaiMianMo maiMianMo = newMaiMianMoProxy();
maiMianMo.maiMianMo(new Object());
}
}
所谓动态代理,是指利用Java的反射技术(JavaReflection)生成字节码,在运行时创建一个新的类给指定接口或接口引用,从而实现代理了或接口的职责。
JDK提供了动态代理,都在Java.lang.reflect包下,一个接口InvocationHandler,一个类Proxy。
InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method,Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。
Proxy类就是动态代理类,用于在程序运行时,生产生产代理对象。我们可以看下使用JDK的动态代理,来帮助我们成为微商,售卖面膜。
第一步,实现InvocationHandler接口。
package com.pz.se.demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MaiMianMoInvocationHandler implements InvocationHandler {
private MaiMianMo maiMianMo;
publicMaiMianMoInvocationHandler(MaiMianMo maiMianMo) {
this.maiMianMo = maiMianMo;
}
@Override
public Object invoke(Object proxy, Methodmethod, Object[] args) throws Throwable {
//微商嘛自然喜欢干点别的事情
//做还是不做都在于你
Object invoke =method.invoke(maiMianMo, args);
System.out.println("我是微商我想干嘛干嘛的");
System.out.println("我甚至不调用你能拿我怎样?");
System.out.println("到了我手里我想干嘛就干嘛的");
return invoke;
}
}
第二步,使用Proxy类生产代理对象,然后调用就好。
package com.pz.se.demo;
importjava.lang.reflect.Proxy;
public classTestProxyMaiMianMo {
public static void main(String args[]){
MaiMianMoInvocationHandler handler =new MaiMianMoInvocationHandler(new MaiMianMoImpl());
MaiMianMo maiMianMo= (MaiMianMo)Proxy.newProxyInstance(MaiMianMo.class.getClassLoader(),newClass[]{MaiMianMo.class},handler);
maiMianMo.maiMianMo(new Object());
}
}
根据JDK提供的动态代理,看起来只是针对接口来说的,我们可以想想一下,代理到底是什么?无非是代替某个类的功能而已,一个类有属性和方法,那么编写一个类继承要实现的类不就好了?结合反射来看,可以拿到太多的信息了,无非输出一个源程序文件,然后吧,假如编译了呢?然后吧,编译,然后加载然后创建了对象了呢?下面就是个例子稍微改改,妙用无穷噢。
public class ProxyUtil {
public static Object proxyClass(Class clazz){
String packageNam=clazz.getPackage().getName();
return null;
}
private static StringappendPackageStr(Class clazz){
return "package "+clazz.getPackage().getName()+";n";
}
private static StringappendClassBodyBegin(Class clazz){
return "public class "+clazz.getSimpleName()+"Proxy extends " +clazz.getName()+"{n";
}
private static String getFileName(Class clazz){
returnclazz.getSimpleName()+"Proxy.java";
}
private static String appendClassBodyEnd(){
return "}n";
}
private static String appendImports(Classclazz) {
StringBuilder builder = newStringBuilder();
for( Method method:clazz.getDeclaredMethods()){
Class<?>[] parameterTypes=method.getParameterTypes();
for(Class param:parameterTypes){
builder.append("import "+param.getName()+";n");
}
}
return builder.toString();
}
private static String appendMethod(Methodmethod,String codes) {
StringBuilder builder = newStringBuilder();
builder.append("public ");
builder.append(method.getReturnType().getName() +" ");
builder.append(method.getName()+"");
builder.append("( ");
Class<?>[] parameterTypes=method.getParameterTypes();
for(int i =0;i<parameterTypes.length;i++){
if(i==parameterTypes.length-1){
builder.append(parameterTypes[i].getName() +" "+parameterTypes[i].getSimpleName());
builder.append(") ");
}
}
builder.append("{n");
builder.append(codes);
builder.append("}n");
return builder.toString();
}
public static ObjectcomplierAndReturnProxyObject(Class clazz, String content) throws IOException {
StringfileName=System.getProperty("user.dir")+"/target/classes"+"/"+clazz.getPackage().getName().replace(".","/")+"/"+getFileName(clazz);
File file = new File(fileName);
if(!file.exists()){
file.createNewFile();
}
FileOutputStream fos = newFileOutputStream(fileName);
fos.write(content.getBytes());
fos.close();
try {
//动态编译
JavaCompiler javac =ToolProvider.getSystemJavaCompiler();
int status = javac.run(null, null,null, "-d",System.getProperty("user.dir")+"/target/classes",fileName);
if(status!=0){
System.out.println("没有编译成功!");
}
return Class.forName(clazz.getName()+"Proxy").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String args[])throws IOException {
StringBuilder builder = newStringBuilder();
builder.append(appendPackageStr(MaiMianMoImpl.class));
builder.append(appendImports(MaiMianMoImpl.class));
builder.append(appendClassBodyBegin(MaiMianMoImpl.class));
Method[] methods=MaiMianMoImpl.class.getDeclaredMethods();
for( Method method: methods){
builder.append(appendMethod(method," return null;n"));
}
builder.append(appendClassBodyEnd());
String codes =builder.toString();
System.out.println(codes);
MaiMianMo maiMianMo = (MaiMianMo)complierAndReturnProxyObject(MaiMianMoImpl.class,codes);
maiMianMo.maiMianMo(new Object());
}
}
以上的方式都是运行时期来生产代理类的。当然除了jdk的代理之外,还有很多工具也可以做到这一点。比如经常提到的开源项目cglib,ASM,Javassist,这些框架能够更容易的做到,在运行时期,扩展一个类,总比咱自己JDK硬写编译要强吧?
那还有没有是什么时候可以做手脚去扩展一个代理类出来呢?我们之前学过java代码的执行套路,一个代码需要执行,是需要编译的吧?我们也可以在编译时期做一些手脚嘛,反正原理无非就是,读取一个类的内容,然后加入自己想加入的内容就好了。Java也有一些开源项目,比如AspectJ可以轻松的做到这一点。
介于篇幅,以上的开源项目的使用,我就暂时不做展示了。后续的内容,我会给出例子的。
不过最重要的一点需要记住,代理后,做任何事情都可以,被代理的方法只关心返回值,要不你可能学AOP只能停留在打日志的阶段了噢。
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 去哪儿景点信息爬取并使用Django框架网页展示
- Kubernetes v1.15.3 升级到 v1.18.5 心得
- 结巴分词seo应用,Python jieba库基本用法及案例参考
- nali一个可以查询IP归属和CDN的命令
- 图片采集,python多线程采集头像图片源码附exe程序及资源包
- Python json数据爬取处理,红点官网大奖设计作品爬取
- 斗图狂魔必备沙雕表情包,python多线程爬取斗图啦表情图片
- 5个基本Linux命令行工具的现代化替代品
- Chrome 84 正式发布,支持私有方法、用户空闲检测!
- 类及数据库的应用,G-MARK网站数据Python爬虫系统的构建
- 获取素材图无忧,Pixabay图库网Python多线程采集下载
- Python关键词数据采集案例,5118查询网站关键词数据采集
- Python结巴分词,字符串余弦相似度算法实现关键词筛选及整理
- git的分支远程连接和远程分支的拉取推送及冲突处理
- requests session的应用,python金点设计奖数据爬虫