使用代理模式主要有两个目的:一保护目标对象,二增强目标对象
静态代理
静态代理类 -- 代表是在代码编译阶段已经生产的代理类。
与被代理类实现相同接口
模拟火车站买票 -- 静态代理类模拟火车票售卖点
public interface SellTicket {
void sell();
}
public class TrainStation implements SellTicket {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
public class StaticProxy implements SellTicket {
private SellTicket sellTicket;
public StaticProxy(SellTicket sellTicket) {
this.sellTicket = sellTicket;
}
@Override
public void sell() {
System.out.println("黄牛 : 静态代理类 -- 收取手续费");
sellTicket.sell();
}
}
public class ProxyTest {
public static void main(String[] args) {
SellTicket sellTicket = new StaticProxy(new TrainStation());
sellTicket.sell();
}
}
public class ProxyTest {
public static void main(String[] args) {
SellTicket sellTicket = new StaticProxy(new TrainStation());
sellTicket.sell();
}
}
运行结果:
黄牛 : 静态代理类 -- 收取手续费
火车站卖票
动态代理类
动态代理类 -- 代表是在代码运行期间生成的代理类
动态代理类:1.JDK生成的动态代理类 2.CGLIB生成的动态代理类
JDK 动态代理类
JDK生成动态代理类用一个限制,必须被代理的类具有接口。否则不能代理
public class JDKProxyFactory {
public static SellTicket getProxy(SellTicket sellTicket) {
SellTicket sellTicketProxy = (SellTicket) Proxy.newProxyInstance(
//在运行期间生成代理类,则需要类加载器
sellTicket.getClass().getClassLoader(),
//生成的代理类需要增强哪些接口的方法
sellTicket.getClass().getInterfaces(),
//当代理对象的方法被调用时,被handler的invoke方法接受处理
new InvocationHandler() {
/**
* @param proxy 代理对象本身 = sellTicketProxy
* @param method 被调用的方法
* @param args 被调用的方法传入的参数
* @return obj 返回值,void方法时为null
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("黄牛 2: JDK动态代理类 -- 收取手续费");
Object obj = method.invoke(sellTicket, args);
return obj;
}
}
);
return sellTicketProxy;
}
}
public class ProxyTest {
public static void main(String[] args) {
SellTicket jdkProxy = JDKProxyFactory.getProxy(new TrainStation());
jdkProxy.sell();
}
}
运行结果:
黄牛 2: JDK动态代理类 -- 收取手续费
火车站卖票
CGLIB 动态代理类
cglib可以在代理没有接口的普通类,原理:生成的代理类为目标类的子类,所以如果被代理的类是final类型,或者方法时final修饰的则不能被代理.
因为使用CGLIB,则需要导入依赖,如果是springboot项目则不需要单独导入,springboot的依赖已经自带了
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
public class CglibProxyFactory {
public static <T> T getProxy(T t) {
Enhancer enhancer = new Enhancer();
//设置生成谁的子类
enhancer.setSuperclass(t.getClass());
enhancer.setCallback(new MethodInterceptor() {
/**
*当代理对象的方法被调用时,被intercept方法接受处理
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("黄牛 3: CGLIB动态代理类 -- 收取手续费");
Object obj = method.invoke(t, args); // 用方法反射调用目标
// Object obj = methodProxy.invoke(t, args); // 内部没有用反射, 需要目标 (spring)
// Object result = methodProxy.invokeSuper(o, args); // 内部没有用反射, 需要代理
return obj;
}
});
return (T) enhancer.create();
}
}
public static void main(String[] args) {
System.out.println("--------------------------------");
SellTicket cglibProxy = CglibProxyFactory.getProxy(new TrainStation());
cglibProxy.sell();
}
运行结果:
黄牛 3: CGLIB动态代理类 -- 收取手续费
火车站卖票
使用methodProxy.invoke(), methodProxy.invokeSuper()都没有使用反射的机制进行方法调用,所以效率会高于method.invoke()
总结:
被代理类是存在接口的就使用JDK生成代理类,如果是没有接口的就使用CGLIB生成代理类