在第一节中,我们自己模拟了一个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); } }
注:配置文件被注入到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 }*/ }
问题又来了,如果系统有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"); } }
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; } }
@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用户的日志记录工作
项目结构如下: