java框架之Spring(5)-注解驱动开发
准备
1、使用 maven 创建一个 java 项目,依赖如下:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency>
pom.xml
2、创建测试 pojo:
package com.springanno.pojo; public class User { public User() { } public User(String name, Integer age) { this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
com.springanno.pojo.User
组件注册
@Configuration-配置类
@Configuration 标注的类就是一个 Spring 配置类,配置类的作用就相当于之前我们使用的 Spring 配置文件。
1、创建配置类,通过 @Configuration 注解标注一个类为配置类:
package com.springanno.config; import org.springframework.context.annotation.Configuration; /** * 配置类 */ @Configuration public class MainConfig { }
com.springanno.config.MainConfig
2、通过读取注解配置类来创建 IOC 容器:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
test
@ComponentScan-包扫描
@ComponentScan 注解的作用相当于在 Spring 配置文件中配置 <context:component-scan> 标签。
1、创建测试 Service 类:
package com.springanno.service; import org.springframework.stereotype.Service; @Service public class UserService { }
com.springanno.service.UserService
2、使用 @ComponentScan 注解:
package com.springanno.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * 配置类 */ @Configuration @ComponentScan("com.springanno") public class MainConfig { }
com.springanno.config.MainConfig
3、测试:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService bean = applicationContext.getBean(UserService.class); System.out.println(bean); /* com.springanno.service.UserService@77e9807f */
test
@ComponentScan 相关属性:
- ComponentScan.Filter[] excludeFilters() default {} :排除扫描匹配到的类。
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}, // 不扫描指定注解标注的类
例:
- ComponentScan.Filter[] includeFilters() default {} :仅扫描匹配到的类,要生效需同时指定 useDefaultFilters = false 。
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})}, // 仅扫描指定注解标注的类 useDefaultFilters = false
例:
@ComponentScan.Filter 指定过滤规则:
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class}), // 过滤指定注解标注的类 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {UserService.class}), // 过滤给定类型的类 @ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = {"com.springanno.service.*Service"}), // 通过 ASPECTJ 表达式过滤指定类 @ComponentScan.Filter(type = FilterType.REGEX, pattern ={".*.*Service"}), // 通过正则过滤指定类
还可自定义过滤规则,先自定义一个 TypeFilter 类:
package com.springanno.config; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; import java.io.IOException; public class MyTypeFilter implements TypeFilter { /** * @param metadataReader 正在扫描的类的信息 * @param metadataReaderFactory 可获取其它类信息 * @return 如果返回 true ,说明当前扫描的类匹配成功 */ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // 获取当前类注解信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); // 获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); // 获取当前类资源 Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->"+className); return true; } }
com.springanno.config.MyTypeFilter
使用如下:
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}) // 自定义规则过滤
@Bean-实例
@Bean 注解的作用相当于在 Spring 配置文件中配置 <bean> 标签。
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { /** * 默认情况下方法名为 bean 的 id * 返回值为加入到 IOC 容器的实例 * 可通过 @Bean 注解的 value 属性指定 bean 的 id * * <bean id="user1" class="com.springanno.pojo.User"> * <property name="name" value="张三"/> * <property name="age" value="20"/> * </bean> */ @Bean(value = "user1") public User user(){ return new User("张三", 20); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Object user1 = applicationContext.getBean("user1"); System.out.println(user1); /* User{name='张三', age=20} */
test
还可以通过 FactoryBean 方式注册:
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.beans.factory.FactoryBean; public class UserFactoryBean implements FactoryBean<User> { /** * 返回的对象将会添加到容器 */ public User getObject() throws Exception { return new User("ice",22); } /** * 对象类型 */ public Class<?> getObjectType() { return User.class; } /** * 是否单例 */ public boolean isSingleton() { return true; } }
com.springanno.config.UserFactoryBean
package com.springanno.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Bean public UserFactoryBean userFactoryBean(){ return new UserFactoryBean(); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } Object user = applicationContext.getBean("userFactoryBean"); System.out.println(user); /* mainConfig userFactoryBean User{name='ice', age=22} */ // 可以看到,我们获取的是 userFactoryBean,但实际上返回的是 userFactoryBean 对应实例的 getObject 方法的返回值 // 如果我们的确要获取 userFactoryBean 对应的实例,可通过 &id 这种方式获取: UserFactoryBean userFactoryBean = (UserFactoryBean) applicationContext.getBean("&userFactoryBean"); System.out.println(userFactoryBean); /* com.springanno.config.UserFactoryBean@4461c7e3 */
test
@Scope-单/多例
@Scope 注解作用相当于在 <bean> 标签上的 scope 属性。
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration public class MainConfig { /** * @Scope * value 属性有如下可选值: * singleton(默认): 单例。IOC 容器启动时就会调用方法创建对象放到容器,之后每次使用都是直接从容器中取。 * prototype : 多例。只有要使用该对象时才会调用该方法创建对象。 * request (web 环境): 一次请求。 * session (web 环境): 一次会话。 */ @Scope("prototype") @Bean public User user(){ return new User("张三", 20); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Object user1 = applicationContext.getBean("user"); Object user2 = applicationContext.getBean("user"); System.out.println(user1==user2); /* false */
test
@Lazy-懒加载
@Lazy 注解作用相当于在 <bean> 标签上配置属性 lazy-init="true" 。
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; @Configuration public class MainConfig { /** * 懒加载:针对单实例 bean,控制容器启动时不创建对象,第一次获取该 bean 时才创建对象。 */ @Lazy @Bean public User user(){ System.out.println("创建了"); return new User("张三", 20); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); /* [无任何输出] */
test
@Conditional-条件注册
按照指定的条件进行判断,满足条件才在容器中注册 bean。
有如下示例,如果当前操作系统为 Windows 时,我们注册一个 id 为 windows 的 bean,当前系统为 Linux 时,注册一个 id 为 linux 的 bean。
1、创建 Condition 类:
package com.springanno.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 判断是否是 Windows 系统 */ public class WindowsCondition implements Condition { public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Environment environment = conditionContext.getEnvironment(); String osName = environment.getProperty("os.name").toLowerCase(); return osName.contains("windows"); } }
com.springanno.condition.WindowsCondition
package com.springanno.condition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 判断是否是 Linux 系统 */ public class LinuxCondition implements Condition { /** * @param conditionContext 判断条件能使用的上下文 * @param annotatedTypeMetadata 注解信息 */ public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { // 获取到容器使用的 BeanFactory ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory(); // 获取类加载器 ClassLoader classLoader = conditionContext.getClassLoader(); // 获取当前环境信息 Environment environment = conditionContext.getEnvironment(); // 获取 bean 定义的注册类 BeanDefinitionRegistry registry = conditionContext.getRegistry(); String osName = environment.getProperty("os.name").toLowerCase(); return osName.contains("linux"); } }
com.springanno.condition.LinuxCondition
2、使用:
package com.springanno.config; import com.springanno.condition.LinuxCondition; import com.springanno.condition.WindowsCondition; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Conditional({WindowsCondition.class}) @Bean public User windows() { return new User("windows", 20); } @Conditional({LinuxCondition.class}) @Bean public User linux() { return new User("linux", 3); } }
com.springanno.config.MainConfig
3、测试:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanNames = applicationContext.getBeanDefinitionNames(); for (String beanName : beanNames) { System.out.println(beanName); } /* mainConfig windows */ // 当前是在 windows 下,所以可以看到只注册了 windows bean
test
@Import-快速注册
@Import 提供了下面几种注册 bean 到容器的方式。
@Import
可以直接将指定 bean 实例注册到容器:
@Configuration /** * @Import 可以直接将注册指定 bean 到容器中,id 为 bean 的类全路径名 */ @Import({User.class}) public class MainConfig { }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } /* mainConfig com.springanno.pojo.User */
test
@ImportSelector
通过 @ImportSelector 返回 bean 的全路径数组批量注册:
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.List; public class MyImportSelector implements ImportSelector { public String[] selectImports(AnnotationMetadata annotationMetadata) { List<String> classNameList = new ArrayList<String>(); classNameList.add(User.class.getName()); return StringUtils.toStringArray(classNameList); } }
com.springanno.config.MyImportSelector
package com.springanno.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration /** * MyImportSelector.selectImports() 方法返回的类的全路径列表, * @Import 将会把这些全路径对应的类都注册到容器,id 为类的全路径名 */ @Import({MyImportSelector.class}) public class MainConfig { }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } /* mainConfig com.springanno.pojo.User */
test
@ImportBeanDefinitionRegistrar
通过 @ImportBeanDefinitionRegistrar 手动定义 bean 的信息完成 bean 的注册:
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * * @param annotationMetadata 当前类注解信息 * @param beanDefinitionRegistry BeanDefinition 注册类 * */ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { // 判断 IOC 容器中是否已经注册了 user boolean isContainsUser = beanDefinitionRegistry.containsBeanDefinition("user"); // 可以通过 beanDefinitionRegistry.registerBeanDefinition() 方法注册所有需要添加到容器中的 bean if(!isContainsUser){ RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class.getName()); // id=user beanDefinitionRegistry.registerBeanDefinition("user", beanDefinition); } } }
com.springanno.config.MyImportBeanDefinitionRegistrar
package com.springanno.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration /** * @Import 会执行 MyImportBeanDefinitionRegistrar.registerBeanDefinitions(),在该方法中完成 bean 的注册 */ @Import({MyImportBeanDefinitionRegistrar.class}) public class MainConfig { }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } /* mainConfig user */
test
生命周期
这里说的生命周期指的是注册进 IoC 容器的 bean 的生命周期,可通过下面几种方式来监听 bean 的生命周期变化。
@Bean的initMethod&destroyMethod属性
通过 @Bean 注解的 initMethod 属性来指定 bean 的初始化方法, destroyMethod 属性来指定 bean 的销毁方法。
package com.springanno.pojo; public class User { public User() { } public User(String name, Integer age) { System.out.println("User 构造方法"); this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void init(){ System.out.println("User 初始化方法"); } public void destroy(){ System.out.println("User 销毁方法"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
com.springanno.pojo.User
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { /* 通过 @Bean 注解的 initMethod 属性来指定 bean 的初始化方法,destroyMethod 属性来指定 bean 的销毁方法 构造方法: 单实例 bean 在容器启动的时候执行 多实例 bean 在每次获取 bean 的时候执行 初始化方法: 构造方法执行后执行 销毁方法: 单实例 bean 在容器关闭时执行 多实例时,容器不会管理这个 bean ,所以不会调用销毁方法 */ @Bean(initMethod = "init", destroyMethod = "destroy") public User user(){ return new User("tom", 12); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); ((AnnotationConfigApplicationContext) applicationContext).close(); /* User 构造方法 User 初始化方法 User 销毁方法 */
test
@PostConstruct&@PreDestroy注解
通过将 @PostConstruct 和 @PreDestroy 注解标注在 bean 的方法上让其成为初始化方法和销毁方法。
package com.springanno.pojo; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; public class User { public User() { } public User(String name, Integer age) { System.out.println("User 构造方法"); this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @PostConstruct public void init() throws Exception { System.out.println("User init"); } @PreDestroy public void destroy() throws Exception { System.out.println("User destroy"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
com.springanno.pojo.User
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Bean public User user(){ return new User("tom", 12); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); ((AnnotationConfigApplicationContext) applicationContext).close(); /* User 构造方法 User init User destroy */
test
实现InitializingBean&DisposableBean接口
通过让 bean 实现 InitializingBean 来定义初始化逻辑,实现 DisposableBean 来定义销毁逻辑。
package com.springanno.pojo; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; public class User implements InitializingBean, DisposableBean { public User() { } public User(String name, Integer age) { System.out.println("User 构造方法"); this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void afterPropertiesSet() throws Exception { System.out.println("User afterPropertiesSet"); } public void destroy() throws Exception { System.out.println("User destroy"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
com.springanno.pojo.User
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig { @Bean public User user(){ return new User("tom", 12); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); ((AnnotationConfigApplicationContext) applicationContext).close(); /* User 构造方法 User afterPropertiesSet User destroy */
test
实现BeanPostProcessor接口
定义一个类实现 BeanPostProcessor 接口,将其注册到容器中后它便可以监听容器中所有 bean 的初始化前和初始化后操作。
package com.springanno.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyBeanPostProcessor implements BeanPostProcessor { /** * 初始化操作执行之前执行 */ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + "---->postProcessBeforeInitialization"); return bean; } /** * 初始化操作执行之后执行 */ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + "---->postProcessAfterInitialization"); return bean; } }
com.springanno.config.MyBeanPostProcessor
package com.springanno.pojo; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; public class User { public User() { } public User(String name, Integer age) { System.out.println("User 构造方法"); this.name = name; this.age = age; } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @PostConstruct public void init() throws Exception { System.out.println("User init"); } @PreDestroy public void destroy() throws Exception { System.out.println("User destroy"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
com.springanno.pojo.User
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); ((AnnotationConfigApplicationContext) applicationContext).close(); /* User 构造方法 user---->postProcessBeforeInitialization User init user---->postProcessAfterInitialization User destroy */
test
Spring 底层给 bean 赋值、@Autowried 注入、生命周期注解等功能都是通过 BeanPostProcessor 完成的。
ScheduledAnnotationBeanPostProcessor (org.springframework.scheduling.annotation) AdvisorAdapterRegistrationManager (org.springframework.aop.framework.adapter) BeanPostProcessorChecker in PostProcessorRegistrationDelegate (org.springframework.context.support) ImportAwareBeanPostProcessor in ConfigurationClassPostProcessor (org.springframework.context.annotation) LoadTimeWeaverAwareProcessor (org.springframework.context.weaving) AbstractAdvisingBeanPostProcessor (org.springframework.aop.framework) AbstractBeanFactoryAwareAdvisingPostProcessor (org.springframework.aop.framework.autoproxy) DestructionAwareBeanPostProcessor (org.springframework.beans.factory.config) InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation) ApplicationListenerDetector in PostProcessorRegistrationDelegate (org.springframework.context.support) ApplicationContextAwareProcessor (org.springframework.context.support) MergedBeanDefinitionPostProcessor (org.springframework.beans.factory.support) InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation) RequiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation) AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation) ApplicationListenerDetector in PostProcessorRegistrationDelegate (org.springframework.context.support) BeanValidationPostProcessor (org.springframework.validation.beanvalidation) InstantiationAwareBeanPostProcessor (org.springframework.beans.factory.config) SmartInstantiationAwareBeanPostProcessor (org.springframework.beans.factory.config) CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)
BeanPostProcessor的子接口及实现类
组件赋值
@Value&@PropertySource
使用 @Value 可以为属性赋基本数值,也可以通过语法 @Value("#{SpEL}") 通过 SpEL 表达式赋值,还可以通过 ${属性名} 取出配置文件(环境变量)中的值,而 @PropertySource 注解就可以帮我们读取属性文件中的属性到环境变量。
user.testKey=testValue
user.properties
package com.springanno.pojo; import org.springframework.beans.factory.annotation.Value; public class User { public User() { } public User(String name, Integer age) { this.name = name; this.age = age; } @Value("张三") private String name; @Value("#{19+3}") private Integer age; @Value("${user.testKey}") private String testKey; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", testKey='" + testKey + '\'' + '}'; } }
com.springanno.pojo.User
package com.springanno.config; import com.springanno.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @PropertySource(value = {"classpath:user.properties"}) // 使用该注解将指定属性文件加载到环境变量 public class MainConfig { @Bean public User user() { return new User(); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); User bean = applicationContext.getBean(User.class); System.out.println(bean); /* User{name='张三', age=22, testKey='testValue'} */
test
@Profile
通过使用 @Profile 注解,Spring 可以实现根据当前环境动态的激活和切换一些列组件的功能。
package com.springanno.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration public class MainConfig { /* 指定环境了的组件只有在指定环境下运行时才会注册到容器中,不指定环境的组件任何环境都能注册到容器中,默认为 default 环境 这里模拟三个环境下的数据源 */ @Profile("dev") @Bean("dataSource") public String devStr(){ return "devDataSource"; } @Profile("test") @Bean("dataSource") public String testStr(){ return "testDataSource"; } @Profile("prod") @Bean("dataSource") public String prodStr(){ return "prodDataSource"; } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Object dataSource = applicationContext.getBean("dataSource"); System.out.println(dataSource); // 通过 VM-options 来指定运行环境 // -Dspring.profiles.active=dev 时,输出 devDataSource // -Dspring.profiles.active=test 时,输出 testDataSource // -Dspring.profiles.active=prod 时,输出 prodDataSource
test 例:通过 VM-options 来指定运行环境
String profileStr = "test"; AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 指定运行环境 applicationContext.getEnvironment().setActiveProfiles(profileStr); applicationContext.register(MainConfig.class); applicationContext.refresh(); Object dataSource = applicationContext.getBean("dataSource"); System.out.println(dataSource); /* 编码指定运行环境 profileStr="test" 时,输出 testDataSource profileStr="prod" 时,输出 prodDataSource profileStr="dev" 时,输出 devDataSource */
test 例:编码指定运行环境
组件注入
Spring 利用 DI(依赖注入),完成对 IoC 容器中各个组件的依赖赋值。
@Autowired
@Autowired 将会自动从 IoC 容器中查找与标注属性同类型的 bean 注入, 如果容器中有多个相同类型的 bean,将会注入 id 与 属性名相同的那个 bean。
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { }
com.springanno.dao.UserDao
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { /** * 默认情况下,如果容器中没有该属性类型对应的 bean,那么将会抛出异常 * 可通过指定 @Autowired 的属性 required = false 让此次的自动装配不必须,此时容器中如果不存在该类型 bean 就不会抛出异常 */ @Autowired(required = false) private UserDao userDao; public void printDao() { System.out.println(userDao); } }
com.springanno.service.UserService
package com.springanno.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); /* com.springanno.service.UserService@60dcc9fe com.springanno.dao.UserDao@222114ba */
test
@Autowired 不仅可以用在属性上,还可以用在方法上:
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; /* 将会从容器中找到与参数类型相同的 bean 传入来执行该方法 */ @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
com.springanno.service.UserService
也可以标注在构造器上:
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; /* 将会从容器中找到与参数类型相同的 bean 传入来执行该构造器来创建 UserService 实例 */ @Autowired public UserService(UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
com.springanno.service.UserService
不仅如此,还可以标注在参数上:
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; /* 将会从容器中找到与参数类型相同的 bean 传入来执行该构造器来创建 UserService 实例 */ public UserService(@Autowired UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
com.springanno.service.UserService
如果当前组件只有一个有参构造器, @Autowired 可以省略不写:
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; /* 当前组件只有一个有参构造器 将会从容器中找到与参数类型相同的 bean 传入来自动执行该构造器来创建 UserService 实例 */ public UserService (UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
com.springanno.service.UserService
通过 @Bean 标注的方法注册 bean 时,该方法的参数也会默认从容器中获取:
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
com.springanno.dao.UserDao
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void printDao() { System.out.println(userDao); } }
com.springanno.service.UserService
package com.springanno.config; import com.springanno.dao.UserDao; import com.springanno.service.UserService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { @Bean("userService2") public UserService userService(UserDao userDao){ UserService userService = new UserService(); userService.setUserDao(userDao); return userService; } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = (UserService) applicationContext.getBean("userService2"); UserDao userDao = applicationContext.getBean(UserDao.class); userService.printDao(); System.out.println(userDao); /* com.springanno.dao.UserDao@21507a04 com.springanno.dao.UserDao@21507a04 */
test
@Qualifier
可以通过 @Qualifier 与 @Autowired 搭配使用来指定此次要装配 bean 的 id,如下:
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
com.springanno.dao.UserDao
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @Service public class UserService { @Qualifier("userDao") // 指定装配 id 为 userDao 的 bean @Autowired private UserDao userDao; public void printDao(){ System.out.println(userDao); } }
com.springanno.service.UserService
package com.springanno.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); /* com.springanno.service.UserService@60dcc9fe com.springanno.dao.UserDao@222114ba */
test
@Primary
@Primary 也是用来对自动装配进行控制的,他用来指定当容器中存在多个类型相同的 bean 时,自动装配优先装配哪个 bean,和 @Bean 一起使用。当然,它不能和 @Qualifier 同时使用。
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
com.springanno.dao.UserDao
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserDao userDao; public void printDao(){ System.out.println(userDao); } }
com.springanno.service.UserService
package com.springanno.config; import com.springanno.dao.UserDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration @ComponentScan("com.springanno") public class MainConfig { @Primary // id 为 userDao2 的 bean 优先装配 @Bean public UserDao userDao2() { return new UserDao(); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); UserDao userDao = (UserDao) applicationContext.getBean("userDao2"); System.out.println(userDao); /* com.springanno.service.UserService@3b2da18f com.springanno.dao.UserDao@5906ebcb com.springanno.dao.UserDao@5906ebcb */
test
@Resource
@Resource 注解(JSR250 中定义)相当于 @Autowired 和 @Qualifier 注解一起使用,既能完成自动装配,也能指定要装配 bean 的 id,不支持 @Primary 注解。
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
com.springanno.dao.UserDao
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class UserService { @Resource(name = "userDao2") private UserDao userDao; public void printDao(){ System.out.println(userDao); } }
com.springanno.service.UserService
package com.springanno.config; import com.springanno.dao.UserDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { @Bean public UserDao userDao2() { return new UserDao(); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); UserDao userDao = (UserDao) applicationContext.getBean("userDao2"); System.out.println(userDao); /* com.springanno.service.UserService@2d9d4f9d com.springanno.dao.UserDao@4034c28c com.springanno.dao.UserDao@4034c28c */
test
@Inject
@Inject 注解(JSR330 中定义)使用与 @Autowired 注解一致,只是没有属性。
package com.springanno.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public UserDao(){} }
com.springanno.dao.UserDao
package com.springanno.service; import com.springanno.dao.UserDao; import org.springframework.stereotype.Service; import javax.inject.Inject; @Service public class UserService { /* 与 Autowired 相同,需导包 <dependency> <groupId>javax.inject</groupId> <artifactId>com.springsource.javax.inject</artifactId> <version>1.0.0</version> </dependency> */ @Inject private UserDao userDao; public void printDao() { System.out.println(userDao); } }
com.springanno.service.UserService
package com.springanno.config; import com.springanno.dao.UserDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration @ComponentScan("com.springanno") public class MainConfig { @Primary @Bean public UserDao userDao2() { return new UserDao(); } }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); userService.printDao(); UserDao userDao = (UserDao) applicationContext.getBean("userDao2"); System.out.println(userDao); /* com.springanno.service.UserService@2aa5fe93 com.springanno.dao.UserDao@5c1a8622 com.springanno.dao.UserDao@5c1a8622 */
test
@Autowired 是 Spring 定义的,而 @Resource 和 @Inject 是Java 规范中定义的。
Aware接口
如果我们自定义的组件想要使用 Spring 容器底层的一些组件,例如 ApplicationContext、BeanFactory 等,可以让自定义组件实现相应的 Aware 接口,Spring 容器启动时会通过接口的回调给我们注入相应的组件。
package com.springanno.service; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanNameAware; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @Service("testUserServer") public class UserService implements ApplicationContextAware, BeanNameAware, EnvironmentAware { public void setBeanName(String beanName) { System.out.println(beanName); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println(applicationContext.getBean("testUserServer")); } public void setEnvironment(Environment environment) { System.out.println(environment); } }
com.springanno.service.UserService
package com.springanno.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.springanno") public class MainConfig { }
com.springanno.config.MainConfig
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); /* testUserServer StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[MapPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]} com.springanno.service.UserService@3f56875e */
test
下面是 Spring 提供的继承了 Aware 接口的类:
ApplicationEventPublisherAware (org.springframework.context) NotificationPublisherAware (org.springframework.jmx.export.notification) MessageSourceAware (org.springframework.context) BeanFactoryAware (org.springframework.beans.factory) EnvironmentAware (org.springframework.context) EmbeddedValueResolverAware (org.springframework.context) ResourceLoaderAware (org.springframework.context) ImportAware (org.springframework.context.annotation) LoadTimeWeaverAware (org.springframework.context.weaving) BeanNameAware (org.springframework.beans.factory) BeanClassLoaderAware (org.springframework.beans.factory) ApplicationContextAware (org.springframework.context)
extends Aware
AOP
示例
1、创建被代理类:
package com.springanno.service; public class CalculateService { public Integer div(int i,int j){ return i/j; } }
com.springanno.service.CalculateService
2、编写切面类:
package com.springanno.aspects; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import java.util.Arrays; @Aspect public class LogAspect { // 如果要在别的类引用这个切入点表达式,可以使用 com.springanno.aspects.LogAspect.pc() @Pointcut(value = "execution(* com.springanno.service.CalculateService.div(..))") public void pc() { } @Before("pc()") public void logStart(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); String methodName = joinPoint.getSignature().getName(); System.out.printf("%s 运行了,参数为 %s \n", methodName, Arrays.toString(args)); } @After("pc()") public void logEnd(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println(methodName + " 执行完毕"); } @AfterReturning(value = "pc()", returning = "result") public void logReturn(JoinPoint joinPoint, Object result) { // 注意,如果使用 JoinPoint 参数,该参数只能在第一个位置 String methodName = joinPoint.getSignature().getName(); System.out.printf("%s 运行了,结果为 %s \n", methodName, result); } @AfterThrowing(value = "pc()",throwing = "ex") public void logException(JoinPoint joinPoint,Exception ex){ String methodName = joinPoint.getSignature().getName(); System.out.printf("%s 出现异常,异常信息为 %s \n", methodName, ex); } }
com.springanno.aspects.LogAspect
3、将被代理类与切面类注册到容器,使用 @EnableAspectJAutoProxy 注解开启切面自动代理功能:
package com.springanno.config; import com.springanno.aspects.LogAspect; import com.springanno.service.CalculateService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @EnableAspectJAutoProxy // 开启切面自动代理功能 @Configuration public class MainConfig { @Bean public CalculateService calculateService(){ return new CalculateService(); } @Bean public LogAspect logAspect(){ return new LogAspect(); } }
com.springanno.config.MainConfig
4、测试:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); CalculateService calculateService = applicationContext.getBean(CalculateService.class); calculateService.div(4, 1); /* div 运行了,参数为 [4, 1] div 执行完毕 div 运行了,结果为 4 */
test
updating....
java框架之Spring(5)-注解驱动开发的更多相关文章
- java框架之Spring(2)-注解配置IOC&AOP配置
注解配置IoC 准备 1.要使用注解方式配置 IoC,除了之前引入的基础 jar 包,还需要引入 spring-aop 支持包,如下: 2.在 applicationContext.xml 中引入 c ...
- 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!
写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...
- 【Spring注解驱动开发】在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean
写在前面 在前面的文章中,我们学习了如何使用@Import注解向Spring容器中导入bean,可以使用@Import注解快速向容器中导入bean,小伙伴们可以参见<[Spring注解驱动开发] ...
- 【Spring注解驱动开发】关于BeanPostProcessor后置处理器,你了解多少?
写在前面 有些小伙伴问我,学习Spring是不是不用学习到这么细节的程度啊?感觉这些细节的部分在实际工作中使用不到啊,我到底需不需要学习到这么细节的程度呢?我的答案是:有必要学习到这么细节的程度,而且 ...
- 【Spring注解驱动开发】如何使用@Value注解为bean的属性赋值,我们一起吊打面试官!
写在前面 在之前的文章中,我们探讨了如何向Spring的IOC容器中注册bean组件,讲解了有关bean组件的生命周期的知识.今天,我们就来一起聊聊@Value注解的用法. 项目工程源码已经提交到Gi ...
- 0、Spring 注解驱动开发
0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...
- 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则
写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...
- 【Spring注解驱动开发】自定义TypeFilter指定@ComponentScan注解的过滤规则
写在前面 Spring的强大之处不仅仅是提供了IOC容器,能够通过过滤规则指定排除和只包含哪些组件,它还能够通过自定义TypeFilter来指定过滤规则.如果Spring内置的过滤规则不能够满足我们的 ...
- 【Spring注解驱动开发】使用@Scope注解设置组件的作用域
写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...
随机推荐
- GridView不执行RowCommand事件
web.config里把viewstate禁用了.如果是的话在页面里单独开起来就好了. <%@ Page Title="" Language="C#" M ...
- matplotlib绘图不显示问题解决plt.show()
最近在看<Python数据分析>这本书,而自己写代码一直用的是Pycharm,在练习的时候就碰到了plot()绘图不能显示出来的问题.网上翻了一下找到知乎上一篇回答,试了一下好像不行,而且 ...
- 微信小程序,图片居中显示,适配不同机型
<view style='width:100%;height:100%;text-align:center;' class="picture-2"> <ima ...
- MyBatis 配置多数据源
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- BarTender中如何为称重设备设置秤显示?
有关BarTender 2016表单中的称显示,前面都给大家介绍过了,包括秤显示属性设置,链接数据源属性设置等等.本文,将以图文并茂的方式,教大家如何为称重设备设置秤显示控件. 我们打开BarTend ...
- SVD与SVD++
参考自:http://blog.csdn.net/wjmishuai/article/details/71191945 http://www.cnblogs.com/Xnice/p/4522671.h ...
- sklearn中的模型评估-构建评估函数
1.介绍 有三种不同的方法来评估一个模型的预测质量: estimator的score方法:sklearn中的estimator都具有一个score方法,它提供了一个缺省的评估法则来解决问题. Scor ...
- git error: Your local changes to the following files would be overwritten by merge:xxxxxx ,Please commit your changes or stash them before you merge.的phpstorm解决办法
git报错 error: Your local changes to the following files would be overwritten by merge: .idea/encoding ...
- 禅道docker化(Centos7.2)
操作步骤 确认服务器禅道版本及容器禅道版本 服务器禅道版本:9.6.2 容器禅道版本:9.6.3 版本sql比对 下载官方9.6.3源码包url:http://dl.cnezsoft.com/zent ...
- SQL 四大功能DDL/DML/DCL/TCL
SQL主要分成四部分:(1)数据定义.(SQL DDL)用于定义SQL模式.基本表.视图和索引的创建和撤消操作.(2)数据操纵.(SQL DML)数据操纵分成数据查询和数据更新两类.数据更新又分成插入 ...