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());
}
}