1、什么是代理模式
代理模式就是使用代理对象来替代真实对象的访问,这样就可以在不修改目标对象的基础上,增加新的功能操作,扩展目标对象的功能。
简单来说,代理就是增强目标方法的功能。
举例来说,比如一个service
方法中有增删改查等诸多操作数据库的方法,每个方法在完成自己本身业务逻辑的情况下,还需要在业务逻辑前后增加开启事务和提交事务的逻辑。这样会造成大量的重复代码,这时候,我们就可以使用代理模式,把service
作为目标对象,再新建一个增强类,这个类中有两个方法,开启事务和提交事务。再创建一个代理类,用来将增强类织入
目标类中。这样我们就只用关注业务代码,而像一些通用逻辑代码,如开启事务等,放到增强类中统一管理,不用在每个方法中重复编程。实现了代码的高度解耦。也更利于代码的维护。
代理模式分为静态代理
和动态代理
。
2、静态代理模式
静态代理中,我们对于每个目标方法的增强都是手动完成的,即每一个目标类都有一个代理类(代理类实现相同的接口),非常不灵活,如果目标类中发生改动,也要对应的修改代理类。
从JVM
层面上来说,静态代理就是在编译时,就将接口、目标类、代理类等变成了一个个class
文件。
在实际运用中,很少使用静态代理。
代码示例:
接口代码
package com.kk.first.aop;
public interface AopStaticService {
public String add(String name);
public void update(String name);
public void delete(String name);
}
实现类代码
package com.kk.first.aop;
public class AopStaticServiceImpl implements AopStaticService {
@Override
public String add(String name) {
System.out.println("新增成功" + name);
return name;
}
@Override
public void update(String name) {
System.out.println("修改成功" + name);
}
@Override
public void delete(String name) {
System.out.println("删除成功" + name);
}
}
增强类代码
package com.kk.first.aop;
public class AopHigh {
public void beginTransaction(){
System.out.println("开启事务 ");
}
public void commit(){
System.out.println("提交事务");
}
}
代理类代码
package com.kk.first.aop;
public class AopStaticProxy implements AopStaticService {
/**
* 目标类
*/
private AopStaticService aopStaticService;
/**
* 增强类
*/
private AopHigh aopHigh;
public AopStaticProxy(AopStaticService aopStaticService, AopHigh aopHigh) {
this.aopStaticService = aopStaticService;
this.aopHigh = aopHigh;
}
@Override
public String add(String name) {
aopHigh.beginTransaction();
String result = aopStaticService.add(name);
aopHigh.commit();
return result;
}
@Override
public void update(String name) {
aopHigh.beginTransaction();
aopStaticService.update(name);
aopHigh.commit();
}
@Override
public void delete(String name) {
aopHigh.beginTransaction();
aopStaticService.delete(name);
aopHigh.commit();
}
}
测试代码
package com.kk.first.aop;
public class AopTest {
public static void main(String[] args) {
AopStaticService aservice = new AopStaticServiceImpl();
AopHigh ahigh = new AopHigh();
AopStaticProxy sp = new AopStaticProxy(aservice, ahigh);
sp.add("测试新增");
sp.update("测试修改");
sp.delete("测试删除");
}
}
结果如下
开启事务
新增成功测试新增
提交事务
开启事务
修改成功测试修改
提交事务
开启事务
删除成功测试删除
提交事务
3、动态代理模式
动态代理相对于静态代理而言更加灵活,不需要争对每一个目标类写写一个代理类,也不是必须要实现接口,可以直接代理实现类(CGLIB动态代理机制
)。
动态代理在实际的业务代码开发中,使用的不多,但是在框架开发中是一项重要的实现机制。
Java AOP
和RPC
框架都依赖了动态代理来实现相关功能。
动态代理有两种实现方式:JDK动态代理机制
和CGLIB动态代理机制
。
4、JDK动态代理机制
在Java动态代理中
,InvocationHandler
接口和Proxy
类是核心。
Proxy
类主要是调用newProxyInstance
方法来生成被代理类的一个代理对象。
实现InvocationHandler
接口的类可以用于拦截被代理类调用的方法,在方法执行前后增加自己的逻辑,主要在invoke
方法中。
代码示例:
接口代码、实现类代码和增强类代码同静态代理一样。
动态代理类代码
package com.kk.jdk.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* jdk动态代理类(方法拦截器)
* @author kk
*
*/
public class AopInvocationHandler implements InvocationHandler{
/**
* 目标类
*/
private Object target;
/**
* 增强类
*/
private AopHigh high;
public AopInvocationHandler(Object target, AopHigh high) {
this.target = target;
this.high = high;
}
/**
* 调用被代理类的方法,可以增加自己的逻辑,也就是加入增强类的逻辑
* proxy:动态生成的代理类
* method:与代理类对象调用的方法相对应
* args:当前 method 方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
high.beginTransaction();
Object result = method.invoke(target, args);// 参数为被代理的类和调用方法传入的参数
high.commit();
return result;
}
}
代理工厂类代码
package com.kk.jdk.aop;
import java.lang.reflect.Proxy;
/**
* 代理工厂
* @author kk
*
*/
public class JdkProxyFactory {
/**
* 获取某个类的代理对象
* @param target 目标类
* @param high 增强类
* @return
*/
public static Object getProxy(Object target, AopHigh high) {
// Proxy.newProxyInstance三个参数的含义:
// 第一个:类加载器,用于加载代理对象
// 第二个:目标类实现的接口
// 第三个:实现了 InvocationHandler 接口的对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new AopInvocationHandler(target, high));
}
}
测试代码
package com.kk.jdk.aop;
public class JdkTest {
public static void main(String[] args) {
AopService service = (AopService)JdkProxyFactory.getProxy(new AopServiceImpl(), new AopHigh());
service.add("jdk测试新增");
service.update("jdk测试更新");
service.delete("jdk测试删除");
}
}
结果如下
开启事务
新增成功jdk测试新增
提交事务
开启事务
修改成功jdk测试更新
提交事务
开启事务
删除成功jdk测试删除
提交事务
5、CGLIB动态代理机制
JDK动态代理机制有一个致命的问题就是,只能代理实现了接口的类。
为了解决这个问题,可以使用CGLIB动态代理机制
。
CGLIB
是一个基于ASM
的字节码生成库,可以在运行时对字节码进行修改和动态生成。
CGLIB动态代理机制
中MethodInterceptor
接口和Enhancer
类是核心。
MethodInterceptor
接口用于拦截目标类调用的方法,增加扩展的逻辑。
Enhancer
类用于创建代理类。
使用CGLIB动态代理
需要引入CGLIB
的jar
包。
gradle
添加引用如下
implementation group: 'cglib', name: 'cglib', version: '3.3.0'
代码示例:
目标实现类
package com.kk.jdk.cglib;
public class CglibServiceImpl {
public String add(String name) {
System.out.println("新增成功" + name);
return name;
}
}
增强类同jdk动态代理
。
CGLIB
动态代理类(方法拦截器)
package com.kk.jdk.cglib;
import java.lang.reflect.Method;
import com.kk.jdk.aop.AopHigh;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 方法拦截器
* @author kk
*
*/
public class CglibMethodInterceptor implements MethodInterceptor{
/**
* 目标对象
*/
private Object target;
/**
* 增强类
*/
private AopHigh high;
public CglibMethodInterceptor(Object target, AopHigh high) {
this.target = target;
this.high = high;
}
/**
* 增强方法
* obj:被代理的对象(需要增强的对象)
* method:被代理的对象(需要增强的对象)
* args:方法入参
* proxy:用于调用原始方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
high.beginTransaction();
Object result = proxy.invoke(target, args);
high.commit();
return result;
}
}
CGLIB
代理工厂类
package com.kk.jdk.cglib;
import com.kk.jdk.aop.AopHigh;
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyFactory {
public static Object getProxy(Object target, AopHigh high) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(target.getClass().getClassLoader());
// 设置被代理类
enhancer.setSuperclass(target.getClass());
// 设置方法拦截器
enhancer.setCallback(new CglibMethodInterceptor(target, high));
// 返回创建的代理对象
return enhancer.create();
}
}
测试类
public class CglibTest {
public static void main(String[] args) {
CglibServiceImpl service = (CglibServiceImpl)CglibProxyFactory.getProxy(new CglibServiceImpl(), new AopHigh());
service.add("CGLIB测试新增");
}
}
结果如下
开启事务
新增成功CGLIB测试新增
提交事务
6、jdk动态代理与cglib动态代理的对比
(1)jdk动态代理
只能代理实现了接口的类或直接代理接口,cglib动态代理
可以代理未实现接口的类。
(2)就效率而言,大部分情况下,jdk动态代理
更加优秀。
7、静态代理与动态代理的对比
(1)动态代理
更加灵活,不需要必须实现接口,可以直接代理实现类,也不需要为每一个目标类都创建一个代理类。而静态代理
中,接口一旦增加新的方法,目标类和代理类都需要修改逻辑。
(2)就JVM
而言,动态代理
是在运行时动态生成字节码,并加载到jvm
中,而静态代理
是在编译期就把接口、目标类、代理类等变成了class
文件。