一.注解的定义
所有定义的注解都默认实现了Annotation接口,就像所有的类都默认继承了Object类,定义的注解至少有2个注解用于指定注解的可使用的范围和运行范围 类或方法等使用注解,被正常调用时是没有任何作用的,只有被反射调用时,才会发挥作用(反射针对注解做了处理)
@Override注解
@Taeget注解 指定 注解可以使用的 位置
METHOD | 可用于方法上 |
PARAMETER | 可用于参数上 |
CONSTRUCTOR | 可用于构造方法上 |
LOCAL_VARIABLE | 可用于局部变量上 |
ANNOTATION_TYPE | 可用于注解类型上(被interface修饰的类型) |
PACKAGE | 用于记录java文件的package信息 |
ANNOTATION_TYPE | 可用于注解类型上(被interface修饰的类型) |
@Retention(RetentionPolicy.RUNTIME) 必须指定 运行范围 RUNTIME(这个才会被反射获取到)
二.注解的使用
springboot最核心的注解:
@SpringBootApplication = (默认属性)@Configuration
+ @EnableAutoConfiguration
+ @ComponentScan.
@Configuration
用于定义配置类,可替换XML配置文件,被注解的类内部包含一个或多个@Bean
注解方法。
@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
@EnableAutoConfiguration:能够自动配置spring的上下文,试图猜测和配置你想要的bean类,通常会自动根据你的类路径和你的bean定义自动配置。
@ComponentScan:会自动扫描指定包下的全部标有@Component的类,并注册成bean,当然包括@Component下的子注解@Service,@Repository,@Controller
二.@Component 和 @Bean 是两种使用注解来定义bean的方式
@Component
声明性的。负责创建实例(也就是bean)并存入IOC容器, @Autowired取出实例注入到引用上
web开发,提供3个取代@Component注解的衍生注解(功能一样,都是创建bean,放入容器)
@Repository(“名称”):dao层
@Service(“名称”):service层
@Controller(“名称”):web层
@service注解本身就承担了两个职责:
一是Bean的创建;可用@Component 代替
二是将一个类标识为一个服务。
@Component注解默认实例化的对象是单例,如果想声明成多例对象可以使用
@Component
@Scope("prototype")
@Repository默认单例 (待验证)
@Service默认单例 (待验证)
@Controller默认多例 (待验证)
@Bean
用于显式声明单个bean,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许您精确地创建和配置bean。(@Bean 比较方便的声明一个对象,)
@Configuration 加后被spring管理 单例模式 @Configuration是单例
@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现
三.@Resource与@Autowired
1. @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired。
@Resource注解由J2EE提供,需要导入包javax.annotation.Resource
2.@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入
@Autowired是根据类型进行自动装配的。如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,
也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题
@Qualifier告诉spring具体去装配哪个对象
四. @Value注解使用
通常使用第一种方式@Value(“${}”)读取配置文件的值(application.properties或application.yml)
代码
配置文件
第二种 @Value("#{}") Spring 表达式语言(简称SpEL)
五.@PostConstruct
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次
Constructor > @Autowired > @PostConstruct
//------------------------------------------------------------------------------------------------------------------
六. @Around
- 可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标目标方法的执行;
- 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值; 当需要改变目标方法的返回值时,只能使用Around方法 通常需要在线程安全的环境下使用
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
//------------------------------------------------------------------------------------------------------------------
@ConditionalOnProperty来控制Configuration是否生效
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
String[] value() default {}; //数组,获取对应property名称的值,与name不可同时使用
String prefix() default "";//property名称的前缀,可有可无
String[] name() default {};//数组,property完整名称或部分名称(可与prefix组合使用,组成完整的property名称),与value不可同时使用
String havingValue() default "";//可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
boolean matchIfMissing() default false;//缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错
boolean relaxedNames() default true;//是否可以松散匹配,至今不知道怎么使用的
}
}
//在application.properties配置 framework.swagger.enable=true
通过其两个属性name以及havingValue来实现的,其中name用来从application.properties中读取某个属性值。
如果该值为空,则返回false;
如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true;否则返回false。
如果返回值为false,则该configuration不生效;为true则生效。
@Configuration
//在application.properties配置"framework.swagger.enable",对应的值为true
@ConditionalOnProperty(prefix="framework.swagger",name = "enable", havingValue = "true")
public class SwaggerConfig {
@Autowired
private HelloServiceProperties helloServiceProperties;
@Bean
public HelloService helloService(){
HelloService helloService = new HelloService();
helloService.setMsg(helloServiceProperties.getMsg());
return helloService;
}
}
//------------------------------------------------------------------------------------------------------------------
@ConfigurationProperties("jwt")
//------------------------------------------------------------------------------------------------------------------
自定义注解:
//------------------------------------------------------------------------------
spring.factories 优先加载实例
Spring Factories实现原理
spring-core包里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。在这个类中定义了两个对外的方法:
loadFactories 根据接口类获取其实现类的实例,这个方法返回的是对象列表。
loadFactoryNames 根据接口获取其接口类的名称,这个方法返回的是类名的列表。
上面的两个方法的关键都是从指定的ClassLoader中获取spring.factories文件,并解析得到类名列表,具体代码如下
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
从代码中我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。也就是说我们可以在自己的jar中配置spring.factories文件,不会影响到其它地方的配置,也不会被别人的配置覆盖。
spring.factories的是通过Properties解析得到的,所以我们在写文件中的内容都是安装下面这种方式配置的:
com.xxx.interface=com.xxx.classname
在日常工作中,我们可能需要实现一些SDK或者Spring Boot Starter给被人使用时,
我们就可以使用Factories机制。Factories机制可以让SDK或者Starter的使用只需要很少或者不需要进行配置,只需要在服务中引入我们的jar包即可
spring注解的作用:
//----------------------------------------------------------------
@Documented
在自定义注解的时候可以使用@Documented来进行标注,如果使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来。
要牢记,只要用到注解,必然有三角关系:
- 定义注解
- 使用注解
- 读取注解
仅仅完成前两步,是没什么卵用的。就好比你写了一本武林秘籍却没人去学,那么这门武功还不如一把菜刀.
注解的读取并不只有反射一种途径.