Spring aop使用步骤及细节

穿精又带淫゛_ 提交于 2020-03-08 00:03:32

AOP使用步骤:
1)、导包

commons-logging-1.2.jar
spring-aop-4.0.0.RELEASE.jar
spring-beans-5.0.4.RELEASE.jar
spring-context-5.0.4.RELEASE.jar
spring-core-5.0.4.RELEASE.jar
spring-expression-5.0.4.RELEASE.jar

spring支持面向切面编程的包:
spring-aspects-4.0.0.RELEASE.jar 基础版
加强版的面向对象编程 (即使目标对象没有实现任何接口也能创建动态代理):
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

2)、写配置
1、将目标类和切面类(封装了通知方法(在目标方法执行前后执行的方法))加入到IOC容器中
2、还应该告诉spring到底哪个是切面类 用@Aspect
3、告诉spring,切面类里面的每一个方法,都是合适何地运行;


```java
*告诉spring每个方法都什么时候运行                               
 * * @Before:在执行目标方法之前运行                     前置通知     
 * * @After:在执行目标方法结束的时候运行                后置通知     
 * * @AfterReturning:在目标方法正常执行后运行           返回通知    
 * * @AfterThrowing:在目标方法抛出异常后运行            异常通知    
 * * @Around :环绕                                     环绕通知    
 * * */    
 * //想在执行目标方法之前运行  ;写切入点表达式    
 * //execution(方法签名)
 * @Before("execution(public int  com.ssm.impl.MyMathCalculator.*(int, int))")
 * public static void logStart( ){    
 * System.out.println("这是动态代理将要帮你执行方法。。。。");    
 * System.out.println("[xxx]方法开始执行了用的参数[xxx]");
 * }
 * //想在执行目标方法之后运行
 * @AfterReturning("execution(public int  com.ssm.impl.MyMathCalculator.*(int, int))")
 * public static void logEnd() {    
 * System.out.println("[xxx]方法执行完成 结果是:[xxx]");    
 * }
 * //想在目标方法出现异常运行
 * @AfterThrowing("execution(public int  com.ssm.impl.MyMathCalculator.*(int, int))")
 * public static void logException() {    
 * System.out.println("[xxx]方法执行出现异常 异常信息是:[xxx]");
 * }
 * //想在目标方法正常结束后运行   
 * @After("execution(public int  com.ssm.impl.MyMathCalculator.*(int, int))")
 * public static void logfinally() {   
 * System.out.println("[xxx]方法最终结束了");    
 * }

``

4、开启基于注解的AOP模式

3)、测试

1、细节一:IOC容器中保存的是组件的代理对象;
@Test    
public void test01() {        
//从ioc中拿到目标对象;  注意:如果想要用类型,一定用她的接口类型,不要用它本类        
/*细节一:com.ssm.impl.MyMathCalculator@4961f6af         
*      class com.sun.proxy.$Proxy17         
* *      Aop底层就是动态代理 容器中保存的组件就是他的代理对象 :$Proxy17。当然不是本类类型        
*  */       
*  /*Calculator bean =    ioc.getBean(Calculator.class);           
* //bean.add(8, 850);           
* //  System.out.println(Calculator.class);           
* //System.out.println(bean);            
* System.out.println(bean.getClass());
* */                       
* //获取代理对象还可以用id    注解扫描创建的bean的id默认值是类名首字母小写。          
* /* Calculator bean1 =   (Calculator)  ioc.getBean("myMathCalculator");            
* System.out.println(bean1.getClass());
* */                        
* /*         
* // 1)、有接口就是非本类类型        
* Calculator bean =    ioc.getBean(MyMathCalculator.class);       
*  注意:MyMathCalculator 是本类 获取他时容器会为他创建代理对象 即$Proxy17 容器中存在的是它的代理对象 并没有本类 ;       
*  */           
* // 2)、没有接口就是本类类型        
* //通过Id获取对象        
* MyMathCalculator bean =    (MyMathCalculator)  ioc.getBean("myMathCalculator");        
* bean.add(2, 5);        
* //通过BySpringCGLIB创建的一个代理对象        
* System.out.println(bean.getClass());        
* //通过类型获取对象        
* MyMathCalculator bean1 =    ioc.getBean(MyMathCalculator.class);        
* bean.add(2, 5);       
*  System.out.println(bean1.getClass());
* }
2、细节二:切入点表达式写法(通配符);

切入点表达式的写法:
固定格式:execution(访问权限符 返回值类型 方法全类名(参数表))
通配符:"":
1)、匹配一个或多个字符 execution(public int com.ssm.impl.MyMath
.(int, int))
2)、匹配任意一个参数
3)、只能匹配一层路径
4)、权限位置不能写
不写即为任意权限;

 @Before("execution(public int  com.ssm.impl.MyMathCalculator.*(int, *))")

通配符: …:
1)、匹配任意多个参数,任意类型参数
2)、匹配任意多层路径

@Before("execution(public int  com..MyMathCalculator.*(int, *))")//com下任意层路径下的MyMathCalculator
3、细节三:通知方法的执行顺序

try{
@Before
mothod.invoke(obj,args);
@AfterReturning
}catch{
@AfterThrowing
}finally{
@After
}
正常执行:@Before(前置通知)=@After(后置通知)=@AfterReturning(正常返回)
异常执行:@Before(前置通知)=@After(后置通知)=@AfterThrowing(异常返回)

4、细节四、五:我们可以在通知方法运行的时候,拿到目标方法的详细信息,

1)、只需要为通知方法的参数列表写一个参数:
JoinPoint joinPoint :封装了当前目标方法的详细信息
2)、告诉spring这个throwing是用来接受返回值的
throwing=“exception”
告诉spring这个result是用来接受返回值的
returning=“result”
3)、Exception exception:指定通知方法可以接收哪些异常;
Object result:指定通知方法可以接收哪些结果;

5、细节六:spring对通知方法的约束,(参数表一定正确)

spring对通知方法不严格:以下两种情况都能正常运行
唯一要求就是方法的参数列表不能乱写:
通知方法是spring利用反射调用的,每次方法调用逗得确定这个方法的参数表的值;
参数表上的每个参数,spring都得知道;
不知道的参数一定要告诉spring

public int logEnd(JoinPoint joinPoint ) {    
Signature signature =  joinPoint.getSignature();    
String name = signature.getName();    
System.out.println("["+name+"]方法最终结束了");    
return 0;
}
private int logEnd(JoinPoint joinPoint ) {    
Signature signature =  joinPoint.getSignature();    
String name = signature.getName();    
System.out.println("["+name+"]方法最终结束了");    
return 0;
}
6、细节七:抽取可重用的切入点表达式;

1、随声明一个没有实现的返回void的空方法
2、给方法上标注@pointcut注解

@After("hahaMyPoint()")
public static void logEnd(JoinPoint joinPoint )  {    
Signature signature =  joinPoint.getSignature();    
String name = signature.getName();    
ystem.out.println("["+name+"]方法最终结束了");
}
@Pointcut("execution(public int  com.ssm.impl.MyMathCalculator.*(int, int))")
public  void hahaMyPoint(){    }
7、细节八:环绕通知;

@Around :环绕通知是spring中最强大的通知
实际就是手写的动态代理
try{
前置通知
mothod.invoke(obj,args); //目标方法执不执行环绕通知说的算
返回通知
}catch{
异常通知
}finally{
后置通知 }
四合一就是环绕通知;
环绕通知中有一个参数:ProceedingJoinPoint pjp

@Around("hahaMyPoint()")
public Object myAround(ProceedingJoinPoint pjp)  throws Throwable{    
Signature signature = pjp.getSignature();    
String name = signature.getName();    
Object[] args = pjp.getArgs();    
Object proceed=null;    
try {        
System.out.println("环绕前置通知["+name+"]方法开始执行了用的参数["+Arrays.asList(args)+"]");
            
            //就是利用反射调用目标方法即可  就是method.invoke(obj,args)        
            proceed = pjp.proceed(args);        
            System.out.println("环绕返回通知["+name+"]方法执行完成 结果是:["+proceed+"]");    
            } catch (Exception e) {        
            System.out.println("环绕异常通知["+name+"]方法执行出现异常 异常信息是:["+e+"]");    
            }finally{        
            System.out.println("环绕后置通知["+name+"]方法最终结束了");   
             }   
             //反射调用后的返回值也一要返回出去;    
             return proceed;
             }
8、细节九:环绕通知的执行顺序;

环绕通知优先于普通通知执行
执行顺序:
普通前置
{
try{
环绕前置
环绕执行:目标方法执行
环绕正常返回
}catch{
环绕出现异常
}finally{
环绕后置
}
}
普通后置 普通返回/异常
新的顺序: (环绕前置普通前置)目标方法执行环绕正常返回/出现异常环绕后置普通后置普通返回/异常
(环绕前置==普通前置) 这俩顺序随机无所谓 一旦目标方法执行完毕 需严格按照环绕在前 普通在后

9、细节十:多切面的运行顺序;

1)、字母顺序会决定顺序;
2)、 @order(num): num越大优先级越高

切面图:没有@Around 时执行顺序
在这里插入图片描述

有@Around 时执行顺序

在这里插入图片描述

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!