- 静态代理
- 动态代理
- cglib代理
代理模式的用途主要是对目标对象的方法功能进行扩展,不是用来new对象的设计模式
比如你希望该类所有方法都新增一个功能,但是不需要对该类修改代码,只需要写一个代理类
可以用于输出日志信息,统一进行事务管理
Spring 框架的AOP就用到了代理模式中的动态代理
静态代理
对目标对象的方法进行增强
使用租房收房举例
定义一个出租的接口,指定出租和收回两个方法
public interface Rent {
/**
* 出租
*/
void rent();
/**
* 回收房
*/
void recycle();
}
定义房东类,实现出租接口
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子!!!");
}
@Override
public void recycle() {
System.out.println("房东回收房子");
}
}
然后写一个代理类,用于增强目标对象的方法
将房东类的所有方法进行增强,类似于运行日志
public class HostProxy implements Rent {
private Host target;
public HostProxy(Host target) {
this.target = target;
}
@Override
public void rent() {
System.out.println("开始代理。。。。。。。。。");
Long start = System.currentTimeMillis();
target.rent();
Long end = System.currentTimeMillis();
System.out.println("提交结束。。。。。。。。。共耗时:" + (end - start) + " ms");
}
@Override
public void recycle() {
System.out.println("开始代理。。。。。。。。。");
Long start = System.currentTimeMillis();
target.recycle();
Long end = System.currentTimeMillis();
System.out.println("提交结束。。。。。。。。。共耗时:" + (end - start) + " ms");
}
}
使用代理对象
public static void main(String[] args) {
//创建目标对象(被代理对象)
Host host = new Host();
//创建代理对象
HostProxy hostProxy = new HostProxy(host);
//通过代理对象调用被代理的方法
hostProxy.rent();
hostProxy.recycle();
}
输出结果为,这样就在原有方法的基础上进行了增强
开始代理。。。。。。。。。
房东要出租房子!!!
提交结束。。。。。。。。。共耗时:0 ms
开始代理。。。。。。。。。
房东回收房子
提交结束。。。。。。。。。共耗时:0 ms
但是这样做扩展性很差,被代理对象每增加一个方法就需要在代理类中修改代码,麻烦!更推荐使用动态代理
动态代理(JDK代理)
动态代理在实现静态代理的基础上,被代理对象即使新增方法也不需要修改代理类的代码
需要用到jdk的包java.lang.reflect.Proxy
提供的一个方法来获取代理对象,所以又叫jdk代理
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
接收的三个参数:
- 目标对象使用的类加载器
- 目标对象实现的接口类型,使用泛型方法确认类型
- InvocationHandler接口的实现类:事情处理,执行目标对象方法时,会触发处理器方法,会把当前执行的目标对象方法作为参数传入
InvocationHandler接口提供了一个方法
使用反射的机制来实现被代理的方法调用
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
代理类的代码
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口,使用Object类就可以百搭了
private Object target;
//设置被代理的接口
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
//Proxy提供创建动态代理类和实例的静态方法
public Object getProxy() {
//返回被代理的对象
//参数:
//1、目标对象使用的类加载器
//2、目标对象实现的接口类型,使用泛型方法确认类型
//3、InvocationHandler接口的实现类:由于本类实现了该接口所以就传个this
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果 该方法由InvocationHandler提供
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//查看调用的是哪个方法
log(method.getName());
Long start = System.currentTimeMillis();
//使用反射机制实现动态代理
Object result = method.invoke(target, args);
Long end = System.currentTimeMillis();
System.out.println("方法执行完成!共耗时:" + (end - start) + " ms");
return result;
}
//日志方法
private void log(String name) {
System.out.println("----------------------");
System.out.println("代理开始");
System.out.println("使用了" + name + "方法");
}
}
然后调用
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色(现在没有)
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//通过调用程序来处理我们要调用的对象
handler.setTarget(host);
//得到被代理的对象
Rent proxy = (Rent) handler.getProxy();
proxy.rent();
proxy.recycle();
}
运行结果
----------------------
代理开始
使用了rent方法
房东要出租房子!!!
方法执行完成!共耗时:1 ms
----------------------
代理开始
使用了recycle方法
房东回收房子
方法执行完成!共耗时:0 ms
这样即使被代理的类新增了方法也不需要改动代理类的方法
但是这样的动态代理有一个缺点,就是被代理的类必须实现接口
有时候想要代理不实现任何接口的类就需要用到Cglib代理了
Cglib代理
Cglib代理也算是动态代理的一种,本质是代理被代理对象的子类,所以使用final定义的类就不能被代理
想要使用cglib代理需要导入一个依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
创建一个不实现任何接口的类
用老师类来举例
public class TeacherService {
public void teach() {
System.out.println("老师授课中。。。。。。");
}
}
然后写代理类,需要实现cglib提供的接口MethodInterceptor
MethodInterceptor提供了一个intercept方法和上面动态代理的InvocationHandler接口提供的invoke方法类似
代理类
public class ProxyFactory implements MethodInterceptor {
/**
* 维护一个目标对象(被代理的对象)
*/
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 返回代理对象
*
* @return target的代理对象
*/
public Object getProxyInstance() {
//创建一个工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
//调自己
enhancer.setCallback(this);
//创建子类对象(代理对象)
return enhancer.create();
}
/**
* 实现目标对象的调用
* 调用目标对象的方法
*
* @param o
* @param method
* @param args
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib代理模式开启。。。。。");
Long start = System.currentTimeMillis();
//调用代理对象的方法
Object result = method.invoke(target, args);
Long end = System.currentTimeMillis();
System.out.println("Cglib代理模式提交。。。。。共耗时:" + (end - start) + " ms");
return result;
}
}
然后运行
public static void main(String[] args) {
//创建目标对象
TeacherService target = new TeacherService();
ProxyFactory proxyFactory = new ProxyFactory(target);
//返回被代理的对象
TeacherService proxy = (TeacherService) proxyFactory.getProxyInstance();
//执行代理对象的方法
proxy.teach();
}
输出运行结果,同样实现了效果
Cglib代理模式开启。。。。。
老师授课中。。。。。。
Cglib代理模式提交。。。。。共耗时:0 ms
cglib代理的范围比jdk代理更广
Spring AOP中的应用
由AopProxyFactory根据AdvisedSupport对象的配置来决定
默认策略如果目标类是接口,则使用JDK代理,否则使用cglib