Spring之 AOP面向切面编程

AOP(Aspect Oriented Programming),面向切面(横向)编程,是面向对象编程OOP(Object Oriented Programming,纵向)的补充和完善。
OOP是一种纵向编程,它引入封装、继承、多态等概念来建立一种对象纵向层次结构,如下图所示:
 
AOP是一种横向编程,它把那些与业务无关,却为业务模块所共有调用的逻辑或责任封装起来,可以减少系统的重复代码、降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
 
应用场景:日志记录、性能统计、安全控制、事务处理、异常处理等等
主要意图:将日志记录、性能统计、安全控制、事务处理、异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非业务逻辑的方法中去,进而改变这些行为的时候不会影响到业务逻辑的代码。

(1) AOP核心概念

切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。
连接点(Joinpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,spring只支持方法执行连接点,其实在类中有很多的方法可以被增强,那么在实际操作中,定义要被增强的方法为切入点即可。
切点(Pointcut):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

这里写图片描述
除了用execution,也可以使用annotation来定义
 
通知(Advice):所谓通知是指拦截到Joinpoint之后要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知、环绕通知
前置通知(Before Advice):在方法之前执行
后置通知(After Advice):在方法之后执行
环绕通知(Around Advice):在方法之前和之后执行
异常通知(AfterThrowing Advice):方法出现异常执行
最终通知(AfterReturning Advice):在返回之后执行
 
执行顺序: @Around→@Before→@After→@Around执行 ProceedingJoinPoint.proceed() 之后的操作→@AfterRunning(如果有异常→@AfterThrowing)
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect   //切面
@Order(1)  //order越小,执行优先级越高
public class LogRecordAspect {

    //表示controller包中的所有方法
    @Pointcut("execution(public * com.h3c.fuxi.business.*.controller.*(..))")
    public void logsPointcut(){};

    //表示所有使用LogRecord的所有方法,切点
    @Pointcut("@annotation(com.h3c.fuxi.common.aop.annotation.LogRecord)")
    public void logManagePointcut() {
    }

    //在方法之前执行
    @Before("logManagePointcut()")
    public void beforeData(){
        System.out.println("在方法执行前调用的方法");
    }

    //
    @Around("logManagePointcut()")
    public Object aroundData(ProceedingJoinPoint joinPoint){
        System.out.println("在方法执行前和执行后调用的方法");
        Signature signature = joinPoint.getSignature();
        Object object = null;
        try {
            object = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return object;
    }

    //在方法之后执行
    @After("logManagePointcut()")
    public void afterData(){
        System.out.println("在方法执行后调用的方法");
    }

    //出现异常执行
    @AfterThrowing(pointcut = "logManagePointcut()", throwing = "ex")
    public void afterThrowing(JoinPoint point, Throwable ex){
        System.out.println("在方法出现异常后调用的方法");
    }
}

引入(Introduction):是一种特殊的通知在不修改类代码前提下,Introduction可以在允许期间为类动态地添加一些方法或属性

目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。
 
AOP代理(AOP Proxy):AOP框架使用代理模式创建的对象,从而实现在连接点处插入通知(即应用切面),就是通过代理来对目标对象应用切面。在spring中,AOP代理可以用JDK代理或CGLIB代理实现,而通过拦截器模型应用切面。
织入(Weaving):织入就是把一个advice应用到target的过程,是将切面增强逻辑应用到目标对象的连接点从而创建处AOP代理对象的过程。织入可以在编译期、类装载期、运行期进行。
 

(2) spring AOP的底层实现

spring中AOP代理由spring的IOC容器负责生成、管理、其依赖关系也由IOC容器负责管理,因此,AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可由IOC容器的依赖注入提供,spring AOP 的底层使用动态代理。
 
 
动态代理的方式
如果代理对象实现了接口,默认使用 JDK动态代理来创建AOP代理,针对接口
如果代理对象没有实现接口,spring会切换使用 CGLIB代理,针对类,当然spring AOP也可强制使用CGLIB
 
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    public DefaultAopProxyFactory() {
    }

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);
        } else {
            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.");
            } else {
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
    }
}

JDK动态代理实现:只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理,原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理,步骤如下

  1. 定义一个实现接口InvocationHandler的类
  2. 通过构造函数或者静态工厂方法等,注入被代理类
  3. 实现invoke(Object proxy,Method method,Object[]args)方法
  4. 在主函数中获得被代理类的类加载器
  5. 使用Proxy.newProxyInstance(classLoader,interfaces,args)产生一个代理对象
  6. 通过代理对象调用各种方法​
//代理对象的接口
public interface SubjectName {
	void rent();
	void hello(String str);
	void myName(String nameOne, String nameTwo);
}
//实现这个接口的类
public class RealSubject implements SubjectName {
	@Override
	public void rent() {
		System.out.println("I want to rent my house");
	}
	@Override
	public void hello(String str) {
		System.out.println("hello: " + str);
	}
	@Override
	public void myName(String nameOne, String nameTwo) {
		System.out.println("hello: " + nameOne);
	}
}
//动态代理的具体实现
public class DynamicProxy implements InvocationHandler {
	//这个就是我们要代理的真实对象
    private Object subject;
    //构造方法,给我们要代理的真实对象赋初值
    public DynamicProxy(Object subject){
        this.subject = subject;
    }
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		 //在代理真实对象前我们可以添加一些自己的操作
        System.out.println("before rent house");
        System.out.println("Method:" + method);
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);
        //在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("after rent house");
        return "";
	}
}

public class Client {
	public static void main(String[] args){
        //我们要代理的真实对象
        SubjectName realSubject = new RealSubject();
        //我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);
        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        SubjectName subject = (SubjectName)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        
        System.out.println(subject.getClass().getName());
        subject.rent();
        subject.hello("world");
        subject.myName("xjz", "jiezhan");
    }
}

CGLIB动态代理实现:针对类代理实现,对是否实现接口无要求,原理是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以被代理的类最好不要声明为final类型。

  1. 定义一个实现了MethodInterceptor接口的类
  2. 实现其intercept()方法,在其中调用proxy.invokeSuper()​
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        @Nullable
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Object target = null;
            TargetSource targetSource = this.advised.getTargetSource();

            Object var16;
            try {
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }

                target = targetSource.getTarget();
                Class<?> targetClass = target != null ? target.getClass() : null;
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = methodProxy.invoke(target, argsToUse);
                } else {
                    retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
                }
/*CglibAopProxy则主要是通过Enhancer对象生成代理对象,同时将Spring AOP的实现封装在callback的回调设置中,并将DynamicAdvisedInterceptor对象的intercept()方法当作回调如果,类似于动态代理的invoke方法,并以CglibMethodInvocation对象返回*/

                retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal);
                var16 = retVal;
            } finally {
                if (target != null && !targetSource.isStatic()) {
                    targetSource.releaseTarget(target);
                }

                if (setProxyContext) {
                    AopContext.setCurrentProxy(oldProxy);
                }

            }

            return var16;
        }

 

静态代理和动态代理的区别
静态代理:自己编写代理类,然后在进行编译,在程序运行之前,代理类的.class文件就已经存在了
动态代理:在实现阶段不用关心代理谁,而在运行阶段(通过反射机制)才指定代理哪一个对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值