尚学堂Spring视频教程(五):Spring AOP

时间:2024-01-03 20:51:08

在第一节中,我们自己模拟了一个Spring,实现一个保存用户的操作,假如现在有一个需求,在保存的时候记录日志,该怎么做呢?

暂且将记录日志操作就简单的变为在保存用户前输出一句话“save start...”,不建议直接在UserDAOImpl的save方法里写代码,因为我们有时候可能得不到源码,这个时候可以添加一个UserDAOImpl2继承UserDAOImpl,然后调用父类的save方法

package com.bjsxt.dao.impl;

import com.bjsxt.model.User;

public class UserDAOImpl2 extends UserDAOImpl {
@Override
public void save(User user) { System.out.println("save start...");
super.save(user); }
}

UserDAOImpl2

注:配置文件被注入到UserService的bean的class要改为UserDAOImpl2,下面也一样

看似实现了效果,但是这样很不灵活,因为只能继承一个类,而且父类发生变化,子类也必须跟着做出改变,我们可以再添加一个类继承UserDAOImpl,但是采用组合的方法

package com.bjsxt.dao.impl;

import com.bjsxt.aop.LogInterceptor;
import com.bjsxt.dao.UserDAO;
import com.bjsxt.model.User; public class UserDAOImpl3 implements UserDAO { private UserDAO userDAO = new UserDAOImpl(); public void save(User user) {
System.out.println("save start...");
/*new LogInterceptor().beforeMethod(null);*/
userDAO.save(user); } /*public void delete() {
// TODO Auto-generated method stub }*/
}

UserDAOImpl3

问题又来了,如果系统有500个需要被注入的bean,每个bean都有一些操作需要被记录日志,难道要组合500个bean来实现这个功能吗?

实际上可以给UserDAOImpl产生了一个代理,我们知道在代理模式中代理类除了可以调用目标对象的方法,也可以在方法前后加入自己的逻辑,在这里就是日志记录

JAVA基础知识:代理这篇文章中,介绍了Proxy类的静态方法newProxyInstance,这个方法需要三个参数,第一个是类装载器,第二个是目标对象的接口,第三个是InvocationHandler,这个最重要,定义了一个invoke方法,方法中可以调用目标对象的方法,也可以添加日志记录的逻辑代码

新建一个包com.bjsxt.aop,添加类LogInterceptor继承InvocationHandler

package com.bjsxt.dao.impl;

import com.bjsxt.dao.UserDAO;
import com.bjsxt.model.User; public class UserDAOImpl implements UserDAO { public void save(User user) { //Hibernate
//JDBC
//XML
//NetWork
System.out.println("user saved!");
} public void delete() {
System.out.println("user deteleted"); } }

UserDAOImpl

package com.bjsxt.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class LogInterceptor implements InvocationHandler {
private Object target; public Object getTarget() {
return target;
} public void setTarget(Object target) {
this.target = target;
} public void beforeMethod(Method m) { System.out.println(m.getName() + " start");
} public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
beforeMethod(m);
m.invoke(target, args);
return null;
}
}

InvocationHandler接口的实现类

    @Test
public void testProxy() {
UserDAO userDAO = new UserDAOImpl();
LogInterceptor li = new LogInterceptor();
li.setTarget(userDAO);
UserDAO userDAOProxy = (UserDAO)Proxy.newProxyInstance(userDAO.getClass().getClassLoader(), userDAO.getClass().getInterfaces(), li);
System.out.println(userDAOProxy.getClass());
userDAOProxy.delete();
userDAOProxy.save(new User()); }

单元测试

我们只需要写一次日志记录的代码,就完成delete和add用户的日志记录工作

尚学堂Spring视频教程(五):Spring AOP

项目结构如下:

尚学堂Spring视频教程(五):Spring AOP