1.代理模式
为目标对象生成个替身,以控制对这个目标对象的访问。通过代理对象访问目标对象的好处是:可以在目标对象实现的基础上,增强额外的功能,即扩展目标对象的功能。
graph LR
Client -->ProxyObject -->TargetObject
2.静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(目标对象)与代理对象需要一起实现相同的接口或者继承相同父类。
-
定义一个接口:IteachDao给被代理对象以及代理对象实现。
-
被代理对象实现TeacherDao实现接口IteachDao。
-
使用静态队里方式,就需要代理对象TeachserDaoProxy中也实现IteachDao接口。
-
调用的时候通过调用代理对象的方法来调用方法。
-
特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。 思路分析图解(类图)
IteachDao类
//接口给代理对象以及被代理对象实现 public interface IteachDao(){ void teach(); }Client类
public class Client{ public static void main(String[] args){ //创建被代理对象 TeacherDao target = new TeacherDao(); //创建代理对象,并通过构造方法把被代理对象传递代理对象 TeacherDaoProxy proxy = new TeahcerDaoProxy(target); //通过代理对象调用被代理对象方法 //即:执行代理对象的方法,代理对象再去执行被代理对象的方法 proxy.teacher(); } }TeacherDao类
public class TeacherDao implements IteachDao(){ @Override public void teach(){ System.out.Println("老师授课中。。。"); } }TeacherDaoProxy类
public class TeacherDaoProxy implements IteacherDao(){ private IteachDao target; //构造器 public TeacherDaoProxy(IteachDao taeget){ this.target = target; } @Override public void teach(){ //增强功能,扩展 System.out.Println("开始代理,完成课前操作"); target.teach(); //增强功能,扩展 System.out.Println("结束代理,收尾操作"); } }优缺点
- 优点:在不修改目标对象的前提下,通过代理对象对目标功能进行扩展。
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,代理对象和目标对象都需要修稿。
3.cglib和jdk动态代理
代理对象不需要实现接口,但目标对象需要实现接口,代理对象的生成是利用jdk的API,动态的内存中构建代理对象。
jdk代理
-
代理类所在包:java.lang.reflect.Proxy
-
JDK实现代理只需要使用newProxyInstance方法,该方法接受三个参数:static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandel handel)
IteacherDao类
//接口
public interface IteacherDao(){
void teach();
void sayHello(String name);
}
ProxyFactory类
public class ProxyFactory(){
//维护一个目标对象
private Object targer;
//构造器 对target初始化
public ProxyFactory(Object target){
this.target = target;
}
//给目标对象生成一个代理对象
public Object getProxyInstance(){
/*
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
//1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
//2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
//3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
*/
return Proxy.newProxyInstance(taget.getCalss().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandel(){
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
System.out.Println("Jdk动态代理开始~~");
Object returnValue = method.invoke(target,args);
System.out.Println("JDK代理提交");
return returnValue;
}
});
}
}
TeacherDao类
public class TeacherDao implements IteacherDao{
@Override
public void teache(){
System.out.Println("老师授课中。。。");
}
@Override
public void sayHello(String name){
System.out.Println("hello "+name);
}
}
Client类
public class Client{
public static void main(String[] args){
//创建目标对象
TeacherDao target =new TeacherDao();
//给目标对象创建代理对象可以转换成IteacherDao
IteacherDao proxy = (IteacherDao)new ProxyFactory(target).getProxyInstance;
// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
System.out.println("proxyInstance=" + proxyInstance.getClass());
proxy.sayHello("lilei");
}
}
cglib代理
-
静态代理和动态代理都需要目标对象实现一个接口,但有时候目标对象只是个简单的对象,并没有实现接口,这时候可以使用目标对象子类来实现代理这就是cglib代理。
-
cglib代理也叫子类代理,他是在内存中构建个子类对象来实现对目标对象的扩展功能。
-
cglib是一个高性能的代码生成包,可以在代码运行期扩展java类与实现java接口,它广泛的被许多Aop框架使用,例如Spring Aop实现方法拦截。
-
在Aop编程中如何选择代理:
-
目标对象需要实现接口:JDK代理
-
目标对象不需要实现接口:cglib代理
-
-
cglib底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。
-
需要引入新的maven
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> -
在内存中构建类,注意不能代理final修饰的类,否则报错,抛出java.lang.IllegalArgumentException
-
目标对象的方法如果是static/final修饰,则不会被拦截,既不会执行目标方法之外额外的业务方法。
TeacherDao类
public class teacherDao{
public String teach(){
System.out.Println("老师授课中 cglib代理,不需要实现任何接口。。。");
return "hello";
};
}
ProxyFactory类
public class ProxyFacotry implements MethodInterceptor{
//维护一个目标对象
private Object target;
//构造器,传入一个被代理对象
public ProxyFactory(Object target){
this.target = target;
}
//返回一个代理对象
public Object getProxyInstance(){
//1.创建个工具类
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperClass(target.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类对象,即代理对象
return enhancer.create();
}
//重写intercpt方法 会调用目标对象的方法
@Override
public Object intercpt(Object arg0,Method method,Object[] args,MethodProxy arg3){
System.out.prinln("cglib代理开始~");
Object returnValue =method.invake(target,args);
System.out.println("cglib代理结束~");
}
}
Client类
public class Client{
public static void manin(String[] args){
//创建目标对象
TeacherDao target =new TeahcerDao();
//传入目标对象,获取代理对象
TeacherDao proxy =(TeahcerDao) new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法,触发intercpt方法,从而实现对目标对象的调用
String res = proxy.teach();
System.out.println(res);
}
}