
回顾JDK代理 Spring AOP 用到了两种动态代理模式:JDK动态代理和CGLIB动态代理,两种动态代理形成互补。今天我们来尝试纯手写一个简版的JDK动态代理,来了解它的底层实现原理。我们先来回顾一下JDK动态代理 动态代理的条件 两个角色: 代理对象,被代理对象 代理对象需要完成被代理对象的需要完成的业务操作 代理对象持有被代理对象的引用 JDK动态代理 被代理对象必须实现接口,CGLIB动态代理被代理类和方法不能用final修饰 实现代码 被代理对象实现的目标接口 package com.nqmysb.proxy.jdk; /** * 目标接口 * @author liaocan * */ public interface Subject { /* * 抽象业务方法 */ void businessMethod(); } 被代理的目标对象 package com.nqmysb.proxy.jdk.impl; import com.nqmysb.proxy.jdk.Subject; /** * 具体的目标对象,实现目标接口的方法 * @author liaocan * */ public class RealSubject implements Subject { @Override public void businessMethod() { System.out.println("我在进行具体的业务操作。。。。。。"); } } 被代理的目标对象 package com.nqmysb.proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; /** * 代理类,必须实现JDK中的InvocationHandler 这是JDK动态代理的标志 * 可以理解:需要实现JDK的代理,必须要这个证书 * @author liaocan * */ public class DynamicProxy implements InvocationHandler { private Subject target; //被代理对象的引用 这里是代理对象的接口 说明 该代理类是可以代理该接口下的所有子类的 //动态生成代理对象 public Object getInstance(Subject target) throws Exception{ //获取代理对象 this.target = target; //代理的对象必须是 suject的实现类 Class<? extends Subject> clazz = target.getClass(); //被代理对象的class是:class com.nqmysb.proxy.jdk.impl.RealSubject System.out.println("被代理对象的class是:"+clazz); //被代理对象的类加载器:sun.misc.Launcher$AppClassLoader@20eb607d ClassLoader classLoader = clazz.getClassLoader(); System.out.println("被代理对象的类加载器:"+classLoader); //被代理对象的class是:[Ljava.lang.Class;@602f892f 返回所有实现接口的数组 [interface com.nqmysb.proxy.jdk.Subject] Class<?>[] classs = clazz.getInterfaces(); System.out.println("被代理对象实现的接口:"+Arrays.asList(classs)); //proxy的静态方法 newProxyInstance // 参数说明:被代理对象的类加载器 clazz.getClassLoader ,被代理对象实现的接口 clazz.getInterfaces() ,当前对象 this //this 参数传入 把代理类和被代理类产生关联 当要执行被代理对象的方法时 会自动调用代理对象invoke方法进行代理执行 return Proxy.newProxyInstance(classLoader, classs, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始代理..."); System.out.println("代理之前做的事情..."); System.out.println("------------"); //执行代理方法 method.invoke(this.target, args); System.out.println("------------"); System.out.println("代理之后做的事情..."); System.out.println("结束代理..."); return null; } } JDK动态代理测试类 package com.nqmysb.proxy.jdk; import com.nqmysb.proxy.jdk.impl.RealSubject; /** * JDK动态代理测试类 * * @author liaocan * */ public class DynamicProxyTest { public static void main(String[] args) { try { Subject subject = (Subject)new DynamicProxy().getInstance(new RealSubject()); System.out.println(subject.getClass()); subject.businessMethod(); } catch (Exception e) { e.printStackTrace(); } } } 运行结果如下 被代理对象的class是:class com.nqmysb.proxy.jdk.impl.RealSubject 被代理对象的类加载器:sun.misc.Launcher$AppClassLoader@73d16e93 被代理对象实现的接口:[interface com.nqmysb.proxy.jdk.Subject] class com.sun.proxy.$Proxy0 开始代理... 代理之前做的事情... ------------ 我在进行具体的业务操作。。。。。。 ------------ 代理之后做的事情... 结束代理... 手写JDK动态代理 JDK做了哪些事? 在自己手写JDK动态代理之前我们分析一下JDK动态代理帮我们做了哪些事情? 代理类必须实现JDK中的java.lang.reflect.InvocationHandler接口,这个接口中只有一个方法,如下: /** * @param proxy the proxy instance that the method was invoked on * @param method the {@code Method} instance corresponding to * the interface method invoked on the proxy instance. * @param args an array of objects containing the values of the * arguments passed in the method invocation on the proxy instance, */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; 代理类需要重写和实现该方法,在这个方法里面我们可以进行附加业务操作,再调用被代理对象的方法。从而对被代理对象的方法进行扩展。 生成代理类对象,代理类中的getInstance(Subject target)方法产生代理对象。关键代码如下: /** * 该方法传入三个参数:1.类加载,用于加载动态生成代理类。2.被代理类实现的所有接口 3.代理类对象本身引用,用于回调我们实现的invoke方法 */ Proxy.newProxyInstance(classLoader, classs, this) 从上面两点可以得出JDK动态代理帮我们生成了代理类,代理实现了被代理类的所有接口,并将代理类加载到JVM中,生成代理实例,并回调我们实现的invoke方法进行动态代理。JDK给我们提供了 java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 代理类 其中JDK1.8版本中的Proxy类中有一个私有的静态内部类 private static final class ProxyClassFactory 该类用于生成代理类的class对象。源代码如下: private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { //$Proxy 作为代理类的标志 private static final String proxyClassNamePrefix = "$Proxy"; private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; int accessFlags = Modifier.PUBLIC | Modifier.FINAL; for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } long num = nextUniqueNumber.getAndIncrement(); //动态生成代理类的全名 String proxyName = proxyPkg + proxyClassNamePrefix + num; //动态生成代理类class对象 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } } 手写JDK动态代理代码实现 前两个要素不变:目标接口和目标实现类。 被代理对象实现的目标接口 package com.nqmysb.proxy.jdk; /** * 目标接口 * @author liaocan * */ public interface Subject { /* * 抽象业务方法 */ void businessMethod(); } 被代理的目标对象 package com.nqmysb.proxy.jdk.impl; import com.nqmysb.proxy.jdk.Subject; /** * 具体的目标对象,实现目标接口的方法 * @author liaocan * */ public class RealSubject implements Subject { @Override public void businessMethod() { System.out.println("我在进行具体的业务操作。。。。。。"); } } 自定义类加载器 package com.nqmysb.myproxy.proxytools; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; /** * 自定义类加载器 * * 作用:将我们动态生成的class文件生成class对象加载JVM内存中 * @author liaocan * */ public class MyClassLoader extends ClassLoader{ private File baseDir; public MyClassLoader(){ Class<MyClassLoader> clazz = MyClassLoader.class; String basePath = clazz.getResource("").getPath(); // String basePath = clazz.getResource("/").getPath() +"com/nqmysb/myproxy/proxytools"; this.baseDir = new java.io.File(basePath); } /** * 自定义类加载方法 ,加载自定义类 */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { //获取需要加载的类的全名 com.nqmysb.myproxy.proxytools.$Proxy0 String className = MyClassLoader.class.getPackage().getName() + "." + name; if(baseDir != null){ //获取 class文件对象 E:\workspace\spring-source-analysis\spring-source-proxy\bin\com\nqmysb\myproxy\proxytools\$Proxy0.class File classFile = new File(baseDir,name.replaceAll("\\.", "/") + ".class"); if(classFile.exists()){ FileInputStream in = null; ByteArrayOutputStream out = null; try{ in = new FileInputStream(classFile); out = new ByteArrayOutputStream(); byte [] buff = new byte[1024]; int len; while ((len = in.read(buff)) != -1) { out.write(buff, 0, len); } //根据class文件和类名生成class对象 并加载到JVM中 return defineClass(className, out.toByteArray(), 0,out.size()); }catch (Exception e) { e.printStackTrace(); }finally{ if(null != in){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if(null != out){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } //删除Class文件 classFile.delete(); } } } return null; } /** * 测试了解Java类加载机制 * 1.类加载器的作用:类加载器负责将class文件读入JVM内存,并为之生成对应的java.lang.Class对象。 * java类加载器分为三类: * (Bootstrap ClassLoader) 启动类加载器 : 负责加载Java的核心类(如String、System等), rt.jar * 它比较特殊,因为它是由原生C++代码实现的,并不是java.lang.ClassLoader的子类 * (Extension ClassLoader )扩展类加载器:它负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext)中JAR包的类,java实现 * 我们可以通过把自己开发的类打包成JAR文件放入扩展目录来为Java扩展核心类以外的新功能。 * (ApplicationClassLoader)应用程序类加载器:它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,java实现 * 或CLASSPATH环境变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader来获取系统类加载器: * * 类加载机制之双亲委派 * 加载类时 会现递归交于父类加载器加载,如果所有父类没有加载才有自己加载,好处防止类的重复加载 * */ public static void main(String[] args) { //(Bootstrap ClassLoader) 启动类加载器 : 负责加载Java的核心类(如String、System等)rt.jar //它比较特殊,因为它是由原生C++代码实现的,并不是java.lang.ClassLoader的子类 所以输出结果为null //Returns the class loader for the class. Some implementations may use //null to represent the bootstrap class loader. This method will return //null in such implementations if this class was loaded by the bootstrap //class loader. System.out.println(String.class.getClassLoader()); System.out.println(HashMap.class.getClassLoader()); //sun.misc.Launcher$AppClassLoader System.out.println(MyClassLoader.class.getClassLoader().getClass().getName()); //sun.misc.Launcher$AppClassLoader System.out.println(ClassLoader.getSystemClassLoader().getClass().getName()); } } 自定义 InvocationHandler 接口 package com.nqmysb.myproxy.proxytools; import java.lang.reflect.Method; public interface MyInvocationHandler { Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } 自定义 Proxy 代理类 package com.nqmysb.myproxy.proxytools; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; /** * 自己实现的Proxy 对应JDK中的java.lang.reflect.Proxy类 * * @author liaocan * */ public class MyProxy { //换行符 private static String ln = "\r\n"; /** * 生成代理类对象 * 对应JDK中Proxy的newProxyInstance方法生成代理类对象 * JDK1.8中的实现是:通过Proxy 中的的静态内部类ProxyClassFactory =>(private static final class ProxyClassFactory)中的apply方法 * 注意:JDK1.7中是通过getProxyClass()方法实现 有点小区别 但是核心代码大致相同 * 核心代码如下: * //所有代理类的前缀 * private static final String proxyClassNamePrefix = "$Proxy"; * //代理类的序号 * long num = nextUniqueNumber.getAndIncrement(); * //代理类的全名 * String proxyName = proxyPkg + proxyClassNamePrefix + num; * //生成代理类class字节码文件 ,反编译生成的class文件可以发现代理类继承Proxy类实现了被代理类的implements的所有接口 =>(public final class $Proxy0 extends Proxy implements Subject ) * //generateProxyClass()可以再看看具体实现 * byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); * try { * //根据class字节码生成实例对象 * return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length); * } * * @param classLoader * @param interfaces * @param myInvocationHandler * @return */ public static Object newProxyInstance(MyClassLoader classLoader,Class<?>[] interfaces,MyInvocationHandler myInvocationHandler){ try{ //1、动态生成代理类的源代码 .java文件 String proxySrc = generateSrc(interfaces[0]); //2、将生成的源代码输出到磁盘,保存为.java文件 String filePath = MyProxy.class.getResource("").getPath(); //打jar包之后抛空指针 // String filePath = MyProxy.class.getResource("/").getPath() +"com/nqmysb/myproxy/proxytools/"; File f = new File(filePath + "$Proxy0.java"); FileWriter fw = new FileWriter(f); fw.write(proxySrc); fw.flush(); fw.close(); //3、编译源代码,并且生成.class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null); Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(f); CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable); task.call(); manager.close(); //4.将class文件中的内容,动态加载到JVM中来 //5.返回被代理后的代理类class对象 Class<?> proxyClass = classLoader.findClass("$Proxy0"); //通过class对象和参数获取指定构造对象 Constructor<?> c = proxyClass.getConstructor(MyInvocationHandler.class); //删除Java文件 f.delete(); return c.newInstance(myInvocationHandler); }catch (Exception e) { e.printStackTrace(); } return null; } /** * * 动态生成代理类的.java文件的代码内容 * @param interfaces * @return */ private static String generateSrc(Class<?> interfaces){ StringBuffer src = new StringBuffer(); //代理类包名 src.append("package com.nqmysb.myproxy.proxytools;" + ln); //导入反射包method类 src.append("import java.lang.reflect.Method;" + ln); //声明类名 实现被代理类的接口这里简化处理只实现一个接口, JDK中生成的代理类会继承proxy类 实现被代理类的所有接口 src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln); //生成代理类中持有MyInvocationHandler引用 //JDK中InvocationHandler是Proxy中的成员变量=>(protected InvocationHandler h;) //生成的代理类继承Proxy也继承了它的protected的成员变量,我们自己生成的代理类没有继承Proxy 所有需要自己提供 src.append("MyInvocationHandler h;" + ln); //代理类中构造方法中传入MyInvocationHandler给InvocationHandler初始化 src.append("public $Proxy0(MyInvocationHandler h) {" + ln); src.append("this.h = h;" + ln); src.append("}" + ln); //生成所有代理方法 for (Method m : interfaces.getMethods()) { src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln); src.append("try{" + ln); //通过代理方法对象 src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" +m.getName()+"\",new Class[]{});" + ln); //调用代理方法 src.append("this.h.invoke(this,m,null);" + ln); src.append("}catch(Throwable e){e.printStackTrace();}" + ln); src.append("}" + ln); } src.append("}"); System.out.println("动态生成的代理类$Proxy0:"+ln+src); /* 生成代理类如下: package com.nqmysb.myproxy.proxytools; import java.lang.reflect.Method; public class $Proxy0 implements com.nqmysb.myproxy.Subject{ MyInvocationHandler h; public $Proxy0(MyInvocationHandler h) { this.h = h; } public void businessMethod(){ try{ Method m = com.nqmysb.myproxy.Subject.class.getMethod("businessMethod",new Class[]{}); //此处调用的其实就是传入的com.nqmysb.myproxy.MyDynamicProxy对象的invoke方法 //然后再在invoke中对代理对象的方法前后进行业务方法操作 this.h.invoke(this,m,null); }catch(Throwable e){ e.printStackTrace(); } } } */ return src.toString(); } } 使用自己手写的动态代理 package com.nqmysb.myproxy; import java.lang.reflect.Method; import java.util.Arrays; import com.nqmysb.myproxy.proxytools.MyClassLoader; import com.nqmysb.myproxy.proxytools.MyInvocationHandler; import com.nqmysb.myproxy.proxytools.MyProxy; /** * 代理类,必须实现JDK中的InvocationHandler 这是JDK动态代理的标志 * 可以理解:需要实现JDK的代理,必须要这个证书 * @author liaocan * */ public class MyDynamicProxy implements MyInvocationHandler { private Subject target; //被代理对象的引用 这里是代理对象的接口 说明 该代理类是可以代理该接口下的所有子类的 //动态生成代理对象 public Object getInstance(Subject target) throws Exception{ //获取代理对象 this.target = target; //代理的对象必须是 suject的实现类 Class<? extends Subject> clazz = target.getClass(); //获取被代理类实现的所有接口 从这里可以看出如果被代理类没有实现接口,是无法使用JDK动态代理的 Class<?>[] classs = clazz.getInterfaces(); System.out.println("被代理对象实现的接口:"+Arrays.asList(classs)); return MyProxy.newProxyInstance(new MyClassLoader(), classs, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始代理..."); System.out.println("代理之前做的事情..."); System.out.println("------------"); //执行代理方法 method.invoke(this.target, args); System.out.println("------------"); System.out.println("代理之后做的事情..."); System.out.println("结束代理..."); return null; } } 测试动态代理 package com.nqmysb.myproxy; import com.nqmysb.myproxy.impl.RealSubject; /** * 手写JDK动态代理测试类 * * * @author liaocan * */ public class MyDynamicProxyTest { public static void main(String[] args) { try { Subject subject = (Subject)new MyDynamicProxy().getInstance(new RealSubject()); System.out.println(subject.getClass()); subject.businessMethod(); } catch (Exception e) { e.printStackTrace(); } } } 运行结果 被代理对象实现的接口:[interface com.nqmysb.myproxy.Subject] 动态生成的代理类$Proxy0: package com.nqmysb.myproxy.proxytools; import java.lang.reflect.Method; public class $Proxy0 implements com.nqmysb.myproxy.Subject{ MyInvocationHandler h; public $Proxy0(MyInvocationHandler h) { this.h = h; } public void businessMethod(){ try{ Method m = com.nqmysb.myproxy.Subject.class.getMethod("businessMethod",new Class[]{}); this.h.invoke(this,m,null); }catch(Throwable e){e.printStackTrace();} } } class com.nqmysb.myproxy.proxytools.$Proxy0 开始代理... 代理之前做的事情... ------------ 我在进行具体的业务操作。。。。。。 ------------ 代理之后做的事情... 结束代理... 总结 动态代理的底层实现的原理: 反射机制:获取被代理类的方法,并实现动态字节码重组生成新的代理类值得注意:JDK动态代理和CGLIB动态代理使用的反射机制是不一样的, 分别是:java.lang.reflect.Method和net.sf.cglib.reflect.FastClass 类实现的 函数回调机制:将代理类需要执行的附加方法传入新生成的代理类中,执行被代理类方法时会回调代理类的方法 代码工程 github地址:https://github.com/nqmysb/spring-source-analysis
Springboot2.0从零开始搭建脚手架(三)-集成swagger2+lombok+fastjosn+MybatisPlus分页插件+sqlj执行性能监控+ 添加依赖 <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.49</version> <!-- <scope>test</scope> --> </dependency> <!-- TypeBuilder --> <dependency> <groupId>com.github.ikidou</groupId> <artifactId>TypeBuilder</artifactId> <version>1.0</version> <!-- <scope>test</scope> --> </dependency> <!-- swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency> 添加MP分页插件和sql执行监控配置 package com.nqmysb.scaffold.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor; /** * @author liaocan * @since 2018-08-10 */ @Configuration @MapperScan("com.nqmysb.scaffold.mapper.*.*") public class MybatisPlusConfig { /** * 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } /** * SQL执行效率插件 * 性能分析拦截器,用于输出每条 SQL 语句及其执行时间 */ @Bean // @Profile({"dev","pro"})// 设置 dev pro 环境开启 public PerformanceInterceptor performanceInterceptor() { return new PerformanceInterceptor(); } } 主配置文件 3.添加mapper地址 #服务端口 server: port=8080 # druid配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:oracle:thin:@//192.168.6.6:1521/orclpdb username: nqmysb password: nqmysb druid: initial-size: 2 max-active: 30 min-idle: 2 max-wait: 1234 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 5 filter: stat: enabled: true filters: stat,wall,log4j connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 web-stat-filter: enabled: true stat-view-servlet: enabled: true reset-enable: false aop-patterns: com.nqmysb.scaffold.user.service.*.*,com.nqmysb.scaffold.user.controller.*.* mybatis-plus: mapper-locations: classpath:/mapper/*/*Mapper.xml swagger配置类 package com.nqmysb.scaffold.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 // 启用Swagger2 public class Swagger2 { @Bean public Docket createRestApi() {// 创建API基本信息 return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.nqmysb.scaffold.controller"))// 扫描该包下的所有需要在Swagger中展示的API,@ApiIgnore注解标注的除外 .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() {// 创建API的基本信息,这些信息会在Swagger UI中进行显示 return new ApiInfoBuilder() .title("微服务后台脚手架工程API")// API 标题 .description("swagger2API清单")// API描述 .version("1.0")// 版本号 .build(); } } 接口添加文档描述 @Controller @RequestMapping("/user") @Api(value="用户资源", tags="用户管理") public class UserinfoController { @Autowired UserinfoServiceImpl userinfoServiceImpl; @ApiOperation(value="查询用户信息", notes="通过用户输入的条件,查询满足条件的用户信息列表", httpMethod = "GET") @ApiImplicitParams({ @ApiImplicitParam(paramType = "query", name = "userId", dataType = "string", required = false, value = "用户编号"), @ApiImplicitParam(paramType = "query", name = "userName", dataType = "string", required = false, value = "用户账号,可以模糊查找"), @ApiImplicitParam(paramType = "query", name = "fullName", dataType = "string", required = false, value = "用户姓名,可以模糊查找"), @ApiImplicitParam(paramType = "query", name = "email", dataType = "string", required = false, value = "电子邮件,可以模糊查找"), @ApiImplicitParam(paramType = "query", name = "mobile", dataType = "string", required = false, value = "联系方式,可以模糊查找"), @ApiImplicitParam(paramType = "query", name = "status", dataType = "string", required = false, value = "用户状态,0:停用、1:正常") }) @RequestMapping("/getUsers") @ResponseBody public ArrayList<Userinfo> getUsers() { Wrapper<Userinfo> queryWrapper = null; ArrayList<Userinfo> data = (ArrayList<Userinfo>) userinfoServiceImpl.list(queryWrapper); return data; } 访问swagger页面 http://localhost:8080/swagger-ui.html 开启logback日志 添加配置文件 logback.xml <?xml version="1.0" encoding="UTF-8" ?> <configuration scan="true" scanPeriod="1800 seconds" debug="true"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <logger name="com.nqmysb.scaffold.dao" level="DEBUG" /> <root level="info"> <appender-ref ref="STDOUT" /> </root> </configuration> 工程源代码地址 https://github.com/nqmysb/springboot-scaffold
基于D3.js绘图组件的后端架构师技术栈图谱树 组件效果 GitHub项目传送门 https://github.com/nqmysb/knowledge_graph 效果预览地址 https://liaocan.top/knowledge_graph/
第一步:点击F12进入开发者模式 第二步:Ctrl+shift+p 输入full 第三步:点击screenshot 即可下载完整网页长图 如下:效果
创建对象几种方法 第一种方式:字面量 var o1 = {name: 'o1'}; var o2 = new Object({name: 'o2'}); 第二种方式:构造函数 var M = function (name) { this.name = name; }; var o3 = new M('o3'); 第三种方式:Object.create var p = {name: 'p'}; var o4 = Object.create(p); M.prototype.say = function () { console.log('say hi'); }; var o5 = new M('o5'); new 运算符的工作原理 var new2 = function (func) { //生成一个新对象 继承自构造函数的原型对象 var o = Object.create(func.prototype); // 执行构造函数 转移上下文 var k = func.call(o); if (typeof k === 'object') { return k; } else { return o; } }; var o4 = Object.create(p) 是通过原型链来链接的 o4.__proto__ === p ===》trueo4其实是一个空对象,并没有name这个属性,他是通过原型链来链接到p 找到name属性,所以和上面三个不同 原型,构造函数,实例,原型链 对象就是实例 函数的实例都是对象,对象就有__proto__属性 获取它的原型对象 函数的原型对象是function的原型 也就是说 M 是function的实例M.__proto__.constructor=== Function ===>true M.__proto__ === Function.prototype ===>true new 运算符操作的函数 就是构造函数 构造函数可以可以根据prototype 获取它的实例的原型对象o3.__proto__=== M.prototype ==>true 任何函数都可以是构造函数,只要用 new 去操作它 ,没有就是普通函数 函数才有prototype 对象没有原型对象可以通过constructor获取对应的构造函数 M.prototype.constructor === M ==>trueo3 instanceof M ==> trueo3.__proto__.constructor ===M ==>true Object 既是一个对象 也是一个构造函数 Object.prototype 获得原型链的顶端o3.__proto__.__proto__===Object.prototype ===》true 原型链的作用 ,可以给共同原型链对象添加方法,其他对象都可以获得这个方法 M.prototype.say = function () { console.log('say hi'); }; o3.say() say hi o5.say() say hio3对象会通过 .__proto__.__proto__去找它的原型链对象是否有这个方法如果有则执行 都没有就报错 instanceof的原理 实例 instanceof 构造函数 就是判断实例.__proto__ === 构造函数.prototype 两边得到都是原型对象 看它是否是同一个引用 instanceof Object ==>true o3.__proto__ ===M.prototype ==>trueM.prototype._proto_ ===Object.prototype ==>trueo3.__proto__.__proto__ ===Object.prototype ==>true new运算符 //new 运算符的工作原理 var new2 = function (func) { //生成一个新对象 继承自构造函数的原型对象 var o = Object.create(func.prototype); // 执行构造函数 转移上下文 var k = func.call(o); //判断构造函数返回的是不是一个对象 if (typeof k === 'object') { return k; } else { return o; } 验证 new2 是否和 new 一个效果 o6 = new2(M);M {name: undefined}o6.__proto__=== M.prototypetrueo6.__proto__.constructor ===Mtrueo6 instanceof Mtrue