- 静态代理
- 动态代理
- cglib代理
代理模式的用途主要是对目标对象的方法功能进行扩展,不是用来new对象的设计模式
比如你希望该类所有方法都新增一个功能,但是不需要对该类修改代码,只需要写一个代理类
可以用于输出日志信息,统一进行事务管理
Spring 框架的AOP就用到了代理模式中的动态代理
静态代理
对目标对象的方法进行增强
使用租房收房举例
定义一个出租的接口,指定出租和收回两个方法
public interface Rent {/*** 出租*/void rent();/*** 回收房*/void recycle();}
定义房东类,实现出租接口
public class Host implements Rent {@Overridepublic void rent() {System.out.println("房东要出租房子!!!");}@Overridepublic void recycle() {System.out.println("房东回收房子");}}
然后写一个代理类,用于增强目标对象的方法
将房东类的所有方法进行增强,类似于运行日志
public class HostProxy implements Rent {private Host target;public HostProxy(Host target) {this.target = target;}@Overridepublic void rent() {System.out.println("开始代理。。。。。。。。。");Long start = System.currentTimeMillis();target.rent();Long end = System.currentTimeMillis();System.out.println("提交结束。。。。。。。。。共耗时:" + (end - start) + " ms");}@Overridepublic 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接口的实现类:由于本类实现了该接口所以就传个thisreturn Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}//处理代理实例,并返回结果 该方法由InvocationHandler提供@Overridepublic 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*/@Overridepublic 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
