目录
一.什么是代理模式
代理模式(Proxy Pattern)是指为其他对象提供一种代理,以控制对这个对象的访问。
代理对象在客服端和目标对象之间起到中介作用。
属于结构型设计模式。
二.代理模式的作用
设想这样的一个场景:我们想请杰伦演出,但是杰伦只唱歌别的琐碎的业务不想做,这时候还怎么办?这时候当然是需要找一个代理,来帮杰伦谈薪资,安排行程,买火车票。这就相相当与我们的代理模式。
三.静态代理
直接定义代理类,实现目标对象的接口,实现他的方法并对其增强
这种方式不宜与扩展。
先创建目标对象的接口 与实现类:
ISingServer:
public interface ISingService {
String sing(String str);
}
JaySingServer实现类:
public class JaySingService implements ISingService {
@Override
public String sing(String str) {
if ("花海".equals(str) ) {
System.out.println("不要你离开~距离隔不开~");
return str;
} else if ("明明就".equals(str)) {
System.out.println("明明就他比较温柔~~~");
return str;
}
return "不会唱~~~~";
}
}
VaeSingServer实现类:
public class VaeSingService implements ISingService {
@Override
public String sing(String str) {
if ("有何不可".equals(str) ) {
System.out.println("为你唱这首歌~他没什么风格~仅仅代表着我想给你快乐~");
return str;
}
return "不会唱~~~~";
}
}
静态代理:
public class StaticProxy implements ISingService {
//静态代理需要继承目标对象 然后重写方法并对其增强
//目标对象
private ISingService target;
//创建构造器
public StaticProxy(ISingService target) {
this.target = target;
}
@Override
public String sing(String str) {
//执行目标方法前需要做的事
before();
String result = target.sing(str);
//执行目标方法后需要做的事
after();
return result + "--好听!好听!";
}
public void before() {
System.out.println("演出前需要与经纪人先谈好演出费");
}
public void after() {
System.out.println("唱完了,经纪人需要安排回去的返程");
}
}
test:
public static void main( String[] args )
{
ISingService jaySing = new JaySingService();
//静态代理
StaticProxy proxy = new StaticProxy(jaySing);
System.out.println(proxy.sing("花海"));
}
}
四.Jdk动态代理
静态代理的灵活性不高,所以我们引入了动态代理的方式
如果目标对象实现的有接口,那么我们就可以使用JDK代理模式。JDK动态代理相当与实现目标对象的接口,然后重写他的方法并对其进行增强。
public class JdkProxy implements InvocationHandler {
//要被代理的对象
private ISingService target;
public JdkProxy(ISingService target) {
this.target = target;
}
public ISingService creatTarget() {
/*
*Proxy.newProxyInstance
*第一个参数:目标的对象加载器
* 第二个参数:目标对象的接口
* 第三个参数是InvocationHandler类 这个类的invoke方法是做增强的地方
*/
ISingService proxyTarget = (ISingService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
return proxyTarget;
}
/**
* Jdk动态代理要实现这个方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行目标方法前需要做的事
before();
//执行目标对象的方法
String result = (String) method.invoke(target,args);
//执行目标方法后需要做的事
after();
return result + "--好听!好听!";
}
public void before() {
System.out.println("演出前需要与经纪人先谈好演出费");
}
public void after() {
System.out.println("唱完了,经纪人需要安排回去的返程");
}
}
test:
public class App
{
public static void main( String[] args )
{
//Jdk动态代理
ISingService vaeSing = new VaeSingService();
ISingService proxy = new JdkProxy(vaeSing).creatTarget();
System.out.println(proxy.sing("有何不可"));
}
}
五.Cglib动态代理
但是当目标对象没有实现任何接口的时候我们就没法使用JDK动态代理的方式。那么这个时候我们只能使用CGLIB代理。CGLIB的本质其实是代理类继承了目标对象,并覆盖相关方法。
创建CGLIB代理类实现MethodInterceptor 接口 并重写他的intercept接口:
public class CglibProxy implements MethodInterceptor {
private ISingService target;
public CglibProxy(ISingService target) {
this.target = target;
}
public ISingService creatTaget() {
Enhancer enhancer = new Enhancer();
//指定父类class
enhancer.setSuperclass(target.getClass());
//指定回调方法
enhancer.setCallback(this);
//创建代理对象
return (ISingService) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//执行目标方法前需要做的事
before();
//执行目标对象的方法
String result = (String) method.invoke(target,objects);
//String result = (String) methodProxy.invokeSuper(o,objects); //这种方式一样
//执行目标方法后需要做的事
after();
return result + "--好听!好听!";
}
public void before() {
System.out.println("演出前需要与经纪人先谈好演出费");
}
public void after() {
System.out.println("唱完了,经纪人需要安排回去的返程");
}
}
test:
public class App
{
public static void main( String[] args )
{
ISingService jaySing = new JaySingService();
//静态代理
// StaticProxy proxy = new StaticProxy(jaySing);
// System.out.println(proxy.sing("花海"));
//Jdk动态代理
// ISingService vaeSing = new VaeSingService();
// ISingService proxy = new JdkProxy(vaeSing).creatTarget();
// System.out.println(proxy.sing("有何不可"));
//Cglib动态代理
ISingService proxy = new CglibProxy(jaySing).creatTaget();
System.out.println(proxy.sing("明明就"));
}
}
六.JDK动态代理与CGLIB动态代理的区别
- 生成代理类,CGLIB要用到asm框架,要比IDK慢,调用cglib要快,因为是fastclass机制,但是可以忽略,这也是为什么源码一般用jdk
- jdk代理是通过接口,所以代理类必须要实现接口,或者可以直接代理空接口;cglib代理是通过继承的方式去实现,所以不能被继承的类或者方法是不能代理的
- JDK跟CGLIB都不能代理非public方法(spring事务失效)