Spring的IOC和AOP

99 阅读6分钟

Spring的IOC和AOP

IOC和DI

  • IoC:Inverse of Control(控制反转):将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理,即由 Spring 容器负责创建 Bean(即 Java 对象)
  • DI:Dependency Injection(依赖注入):Spring 容器管理容器中 Bean 之间依赖关系的方法

IoC 相关注解

  • 标注 Bean 类,指定该 Java 类作为 Spring Bean,Bean 实例的名称默认是 Bean 类的首字母小写,其它部分不变@Component("bean 的 id") // 缺省:首字母小写的 Bean 类名@Repository:在数据访问层(dao 层)使用@Service:在业务逻辑层(service 层)使用@Controller:在展现层(MVC→Spring MVC)使用@Primary:@Autowired 自动装配找到多个匹配的 Bean时,首选该 Bean
  • 指定 Bean 的作用域,如 @Scope("prototype")
  • 指定 Bean 的加载顺序,如 @Order(1)
  • 标注方法@PostConstruct,指定 Bean 的初始化方法@PreDestroy,指定 Bean 销毁之前的方法

DI 相关注解(配置依赖)

  • @Autowired 自动装配

    • 可修饰实例变量、setter 方法、普通方法(方法的参数可以有多个)和构造器等
    • 自动搜索容器中类型匹配的 Bean 实例
    • 匹配顺序(byType 自动装配策略):依赖对象的类型或该类型的实现类、依赖对象的名称
    • 若找不到或找到多个 Bean 则抛出异常,可设置 @Autowired(required=false)
  • @Qualifier("指定 Bean 的 id") 精确装配

    • 可修饰实例变量、方法的形参
    • 若找不到则不注入
  • @Resource(name="指定 Bean 的 id")

    • JavaEE 的注解
    • 可修饰实例变量或 setter 方法
    • 默认匹配顺序:对象的名称、对象的类型(如果 name 属性一旦指定,就只会按照名称进行装配)
  • @Value:属性占位符需要放到“${ … }”之中,SpEL 表达式要放到“#{ … }”之中

Aop

  • AOP(Aspect Orient Programming),面向切面编程,将程序运行过程分解成各个切面

  • AOP 的作用:为系统中业务组件的多个业务方法添加某种通用功能(在执行目标方法之前、之后插入一些通用处理)

  • AOP 的过程:把业务方法中与业务无关的、却为业务模块所共同调用的操作抽离到不同的对象的方法中,最后使用代理的方式组合起来

  • 使用场景:日志、用户鉴权、全局性异常处理、性能监控、事务处理等

  • 术语:

    • 切面(Aspect):用于组织多个 Advice,Advice 放在切面中定义,在实际应用中通常是一个存放通用功能实现的普通 Java 类,如日志切面、权限切面、事务切面等
    • 增强处理(Advice):AOP 框架在特定的切入点执行的增强处理,处理有 around、before 和 after 等类型,在实际应用中通常是切面类中的一个方法
    • 连接点(Joinpoint):程序在运行过程中能够插入切面的点,如方法的调用、异常的抛出或成员变量的访问和更新等(Spring AOP 只支持将方法调用作为连接点)
    • 切入点(Pointcut):可以插入增强处理的连接点,当某个连接点满足指定要求(由切入点的正则表达式来定义)时,该连接点将被添加增强处理,该连接点也就变成了切入点
    • 织入(Weaving):将增强添加到目标对象(Target)中,并创建一个被增强的对象——AOP 代理(Proxy)的过程(Spring AOP 在运行时完成织入)

使用注解配置 AOP

  • 需在 XML 文件中开启 AOP 注解解析器 <aop:aspectj-autoproxy/>,启动 @AspectJ 支持;或者在启动类上添加 @EnableAspectJAutoProxy
  • 定义切面类 Bean @Aspect
  • 定义切入点 @Pointcut@Pointcut("execution( *com.example.tx.service.*Service.*(..))")使用一个返回值为 void、方法体为空的方法来命名切入点,public void pc() {}
  • 定义增强处理,指定切入点,即 value 属性值@Before("pc()")@AfterReturning("pc()") :在目标方法正常完成后被织入@AfterThrowing(value="pc()", throwing="ex")@After("pc()"):不管目标方法如何结束(包括成功完成和遇到异常中止两种情况)都会被织入@Around("pc()")
  • 当定义 Around 增强处理方法时,该方法的第一个形参必须是 ProceedingJoinPoint 类型(JoinPoint 类型的子类,代表了织入增强处理的连接点),在增强处理方法体内调用 ProceedingJoinPoint 参数的 proceed() 方法才会执行目标方法
  • JoinPoint 接口中常用的方法Object[] getArgs():返回执行目标方法时的参数Signature getSignature():返回被增强的方法的相关信息Object getTarget():返回被织入增强处理的目标对象Object getThis():返回 AOP 框架为目标对象生成的代理对象
  • ProceedingJoinPoint 接口(JoinPoint 的子接口)中常用的方法Object proceed():执行目标方法Object proceed(Object[] args):args 中的值被传入目标方法作为执行方法的实参
// 定义一个切面 Bean
@Aspect
@Component
public class FourAdviceTest {
    // 定义 Around 增强处理
    @Around("execution(* com.example.app.service.impl.*.*(..))")
    public Object processTx(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("Around 增强:执行目标方法之前,模拟开始事务...");
        // 访问执行目标方法的参数
        Object[] args = jp.getArgs();
        // 当执行目标方法的参数存在,且第一个参数是字符串时
        if (args != null && args.length > 0 && args[0].getClass() == String.class) {
            // 修改目标方法调用参数的第一个参数
            args[0] = "【增加的前缀】" + args[0];
        }
        // 执行目标方法,并保存目标方法执行后的返回值
        Object rvt = jp.proceed(args);
        System.out.println("Around 增强:执行目标方法之后,模拟结束事务...");
        // 如果 rvt 的类型是 Integer,将 rvt 改为它的平方
        if (rvt != null && rvt instanceof Integer)
            rvt = (Integer) rvt * (Integer) rvt;
        return rvt;
    }
    // 定义 Before 增强处理
    @Before("execution(* com.example.app.service.impl.*.*(..))")
    public void authority(JoinPoint jp) {
        System.out.println("Before 增强:模拟执行权限检查...");
        // 返回被织入增强处理的目标方法
        System.out.println("Before 增强:被织入增强处理的目标方法为:"
                + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("Before 增强:目标方法的参数为:"
                + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("Before 增强:被织入增强处理的目标对象为:"
                + jp.getTarget());
    }
    //定义 AfterReturning 增强处理
    @AfterReturning(pointcut = "execution(* com.example.app.service.impl.*.*(..))",
            returning = "rvt")
    public void log(JoinPoint jp, Object rvt) {
        System.out.println("AfterReturning 增强:获取目标方法返回值:" + rvt);
        System.out.println("AfterReturning 增强:模拟记录日志功能...");
        // 返回被织入增强处理的目标方法
        System.out.println("AfterReturning 增强:被织入增强处理的目标方法为:"
                + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("AfterReturning 增强:目标方法的参数为:"
                + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("AfterReturning 增强:被织入增强处理的目标对象为:"
                + jp.getTarget());
    }
    // 定义 After 增强处理
    @After("execution(* com.example.app.service.impl.*.*(..))")
    public void release(JoinPoint jp) {
        System.out.println("After 增强:模拟方法结束后的释放资源");
        // 返回被织入增强处理的目标方法
        System.out.println("After 增强:被织入增强处理的目标方法为:"
                + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("After 增强:目标方法的参数为:"
                + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("After 增强:被织入增强处理的目标对象为:"
                + jp.getTarget());
    }
}