最近在看Spring的一些知识,看到AOP就想总结下有关 增强一个方法的知识。
- 我在这里介绍4种方式:
- 继承
- 装饰设计模式
- JDK的动态代理
- CGlib
- 继承
- 用法。
继承你要增强的方法的类,重写你要增强的方法,在方法体内进行增强。
创建增强后的对象调用方法。 - 缺点。
假如我们想要增强的方法是一个接口里面的。
比如Servlet里的HttpServletRequest里的getParameter(String name)让它具有过滤敏感字符的功能,但是你发现HttpServletRequest是一个接口。 - 示例。
- 用法。
> 需求在保存用户前后开启事务和提交事务
> UserServiceImpl
public class UserServiceImpl implements IUserService{
//持有dao对象
private IUserDao userDao;
//保存一个用户
public void save(User user){
userDao.save(user);
}
}
public class MyUserServiceImpl extends UserServiceImpl{
//重写save()方法
public void save(User user){
//开始增强
syso("开启事务...");
super.save(user);
syso("提交事务...");
}
}
- 装饰设计模式
- 用法。
- 条件。
- 与目标类实现同一个接口。
- 增强类中要有被增强类的引用。
- 在增强类中添加增强的方法。
- 使用。
- 创建增强类的对象,传入目标类的对象。
- 用增强类来调用方法 。
- 条件。
- 缺点。
- 假如接口中方法太多,都需要实现。
- 虽然适配器模式能解决,但是一般没人用。
- 示例。
- 用法。
public class MyUserServiceImpl implements IUserService{
private IUserService userService;
//持有被增强类
public MyUserServiceImpl(IUserService userService){
this.userService=userService;
}
public viod save(User user){
//开始增强
syso("开启事务..");
userService.save();
syso("提交事务..");
}
}
JDK提供的动态代理
- 条件。
- 需要目标类实现接口。
- 用法。
- 通过Proxy.newInstance(参数后介绍)来动态生成增强的对象。
- 运用生成的对象来调用方法。
- 关系。
- 代理类和目标类是兄弟关系。(实现了相同的接口)
- 示例。
- 条件。
@Test
public void testDynamic(){
/*
* 为什么局部内部类调用的局部变量要加final修饰
* 因为局部内部类要编译成一份独立的class文件,
* 当前线程运行结束时局部变量要被垃圾回收器回收,但是内部类还需要这个变量,
* 所以将局部变量加final修饰变成常量。
*/
final IUser user = new User();
//返回值.增强后的对象
IUser newUser = (IUser)Proxy.newProxyInstance(
//参数1.与目标类相同的类加载器
//只有相同的类加载器加载的类才能互相调用
user.getClass().getClassLoader(),
//参数2.目标类实现的所有接口
//也就是获得目标类上的方法
user.getClass().getInterfaces(),
//参数3.InvocationHandler实现类
new InvocationHandler() {
@Override
public Object invoke(
//参数1.代理出来的对象 一般不用关心
Object proxy,
//参数2.正在代理的方法
Method method,
//参数3.正在代理的方法需要的参数
Object[] args) throws Throwable {
//省略if("save".equals(method.getName()))只增强save方法
//开始增强
System.out.println("开启事务..");
//接收原方法的返回值
//用原对象调用原方法,学习反射知识时用过
Object invoke = method.invoke(user, args);
System.out.println("关闭事务..");
//将接受的返回值返回
return invoke;
}
});
//用代理出来的对象来调用方法,反编译可知实质调用的invoke方法。
newUser.save();
}
CGlib提供的动态代理
- 条件。
- 只需要目标类,且不被final修饰的类。
- 用法。
- 导包(Spring项目不需要)。
- 通过Enhance.creat( 参数后介绍 )来获取增强的对象
- 用增强的对象来调用方法。
- 关系。
- 代理类和目标类是父子关系。
- 示例。
- 条件。
@Test
public void testDynamic(){
//加final修饰
final User user=new User();
User newUser = (User)Enhancer.create(
//参数1.目标类字节码文件
//根据此参数可以获得相同的类加载器和方法
user.getClass(),
//参数2.局部内部类
new MethodInterceptor() {
@Override
public Object intercept(
//参数1.代理对象类型 忽略
Object arg0,
//正在代理的方法
Method method,
//正在代理的方法需要的参数
Object[] args,
//方法的代理 一般不用
MethodProxy arg3) throws Throwable {
//省略if("save".equals(method.getName()))只增强save方法
//开始增强
System.out.println("开启事务");
//接收原方法的返回值
//用原对象调用原方法,学习反射知识时用过
Object invoke = method.invoke(user, args);
System.out.println("关闭事务");
//返回接收的返回值
return invoke;
}
});
newUser.save();
}