其他分享
首页 > 其他分享> > 第十三章、Spring动态代理详解

第十三章、Spring动态代理详解

作者:互联网

在这里插入图片描述

1.额外功能详解

1.MethodBeforeAdvice分析

1. MethodBeforeAdvice接⼝作⽤:额外功能运⾏在原始⽅法执⾏之前,进⾏额外功能操作。
public class Before1 implements MethodBeforeAdvice {
 /*
 作⽤:需要把运⾏在原始⽅法执⾏之前运⾏的额外功能,书写在before⽅法中
 before方法中的三个参数:
 Method: 额外功能所增加给的那个原始⽅法
 login⽅法
 register⽅法
 showOrder⽅法
 Object[]: 额外功能所增加给的那个原始⽅法的参数,和method一一对应。eg:method如果是login(String name,String password)方法,那么Object[]:String name,String password。
 Object: 额外功能所增加给的那个原始对象 UserServiceImpl
 */
 @Override
 public void before(Method method, Object[] args, Object target) throws Throwable {
 System.out.println("-----new method before advice log------");
 }
} 

2. before⽅法的3个参数在实战中,该如何使⽤。
 before⽅法的参数,在实战中,会根据需要进⾏使⽤,不⼀定都会⽤到,也有可能都不⽤。

2.MethodInterceptor(⽅法拦截器)

methodinterceptor接⼝:额外功能可以根据需要运⾏在原始⽅法执⾏ 前、后、或前后都有。

public class Arround implements MethodInterceptor {
    /*
    invoke方法的作用:额外功能书写在invoke方法中
                    额外功能 :运行在原始方法之前、之后、之前之后。
     确定一件事:原始方法怎么运行
     参数:MethodInvocation :额外功能所增加给的那个原始方法。类似before方法的Method
     invocation.proceed() 代表原始方法开始运行。
     额外功能写在invocation.proceed();之前就是在原始方法之前运行额外功能。
     额外功能写在invocation.proceed();之后就是在原始方法之后运行额外功能。
      
      返回值:Object:原始方法的返回值
      通过invocation.proceed()的运行进而获取到原始方法的返回值。
      Object ret = invocation.proceed();
     */

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("额外功能运行在原始方法之前。");
        Object ret = invocation.proceed();
        System.out.println("额外功能运行在原始方法之后。");
        return ret;
    }
}
什么样的额外功能 运⾏在原始⽅法执⾏之前,之后都要添加:事务
    
额外功能运⾏在原始⽅法抛出异常的时候    
public Object invoke(MethodInvocation invocation) throws Throwable {
 Object ret = null;
 try {
 ret = invocation.proceed(); //原始方法抛出异常
 } catch (Throwable throwable) {//捕获异常
 System.out.println("-----原始⽅法抛出异常 执⾏的额外功能 ----");
 throwable.printStackTrace();
 }
 return ret; }

MethodInterceptor影响原始⽅法的返回值

原始⽅法的返回值,直接作为invoke⽅法的返回值返回,MethodInterceptor不会影响原始⽅法的返回值。
      public  boolean login(String name,String password){
        System.out.println("UserServiceImpl.login");
        return  true;
    }
    eg:  public Object invoke(MethodInvocation invocation) throws Throwable { 		
        System.out.println("------log-----");
        Object ret = invocation.proceed();
        return ret; //返回的ret还是true
    }
MethodInterceptor影响原始⽅法的返回值如何做:
Invoke⽅法的返回值,不要直接返回原始⽅法的运⾏结果即可。
public Object invoke(MethodInvocation invocation) throws Throwable {
 System.out.println("------log-----");
 Object ret =invocation.proceed();
 return false; //这里返回给原始方法的返回值是false(一般不改)
}

2.切入点详解

切⼊点决定额外功能加⼊位置(⽅法) 
<aop:pointcut id="pc" expression="execution(* *(..))"/>
exection(* *(..)) ---> 匹配了所有⽅法

1. execution() 切⼊点函数
2. * *(..) 切⼊点表达式

2.1 切入点表达式

1.方法切入点表达式

在这里插入图片描述

修饰符 返回值 方法名 参数
    
* *(..) --> 所有⽅法
    
第一个* ---> 修饰符 返回值
第二个* ---> ⽅法名
()---> 参数表
..---> 对于参数没有要求 (参数有没有,参数有⼏个都⾏,参数是什么类型的都⾏) 

eg:定义login⽅法作为切⼊点
* login(..)

定义register register作为切⼊点
* register(..)

* login(String,String)

eg:定义login⽅法且login⽅法有两个字符串类型的参数 作为切⼊点
##注意:⾮java.lang 包中的类型,必须要写全限定名
* register(com.baizhiedu.proxy.User)

 ..可以和具体的参数类型连⽤
* login(String,..) 可以匹配第一个参数是String,后面的参数没有要求。
可以匹配如下方法:
login(String),login(String,String),login(String,com.baizhiedu.proxy.User)

上面所讲解的方法切入点表达式不精准。
eg:在两个包(a,b)中有相同方法名和参数列表的方法login,而我只想要匹配其中一个包的方法。login(String,String)会把两个包的login方法都匹配到。

2.2精准⽅法切⼊点限定

在这里插入图片描述

修饰符 返回值 	包.类.⽅法(参数)
    * 			com.baizhiedu.proxy.UserServiceImpl.login(..)
    * 			com.baizhiedu.proxy.UserServiceImpl.login(String,String)

2.3 类切⼊点

在这里插入图片描述

指定特定类作为切⼊点(额外功能加⼊的位置),那么这个类中的所有⽅法,都会加上对应的额外功能
语法1:
    #类中的所有⽅法加⼊了额外功能
* com.baizhiedu.proxy.UserServiceImpl.*(..)
    
语法2:
    #忽略包
1. 类只存在⼀级包 
com.UserServiceImpl(此时类UserServiceImpl直接在com包下)
* *.UserServiceImpl.*(..)
2. 类存在多级包 
com.baizhiedu.proxy.UserServiceImpl(此时类UserServiceImpl在com.baizhiedu.proxy多级包下)
* *..UserServiceImpl.*(..)    --- 多加一个点

2.4包切入点表达式(实战更多)

指定包作为额外功能加⼊的位置,那么包中的所有在配置文件中创建对象的类 以及类中的所有⽅法都会加⼊额外的功能。
语法1:
    #切⼊点包中的所有类,必须在proxy包中,不能在proxy包的⼦包中
* com.baizhiedu.proxy.*.*(..)
语法2:
    #切⼊点当前包及其⼦包都⽣效(在当前包后面多加一个点)
* com.baizhiedu.proxy..*.*(..)

2.5切⼊点函数

切⼊点函数:⽤于执⾏切⼊点表达式

1.execution

1. 最为重要的切⼊点函数,功能最全。
2. 执⾏⽅法切⼊点表达式、类切⼊点表达式、包切⼊点表达式
3. 弊端:execution执⾏切⼊点表达式书写麻烦
 eg:execution(* com.baizhiedu.proxy..*.*(..))
 
4. 注意:其他的切⼊点函数 简化了execution书写复杂度,功能上完全⼀致

2.切入点函数:args

1. 作⽤:主要⽤于函数(⽅法) 参数的匹配
2. 切⼊点:⽅法参数必须得是2个字符串类型的参数:不管你是哪个包下哪个类下的 只关心参数
  		 execution(* *(String,String)) == args(String,String)

3.切入点函数:within

	# 作⽤:主要⽤于进⾏类、包切⼊点表达式的匹配 (把方法和参数取消了)
切⼊点:UserServiceImpl这个类:
1.  execution(* *..UserServiceImpl.*(..)) == within(*..UserServiceImpl)
切⼊点:proxy这个包:
2. execution(* com.baizhiedu.proxy..*.*(..)) ==  within(com.baizhiedu.proxy..* )

4.@annotation

作⽤:为具有特殊注解的⽅法加⼊额外功能  
<aop:pointcut id="pc" expression="@annotation(com.baizhiedu.Log)"/>

在这里插入图片描述

2.6切⼊点函数的逻辑运算

指的是 整合多个切⼊点函数⼀起配合⼯作,进⽽完成更为复杂的需求

and:与操作
案例:login方法同时 参数是2个字符串的方法
1. execution(* login(String,String))  ==
2. execution(* login(..)) and args(String,String)
    3.注意:与操作不能⽤于同种类型的切⼊点函数;
    	eg:execution(* login(..)) and execution(* register(..))
    		这种写法错误,因为不存在既叫login又叫register的方法。

or:或操作
案例:register⽅法 和 login⽅法作为切⼊点
    execution(* login(..)) or execution(* register(..))

标签:额外,String,..,Spring,Object,详解,proxy,第十三章,login
来源: https://blog.csdn.net/dougongzi/article/details/122699997