Spring AOP代理模式
在OOP的对象中,做日志记录,权限校验等如果都让面向对象来做,会产生大量冗余代码。所以引入切面去织入相关逻辑。面向切面编程使用代理模式
一、代理模式
官方定义:为其他对象提供一种代理以控制对这个对象的访问。
代理类是在之前类的基础上做了一层封装。java中有静态代理、JDK动态代理、CGLib代理。
静态代理指的是代理类是在编译期就存在的,动态代理指代理类在运行期动态生成。
二、静态代理
程序在运行之前就已经知道代理类和被代理类的关系。
- 定义一个公共接口
- 定义被代理类
- 定义代理类
1.定义一个公共接口
public interface IUserDao {
void save();
}
2.定义被代理类
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println(" 保存用户 ");
}
}
3.定义代理类
public class UserDaoProxy implements IUserDao{
private UserDao userDao = new UserDao();
@Override
public void save() {
System.out.println("代理操作,开启事务");
userDao.save();
System.out.println("代理操作,关闭事务");
}
public static void main(String[] args) {
IUserDao userDao = new UserDaoProxy();
userDao.save();
}
}
三、JDK动态代理
改动静态代理代码使之成为JDK动态代理
- 实现InvocationHandler
- 重写invoke方法
- 建立代理对象和真是对象的代理关系调用Proxy.newProxyInstance()
/**
* jdk动态代理
*/
public class DynamicProxy implements InvocationHandler {
//被代理类实例
private Object target = null;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始JDK动态代理");
Object result = method.invoke(target, args);
System.out.println("结束JDK动态代理");
return result;
}
/**
* 建立代理对象和真实对象的代理关系,并返回代理对象
* @param target
* @return
*/
public Object bind(Object target){
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy();
IUserDao iUserDao = (IUserDao) dynamicProxy.bind(new UserDao());
iUserDao.save();
}
}
从上面代码看出:代理类是由Proxy.newProxyInstance
方法动态生成的,生成对象后使用“iUserDao.save()”的方式进行方法调用,代理类的被代理类的关系只有在执行这行代码的时候才会生成,因此成为动态代理。
但是必须要使UserDao实现IUserDao才能使用JDK动态代理(从newProxyInstance方法的第二个参数可得知,必须传入被代理类的实现接口),如果没有实现需要CGLib动态代理。
四、CGLib动态代理
CGLib动态代理使用继承被代理类,使用其子类的方式弥补了代理类没有接口的不足。
使用CGLib需要引入第三方类库:
- 导入cglib和asm jar包
- 实现MethodInterceptor的intercept方法实现代理逻辑
- 使用Enhancer创建出代理类
public class MyMethodIntercaptor implements MethodInterceptor {
/**
* 代理逻辑方法
* @param o 代理对象
* @param method 方法
* @param objects 方法参数
* @param methodProxy 方法代理
* @return 代理逻辑返回
* @throws Throwable 异常
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开始CGLib动态代理");
Object object = methodProxy.invokeSuper(o,objects);
System.out.println("结束CGLib动态代理");
return object;
}
/**
* 生成CGLib代理对象
* @param cls 被代理类
* @return
*/
public Object getProxy(Class cls){
//CGLib enhancer增强类对象
Enhancer enhancer = new Enhancer();
//设置增强类型
enhancer.setSuperclass(cls);
//定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
enhancer.setCallback(this);
//生成并返回代理对象
return enhancer.create();
}
public static void main(String[] args) {
MyMethodIntercaptor myMethodIntercaptor = new MyMethodIntercaptor();
UserDao userDao = (UserDao) myMethodIntercaptor.getProxy(UserDao.class);
userDao.save();
}
}
从上面代码看出:使用Enhancer生成代理类,需要设置被代理类,也就是父类
五、总结
静态代理维护成本比较高,需要一个被代理类和代理类,而且需要实现相同接口。
JDK动态代理需要实现InvocationHandler.invoke()方法去创建代理逻辑。使用Proxy.newProxyInstance建立代理逻辑关系
CGLib动态代理需要引入包并实现MethodInterceptor.intercept()方法去创建代理逻辑。使用Enhancer去建立代理逻辑关系。
JDK动态代理和CGLib动态代理区别:
JDK需要被代理类实现接口。CGLib则是生成被代理类的子类,要求被代理类不能使用final,因为final不能被继承。