理解AOP概念,学会AOP的使用
AOP思想
AOP(Aspect Orient Programming),面向切面编程,作为面向对象(OOP)的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、异常处理等。为了将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,Spring 提供了面向切面的编程方式,形成一个独立的切面,AOP可以将这部分独立的代码动态的切入到指定方法、指定位置中。
AOP原理
AOP可以在编译时候将代码织入到指定的方法或者属性上,或者在运行的时候动态的将代码切入到指定的方法或者属性中,AOP实现的关键就是创建AOP代理,代理有静态代理和动态代理之分,其中aspectj为静态代理在编译器植入,Spring AOP是动态代理在运行期植入。
AOP概念
- Aspect (切面): 一个关注点的模块化,这个关注点可能会横切多个对象,切面由切入点和通知组成。
- Adivice(通知/增强): 想要在目标方法出增加的功能或操作,包括前置、后置、环绕等通知类型,很多时候就是所说的拦截器。
- Pointcut(切入点):目标对象中已经增强的方法,就是说在连接点的基础上选中想要的方法,使用通知。
- JointPoint (连接点):在目标对象中所有可以增强的方法,Spring中允许你通知的地方,Spring只支持方法连接点,和方法有关的前前后后都是连接点。
- Target(目标):被一个或者多个切面增强的对象,也就是被通知的对象,应为Spring AOP是通过动态代理实现的,所以这个对象也是被代理的对象。
- Proxy(代理):将通知织入到目标对象之后,形成了代理对象。
- Weaving(织入):将切面连接到切点,并形成一个代理对象的过程。
代理实例
静态代理模式
public class StaticProxy implements IUserDao {
private IUserDao iUserDao;
public StaticProxy(IUserDao iUserDao){
this.iUserDao = iUserDao;
}
@Override
public void save() {
System.out.println("---------------开启事务-------------------");
iUserDao.save();
System.out.println("------------------提交事务--------------------");
}
public static void main(String[] args) {
//目标对象
IUserDao target = new UserDao();
//静态代理
StaticProxy proxy = new StaticProxy(target);
proxy.save();
}
}
interface IUserDao{
public void save();
}
class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("----------------保存数据-----------------");
}
}
关于静态代理的优缺点:
-
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
-
缺点:
冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
动态代理模式
public class DynamicProxy {
//维护一个目标对象
private Object target;
public DynamicProxy(Object target){
this.target = target;
}
// 为目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
// 执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务");
return returnValue;
}
});
}
public static void main(String[] args) {
IUserDao2 userDao2 = new UserDao2();
System.out.println(userDao2.getClass());
IUserDao2 proxy = (IUserDao2) new DynamicProxy(userDao2).getProxyInstance();
System.out.println(proxy.getClass());
proxy.save();
}
}
interface IUserDao2{
void save();
}
class UserDao2 implements IUserDao2{
@Override
public void save() {
System.out.println("-----------数据保存-----------------");
}
}
动态代理的特点:
动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。
动态代理和静态代理的区别
- 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
- 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中