简单理解:
我们在编写增删改查时,如果需要在不改变原有的类的结构的基础上,对这些数据库CRUD操作追加额外的日志记录功能。在项目研发中,通常使用的是spring框架中的aop设计模式或者拦截器。而他们的原理便是基于动态代理的思想。
代理模式是如何实现的?以日志记录为例子。
静态代理:
代理模式中有三个基本角色-----真实角色,抽象角色,代理角色。
抽象角色:增删改查的接口UserService
public interface UserService {
void select();
void save();
void delete();
void update();
}
真实角色:增删改查的实现类UserServiceImpl,实现了UserService接口
public class UserServiceImpl implements UserService {
public void select() {
System.out.println("查");
}
public void save() {
System.out.println("增");
}
public void delete() {
System.out.println("删");
}
public void update() {
System.out.println("改");
}
}
代理角色:同样实现接口的代理类UserProxy
public class UserProxy implements UserService {
private UserService userService;
//通过set方式注入真实对象
public void setUserService(UserService userService) {
this.userService = userService;
}
public void select() {
System.out.println("这是查找");
userService.select();
}
public void save() {
System.out.println("这是增加");
userService.save();
}
public void delete() {
System.out.println("这是删除");
userService.delete();
}
public void update() {
System.out.println("这是修改");
userService.update();
}
}
测试类:
public class StaticProxy {
@Test
public void run(){
//set方式注入真实角色UserServiceImpl
UserProxy userProxy = new UserProxy();
userProxy.setUserService(new UserServiceImpl());
//通过代理角色实现真实角色的相关方法
userProxy.select();
userProxy.save();
userProxy.update();
userProxy.delete();
}
}
结果:
这是查找
查
这是增加
增
这是修改
改
这是删除
删Process finished with exit code 0
由上可知,我们通过将代理类和真实的实现类实现相同的接口UserService,然后在代理类中注入了真实的实现类,通过操作代理类便可调用真实实现类的方法,实现了静态代理。
动态代理:
如果使用静态代理,每一个真实实现类都需要编写一个代理类,这在真实开发中是行不通,大大的降低了程序运行的效率。所以我们需要使用动态代理来取代静态代理,动态代理会根据真实实现类的类型自动去生成代理类对象,提高了效率。
常用的动态代理技术有Jdk动态代理和Cglib动态代理。下面主要讲解Jdk动态代理。
实现动态代理有实现接口和继承类两种方式,Jdk动态代理采用实现接口的方式。
首先需要熟悉InvocationHander接口和Proxy类。实现InvocationHander接口必须要实现invoke方法,而invoke方法在动态代理调用方法的时候被调用。简单来讲,invoke方法里面调用真实的业务逻辑和增强的业务逻辑。Proxy类中有静态方法newProxyInstance用来生成代理对象。
主要代码如下:
public class JdkDynamicProxy implements InvocationHandler {
private Object object;
//注入真实角色
public void setUserService(Object object) {
this.object = object;
}
//生成代理对象
//newProxyInstance为Proxy类的静态方法,三个参数的含义:
//第一个参数ClassLoader loader:类的加载器,传入我们自定义类的加载器
//第二个参数Class<?>[] interfaces 注意很重要 这个参数是传入一个接口数组
//第三个参数 h:类型是InvocationHandler,传入InvocationHandler接口的子类
public Object getProxy(){
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}
//proxy:代理类实例; method:被代理对象的方法;args:被代理对象的方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//日志输出
log(method.getName());
//实现真实角色的方法
method.invoke(object, args); //用了反射的思想
return null;
}
//代理对象的一些附加业务
public void log(String msg){
System.out.println("这是"+msg+"方法");
}
}
测试类:
public class DynamicTest {
@Test
public void test(){
UserService userService = new UserServiceImpl();
//set方式注入真实角色
JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy();
jdkDynamicProxy.setUserService(userService);
UserService proxy = (UserService)jdkDynamicProxy.getProxy();
proxy.select();
}
}
结果:
这是select方法
查
这是save方法
增
这是update方法
改
这是delete方法
删Process finished with exit code 0
动态代理是aop的底层原理,mybatis框架中mapper层的映射也用到了动态代理。