java超快速入门(五):面向切面编程(AOP)

AOP原理

AOP是使用代理模式,在指定方法执行前、执行后等各个阶段进行自定义处理
下面所讲到的代理模式中,一共有两种类型,一种是基于接口的代理,一种是基于继承的代理

  • 基于接口的代理模式,需要指定目标类的接口,java类加载器将根据给定的接口生成一个动态代理类,并且在执行目标方法时,执行指定方法(如invoke方法)。由于动态代理类是由接口生成,因此如果通过代理类执行接口不存在但目标类实现了的方法,将会编译不通过,报错
  • 基于继承的代理模式,目标类不需要有接口,类加载器将动态生成继承自目标类的代理类,因此代理类拥有目标类的所有方法,cglib就是基于这种模式

AOP术语

  • target目标类:需要被代理的类。例如:UserService
  • Joinpoint连接点:所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
  • PointCut切入点:已经被增强的连接点。例如:addUser()
  • advice通知/增强,增强代码。例如:after、before
  • Weaving织入:是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
  • proxy代理类
  • Aspect切面:是切入点pointcut和通知advice的结合

实现AOP模式

手动代理模式

使用Proxy.newProxyInstance()方法进行代理

Target target = new Target()
TargetInterface proxy = (TargetInterface)Proxy.newProxyInstance(Target.class.getClassLoader(), new Class[]{TargetInterface.class}, new InvocationHandler() {
    public Object before(Object proxy, Method method, Object[] args){
        System.out.println("执行" + method.getName() + "
");
        return proxy;
    }
    public Object after(Object proxy, Method method, Object[] args, Object result){
        System.out.println("执行结果" + result.toString() + "
");
        return result;
    }
    public Object exceptionHandler(Object proxy, Method method, Object[] args){
        return null;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try{
            proxy = this.before(proxy, method, args);
            Object result = method.invoke(target, args);
            result = this.after(proxy, method, args, result);
            return result;
        }catch (Throwable throwable){
            return this.exceptionHandler(proxy, method, args)
        }
    }
});

cglib

cglib通过动态创建目标类的子类作为代理类来实现代理模式,cglib代理的好处是,目标类不需要实现任何接口,由于代理类是继承自目标类,因此有目标类的所有方法

先定义一个切面类

/** MyAspect.java **/
package com.sinbxeunha.josechan.aspect;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyAspect {
    public Object before(Object proxy, Method method, Object[] args, MethodProxy methodProxy){
        System.out.println("执行" + method.getName() + "
");
        return null;
    }

    public Object after(Object proxy, Method method, Object[] args, MethodProxy methodProxy, Object result){
        assert result != null && result.toString() != null;
        System.out.println("执行结果:" + result.toString() + "
");
        return result;
    }

    public Object exceptionHandler(Object proxy, Method method, Object[] args, MethodProxy methodProxy, Throwable throwable){
        System.out.println("结果异常:" + throwable.getMessage() + "
");
        return null;
    }
}

使用cglib进行代理

//cglib代理
Target target = new Target();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
MyAspect aspect = new MyAspect();
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        try{
            //前方法
            proxy = aspect.before(proxy, method, args, methodProxy);
            //下面两句是一样的
            Object result = method.invoke(target, args);
//                    Object result = Proxy.invokeSuper(target, args);
            //后方法
            return result = aspect.after(proxy, method, args, methodProxy, result);
        }catch (Throwable throwable){
            return aspect.exceptionHandler(proxy, method, args, methodProxy, throwable);
        }
    }
});

aopalliance

aop联盟定义了一系列规范,实现这些接口就能实现切面编程
同样的我们实现切面类

/*** MyAspect.java ***/
package com.sinbxeunha.josechan.aspect;

import org.aopalliance.intercept.MethodInvocation;

public class MyAspect {
    public Object before(MethodInvocation invocation){
        System.out.println("执行" + invocation.getMethod().getName() + "
");
        return null;
    }

    public Object after(Object result){
        System.out.println("执行结果:" + result.toString() + "
");
        return result;
    }

    public Object exceptionHandler(Throwable throwable){
        System.out.println("结果异常:" + throwable.getMessage() + "
");
        return null;
    }
}

定义通知,实现Advice接口,这里我们实现MethodInterceptor接口,该接口底层继承自Advice接口

/**MyInterceptor.java**/
package com.sinbxeunha.josechan.aspect;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;

public class MyInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        MyAspect aspect = new MyAspect();

        try{
            aspect.before(invocation);
            Object result = invocation.proceed();
            assert result != null;
            return aspect.after(result);
        }catch (Throwable throwable){
            return aspect.exceptionHandler(throwable);
        }
    }
}

创建代理对象

//工厂bean
ProxyFactoryBean bean = new ProxyFactoryBean();
//配置
AdvisedSupport config = new AdvisedSupport();
//目标类
Target target = new Target();
//通知
MyInterceptor interception = new MyInterceptor();
//设置目标类的接口
config.setInterfaces(new Class[]{TargetInterface.class});
//设置目标
config.setTarget(target);
//设置通知
config.addAdvice(interception);
//获得代理对象,链式调用 bean.获得工厂.配置工厂.获得代理对象
TargetInterface proxy = (TargetInterface) bean.getAopProxyFactory().createAopProxy(config).getProxy();

AspectJ

  • 切入点表达式
    execution(<方法的返回类型> <完整类名>.<方法名>(…<参数类型>))

  • aspectJ定义了一系列通知,并通过注解的方式指定通知的类型

  • 通知类型如下:

    • Before 前置通知,在目标方法执行前执行
    • AfterReturning 后置通知,在目标方法执行后执行
    • AfterThrowing 抛出异常通知,在目标方法抛出异常时执行
    • After 最终通知,所有流程走完后执行该通知
    • Around 环绕通知,类似于前面MyInterceptor类的invoke方法,需要手动写整个增强过程
  • 当指定了目标类的接口时,aspectj会通过接口实现代理类,否则将采用cglib的方式,继承目标类实现代理,当然,你也可以通过配置,让aspectj强制使用cglib的模式

第一先定义一个切面类

/** AspectJAspect.java **/
package com.sinbxeunha.josechan.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.weaver.tools.JoinPointMatch;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

//@Component
//@EnableAspectJAutoProxy
@Aspect
public class AspectJAspect {

    @Pointcut("execution(* com.sinbxeunha.josechan.service.*.*(..))")
    private void pointCut() {
    }

    //注解前置通知
    @Before("pointCut()")
    public Object before(JoinPoint joinPoint) {
        System.out.println("执行" + joinPoint.getSignature().toString() + "
");
        return null;
    }

    //注解后置通知
    @AfterReturning(value = "pointCut()", returning = "result")
    public Object after(JoinPoint joinPoint, Object result) {
        assert result != null && result.toString() != null;
        System.out.println("执行结果:" + result.toString() + "
");
        return result;
    }

    //注解抛出异常通知
    @AfterThrowing(value = "pointCut()", throwing = "throwable")
    public Object exceptionHandler(JoinPoint joinPoint, Throwable throwable) {
        System.out.println("结果异常:" + throwable.getMessage() + "
");
        return null;
    }

    //注解最终通知
    @After("pointCut()")
    public Object afterAll(JoinPoint joinPoint) {
        return null;
    }

    //注解环绕通知,这个通知的增强内容相当于上面所有通知的并集
//    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            before(joinPoint);
            Object result = joinPoint.proceed();
            assert result != null;
            return after(joinPoint, result);
        } catch (Throwable throwable) {
            return exceptionHandler(joinPoint, throwable);
        } finally {
            afterAll(joinPoint);
        }
    }
}

接下来通过工厂构造代理

// AspectJ
//目标类
Target target = new Target();
//切面类
AspectJAspect aspect = new AspectJAspect();
//代理工厂
AspectJProxyFactory factory = new AspectJProxyFactory();
factory.setTarget(target);
factory.setInterfaces(new Class[]{TargetInterface.class});
factory.addAspect(aspect);
//        //强制使用cglib
//        factory.setProxyTargetClass(true);
TargetInterface proxy = factory.getProxy();

通过spring容器实现自动AspectJ代理

要通过spring容器实现自动代理,需要将切面类解析道容器中,并且开启自动代理选项
只需要在切面类上增加两行注解:

/** AspectJAspect.java **/
//...省略

@Component
@EnableAspectJAutoProxy
////强制使用cglib
//@EnableAspectJAutoProxy(proxyTargetClass = true)
@Aspect
public class AspectJAspect {
//...省略
}

并且通过容器去获取目标类

//将目标类解析到容器中
//...省略
@Service
public class Target implements TargetInterface {
//...省略

//通过容器获取目标类
/** @var ApplicationContext applicationContext 容器实例 **/
Target proxy = applicationContext.getBean(Target.class);

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
杨涛院方琳美国际修复整形的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容