4.设计模式-代理模式(结构型)
先说下如果没有代理模式的代码
package start.service;
public interface WuPinService {
void goumai();
}
package start.service.impl;
import start.service.WuPinService;
public class MingServiceImpl implements WuPinService {
@Override
public void goumai() {
System.out.println("明购买物品");
before();
after();
//可不可以把快递这部分交给别人来做呢?所以我们要用代理模式 先说静态代理
}
private void after() {
System.out.println("快递员取货");
}
private void before() {
System.out.println("快递员送货");
}
}
我们可不可以把before();、after();等无关的方法交给别人来处理呢?相当于找一个房屋中介,来处理这些事情,使调用者无感知,那么下面就说静态代理模式
静态代理
抽象主题角色(被代理类和代理类都要实现的接口)
public interface WuPinService {
void goumai();
}
真实主题角色(被代理类)
package staticproxy.service.impl;
import staticproxy.service.WuPinService;
public class MingServiceImpl implements WuPinService {
@Override
public void goumai() {
System.out.println("明购买物品");
}
}
代理主题角色 (代理类)
package staticproxy.proxy;
import staticproxy.service.WuPinService;
import staticproxy.service.impl.MingServiceImpl;
public class WuPinProxy implements WuPinService {
private MingServiceImpl mingServiceImpl;
public WuPinProxy(MingServiceImpl mingService){
mingServiceImpl = new MingServiceImpl();
}
@Override
public void goumai() {
mingServiceImpl.goumai();
before();
after();
}
private void after() {
System.out.println("快递员送货");
}
private void before() {
System.out.println("快递员取货");
}
}
从代理类来看的话,如果又来一个人也要买东西,也需要快递员送货取货,这个时候这个代理类是实现不了的,所以引出动态代理的概念
动态代理
JDK
接口
package dynamicproxy.jdk.service;
public interface WuPinService {
void goumai();
}
被代理类
package dynamicproxy.jdk.service.impl;
import dynamicproxy.jdk.service.WuPinService;
public class MingServiceImpl implements WuPinService {
@Override
public void goumai() {
System.out.println("明购买物品");
}
}
package dynamicproxy.jdk.service.impl;
import dynamicproxy.jdk.service.WuPinService;
public class YuServiceImpl implements WuPinService {
@Override
public void goumai() {
System.out.println("雨购买物品");
}
}
代理类
package dynamicproxy.jdk.proxy;
import dynamicproxy.jdk.service.WuPinService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class WuPinProxy implements InvocationHandler {
private WuPinService wuPinService;
public WuPinService getInstance(WuPinService wuPinService){
this.wuPinService = wuPinService;
Class<? extends WuPinService> clazz = wuPinService.getClass();
return (WuPinService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = method.invoke(this.wuPinService, args);
before();
after();
return obj;
}
private void after() {
System.out.println("快递员送货");
}
private void before() {
System.out.println("快递员取货");
}
}
调用
package dynamicproxy.jdk;
import dynamicproxy.jdk.proxy.WuPinProxy;
import dynamicproxy.jdk.service.WuPinService;
import dynamicproxy.jdk.service.impl.MingServiceImpl;
import dynamicproxy.jdk.service.impl.YuServiceImpl;
public class Test {
public static void main(String[] args) {
WuPinProxy wuPinProxy = new WuPinProxy();
WuPinService wuPinService = wuPinProxy.getInstance(new YuServiceImpl());
wuPinService.goumai();
}
}
步骤
- 通过反射获取到被代理类的接口
- jdk动态代理重新生成一个新的类,这个新的类实现上面第1部所说的所有接口
- 动态生成java代码的同时,把新加的业务添加进去
- 编译生成class文件,然后执行
所以,jdk代理的目标类(重新生成的那个类)必须要实现被代理类的接口,因为jdk代理是根据被代理对象的所有接口去生成新的方法!
通过Proxy.newProxyInstance()的返回可以看出,返回的是接口,所以,jdk代理只能代理接口
CGLib
被代理类
package dynamicproxy.cglib.service.impl;
public class MingServiceImpl {
public void goumai() {
System.out.println("明购买物品");
}
}
package dynamicproxy.cglib.service.impl;
public class YuServiceImpl {
public void goumai() {
System.out.println("雨购买物品");
}
}
代理类
package dynamicproxy.cglib.proxy;
import dynamicproxy.jdk.service.WuPinService;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class WuPinProxy implements MethodInterceptor {
public Object getInstance(Class<?> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
after();
Object obj = methodProxy.invokeSuper(o, objects);
return obj;
}
private void after() {
System.out.println("快递员送货");
}
private void before() {
System.out.println("快递员取货");
}
}
调用
package dynamicproxy.cglib;
import dynamicproxy.cglib.proxy.WuPinProxy;
import dynamicproxy.cglib.service.impl.MingServiceImpl;
import dynamicproxy.cglib.service.impl.YuServiceImpl;
import java.util.*;
public class Test {
public static void main(String[] args) {
WuPinProxy wuPinProxy = new WuPinProxy();
MingServiceImpl service = (MingServiceImpl) wuPinProxy.getInstance(MingServiceImpl.class);
service.goumai();
// JDK Proxy代理需要实现接口
// CGLib除了final修饰的方法都可以代理
// JDK Proxy是JDK自带的
// CGLib属于第三方组件
}
}
步骤
- 创建被代理的类及方法
- 创建一个实现MethodInterceptor 的代理类,重写 intercept 方法;
- 创建获取被代理类的方法 getInstance(Object target);
- 获取代理类,调用代理类方法。
CGLib代理的不需要被代理对象实现任何接口,它会重写被代理类的所有方法,所以不能代理final修饰的方法或者
类
因为被final修饰后,表明这个类不能被继承
动态代理
JDK动态代理
- 目标类一定要实现接口
- 通过目标类得到代理类,三个参数:1)类加载器,2)目标类实现的接口 3)InvocationHandler
- 生成的代理类是final修饰的
- jdk只生成一个代理类文件
- 通过反射去调用方法
CGLib动态代理
- 生成的代理类会去继承目标类
- 通过index去找到方法
- cglib生成三个文件
生成代理类时jdk更快,cglib慢
调用时jdk用的反射,cglib用的fastClass机制通过index去定位,cglib会更快
jdk1.8之后(反射有了很大的优化)
CGLib为什么要生成三个文件?
- 代理类的文件
- 先去找到我们的索引
- 通过索引定位到我们的方法
- 还有个继承了Factory
我们的代理类还能再被代理吗?
-
jdk代理后的类再次被jdk代理?
jdk不能再次被jdk代理,因为会造成handler调用死循环。如果使用不同的InvocationHandler可以,mybatis中的
多重插件就是这么做的 -
jdk代理后的类再次被cglib代理?
jdk生成代理类是final,cglib生成代理类去继承目标类,所以不能被代理 -
cglib代理后的类再次被cglib代理?
会报方法名重复 Duplicate method name "newInstance" with signature -
cglib代理后的类再次被jdk代理?
类的签名已经改变,没有目标方法了
只有jdk代理能再次被jdk代理,必须使用不同的InvocationHandler,其他都不可以
什么样类不能被代理?
- 对于jdk,必须有接口实现
- final修飾的方法不能被cglib代理
- 方法不是public的
接口能够被代理吗?
接口能被代理,比如mybatis中的dao接口就是这样做
你工作当中有没有用过?
这个后期总结下!!!
JDK代理和CGLib代理的区别?
- JDK动态代理通过反射去实现被代理对象的所有接口,所以JDK只能代理实现了接口的对象,CGLlib对代理类
所有方法重写 - JDK动态代理跟CGLib代理都是在运行期生成新的字节码,但是CGLib实现更为复杂,用的是ASM框架,生成
代理类的效率比JDK低 - JDK动态代理是通过反射机制去查询所有被代理对象的接口,CGLib代理是通过FastClass机制直接调用方法,
所有执行效率,CGLib比JDK代理要高
Spring用的是哪个代理模式?
bean有实现接口的时候用jdk,没有实现的话模式cglib,也可以强制用cglib