代理模式
说明
代理模式(Proxy Pattern)属于结构性模式,是指为其他对象提供一种代理,以控制对这个对象的访问。
在不修改目标对象的情况下对目标对象进行增强,Spring的AOP本质就是代理模式。
静态代理
定义一个接口
package com.gupaoedu.service;
public interface SomeService {
public String doSome(String msg);
}
定义目标对象,实现接口中的方法
/**
*
* 代理模式的目标对象
*/
public class SomeServiceImpl implements SomeService {
public String doSome(String msg) {
System.out.println("目标对象方法执行了...." + msg);
return "hello";
}
}
定义静态代理类,实现接口中的方法,并且包含对目标对象的引用,在方法中调用目标对象的方法,同时在调用方法前后可以根据需要进行增强
public class StaticSomeProxy implements SomeService {
private SomeService target; // 需要增强的目标对象
public StaticSomeProxy(SomeService target) {
this.target = target;
}
public String doSome(String msg) {
System.out.println("目标对象执行之前的操作....");
// 调用目标对象的方法
String res = target.doSome(msg);
System.out.println("目标对象执行之后的操作....");
return res.toUpperCase();
}
}
测试
public static void main(String[] args) {
// 获取目标对象
SomeService target = new SomeServiceImpl();
// 获取代理对象 增强目标对象
SomeService proxy = new StaticSomeProxy(target);
// 处理
System.out.println(proxy.doSome("nihao"));
}
动态代理
JDK动态代理
如果目标对象有实现接口,可以使用JDK动态代理
public class Test02 {
public static void main(String[] args) {
// 获取目标对象
final SomeService target = new SomeServiceImpl();
// 获取代理对象
SomeService proxy = (SomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader() // 类加载器
, target.getClass().getInterfaces() // 目标对象实现的接口数组
, new InvocationHandler() { // InvocationHandler的实现
/**
* 当代理对象执行相关方法的时候会执行的逻辑
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("目标方法执行之前....");
// 调用目标对象的方法
String msg = (String) method.invoke(target, args[0]);
System.out.println("目标方法执行之后....");
return msg.toUpperCase();
}
});
System.out.println(proxy.doSome("aaa"));
}
}
CGLIB动态代理
如果目标对象没有实现任何的接口那么我们是使用不了JDK动态代理的,那么这个时候我们只能使用
CGLIB代理。CGLIB的本质其实的代理类继承了目标对象,并重写相关方法。
引入依赖
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
</dependencies>
创建目标对象
public class SomeServiceImpl implements SomeService {
public String doSome(String msg) {
System.out.println("目标对象方法执行了...." + msg);
return "hello";
}
}
创建代理类
public class CglibProxy implements MethodInterceptor {
private UserServiceImpl target;
public CglibProxy(UserServiceImpl target) {
this.target = target;
}
/**
* 给外界暴露的创建代理对象的方法
* @return
*/
public UserServiceImpl createProxy(){
Enhancer enhancer = new Enhancer();
// 指定父类
enhancer.setSuperclass(UserServiceImpl.class);
// 指定回调方法
enhancer.setCallback(this);
// 创建对象代理对象
return (UserServiceImpl) enhancer.create();
}
/**
* 需要执行具体的代理方法
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
String msg = (String) method.invoke(target, objects[0]);
System.out.println("after...");
return msg.toUpperCase();
}
}
测试
public class Test03 {
public static void main(String[] args) {
// 获取目标对象
UserServiceImpl target = new UserServiceImpl();
// 获取代理对象
UserServiceImpl proxy = new CglibProxy(target).createProxy();
System.out.println(proxy.say("gp"));
}
}
JDK与Cglib的区别
- JDK动态代理实现了被代理对象的接口,CGLib代理继承了被代理对象;
- JDK动态代理和CGLib代理都是在运行期间生成字节码,JDK动态代理直接写Class字节码,CGLib代理使用ASM框架写Class字节码,CGLib代理实现更加复杂,生成代理类比JDK动态代理效率低;
- JDK动态代理调用代理方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用的,CGLib代理的执行效率更高;
- Cglib不能代理final的方法。