目标:生成一个UserDao的代理,然后在代理执行的过程中,把关于日志记录的逻辑加进去。
UserDao.java:
package cn.edu.hpu.dao;
import cn.edu.hpu.model.User;
public interface UserDao {
public void save(User u);
}
UserDaoImpl.java:
package cn.edu.hpu.dao.Impl;
import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;
public class UserDaoImpl implements UserDao{
public void save(User u) {
System.out.println("add success!!");
}
}
现在需要要在执行UserDaoImpl的save方法之前和之后执行LogIntercept类的beforeMethod与afterMethod方法赖创建日志。
首先让LogIntercept类实现动态代理的接口InvocationHandler
然后重写invoke方法。
package cn.edu.hpu.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//日志的拦截器
public class LogIntercept implements InvocationHandler{
private Object terget;
public void beforeMethod(){
System.out.println("save start...");
}
public void afterMethod(){
System.out.println("save end...");
}
public void setTarget(Object terget) {
this.terget=terget;
}
//无论调用代理对象的哪个方法,invoke方法都要被调用
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
beforeMethod();
m.invoke(terget, args);
afterMethod();
return null;
}
}
测试方法:
@Test
public void testProxy(){
//产生一个被代理的对象
UserDao userDao=new UserDaoImpl();
LogIntercept li=new LogIntercept();
li.setTarget(userDao);
UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
new Class[]{UserDao.class},
li);
userDaoProxy.save(new User());
}
测试结果:
save start...
add success!!
save end...
当你有一件事情,需要在多个方法上面都要加相同的业务逻辑的时候,用动态代理会帮你省好多事情。
剖析一下动态代理的实现:
UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
new Class[]{UserDao.class},
li);
得到的是UserDao接口吗?测试一下:
System.out.println(userDaoProxy.getClass());
得到:class $Proxy4
说明userDaoProxy并不是UserDao接口,而是JDK的一个动态代理类$Proxy4,它是根据你传给它的参数中的接口那一项,得到接口本身,继而拿到接口的所有方法,哪个方法调用都会把接口本身传给invocationHandlder(li的invoke方法拿到)。
好了,我们写完了动态代理的AOP,但是,到底什么是AOP(Aspect-Oriented-Programming)面向切面编程呢?
你会发现,我们正常的程序写起来就是一条直线,比如:
接受用户名密码--->连接数据库--->创建XXX对象--->存储--->创建日志--->END
而面向切面编程就好像我们在中间"咔嚓"砍掉一刀,在上面加上逻辑,在另外一个地方"咔嚓"一刀,也在上面加逻辑,而方法自己本身并不知道。
面向切面编程是面向对象编程的有一伟大思想。应用的地方太多,权限、日志、检查、事务(可以在CRUD方法前加上Transiention.start(),事务结束后加上Transiention.commit())、异常的管理(单独写一个业务逻辑,想给哪个方法加异常管理就加)。
比如权限,再给每个JSP文件设权限的时候,在每个JSP文件开始的时候要做个检查,正常的写JSP文件或方法,我们的业务逻辑才是我们的主要内容,但是由于要检查权限,非要在前面加上检查权限的代码,代码会很繁琐,不会专注于特定的内容了,而且去掉它的时候会很麻烦。
但是利用面向切面的编程,就不会产生这些问题,而且这些加上的业务逻辑可以利用Spring的配置文件轻松的去掉或者加上。
现在是实现了InvocationHandler,如果利用Spring,不用实现InvocationHandler{接口也可以,它就是使用了直接生成二进制码,用继承。
Sturts2中的拦截器使用的就是AOP切面编程
顺便说一下,一般写程序推荐面向接口编程。