Spring5------------AOP
AOP(Aspect Oriented Programming):面向切面编程
作用:不改变源代码的情况下增强功能
AOP原理:动态代理

两种情况的动态代理
(1)有接口的情况,使用JDK动态代理
创建接口实现类代理对象,增强类的方法

(2)没有接口的情况:使用CGLIB动态代理
创建当前类子类的代理对象,增强类的方法

JDK动态代理
使用Proxy类里面的方法创建代理对象

调用newProxyInstance方法

方法有三个参数:
- 类加载器
- 被代理类实现的接口(支持多个接口)
- 调用处理器(实现InvocationHandler接口,创建代理对象,写增强方法)
package com.tang.spring;
public interface UserDao {
int add(int a,int b);
String update(String id);
}
package com.tang.spring;
import org.springframework.stereotype.Repository;
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
System.out.println("add方法执行");
return a+b;
}
@Override
public String update(String id) {
System.out.println("update方法执行");
return id;
}
}
package com.tang.spring;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
//创建接口实现类代理对象
UserDao userDao=(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(new UserDaoImpl()));
int sum=userDao.add(2, 3);
System.out.println("sum:"+sum);
}
}
class UserDaoProxy implements InvocationHandler{
private Object object;
//把被代理对象传进来,这里的被代理对象是UserDao的实现类,即UserDaoImpl
//通过有参构造方法传递进来
public UserDaoProxy(Object object){
this.object=object;
}
//增强的逻辑代码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行————"+method.getName()+"————传递的参数:"+ Arrays.toString(args));
//被增强的方法执行
Object result=method.invoke(object, args);
//方法之后
System.out.println("方法执行之后————"+object);
return result;
}
}
AOP术语
(1)连接点:类里面可以被增强的方法称为连接点
(2)切入点:实际真正被增强的方法称为切入点
(3)通知(增强):实际增强的逻辑代码称为通知(增强)
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
(4)切面:把通知(增强)应用到切入点 的过程
AspectJ
Spring框架一般都基于AspectJ实现AOP操作
什么是AspectJ :AspectJ不是Spring组成部分,是一个独立的AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
引入依赖

切入点表达式
切入点表达式作用:知道对哪个类里面的哪个方法进行增强
语法结构:excution(访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表))
访问修饰符可省略,默认为public
表示当前包及其子包*..
..表示有无参数均可,有参数时可以是任意类型
1.最全的写法
拦截返回void,指定类的指定方法,参数必须有两个:int、String
execution(public void com.sunny.service.Impl.AccountServiceImpl.save(int,java.lang.String))
2.省略访问修饰符,返回值任意的指定类的save方法,无参数
execution(* com.sunny.service.Impl.AccountServiceImpl.save())
3.拦截com包下所有的类、以及其子包下所有的类的save()方法
execution(void com..*.save()) 包名与类名或方法名称都可以使用*
4.拦截save()方法/拦截所有方法
execution(* save()) 拦截save()
execution(* *()) 拦截所有方法
5.不拦截save()方法
!execution(* save())
not execution(* save()) 注意not前面要有空格
6.拦截save()方法或者update()方法
execution(* save()) || execution(* update()))
execution(* save()) or execution(* update()))
7.拦截所有方法,参数任意,但必须有参数
execution(* *(*))
8.拦截所有方法,参数任意,参数可有可无
execution(* *(..))
9.对IOC容器中以Service结尾的类,生成代理对象
bean(*Service)
10.最常用
execution(* com.sunny..*ServiceImpl.*(..))
表示com.sunny包及其子包下所有的以ServiceImpl结尾的类生成代理对象
基于xml配置文件实现
<!--创建对象-->
<bean id="user" class="javax.jws.soap.SOAPBinding$Use"></bean>
<bean id="userProxy" class="com.tang.spring.UserProxy"></bean>
<!--配置AOP增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.tang.spring.User.add(..))"/>
<!--配置切面-->
<aop:aspect ref="userProxy">
<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
基于注解方式实现
通知的配置
第一步:在配置文件中开启注解扫描
第二步:使用注解创建对象
第三步:在增强类上添加@Aspect注解
第四步:在配置文件中开启生成代理对象
package com.tang.spring;
import org.springframework.stereotype.Component;
//被增强的类
@Component(value = "user")
public class User {
public void add(){
System.out.println("add..........");
}
}
package com.tang.spring;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
//通知顺序:环绕前——前置——AfterReturning——后置——环绕后
//前置通知
//@Before表示作为前置通知
@Before(value = "execution(* com.tang.spring.User.add(..))")
public void before(){
System.out.println("before.........");
}
@After(value = "execution(* com.tang.spring.User.add(..))")
public void after(){
System.out.println("after..........");
}
@AfterReturning(value = "execution(* com.tang.spring.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning");
}
//异常通知
@AfterThrowing(value = "execution(* com.tang.spring.User.add(..))")
public void afterThrowing(){
}
//add方法执行之前和之后都会执行
//环绕通知
@Around(value = "execution(* com.tang.spring.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("before around");
//执行被增强的方法
proceedingJoinPoint.proceed();
System.out.println("after around");
}
}
相同切入点抽取
package com.tang.spring;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
//通知顺序:环绕前——前置——AfterReturning——后置——环绕后
//相同切入点抽取
@Pointcut(value = "execution(* com.tang.spring.User.add(..))")
public void point(){
}
//前置通知
//@Before表示作为前置通知
@Before(value = "point()")
public void before(){
System.out.println("before.........");
}
//后置通知
@After(value = "point()")
public void after(){
System.out.println("after..........");
}
@AfterReturning(value = "point()")
public void afterReturning(){
System.out.println("afterReturning");
}
//异常通知
@AfterThrowing(value = "point()")
public void afterThrowing(){
}
//add方法执行之前和之后都会执行
//环绕通知
@Around(value = "point()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("before around");
//执行被增强的方法
proceedingJoinPoint.proceed();
System.out.println("after around");
}
}
多个增强类对同一个方法进行增强,设置优先级
在增强类上面增加注解@Order(数字),数字从开始,数字越小优先级越高
例如:@Order(1) 优先级大于 @Order(2)
完全注解开发,创建配置类,不需要xml配置文件
package com.tang.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration //指定当前类作为配置类,替代xml文件
@ComponentScan(basePackages = {"com.tang.spring"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class MyConfig {
}
浙公网安备 33010602011771号