代理模式:
代理是基本的设计模式之一,它为你提供额外的或者不同的操作,而插入的用来代理"实际"对象的对象.这些操作通常涉及与"实际"对象的通信,因此代理通常充当着中间人的角色.
动态代理
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象.
动态代理的优点:
- 职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。 - 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。也就是可以在不修改目标对象的基础上增强功能.
- 高扩展性,可以给你需要的方法添加你需要的功能.
基于jdk实现动态代理:
在jdk的api中提供了java.lang.reflect.Proxy它可以帮助我们完成动态代理创建
注意:在java中使用Proxy来完成动态代理对象的创建,只能为目标实现了接口的类创建代理对象。
动态代理是在内存中直接生成代理对象。
继续看api:
一般我们使用这个方法来构造proxy对象,其中需要三个参数:
- ClassLoader loader:
翻译过来就是: 每个Class对象都包含对定义它的ClassLoader的引用。 数组类的类对象不是由类加载器创建的,而是根据Java运行时的需要自动创建的。 Class.getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同; 如果元素类型是基本类型,则数组类没有类加载器。 所以只要我们需要代理的对象不是数组,就可以用Class对象的getClassLoader()获取ClassLoader 对象;
- Class<?>[] interfaces:
在class类里面有这个方法,可以直接获取,这个类代表的是目标对象实现的接口
- InvocationHandler h:
点进去发现这是一个接口,有一个未实现的invoke(…)方法
也就是说,这里需要传入的对象是需要实现这个接口的,并且需要完成这个invoke()方法,下面解释下这个方法里面的三个参数:
-
参数一:Object proxy, ,这个就是我们要代理的对象
-
参数二:Method method:我们需要访问的目标行为,也就是需要调用的方法
-
参数三:Object[] args:调用行为的时候需要的参数
这个方法的主要作用是,当我们通过代理对象调用行为时,来控制目标行为是否可以被调用。
下面用代码进行实现:
//接口
public interface IUserService {
public String addUser(String username,String password);
}
//实现类
public class UserServiceImpl implements IUserService {
@Override
public String addUser(String username, String password) {
System.out.println("添加用户:" + username + " " + password);
return "hello " + username;
}
}
现在我们要写一个代理类进行UserServiceImpl 的代理了:
public class UserServiceProxy implements InvocationHandler {
// 目标对象
private Object target;
public UserServiceProxy(Object target) {
this.target = target;
}
// 作用:创建代理对象
public Object createProxy() {
ClassLoader loader = target.getClass().getClassLoader();
Class[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
System.out.println(Arrays.asList (args));
System.out.println("方法调用之前");
Object result = method.invoke (target,args);
System.out.println("方法调用之后");
return result;
}
}
接下来写一个测试类:
public class Test {
public static void main(String[] args) {
//需要代理的对象
IUserService userservice = new UserServiceImpl ();
//创建代理类
UserServiceProxy up = new UserServiceProxy(userservice);
IUserService proxy = (IUserService) up.createProxy ();
//使用代理类进行方法增强
String s = proxy.addUser ("tom", "123");
System.out.println("s: " + s);
}
}
输出结果:
下面分析下输出:
第一个输出的是method对象,它指向的是IUserService.addUser,第二个是invoke()里面的第三个参数,其实就是我们使用代理对象时候用到的参数(第一个参数一般不使用),然后在addUser ()方法前后我们也将我们自定义的内容加进去了,而且还获得了方法调用的返回值.
总结:
- 动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象的一种代理方法。
- jdk的动态代理方式是使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法产生代理对象,第三个参数需要代理类实现InvocationHandler 接口,并实现invoke()方法,最后使用代理类进行方法的增强.
- 在开发中,我们使用动态代理可以完成性能监控,权限控制,日志记录等操作,spring的AOP有一部分是使用jdk的动态代理实现的.