Spring之AOP

前言

AOP作为OOP的延续,可以进一步解耦,提高开发和维护效率。

一、AOP

1、AOP基本概念

AOP(Aspect Oriented Programming),面向切面编程,是OOP的延续。AOP可以降低业务逻辑各部分之间的耦合度,提高程序的可重用性和开发效率。

根据它以上的特点,所以主要用户以下场景,
1)日志记录
2)性能统计
3)安全控制
4)事务处理
5)异常处理
在这些场景下,可以将以上部分与事务隔离开来,放到非业务部分,进而改变这些代码时,不会影响实际业务。

举一个实际例子,
如果登录要判断其权限问题,原始的做法就是在登录的逻辑中写判断代码,而AOP的方式为,单独写好权限控制模块,然后通过配置将该模块配置到登录逻辑的主干部分,而不用修改原来的登录逻辑。

2、AOP底层逻辑

AOP通过动态代理来增强类中某个方法的功能,代理对象比原始对象能做更强的事情。

动态代理的两种情况,
1)有接口的情况,使用JDK动态代理。
在这里插入图片描述

2)无接口的情况,使用CGLIB动态代理。
在这里插入图片描述

3、Case

A.JDK动态代理

1)写一个接口和实现类
2)使用Proxy类来创建代理对象
JDK代理的方法,

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
        Objects.requireNonNull(h);

        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

        /*
         * Look up or generate the designated proxy class and its constructor.
         */
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

        return newProxyInstance(caller, cons, h);
    }

具体实现,

package com.xhu.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxy {
    private static UserDAO ud = new UserDAOImpl();

    public static void main(String[] args) {
        UserDAO udProxy = (UserDAO) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{UserDAO.class}, new UserDAOProxy(ud));
        System.out.println(udProxy.add(1, 2));
    }
}

class UserDAOProxy implements InvocationHandler {
    private Object obj;

    public UserDAOProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println("methodName:" + method.getName());
        //执行原方法
        Object invoke = method.invoke(obj, args);
        //方法之后
        System.out.println("obj:" + obj);
        return invoke;
    }
}

B.CGLIB动态代理

4、AOP术语

1)连接点,类中可以被增强的方法,这些方法称为连接点。
2)切入点,实际被增强的方法,这些方法称为切入点。
3)通知(增强),真正被增强的逻辑部分,比如在方法最后加个日志,加这个日志就是通知。或者登录之前权限判断,这个权限判断就是通知。
注:通知类型如下,
A)前置通知,方法之前通知。
B)后置通知,方法之后通知。
C)环绕通知,方法之前之后都通知。
D)异常通知,当方法出现异常,则通知。
E)最终通知,类finally,如果方法发生异常,则后置通知肯定不执行,但是要执行最终通知。
4)切面,把通知应用到切入点的过程就是切面。

5、AspectJ

AspectJ是一个独立的AOP框架,可以结合Spring一起来进行Spring项目的AOP操作。可XML配置可注解。

A.切入点表达式

用于对那个类的那个方法进行增强,即找到切入点。
1)语法结构,
execution([权限修饰符][返回路径][类全路径][方法名称][参数列表])
2)例子
例1)对com.xhu.dao.BookDAO 的方法add进行增强.
execution(* com.xhu.dao.BookDAO.add(…)),返回类型省略,用空格替代。
例2)对com.xhu.dao.BookDAO 的所有方法进行增强.
execution(* com.xhu.dao.BookDAO.*(…))
例3)对com.xhu.dao包的所有类的所有方法进行增强.
execution(* com.xhu.dao.*.*(…))

6、AspectJ+Spring

A.基于注解

1)两种基本类,
被增强类、增强类1、增强类2、

package com.xhu.aop;

import org.springframework.stereotype.Component;

/**
 * 被增强类
 */
@Component
public class User {

    public void add(){
        System.out.println("add......................");
    }
}

package com.xhu.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 通知类
 */
@Component
@Aspect
@Order(2)//越大优先级越小
public class UserProxy {
    //相同切入点抽取
    @Pointcut(value = "execution(* com.xhu.aop.User.add(..))")
    public void point() {
    }
    //多个增强类对同一个方法增强,设置增强的优先级。

    /**
     * 前置通知
     */
    @Before(value = "point()")
    public void before() {
        System.out.println("before............................");
    }

    /**
     * 最终通知
     */
    @After(value = "execution(* com.xhu.aop.User.add(..))")
    public void after() {
        System.out.println("after.......................................");
    }

    /**
     * 后置通知
     */
    @AfterReturning(value = "execution(* com.xhu.aop.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning.......................................");
    }

    /**
     * 异常通知
     */
    @AfterThrowing(value = "execution(* com.xhu.aop.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.......................................");
    }

    /**
     * 环绕通知
     *
     * @param pjp
     * @throws Throwable
     */
    @Around(value = "execution(* com.xhu.aop.User.add(..))")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around前.......................................");
        pjp.proceed();
        System.out.println("around后.......................................");
    }

}

package com.xhu.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Order(1)//越小优先级越高
public class UserProxyPro {
    /**
     * 前置通知
     */
    @Before(value = "execution(* com.xhu.aop.User.add(..))")
    public void before() {
        System.out.println("before PRO............................");
    }
}

2)配置类

package com.xhu.aop;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * @Configuration 告诉spring这是一个配置类
 */
@Configuration
//配置要做的事为,开启组件扫描和启用AspectJ自动代理
@ComponentScan(basePackages = {"com.xhu.aop"})
@EnableAspectJAutoProxy
public class SpringConfig {
}

3)测试类

package com.xhu.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestAOP {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user = ac.getBean("user", User.class);
        user.add();
    }
}

B.基于XML

1)配置xml中的组件扫描和Aspect自动代理

<!-- 配置对象-->
    <bean id="user" class="com.xhu.aop.User"></bean>
    <bean id="userProxy" class="com.xhu.aop.UserProxy"></bean>
    <!-- 配置aop具体信息-->
    <aop:config>
        <!-- 切入点-->
        <aop:pointcut id="point" expression="execution(* com.xhu.aop.User.add(..))"/>
        <!-- 配置切面-->
        <aop:aspect ref="userProxy">
            <!-- 配置增强-->
            <aop:before method="before" pointcut-ref="point"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>

2)test类

package com.xhu.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAOP {
    public static void main(String[] args) {
        //1.获取ApplicationContext对象来关联配置文件,为接下来的IOC做准备
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象实例
        User user = ac.getBean("user", User.class);
        user.add();
    }
}

总结

1)AOP作为OOP的延续,可以进一步解耦,提高开发和维护效率。

参考文献

[1]Spring5 尚硅谷

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值