1.概念
为其他对象提供一种代理以控制对该对象的访问,代理就相当于一个中介。比如你去找房子,可以通过中介,你只要告诉中介需要什么样的房子,中介就会将对应的房子介绍给你。
2.常见的代理模式
远程代理:为不同地理位置的对象,提供一个局域网代表对象。
虚拟代理:根据需要将资源消耗量很大的对象进行延迟,真正需要时候才创建。
保护代理:主要用来对不同权限进行控制。
智能引用代理:提供对目标对象额外的服务。
这里主要介绍智能引用代理,在开发过程中,该代理使用到的最多的。它有两种实现方式,静态代理和动态代理,看一下关系图:
通过关系图我们可以发现,在不改变原有类的基础上,我们可以通过代理来增加一些功能,符合开闭原则,这是代理模式的优点。在我们实际开发中,由于时间紧迫我们一开始没有加如权限、日志等这些公共的功能,后期版本迭代时候想加入,这是我们就可以使用代理来完成,而没有必要修改原来的代码逻辑。
3.静态代理
代理对象是由我们手动创建的,代理对象和被代理对象在代理前关系已经确定,它们都实现相同的接口或继承相同的抽象类。静态代理实现很简单,首先需要定义一个接口,然后定义接口实现类和一个代理类实现接口。
第一步:定义一个接口
//操作用户信息接口
public interface UserService {
public void add();
public void delete();
}
第二步:定义接口实现类
//操作用户信息实现类
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("添加一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
}
第三步:定义代理类
//代理类
public class MyProxy implements UserService {
private UserService us;
public MyProxy(UserService us) {
this.us = us;
}
@Override
public void add() {
System.out.println("开启事务");
us.add();
System.out.println("开启关闭事务");
}
@Override
public void delete() {
System.out.println("开启事务");
us.delete();
System.out.println("开启关闭事务");
}
}
测试类
//测试类
public class Test {
public static void main(String[] args) {
UserService us = new UserServiceImpl();
MyProxy proxy = new MyProxy(us);
proxy.add();
proxy.delete();
}
}
静态代理就实现了,通过以上简单代码我们发现一个代理类只可以服务一个接口,一个Proxy类实现了一个UserService接口,在实际开发中会有很多接口,如果使用静态代理就需要为这些接口一一创建代理类,而这些代理类除了调用方法不同外完成的功能是相同的,这就造成了代码的严重冗余,我们可不可以通过一个代理类来完成代理所有代理功能呢,动态代理就可以完成。
4.动态代理
动态的产生代理类,实现对不同类不同方法的代理,jdk动态代理的实现使用到了反射机制,有以下几个步骤:
1. 创建一个类实现java.lang.reflect.InvocationHandler接口,实现invoke()方法
2. 创建被代理类以及接口
3. 调用java.lang.reflect.Proxy类的静态方法,创建一个代理类
Proxy.newProxyInstance(ClassLoader l,Class[] interfaces,InvocationHandler h);
参数:
ClassLoader l:被代理类的类加载器
Class[] interfaces:被代理类实现的接口
InvocationHandler h:InvocationHandler接口的子类实例
4.通过以上创建的代理类调用对应方法
下面我们简单实现动态代理,使用以上静态代理例子中的UserService和UserServiceImpl,只修改代理类和测试类。
代理类(JDK动态代理的代理类):
public class JDKProxy implements InvocationHandler {
public Object object;
public JDKProxy(Object object){
this.object = object;
}
/**
* 参数说明:
* proxy:被代理的对象
* method:被代理对象的方法Method实例
* args:方法的参数
*
* 返回值:
* Object:调用被代理类的方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
//反射
Object obj = method.invoke(us, args);
System.out.println("关闭事务");
return obj;
}
}
测试类:
public class Test {
public static void main(String[] args) {
UserService us = new UserServiceImpl();
//动态代理的入口
UserService obj = (UserService)Proxy.newProxyInstance(
UserService.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
new JDKProxy(us));
obj.add();
obj.delete();
}
}
这是JDK为我们提供的动态代理实现方式,在程序运行时动态的为我们创建代理类,需要被代理类实现接口,JDK动态代理是针对实现接口的类来生成代理的。假如类没有实现接口,就不能使用JDK了,可以使用CGLIB。
CGLIB动态代理:针对类实现代理的,对指定的目标类产生一个子类(使用继承),然后覆盖父类方法,因为使用继承,所以被final修饰的类是不能使用CGLIB动态代理的。使用CGLIB需要导入Jar包cglib-nodep-2.2.jar如果是Maven项目在pom文件添加以下依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
使用CGLIB例子
public class Car {
public void addCar(){
System.out.println("添加一辆车");
}
}
实现CGLIB动态代理需要定义一个类实现MethodInterceptor
package proxy.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CGLIBProxy implements MethodInterceptor {
private Enhancer e = new Enhancer();
public Object getCGLIBProxy(Class<?> clazz){
//设置为clazz类生成代理
e.setSuperclass(clazz);
e.setCallback(this);
//生成代理类实例
return e.create();
}
/**
* 拦截所有目标类的方法
* 参数:
* obj 被代理类实例
* m 被代理类方法
* args 方法的参数
* proxy 代理类对象
*/
@Override
public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事务");
Object result = null;
//调用父类方法
result = proxy.invokeSuper(obj, args);
System.out.println("关闭事务");
return result;
}
}
测试类
public class Test {
public static void main(String[] args) {
CGLIBProxy proxy = new CGLIBProxy();
CarService cs = (CarService)proxy.getCGLIBProxy(CarService.class);
cs.addCar();
}
}
动态代理的作用可以理解为”横向抽取”,之所以称为”横向抽取”,是相对于继承而言的,在继承中可以把相同的逻辑抽取到父类中,继承父类即可,继承也称为”纵向抽取”。在程序中每业务逻辑都需要记录日志、添加权限校验以及开启关闭事务等,而这些操作都是相同的,却不能通过继承抽取。”横向抽取”就是将这些相同的操作抽取出来统一管理,这也是AOP的原理。
5.模拟JDK动态代理原理
JDK动态代理是通过Proxy的newProxyInstance方法生成代理对象的,下面自定义一个MyProxy类模拟Proxy实现动态创建代理对象,该类有一静态方法可以动态的为实现了某一/些接口(或本身就是接口)任意类的任一方法产生任意代理对象。
第一步:模拟JDK中Proxy.newProxyInstance方法
package proxy.customjdk;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import org.apache.commons.io.FileUtils;
/**
* JDK动态代理是通过Proxy的newProxyInstance方法生成代理对象的,下面将自定义一个MyProxy类模拟Proxy实现动态创建代理对象
* 该类有一静态方法可以动态的为实现了某一/些接口(或本身就是接口)任意类的任一方法产生任意代理对象
* 实现思路:首先需要声明一段源码,可以动态的生成代理类,然后使用JDK Compiler API编译该源码,生成一个新的类,
* 然后将该类加载的内存中,产生一个新的对象,该对象既是我们需要的代理对象,最后返回改对象即可。
*/
public class MyProxy {
public static Object newProxyInstance(Class<?> interfaces,MyInvocationHandler h) throws Exception{
//Windows下的换行符号
String wrap = "\r\n";
String method = "";
for(Method m:interfaces.getMethods()){
method +=" @Override"+ wrap +
" public void "+m.getName()+"() {"+ wrap +
" try{" + wrap +
//获取接口方法的反射对象
" Method md = " +interfaces.getName() +".class.getMethod(\""+m.getName()+"\");" +wrap +
//调用自定义InvocationHandler的invoke()方法
" h.invoke(this,md);"+ wrap +
" }catch(Exception e){e.printStackTrace();}" + wrap +
" }"+ wrap ;
}
//声明一段源码
String source =
"package proxy.customjdk;"+ wrap +
"import java.lang.reflect.Method;" +wrap +
"import proxy.customjdk.MyInvocationHandler;"+ wrap +
//模拟JDK生成的代理类名称$Proxy0
"public class $Proxy0 implements " + interfaces.getName() + "{"+ wrap +
" private MyInvocationHandler h;"+ wrap +
" public $Proxy0(MyInvocationHandler h) {"+ wrap +
" this.h = h;"+ wrap +
" }"+ wrap +
method+
"}";
//编译以上源码,需要生成一个Java文件,对该文件进行编译
//定义文件路径,当前包下
String fileName = System.getProperty("user.dir") +"/src/main/java/proxy/customjdk/$Proxy0.java";
File file = new File(fileName);
//写文件,在类目录下生成一个$Proxy0.java文件,需要导如commons-io-2.4.jar包
FileUtils.writeStringToFile(file, source);
//编译,以下编译方法可查询JavaAPI,在这里不详细说明
//获取当前使用的编译器实例
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//获取一个标准文件管理器实例
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
//获取文件
Iterable jfile = fileManager.getJavaFileObjects(fileName);
//编译任务
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, jfile);
//编译,生成$Proxy0.class文件
task.call();
fileManager.close();
//加载到内存
ClassLoader loader = ClassLoader.getSystemClassLoader();
//使用指定的二进制名称加载类
Class c = loader.loadClass("proxy.customjdk.$Proxy0");
//获取构造方法反射对象
Constructor constructor = c.getConstructor(MyInvocationHandler.class);
//创建类的实例并返回
return constructor.newInstance(h);
}
}
第二步:模拟InvocationHandler
package proxy.customjdk;
import java.lang.reflect.Method;
//定义自己的InvocationHandler
public interface MyInvocationHandler {
/**
* 模拟调用无参方法,没有传递参数
* @param o 代理对象
* @param m 被代理方法
*/
public void invoke(Object o,Method m);
}
第三步:实现InvocationHandler
package proxy.customjdk;
import java.lang.reflect.Method;
public class UserHandler implements MyInvocationHandler {
//被代理对象
private Object obj;
public UserHandler(Object obj) {
this.obj = obj;
}
@Override
public void invoke(Object o, Method m) {
try {
System.out.println("开启事务");
m.invoke(obj);
System.out.println("关闭事务");
} catch (Exception e) {
e.printStackTrace();
}
}
}
第四步:测试
package proxy.customjdk;
//这里使用的UserService和UserServiceImpl是以上静态代理的例子
public class Test {
public static void main(String[] args) throws Exception {
UserHandler handler = new UserHandler(new UserServiceImpl());
UserService u = (UserService)MyProxy.newProxyInstance(UserService.class,handler);
u.add();
u.delete();
}
}
执行之后类目录结构,$Proxy0.java和$Proxy0.class
既是生成的的代理文件
打印结果
开启事务
添加一个用户
关闭事务
开启事务
删除一个用户
关闭事务