简单介绍
代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,我们可以对实际对象中的方法进行一定程度的增强。
静态代理
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
根据上面代理模式的类图,来写一个简单的静态代理的例子:
有一个User的实体类,我们有一个业务层,业务层干的事情就是保存一个实体对象,业务层调用持久层dao,进行数据库存储。我们通过一个代理对象来代理业务层的操作。
- 实体类
package com.company.proxy;
public class User {
private Integer userId;
private String userName;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
- 业务层接口
package com.company.proxy;
public interface UserService {
int save(User user);
}
- 业务层实现类
package com.company.proxy;
public class UserServiceImpl implements UserService{
private UserDao userDao;
@Override
public int save(User user) {
userDao = new UserDaoImpl();
userDao.insert(user);
System.out.println("Service 调用dao层添加user");
return 1;
}
}
- 持久层接口
package com.company.proxy;
public interface UserDao {
int insert(User user);
}
- 持久层实现类
package com.company.proxy;
public class UserDaoImpl implements UserDao{
@Override
public int insert(User user) {
System.out.println("插入一条数据成功!!!");
return 1;
}
}
- 静态代理类
package com.company.proxy.staticproxy;
import com.company.proxy.User;
import com.company.proxy.UserService;
import com.company.proxy.UserServiceImpl;
public class UserServiceStaticProxy2 implements UserService {
// 被代理的对象
private UserService userService;
public UserServiceStaticProxy2(UserService userService1){
if (userService1.getClass() == UserServiceImpl.class) {
this.userService = (UserService)userService1;
}
}
@Override
public int save(User user) {
beforeMethod();
this.userService.save(user);
afterMethod();
return 0;
}
public void beforeMethod(){
System.out.println("before mothod ===========");
}
public void afterMethod(){
System.out.println("after mothod ===========");
}
}
该静态代理类主要的作用就是对业务层方法进行代理,同时befoe、after方法是可以对业务层方法进行增强。
- 测试类
package com.company.proxy.staticproxy;
import com.company.proxy.User;
import com.company.proxy.UserService;
import com.company.proxy.UserServiceImpl;
public class test2 {
public static void main(String[] args) {
User user = new User();
user.setUserId(2);
user.setUserName("xiaohuang");
// 需要被代理的对象
UserService userService = new UserServiceImpl();
// 生成代理对象
UserServiceStaticProxy2 proxy2 = new UserServiceStaticProxy2(userService);
// 使用代理对象代理执行业务
proxy2.save(user);
}
}
Spring中的AOP面向切面编程,我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作(增强),这个切点就相当于我们这个例子中的save方法。
动态代理
代理类在程序运行时创建的代理方式被成为动态代理。
我们上面静态代理的例子中,代理类是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 比如说,想要在每个代理的方法前都加上一个处理方法:
public void giveMoney() {
//调用被代理方法前加入处理方法
beforeMethod();
stu.giveMoney();
}
这里只有一个giveMoney方法,就写一次beforeMethod方法,但是如果除了giveMonney还有很多其他的方法,那就需要写很多次beforeMethod方法,麻烦。所以建议使用动态代理实现。
动态代理的简单实现
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
这里我们的业务与静态代理完全相同,只需要把静态代理修改为动态代理就行。
- 代理类
package com.company.proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class UserServiceDynamicProxy implements InvocationHandler {
// 需要进行代理的对象(被代理对象)
private Object target;
// 通过构造函数传入被代理对象
public UserServiceDynamicProxy(Object target) {
this.target = target;
}
/**
* 创建被代理对象的代理类的对象
* @return 代理类对象
*/
public Object bind() {
Class cls = target.getClass();
/**
* loader : 确定需要谁的代理类对象(确定被代理类)
* interfaces : 该被代理类实现了哪些接口
* h : InvocationHandler
*/
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), this);
}
/**
* 代理类执行业务方法(当我们使用代理类对象调用被代理类的方法时执行)
* @param proxy
* @param method : 执行的方法
* @param args : 该方法拥有的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object argObject = args[0];
beforeMethod();
// invoke : Method类中提供的invoke方法,作用就是执行被代理对象target的method方法(会将代理类中的参数传递过去执行)
Object object = method.invoke(target, args);
afterMethod();
return object;
}
public void beforeMethod(){
System.out.println("DynamicProxy before mothod ===========");
}
public void afterMethod(){
System.out.println("DynamicProxy after mothod ===========");
}
}
- 测试
public class test {
public static void main(String[] args) {
User user = new User();
user.setUserId(1);
user.setUserName("fulin");
UserService userServiceDynamicProxy = (UserService)new UserServiceDynamicProxy(new UserServiceImpl()).bind();
userServiceDynamicProxy.save(user);
}
}
通过分析这个代理类,大致应该就是如果我们需要对某些类进行代理,且要增强的方法相同,就可以使用相同的代理类,增强方法不同,那么就重新搞个代理类就行了。
动态代理的优势:
在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了。
动态代理类看不见源码具体实现,理解起来非常的抽象,如果仅仅是学会使用,可以直接背下来,以后直接根据模板弄。需要更深入的了解还是需要进行源码级别的解读。
动态代理原理分析
我们利用Proxy类的 newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,发现它只是封装了创建动态代理类的步骤:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException {
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
重点是这四处位置:
final Class<?>[] intfs = interfaces.clone();
// 动态代理的关键:产生代理类(不是代理对象)
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
上面第二句中,我们深入源码(这里不展示),会发现这个类文件时缓存在java虚拟机中的。我们可以通过下面的方法将其打印到文件里面,一睹真容:
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",Student.class.getInterfaces());
String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类class文件写入成功");
} catch (Exception e) {
System.out.println("写文件错误");
}
对这个class文件进行反编译,我们看看jdk为我们生成了什么样的内容:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
*注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
*为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
*被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
*
*super(paramInvocationHandler),是调用父类Proxy的构造方法。
*父类持有:protected InvocationHandler h;
*Proxy构造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//这个静态块本来是在最后的,我把它拿到前面来,方便描述
static
{
try
{
//看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
/**
*
*这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void giveMoney()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
}
算了,第一遍也不打算再深入了,后面复习看这篇博客去跑一边。推荐博客