依赖注入与自动装配
基础装配
高级装配
从Profile注解的实现看Conditional注解和Condition接口
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}
class ProfileCondition implements Condition {
ProfileCondition() {
}
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//从Profile类中获取所有的注解属性
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
//从注解属性中得到value值,该值对应@Profile(配置名)中传入的配置名
Iterator var4 = ((List)attrs.get("value")).iterator();
Object value;
//遍历配置名,判断在上下文环境中是否时激活profile,是则返回true
do {
if (!var4.hasNext()) {
return false;
}
value = var4.next();
} while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));
return true;
} else {
return true; //默认返回true,视为无注解的加载
}
}
}
再来理解下Scope注解中proxyMode指定的动态代理:
面向切面AOP
依赖注入将组件及其协作组件解耦;AOP将应用组件与跨多个组件的任务进行解耦。
在Performance接口的perform方法被调用前会执行切面Audience的silenceCellPhones逻辑;当perform方法执行完成会执行切面逻辑applause,否则抛出异常执行切面逻辑demandRefund。
注解完成切面
① 核心流程——以舞台演出为例,interface Performance定义演出接口;PerformanInstance类提供具体实现,并将其注解为组件,以自动注入
public interface Performance {
void perform();
}
@Component
public class PerformInstance implements Performance {
@Override
public void perform() {
System.out.println("唱一曲《向天再借五百年》!");
}
}
② 织入切面Audience,增强功能。实现演出前后的观众动作。
@Aspect
@Component
public class Audience {
@Pointcut("execution(** aspect.Performance.perform(..))")
public void performance() {}
//定义切点,用于可重用的切点,performance方法只是一个标识,供@Pointcut注解依附
@Before("performance()") //等价于demandRefund的切点表达式用法
public void silenceCellPhones() {
System.out.println("Silencing cell phones 11111111");
}
@AfterReturning("performance()")
public void applause() {
System.out.println("Clap Clap Clap 22222222");
}
@AfterThrowing("execution(** aspect.Performance.perform(..))")
public void demandRefund() {
System.out.println("Demanding a refund.");
}
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint joinPoint) {
try{
System.out.println("Silencing cell phones 33333333");
joinPoint.proceed(); //目标方法被调用,此处对应被增强的切点,即performance定义
System.out.println("Clap Clap Clap 44444444");
} catch (Throwable throwable) {
demandRefund();
}
}
}
③ 引入切面,增加新功能。Encorable和DefaultEncoreable分别定义新接口并提供默认实现;EncoreableIntroducer注解为切面以及组件,声明为aspect.Performance子类的增强接口,默认实现为DefaultEncoreable
public interface Encorable {
void dance();
}
public class DefaultEncoreable implements Encorable {
@Override
public void dance() {
System.out.println("再跳一支舞。");
}
}
@Aspect
@Component
public class EncoreableIntroducer {
@DeclareParents(value = "aspect.Performance+", defaultImpl = DefaultEncoreable.class)
public static Encorable encorable;
}
④ JavaConfig配置类,注解EnableAspectJAutoProxy 启用Aspect J自动代理功能,启用组件扫描
@Configuration
@EnableAspectJAutoProxy //启用AspectJ自动代理功能
@ComponentScan
public class AutoConfig {
}
⑤测试类及其结果
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AutoConfig.class})
public class AspectTest {
@Autowired
private Audience audience;
@Autowired
private Performance performance;
@Test
public void testAspect() {
performance.perform();
if (performance instanceof Encorable) {
((Encorable) performance).dance();
}
}
}
结果为:
Silencing cell phones 33333333
Silencing cell phones 11111111
唱一曲《向天再借五百年》!
Clap Clap Clap 44444444
Clap Clap Clap 22222222
再跳一支舞。
XML完成切面
主要用于通知类无法获知源码或者无法更改的情况下,将POJO的通知类声明为bean,声明为切面,进行AOP操作。思路与基于Config配置类完全一致。不再举例。
总结
AOP是OOP的强大补充。通过Aspect J,将分散在应用各处的行为放入可重用的模块中。显式地声明在何处如何应用这些行为。减少了代码冗余,提高核心功能对自身的关注度。