Spring
前置通知
before
* 1、是一个公共方法 public
* 2、没有返回值
* 3、方法名称自定义
* 4、方法可以又参数,也可以没有参数
* 如果有参数,参数不是自定义的,有几个参数类型可以使用
*
* @Before: 前置通知注解
* 属性 value:是切入点表达式 表示切面的功能执行的位置
* 位置: 在方法的上面
* 特点:
* 1。在目标方法之前先执行的
* 2.不会改变方法的执行结果
*
*
大坑
Spring AOP 有两种代理方法,aspectj使用的是第二种cglib
一种是常规JDK,一种是CGLIB。
当代理对象实现了至少一个接口时,默认使用JDK动态创建代理对象;
当代理对象没有实现任何接口时,就会使用CGLIB方法。
如果实现了接口,强制转换必须用父类接口来定义
也就是说 当目标类实现了接口 就必须要强转为父类接口
如下代码
接口
package com.hgzy.ba01_before;
public interface SomeService {
void doSome(String name,Integer age);
}
目标类
package com.hgzy.ba01_before;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
//给doSome方法增添功能 在方法执行之前输出方法执行的时间
System.out.println("deSome方法执行了");
}
}
切面类
package com.hgzy.ba01_before;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Date;
@Aspect
public class MyAspect {
/**
* @Before: 前置通知注解
* 属性 value:是切入点表达式 表示切面的功能执行的位置
* 位置: 在方法的上面
* 特点:
* 1。在目标方法之前先执行的
* 2.不会改变方法的执行结果
* 3.
*/
// 做增强处理 输出当前实现
@Before(value = "execution( * com.hgzy.ba01_before.*.*(..))")
public void myBefore(JoinPoint jp){
for(Object o:jp.getArgs()){
System.out.println(o);
}
System.out.println("切面功能 在目标方法之前执行 输出当前时间"+new Date());
}
}
测试类
package com.hgzy.ba01_before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
@org.junit.Test
public void fun1(){
// Spring配置文件
String config= "ba01_before/applicationContext.xml";
// 获取容器对象
ApplicationContext ac= new ClassPathXmlApplicationContext(config);
// 从容器中获取目标类对象
SomeService service= (SomeService) ac.getBean("someService");
{SomeServiceImpl someService=(SomeServiceImpl) ac.getBean("someService");
someService.doSome("1",2);}
这样写发生异常 如下图
// 调用目标类方法 触发前置通知
service.doSome("z",1);
}
}
发生的异常如下
发生了异常 类型转换异常 我的理解是 cglib无法创建该类的代理 本人纯小白 忘大佬纠正
Spring配置文件
<?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"
xmlns:conext="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 声明目标对象-->
<bean id="someService" class="com.hgzy.ba01_before.SomeServiceImpl"/>
<bean id="some1" class="com.hgzy.ba01_before.Some"/>
<!-- 声明切面类对象-->
<bean id="myAspect" class="com.hgzy.ba01_before.MyAspect"/>
<!-- 声明自动代理生成器-->
<aop:aspectj-autoproxy/>
<!-- 声明组件扫瞄器 指注解所在的包-->
<conext:component-scan base-package="com.hgzy.ba02_AfterReturning"/>
</beans>
后置通知(可接受目标方法的返回值 )
如果返回值是字符串类型 不能修改 其他均可以
package com.hgzy.ba02_AfterReturning;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
/**
* 定义方法实现切面功能的
* 方法的定义要求
* 后置通知
* 1、是一个公共方法 public
* 2、没有返回值
* 3、方法名称自定义
*4、方法有参数 推荐是object 参数名 自定义
*/
/**
* @AfterReturning: 后置通知注解
* 属性 :1.value:是切入点表达式 表示切面的功能执行的位置.
* 2.
* 位置: 在方法的上面
* 特点:
* 1。在目标方法之后先执行的
* 2.能够获取到目标方法的返回值 可以根据这个返回值做不同处理
* 3.可以修改这个返回值
*
* returning 变量值因与后置通知参数 保持一直
*/
@AfterReturning(returning = "res",value = "execution(* *..SomeServiceImpl.do*(..))")
public void AfterReturning(Object res){
System.out.println("后置通知 在目标方法之后执行 可以获取到目标方法的返回值"+res);
// 因为 目标方法的返回参数不清楚 为了防止异常的发生 进行判断 输出对应的结果
if (res instanceof String){
res=(String)res +"字符串";
}else if (res instanceof Integer){
res=(Integer)res+1;
}else if (res instanceof Student){
res=(Student)res;
((Student) res).setAge(20);
}
System.out.println("后置通知 修改后的值:"+res);
}
}