Spring AOP的核心技术就是动态代理,所以小编学习并整理了代理模式的材料,供大家一起学习。
1、代理模式满足的三个必要条件:
- 两个角色:执行者、被代理对象
- 这个过程必须要做,但是自己不能做或者不想做,交给专业的人(媒婆)
- 执行者必须拿到被代理对象的引用(需要知道你要什么信息)
2、代理模式分:静态代理、动态代理(JDK动态代理和cglib动态代理)
3、静态代理:
a.定义一个接口
public interface Count(){
public void queryCount();
}
b.业务逻辑类实现接口
public class CountImpl implements Count(){
@Override
public void queryCount(){
System.out.println("实现业务逻辑类");
}
}
c.定义业务代理类
public class CountProxy implements Count(){
private CountImpl countImpl;
public CountProxy(CountImpl countImpl){
this.countImpl = countImpl;
}
@Override
public void queryCount(){
System.out.println("在调用代理业务类之前执行");
countImpl.queryCount();
System.out.println("在调用代理业务类之后执行");
}
}
d.调用
public static void main(String []args){
CountImpl countImpl = new CountImpl();
CountProxy countProxy = new CountProxy(countImpl);
countProxy.queryCount();
}
缺点:一个代理类只能代理一个业务接口,如果要代理多个业务接口需要定义多个实现类和代理类,如果在调用代理业务类前后的代码是一样的,则多个代理类会有很多冗余代码。
4、动态代理:根据传进来的业务实现类和方法名进行具体调用
JDK动态代理
a.定义业务逻辑接口
public interface BookFacade(){
public void addBook();
}
b.定义业务实现类
public class BookFacadeImpl implements BookFacade(){
@Override
public void addBook(){
System.out.printin("增加图书的方法");
}
}
c.创建动态代理类:InvocationHandler是调用管理接口
public class BookFacadeProxy implements InvocationHandler {
// 业务实现类对象,用来调用具体的业务方法
private Object target;
// 绑定业务对象并返回一个代理类
public Object bind(Object targer) {
this.targer = targer;
// 通过反射机制,创建一个代理类对象并返回实例,用户进行方法调用时使用
// 创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,调用真的的业务方法)、接口、handler实现类
return Proxy.newProxyInstance(this.targer.getClass().getClassLoader(), this.targer.getClass().getInterfaces(), this);
}
// 包装调用方法:进行预处理、调用后处理
public Object invoke(Object proxy, Method method, Object []args) throws Throwable {
Object result = null;
System.out.println("进行预处理");
// 调用真正的业务方法
method.invoke(targer, args)
System.out.println("进行调用后处理");
}
}
d.在使用时,先创建一个业务实现类对象和一个代理类对象,然后定义接口引用(向上转型)并用代理对象.bind(业务实现类对象)的返回值进行赋值,最后通过接口引用调用真实业务方法。(接口引用指向一个绑定了业务类的代理类对象,所以通过接口名调用的是被代理的方法们)
public static void main(String []args) {
BookFacadeImpl bookFacadeImpl = new BookFacadeImpl();
BookFacadeProxy proxy = new BookFacadeProxy();
BookFacade bookFacade = (BookFacade)proxy.bind(bookFacadeImpl);
bookFacade.addBook();
}
缺点:JDK动态代理的代理对象在创建时,需要有业务实现类所实现的接口作为参数(因为后面代理方法需要根据接口内的方法名进行调用)。如果业务实现类没有实现接口而是直接定义接口的话,就无法使用JDK动态代理。并且如果业务实现类中新增了接口中没有的方法,这些方法也是无法被代理的(因为无法调用)。
CGlib动态代理:是根据针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,(因为采用的是继承,所以不能对final修饰的类进行代理)。
a.定义业务类,无需实现接口
public calss BookFacadeImpl1 {
public void addBook() {
System.out.println("增加一本书");
}
}
b.实现MethodInterceptor方法代理接口,创建代理类
public class BookFacadeCglib implements MethodInterceptor {
// 业务对象类,代理方法中进行真正的业务方法调用
private Object targer;
// 相当于JDK动态代理的bind 绑定
public Object getInstance(Object targer) {
this.targer = targer;
// 创建加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
// 加强器要指定代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSupperclass(this.targer.getClass());
// 设置回调:对于代理类上所有方法的调用,都会调用CallBack,
enhancer.setCallBack(this);
// 创建动态代理对象并返回
return enhancer.create();
}
// 实现回调方法
public Object intercept(Object obj, Method method, Object []args, MethodProxy proxy) {
System.out.println("预处理方法");
// 调用父类的方法
proxy.invokeSuper(obj, args);
System.out.println("调用后方法");
}
}
c.创建业务类对象和代理类对象,代理类对象.getInstance(业务类对象)返回一个动态代理对象(它是业务类的子类,可以用业务类引用指向它),最后通过动态代理类对象进行方法调用。
public static void main(String []args) {
BookFacadeImpl1 bookFacadeImpl1 = new BookFacadeImpl1();
BookFacadeCglib bookFacadeCglib = new BookFacadeCglib();
BookFacadeImpl1 bookFacade = (BookFacadeImpl1)bookFacadeCglib.getInstance(bookFacadeImpl1);
bookFacade.addBook();
}
5.静态代理、JDK动态搭理、CGlib动态代理比较:
静态代理:通过在代码中显式定义一个业务类一个代理,在代理类中对业务方法进行包装,用户通过代理类调用被包装过的业务方法。
缺点:每个代理类只能代理一个业务类,如果有多个业务类需要代理则需要些多个代理类
JDK动态代理:通过传进来的业务实现类和方法进行调用业务实现类的同名方法
缺点:如果该业务实现类没有实现接口而是直接定义接口,或者是该业务实现类中增加了接口没有的方法,则就无法被代理类调用
CGlib动态代理:通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行调用
据说原来的CGlib比JDK的动态代理性能要高出很多,但是由于JDK6 7 8逐渐完善,差距越来越小,而且有被反超的可能