前言
由于目前网上比较的文章都是基于xml,完全基于注解开发,或者是基于注解源码分析比较少。所以决定写一写spring基于注解驱动开发的使用,以及实现原理分析。
一、Spring的注解有哪些?功能是什么?
spring提供的注解有很多,常用的有:@Component、@Configuration&@Bean、@ComponentScan、@ComponentScans、@Repository、@Service、@Controller 、@Scope、@Lazy 、@Conditional、@Import、@DependsOn、 @PostConstruct、@PreDestroy、@Value、@Resouce和@Inject、@ProtertySource等等,下面我按功能来逐个详细介绍。
1.组件注册
@Configuration:标明这个类是一个配置类。组合注解,组合了@Component。所以本身也是一个bean。
@Bean:在@Configuration注解的类里面,作用于方法。value和name一样 数组,即bean的id,默认是方法名 。initMethod destroyMethod 指定方法。如果修饰的是带参数的方法,参数名即为bean的id,如果只有一个 就会按类型匹配。这个参数也是Bean的依赖注入。
@ComponentScan:该注解必须和@Configuration配合使用。扫描组件。里面的比较参数:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {}; //被扫描包,可以配置多个
@AliasFor("value")
String[] basePackages() default {};//被扫描包,可以配置多个
Class<?>[] basePackageClasses() default {};//包含当前类,以及该类的包和子包,可以配置多个
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;//BeanName生成器
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;//scope代理
String resourcePattern() default "**/*.class";//默认匹配class资源
boolean useDefaultFilters() default true;//使用默认过滤器,即扫描@Component注解,其中@Service、@Controller、@Configuration、@repository都组合了@Component注解。
ComponentScan.Filter[] includeFilters() default {};//如果配置这个,默认就自动失效
ComponentScan.Filter[] excludeFilters() default {};//排除
boolean lazyInit() default false;//是否扫描到的组件需要懒加载
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;//按注解类型排除,和Classes配合使用
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};//如果类型为正则的话,使用这个参数
}
}
这三个注解等同于@Component,没有什么其他意思,标明是一个组件。源码都是组合了@Component
@Scope:默认注册的组件是单例,也可以设置为原型模式。
@Lazy :默认注册的组件是false,可以使用该注解标明懒加载。即使用的时候再初始化。
@DependsOn:作用在@Componet,在注入Bean之前,先注入被依赖的Bean。
该注解也必须和@Configuration组合使用。我们常用的@Enable*注解就是使用了@Import注解来实现的。
他的属性可以配置多个类,这个类可以实现ImportSelector、ImportBeanDefinitionRegistrar。
@Conditional:符合条件的才会被注册,他的参数是一个class ,必须继承Condition接口。里面有个方法,返回true就会被注册。boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
Class extends Condition>[] value(); 参数只有一个,必须要继承Condition。该接口只有一个方法
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
可以结合@Bean @Component使用
@Profile:指定一个环境,如何是激活的环境值一样,则被加载,底层实现原理就是 @Conditional。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})//实现原理
public @interface Profile {
String[] value();
}
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {//与激活的环境值比较
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
}
@Value取值方式:
@ProtertySource(“classpath:/com/myco/app.properties”)
用来指定资源的位子,加载到环境里面
Indicate the resource location(s) of the properties file to be loaded.
* For example, {@code “classpath:/com/myco/app.properties”} or
* {@code “file:/path/to/file”}.
1)@Autowired 自动注入
1)默认按照类型去容器中找对应得组件 applicationContext.getBean(Book.class)
2)如果找到多个相同类型的组件,再按照属性名作为id去查找
3)使用@Qualifier(“book”)可以限定bean的id,不适用属性名
4)自动装配 默认 require=true 找不到会报错
5)@Primary :在自动装配的时候默认使用主要的组件,
也可以继续使用@Qualifier限定组件的id
2)@Resouce和@Inject
@Resource:默认是按照属性名称注入 不支持@Primary 不支持require=false
@Inject:需要导包javax.inject 不支持require=false
@Autowired :spring定义的 功能最强
@Resource/@Inject:Java规范,不建议使用
除了作用在属性上,还可以作用在方法上,参数上。效果都是一样的。
实现原理:AutowiredAnnotationBeanPostProcessor.class
注解编写AOP的三步:
1)将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类 @Aspect
2)在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(编写切点表达式)
3)开启基于注解的aop模式;@EnableAspectJAutoProxy
实战演示:
第一步:导入jar包
<!--Spring aop 源码内置了aopalliance-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!--aspectj:使用注解开发,会使用到aspectj的注解-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
第二步:编写通知
@Aspect
@Component
public class LogAspect {
/*
* @Before:前置通知。value参数定义切点
*
* @AfterReturning:目标方法执行结束后执行。参数:value:切点 returning:方法返回值
*
* @AfterThrowing:在目标方法抛出异常后执行。参数:throwing:被抛出的异常绑定
*
* @After:Final增强,不管是抛出异常还是正常退出,都会执行。可以看做是
*
* @Around:环绕通知,手动执行目标方法。参数:value:切点
*/
@Pointcut(value = "execution(public void com.xinchao.test.*(..))")
public void pointCut(){}
@Around("pointCut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("start");
proceedingJoinPoint.proceed();//异常直接抛,调用目标方法
System.out.println("end");
}
}
第三步:@EnableAspectJAutoProxy 开启AOP
1)@EnableTransactionManagement 开启基于注解的事务管理器
2)给方法上标注@Transactional 表示当前方法是一个事务方法
3)配置事务管理器来控制事务
Spring提供了很多注解,让我们不再使用XML配置,注解配置也是大势所趋,目前我们用到的项目几乎都没有xml配置了。所以掌握这些注解对以后的开发大有裨益。
后续我也会详解每个注解的解析,Spring是如何启动并加载这些Bean。AOP又是如何开启的等。
Spring的源码比较复杂,分支旁路众多,建议读者自己学会去摸索。