什么是代理?
为某一个对象创建一个代理对象,程序不直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间
什么是静态代理?
由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在
通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的。
优点
代理使客户端不需要知道实现类是什么,怎么做,而客户端只需知道代理即可
方便增加功能,扩展业务逻辑
缺点
代理类中常出现大量冗余的代码,非常不利于扩展和维护
如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度
案例演示
PayService.java(接口)
public interface PayService {
/**
* 支付回调
* @param outTradeNo
* @return
*/
String callback(String outTradeNo);
/**
* 下单
* @param userId
* @param productId
* @return
*/
int save(int userId, int productId);
}
PayServiceImpl.java(接口实现类)
public class PayServiceImpl implements PayService {
public String callback(String outTradeNo) {
System.out.println("目标类 PayServiceImpl 回调 方法 callback");
return outTradeNo;
}
public int save(int userId, int productId) {
System.out.println("目标类 PayServiceImpl 回调 方法 save");
return productId;
}
}
StaticProxyPayServiceImpl.java(接口实现类,静态代理) 复杂新增方法要重新也方法增强
private PayService payService;
public StaticProxyServiceImpl(PayService payService) {
this.payService = payService;
}
@Override
public String callback(String outTradeNo) {
System.out.println("静态代理 --- start");
String result = payService.callback(outTradeNo);
System.out.println("静态代理 --- end");
return result;
}
@Override
public int save(int userId, int productId) {
System.out.println("静态代理 --- start");
int result = payService.save(userId, productId);
System.out.println("静态代理 --- end");
return result;
}
演示
什么是动态代理?https://blog.csdn.net/yaomingyang/article/details/80981004
在程序运行时,运用反射机制动态创建而成,无需手动编写代码
- JDK动态代理CGLIB动态代理(原理:是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理)
jdk动态代理演示
-
定义一个类,去实现InvocationHandler这个接口,并车从写invoke方法
-
//Object proxy:被代理的对象
-
//Method method:要调用的方法
-
//Object[] args:方法调用时所需要参数
-
public Object invoke(Object proxy, Method method, Object[] args){}
JdkProxy.java(jdk动态代理类)
public class JdkProxy implements InvocationHandler {
// 目标类
private Object targetObject;
// 获取代理对象
public Object newProxyInstance(Object targetObject) {
this.targetObject = targetObject;
// 绑定关系,就是和具体的哪个实现类关联
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try{
System.out.println("JDK动态代理调用"+method.getName() +", 打印日志 begin");
result = method.invoke(targetObject,args);
System.out.println("JDK动态代理调用"+method.getName() +", 打印日志 end");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
演示
CGLIB动态代理演示
CglibProxy.java(CGLIB动态代理类)
public class CglibProxy implements MethodInterceptor {
// 目标类
private Object targetObject;
// 绑定关系
public Object newProxyInstance(Object targetObject) {
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
// 设置代理类的父类(目标类)
enhancer.setSuperclass(this.targetObject.getClass());
// 设置回调函数
enhancer.setCallback(this);
// 创建子类(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
try{
System.out.println("通过CGLIB动态代理调用 "+method.getName() +", 打印日志 begin");
result = methodProxy.invokeSuper(o,args);
System.out.println("通过CGLIB动态代理调用 "+method.getName() +", 打印日志 begin");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
演示
总结
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理,解耦和易维护。
两种动态代理的区别
- JDK动态代理:要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以用CGLIB动态代理
- JDK动态代理是自带的,CGLIB需要引入第三方包
- CGLIB动态代理,它是内存中构建一个子类对象从而实现对目标对象功能的扩展
- CGLIB动态代理基于继承来实现代理,所以无法对final类,private方法和static方法实现代理
Spring AOP中的代理使用的默认策略
- 如果目标对象实现类接口,则默认采用JDK动态代理
- 如果目标对象没有实现接口,则采用CGLIB进行动态代理