1. 静态代理
- 名词术语:
目标对象(目标类)-Target、
代理对象(代理类)-Proxy
目标方法(本质工作)
额外操作(增强方法)
- 静态代理的缺点:
额外操作的调用和目标方法的调用混合在一起 如果业务发生变化,一定需要去对方法细节做调整
实现: 在原来的卖烧饼的方法增强一个送大麦茶的方法
1.创建ISale接口
java
public interface ISale {
void saleShaoBing();
void saleJianBing();
void saleYueBing();
void saleManTou();
}
2.创建WuDaLang实现类:目标类
java
public class WuDaLang implements ISale{
@Override
public void saleShaoBing() {
System.out.println("卖烧饼...");
}
@Override
public void saleJianBing() {
System.out.println("卖煎饼...");
}
@Override
public void saleYueBing() {
System.out.println("卖月饼...");
}
@Override
public void saleManTou() {
System.out.println("卖馒头...");
}
}
3.创建WuDaLangProxy实现类:代理类
java
public class WuDaLangProxy implements ISale{
private WuDaLang target ;
public WuDaLangProxy(WuDaLang target){
this.target = target;
}
@Override
public void saleShaoBing() {
target.saleShaoBing();
song();
}
@Override
public void saleJianBing() {
target.saleJianBing();
song();
}
@Override
public void saleYueBing() {
target.saleYueBing();
song();
}
@Override
public void saleManTou() {
target.saleManTou();
}
private void song(){
System.out.println("送大麦茶...");
}
}
4.建立测试类测试
java
public class ProxyTest {
@Test
public void test01(){
WuDaLang target = new WuDaLang();
ISale wuDaLang = new WuDaLangProxy(target); //new WuDaLang();
wuDaLang.saleShaoBing();
}
}
目标类即:武大郎类具有的方法,卖烧饼
代理类即:武大郎代理类具有的方法,买烧饼+送大麦茶
2. 动态代理(JDK)
-
我们发现一个规律:如果你的方法是以"Bing"结尾,我就增强(送大麦茶)
-
JDK动态代理的特点:产生的代理对象和目标对象是兄弟关系
-
JDK动态代理的缺点:要求目标对象必须有父接口
1.创建 ISale 接口
java
public interface ISale {
void saleShaoBing();
void saleJianBing();
void saleYueBing();
void saleManTou();
}
2.创建WuDaLang类:目标类
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WuDaLang {//implements ISale{
public void saleShaoBing() {
System.out.println("卖烧饼...");
}
public void saleJianBing() {
System.out.println("卖煎饼...");
}
public void saleYueBing() {
System.out.println("卖月饼...");
}
public void saleManTou() {
System.out.println("卖馒头...");
}
}
3.创建WuDaLangInvocationHandler类:代理类 实现以bing结束的方法大麦茶就送
java
public class WuDaLangInvocationHandler implements InvocationHandler {
private Object target ;
public WuDaLangInvocationHandler(Object target){
this.target = target ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy:代理对象 method:目标方法 args:目标方法的实参
//获取目标方法名
String methodName = method.getName() ;
if(methodName.endsWith("Bing")){
song();
}
return method.invoke(target,args);
}
private void song(){
System.out.println("送大麦茶...");
}
}
4.建立测试类
java
@Test
public void test01()throws Exception{
WuDaLang target = new WuDaLang();
WuDaLangInvocationHandler wuDaLangInvocationHandler = new WuDaLangInvocationHandler(target);
//通过Proxy的静态方法newProxyInstance创建代理对象
//这个方法有三个参数:① 类加载器。因为目标类和代理类使用的类加载器是同一级别,都是应用程序类加载器,因此我们使用Target的类加载器
//② 目标类的父接口数组。 因为我们在使用JDK动态代理产生代理对象时,需要使用目标类的父接口作为原料,因此需要传入接口数组
//③ 产生的代理对象的逻辑是什么,我们需要在handler中进行说明,因此需要传入InvocationHandler实例
Object proxyObj = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),wuDaLangInvocationHandler);
//产生的代理对象是接口的实现类,因此proxyObj可以强转为ISale类型
//WuDaLang iSale = (WuDaLang)proxyObj;
//iSale.saleManTou();//saleShaoBing();
Method method = proxyObj.getClass().getDeclaredMethod("saleShaoBing");
method.invoke(proxyObj);
}
动态代理与静态代理的区别:
-
静态代理增强的方法耦合在目标类中,动态代理增强的方法则另外处理
-
静态代理适合态代理适合目标对象固定且数量较少的情况,动态代理则更适合目标对象数量多或者目标对象在编译期不确定的情况,它能提供更加高效和灵活的代理服务
3. 动态代理(CGLIB)
-
目标类不需要有接口
-
目标类不能被final修饰
-
产生的代理对象是目标类的子类
-
早期(JDK1.4),JDK动态代理创建代理对象的性能较高,调用代理方法的性能较差;CGLIB恰好相反。 JDK8之后,JDK的动态代理性能越来越高,CGLIB的优势已经不明显。因此在2019年8月CGLIB停止更新。 Spring团队依然在默默维护CGLIB。因此在spring环境下,仍然可以使用CGLIB。