java动态代理详解,并用动态代理和注解实现日志记录功能

动态代理的概念

动态代理是程序在运行过程中自动创建一个代理对象来代替被代理的对象去执行相应的操作,例如, 我们有一个已经投入运行的项目中有一个用户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);
	}

}

有一天我们现在UserDao的每个方法执行前记录日志,我们定义了一个Logger类来专门输出日志

public class Logger {
	public static void log(String info) {
		System.out.println(info);
	}

}

还实现了一个代理类:

// 1、写一个类继承InvocationHandler
public class LogProxy implements InvocationHandler {
	// 3、创建被代理对象
	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 "";
}

一切完了之后,此时如果我们想在对应的方法执行前记录日志,我们只需要在IUserDao的想加入日志的方法手使用LogInfo注解后,在方法执行时就会自动加入日志记录功能了,如下:

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());
	}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2025-01-07 01:43:06

java动态代理详解,并用动态代理和注解实现日志记录功能的相关文章

MyBatis的动态SQL详解

MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑,本文详解mybatis的动态sql,需要的朋友可以参考下 MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力.如果你有使用 JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空 格或在列表的最后省略逗号.动态 SQL 可以彻底处理这种痛苦. 通常使用动态SQL不可能是独立的一部分,MyBatis当然使用一种强大的动态SQL语言来改进这种

Qt on Android: Qt Quick 组件与对象动态创建详解

在<Qt on Android: Qt Quick 事件处理之信号与槽>一文中介绍自定义信号时,举了一个简单的例子,定义了一个颜色选择组件,当用户在组建内点击鼠标时,该组件会发出一个携带颜色值的信号,当时我使用 Connections 对象连接到组件的 colorPicked 信号,改变文本的颜色. 当时用到的 Component . Loader 两个特性,一直没来得及介绍,可能很多人都还在雾里看花呢.这次呢,我们就来仔仔细细地把他们讲清楚. 请给我的参赛文章<Qt on Androi

Oracle中动态SQL详解(EXECUTE IMMEDIATE)

Oracle中动态SQL详解(EXECUTE IMMEDIATE) 2017年05月02日 18:35:48 悠悠倾我心 阅读数:744 标签: oracle动态sqloracle 更多 个人分类: 数据库 Oracle中动态SQL详解 1.静态SQLSQL与动态SQL Oracle编译PL/SQL程序块分为两个种:其一为前期联编(early binding),即SQL语句在程序编译期间就已经确定,大多数的编译情况属于这种类型:另外一种是后期联编(late binding),即SQL语句只有在运

1.7动态输入详解

1.7动态输入详解 动态输入在绘图区域中的光标附近提供命令界面就像它在命令行中所做的一样. 区别是用户的注意力可以保持在光标附近. 动态输入不会取代命令窗口. 控制动态输入设置: 动态输入有三个组件:光标(指针)输入.标注输入.动态提示 注:按下F12键可以临时关闭动态输入 原文地址:https://www.cnblogs.com/chenxi188/p/10921841.html

Java反射机制详解

Java反射机制详解 Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制. 1.关于Class 1.Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性    2.对象照镜子后(反射)可以得到的信息:某个类的数据成员名.方法和构造器.某个类到底实现

Java线程池详解(二)

一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉得还有太多内容需要补充,每次都是修修补补,总觉得还缺点什么.在第一篇中,我着重描述了java线程池的原理以及它的实现,主要的点在于它是如何工作的.而本文的内容将更为上层,重点在于如何应用java线程池,算是对第一篇文章的一点补充,这样对于java线程池的学习和总结稍微完整一些. 使用过java线程池

【夯实基础】java关键字synchronized 详解

尊重版权:http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行.另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块. 二.然而,当一个线程访问object的一个sy

java异常处理机制详解

java异常处理机制详解 程序很难做到完美,不免有各种各样的异常.比如程序本身有bug,比如程序打印时打印机没有纸了,比如内存不足.为了解决这些异常,我们需要知道异常发生的原因.对于一些常见的异常,我们还可以提供一定的应对预案.C语言中的异常处理是简单的通过函数返回值来实现的,但返回值代表的含义往往是由惯例决定的.程序员需要查询大量的资料,才可能找到一个模糊的原因.面向对象语言,比如C++, Java, Python往往有更加复杂的异常处理机制.这里讨论Java中的异常处理机制. 异常处理 Ja

java移位运算符详解[转]

java移位运算符不外乎就这三种:<<(左移).>>(带符号右移)和>>>(无符号右移). 1. 左移运算符 左移运算符<<使指定值的所有位都左移规定的次数. 1)它的通用格式如下所示: value << num num 指定要移位值value 移动的位数. 左移的规则只记住一点:丢弃最高位,0补最低位 如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模.如对int型移动33位,实际上只移动了332=1位. 2)运算规则 按