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