AOP概述
    
    AOP定义
    
    AOP:Aspect Oriented Programming(面向切面编程),是通过预编译和运行期动态代理来实现程序功能的统一维护的技术
    
    不同的业务块有时会具有相同的操作,如图:
   
    
    将这样相同的操作提取出来就是切面,aop则是面向这些多个业务块横向切取的公共片段编程,在维护期间,仅需要对切面进行修改即可,降低了耦合度,可维护性大大增强
    
    将切面提取之后,原对象和切面就被分隔开:
   
此时aspect和pointcut互不相关,交由代理将其联系在一起,调用时直接通过代理获取目标对象即可
    相关概念
    
    Advice,通知:需要单独封装的功能,定义在类的方法中;通知定义Pointcut中的具体需要执行的操作
    
    前置通知:在目标方法执行之前执行
    
    环绕通知:在目标方法执行之前和之后都可以执行
    
    后置通知:在目标方法执行之后执行
    
    异常通知:在目标方法抛出异常时执行
    
    最终通知:在目标方法执行之后执行,不同于后置通知的是,后置通知只有程序正常时执行,抛出异常时后置通知不会执行;而最终通知不论是否异常都会执行
   
    JoinPoint,连接点:可以使用通知的地方,如事务控制中,使用到事务控制的方法;自身可嵌套其他的JoinPoint
    
    Pointcut,切入点:定义使用通知的连接点的集合,与连接点是一对多的关系
    
    Aspect,切面:通知和切入点的组合
    
    Weaving,织入:把切面应用到应用程序中的过程
    
    Target,目标:应用切面的对象
    
    Introduction,引入:向现有的类添加新方法或新属性
    
    应用场景
    
    日志记录
    
    事务管理
    
    安全控制
    
    异常处理
    
    性能统计
    
    SpringAOP实现方式(编码方式、xml方式、命名空间方式)
    
    前置通知:实现MethodBeforeAdvice接口
    
    后置通知:实现AfterReturningAdvice接口
    
    环绕通知:实现MethodInterceptor接口;环绕通知返回结果为方法的返回值,简单的切面可以只需要用环绕通知就实现了前置、后置、异常通知的功能
    
    异常通知:实现ThrowsAdvice接口;异常通知并没有任何方法,如果使用该接口则必须实现这样格式的方法void afterThrowing([method,args,target],throwable);其中出了异常对象,其他的都可以去掉
    
    代码演示
    
    目标接口
   
    public interface Service {
    
    
    public void add();
    
    public void update() throws Exception;
    
    }
    
    1
    
    2
    
    3
    
    4
    
    目标实现类
   
    public class MyBeforeAdvice implements MethodBeforeAdvice {
    
   
    @Override
    
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
    
    System.out.println(“MyBeforeAdvise before”);
    
    }
    
    }
前置通知
    public class MyBeforeAdvice implements MethodBeforeAdvice {
    
   
    @Override
    
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
    
    System.out.println(“before”);
    
    }
    
    }
后置通知
    public class MyAfterAdvice implements AfterReturningAdvice {
    
    
    @Override
    
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    
    
    System.out.println(“after”);
    
    }
    
    }
环绕通知
    public class MyRoundAdvice implements MethodInterceptor {
    
    
    @Override
    
    public Object invoke(MethodInvocation invocation) throws Throwable {
    
    
    System.out.println(“开始”);
    
    Object result = invocation.proceed();//执行目标对象的方法,并获取方法返回值
    
    System.out.println(“结束”);
    
    return result;
    
    }
    
    }
异常通知
    public class MyThrowsAdvice implements ThrowsAdvice {
    
   
    public void afterThrowing(Method method,Object[] args,Object target, Exception ex){
    
    
    System.out.println(“Exception”);
    
    }
    
    }
    编码方式
    
    使用ProxyFacoty类的方法setTarget()和addAdvice(),代理使用getProxy获取
    
    代理工厂类
   
    public class MyProxyFactory {
    
   
    public static <T>T getProxy(Class<T> c) throws Exception {
    
    
    T t = c.newInstance();
    
    MyBeforeAdvice myBeforeAdvise = new MyBeforeAdvice();
    
    MyAfterAdvice myAfterAdvice = new MyAfterAdvice();
    
    MyRoundAdvice myRoundAdvice = new MyRoundAdvice();
    
    MyThrowsAdvice myThrowsAdvice = new MyThrowsAdvice();
   
    //创建代理工厂
    
    ProxyFactory proxyFactory = new ProxyFactory();
   
    //设置目标与添加切面,将目标与切面联系在一起
    
    //        proxyFactory.setInterfaces(Service.class);该语句为设置实现的接口,当代理接口时使用setInterfaces方法可以指定代理的接口,spring将使用jdk的代理;代理类或没有setInterfaces时spring将使用cglib的代理
    
    proxyFactory.setTarget(t);
    
    proxyFactory.addAdvice(myBeforeAdvise);
    
    proxyFactory.addAdvice(myAfterAdvice);
    
    proxyFactory.addAdvice(myRoundAdvice);
    
    proxyFactory.addAdvice(myThrowsAdvice);
   
    //获取代理对象
    
    return (T) proxyFactory.getProxy();
    
    }
    
    }
测试
    public class TestBycode {
    
    
    @Test
    
    public void test1() throws Exception {
    
    
    Service proxy = MyProxyFactory.getProxy(ServiceImpl.class);
    
    proxy.add();
    
    proxy.update();
    
    }
   
}
结果
    before
    
    开始
    
    add
    
    结束
    
    after
    
    15:07:45.857 [main] DEBUG o.s.a.f.a.ThrowsAdviceInterceptor – Found exception handler method on throws advice: public void com.aop.bycode.MyThrowsAdvice.afterThrowing(java.lang.reflect.Method,java.lang.Object[],java.lang.Object,java.lang.Exception)
    
    before
    
    开始
    
    update
    
    Exception
   
    java.lang.Exception
    
    at com.aop.bycode.ServiceImpl.update(ServiceImpl.java:20)
    
    at com.aop.bycode.ServiceImpl$$FastClassBySpringCGLIB$$9c5bb4a4.invoke(<generated>)
    
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
    
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    
    at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:113)
    
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    
    at com.aop.bycode.MyRoundAdvice.invoke(MyRoundAdvice.java:18)
    
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:57)
    
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:58)
    
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
    
    at com.aop.bycode.ServiceImpl$$EnhancerBySpringCGLIB$$3796a5e3.update(<generated>)
    
    at aop.bycode.TestBycode.test1(TestBycode.java:41)
    
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    
    at java.lang.reflect.Method.invoke(Method.java:498)
    
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
    
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
   
    
    Process finished with exit code -1
   
    
    代理类时,spring将使用cglib的代理
    
    代理的类实现了接口时,需要指定代理的接口类型,即使用proxyFactory.setIntegerfaces方法,否则还是使用cglib代理的实现类,指定接口之spring将使用jdk代理接口
   
    xml方式
    
    在spring容器中配置需要代理的目标对象的bean以及通知的bean,然后配置ProxyFactoryBean,指定属性target和interceptorNames,配置好的ProxyFactoryBean就是代理的bean直接注入即可
    
    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”
    
    xsi:schemaLocation=”http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd”>
   
    <bean id=”serviceImpl” class=”com.aop.ServiceImpl”/>
    
    <bean id=”myAfterAdvice” class=”com.aop.MyAfterAdvice”/>
    
    <bean id=”myBeforeAdvice” class=”com.aop.MyBeforeAdvice”/>
    
    <bean id=”myRoundAdvice” class=”com.aop.MyRoundAdvice”/>
    
    <bean id=”myThrowsAdvice” class=”com.aop.MyThrowsAdvice”/>
   
    <!–在配置文件中配置一个ProxyFactoryBean,可以得到一个对应代理的Bean–>
    
    <bean id=”serviceProxy” class=”org.springframework.aop.framework.ProxyFactoryBean”>
    
    <!–设置代理的目标–>
    
    <property name=”target” ref=”serviceImpl”/>
    
    <property name=”interceptorNames”>
    
    <array>
    
    <value>myAfterAdvice</value>
    
    <value>myBeforeAdvice</value>
    
    <value>myRoundAdvice</value>
    
    <value>myThrowsAdvice</value>
    
    </array>
    
    </property>
    
    </bean>
    
    </beans>
使用xml方式不需要指定接口类型,如果代理的类实现了接口,则会使用jdk代理,否则会使用cglib动态代理
    命名空间方式(最简洁方便)
    
    execution表达式(切点函数):定义切入点
    
    权限修饰符返回类型 包名.类名.方法名.(参数)
    
    其中,出参数外,其他部分用*表示所有的,参数用…表示所有的
    
    常用的表达式:
   
    (* package.*.*(..))定义切点为package包中的所有类的任何参数的方法
    
    public* package.*.*(..)定义切点为package包中的所有public类的任何参数的方法
    
    通过spring配置文件,引入aop命名空间,通过aop-config标签来配置目标和切面之间的联系
    
    配置文件
   
    <?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:aop=”http://www.springframework.org/schema/aop”
    
    xsi:schemaLocation=”http://www.springframework.org/schema/beans
    
    http://www.springframework.org/schema/beans/spring-beans.xsd
    
    http://www.springframework.org/schema/aop
    
    http://www.springframework.org/schema/aop/spring-aop.xsd”>
   
    
    <bean id=”serviceImpl” class=”com.aop.target.ServiceImpl”/>
    
    <bean id=”myAfterAdvice” class=”com.aop.aspect.MyAfterAdvice”/>
    
    <bean id=”myBeforeAdvice” class=”com.aop.aspect.MyBeforeAdvice”/>
    
    <bean id=”myRoundAdvice” class=”com.aop.aspect.MyRoundAdvice”/>
    
    <bean id=”myThrowsAdvice” class=”com.aop.aspect.MyThrowsAdvice”/>
    
    <aop:config>
    
    <aop:pointcut id=”myPointcut” expression=”execution(* com.aop.target.*.*(..))”/>
    
    <aop:advisor advice-ref=”myThrowsAdvice” pointcut-ref=”myPointcut”/>
    
    <aop:advisor advice-ref=”myBeforeAdvice” pointcut-ref=”myPointcut”/>
    
    <aop:advisor advice-ref=”myAfterAdvice” pointcut-ref=”myPointcut”/>
    
    <aop:advisor advice-ref=”myRoundAdvice” pointcut-ref=”myPointcut”/>
    
    </aop:config>
    
    <aop:aspectj-autoproxy proxy-target-class=”true”/>
    
    </beans>
测试
    public class TestByNamespace {
    
    
    @Test
    
    public void test1() throws Exception {
    
    
    ApplicationContext context = new ClassPathXmlApplicationContext(“aopNamespace.xml”);
    
    ServiceImpl proxy =  context.getBean(“serviceImpl”,ServiceImpl.class);
    
    proxy.add();
    
    }
   
}
    使用命名空间的注意事项
    
    代理实现类时的误操作
    
    当代理的是实现类时,spring默认使用的是jdk实现动态代理,在通过getbean获取实例时,如果使用getbean(name,classType)方式的话,会报错,因为从spring容器中得到的实例是jdk的Proxy类型,此时则需要使用getBean(name)的方式,然后将获取到的对象强转;或者使用<aop:aspectj-autoproxy proxy-target-class=”true”/>将代理主动设置为cglib的形式,由于cglib代理是通过创建子类的方式,所以传入的目标类的class和获取到的类型是符合的
   
    通知执行的顺序问题
    
    使用命名空间时,定义通知的标签会重新排列通知执行的顺序,当前置通知、后置通知、异常通知生民在环绕通知之前时,会按照原顺序执行;当任一个通知定义在环绕通知之后时,该通知会和环绕通知的前置环绕或后置环绕顺序调换
    
    https://blog.csdn.net/m0_48468380/article/details/118480932
   
 
