课程介绍
2-2 Spring Framework 手动装配
<context:component-scan>:Spring2.5的注解
@ComponentScan:Spring3.1的注解
@Component @Service是Spring2.5的注解
@Configuration 是Spring3.0的注解
@Component 作为一种由 Spring 容器托管的通用模式组件,任何被 @Component 标准的组件均为组件扫描的候选对象。类 似地,凡是被 @Component 元标注(meta-annotated)的注解,如 @Service ,当任何组件标注它时,也被视作组件扫 描的候选对象 (派生性)
装配方式
<context:component-scan> 方式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/springcontext.xsd">
<!-- 激活注解驱动特性 -->
<context:annotation-config />
<!-- 找寻被 @Component 或者其派生 Annotation 标记的类(Class),将它们注册为 Spring Bean -->
<context:component-scan base-package="com.imooc.dive.in.spring.boot" />
</beans>
@ComponentScan 方式
@ComponentScan(basePackages = "com.imooc.dive.in.spring.boot")
public class SpringConfiguration { ... }
2-3 Spring Framework手动装配自定义模式注解
自定义模式注解
/** * 一级 {@link Repository @Repository} * * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a> * @since 1.0.0 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repository
public @interface FirstLevelRepository {
String value() default "";
}
@Component
@Repository
@FirstLevelRepository
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@FirstLevelRepository
public @interface SecondLevelRepository {
String value() default "";
}
@Component
@Repository
@FirstLevelRepository
@SecondLevelRepository
注解的成员变量称之为"签名"
2-4 @Enable 模块装配两种方式
Spring Framework 3.1 开始支持”@Enable 模块驱动“。所谓“模块”是指具备相同领域的功能组件集合, 组合所形成一个独立 的单元。比如 Web MVC 模块、AspectJ代理模块、Caching(缓存)模块、JMX(Java 管 理扩展)模块、Async(异步处 理)模块等。
@Enable 注解模块举例
实现方式
注解驱动方式
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { ...
}
接口编程方式
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching { ... }
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
/**
* {@inheritDoc}
* @return {@link ProxyCachingConfiguration} or {@code AspectJCacheConfiguration} for * {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()}, respectively
* */
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] { AutoProxyRegistrar.class.getName(),ProxyCachingConfiguration.class.getName() };
case ASPECTJ:
return new String[] { AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME };
default:
return null;
}
}
@configuration和@component之间的区别
@configuration和@component之间的区别是:@Component注解的范围最广,所有类都可以注解,但是@Configuration注解一般注解在这样的类上:这个类里面有@Value注解的成员变量和@Bean注解的方法,就是一个配置类。
@component多例的,@configuration是单例的
2-5 Spring条件装配
- 从 Spring Framework 3.1 开始,允许在 Bean 装配时增加前置条件判断
条件注解举例
实现方式
配置方式 - @Profile
编程方式 - @Conditional
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
/** * The classes that must be present. Since this annotation is parsed by loading class * bytecode, it is safe to specify classes here that may ultimately not be on the * classpath, only if this annotation is directly on the affected component and * <b>not</b> if this annotation is used as a composed, meta-annotation. In order to * use this annotation as a meta-annotation, only use the {@link #name} attribute. * @return the classes that must be present */
Class<?>[] value() default {};
/** * The classes names that must be present.
* @return the class names that must be present.
* */
String[] name() default {};
}
2-6 基于配置方式实现自定义条件装配
基于配置方式实现 - @Profile
计算服务,多整数求和 sum
@Profile("Java7") : for 循环
@Profile("Java8") : Lambda
Calculate.java
public interface Calculate {
int calculate(Integer ... nums);
}
JDK7Calculate .java
public class JDK7Calculate implements Calculate {
@Override
public int calculate(Integer... nums) {
System.out.println("JDK7 的计算方法");
int sum = 0;
for (Integer i : nums) {
sum += i;
}
return sum;
}
}
JDK8Calculate.java
import java.util.stream.Stream;
public class JDK8Calculate implements Calculate {
@Override
public int calculate(Integer... nums) {
System.out.println("JDK8 的计算方法");
return Stream.of(nums).reduce(0,Integer::sum);
}
}
CalculateConfiguration .java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class CalculateConfiguration {
@Profile("JDK7")
@Bean
public Calculate getJDK7Caculate(){
return new JDK7Calculate();
}
@Profile("JDK8")
@Bean
public Calculate getJDK8Caculate(){
return new JDK8Calculate();
}
}
Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(Application.class)
.profiles("JDK7")
.run(args);
Calculate calculate = context.getBean(Calculate.class);
int sum = calculate.calculate(8,9,10);
System.out.println(sum);
}
}
2-7 基于编程方式实现条件装配
基于编程方式实现 - @ConditionalOnSystemProperty
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
public class OnSystemPropertyCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Map<String, Object> propertyMap = annotatedTypeMetadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
String name = (String) propertyMap.get("name");
String value = (String) propertyMap.get("value");
String systemValue = System.getProperty(name);
//systemValue = conditionContext.getEnvironment().getProperty(name);
return value.equals(systemValue);
}
}
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
/**
* 参考 {@link ConditionalOnProperty}
* */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnSystemPropertyCondition.class})
public @interface ConditionalOnSystemProperty {
String name() default "";
String value() default "";
}
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
public class SystemPropertyBootstrap {
@Bean
@ConditionalOnSystemProperty(name = "user.name",value = "Dell")
public String helloWorld(){
return "Hello Everyone";
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SystemPropertyBootstrap.class);
String value = (String) context.getBean("helloWorld");
System.out.println(value);
}
}
2-8 Spring Boot 自动装配
在 Spring Boot 场景下,基于约定大于配置的原则,实现 Spring 组件自动装配的目的。其中使用了
-
底层装配技术
-
Spring 模式注解装配
-
Spring @Enable 模块装配
-
Spring 条件装配装配
-
Spring 工厂加载机制
- 实现类:
SpringFactoriesLoader
- 配置资源:
META-INF/spring.factories
- 实现类:
-
自动装配举例
-
参考
META-INF/spring.factorie
-
自定义自动装配
-
HelloWorldAutoConfiguration
- 条件判断:
user.name == "Mercy"
- 模式注解:
@Configuration
- @Enable 模块:
@EnableHelloWorld -> HelloWorldImportSelector -> HelloWorldConfiguration - > helloWorld
- 条件判断:
2-9 自定义自动装配
-
实现方法
-
- 激活自动装配
@EnableAutoConfiguration
- 激活自动装配
-
- 实现自动装配 -
XXXAutoConfiguration
- 实现自动装配 -
-
- 配置自动装配实现 -
META-INF/spring.factories
- 配置自动装配实现 -
Application.java
package com.example.test2_9;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(Application.class)
.run(args);
String hello = (String) context.getBean("whaleson");
System.out.println(hello);
}
}
HelloWorldAutoConfiguration2_9.java
package com.example.test2_9;
import com.example.demo.annotation.EnableHelloWorldAnnotation;
import com.example.demo.conditional.ConditionalOnSystemProperty;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableHelloWorldAnnotation
@ConditionalOnSystemProperty(name = "user.name",value = "Dell")
public class HelloWorldAutoConfiguration2_9 {
}
HelloWorldConfiguration2_9.java
package com.example.test2_9;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HelloWorldConfiguration2_9 {
@Bean(name = "whaleson")
public String getHello(){
return "Hello World,Hello Everybody.";
}
}
HelloWorldImportSelector2_9.java
package com.example.test2_9;
import com.example.demo.configuration.HelloWorldConfiguration;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class HelloWorldImportSelector2_9 implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String []{HelloWorldConfiguration.class.getName()};
}
}