动态代理模式

   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;
	}

	@Override
	public 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;
	}

	@Override
	public 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;
	}

	@Override
	public 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{
	
	@Override
	public 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;
	}
	
	@Override
	public 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());
	}
	
	@Override
	public Object getTarget() {
		return target;
	}

	@Override
	public 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)书写测试类方法


@Test
	public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值