Spring AOP 面向切面编程学习

AOP

AOP(Aspect Oriented Programming) ,面向切面编程

在这里插入图片描述
左边是传统方式,右边是面向切面方式
所谓切面编程就是在执行某个功能的时候,在前面添加点东西,还可以在后面添加点东西。而并没有去改变原有的代码

在这里插入图片描述
在程序原有纵向执行流程中,针对某一个或某一些方法添加通 知,形成横切面过程就叫做面向切面编程

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码,主要体现在事务处理,日志管理,权限控制,异常处理等方面,使开发人员在编写业务逻辑时可以专心于核心业务,提高了代码的可维护性。

AOP术语
Joinpoint(连接点):是指那些被拦截到的点
Pointcut(切入点):是指要对哪些Joinpoint进行拦截,即被拦截的连接点。也可以理解为原有的功能
Advice(通知):是指拦截到Joinpoint之后要做的事情,既对切入点增强的内容
before advice(前置通知):在切入点之前执行的功能.
after advice(后置通知):在切点之后执行的功能
throws advice(异常通知):在切点执行过程中出现异常,会触发异常通知.
Aspect(切面):切入点和通知的结合

Spring提供了2种AOP实现方式
1.Schema-based
在Schema-based方式中,每个通知都要实现特定的接口或类
并在spring配置文件中,添加<aop:config>标签,然后再<aop:config>标签下进行配置
2.AspectJ
在AspectJ方式中,每个通知不需要实现特定的接口或类
配置 spring 配置文件是在<aop:config>的子标签 <aop:aspect>中配置

环境搭建

在这里插入图片描述
导入相关jar包

Schema-based 方式

在Schema-based方式中,每个通知都要实现特定的接口或类

前置通知

新建一个前置通知类实现接口

package cn.com.advice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知");
    }
}

前置通知类需要实现MethodBeforeAdvice接口

Method method:切点方法对象
Object[] objects:切点方法参数
Object o:切点方法所在类的对象

<?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-->
    <bean id="test" class="cn.com.test.Test"/>
    <bean id="mybefore" class="cn.com.advice.MyBeforeAdvice"></bean>

    <!--配置切面-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>
        <!--配置通知-->
        <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
    </aop:config>

</beans>

在配置文件中,配置了在Test类下的method1方法下插入前置通知

package cn.com.test;

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

public class Test {
    public void method1(){
        System.out.println("method1");
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");   //spring实现aop,对象要交给spring管理
        Test test=ac.getBean("test",Test.class);
        test.method1();
        test.method2();
        test.method3();
    }
}

在这里插入图片描述

后置通知

后置通知:在切点的后面插入 实现AfterReturningAdvice

 import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class MyAfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知");
    }
}

Object o:切点方法返回值
Method method:切点方法对象
Object[] objects:切点方法参数
Object o1:切点方法所在类的对象

<?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-->
    <bean id="test" class="cn.com.test.Test"/>
    <bean id="mybefore" class="cn.com.advice.MyBeforeAdvice"></bean>   <!--前置通知所在类-->
    <bean id="myafter" class="cn.com.advice.MyAfterAdvice"></bean>     <!--后置通知所在类-->
    
    <!--配置切面-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>
        <!--配置通知-->
        <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>   <!--前置通知-->
        <aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>     <!--后置通知-->
    </aop:config>

</beans>
package cn.com.test;

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

public class Test {
    public void method1(){
        System.out.println("method1");
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Test test=ac.getBean("test",Test.class);
        test.method1();
        test.method2();
        test.method3();
    }
}

添加前置通知和后置通知并没有改变原有代码
在这里插入图片描述

环绕通知

环绕通知:在切点前后都加功能

package cn.com.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyArroundAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("环绕执行前");
        Object result=methodInvocation.proceed();   //放行
        System.out.println("环绕通知后");
        return result;
    }
}

实现MethodInterceptor接口

<?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-->
    <bean id="test" class="cn.com.test.Test"/>
    <bean id="mybefore" class="cn.com.advice.MyBeforeAdvice"></bean>   <!--前置通知所在类-->
    <bean id="myafter" class="cn.com.advice.MyAfterAdvice"></bean>     <!--后置通知所在类-->
    <bean id="myarround" class="cn.com.advice.MyArroundAdvice"></bean>    <!--环绕通知所在类-->
    <!--配置切面-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>
        <!--配置通知-->
        <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>   <!--前置通知-->
        <aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>     <!--后置通知-->
        <aop:advisor advice-ref="myarround" pointcut-ref="mypoint"/>   <!--环绕通知-->
    </aop:config>

</beans>
package cn.com.test;

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

public class Test {
    public void method1(){
        System.out.println("method1");
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Test test=ac.getBean("test",Test.class);
        test.method1();
        test.method2();
        test.method3();
    }
}

在这里插入图片描述

异常通知

当切点执行出现错误时,执行异常通知

定义异常通知,需要实现ThrowsAdvice接口

在这里插入图片描述
查看源码,发现ThrowsAdvice接口并没有定义方法,

在实现ThrowsAdvice接口时,需要自己写方法,并且方法名必须叫afterThrowing

package cn.com.advice;

import org.springframework.aop.ThrowsAdvice;

public class MyThrowAdvice implements ThrowsAdvice {
    public void afterThrowing(Exception e) throws Throwable{
        System.out.println("异常通知:"+e.getMessage());
    }
}

<?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-->
    <bean id="test" class="cn.com.test.Test"/>
    <bean id="mybefore" class="cn.com.advice.MyBeforeAdvice"></bean>   <!--前置通知所在类-->
    <bean id="myafter" class="cn.com.advice.MyAfterAdvice"></bean>     <!--后置通知所在类-->
    <bean id="myarround" class="cn.com.advice.MyArroundAdvice"></bean>    <!--环绕通知所在类-->
    <bean id="mythrow" class="cn.com.advice.MyThrowAdvice"></bean>     <!--配置异常通知-->
    <!--配置切面-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>
        <!--配置通知-->
        <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>   <!--前置通知-->
        <aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>     <!--后置通知-->
        <aop:advisor advice-ref="myarround" pointcut-ref="mypoint"/>   <!--环绕通知-->
        <aop:advisor advice-ref="mythrow" pointcut-ref="mypoint"/>    <!--异常通知-->
    </aop:config>

</beans>

package cn.com.test;

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

public class Test {
    public void method1(){
        Test test=null;
        test.method3();    //定义了一个异常
        System.out.println("method1");
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Test test=ac.getBean("test",Test.class);
        test.method1();
        test.method2();
        test.method3();
    }
}

在这里插入图片描述

实现接口中方法的参数还可以写成四个参数

import java.lang.reflect.Method;

public class MyThrowAdvice implements ThrowsAdvice {
/*    public void afterThrowing(Exception e) throws Throwable{
        System.out.println("异常通知:"+e.getMessage());
    }*/

     public void afterThrowing(Method m, Object[] args, Object o, Exception e) {
     System.out.println("异常通知:"+e.getMessage());
     }
}

注:
参数中的异常对象最好定义为Exception的对象

在method1()方法中,出现的异常为NullPointerException,所以可以方法中参数可以定义为NullPointerException


public class MyThrowAdvice implements ThrowsAdvice {
    public void afterThrowing(NullPointerException e) throws Throwable{
        System.out.println("异常通知:"+e.getMessage());
    }
}

在这里插入图片描述
可以看出是执行了异常通知了的

如果将参数换做不是NullPointerException的异常对象,假如换做ClassNotFoundException

public class MyThrowAdvice implements ThrowsAdvice {
    public void afterThrowing(ClassNotFoundException e) throws Throwable{
        System.out.println("异常通知:"+e.getMessage());
    }
}

在这里插入图片描述
可以看出没有执行异常通知

*和…的使用

在配置切点是可以使用*来指定包或类

1.

<!--配置切点-->
<aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/> 

这表示只能是Test类下的method1方法,并且method1必须是无参数的
在这里插入图片描述
将method1变为有参数的,可以看出并没有执行通知

2.

 <!-- 配置切点-->
 <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1(..))"/>

这表示是method1是有参数的

3.

<!--配置切点-->
<aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.*())"/> 

这表示是Test类下的任意无参方法

4.

<!--配置切点-->
<aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.*(..))"/> 

这表示是Test类下的任意方法,不管是有参还是无参

5.

<!--配置切点-->
<aop:pointcut id="mypoint" expression="execution(* cn.com.test.*.*(..))"/> 

这表示test包下的任意类的任意方法,不管是有参还是无参

以此类推

AspectJ方式

AspectJ方式不需要实现接口
可以自定义一个类,类下写方法,使用标签来说明类下的方法是什么类型的通知
使用配置文件的方式
<aop:config>下的<aop:aspect >标签下进行配置
<aop:aspect >标签的ref属性是指引用自己定义的通知类,方法在哪个类中

<bean id="test" class="cn.com.test.Test"/>
<bean id="before" class="cn.com.advice.Advice"></bean>   <!--配置通知所在类-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect ref="before">
            <!--配置切点-->
            <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>  
            <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
        </aop:aspect>
    </aop:config>

<aop:before>前置通知标签
<aop:after/> 后置通知,是否出现异常都执行
<aop:after-returing/> 后置通知,只有当切点正确执行时
<aop:after-throwing/>异常通知
<aop:after/><aop:after-returing/><aop:after-throwing/>执行顺序和配置顺序有关

<aop: xxxx/> 表示什么通知
method属性: 当触发这个通知时,调用哪个方法

前置通知

随便定义一个类,里面写一个方法,方法名也随便起

package cn.com.advice;

public class Advice {
    public void beforeAdvice(){
        System.out.println("前置通知");
    }
}
<?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-->
    <bean id="test" class="cn.com.test.Test"/>
    <bean id="before" class="cn.com.advice.Advice"></bean>   <!--配置通知所在类-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect ref="before">
            <!--配置切点-->
            <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>  
            <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
        </aop:aspect>
    </aop:config>
</beans>

method属性: 当触发这个通知时,调用哪个方法 pointcut-ref属性:引用哪个切点


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

public class Test {
    public void method1(){
        System.out.println("method1 ");
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Test test=ac.getBean("test",Test.class);
        test.method1();
        test.method2();
        test.method3();
    }
}

在这里插入图片描述

后置通知

package cn.com.advice;

public class Advice {
    public void beforeAdvice(){
        System.out.println("前置通知");
    }
    public void afterAdvice(){
        System.out.println("后置通知");
    }
}
    <bean id="test" class="cn.com.test.Test"/>
    <bean id="before" class="cn.com.advice.Advice"></bean>   <!--配置通知所在类-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect ref="before">
            <!--配置切点-->
            <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>
            <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
            <aop:after method="afterAdvice" pointcut-ref="mypoint"></aop:after>     <!--配置后置通知-->
        </aop:aspect>
    </aop:config>
package cn.com.test;

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

public class Test {
    public void method1(){
        System.out.println("method1 ");
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Test test=ac.getBean("test",Test.class);
        test.method1();
        test.method2();
        test.method3();
    }
}

在这里插入图片描述

1. aop:after和aop:after-returing区别

后置通知还有个标签是 <aop:after-returing/>
它与<aop:after>标签的区别是 <aop:after-returing/> ,在切点出现异常时<aop:after>依然能通知

 <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
 <aop:after method="afterAdvice" pointcut-ref="mypoint"></aop:after>     <!--配置后置通知-->

在这里插入图片描述
可以看出依然执行了后置通知

<aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
<aop:after-returning method="afterAdvice" pointcut-ref="mypoint"></aop:after-returning>

在这里插入图片描述

可以看出并没有执行后置通知

2.标签的好处

因为是使用标签来指定方法到底是什么类型的通知,所以标签可以随意指定方法

 <aop:before method="afterAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
 <aop:after method="beforeAdvice" pointcut-ref="mypoint"></aop:after>     <!--配置后置通知-->

将两个标签所指定的方法互换一下
在这里插入图片描述
可以看出互换成功

环绕通知

只需在Advice类中添加环绕通知的方法,并且要在参数中添加 ProceedingJoinPoint 的对象

package cn.com.advice;

import org.aspectj.lang.ProceedingJoinPoint;

public class Advice {
    public void beforeAdvice(){
        System.out.println("前置通知");
    }
    public void afterAdvice(){
        System.out.println("后置通知");
    }

    public Object arroundAdvice(ProceedingJoinPoint p) throws Throwable {
        System.out.println("环绕前置通知");
        Object result=p.proceed();
        System.out.println("环绕后置通知");
        return result;
    }

}

在xml中使用<aop:around>标签

<bean id="test" class="cn.com.test.Test"/>
<bean id="before" class="cn.com.advice.Advice"></bean>   <!--配置通知所在类-->
<aop:config>
        <!--配置切面-->
        <aop:aspect ref="before">
            <!--配置切点-->
            <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>
            <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
            <aop:after-returning method="afterAdvice" pointcut-ref="mypoint"></aop:after-returning>
            <aop:around method="arroundAdvice" pointcut-ref="mypoint"></aop:around>
        </aop:aspect>
    </aop:config>
package cn.com.test;

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

public class Test {
    public void method1(){
        System.out.println("method1 ");
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Test test=ac.getBean("test",Test.class);
        test.method1();
        test.method2();
        test.method3();
    }
}

在这里插入图片描述

异常通知

在Advice类中添加异常通知的方法,并且要在参数中添加 Exception 的对象


package cn.com.advice;

import org.aspectj.lang.ProceedingJoinPoint;

public class Advice {
    public void beforeAdvice(){
        System.out.println("前置通知");
    }
    public void afterAdvice(){
        System.out.println("后置通知");
    }

    public Object arroundAdvice(ProceedingJoinPoint p) throws Throwable {
        System.out.println("环绕前置通知");
        Object result=p.proceed();
        System.out.println("环绕后置通知");
        return result;
    }

    public void throwAdvice(Exception e){
        System.out.println("异常通知:"+e.getMessage());
    }
}

定义<aop:after-throwing>标签定指定异常通知方法

<bean id="test" class="cn.com.test.Test"/>
<bean id="before" class="cn.com.advice.Advice"></bean>   <!--配置通知所在类-->
 <aop:config>
        <!--配置切面-->
        <aop:aspect ref="before">
            <!--配置切点-->
            <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1())"/>
            <aop:before method="beforeAdvice" pointcut-ref="mypoint"></aop:before>   <!--配置前置通知-->
            <aop:after method="afterAdvice" pointcut-ref="mypoint"></aop:after>     <!--配置后置通知-->
<!--            <aop:after-returning method="afterAdvice" pointcut-ref="mypoint"></aop:after-returning>-->
            <aop:around method="arroundAdvice" pointcut-ref="mypoint"></aop:around>
            <aop:after-throwing method="throwAdvice" pointcut-ref="mypoint" throwing="e"></aop:after-throwing>
        </aop:aspect>
    </aop:config>

<aop:after-throwing>标签中要添加一个throwing属性,指明异常对象

package cn.com.test;

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

public class Test {
    public void method1(){
        Test test=null;
        test.method2();
        System.out.println("method1 ");
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Test test=ac.getBean("test",Test.class);
        test.method1();
        test.method2();
        test.method3();
    }
}

在这里插入图片描述

AspectJ多参数

切点的方法有参数,通知如何获取参数中的内容

以前置通知举例

public class Advice {
    public void beforeAdvice(String name,String sex,int age){
        System.out.println("前置通知: "+name+" "+sex+" "+age);
    }
}
Test下的method1方法

 public void method1(String name,String sex,int age){
        System.out.println("method1 "+name+" "+sex+" "+age);
    }
<bean id="test" class="cn.com.test.Test"/>
<bean id="before" class="cn.com.advice.Advice"></bean>   <!--配置通知所在类-->
 <aop:config>
        <!--配置切面-->
        <aop:aspect ref="before">
            <!--配置切点-->
            <aop:pointcut id="mypoint" expression="execution(* cn.com.test.Test.method1(String,String,int)) and args(name,sex,age) "/>
            <aop:before method="beforeAdvice" pointcut-ref="mypoint" arg-names="name,sex,age"></aop:before>   <!--配置前置通知-->
        </aop:aspect>
    </aop:config>

1.首先在execution(* )中为method1添加参数类型,严格与method1对应
2.在execution(* ) 后添加 and args()
3.在args()添加参数名称,可以随便起,但要和beforeAdvice(String name,String sex,int age)的参数名对应
4.在<aop:before>标签中添加arg-names属性,属性内容和args()中的内容对应

package cn.com.test;

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

public class Test {
    public void method1(String name,String sex,int age){
        System.out.println("method1 "+name+" "+sex+" "+age);
    }
    public void method2(){
        System.out.println("method3");
    }
    public void method3(){
        System.out.println("method4");
    }
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Test test=ac.getBean("test",Test.class);
        test.method1("kryie","男",10);
        test.method2();
        test.method3();
    }
}

在这里插入图片描述

其他通知类似,注意要一一对应

代理模式

AOP的原理基于动态代理,是代理模式的一种

比如:
明星都有经济人,活动的安排都需要先经过经纪人。安排一个商演活动,活动方要找某位明星做商业演出。但找这个明星需要先通过他的经纪人,经纪人得到消息就可以查看明星的日程是否有冲突。若没有冲突,则就通知明星,出席商业演出。商业演出结束后,明星离开。结束后,活动方和经纪人商谈后续事宜。

在这个例子中,明星就是真实对象,经纪人就是代理对象,商业演出就是抽象对象(也叫抽象功能)

可以对比AOP
经纪人得到消息就可以查看明星的日程是否有冲突,相当于前置通知,在商演前执行点某个功能
明显出席商业演出,相当于切点,执行真正的功能
结束后活动方和经纪人商谈后续事宜,相当于后置通知。在商演结束后执行点某个功能

代理设计模式的优点:
1.保护真实对象 。
保护明星
2.让真实对象职责更明确.。
明星只管唱歌,演出。活动安排交给经济人
3.扩展
对比AOP

静态代理模式

首先来看静态代理模式

package cn.com;

public interface Performance {
    public void show();
}

定义一个接口,代表一个抽象对象(抽象功能)

package cn.com;

public class Star implements Performance {
    @Override
    public void show() {
        System.out.println("明星演出");
    }
}

定义明星对象实现Performance 接口

package cn.com;

public class Agent implements Performance {
    Star star=new Star();
    @Override
    public void show() {
        System.out.println("查看日程");
        star.show();
        System.out.println("商量后续");
    }
}

定义经纪人对象实现Performance接口

package cn.com;

public class Company {
    public static void main(String[] args) {
        Agent agent=new Agent();
        agent.show();
    }
}

公司对象
在这里插入图片描述
静态代理模式就是由代理对象代理所有真实对象的功能.
并且自己编写代理类
每个代理的功能需要单独编写

静态代理设计模式的缺点: 当代理功能比较多时,代理类中方法需要写很多

动态代理模式

动态代理可以解决静态代理中要写很多代理方法的缺点

动态代理有JDK动态代理,和cglib动态代理

1.jdk动态代理

jdk 自带,不需要额外导入 jar
真实对象必须实现接口
利用反射机制.效率不高.

package cn.com;

public interface Performance {
    public void show();
}

package cn.com;

public class Star implements Performance {
    @Override
    public void show() {
        System.out.println("明星演出");
    }
}

真实对象实现接口

package cn.com;



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


public class Agent implements InvocationHandler {
    private Star star=new Star();
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("查看日程");
        Object result=method.invoke(star,args);
        System.out.println("商量后续");
        return result;
    }
}

代理对象需要实现InvocationHandler 接口
重写invoke方法

package cn.com;

import java.lang.reflect.Proxy;

public class Company {
    public static void main(String[] args) {
        Performance performance= (Performance) Proxy.newProxyInstance(Company.class.getClassLoader(),new Class[]{Performance.class},new Agent());
        performance.show();
    }
}

使用Proxy的newProxyInstance()静态方法

第一个参数:反射时使用的类加载器 因为java只有一个类加载器,所以随便哪个类的类名.class.getClassLoader()都行
第二个参数:代理对象需要实现什么接口
第三个参数:通过接口对象调用方法时,需要调用哪个类的invoke方法,即代理对象

接口对象是不能转换为真实对象的,会报异常

public static void main(String[] args) {
        Performance performance= (Performance) Proxy.newProxyInstance(Company.class.getClassLoader(),new Class[]{Performance.class},new Agent());
        performance.show();

        Star star= (Star) performance;   //转换为真实对象
    }

在这里插入图片描述

2.cglib 动态代理

cglib 是基于字节码的,它会生成真实对象的子类。而JDK动态代理是基于反射的,所以运行效率高于 JDK 动态代理.
cglib不需要实现接口,但需要导入jar包

在这里插入图片描述

package cn.com;

public class Star {
    public void show(){
        System.out.println("明星演出");
    }
}

package cn.com;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Agent implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("查看日程");
        Object result=methodProxy.invokeSuper(o,objects);
        System.out.println("商量后续");
        return result;
    }
}

代理对象实现MethodInterceptor 接口

o:生成的子类对象
method:代理的方法
objects:参数
methodProxy: 子类的代理方法(重写的方法) intercept

package cn.com;

import net.sf.cglib.proxy.Enhancer;

public class Company {
    public static void main(String[] args) {
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(Star.class);
        enhancer.setCallback(new Agent());

        Star star= (Star) enhancer.create();
        star.show();
    }
}

在这里插入图片描述

在SpringAOP中,默认是使用JDK动态代理。所以代理对象是不能转换为真实对象的。当出现 Proxy 和真实对象转换异常,可以使用标签将SpringAOP的代理模式设置为cglib动态代理
在这里插入图片描述
true:表示使用cglib动态代理
false:表示使用jdk动态代理

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值