存在问题
在我们开发过程中,会遇到很多大量改动代码的情况,因为需求在不断地变化不定,比如字段的更新,日志的处理,格式的变换等等一系列问题,少量的还可以使用替换等操作来改动,但是有可能改动的代码量过于庞大,替换似乎就不太那么可能了。
所以,加入一种切面的思想,就是在不改动原有的代码的基础上,实现整体的功能,比如:现在我所有的接口(1万个接口)已经开发好了,但是现在想在每个接口里面加入操作日志的记录,不可能手动去更改每个接口,那该如何操作呢?如下。
原来的具体实现
有这样一个接口
public interface UserService {
User getUser();
Integer addUser(User user);
Integer updateUser(User user);
Integer deleteUser(Integer id);
}
这是它对应的实现类
public class UserServiceImpl implements UserService {
@Override
public Integer addUser(User user) {
addLog();//我们在这里使用增加日志的方法,但是懊恼的是,我们有很多类似addUser()的方法都要调用它。
System.out.println("新增了一个用户");
return 1;
}
//其余重写方法大同小异
//增加日志方法
public void addLog(){
System.out.println("【log】执行了新增方法!");
}
}
然后调用的时候通过实现类来进行调用
public class UserController {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.addUser(new User());
}
}
输出
【log】执行了新增方法!
新增了一个用户
这是原有的方式实现一个接口的实现和调用,然后使用新的方式开始对每一个接口增加日志记录的操作
新的实现方式(使用动态代理)
原理
- 动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理--cglib
- 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
- 我们这里使用JDK的原生代码来实现,其余的道理都是一样的
JDK的动态代理需要了解两个类
核心 : InvocationHandler
和Proxy
,打开JDK帮助文档看看
- InvocationHandler:调用处理程序
-
invoke方法
-
Proxy:代理类
-
newProxyInstance 方法
我们需要定义一个动态代理类
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
public void setTarget(Object target){
this.target = target;
}
//proxy:代理类
//method:代理类的调用处理程序的方法对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
addLog(method.getName());//在这个方法中,在我们需要真正执行的方法前后我们可以使用这样的方式来实现。
Object result = method.invoke(target,args);//这是执行的具体我们调用的方法,也就是下面调用中的addUser()方法。
showLog(method.getName());
return result;//这里的result也就是我们真正执行方法addUser()返回的结果。
}
//如果我们再要想加入其他的方法,直接在这个代理类中添加即可,无需再每一个接口上面进行修改
//具体要新增的切入方法
public void addLog(String methodName){
System.out.println("执行了"+methodName+"方法!");
}
//具体要新增的切入方法
public void showLog(String methodName){
System.out.println("打印一下:"+methodName+"方法!");
}
}
如何调用
public class UserController {
public static void main(String[] args) {
//真实的对象
UserServiceImpl userService = new UserServiceImpl();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置要代理的对象
pih.setTarget(userService);
//动态生成代理类
UserService prozy = (UserService) pih.getProxy();
prozy.addUser(new User());
}
}
输出
执行了addUser方法!
新增了一个用户
打印一下:addUser方法!
当我们再需要更改对接口的大量更改就可是使用这样的方式来实现,但是这样写起来似乎也比较麻烦,还有种通过Spring注解的方式实现,前往查看