什么是代理模式
代理模式主要对我们方法执行之前与之后实现增强
代理模式应用场景
日志的采集、权限控制、实现aop、Mybatis mapper、Spring的事务、全局捕获异常、Rpc远程调用、接口分布式事务原理代理数据源
代理模式实现的原理
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如图所示:
抽象主题角色:可以是接口,也可以是抽象类;
委托类角色:真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
静态代理
静态代理需要开发人员手动编写代理类代码
public interface SampleService {
void hello();
}
public class SampleServiceImpl implements SampleService {
public void hello() {
System.out.println("hello");
}
}
@Data
@AllArgsConstructor
public class SampleServiceProxy implements SampleService {
private SampleService sampleService;
public void hello() {
System.out.println("前置增强");
sampleService.hello();
System.out.println("后置增强");
}
}
public class SampleServiceProxyTest {
public static void main(String[] args) {
SampleService sampleServiceProxy = new SampleServiceProxy(new SampleServiceImpl());
sampleServiceProxy.hello();
}
}
动态代理
动态代理是在实现阶段不用关心代理类,在运行阶段才指定哪一个对象。
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成。
Jdk动态代理
1.创建被代理的接口和类;
2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;
3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;
@Data
@AllArgsConstructor
public class JdkInvocationHandler implements InvocationHandler {
/**
* 目标对象
*/
private Object target;
/**
*
* @param proxy 使用jdk程序生成的代理类
* @param method 目标方法
* @param args 方法需要传递的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("使用Jdk动态代理前置增强" );
Object result = method.invoke(target, args);
System.out.println("使用Jdk动态代理后置增强");
return result;
}
/**
* 生成代理类
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
}
public class JdkInvocationHandlerTest {
public static void main(String[] args) {
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new SampleServiceImpl());
SampleService sampleServiceJdkProxy = jdkInvocationHandler.getProxy();
sampleServiceJdkProxy.hello();
}
获取代理的生成的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
public final class $Proxy0 extends Proxy implements SampleService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void hello() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.hanke.SampleService").getMethod("hello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
方法m3通过反射获得sampleService的hello方法
注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
CGLIB动态代理
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
</dependencies>
public class CglibSampleServiceProxy implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib前置增强");
Object reuslt = methodProxy.invokeSuper(o, objects);
System.out.println("cglib后置增强");
return reuslt;
}
}
public class CglibSampleServiceProxyTest {
public static void main(String[] args) {
CglibSampleServiceProxy cglibSampleServiceProxy = new CglibSampleServiceProxy();
Enhancer enhancer = new Enhancer();
// 设置代理类的付类
enhancer.setSuperclass(SampleServiceImpl.class);
// 设置回调对象
enhancer.setCallback(cglibSampleServiceProxy);
// 创建代理对象
SampleServiceImpl sampleService = (SampleServiceImpl) enhancer.create();
sampleService.hello();
}
}
JDK和CGLIB动态代理区别
jdk:
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,
在调用具体方法前调用InvokeHandler来处理。
只能对实现了接口的类生成代理,而不能针对类。
cglib:
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
GLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,
并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。
CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍(运行快)
CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;(创建对象慢)
spring何时使用JDK还是CGLiB?
1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
如何强制使用CGLIB实现AOP?
1)添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)
2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>