@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
//基本属性
value/basePackages:指定一组要扫描的包,将扫描这些包及其子包下的文件.(默认基包为配置类所在的包)
classes:直接指定扫描的一些类,这些类所在的包及其子包也会被扫描.
nameGenerator:指定一个默认扫描组件的命名类,默认命名使用组件类名第一个字小写.
excludeFilters:需要一组@ComponentScan.Filter的注解配置,每个@Filter可以配置一组过滤规则,多个@Filter可以基于正则/注解/具体类配置不同的过滤规则.
includeFilters:反上.
type | value/classes | pattern |
---|---|---|
FilterType.ANNOTATION(默认) | 一组注解,命中所有使用该注解的类,{MyAnno.class} | - |
FilterType.ASSIGNABLE_TYPE | 一组具体类 | - |
FilterType.ASPECTJ | - | 一组表达式,使用Aspectj表达式命中类 |
FilterType.REGEX | - | 一组表达式,使用正则命中类 |
FilterType.CUSTOM | 自定义的TypeFilter. | - |
@Component,标记一个类为组件类,扫描时自动创建bean,创建顺序如下:
@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"/>
@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>
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;
}
}
@AutoWired是通过类型进行注入的,当同一个接口有多个实现,且都注册为bean时,
spring无法选择哪个候选bean用于注入,此时通过将bean注解为@Primary,则注入
有冲突时优先选择主候选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();
}
属性 | 作用 |
---|---|
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配置后的工作模式,可以看到通过注入一个动态代理,可以在一个单例中调用不同的注入对象。
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"));
}
}
//启用属性占位符
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
class A{
//使用@Value("${key}")获取属性,并设置到属性上
A(@Value("${key}")String x){
this.x = x;
}
}
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 |