Spring 两大特性 :IOC和AOP,在Spring5 学习笔记1中已经对IOC有了详细的记录,此篇学习笔记主要记录AOP
概念
1、什么是AOP
(1)面相切面编程:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率
(2)通俗描述:不修改源代码的前提,在原来的业务逻辑上进行功能增强。
底层原理
1、AOP底层是通过使用动态代理来实现的
(1)动态代理分成两种情况
第一种 JDK动态代理,主要是用来处理有实现接口的情况
- 创建接口实现类代理对象,增强类的方法
调用newInstanceProxy方法
参数说明
loader:类加载器
interfaces:增强方法所在的类实现的接口,支持多个接口
hadnler:实现这个接口InvocationHandler,创建代理对象,实现方法增强
举例说明
// 创建接口
public interface UserService {
public void add();
}
// 创建实现类
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("UserServiceImpl....add");
}
}
// 创建代理类
public class UserProxy implements InvocationHandler {
private Object target;
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("在干嘛呢。。。" + t.getClass().getName() + " ------ "+ method.getName());
Object invoke = method.invoke(t, objects);
System.out.println("好的吧。。。。");
return invoke;
}
public void setTarget(Object target) {
this.target = target;
}
}
// 创建测试类
public class TestAop {
@Test
public void test() {
UserProxy userProxy = new UserProxy();
userProxy.setTarget(new UserServiceImpl());
UserService o = (UserService)Proxy.newProxyInstance(this.getClass().getClassLoader(), UserServiceImpl.class.getInterfaces(), userProxy);
o.add();
}
}
输出结果
第二种 CGLIB动态代理,主要是用来处理没有实现接口的情况
- 创建子类的代理对象,增强类的方法
举例说明
// 创建被代理类
public class Student {
public void read() {
System.out.println("读书呢。。。。");
}
}
// 创建代理类
public class StudentProxy<T> implements MethodInterceptor {
private T t;
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("在干嘛呢。。。" + t.getClass().getName() + " ------ "+ method.getName());
Object invoke = method.invoke(t, objects);
System.out.println("好的吧。。。。");
return invoke;
}
public T getT(T t) {
this.t = t;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(t.getClass());
enhancer.setCallback(this);
T o = (T)enhancer.create();
return o;
}
}
// 创建测试类
public class TestAop {
@Test
public void test2() {
StudentProxy<Student> studentStudentProxy = new StudentProxy<>();
Student t = studentStudentProxy.getT(new Student());
t.read();
}
}
测试结果
AOP术语
(1)连接点
类里面可以被增强的方法被称为连接点
(2)切入点
类里面实际被增强的方法被称为切入点
(3)通知(增强)
[1]实际增强的逻辑部分称为通知(增强)
[2]通知分为多种类型
- 前置通知
- 后置通知
- 环绕通知(方法前后都会执行)
- 异常通知
- 最终通知(类似于finally,不管有不有异常都会执行)
(4)切面
[1]把通知应用到切入点的过程称为切面
准备
1、Spring框架一般都是基于AspectJ实现AOP操作
- AspectJ不是Spring组成部分,是一个独立的AOP框架,一般把AspectJ和Spring组合使用,进行AOP操作
2、基于AspectJ实现AOP操作
(1)基于xml配置文件实现
(2)基于注解方式实现
3、引入相关依赖
4、切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个类方法进行增强
(2)语法结构
execution([权限修饰符][返回类型][类全路径][增强的方法])
例1:对com.company.spring7.BookDao类中的add方法增强
execution(* com.company.spring6.BookDao.add(…))
例2:对com.company.spring7.BookDao类中的所有方法增强
execution(* com.company.spring6.BookDao.*(…))
AspectJ注解
1、创建一个被增强的类
@Service("bookDao")
public class BookDao {
public void add() {
System.out.println("add.....");
}
}
2、创建aop类
@Component // 注册为一个Bean
@Aspect // 表明这是一个aop代理类
public class BookProxy {
// 定义切入点,这样做的好处是不用在每个通知注解中都再次定义execution
@Pointcut(value = "execution(* com.company.base.spring7.aopanno.BookDao.add(..))")
public void advice() {
}
// 最终通知,不管是否发生异常都会执行
@After("advice()")
public void after(){
System.out.println("最终增强啦");
}
// 前置通知,在方法执行前执行
@Before("advice()")
public void before() {
System.out.println("前置增强啦");
}
// 环绕通知,如果无参的情况下只会在方法执行之后执行
// 有ProceedingJoinPoint参数的情况,可以在ProceedingJoinPoint的proceed方法执行前后进行增强
@Around("advice()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕增强啦1");
joinPoint.proceed();
System.out.println("环绕增强啦2");
}
// 异常通知,只会在发生异常的情况才会执行
@AfterThrowing("advice()")
public void afterThrowing() {
System.out.println("异常增强啦");
}
// 后置通知-也叫返回结果通知,方法正常执行完成之后才会执行
@AfterReturning("advice()")
public void afterReturning() {
System.out.println("后置增强啦");
}
}
3、创建注解配置类
@Configuration// 表明这是一个配置类
@ComponentScan(basePackages = {"com.company.base.spring7.aopanno"}) // 配置需要扫描的包
@EnableAspectJAutoProxy // 自动开启aop代理
public class SpringConfig {
}
4、执行测试
public class TestSpring7 {
@Test
public void test() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = context.getBean("bookDao", BookDao.class);
bookDao.add();
}
}
5、多个增强类的情况下,定义优先级
(1)添加注解@Order(数字类型值),值越小,优先级越高
AspectJ配置文件
1、创建UserDao类
public class UserDao {
public void speak() {
System.out.println("我在说话啦");
}
}
2、创建aop代理类
public class UserProxy {
public void before() {
System.out.println("你能说话吗");
}
}
3、引入aop命名空间
4、配置xml信息
5、测试结果