spring注解

  • 注解的使用,首先编写一个注册文件Configure.java,并用@Configuration注解,然后使用注解容器类加载bean即可.
@Configuration
@ComponentScan(basePackages = {"music"},excludeFilters = {@ComponentScan.Filter(value = MF.class)})
public class Configure {
    @Bean
    public A excplicateBean(){
        return new A();
    }
}

//使用
AnnotationConfigApplicationContext acfc = new AnnotationConfigApplicationContext(Configure.class);
//web使用AnnotationConfigWebApplicationContext
  • @ComponentScan,使用在配置类上,用于自动扫描bean.
//基本属性
value/basePackages:指定一组要扫描的包,将扫描这些包及其子包下的文件.(默认基包为配置类所在的包)
classes:直接指定扫描的一些类,这些类所在的包及其子包也会被扫描.
nameGenerator:指定一个默认扫描组件的命名类,默认命名使用组件类名第一个字小写.
excludeFilters:需要一组@ComponentScan.Filter的注解配置,每个@Filter可以配置一组过滤规则,多个@Filter可以基于正则/注解/具体类配置不同的过滤规则.
includeFilters:反上.
  • @ComponentScan.Filter,用于配置扫描过滤规则
    属性如下
type value/classes pattern
FilterType.ANNOTATION(默认) 一组注解,命中所有使用该注解的类,{MyAnno.class} -
FilterType.ASSIGNABLE_TYPE 一组具体类 -
FilterType.ASPECTJ - 一组表达式,使用Aspectj表达式命中类
FilterType.REGEX - 一组表达式,使用正则命中类
FilterType.CUSTOM 自定义的TypeFilter. -
  • @Component,标记一个类为组件类,扫描时自动创建bean,创建顺序如下:

    1. 若该类有一个@AutoWired注解的构造器,则使用该构造器实例化,若没有则使用空白构造器实例化.(两者皆无则报错)
    2. 先注入注解在实例域上的@AutoWired/@Value.
    3. 再解析注解在实例方法上的@AutoWired/@Value
  • @Bean用在配置类的方法中,表示该方法会返回一个bean,beanId与方法名同,用于显式地配置一个bean.

@Configuration
@ComponentScan
public class Configure {
    @Bean
    //@Scope("prototype")未设置prototype则该方法会一直返回同一个单例
    //singleton模式则第二次调用时直接返回已创建对象,不再执行new/set等方法
    public A a(){
        A a = new A();
        a.setX(10);
        return a;
    }
    //相当于引用了a()产生的对象,a()在singleton模式下只返回单例
    @Bean
    public B b1(){
        return new B(a());
    }
    //可以把其他bean放在参数中自动注入,好处是注入的bean可以来自xml配置/自动扫描
    @Bean
    public B b2(A a){
        return new B(a);
    }
}
@Bean创建bean的过程如下:
1. 调用注解的方法产生一个实例对象,如果该方法有参数,则对参数注入.
2. 如果返回对象的对应类的某些实例域上有@Value/@AutoWired注解,则对返回对象的这些实例域进行注入.
3. 如果返回对象的对应类的某些方法上有@Value/@AutoWired注解,则对返回对象的这些方法进行注入.

@Import,用于导入另外一个配置类的信息到当前配置类.
@ImportResource,用于导入配置文件到当前配置类.

@Configuration
@Import(Part1Config.class)
@ImportResource("classpath:cd-config.xml")
public class RootConfig{
}
//tips,如果需要导入一个java config到xml配置文件中,直接使用bean即可
//<bean class="PartOneConfig.class"/>
  • @Profile,用于配置不同环境下生成不同的bean.
    可以用在@Component注解的自动扫描组件和@Bean注解的(方法)显式组件中,如果用在配置类上则表示所有经由该配置类产生的bean(显式/组件扫描)都有profile控制.
@Configuration
public class Configure {
    //未配置profile则在所有环境下都会被创建
    @Bean
    public ComponentDisc componentDisc(){
        System.out.println("create bean cdisk");
        return new SgtPers();
    }
    @Bean
    //该bean只有在名为dev的profile激活时才能被创建
    @Profile("dev")
    public DisckPlayer disckPlayer(){
        DisckPlayer disckPlayer = new DisckPlayer();
        disckPlayer.setDisc(componentDisc());
        disckPlayer.setPlayTime(10);
        return disckPlayer;
    }
    @Bean
    //该bean只有在名为test的profile激活时才能被创建
    @Profile("test")
    public DisckPlayer disckPlayer1(){
        DisckPlayer disckPlayer = new DisckPlayer();
        disckPlayer.setDisc(componentDisc());
        disckPlayer.setPlayTime(12);
        return disckPlayer;
    }
}
通过两个参数可以选择激活哪些profiles:
spring.profiles.active 
spring.profiles.default ;active未配置时生效
当两个参数都没有配置时,则所有的profile限定bean都不被创建.

参数可以在以下位置配置:
As initialization parameters on DispatcherServlet
As context parameters of a web application
As JNDI entries
As environment variables
As JVM system properties
Using the @ActiveProfiles annotation on an integration test class

//在web.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<context-param>
    <param-name>spring.profiles.default</param-name>
    <param-value>dev</param-value>
</context-param>
<listener>
    <listener-class>
    org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>
    org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>dev</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
  • @Conditional,配置条件bean,该注解需要传入一组Condition实现类,只有指定的所有Condition的实现类中的matches返回true,bean才能被创建.
tips:4.0以后@Profile使用@Conditional实现,对应的Condition实现类为ProfileCondition
以下为ProfileCondition源码
class ProfileCondition implements Condition {
    //所有的Condition实现类都能获得ConditionContext与AnnotatedTypeMetadata
    @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;
    }

}
  • @Primary标记一个bean为主候选bean,用于解决自动注入的冲突.
@AutoWired是通过类型进行注入的,当同一个接口有多个实现,且都注册为bean时,
spring无法选择哪个候选bean用于注入,此时通过将bean注解为@Primary,则注入
有冲突时优先选择主候选bean.
  • @Qualifier,用于缩小候选bean范围.
tips:
@Qualifier("q")用于bean声明处(@Bean/@Component)时,表示限定该bean的选择范围为q.
@Qualifier用于注入点(@Autowired)时,表示选择注解为@Qualifier("q")的bean进行注入或者使用id为q的bean注入.
可以同时使用多个@Qualifier,进行更为详细的特征限定.
@Bean
@Qualifier("cold")
public Dessert iceCream() {
    return new IceCream();
}
//选择候选名为cold的可能候选bean进行注入
@Autowired
@Qualifier("cold")
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}
//由于java不允许在一个位置重复使用一个注解,所以当需要使用多个特征进行限定时则需要自定义注解。
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
//Spring扫描注解时,会解析注解上的注解并应用
@Qualifier
@interface Cold{}

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@interface Soft{}

使用时可以一起使用,进行多特征限定
@Bean
@Cold
@Soft
public Dessert iceCream() {
    return new IceCream();
}
  • @Scope,用于控制bean的生成模式
属性 作用
scopeName/value singleton:单例模式。prototype:每次调用生成新对象。session:用于web,为每个session生成一个实例。request:用于web,为每个request生成实例
proxyMode ScopeProxyMode.INTERFACES:基于接口使用jdk动态代理,当代理的类为借口时使用。ScopeProxyMode.TARGET_CLASS:基于CGLib的动态代理,当代理的类为具体类时使用
代理模式的作用,考虑以下情况
@Bean
@Scope("prototype")
public A a(){
    return new A();
}
@Bean
@Scope("singleton")
public B b(){
    return new B(a());
}
此时由于bean-b为单例模式,所以只创建一次,只调用一次
a(),所以每次调用b.a.action(),实际调用的是同一个
bean-a,如果希望每次调用b.a.action()都能使用新创建
的A对象,那么必须配置一个proxyMode属性,它会创建一个
A的代理类,当具体调用A类方法时,再调用getBean("a")
从Spring容器中获得一个新创建的A,然后调用.

最常用的情况是在scopeName为request/session,因为
service层一般是单例,而request/session域的bean-
rs是根据session/request创建、获取的,所以在启动时往
service中注入rs是不可能的,只能先创建一个动态代
理,当具体连接到来,再通过动态代理调用getBean("class-sessionId")获得具
体不同的实例对象并调用具体方法。

总而言之,proxyMode属性设置后,会延迟到属性/方法的调用时才会真正的去加载一
个bean,每次调用都要重新加载,对于prototype,每次都返回新对象,request
/session则返回对应的bean.

以下为proxyMode配置后的工作模式,可以看到通过注入一个动态代理,可以在一个单例中调用不同的注入对象。

  • @PropertySource(“classpath:/com/soundsystem/app.properties”)+Environment用于加载properties文件的值.
tips:Environment除了可以使用getProperties加载资源还有getActivyProfiles等用于检测profiles的方法.
@PropertySource("classpath:/com/soundsystem/app.properties")
@Configuration
class Configure{
    @Autowired
    Environment env;
    @Bean
    A a(){
        return new A(env.getProperties("key","defaultValue"));
    }
}
  • @Value+PropertySourcesPlaceholderConfigurer实现属性占位符
//启用属性占位符
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
    return new PropertySourcesPlaceholderConfigurer();
}
class A{
    //使用@Value("${key}")获取属性,并设置到属性上
    A(@Value("${key}")String x){
        this.x = x;
    }
}
  • SpEL,以#{…}为标志,内部为方法体.
class A{
    //加载一个属性,与属性占位符同效果
    @Value("#{systemProperties['key']}")
    private String x;
    //通过T操作符使用静态方法与静态域
    @Value("T(System).currentTimeMillis()"
    private long time;
    //直接使用另外一个bean的方法/属性
    @Value("b.getPoint()")
    private String point;
    //?.操作,只有在左部非空才往后调用,否则返回null,保证不会有NPE.
    @Value("b?.getPoint()?.toUpperCase()")
    private String dot;
    //+操作
    @Value("p.firstName+'.'+p.sencodName")
    //正则判断
    @Value("p.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'")
    //集合操作,取集合/数组中的第n个元素
    @Value("singer.songs[1]")
    //.?[expression]根据expression的返回值过滤集合
    @Value("singer.songs.?[name.contains('love')]")
    //.^[expression],选择集合中满足expression的第一个元素
    @Value("singer.songs.^[getName() matches '*love*']")
    //.$[expression],选择集合中满足expression的最后一个元素
    @Value("singer.songs.$[getName() matches '*love*']")
    //.![expression],根据expression的返回值形成新集合
    @Value("singer.songs.![getName()]")
}

SpEL支持的操作

Operator type Operators
Arithmetic +, -, *, /, %, ^
Comparison <, lt, >, gt, ==, eq, <=, le, >=, ge
Logical and, or, not, |
Conditional ?: (ternary), ?: (Elvis)
Regular expression matches

你可能感兴趣的:(注解,spring)