首先声明,这篇文是看马士兵老师的Spring动态代理视频后写出来的。我是初学者,欢迎指正!
本示例模拟的是为一个接口的实现类的每个方法执行前和执行后各加一个日志。
Spring AOP就是使用了jdk的动态代理。动态代理的好处,即可以在一个类的某一个方法前后加如日志记录、权限管理和数据库事务管理等其他的业务,被代理的类本身不知道代理类,这样就可以使程序员专注于业务开发而不必去关心权限管理等非核心业务逻辑,同时由于核心业务代码与其他的非核心业务代码如日志记录等解耦,便于程序维护和修改,比如如果想除去一个日志记录的代码,直接修改代理处理类就好了,不必去修改核心业务处理代码等。
本文是以马士兵老师的设计模式视频之动态代理为基础,看过这个视频对理解springAOP很有帮助,他在讲课过程中也一直强调,虽然是2010年左右的视频,但是我感觉依然不过时,因为我除了马老师的视频,别的都看不下去,作为一个华东地区的北方人,别的老师要嘛是说话口音太不飘准,要嘛就是讲课死气沉沉,看着看着就看不下去了,谁有比马老师讲的更好的,求推荐,反正我还没遇到,个人浅见,如有冒犯,请见谅。。。
首先,第一步,通用接口
<pre name="code" class="java">package com.spring.dao;
import com.spring.model.*;
public interface Dao<T> {
public void save(T t);
public void delete(T t);
}
第二步:通用接口的实现类
package com.spring.impl;
import org.springframework.stereotype.Component;
import com.spring.dao.Dao;
import com.spring.model.User;
/**
* @Component 注解方式:
* 1.bean.xml中加这一个元素 <context:component-scan base-package="com.spring"></context:component-scan>
* 2.将bean.xml不需要再配置相关的bean元素
* 3.在实际要被 依赖注入 到别的类中的那个对象的类类声明上加注解:@Component ,即:添加@Component的类会被实例化并放到容器中,然后注入到对应的类中
* 4.在使用依赖注入对象的类对应这个要被注入对象的setter方法上加注解@Resource注解
* @Component("userDaoImpl") :括号中表示这个被spring实例化的对象的名字
*/
@Component
public class UserDaoImpl implements Dao<User>{
@Override
public void save(User u) {
System.out.println("A User is saved!");
}
<pre name="code" class="java"> @Override
public void delete(User t) {
// TODO Auto-generated method stub
System.out.println("A User is deleted!");
}
} 第三步:编写被代理对象的处理类
package com.spring.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import com.spring.dao.Dao;
import com.spring.impl.UserDaoImpl;
public class LogInvocationHandler implements InvocationHandler {
//target表示被代理对象,因为可以接收不同的被代理对象,所以是Object类型
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//系统会利用反射,获取被代理类的被执行的那个方法,即参数中的这个method
System.out.println("记录日志:" + target.getClass().getName() + "类的" + method.getName() + "方法开始执行了");
method.invoke(target, args);
System.out.println("记录日志:" + target.getClass().getName() + "类的" + method.getName() + "执行完毕");
return null;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
第四步:测试
import java.lang.reflect.Proxy;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.spring.dao.Dao;
import com.spring.impl.UserDaoImpl;
import com.spring.model.Cat;
import com.spring.model.User;
import com.spring.proxy.LogInvocationHandler;
import com.spring.service.CatService;
import com.spring.service.UserService;
public class TestUserService {
private ApplicationContext cx = new ClassPathXmlApplicationContext("beans.xml");
/**
* 搞清楚动态代理的原理 :这里的通用接口以Dao为例
* 第一:有一个通用的接口 Dao接口
* 第二:Dao接口有许多的实现类,UserDaoImpl或CatDaoImpl等等
* 第三:需要实现的通用业务处理类Handler要实现InvocationHandler接口,并实现其invoke方法;
* 其中,需要将一个Object类型的对象作为InvocationHandler实现类的成员变量,并设置相关的setter和getter方法,
* 这个Object类型的对象即代表实际被代理接口类型的实现类
* 第四:动态代理制造类出场:Proxy类的newProxyInstance方法,通过接收被代理对象实现的接口和InvocationHandler代理类作为参数,
* 动态的返回一个代理类对象,这个代理对象通过反射,获取通用接口的方法名,作为自己的方法名,
* 接口有什么方法,动态代理对象就有什么方法,而动态代理对象执行对应的方法时,是将自己,还有被代理对象对应的相同的方法,
* 还有被代理对象方法的参数一同传给InvocationHandler实现类,让实现类去执行invoke方法,这一点可以从实现类的方法的形参看出
* <span style="font-family:SimSun;">public Object invoke(Object proxy, Method method, Object[] args)</span>
*
*/
@Test
public void testProxy() throws NoSuchMethodException, SecurityException, Throwable{
Dao userDaoImpl = new UserDaoImpl();
LogInvocationHandler li = new LogInvocationHandler();
li.setTarget(userDaoImpl);
//动态地返回一个和被代理对象实现了同样接口的代理类对象,所以这个代理对象可以是Dao类型的
Dao daoProxy = (Dao)Proxy.newProxyInstance(this.getClass().getClassLoader(),
userDaoImpl.getClass().getInterfaces(),
li);
//调用代理对象的save方法,相当于调用LogInvocationHandler类的invoke方法,
//而这个invoke方法,是加了日志逻辑,同时又调用了被代理对象的save方法
daoProxy.save(new User());
daoProxy.delete(new User());
}
}
欢迎指正!