动态代理的概念
动态代理是程序在运行过程中自动创建一个代理对象来代替被代理的对象去执行相应的操作,例如, 我们有一个已经投入运行的项目中有一个用户DAO类UserDao用来对User对象进行数据库的增删改查操作,但是有一天,要求在对用户的增删改查操作时记录相应的日志,这是怎么办呢?难道我们去直接修改UserDao的源代码,然后在UserDao的每个方法中加入日志记录功能,这显然是不合理的,它违背了java的OCP原则,即对修改关闭对扩张开放。比如改现有的代码如下:
接口类
public interface IUserDao {
public void add(User user);
public User load(int id);
public void delete(int id);
public void update(User user);
}
public class UserDao implements IUserDao {
public void add(User user) {
System.out.println("user added:" + user);
}
public User load(int id) {
System.out.println("load user, id=" + id);
return null;
}
public void delete(int id) {
System.out.println("deleted user, id=" + id);
}
@Override
public void update(User user) {
System.out.println("updated user:" + user);
}
}
public interface IUserService {
public void add(User user);
public void delete(int id);
public User load(int id);
public void update(User user);
}
public class UserService implements IUserService {
IUserDao userDao;
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
public void add(User user) {
userDao.add(user);
}
public void delete(int id) {
userDao.delete(id);
}
public User load(int id) {
return userDao.load(id);
}
@Override
public void update(User user) {
userDao.update(user);
}
}
public class Logger {
public static void log(String info) {
System.out.println(info);
}
}
// 1、写一个类继承InvocationHandler
public class LogProxy implements InvocationHandler {
// 2、创建被代理对象
private Object target;
// 3、创建代理对象,参数是要被代理的对象,返回值是代理对象
public static Object getInstance(Object o) {
LogProxy proxy = new LogProxy();
proxy.target = o;
Object result = Proxy.newProxyInstance(o.getClass().getClassLoader(),
o.getClass().getInterfaces(), proxy);
return result;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.isAnnotationPresent(LogInfo.class)) { // 检查该方法上是否有LogInf注解
LogInfo li = method.getAnnotation(LogInfo.class); // 取得注解
Logger.log(new Date().toString() + " ---> " + li.value()); // 取得直接的值,输出日志
}
Object o = method.invoke(target, args);
return o;
}
}
自定义Annotation如下:
@Retention(RetentionPolicy.RUNTIME)
public @interface LogInfo {
public String value() default "";
}
public interface IUserDao {
@LogInfo("Add a user")
public void add(User user);
public User load(int id);
@LogInfo("Delete a user")
public void delete(int id);
@LogInfo("Update a user")
public void update(User user);
}
上面只有被加上了@LogInfo注解的方法才会记录日志,如add,delete,update,而load则不会记录日志,所以利用注解可以灵活的控制想要在哪些方法上记录日志
使用
public static void main(String[] args) {
IUserDao userDao = new UserDao();
IUserDao userDaoProxy = (IUserDao) LogProxy.getInstance(userDao);
UserService userService = new UserService();
userService.setUserDao(userDaoProxy);
userService.add(new User());
}