代理模式网上有一大片一大片的技术帖子,这里不做过多的论述,
代理模式传送门
下面来一步步从静态代理演化为动态代理。
首先来写一个静态代理的实例:
首先 subject (抽象主题角色):IUserService
package com.soft.day1103.dao;
import com.soft.test.model.User;
import java.util.List;
public interface IUserService {
List findAllUser();
User findUserById(String id);
int add(User user);
}
package com.soft.day1103.dao.impl;
import com.soft.day1103.dao.IUserService;
import com.soft.test.model.User;
import java.util.ArrayList;
import java.util.List;
public class UserServiceImpl implements IUserService {
@Override
public List findAllUser() {
System.out.println("查询到所有用户信息");
return new ArrayList();
}
@Override
public User findUserById(String id) {
System.out.println("通过id" + id + " 查询到用户信息");
return new User();
}
@Override
public int add(User user) {
System.out.println("添加成功");
return 0;
}
}
proxy代理主题角色 UserServiceStaticProxy
package com.soft.day1103.proxy;
import com.soft.day1103.dao.IUserService;
import com.soft.day1103.dao.impl.UserServiceImpl;
import com.soft.test.model.User;
import java.util.List;
public class UserServiceStaticProxy implements IUserService {
// 维持真是对象的引用
private UserServiceImpl userService = new UserServiceImpl();
/*做一些代理操作逻辑 start*/
/**
* 方法执行前的逻辑处理
* @param methodName
*/
private void pre(String methodName){
System.out.println(methodName + "开始执行");
}
/**
* 方法之后结束后的逻辑处理
* @param methodName
*/
private void after(String methodName){
System.out.println(methodName + "执行结束");
}
/*做一些代理操作 end*/
@Override
public List findAllUser() {
pre("findAllUser");
List list = userService.findAllUser();
after("findAllUser");
return list;
}
@Override
public User findUserById(String id) {
pre("findUserById");
User user = userService.findUserById(id);
after("findUserById");
return user;
}
@Override
public int add(User user) {
pre("add");
int addRes = userService.add(user);
after("add");
return addRes;
}
}
实体类:(测试用的没有意义)
package com.soft.day1103.model;
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试代理类
package com.soft.day1103.proxy;
import com.soft.day1103.dao.IUserService;
import com.soft.test.model.User;
import org.junit.Test;
public class UserServiceStaticProxyTest {
private IUserService userService = new UserServiceStaticProxy();
@Test
public void findAllUser() throws Exception {
userService.findAllUser();
print();
}
@Test
public void findUserById() throws Exception {
userService.findUserById("111");
print();
}
@Test
public void add() throws Exception {
userService.add(new User());
print();
}
private void print(){
System.out.println("=====================================");
}
}
测试结果:
add开始执行
添加成功
add执行结束
=====================================
findAllUser开始执行
查询到所有用户信息
findAllUser执行结束
=====================================
findUserById开始执行
通过id111 查询到用户信息
findUserById执行结束
=====================================
结果可以看出,每次执行真是对象的方法的前后都会插入代理逻辑,这里就说明代理成功了。
但是静态代理有个缺点:
每个真实主题角色都必须有一个代理类。也就是如果再有一个主题角色,就必须再有一个代理类。会导致类的膨胀。
比如现在有个 CustomerServiceImpl 需要代理,这个时候就需要在弄一个代理类出来。
package com.soft.day1103.proxy;
import com.soft.day1103.dao.ICustomerService;
import com.soft.day1103.dao.impl.CustomerServiceImpl;
import com.soft.day1103.model.Customer;
import java.util.List;
public class CustomerServiceStaticProxy implements ICustomerService {
// 维持真是对象的引用
private ICustomerService userService = new CustomerServiceImpl();
/*做一些代理操作逻辑 start*/
/**
* 方法执行前的逻辑处理
*
* @param methodName
*/
private void pre(String methodName) {
System.out.println(methodName + "开始执行");
}
/**
* 方法之后结束后的逻辑处理
*
* @param methodName
*/
private void after(String methodName) {
System.out.println(methodName + "执行结束");
}
/*做一些代理操作 end*/
@Override
public List findAllCustomer() {
pre("findAllUser");
List list = userService.findAllCustomer();
after("findAllUser");
return list;
}
@Override
public Customer findUserById(String id) {
pre("findUserById");
Customer user = userService.findUserById(id);
after("findUserById");
return user;
}
@Override
public int add(Customer user) {
pre("add");
int addRes = userService.add(user);
after("add");
return addRes;
}
}
通过观察两个静态代理类,可以发现优化项,每个方法执行前后的代码重复,可以提成公共部分,但是由于执行的方法不同,这时候可以想到用反射来执行方法,然后反射执行方法前加上代理逻辑处理。通过优化后的代码:
package com.soft.day1103.proxy;
import com.soft.day1103.dao.IUserService;
import com.soft.day1103.dao.impl.UserServiceImpl;
import com.soft.test.model.User;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public class UserServiceStaticProxy implements IUserService {
// 维持真是对象的引用
private UserServiceImpl userService = new UserServiceImpl();
/*做一些代理操作逻辑 start*/
/**
* 方法执行前的逻辑处理
* @param methodName
*/
private void pre(String methodName){
System.out.println(methodName + "开始执行");
}
/**
* 方法之后结束后的逻辑处理
* @param methodName
*/
private void after(String methodName){
System.out.println(methodName + "执行结束");
}
/*做一些代理操作 end*/
@Override
public List findAllUser() {
return (List)excute("findAllUser",new Object[]{},null);
}
@Override
public User findUserById(String id) {
return (User)excute("findUserById",new Object[]{id},String.class);
}
@Override
public int add(User user) {
return (int)excute("add",new Object[]{user},User.class);
}
private Object excute(String methodName, Object[] param, Class... params){
Object obj = null;
try {
Method method = userService.getClass().getMethod(methodName, params);
pre(methodName);
obj = method.invoke(userService,param);
after(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return obj;
}
}
这样优化后在进行测试。
add开始执行 添加成功 add执行结束 ===================================== findAllUser开始执行 查询到所有用户信息 findAllUser执行结束 ===================================== findUserById开始执行 通过id111 查询到用户信息 findUserById执行结束 =====================================
然后将CustomerServiceStaticProxy里面的代码也做一个整理。
package com.soft.day1103.proxy;
import com.soft.day1103.dao.IUserService;
import com.soft.day1103.dao.impl.UserServiceImpl;
import com.soft.test.model.User;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public class UserServiceStaticProxy implements IUserService {
// 维持真是对象的引用
private UserServiceImpl userService = new UserServiceImpl();
/*做一些代理操作逻辑 start*/
/**
* 方法执行前的逻辑处理
* @param methodName
*/
private void pre(String methodName){
System.out.println(methodName + "开始执行");
}
/**
* 方法之后结束后的逻辑处理
* @param methodName
*/
private void after(String methodName){
System.out.println(methodName + "执行结束");
}
/*做一些代理操作 end*/
@Override
public List findAllUser() {
return (List)excute("findAllUser",new Object[]{},null);
}
@Override
public User findUserById(String id) {
return (User)excute("findUserById",new Object[]{id},String.class);
}
@Override
public int add(User user) {
return (int)excute("add",new Object[]{user},User.class);
}
private Object excute(String methodName, Object[] param, Class... params){
Object obj = null;
try {
Method method = userService.getClass().getMethod(methodName, params);
pre(methodName);
obj = method.invoke(userService,param);
after(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return obj;
}
}
对比两个静态代理类,还是有可以提取出来的部分,excute方法可以再次做拆分处理,放到一个共通处理类里面去。
提取后的代码
package com.soft.day1103.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class InvokeHandler {
/**
* 代理对象的引用
*/
private Object obj;
public InvokeHandler(Object obj) {
this.obj = obj;
}
/*做一些代理操作逻辑 start*/
/**
* 方法执行前的逻辑处理
* @param methodName
*/
private void pre(String methodName){
System.out.println(methodName + "开始执行");
}
/**
* 方法之后结束后的逻辑处理
* @param methodName
*/
private void after(String methodName){
System.out.println(methodName + "执行结束");
}
/*做一些代理操作 end*/
public Object excute(String methodName, Object[] param, Class... params){
try {
Method method = obj.getClass().getMethod(methodName, params);
pre(methodName);
obj = method.invoke(obj,param);
after(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return obj;
}
}
然后两个静态代理类,的变化比较大
package com.soft.day1103.proxy;
import com.soft.day1103.dao.IUserService;
import com.soft.day1103.dao.impl.UserServiceImpl;
import com.soft.test.model.User;
import java.util.List;
public class UserServiceStaticProxy implements IUserService {
// 维持真是对象的引用
private UserServiceImpl userService = new UserServiceImpl();
private InvokeHandler handler = new InvokeHandler(userService);
@Override
public List findAllUser() {
return (List) handler.excute("findAllUser", new Object[]{}, null);
}
@Override
public User findUserById(String id) {
return (User) handler.excute("findUserById", new Object[]{id}, String.class);
}
@Override
public int add(User user) {
return (int) handler.excute("add", new Object[]{user}, User.class);
}
}
看着是否清爽多了,这样代理逻辑单独封装,代理类对代理逻辑进行调用。
但是这样还有不爽的地方,那就是代码还比较死。如果代理处理逻辑有变化,或者类名有变化等,静态代理类就需要修改。
这时候想到用接口类替代InvokeHandler,然后需要代理的类交给代理逻辑类(InvokeHandler)进行处理。
这里这个接口名字就用jdk动态代理里面的名字InvocationHandler
package com.soft.day1103.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public Object invoke(Method method, Object[] param);
}
InvokeHandler
package com.soft.day1103.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class InvokeHandler implements InvocationHandler{
/**
* 代理对象的引用
*/
private Object obj;
public InvokeHandler(Object obj) {
this.obj = obj;
}
/*做一些代理操作逻辑 start*/
/**
* 方法执行前的逻辑处理
* @param methodName
*/
private void pre(String methodName){
System.out.println(methodName + "开始执行");
}
/**
* 方法之后结束后的逻辑处理
* @param methodName
*/
private void after(String methodName){
System.out.println(methodName + "执行结束");
}
/*做一些代理操作 end*/
@Override
public Object invoke(Method method, Object[] param) {
Object object = null;
try {
pre(method.getName());
object = method.invoke(obj,param);
after(method.getName());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return object;
}
}
UserServiceStaticProxy
package com.soft.day1103.proxy;
import com.soft.day1103.dao.IUserService;
import com.soft.test.model.User;
import java.lang.reflect.Method;
import java.util.List;
public class UserServiceStaticProxy implements IUserService {
// 维持真是对象的引用
private InvocationHandler handler;
public UserServiceStaticProxy(InvocationHandler handler) {
this.handler = handler;
}
@Override
public List findAllUser() {
return (List) excute("findAllUser", new Object[]{}, null);
}
@Override
public User findUserById(String id) {
return (User) excute("findUserById", new Object[]{id}, String.class);
}
@Override
public int add(User user) {
return (int) excute("add", new Object[]{user}, User.class);
}
private Object excute(String methodName, Object[] param, Class... params) {
Object obj = null;
try {
Method method = IUserService.class.getMethod(methodName, params);
obj = handler.invoke(method, param);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return obj;
}
}
注意下UserServiceStaticProxy代码也有些许调整,这是为了跟jdk动态代理的参数等等一致所做的调整,但是思想是一样的。
这时候如果在测试的话。代码就该这么写了
package com.soft.day1103.proxy;
import com.soft.day1103.dao.IUserService;
import com.soft.day1103.dao.impl.UserServiceImpl;
import com.soft.test.model.User;
import org.junit.Test;
public class UserServiceStaticProxyTest {
// 动态代理快成功了,这里就这样写了。
private IUserService userServicesss = new UserServiceImpl();
InvocationHandler handler = new InvokeHandler(userServicesss);
private IUserService userService = new UserServiceStaticProxy(handler);
@Test
public void findAllUser() throws Exception {
userService.findAllUser();
print();
}
@Test
public void findUserById() throws Exception {
userService.findUserById("111");
print();
}
@Test
public void add() throws Exception {
userService.add(new User());
print();
}
private void print(){
System.out.println("=====================================");
}
}
首先实例化出需要代理的类,将需要代理的类放入到代理逻辑类InvocationHandler中,
最后InvocationHandler设置给静态代理类 这样动态代理模型就初现了。
运行测试:
add开始执行 添加成功 add执行结束 ===================================== findAllUser开始执行 查询到所有用户信息 findAllUser执行结束 ===================================== findUserById开始执行 通过id111 查询到用户信息 findUserById执行结束 ===================================== 依然如初。
看看两个代理类现在的结构逻辑,几乎都是相同的套路,想着如果用代码生成这段代理类不就可以实现动态代理了么?
动态生成代理类,代理逻辑放到一处进行同一处理,就是动态代理的机制。
下面进行动态代理的实现。
首先动态代理类:
Proxy
package com.soft.day1103.proxy;
import com.soft.day1103.util.ClassMaker;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Proxy {
public static Object newProxyInstance(Class cla, InvocationHandler handler){
Class createClass = ClassMaker.createProxyClass(cla);
try {
Constructor constructor = createClass.getConstructor(InvocationHandler.class);
return constructor.newInstance(handler);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
类生成器 ClassMaker
package com.soft.day1103.util;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
public class ClassMaker {
private static final String IMPORT = "import ";
private static final String FENHAO = ";\n";
private static final String CLASS_PUBLIC = "public class ";
private static final String IMPLEMANT = " implements ";
private static final String QIANKUOHAO = "{";
private static final String HOUKUOHAO = "}";
private static final String PRIVATE_ = "private ";
private static final String PUBLIC = "public ";
private static final String NEWLINE = "\n";
public static Class classLoad(String classInfo, String className) {
try {
FileOutputStream fo = new FileOutputStream("D:\\myproject\\mybatisLearn\\src\\main\\java\\com\\soft\\day1103\\proxy\\" + className + ".java");
fo.write(classInfo.getBytes());
fo.flush();
Thread.sleep(1000);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
Class retu = null;
try {
retu = Class.forName("com.soft.day1103.proxy." + className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return retu;
}
public static Class createProxyClass(Class targetObject) {
StringBuffer sb = new StringBuffer();
sb.append("package com.soft.day1103.proxy;");
sb.append("import java.lang.reflect.Method;");
String interfaceName = targetObject.getSimpleName();
sb.append("import " + targetObject.getPackage().getName() +"."+ interfaceName + ";");
Method[] methods = targetObject.getMethods();
for (Method method : methods) {
Class returnType = method.getReturnType();
if(needImport(returnType)){
sb.append("import " + returnType.getPackage().getName() +"."+ returnType.getSimpleName() + ";");
}
}
// 生成类开始
sb.append(" public class " + interfaceName + "Proxy implements " + interfaceName + " {");
// 维持真实对象的引用
sb.append("private InvocationHandler handler;");
// 构造函数
sb.append("public " + interfaceName + "Proxy(InvocationHandler handler) {");
sb.append("this.handler = handler;}");
// 生成代理方法
for (Method method : methods) {
Class returnType = method.getReturnType();
Class[] methodParamClass = method.getParameterTypes();
sb.append("@Override" + NEWLINE);
sb.append(PUBLIC + returnType.getName() + " " + method.getName() + "(");
// 反射方法参数
StringBuffer refMethodParamters = new StringBuffer();
int index = 0;
// 方法参数
StringBuffer methodParams = new StringBuffer();
// 获取反射方法参数 class
StringBuffer refMethodClasses = new StringBuffer();
for (Class methodParam : methodParamClass) {
String paramName = methodParam.getName();
methodParams.append(paramName + " param" + index + ",");
refMethodParamters.append(" param" + index + ",");
String[] arr = paramName.split("\\.");
refMethodClasses.append(arr[arr.length - 1] + ".class" + ",");
index++;
}
if (methodParams.length() > 0) {
sb.append(methodParams.substring(0, methodParams.toString().length() - 1));
}
sb.append(")" + QIANKUOHAO + NEWLINE);
if (refMethodParamters.length() > 0) {
sb.append("Object[] objects = { " + refMethodParamters.substring(0, refMethodParamters.length() - 1) + "}" + FENHAO + NEWLINE);
} else {
sb.append("Object[] objects = new Object[" + methodParamClass.length + "]" + FENHAO + NEWLINE);
}
if (needReturn(returnType)) {
sb.append("return (" + returnType.getName() + ")");
}
String classes = null;
if(refMethodClasses.length() > 0){
classes = refMethodClasses.substring(0,refMethodClasses.length()-1);
}
sb.append("excute(\"" + method.getName() + "\" , objects , " + classes + ")" + FENHAO + NEWLINE);
sb.append(HOUKUOHAO + NEWLINE);
}
// 生成反射方法
sb.append(" private Object excute(String methodName, Object[] param, Class... params) {");
sb.append("Object obj = null;");
sb.append("try {");
sb.append("Method method = " + interfaceName + ".class.getMethod(methodName, params);");
sb.append("obj = handler.invoke(method, param);");
sb.append("} catch (NoSuchMethodException e) {");
sb.append(" e.printStackTrace();}");
sb.append("return obj;}");
sb.append(HOUKUOHAO + FENHAO + NEWLINE);
return classLoad(sb.toString(), interfaceName + "Proxy");
}
private static boolean needImport(Class tar) {
return "voidStringintdoublelangfloatbooleanbytecharshort".indexOf(tar.getSimpleName()) == -1;
}
private static boolean needReturn(Class tar) {
return "void".indexOf(tar.getSimpleName()) == -1;
}
}
现在进行动态代理的测试。看看是否可以动态实现代理。
注意:
由于仅仅是模仿,没办法生成java代理类之后直接放入内存。所以需要弄两次,第一次仅仅是生成java代码。然后才可以将其读入内存。 下面进行测试:
先来动态代理 IUserService
@Test
public void testproxy() throws Exception {
IUserService iUserService = (IUserService)Proxy.newProxyInstance(IUserService.class, handler);
iUserService.add(new User());
print();
}
测试结果:
add开始执行 添加成功 add执行结束 =====================================
将接口替换成 ICustomerService
@Test
public void testproxy() throws Exception {
ICustomerService iUserService = (ICustomerService)Proxy.newProxyInstance(ICustomerService.class, handler);
iUserService.add(new Customer());
print();
}
测试结果:
add开始执行 添加成功 add执行结束 =====================================
完了静态代理演化为动态代理了。希望明白了。 生成的两个代理类的代码:
package com.soft.day1103.proxy;
import java.lang.reflect.Method;
import com.soft.day1103.dao.IUserService;
import java.util.List;
import com.soft.test.model.User;
public class IUserServiceProxy implements IUserService {
private InvocationHandler handler;
public IUserServiceProxy(InvocationHandler handler) {
this.handler = handler;
}
@Override
public int add(com.soft.test.model.User param0) {
Object[] objects = {param0};
return (int) excute("add", objects, User.class);
}
@Override
public java.util.List findAllUser() {
Object[] objects = new Object[0];
return (java.util.List) excute("findAllUser", objects, null);
}
@Override
public com.soft.test.model.User findUserById(java.lang.String param0) {
Object[] objects = {param0};
return (com.soft.test.model.User) excute("findUserById", objects, String.class);
}
private Object excute(String methodName, Object[] param, Class... params) {
Object obj = null;
try {
Method method = IUserService.class.getMethod(methodName, params);
obj = handler.invoke(method, param);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return obj;
}
};
package com.soft.day1103.proxy;
import com.soft.day1103.dao.ICustomerService;
import com.soft.day1103.model.Customer;
import java.lang.reflect.Method;
public class ICustomerServiceProxy implements ICustomerService {
private InvocationHandler handler;
public ICustomerServiceProxy(InvocationHandler handler) {
this.handler = handler;
}
@Override
public int add(com.soft.day1103.model.Customer param0) {
Object[] objects = {param0};
return (int) excute("add", objects, Customer.class);
}
@Override
public java.util.List findAllCustomer() {
Object[] objects = new Object[0];
return (java.util.List) excute("findAllCustomer", objects, null);
}
@Override
public com.soft.day1103.model.Customer findUserById(java.lang.String param0) {
Object[] objects = {param0};
return (com.soft.day1103.model.Customer) excute("findUserById", objects, String.class);
}
private Object excute(String methodName, Object[] param, Class... params) {
Object obj = null;
try {
Method method = ICustomerService.class.getMethod(methodName, params);
obj = handler.invoke(method, param);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return obj;
}
};