AOP

发布时间 2023-04-18 19:13:51作者: lwx_R

1.介绍

Aspect Oriented Programming 面向切面编程,将程序中的相同业务逻辑进行横向隔离,将重复的业务逻辑抽取到一个独立的模块。、

  • 连接点Joinpoint:程序执行过程中某个特定的节点
  • 通知Advice:在目标类连接点上执行的代码,有around,before等类型
  • 切点Pointcut:匹配连接点的断言,AOP通过其定位到连接点。
  • 目标对象Target:通知所作用的目标类。
  • 引介Introduction: 特殊的通知,为类添加属性和方法。动态地为业务类添加接口的实现逻辑。
  • 切面Aspect: 对系统中的横切关注点逻辑进行模块化封装的AOP概念实体。
  • 织入Weaving: 将通知添加到目标类具体连接点的过程,在编译,类加载,运行时完成,Spring动态代理织入,AspectJ编译期和类装载器织入。
  • 代理Proxy:目标类被织入增强后产生的结果类,融合了原类和增强的逻辑。

2.实现机制

2.1 JDK动态代理

JDK代理只能为接口创建代理实例

  • LoginService接口
public interface LoginService {
    public void login();
}
  • LoginServiceImpl
public class LoginServiceImpl implements LoginService{

    @Override
    public void login() {
        System.out.println("Login");
    }
}
  • PerformHandler类
public class PerformHandler implements InvocationHandler {
    private Object target;

    public PerformHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //增强的方法
        System.out.println("start");
        //执行被代理类的原方法
        Object invoke = method.invoke(target, args);
        System.out.println("end");
        return invoke;
    }
}
  • Test类
@Test
public void fun(){
    LoginService loginService = new LoginServiceImpl();
    //通过Proxy生成代理对象
    PerformHandler performHandler = new PerformHandler(loginService);
    loginService = (LoginService) Proxy.newProxyInstance(loginService.getClass().getClassLoader(),
            loginService.getClass().getInterfaces(),performHandler);
    loginService.login();
}

2.2 CGLib动态代理

CGLib动态代理为类创建实例 底层字节码技术 继承的方式实现
依靠Enhancer类创建代理实例 MethodInterceptor接口织入方法

  • CglibProxy
public class CglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    //生成代理对象
    public Object getProxy(Class clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    //回调方法 拦截目标类所有方法调用
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("start");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("end");
        return invoke;
    }
}
  • Test类
@Test
public void funG(){
    CglibProxy cglibProxy = new CglibProxy();
    //创建代理对象
    LoginServiceImpl loginService = (LoginServiceImpl) cglibProxy.getProxy(LoginServiceImpl.class);
    loginService.login();
}

3.实现方法

3.1 XML

  • spring.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            ">
 <bean name="loginService" class="service.LoginServiceImpl"></bean>
 <bean name = "xmlAdvice" class="service.XmlAdvice"></bean>
<!--   Spring Aop-->
   <aop:config>
<!--      切点-->
      <aop:pointcut id="pointcut" expression="execution(* service.LoginServiceImpl.*(..))"/>
<!--      切面-->
      <aop:aspect ref="xmlAdvice">
<!--         前置-->
         <aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--         返回-->
         <aop:after-returning method="afterReturning" pointcut-ref="pointcut"></aop:after-returning>
<!--         环绕-->
         <aop:around method="around" pointcut-ref="pointcut"></aop:around>
<!--         异常-->
         <aop:after-throwing method="afterException" pointcut-ref="pointcut"></aop:after-throwing>
<!--         后置-->
         <aop:after method="after" pointcut-ref="pointcut"></aop:after>
      </aop:aspect>
   </aop:config>
</beans>
  • Test类
@Test
public void funXml(){
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring1.xml");
    LoginService loginService = context.getBean("loginService", LoginService.class);
    loginService.login();
}

3.2 注解

  • Spring.xml 修改
    <bean name="annoAdvice" class="service.AnnoAdvice"></bean>
<!--    自动动态代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 <bean name="loginService" class="service.LoginServiceImpl"></bean>
  • AnnoAdvice类
@Aspect
public class AnnoAdvice {
    //切点
    @Pointcut("execution(* service.LoginServiceImpl.*(..))")
    public void pointcut(){

    }
    @Before("pointcut()")
    public void before(){
        System.out.println("before");
    }

    //后置通知 异常不执行
    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("afterReturning");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        System.out.println("around start");
        Object object = point.proceed();
        System.out.println("around end");
        return object;
    }

    @AfterThrowing("pointcut()")
    public void afterException(){
        System.out.println("Exception");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("after");
    }
}