2.11 @Scope注解
@Scope注解源码:
package org.springframework.context.annotation;
@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
public @interface Scope {
@org.springframework.core.annotation.AliasFor("scopeName")
java.lang.String value() default "";
@org.springframework.core.annotation.AliasFor("value")
java.lang.String scopeName() default "";
org.springframework.context.annotation.ScopedProxyMode proxyMode() default org.springframework.context.annotation.ScopedProxyMode.DEFAULT;
}
@Scope可以注解在某个交给spring管理的类上方,也可以注解在@Bean注解的方法上方。作用和配置文件中bean标签的scope属性一致。主要用于配置交给spring管理的bean的作用域。
主要有以下几个值:
singleton:单例模式(默认); prototype:多例模式; request:作用于web应用的请求范围; session:作用于web应用的会话范围;
当然我们还可以使用字符串常量配置:
ConfigurableBeanFactory.SCOPE_SINGLETON(单利)
ConfigurableBeanFactory.SCOPE_PROTOTYPE(多例)
WebApplicationContext.SCOPE_SESSION(会话范围)
WebApplicationContext.SCOPE_REQUEST(请求范围)
单利模式下,spring只会为当前bean创建一个实例,每次从spring容器中获取的都是同一个对象。并且在spring容器初始化时,就会创建该对象。
多利模式下,spring初始化时不会创建该对象,每次从spring容器中获取这个bean对象时,spring都会创建要给新的。
回话范围和请求范围是web应用专用的,回话范围,spring会为每次回话创建要给当前Bean的实例。请求范围,spring会为每次请求创建一个当前bean的实例。
案例:
@Component
@Scope(value= ConfigurableBeanFactory.SCOPE_SINGLETON)
public class AuthorDAO implements IAutorDAO {
@Override
public String get() {
return "戴着假发的程序员";
}
}
注意:value属性和scopeName并不是互斥的,也就是可以同时配置,但是这两个配置的范围必须一致,如果不一致会抛异常。(当然我觉得不会有人非要两个属性一起配置)
2.12 @Scope的proxyMode属性
proxyMode是用来配置当前类的代理模式的。主要用于scope非singleton的情况。因为非singleton的bean spring并不会立刻创建对象,如果需要注入时就产生一个代理对象,这时代理模式就起作用了。
有下面的几个值:
public enum ScopedProxyMode {
DEFAULT, NO, INTERFACES, TARGET_CLASS;
private ScopedProxyMode() { /* compiled code */ }
}
默认值DEFAULT 就是 NO :意思就是不使用代理,如果需要就立刻创建。
INTERFACES : 表示使用jdk实现动态代理。
TARGET_CLASS:表示使用CGLib实现动态代理。
2.13 @Lazy注解
@Lazy注解,是用来配置bean是否延迟加载,默认是true,但是配置了才生效,不配置就不生效。
@Lazy源码:
package org.springframework.context.annotation;
@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
public @interface Lazy {
boolean value() default true;
}
@Lazy可以注解在某个交给spring管理的类上方,也可以注解在@Bean注解的方法上方。作用和配置文件中bean标签的lazy-init属性一致。 主要用于表示这bean是否要延迟实例化。 默认值是true。所以我们一旦配置了@Lazy,那么这个Bean就会延迟加载。 如果不希望延迟加载就可以不配置或者配置之后,将属性value修改为false。
@Bean
@Lazy(false)
public UserService userService(){
return new UserService();
}
@Component
@Lazy
public class AuthorDAO implements IAutorDAO {}
2.14@DependsOn
@DependsOn可以注解在某个交给spring管理的类上方,也可以注解在@Bean注解的方法上方。作用和配置文件中bean标签的DependsOn属性一致。主要是来配置当前类的依赖类。
spring会在实例化当前类之前,先实例化DependsOn指定的bean,在销毁当前类之后才会销毁DependsOn指定的bean。
案例:
我们给ArticleDAO和AuthroDAO都添加构造方法,并且输出信息。
@Component
public class AuthorDAO implements IAutorDAO {
public AuthorDAO(){
System.out.println("实例化AuthorDAO");
}
}
@Component
public class ArticleDAO implements IArticleDAO {
public ArticleDAO(){
System.out.println("实例化ArticleDAO");
}
}
创建容器测试:
先实例化ArticleDAO,后实例化AuthorDAO。
我们再ArticleDAO上添加配置:
在测试:
2.15 @Conditional注解
源码:
package org.springframework.context.annotation;
@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
public @interface Conditional {
java.lang.Class<? extends org.springframework.context.annotation.Condition>[] value();
}
@Conditional可以注解在由spring管理的类上方和@bean注解的方法上方。
主要作用是配置一些注册bean对应的条件,如果满足条件就注册bean,如果不满足就不进行bean的注册。
我看到 @Conditional属性value是一组Condition的类型数组。
我们再来看看Condition源码:
package org.springframework.context.annotation;
@java.lang.FunctionalInterface
public interface Condition {
boolean matches(org.springframework.context.annotation.ConditionContext conditionContext, org.springframework.core.type.AnnotatedTypeMetadata annotatedTypeMetadata);
}
该接口中只有一个metches方法,返回true说明条件成立,返回false说明条件不成立。
我们可以自己实现一个。
看案例:
我们自己实现一个Condition。
/**
* @author 戴着假发的程序员
*
* @description
*/
public class WindosCondition implements Condition {
/**
*
* @param conditionContext 判断条件的上下文环境,可以获取环境对象,和工厂对象
* @param annotatedTypeMetadata 注解所在位置的注释信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取环境对象
Environment environment = conditionContext.getEnvironment();
//判断环境的名字
if(environment.getProperty("os.name").contains("Windows")){
return true;
}
return false;
}
}
在AuthorDAO上添加配置:
测试:
修改条件,再测试。
2.16@Primary
package org.springframework.context.annotation;
@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
public @interface Primary {
}
@Primary可以注解在由spring管理的类上方和@bean注解的方法上方。作用和bean标签的primary属性一致。
就是让其他bean在把当前bean作为注入对象时,如果存在多个和当前Bean同类型的bean,则优先选择当前bean。当然如果有多个同类型的bean并且多个bean上都注释了@Primary,则会抛出异常。
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
@Primary
public class ArticleDAO_other implements IArticleDAO {
@Override
public int save(String title) {
System.out.println("ArticleDAO_other-save->保存文章:"+title);
return 1;
}
}
2.17@Profile注解
package org.springframework.context.annotation;
@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
@org.springframework.context.annotation.Conditional({org.springframework.context.annotation.ProfileCondition.class})
public @interface Profile {
java.lang.String[] value();
}
@Profile和配置文件的Profile有一样的作用,就是可以在不同的环境(条件)下让配置(注册生效)。 看案例:
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
@Profile("oracle")
public class ArticleDAO_oracle implements IArticleDAO {
public ArticleDAO_oracle(){
System.out.println("实例化ArticleDAO_oracle");
}
}
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
@Profile("mysql")
public class ArticleDAO_mysql implements IArticleDAO {
public ArticleDAO_oracle(){
System.out.println("实例化ArticleDAO_mysql");
}
}
测试:
/**
* @author 戴着假发的程序员
*/
public class SpringTest {
@Test
public void testAnnotation() throws IOException {
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext();
//设置启动环境
ac.getEnvironment().setActiveProfiles("mysql");
//注册扫描包,注册所有的bean
ac.scan("com.st");
//刷新容器
ac.refresh();
}
}
测试结果:
实例化ArticleDAO_mysql
我们发现ArticleDAO_oracle根本就没有实例化。
当然激活环境的方法还有很多。可以参考 XML配置profile章节。
2.18 @Autowired注解
查看@Autowired注解源码:
package org.springframework.beans.factory.annotation;
@java.lang.annotation.Target({java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.ANNOTATION_TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
public @interface Autowired {
boolean required() default true;
}
我们会发现Autowired有属性required,默认是true。
@AutoWired注解和bean标签中的autoWired属性差不多,可以通知spring帮我们自动组装Bean,例如:
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
public class ArticleService {
@Autowired
private IAutorDAO autorDAO;
@Autowired
private IArticleDAO articleDAO;
public int save(String title){
String author = autorDAO.get();
System.out.println("ArticleService-save:");
System.out.println("author:"+author);
return articleDAO.save(title);
};
}
@Autowired注解可以写在成员变量上方,当然那也可以写在对应的setter方法上。 当然如果没有setter方法,我们会发现spring依然可以帮我们注入这些属性。
@Autowired默认首先是按照类型注入的,如果在当前容器中找到了多个同种类型的bean,就按照名称注入,如果一个都没找到就抛异常。
当然我们也可以通过修改属性required为false,通过spring如果找不到就组装。
2.19 @Qualifier注解
查看源码:
package org.springframework.beans.factory.annotation;
@java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.ANNOTATION_TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Inherited
@java.lang.annotation.Documented
public @interface Qualifier {
java.lang.String value() default "";
}
@Qualifirer主要用来指定要注入的bean的限定符(beanName)。
当我们需要注入某个类型的bean时,同时spring容器中有多同样类型的bean,这时我们可以通过@Qualifirer指定要注入的beanName。
用法如下:
[1]可以和@Autowired配合使用
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
public class ArticleService {
@Autowired
@Qualifier("articleDAO_mysql")
private IAutorDAO autorDAO;
//...
}
[2]也可以在setter方法的参数上使用
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
public class ArticleService {
@Autowired
private IAutorDAO autorDAO;
public void setAutorDAO(@Qualifier("articleDAO_mysql") IAutorDAO autorDao){
this.autorDAO = autorDao;
}
//...
}
[3]也可以在构造方法中使用
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
public class ArticleService {
@Autowired
private IAutorDAO autorDAO;
public ArticleService(@Qualifier("articleDAO_mysql") IAutorDAO autorDao){
this.autorDAO = autorDao;
}
//...
}
[4]你还可以扩展自己的限定符注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
2.20 @Resource注解
@Resource是 JSR-250标准。可以注解在bean的成员变量或者setter方法上。主要也是用于通知spring当前的属性应该注入哪个bean。
@Resource和@Autowired不同的是@Resource会首先按照名称注入,如果名称找不到才会按照类型注入。
我们可以通过@Resource的属性name指定要注入的bean的beanName,当然如果不指定name属性的话spring或自动根据属性或者setter方法找到对应的beanName例如:
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
public class ArticleService {
@Resource
private IArticleDAO articleDAO;
//...
}
上面的程序中spring生成的beanName默认是"artilceDAO"。
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
public class ArticleService {
private IArticleDAO articleDAO;
@Resource
public void setAuthorDAO(IArticleDAO articleDAO){
this.articleDAO = articleDAO;
}
//...
}
上面的程序中spring生成的beanName默认是"authorDAO"。
当然我们可以指定名称:
/**
* @author 戴着假发的程序员
*
* @description
*/
@Component
public class ArticleService {
@Resource(name="articleDAO_oracle")
private IArticleDAO articleDAO;
//...
}
这是spring就会查找beanName为articleDAO_oracle的bean注入。
几个要注意的问题:
[1]@Resource默认会按照名称查找容器中的bean,如果名称找不到就会按照类型查找,如果这时同种类型的bean在容器中出现多个,一样会抛出异常。
[2]如果我们显式的指定了@Resource的name属性,那么spring如果通过这个名字找不到对应的bean就会报错,不会再按照类型查找。
未完待续。。。。 欢迎关注