动态代理模式

来源:互联网 发布:淘宝卖家需求 编辑:程序博客网 时间:2024/06/05 03:17

   Spring的功能之一为动态代理,之前一直一知半解,但看了 马士兵老师 的 Java代理模式 视频后,可以理解所谓的面向切面编程思想,所以写下记录。

             首先,Spring的代理分为两种, (1)通过JDK的Proxy方式,通过被代理的接口生成代理         (2)通过CGLib,插入二进制码进入class文件生成代理

                         默认情况下,如果被代理对象实现某个接口(如Service,Dao层等),则使用Proxy生成代理;

                         如果未实现接口,则通过CGLib生成代理(要求被代理对象有空参的构造函数);

                         同时,也可以通过Spring配置,强制使用某种方式。(Spring3.2后不再需要导入cglib包,已包含)

                          <aop:aspectj-autoproxy proxy-target-class="true"/>    

                        为true所有代理使用cglib,为false所有代理使用Proxy。


           为了更好理解JDK的Proxy机制,以下为模拟JDK动态代理类,基于接口实现日志功能:

           (1)书写实体类(User),业务逻辑层接口(UserService),以及实现类(UserServiceImpl)

                   

package com.entity;public class User {private int id;private String account;private String password;public User() {super();}public User(int id, String account, String password) {super();this.id = id;this.account = account;this.password = password;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getAccount() {return account;}public void setAccount(String account) {this.account = account;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + ((account == null) ? 0 : account.hashCode());result = prime * result + id;result = prime * result + ((password == null) ? 0 : password.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;User other = (User) obj;if (account == null) {if (other.account != null)return false;} else if (!account.equals(other.account))return false;if (id != other.id)return false;if (password == null) {if (other.password != null)return false;} else if (!password.equals(other.password))return false;return true;}@Overridepublic String toString() {return "User [id=" + id + ", account=" + account + ", password=" + password + "]";};}

package com.service;import com.entity.User;public interface UserService {public User login(User checkUser);public void login();}

package com.service.impl;import com.entity.User;import com.service.UserService;public class UserServiceImpl implements UserService{@Overridepublic User login(User checkUser) {User loginUser = null;if (null != checkUser) {if ("admin".equals(checkUser.getAccount()) && "admin".equals(checkUser.getPassword())) {loginUser = new User(1, "admin", "admin");// 模仿从数据库查询出匹配信息System.out.println("查询到用户");}}return loginUser;}@Overridepublic void login(){System.out.println("user has logined");}}


           (2)书写代理调用类接口(InvocationHandler),以及其实现类(LogInvocation)

                      其中包含被代理对象(target),代理方法(如startLog()、endLog())用于添加日志,以及最重要的invoke()方法,用于

                       被动态生成的代理对象($Proxy1)统一调用。

             

package com.proxy.invocation;import java.lang.reflect.Method;public interface InvocationHandler {      public Object getTarget();   public Object invoke(Object o,Method m,Object...params);}

  

package com.proxy.invocation;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Calendar;public class LogInvocation implements InvocationHandler {private Object target;private String msg;public LogInvocation(Object target,String msg) {super();this.target = target;this.msg = msg;}private void startLog(){//模拟日志开始System.out.println(this.msg + "has start at " + Calendar.getInstance().getTime());}private void endLog(){//模拟日志结束System.out.println(this.msg + "has stop at " + Calendar.getInstance().getTime());}@Overridepublic Object getTarget() {return target;}@Overridepublic Object invoke(Object o, Method m, Object... params) {Object obj = null;try {startLog();obj = m.invoke(target, params);endLog();} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {e.printStackTrace();}return obj;}}

           (3)代理生成类(Proxy)

                     代理生成类用于接收接口参数以及代理调用类对象,动态生成代理对象。

               其包含有与被代理对象所有方法相同的方法声明,在外部看来,其使用与被代理对象一致。但所有的方法实现都是通过调用包含的        InvocationHandler的invoke方法

         

package com.proxy;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.net.URL;import java.net.URLClassLoader;import java.util.Arrays;import javax.tools.JavaCompiler;import javax.tools.JavaCompiler.CompilationTask;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;import com.proxy.invocation.InvocationHandler;public class Proxy {private static String rt = "\r\n";private static String porjectPath = System.getProperty("user.dir");    private static String fileName = "$Proxy1";    private static String handlerClass = "com.proxy.invocation.InvocationHandler";public static <T> T newProxyInstance(Class<T> clazz, InvocationHandler invocation) {StringBuffer proxyClass = Proxy.getProxyClass(clazz,invocation);Proxy.createClass(clazz , proxyClass.toString() , fileName+".java");Proxy.compilerClass(clazz,fileName);return Proxy.loadClass(clazz,fileName,invocation);}//生成代理类内容private static StringBuffer getProxyClass(Class clazz, InvocationHandler invocation) {StringBuffer strB = new StringBuffer();strB.append(clazz.getPackage() +";"+rt+rt);strB.append("import java.lang.reflect.Method;"+rt + rt);strB.append("public class " + fileName + " implements "+clazz.getName()+" {"+rt + rt);strB.append("     private "+handlerClass + " h;" + rt+ rt);strB.append(" public "+fileName+"("+ handlerClass +" h){"+rt);strB.append("         this.h = h;" + rt);strB.append("    }" + rt + rt);Method[] methods = invocation.getTarget().getClass().getDeclaredMethods();for(Method m : methods){strB.append("    @Override"+rt);Class returnType = m.getReturnType();String Modifiers = Modifier.toString(m.getModifiers());//获取方法参数及类型,并包装成各种字符串Class[] paramTypes = m.getParameterTypes();StringBuffer params = new StringBuffer();StringBuffer paramsClass = new StringBuffer();StringBuffer paramsName = new StringBuffer();for(int i = 0,length = paramTypes.length;i < length;i++){Class paramClass = paramTypes[i];if(params.length()>0){paramsName.append(',');params.append(',');}String paramName = "arg"+i;paramsName.append(paramName);paramsClass.append(','+paramClass.getName()+".class");params.append(paramClass.getName()+" "+paramName);}//生成代理方法strB.append(" "+Modifiers+" "+returnType.getName()+" "+ m.getName() + "("+params+"){" + rt);strB.append("     Object[] params = new Object[]{"+paramsName+"};"+rt);if("void"!=returnType.getName()){strB.append("     "+returnType.getName()+" returnValue = null;"+rt);}strB.append("     try {"+rt);strB.append("            Method m = " + clazz.getName()+".class.getMethod(\""+m.getName()+"\""+paramsClass+");"+rt);/*判断代理方法是否有返回值,据此调用不同方法if("void"!=returnType.getName()){    strB.append("            returnValue = ("+returnType.getName()+")h.invokeObj(this,m,params);" + rt);}else{strB.append("            h.invokeVoid(this,m,params);" + rt);}*/strB.append("            ");if("void"!=returnType.getName()){    strB.append("returnValue = ("+returnType.getName()+")");}strB.append("h.invoke(this,m,params);" + rt);strB.append("        } catch (NoSuchMethodException | SecurityException e) {"+rt);strB.append("            e.printStackTrace();"+rt);strB.append("        }"+rt);if("void"!=returnType.getName()){strB.append("        return returnValue;"+rt);}strB.append("    }"+rt + rt);}strB.append("}"+rt);return strB;}//生成.java源文件private static void createClass(Class orignalClass , String classContend , String fileName) {String packagePath = orignalClass.getPackage().getName().replace('.', '/');//将包路径转为文件路径File newClass = new File(porjectPath + "/src/"+ packagePath + "/" + fileName);FileWriter fw = null;try {fw = new FileWriter(newClass);fw.write(classContend);fw.flush();} catch (IOException e) {e.printStackTrace();} finally {if (null != fw) {try {fw.close();} catch (IOException e) {e.printStackTrace();}}}}// 编译class文件private static void compilerClass(Class clazz,String fileName) {String packagePath = clazz.getPackage().getName().replace('.', '\\');String srcFile = porjectPath + "\\src\\"+ packagePath + "\\" + fileName+".java";String classOutputFolder = porjectPath + "\\bin";JavaCompiler compiler = null;StandardJavaFileManager fileMgr = null;Iterable units = null;Iterable options = null;CompilationTask t = null;try {compiler = ToolProvider.getSystemJavaCompiler();fileMgr = compiler.getStandardFileManager(null, null, null);units = fileMgr.getJavaFileObjects(srcFile);options = Arrays.asList("-d", classOutputFolder);//生成位置t = compiler.getTask(null, fileMgr, null, options, null, units);t.call();fileMgr.close();} catch (IOException e) {e.printStackTrace();}}//加载编译的class至内存中@SuppressWarnings("unchecked")private static <T> T loadClass(Class<T> clazz,String fileName, InvocationHandler invocation){T instance = null;try {URL[] urls = new URL[] {new URL("file:\\"+porjectPath+"\\bin\\")};URLClassLoader ul = new URLClassLoader(urls);Class c = ul.loadClass(clazz.getPackage().getName()+"."+fileName);Constructor constructor = c.getConstructor(Class.forName(handlerClass));instance = (T) constructor.newInstance(invocation);} catch (Exception e) {e.printStackTrace();}return instance;}}


           (4)书写测试类方法


@Testpublic void testGetProxy(){UserService userService = new UserServiceImpl();InvocationHandler logInvocation = new LogInvocation(userService,"User Service");UserService proxy1 = (UserService)Proxy.newProxyInstance(UserService.class, logInvocation);    User user = proxy1.login(new User(0,"admin","admin"));System.out.println(user);proxy1.login();}


0 0
原创粉丝点击