Spring AOP原理及动态代理

这篇文章主要介绍了Spring AOP原理及动态代理,文章通过围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

一、什么是代理?

指为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上,增强目标对象的业务逻辑.

1、静态代理

静态代理的特点是, 为每一个业务增强都提供一个代理类, 由代理类来创建代理对象. 下面我们通过静态代理来实现对转账业务进行身份验证.

(1) 转账业务

public interface IAccountService {
    //主业务逻辑: 转账
    void transfer();
}

public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 代理类

public class AccountProxy implements IAccountService {
    //目标对象
    private IAccountService target;
    public AccountProxy(IAccountService target) {
        this.target = target;
    }
    /**
     * 代理方法,实现对目标方法的功能增强
     */
    @Override
    public void transfer() {
        before();
        target.transfer();
    }
    /**
     * 前置增强
     */
    private void before() {
        System.out.println("对转账人身份进行验证.");
    }
}

(3) 测试

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        IAccountService target = new AccountServiceImpl();
        //创建代理对象
        AccountProxy proxy = new AccountProxy(target);
        proxy.transfer();
    }
}

结果: 对转账人身份进行验证.调用dao层,完成转账主业务.

2、动态代理

静态代理会为每一个业务增强都提供一个代理类, 由代理类来创建代理对象, 而动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成.

2.1、JDK动态代理

JDK动态代理是使用 java.lang.reflect 包下的代理类来实现. JDK动态代理动态代理必须要有接口.

(1) 转账业务

public interface IAccountService {
    //主业务逻辑: 转账
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 增强

因为这里没有配置切入点, 称为切面会有点奇怪, 所以称为增强.

public class AccountAdvice implements InvocationHandler {
    //目标对象
    private IAccountService target;

    public AccountAdvice(IAccountService target) {
        this.target = target;
    }

    /**
     * 代理方法, 每次调用目标方法时都会进到这里
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        return method.invoke(target, args);
    }

    /**
     * 前置增强
     */
    private void before() {
        System.out.println("对转账人身份进行验证.");
    }
}

(3) 测试

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        IAccountService target = new AccountServiceImpl();
        //创建代理对象
        IAccountService proxy = (IAccountService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new AccountAdvice(target)
        );
        proxy.transfer();
    }
}

结果: 对转账人身份进行验证.调用dao层,完成转账主业务.

2.2、 CGLIB动态代理

JDK动态代理必须要有接口, 但如果要代理一个没有接口的类该怎么办呢? 这时我们可以使用CGLIB动态代理. CGLIB动态代理的原理是生成目标类的子类, 这个子类对象就是代理对象, 代理对象是被增强过的.

注意: 不管有没有接口都可以使用CGLIB动态代理, 而不是只有在无接口的情况下才能使用

(1) 转账业务

public class AccountService {
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 增强

因为这里没有配置切入点, 称为切面会有点奇怪, 所以称为增强.

public class AccountAdvice implements MethodInterceptor {
    /**
     * 代理方法, 每次调用目标方法时都会进到这里
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        before();
        return methodProxy.invokeSuper(obj, args);
        //        return method.invoke(obj, args);  这种也行
    }

    /**
     * 前置增强
     */
    private void before() {
        System.out.println("对转账人身份进行验证.");
    }
}

(3) 测试

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        AccountService target = new AccountService();
        //
        //创建代理对象
        AccountService proxy = (AccountService) Enhancer.create(target.getClass(),
                new AccountAdvice());
        proxy.transfer();
    }
}

结果: 对转账人身份进行验证.调用dao层,完成转账主业务.

二、模拟Spring AOP场景

了解了动态代理后, 我们就可以自己来实现Spring AOP功能了, 所以下面我们来模拟下Spring AOP场景.

(1) 转账业务

public interface IAccountService {
    //主业务逻辑: 转账
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 切面抽象类

定义一个切面抽象类, 该类使用了模板方法的设计模式, 为开始, 结束, 异常, 前置增强, 后置增强提供了默认实现, 当我们定义切面类时只需要按需重写它们就行. isIntercept() 方法用来判断切入点是否正确, 切面类需要重写这个方法.

public abstract class BaseAspect implements MethodInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(BaseAspect.class);

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = null;

        begin();
        try {
            if (isIntercept(method, args)) {
                before();
                result = methodProxy.invokeSuper(obj, args);
                after();
            } else {
                result = methodProxy.invokeSuper(obj,args);
            }
        } catch (Exception e) {
            logger.error("proxy failure", e);
            error(e);
            throw e;
        } finally {
            end();
        }
        return result;
    }
    /**
     * 开始增强
     */
    public void begin() {
    }
    /**
     * 切入点判断
     */
    public boolean isIntercept(Method method, Object[] args) throws Throwable {
        return true;
    }
    /**
     * 前置增强
     */
    public void before() throws Throwable {
    }

    /**
     * 后置增强
     */
    public void after() throws Throwable {
    }

    /**
     * 异常增强
     */
    public void error(Throwable e) {
    }

    /**
     * 最终增强
     */
    public void end() {
    }
}

(3) 切面类

创建一个切面类, 类中配置切入点和增强.

public class AccountAspect extends BaseAspect {
    /**
     * 切入点
     */
    public boolean isIntercept(Method method, Object[] args) throws Throwable {
        return method.getName().equals("transfer");
    }

    /**
     * 前置增强
     */
    public void before() throws Throwable {
        System.out.println("对转账人身份进行验证.");
    }
}

(4) 代理工厂类

定义一个工厂类来创建代理, 其实不创建这个类也行, 但为了模仿Spring还是创建了. @SuppressWarnings是为了抑制警告, 就是编译器上面的黄线.

public class ProxyFactory {

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(final Class<?> targetClass, final MethodInterceptor methodInterceptor) {
        return (T) Enhancer.create(targetClass,methodInterceptor);
    }
}

(5) 测试

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        IAccountService target = new AccountServiceImpl();
        //切面
        BaseAspect accountAspect = new AccountAspect();
        //创建代理对象
        IAccountService proxy = (IAccountService) ProxyFactory.createProxy(target.getClass(), accountAspect);
        proxy.transfer();
    }
}

结果:对转账人身份进行验证.调用dao层,完成转账主业务.

到此这篇关于Spring AOP原理及动态代理的文章就介绍到这了,更多相关Spring AOP 内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!

本文标题为:Spring AOP原理及动态代理

基础教程推荐