(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得
业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录例子说明 AOP
(1)有两种情况动态代理
第一种 有接口情况,使用 JDK 动态代理,创建接口实现类代理对象,增强类的方法.
使用 Proxy 类里面的方法创建代理对象
方法有三个参数:
第一参数,类加载器
第二参数,增强方法所在类的接口,支持多个接口
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
UserDao
package com.limi.dao;
public interface UserDao {
void say();
}
UserDaoImp
package com.limi.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImp implements UserDao{
@Override
public void say(){
System.out.println("UserDaoImp say....");
}
}
UserDaoProxy, 用于编写增强逻辑
package com.limi.dao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//创建代理对象代码
public class UserDaoProxy implements InvocationHandler {
private Object obj;
//把需要创建代理的对象传递进来
public UserDaoProxy(Object obj){
this.obj = obj;
}
//增强的逻辑
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//方法之前执行
System.out.println("方法之前执行...");
//被增强方法的执行
Object res = method.invoke(obj, objects);
//方法之后执行
System.out.println("方法之后执行...");
return res;
}
}
测试类MyTest
package com.limi.test;
import com.limi.dao.UserDao;
import com.limi.dao.UserDaoImp;
import com.limi.dao.UserDaoProxy;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class MyTest {
@Test
public void test1(){
//需要增强方法的接口
Class interfaces[] = {UserDao.class};
//需要增强方法的对象
UserDaoImp userDaoImp = new UserDaoImp();
//获取方法被增强的对象
UserDao userDao = (UserDao)Proxy.newProxyInstance(MyTest.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoImp));
//执行被增强的方法
userDao.say();
}
}
测试结果
第二种 没有接口情况,使用 CGLIB 动态代理,创建子类的代理对象,增强类的方法.
3.AOP术语1.连接点
类里面哪些可以加入增强逻辑的方法, 这些方法称为连接点
2.切入点
实际被你增强了的方法
3.通知
实际增强的逻辑部分称为通知, 通知有多种类型:
前置通知
后置通知
环绕通知
异常通知
最终通知
4.切面
是一个动作, 把通知应用到切入点的过程
1、Spring 框架一般都是基于 AspectJ 实现 AOP 操作,
AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作.
2、基于 AspectJ 实现 AOP 操作
有两种方式:
(1)基于 xml 配置文件实现
(2)基于注解方式实现
3、在项目工程里面引入 AOP 相关依赖
4、切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )
举例 1:对 com.limi.dao.BookDao 类里面的 add 进行增强 execution(* com.limi.dao.BookDao.add(…))
举例 2:对 com.limi.dao.BookDao 类里面的所有的方法进行增强 execution(* com.limi.dao.BookDao.* (…))
举例 3:对 com.limi.dao 包里面所有类,类里面所有方法进行增强 execution(* com.limi.dao.. (…))
5.AOP代码实现1.基于完全注解方式
SpringConfig
package com.limi.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration//配置类,代替xml配置文件
@ComponentScan(basePackages = {"com.limi"})//
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启 Aspect 生成代理对象
public class SpringConfig{
}
UserDao
package com.limi.dao;
import org.springframework.stereotype.Component;
//需要增强的类
@Component
public class UserDao {
public void say(){
System.out.println("UserDao say...");
}
}
UserDaoProxy
package com.limi.dao;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//用于加入增强逻辑的类
@Component
@Aspect //生成代理对象
public class UserDaoProxy {
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "execution(* com.limi.dao.UserDao.say(..))")
public void before(){
System.out.println("before...");
}
//后置通知(返回通知), 当有异常出现不执行
@AfterReturning(value = "execution(* com.limi.dao.UserDao.say(..))")
public void afterReturning(){
System.out.println("afterReturning...");
}
//最终通知
@After(value = "execution(* com.limi.dao.UserDao.say(..))")
public void after(){
System.out.println("after...");
}
//异常通知
@AfterThrowing(value = "execution(* com.limi.dao.UserDao.say(..))")
public void afterThrowing(){
System.out.println("afterThrowing...");
}
//环绕通知
@Around(value = "execution(* com.limi.dao.UserDao.say(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws
Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行, 这里也就是执行UserDao里面的say()方法
//如果使用了环绕通知不执行 proceedingJoinPoint.proceed(); 则被增强的原方法内容不会执行, 只执行其他增强逻辑
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
测试类MyTest
package com.limi.test;
import com.limi.config.SpringConfig;
import com.limi.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
@Test
public void test1(){
//1.加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//2.获取配置的对象, 参数1:bean的id值, 参数2: 类名.class
UserDao userService = context.getBean("userDao", UserDao.class);
//3.使用对象
userService.say();
}
}
测试结果
①注意:@AfterReturning标注的方法当被增强的方法有异常出现时不执行, 执行的是@AfterThrowing标注的方法
UserDao的say()方法内加int a = 10/0; 测试异常情况
测试结果, 可以看到@AfterReturning标注的方法未执行, 执行的是@AfterThrowing标注的方法
②小技巧:使用@Pointcut提取公共切入点
UserDaoProxy
package com.limi.dao;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//用于加入增强逻辑的类
@Component
@Aspect //生成代理对象
public class UserDaoProxy {
//提取公共切入点,可减少代码量
@Pointcut(value = "execution(* com.limi.dao.UserDao.say(..))")
public void pointCut(){
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointCut()")
public void before(){
System.out.println("before...");
}
//后置通知(返回通知), 当有异常出现不执行
@AfterReturning(value = "pointCut()")
public void afterReturning(){
System.out.println("afterReturning...");
}
//最终通知
@After(value = "pointCut()")
public void after(){
System.out.println("after...");
}
//异常通知
@AfterThrowing(value = "pointCut()")
public void afterThrowing(){
System.out.println("afterThrowing...");
}
//环绕通知
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws
Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行, 这里也就是执行UserDao里面的say()方法
//如果使用了环绕通知不执行 proceedingJoinPoint.proceed(); 则被增强的原方法内容不会执行, 只执行其他增强逻辑
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
测试结果
③多个增强类对同一个方法进行增强
(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高, 同类型的方法, 优先级高的类的方法先执行.
第一个增强类:UserDaoProxy
package com.limi.dao;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
//用于加入增强逻辑的类
@Component
@Aspect //生成代理对象
@Order(1)
public class UserDaoProxy {
//提取公共切入点,可减少代码量
@Pointcut(value = "execution(* com.limi.dao.UserDao.say(..))")
public void pointCut(){
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointCut()")
public void before(){
System.out.println("before...");
}
//后置通知(返回通知), 当有异常出现不执行
@AfterReturning(value = "pointCut()")
public void afterReturning(){
System.out.println("afterReturning...");
}
//最终通知
@After(value = "pointCut()")
public void after(){
System.out.println("after...");
}
//异常通知
@AfterThrowing(value = "pointCut()")
public void afterThrowing(){
System.out.println("afterThrowing...");
}
//环绕通知
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws
Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行, 这里也就是执行UserDao里面的say()方法
//如果使用了环绕通知不执行 proceedingJoinPoint.proceed(); 则被增强的原方法内容不会执行, 只执行其他增强逻辑
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
第二个增强类:UserDaoProxy2
package com.limi.dao;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
//用于加入增强逻辑的类
@Component
@Aspect //生成代理对象
@Order(2)
public class UserDaoProxy2 {
//提取公共切入点,可减少代码量
@Pointcut(value = "execution(* com.limi.dao.UserDao.say(..))")
public void pointCut(){
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointCut()")
public void before(){
System.out.println("UserDaoProxy2 before...");
}
}
测试结果, 可以看到同样是前置通知before,由于UserDaoProxy2的优先级更低, 所以后执行.



