一、静态代理与动态代理。
1、代理的优点:
① 可实现日志或事物控制等与业务的解耦。
② 在不修改源码的前提下对方法进行增强,即在方法执行前后添加一些操作。
2、代理的原理和特征:
① 原理:用一个代理将原始对象包装起来,然后使用该代理对象代替原始对象。此时任何对原始对象的调用都要经过代理对象,由代理对象决定是否且何时转到原始对象上。
② 特征:代理的委托类和代理类具有相同的接口,代理类主要负责为委托类进行消息预处理、消息转发和消息传递后处理等操作,且代理类对象本身不实现服务,而是调用委托类的对象方法来实现。
③ 分类:根据代理类的创建时期,可将其分为静态代理和动态代理两种模式。
3、静态代理。
① 代理类在程序运行前就已存在。
② 静态代理事先知道要代理的是什么;通常只代理一个类。
4、动态代理。
① 在程序运行时, 由反射机制动态创建代理类。
② 事先不知道要代理的是什么,只有在运行时才知道;通常代理一个接口下面的多个类。
③ 实现动态代理的方法:
| 实现JDK中InvocationHandler接口的invoke方法,由于代理的是接口,所以业务类需要实现接口,通过proxy的newProxyInstance方法来获取代理对象。
| 通过CGLIB实现动态代理,CGLIB代理的是类,不需业务类实现接口,通过派生子类实现代理,且在运行时动态修改字节码来修改类。
二、静态代理和动态代理的实现。
① 静态代理的实现:
定义接口:
package DynamicProxy;
public interface Subject {
public void helloWorld();
}
接口实现类:
package DynamicProxy;
public class RealSubject implements Subject {
@Override
public void helloWorld() {
System.out.println("Hello My World!");
}
}
静态代理实现方法增强:
package DynamicProxy;
public class StaticProxy implements Subject{
// 代理的目标对象
private Subject subject;
public StaticProxy(Subject subject) {
this.subject = subject;
}
@Override
public void helloWorld() {
System.out.println("---Before---");
subject.helloWorld();
System.out.println("---After---");
}
}
测试类:
package DynamicProxy;
public class StaticClient {
public static void main(String[] args) {
// 创建需要被代理的目标对象
Subject realSubject = new RealSubject();
StaticProxy staticProxy = new StaticProxy(realSubject);
staticProxy.helloWorld();
// 查看staticProxy对象的类型
System.out.println(staticProxy.getClass().getName());
}
}
控制台输出:
---Before---
Hello My World!
---After---
DynamicProxy.StaticProxy
若此接口有多个方法,且每个方法前后都需要进行相同内容增强,则使用静态代理需要每个都写一遍,此时使用动态代理更为方便简洁。
② JDK实现动态代理:
接口和接口实现类同静态代理。
动态代理类【实现InvocationHandler接口】:
package DynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler{
// 代理的目标对象
private Object subject;
// 构造方法,为目标对象赋值
public DynamicProxy(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理目标对象前,可以加一些其它操作,即在不改变原方法的基础上增强原方法
System.out.println("Hello DynamicProxy!");
// 打印method方法
System.out.println("Method:"+method);
// 代理对象通过自动跳转到其关联的Handler对象的invoke方法来调用目标对象方法
Object object = method.invoke(subject, args);
// 代理目标对象后,也可以加一些其它操作,即在不改变原方法的基础上增强原方法
System.out.println("Bye DynamicProxy!");
return object;
}
}
测试类Client:
package DynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// 创建需要被代理的目标对象
Subject realSubject = new RealSubject();
// 将其传到动态代理实现类中
InvocationHandler handler = new DynamicProxy(realSubject);
// 调用DynamicProxy中的invoke方法
Subject subject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(),handler);
subject.helloWorld();
// 查看subject对象的类型
System.out.println(subject.getClass().getName());
}
}
控制台输出:
Hello DynamicProxy!
Method:public abstract void DynamicProxy.Subject.helloWorld()
Hello My World!
Bye DynamicProxy!
com.sun.proxy.$Proxy0
由控制台输出结果可知,当代理对象调用目标对象的方法时,会自动跳转到代理对象关联的handler对象的invoke方法来调用。
其中,newProxyInstance方法参数及返回值如下:
Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
类加载器loader为目标对象的类加载器,且代理类和目标对象都继承同一接口,最后需传递InvocationHandler使代理类能对目标对象进行调用。
AOP面向切面编程使用切面对关注点进行模块化,通过AOP配置XML,使com.aop.HelloWorld类下面的所有方法运行前都执行showTime()函数,运行后执行showLog()函数【都在切面timeHandler类中定义】,即对多个方法做预处理和后续的处理。
<aop:config>
<aop:aspect id="time" ref="timeHandler">
<aop:pointcut id="addAllMethod" expression="execution(* com.aop.HelloWorld.*(..))" />
<aop:before method="showTime" pointcut-ref="addAllMethod" />
<aop:after method="showLog" pointcut-ref="addAllMethod" />
</aop:aspect>
</aop:config>
③ CGLIB动态代理【转】:
由于JDK动态代理只能代理实现了接口的目标对象,且基于反射效率相对较低,于是出现了基于字节码实现的CGLIB(Code Generation Library)动态代理技术,大大扩展了试用范围而且效率相对JDK更高,但由于CGLIB动态代理需要生成目标对象的子类,所以CGLIB不能代理final方法。
具体参考https://blog.csdn.net/danchu/article/details/70238002。