我们知道,Spring自从用了注解后就很方便了,我们可能都用过Spring的一些注解,比如@Controller、@Service、@Repository、@Component等,下面我们就来总结下Spring各大注解的作用。
环境搭建
用maven搭建Spring项目超级简单,只需要加上如下以来即可:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
是的,你没看错,就加这个就可以了,不用加什么aop,beans,corez之类的,因为maven的继承关系已经包括了,点进去可以发现依赖如下:
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.0.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>5.0.11.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
...
然后我们启动Spring项目用如下两个类即可,第一个是用配置文件的方式,第二个是用注解的方式。
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("spring.xml");
AnnotationConfigApplicationContext application=new AnnotationConfigApplicationContext(MyConfig.class);
在之前,我们新建Spring项目,都是直接按配置文件的方式,后面Spring支持注解后,就非常方便了,我推荐用注解,因为在注解里配置dataSource可以读取外部的配置文件,不一定是classpath下面的。
1、@Configurable
现在我们开始看第一个注解,这个注解的意思就是加上这个注解的类是一个配置类,也就相当于之前的application.xml,然后类里面用@Bean 来实例化对象就相当于配置文件中的
@Configurable
public class MyConfig {
@Bean
public User user() {
User user = new User();
user.setUsername("林蛋黄");
return user;
}
}
然后启动的时候就用AnnotationConfigApplicationContext
来启动。
也许,此时就会有人想问,这样子每个对象都要写一个方法,然后加上@Bean注解,岂不是超级麻烦。此时我们可以看第二个注解。
2、@ComponentScan
这个注解其实类似于配置文件的扫包范围,跟@Configurable一起使用,也就相当于配置文件中指定扫包范围。如下:
@Configurable
@ComponentScan(value="cn.myforever.start.object")
public class MyConfig {
}
此时,只要在cn.myforever.start.object包以及子包下面的类都会被扫描,我们只需要在需要实例化的类加上类似@Controller、@Service、@Repository、@Component等注解就可以将对应的类加入到IOC容器中啦。是不是很方便。
这个注解还有过滤的作用,比如我们只想实例化指定包下面的某些类,可以用如下方式:
@ComponentScan(value="cn.myforever.start.object",includeFilters= {@Filter(type=FilterType.ANNOTATION,classes=Controller.class)},useDefaultFilters = false)
上面的意思是说,我们的扫包范围是cn.myforever.start.object,但是我们只实例化加了@Controller注解的类到IOC容器中去,其他的比如@Service都不理,useDefaultFilters需要为false。
我们也可以排除指定包下面的某些类不实例化,如下方式:
@ComponentScan(value="cn.myforever.start.object",excludeFilters= {@Filter(type=FilterType.ANNOTATION,classes=Controller.class)},useDefaultFilters = true)
上面的意思是说,我们的扫包范围是cn.myforever.start.object,但是加了@Controller注解的类到将排除掉,不实例化到IOC容器中去,useDefaultFilters需要为true,否则将啥都不会注入。
FilterType 有四种类型
ANNOTATION:注解类型
ASSIGNABLE_TYPE:ANNOTATION:指定的类型
ASPECTJ:按照Aspectj的表达式,基本上不会用到
REGEX:按照正则表达式
CUSTOM:自定义规则
3、@Bean
这个注解就不需要多说,相当于
@Bean
public User user() {
User user = new User();
user.setUsername("林蛋黄");
return user;
}
实例化的id为方法的名称,正常来说,我们用这个注解都是为了实例化第三方jar包中的类到IOC容器中,毕竟第三方jar包中的类我们不方便加上@Component等注解。
4、@Scope
该注解是指定实例化的类的作用域,spring默认实例化的类都是单例模式,可以有四种选择作用域:
singleton 单例模式:全局有且仅有一个实例。
prototype 原型模式:每次获取Bean的时候都会有一个新的实例。
request 表示针对每次请求都会产生一个新的Bean对象,并且该Bean对象仅在当前Http请求内有效。
session 表示每次请求都会产生一个新的Bean对象,并且该Bean仅在当前Http session内有效。
一般都与@Compnent或者@Bean这些注入对象的注解配合使用。那么既然spring默认是单例模式,是懒汉式还是饿汉式呢。其实默认是饿汉式的,也就是容器一启动就初始化到IOC容器中。我们可以在要实例化的类的默认构造方法中打印日志,然后启动容器会发现,在没有执行getBean之前就打印出来了。有@Lazy注解可以修改这个模式。
5、@Lazy
Lazy表示为懒加载,当真正需要引用获取的时候才会被加载true 表示为懒加载 false表示为在IOC容器加载的时候被创建。加上这个注解,默认就是true,可以指定为false.
@Bean
@Lazy
public User user() {
User user = new User();
user.setUsername("林蛋黄");
return user;
}
饿汉式因为在容器启动的时候就初始化,所以比较占内存,但是效率高,不需要使用的时候才去加载。
6、@Controller、@Service、@Repository、@Component
@Component的意思是加上这个注解的类会被实例化到IOC内存中,然后扫包的时候就会被加进去,其实@Controller、@Service、@Repository本质上用的就是@Component,我们可以点进去看下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
...
}
只不过用不同的名称表示不同的意义,在不同的场景可能会有不同的作用,比如在Web项目中,@Controller表示是一个控制器,接收web请求,@Service表示是一个业务处理逻辑的处理器,@Repository表示的是一个与数据库交互的Dao,当然我们也可以定义我们自己的注解、如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface suibibk {
...
}
那么只要是加了@suibibk注解的类,就会实例化到IOC容器中去。
7、@Conditional
这个注解是用于在满足一定条件后才会实例化某一个类的情况,比如有个场景,只有当前环境是window 7,才会实例化Window7这个类,如何操作呢。我们可以先建立一个MyCondition来实现Condition接口,然后实现一个匹配方法matches。然后当环境是Windows的时候返回true,否则返回false,代码如下(当然还有各种其他的判定):
public class MyCondition implements Condition{
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment =context.getEnvironment();
String osName = environment.getProperty("os.name");
if("Windows 7".equals(osName)) {
return true;
}
return false;
}
}
那么我们就可以在我们的类上加上@Conditional注解,如下:
@Component
@Conditional(MyCondition.class)
public class Window7 {
}
这样子就做到了条件控制。
8、@Import注解
这个注解是很重要并且很好用的注解,在Springboot中使用很广,作用就是实例化一个类到IOC容器中去,下面是使用的三种情况:
实例化一个类
这种情况跟@Bean差不多,都是为了实例化一个第三方jar包中的类,如下:
@Configurable
@Import({MyUser.class})
public class MyConfig {
}
这样子MyUser就会被注入到IOC容器中去,但是跟@Bean有一个小区别,就是@Bean的ID是方法名,@Import的是类全名:cn.myforever.second.MyUser。
实例化一个实现了ImportSelector的类的selectImports方法返回的数组类全名
比如一个类实现了ImportSelector:
public class MyImportSelector implements ImportSelector{
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {"cn.myforever.second.User2"};
}
}
这个类的selectImports方法返回了一个类全名的数组。然后如果用@Import导入,那么会执行这个类的selectImports方法,并且会把返回数组中的所有类都实例化。
@Configurable
@Import({MyImportSelector.class})
public class MyConfig {
}
实例化一个实现了ImportBeanDefinitionRegistrar的类的registerBeanDefinitions方法里定义的类
比如一个类实现了ImportBeanDefinitionRegistrar:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(User3.class);
registry.registerBeanDefinition("user3", beanDefinition);
}
}
其实看到BeanDefinition就知到这个也是实例化一个对象。
@Configurable
@Import({MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
}
此时User3会被实例化,并且ID为user3。
9、Autowired
这个是来给对象注入对象依赖,不用多说,如下:
public class MyUser {
@Autowired
private UserService userService;
}
会将实现了UserService接口的类实例化对象后注入到MyUser这个类的对象属性中。当然前提是MyUser也需要被实例化。默认会去找实现了UserService类的对象,但是我们如果改为如下:
public class MyUser {
@Autowired
private UserService userServiceImpl01;
}
那么久会去找实现了UserServie的UserServiceImpl01类,对象。
但是假如我们就是要叫做userService,而实现了UserService接口的有几个,怎么办呢,如果直接@Autowired,肯定会报错的。
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘cn.myforever.second.UserService’ available: expected single matching bean but found 2: userServiceImpl01,userServiceImpl02nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘cn.myforever.second.UserService’ available: expected single matching bean but found 2: userServiceImpl01,userServiceImpl02
此时可以用如下两个注解解决。
10、@Qualifier
只要加上这个注解就指定了哪一个实现类。如下:
public class MyUser {
@Autowired
@Qualifier("userServiceImpl01")
private UserService userService;
}
那么久会直接找到UserServiceImpl01,不会报错,也可以用下面的注解。
11、@Primary
这个注解是指定当前类对象优先。
@Service
@Primary
public class UserServiceImpl01 implements UserService{
...
}
这样子也不会报错。
未完待续。。。