Demo什么是代理静态代理继承聚合动态代理接口实现类方法增强动态代理类测试总结 Demo在分析动态代理之前,我们先做一个 Demo,来模拟一下 MyBatis 是如果执行 SQL 的 思路是: 写一个 @Select 注解,注解里面写上 SQL 语句写一个 SessionFactory,获取 mapper写一个动态代理,执行 Dao 接口中的 query 方法 @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Select { String value() default "";} ```java public interface UserDao { @Select(“select * from user”) List query(); } ```javapublic class MyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable { System.out.println("db connection..."); Select annotation = method.getAnnotation(Select.class); String sql = annotation.value(); System.out.println("execute sql: " + sql); Class<?> returnType = method.getReturnType(); return null; }}public class SessionFactory { public static <T> T getMapper(Class<T> clazz) { return (T) Proxy.newProxyInstance(SessionFactory.class.getClassLoader(), new Class[]{clazz}, new MyInvocationHandler()); }} public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.register(AppConfig.class); ac.refresh(); UserDao mapper = SessionFactory.getMapper(UserDao.class); mapper.query();}// db connection...// execute sql: select * from user 这就是一个典型的动态代理案例,相当于我们自己实现了一个接口实现类,并重写了业务逻辑 什么是代理代理就是对原有对象进行了一个“改变”,代理分为静态代理和动态代理, 静态代理静态代理的实现有“继承”和“聚合”两种方法 继承优点:使用简单,缺点:不灵活,当有一个新的需求,就需要新写一个类 public class UserService { public void find() { System.out.println("find users"); }}public class UserServiceProxyLog extends UserService { @Override public void find() { System.out.println("record log"); super.find(); }}public class Test { public static void main(String[] args) { UserService userService = new UserServiceProxyLog(); userService.find(); }} 聚合 代理对象和目标对象,都必须实现同一个接口代理对象必须包含目标对象执行代理对象重写的方法 优点:层次清晰,缺点:如果业务流程很复杂,代码量就会比较大 public interface Service { void query();}public class UserService implements Service { @Override public void query() { System.out.println("query for user"); }}public class ServiceLog implements Service { Service target; public ServiceLog(Service target) { this.target = target; } @Override public void query() { System.out.println("log"); target.query(); }}public class ServicePower implements Service { Service target; public ServicePower(Service target) { this.target = target; } @Override public void query() { System.out.println("power"); target.query(); }}public class Test { public static void main(String[] args) { Service proxy = new ServiceLog(new UserService()); proxy.query(); Service proxy1 = new ServicePower(proxy); proxy1.query(); }} 动态代理动态代理有 JDK 动态代理和 CGLIB 动态代理,这里我们模拟一下 JDK 的 Proxy 接口public interface Service { void query(String name);} 实现类我们现在的需求是给实现类加上一个日志方法 public class UserService implements Service { @Override public void query(String name) { System.out.println("query " + name); }} 方法增强public interface MyInvocationHandler { void invoke();} 动态代理类public class MyProxy { public static final String LINE = "\n"; public static final String TAB = "\t"; @SuppressWarnings("unchecked") public static <T> T getInstance(Object target, MyInvocationHandler handler) { Class<?> clazz = target.getClass().getInterfaces()[0]; String interfaceName = clazz.getSimpleName(); String packageContent = "package org.eric.proxy;" + LINE; String importContent = "import " + clazz.getName() + ";" + LINE; importContent += "import " + MyInvocationHandler.class.getName() + ";" + LINE; String clazzFirstLineContent = "public class $Proxy implements " + interfaceName + " {" + LINE; String fieldContent = TAB + "private " + interfaceName + " target;" + LINE; fieldContent += TAB + "private MyInvocationHandler handler;" + LINE; String constructorContent = TAB + "public $Proxy(" + interfaceName + " target, MyInvocationHandler handler) {" + LINE; constructorContent += TAB + TAB + "this.target = target;" + LINE; constructorContent += TAB + TAB + "this.handler = handler;" + LINE; constructorContent += TAB + "}" + LINE; String methodContent = ""; Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Class<?>[] args = method.getParameterTypes(); String argsContent = ""; String paramsContent = ""; int flag = 0; for (Class<?> arg : args) { String temp = arg.getSimpleName(); // String p0, String p1, argsContent += temp + " p" + flag + ", "; // p0, p1, paramsContent += "p" + flag + ", "; flag++; } if (args.length > 0) { argsContent = argsContent.substring(0, argsContent.lastIndexOf(",")); paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",")); } String returnTypeName = method.getReturnType().getSimpleName(); String methodName = method.getName(); methodContent += TAB + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + LINE; methodContent += TAB + TAB + "handler.invoke();" + LINE; methodContent += TAB + TAB + "target." + methodName + "(" + paramsContent + ");" + LINE; methodContent += TAB + "}" + LINE; } String content = packageContent + importContent + clazzFirstLineContent + fieldContent + constructorContent + methodContent + "}"; try { File file = new File("d:\\org\\eric\\proxy\\$Proxy.java"); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } if (!file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file); fw.write(content); fw.flush(); fw.close(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); Iterable<? extends JavaFileObject> javaFileObjects = fileManager.getJavaFileObjects(file); JavaCompiler.CompilationTask t = compiler.getTask(null, fileManager, null, null, null, javaFileObjects); t.call(); fileManager.close(); URL[] urls = new URL[]{new URL("file:D:\\\\")}; URLClassLoader urlClassLoader = new URLClassLoader(urls); Class<?> aClass = urlClassLoader.loadClass("org.eric.proxy.$Proxy"); Constructor<?> constructor = aClass.getConstructor(clazz, MyInvocationHandler.class); return (T) constructor.newInstance(target, handler); } catch (Exception e) { e.printStackTrace(); } return null; }} 测试有了动态代理类后,我们就可以用,动态代理类来增强现有实现类的方法 public class Test { public static void main(String[] args) { Service target = new UserService(); Service proxy = MyProxy.getInstance(target, new MyInvocationHandler() { @Override public void invoke() { System.out.println("record log..."); } }); proxy.query("hello"); }}// record log...// query hello 总结动态代理其实就是改变原有实现类的方法,核心是,直接在内存中生成字节码文件,并返回实现类的接口