什么是AOP?

什么是AOP? 转自https://blog.csdn.net/u010024991/article/details/53467034

这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。这些系统性的编程工作都可以独立编码实现,然后通过AOP技术切入进系统即可。从而达到了 将不同的关注点分离出来的效果。本文深入剖析Spring的AOP的原理。

  1. AOP相关的概念

1) Aspect :切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面;

2) Join point :连接点,也就是可以进行横向切入的位置;

3) Advice :通知,切面在某个连接点执行的操作(分为: Before advice , After returning advice , After throwing advice , After (finally) advice , Around advice );

4) Pointcut :切点,符合切点表达式的连接点,也就是真正被切入的地方;

  1. AOP 的实现原理

AOP分为静态AOP和动态AOP。静态AOP是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。动态AOP是指将切面代码进行动态织入实现的AOP。Spring的AOP为动态AOP,实现的技术为: JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术) 。尽管实现技术不一样,但 都是基于代理模式 , 都是生成一个代理对象 。
1) JDK动态代理

主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法。 JDK动态代理要求被代理实现一个接口,只有接口中的方法才能够被代理 。其方法是将被代理对象注入到一个中间对象,而中间对象实现InvocationHandler接口,在实现该接口时,可以在 被代理对象调用它的方法时,在调用的前后插入一些代码。而 Proxy.newProxyInstance() 能够利用中间对象来生产代理对象。插入的代码就是切面代码。所以使用JDK动态代理可以实现AOP。我们看个例子:

被代理对象实现的接口,只有接口中的方法才能够被代理:

public interface UserService {
    public void addUser(User user);
    public User getUser(int id);
}

被代理对象:

public class UserServiceImpl implements UserService {
    public void addUser(User user) {
        System.out.println("add user into database.");
    }
    public User getUser(int id) {
        User user = new User();
        user.setId(id);
        System.out.println("getUser from database.");
        return user;
    }
}

代理中间类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyUtil implements InvocationHandler {
private Object target; // 被代理的对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(“do sth before….”);
Object result = method.invoke(target, args);
System.out.println(“do sth after….”);
return result;
}
ProxyUtil(Object target){
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}

测试:

import java.lang.reflect.Proxy;
import net.aazj.pojo.User;
public class ProxyTest {
public static void main(String[] args){
Object proxyedObject = new UserServiceImpl(); // 被代理的对象
ProxyUtil proxyUtils = new ProxyUtil(proxyedObject);
// 生成代理对象,对被代理对象的这些接口进行代理:UserServiceImpl.class.getInterfaces()
UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
UserServiceImpl.class.getInterfaces(), proxyUtils);
proxyObject.getUser(1);
proxyObject.addUser(new User());
}
}

执行结果:

do sth before....
getUser from database.
do sth after....
do sth before....
add user into database.
do sth after....

我们看到在 UserService接口中的方法 addUser 和 getUser方法的前面插入了我们自己的代码。这就是JDK动态代理实现AOP的原理。

我们看到该方式有一个要求, 被代理的对象必须实现接口,而且只有接口中的方法才能被代理 。

2)CGLIB (code generate libary)

字节码生成技术实现AOP,其实就是继承被代理对象,然后Override需要被代理的方法,在覆盖该方法时,自然是可以插入我们自己的代码的。因为需要Override被代理对象的方法,所以自然CGLIB技术实现AOP时,就 必须要求需要被代理的方法不能是final方法,因为final方法不能被子类覆盖 。我们使用CGLIB实现上面的例子:

package net.aazj.aop;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CGProxy implements MethodInterceptor{
private Object target; // 被代理对象
public CGProxy(Object target){
this.target = target;
}
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
System.out.println(“do sth before….”);
Object result = proxy.invokeSuper(arg0, arg2);
System.out.println(“do sth after….”);
return result;
}
public Object getProxyObject() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass()); // 设置父类
// 设置回调
enhancer.setCallback(this); // 在调用父类方法时,回调 this.intercept()
// 创建代理对象
return enhancer.create();
}
}

public class CGProxyTest {
    public static void main(String[] args){
        Object proxyedObject = new UserServiceImpl();   // 被代理的对象
        CGProxy cgProxy = new CGProxy(proxyedObject);
        UserService proxyObject = (UserService) cgProxy.getProxyObject();
        proxyObject.getUser(1);
        proxyObject.addUser(new User());
    }
}

输出结果:

do sth before....
getUser from database.
do sth after....
do sth before....
add user into database.
do sth after....

我们看到达到了同样的效果。它的原理是生成一个父类 enhancer.setSuperclass( this.target.getClass()) 的子类 enhancer.create() ,然后对父类的方法进行拦截enhancer.setCallback( this) . 对父类的方法进行覆盖,所以父类方法不能是final的。

3) 接下来我们看下spring实现AOP的相关源码:

@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface()) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

从上面的源码我们可以看到:

if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);

如果被代理对象实现了接口,那么就使用JDK的动态代理技术,反之则使用CGLIB来实现AOP,所以 Spring默认是使用JDK的动态代理技术实现AOP的 。

JdkDynamicAopProxy的实现其实很简单:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug(“Creating JDK dynamic proxy: target source is ” + this.advised.getTargetSource());
}
Class

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值