spring-aop
Spring框架-AOP
JDK动态代理:jdk动态代理要求目标对象必须实现接口,java语言提供三个类支持Proxy Method InvocationHandler
CGLIB动态代理:生成原理是生成目标类的子类,而子类是增强过的这个子类就是代理对象,所以目标类必须可以被继承(不可以是final类)
CGLIB经常被应用于框架中,其效率是高于JDK的
CGLIB动态代理:源码详解系列(一)--cglib动态代理的使用和分析 - 子月生 - 博客园 (cnblogs.com)
注意要使用:org.springframework.cglib.proxy包
依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
public class MyTest01 {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String myString() {
return "MyTest01{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LogInterceptor implements MethodInterceptor {
//需要注意一点,这里要通过proxy.invokeSuper来调用目标类的方法,而不是使用method.invoke,
// 不然会出现栈溢出等问题。如果你非要调用method.invoke,你需要把目标类对象作为LogInterceptor的成员属性,
// 在调用method.invoke时将它作为入参,而不是使用MethodInterceptor.intercept的入参 obj,
// 但是,不推荐这么做,因为这样将无法享受到 cglib 代理类执行快的优势。
//此处的o是指代理类(子类),不是目标类(基类)。method是代理类的method,objects是参数
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(method.getName());
System.out.println("调用前");
Object result=methodProxy.invokeSuper(o,objects);
System.out.println("调用后");
return result;
}
@Test
public void test02(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(MyTest01.class);
enhancer.setCallback(new LogInterceptor());
MyTest01 myTest01=(MyTest01)enhancer.create();
myTest01.setName("geng");
myTest01.setPassword("1234");
System.out.println(myTest01.myString());
}
AOP:面向切面编程 从动态的角度考虑程序的运行过程 底层是使用了动态代理来进行实现
A Aspect:切面 给目标对象增加的功能叫做切面,一般为非业务方法
O Orient:面向
P Programming:
AOP实现框架,在Spring内部实现了aop规范,主要是在事务处理的时候进行使用,但是由于过于笨重很少使用。
aspectJ:一个专门实现AOP的开源框架,spring中也集成了aspectJ
常见术语:
spring中aspectJ实现可以:1、使用xml配置文件2、使用注解
1、@Before2、AfterReturning3、@Around4、AfterThrowing5、@After
切入点表达式:
常用的通配符:
*:0至多个任意字符
..:用在方法参数中,表示任意多个参数
用在包名后,表示当前包及其子包
+: 用在类名后表示当前类及其子类
用在接口后,表示当前接口和其实现类
依赖:
<!--aspectj框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
简单的一个实现样例:
@Aspect
@Component
public class MyAspectJ {
@Before(value = "execution(public void com.geng.service.MyService.doSome())")
public void dosome(){
System.out.println("这是前置通知");
}
}
@Service("myService")
public class MyServiceImpl implements MyService {
@Override
public void doSome() {
System.out.println("这是doSome方法");
}
}
<context:component-scan base-package="com.geng"/>
<aop:aspectj-autoproxy/>
@Test
public void test01(){
ApplicationContext applicationContext=(ApplicationContext) new ClassPathXmlApplicationContext("ApplicationContext.xml");
MyService myService=(MyService)applicationContext.getBean("myService");
myService.doSome();
}
@Before前置通知
属性:value为切入点表达式,表示切面执行的位置
特点:
1、在目标方法执行之前执行2、不会影响目标方法的执行结果
使用要求:
1、公共方法2、方法没有返回值3、方法只可以使用特定参数(JoinPoint 类型)
JoinPoint可以获取目标方法的有效信息(如果有多个参数,他必须在第一个位置处)
@Before(value = "execution(public void com.geng.service.MyService.doSome(..))")
public void dosome(JoinPoint joinPoint){
for(Object o:joinPoint.getArgs()){
System.out.println(o);
}
System.out.println(joinPoint.getKind()+" "+joinPoint.getSignature()+" "+joinPoint.getTarget()+" "+joinPoint.getThis());
//getTarget和getThis返回值相同
//会导致递归调用
/*MyService myService=(MyService)joinPoint.getThis();
myService.doSome(1,"peng");*/
System.out.println("这是前置通知");
}
@AfterReturning后置通知
除了value属性还有一个returning属性表明返回值的参数名称
特点:在目标方法执行之后执行2、可以操作目标方法的返回值
使用要求:
1、公共方法2、方法没有返回值3、方法只可以使用特定参数(JoinPoint 类型)和一个Object类型的参数表示目标方法的返回值。
@AfterReturning(value = "execution(public * com.geng.service..doSome02(..))",returning = "result")
public void doSome2(JoinPoint joinPoint,Object result){
Object temp=result;
for(Object o:joinPoint.getArgs()){
System.out.println(o);
}
if(result!=null){
String res=(String)result;
result=res.toLowerCase();
System.out.println(result==temp+" "+(String)result);
//因为String类型是不可以改变的,所以引用值已经改变了(temp!=result)
//所以目标方法的返回值并没有改变
}
@Around环绕通知
使用要求:
1、公共方法2、有一个返回值3、方法参数固定(ProceedingJoinPoint)
这个ProceedingJoinPoint参数其实是JoinPoint的子类
环绕通知其实和动态代理类似
ProceedingJoinPoint对象的proceed方法就是目标方法
@AfterThrowing异常通知
使用要求:
1、public 2、没有返回值3、方法参数只能是JoinPoint和Exception
属性:
Value:切入点表达式
Throwing:自定义变量,表示目标方法的抛出的异常对象,和方法参数名必须一致
在目标方法抛出异常时候执行,可以做目标方法的监控程序。相当于try catch finally中的catch
@After最终通知
使用要求:
1、public2、没有返回值3、方法参数只能为JoinPoint
总会执行,相当于try catch finally中的finally
@PointCut辅助注解
当使用@PointCut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名了。在其他的通知中,value属性中就可以使用这个别名,代替切入点表达式了。当然一般情况下这个使用@PointCut注解的方法是没有代码的,他只是一个工具。
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class Aspectj {
@Pointcut(value="execution(public * *..SomService.doSome(..))")
public void pointcut(){
}
@Before(value="pointcut()")
public void doLog1(){
System.out.println("这是一个前置通知");
}
@After(value="pointcut()")
public void doLog2(){
System.out.println("这是一个后置通知");
}
}
<aop:aspectj-autoproxy proxy-target-class="true" />
<!--当proxy-target-class的值被设置成true的时候,就会执行CGLIB动态代理,无论存不存在接口-->