Spring注解开发
浅尝Spring注解开发,基于Spring 4.3.12
包含自定义扫描组件、自定义导入组件、手动注册组件、自动注入方法和参数、使用Spring容器底层组件等
Spring注解开发_自定义注册组件、属性赋值、自动装配
Spring注解开发_Bean生命周期及执行过程
Spring注解开发_AOP原理及完整过程分析(源码)
Spring注解开发_声明式事务及原理
Spring注解开发_简单理解BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor、ApplicationListener
Spring注解开发_Spring容器创建概述
Spring注解开发_Servlet 3.0 与 SpringMVC
配置
@Configuration配置类
告诉Spring这是一个配置类,代替以前的xml文件,配置类=配置文件
@Configuration
public class MainConfig {
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
}
注册组件
给容器中注册组件有以下方法:
-
包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
-
@Bean[导入的第三方包里面的组件]
-
@Import[快速给容器中导入一个组件]
- @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
- ImportSelector:返回需要导入的组件的全类名数组;
- ImportBeanDefinitionRegistrar:手动注册bean到容器中
-
使用Spring提供的 FactoryBean(工厂Bean);
-
默认获取到的是工厂bean调用getObject创建的对象
-
要获取工厂Bean本身,我们需要给id前面加一个&
如:&colorFactoryBean
@Bean注册
给容器中注册一个Bean,类型为返回值的类型,id默认是用方法名作为id,也可以指定id
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
IoC容器
创建一个IoC容器AnnotationConfigApplicationContext,传入配置类,获取Bean
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType) {
System.out.println(name);
}
⭐@ComponentScan扫描组件
@Component(包括@Controller@Service@Repository和@Bean目的一样)和@ComponentScan的区别
- @Component:将普通JavaBean实例化到spring容器中,Spring容器统一管理,用起来不用自己new了,相当于配置文件中的<bean id=“” class=“”/>
- @ComponentScan:一般用在核心配置类,由你来定义哪些包需要被扫描。
- @Component标在类上,@Bean标在方法上,两者使用在普通类或方法上时,都不起作用,只有放在配置类中才能注册组件(@Bean可以在写在配置类中,@Component由于本身标在类上,所以不能写在类中),而@ComponentScan就是给了@Component一种注册进容器的办法,只要扫描到@Component标注的类就直接注册进容器
-
自动扫描组件,可有多个@ComponentScan,或一个@ComponentScans
-
value:指定要扫描的包
所有指定的包下的类都要进入扫描,是否能够通过,需要看下面使用了什么规则,有排除、包含、指定类型、正则、和自定义规则,自定义规则通过反射获取包中所有类,然后判断是否能够进入ioc容器中。
-
excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
-
includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
- FilterType.ANNOTATION:按照注解
- FilterType.ASSIGNABLE_TYPE:按照给定的类型
- FilterType.ASPECTJ:使用ASPECTJ表达式
- FilterType.REGEX:使用正则指定
-
FilterType.CUSTOM:使用自定义规则,需要TypeFilter实现类
-
useDefaultFilters:不使用默认的 Filter进行扫描。在includeFilters中使用,取消默认扫描,指定扫描对象,可以在SpringMVC中使用
@ComponentScan(value="com.atguigu",includeFilters = {
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},useDefaultFilters = false)
FilterType.CUSTOM:使用自定义规则,需要实现TypeFilter接口
public class MyTypeFilter implements TypeFilter {
@Override
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);
if(className.contains("er")){
return true;
}
return false;
}
}
@Scope作用域
调整作用域,单实例,多实例
- prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象;
- singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿
- request:同一次请求创建一个实例。web环境中
- session:同一个session创建一个实例。web环境中
@Scope("singleton")
@Lazy
@Bean("person")
public Person person(){
System.out.println("给容器中添加Person....");
return new Person("张三", 25);
}
@Lazy懒加载
- 在单实例bean应用:默认在容器启动的时候创建对象,配置懒加载后,容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化,仍是单实例。
⭐@Conditional条件注册Bean
指定条件向容器中注入Bean
@Conditional({Condition}) : 按照一定的条件进行判断,满足条件才将Bean注册进容器,可以标在@Bean方法和@Configuration类上
- 添加了@Conditional({Condition}) 的Bean方法或配置Bean的类,只有在满足Condition中的条件才能执行(装配Bean)
- Condition:需要实现Condition接口的条件类,可以判断容器中的bean注册情况,也可以给容器中注册bean
Condition条件实现类
创建两个判断条件,判断系统是Windows还是Linux,自定义注册不同的Bean,注解可以标在方法上,也可以标在类上
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
ClassLoader classLoader = context.getClassLoader();
Environment environment = context.getEnvironment();
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
boolean definition = registry.containsBeanDefinition("person");
if(property.contains("linux")){
return true;
}
return false;
}
}
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")){
return true;
}
return false;
}
}
标在方法上
@Configuration
public class MainConfig2 {
@Conditional(WindowsCondition.class)
@Bean("bill")
public Person person01(){
return new Person("Bill Gates",62);
}
@Conditional(LinuxCondition.class)
@Bean("linus")
public Person person02(){
return new Person("linus", 48);
}
}
标在类上
@Conditional({WindowsCondition.class})
@Configuration
public class MainConfig2 {
}
测试方法
-
使用,可配置VM参数:-Dos.name=linux
测试
public class IOCTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void test03(){
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
for (String name : namesForType) {
System.out.println(name);
}
Map<String, Person> persons = applicationContext.getBeansOfType(Person.class);
System.out.println(persons);
}
}
⭐@Import注册
@Import的三种方法都是写在@Import注解中,都是向容器中导入组件
- @Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名
- ImportSelector:返回需要导入的组件的全类名数组
- ImportBeanDefinitionRegistrar:手动注册bean到容器中
@Import
-
@Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名com.xxx.bean.Color
@Configuration
@Import({Color.class,Red.class})
public class MainConfig2 {
/...
}
-
测试
public class IOCTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void testImport(){
printBeans(applicationContext);
}
private void printBeans(AnnotationConfigApplicationContext applicationContext){
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
⭐ImportSelector注册
-
接口,可以选择注册指定的类,返回需要导入的组件的全类名数组;
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"};
}
}
-
配置
@Configuration
@Import({Color.class,Red.class,MyImportSelector.class})
public class MainConfig2 {
}
-
测试
public class IOCTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void testImport(){
printBeans(applicationContext);
Blue bean = applicationContext.getBean(Blue.class);
System.out.println(bean);
}
private void printBeans(AnnotationConfigApplicationContext applicationContext){
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
⭐ImportBeanDefinitionRegistrar
自定义注入组件,在Spring注解源码中使用
-
实现接口,指定规则,手动注入新的bean
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue");
if(definition && definition2){
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}
-
配置
@Configuration
@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
}
-
测试
public class IOCTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void testImport(){
printBeans(applicationContext);
Blue bean = applicationContext.getBean(Blue.class);
System.out.println(bean);
}
private void printBeans(AnnotationConfigApplicationContext applicationContext){
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
⭐FactoryBean注册
多用于第三方组件
接口,给容器中注入组件,使用Spring提供的 FactoryBean(工厂Bean)创建的Bean
-
默认获取到的是工厂bean调用getObject创建的对象,而不是工厂对象本身
-
要获取工厂Bean本身,我们需要给id前面加一个"&“如:context.getBean(”&xxx")
-
实现接口,创建工厂
public class ColorFactoryBean implements FactoryBean<Color> {
@Override
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
-
配置
@Configuration
public class MainConfig2 {
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
-
测试
public class IOCTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void testImport(){
Object bean2 = applicationContext.getBean("colorFactoryBean");
Object bean3 = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的类型:"+bean2.getClass());
System.out.println(bean2 == bean3);
Object bean4 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());
}
private void printBeans(AnnotationConfigApplicationContext applicationContext){
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
属性赋值
@Value赋值
基本数值
public class Person {
@Value("张三")
private String name;
}
SpEL表达式 #{}
public class Person {
@Value("张三")
private String name;
@Value("#{20-2}")
private Integer age;
}
@PropertySource配置文件取值
读取properties配置文件的值,例如读取数据库连接信息
读取配置文件
使用@PropertySource读取外部配置文件
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
@Bean
public Person person(){
return new Person();
}
}
配置文件person.properties
person.nickName=法外狂徒
配置文件取值${}
取出配置文件properties中的值
public class Person {
@Value("张三")
private String name;
@Value("#{20-2}")
private Integer age;
@Value("${person.nickName}")
private String nickName;
}
运行时环境变量取值
运行时properties配置文件加载进环境变量,可以直接取出
public class IOCTest_PropertyValue {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
@Test
public void test01(){
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);
applicationContext.close();
}
}
自动装配
AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能
几种自动装配:
- @Autowired:自动注入
- Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
- @Autowired:构造器,参数,方法,属性,都是从容器中获取参数组件的值
- 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx)
@Autowired
自动注入:
- 默认优先按照类型去容器中找对应的组件,同applicationContext.getBean(BookDao.class),找到就赋值
- 如果找到多个相同类型的组件,再将**属性的名称(@Bean的value或者方法名)**作为组件的id去容器中查找,同applicationContext.getBean(“bookDao”)
- 如果都相同就会报错
@Qualifier
指定装配:
- @Qualifier(“bookDao”):使用@Qualifier指定需要装配的组件的id,而不是使用属性名自动装配,默认一定要将属性赋值好,没有就会报错;
- 如果没有就不装配,可以使用@Autowired(required=false)表示
@Primary
默认装配:
- @Primary:让Spring进行自动装配的时候,默认使用首选的bean;
- 也可以继续使用@Qualifier指定需要装配的bean的名字
下面是两个Bean的情况
@Configuration
@ComponentScan({"com.atguigu.service","com.atguigu.dao",
"com.atguigu.controller","com.atguigu.bean"})
public class MainConifgOfAutowired {
@Primary
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
@Service
public class BookService {
@Qualifier("bookDao")
@Autowired(required=false)
private BookDao bookDao;
}
@Resource
-
可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的
-
没有能支持@Primary和@Autowired(reqiured=false)的功能
-
@Autowired跟spring强耦合,如果换成了JFinal等其他框架,功能就会失效。而@Resource是JSR-250提供的,它是Java标准,绝大部分框架都支持
@Resource(name="bookDao2")
private BookDao bookDao;
@Inject
@Inject
private BookDao bookDao;
⭐@Autowired注入方法,参数
@Autowired:构造器,参数,方法,属性都是从容器中获取参数组件的值
使用[@Bean+方法参数]的方式在SpringBoot中使用很多,在使用各种配置时,只配置方法,方法中的参数直接从容器中获取
- [标注在方法位置]:@Bean+方法参数,参数从容器中获取,默认不写@Autowired效果是一样的,都能自动装配
- [标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
- 放在参数位置
方法上
-
@Autowired直接标在方法上
@Autowired
public void setCar(Car car) {
this.car = car;
}
-
如果是@Bean标注的方法,参数位置不标@Autowired,参数也可以自动装配,都是从容器中获取
@Bean
public Color color(Car car){
Color color = new Color();
color.setCar(car);
return color;
}
构造器
参数上
⭐Aware使用Spring底层组件
-
自定义组件想要使用Spring容器底层的一些组件(如:ApplicationContext,BeanFactory,xxx)只要自定义组件实现xxxAware
-
在创建对象的时候,会调用接口规定的方法注入相关组件,把Spring底层一些组件注入到自定义的Bean中
-
xxxAware:对应功能使用xxxProcessor
ApplicationContextAware==>ApplicationContextAwareProcessor
实现过程在BeanPostProcessor后置处理器中
使用不同的底层组件
@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的ioc:"+applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println("当前bean的名字:"+name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
System.out.println("解析的字符串:"+resolveStringValue);
}
}
@Profile环境标识
@Profile:Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
环境:开发环境、测试环境、生产环境,分别对应:
数据源:devDataSource、testDataSource、prodDataSource
@Profle:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
- 标注
- 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
- 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
- 没有标注环境标识的bean,在任何环境下都是加载的;
- 使用
- 如果对Bean标注了@Profile(“default”),即使不激活,也默认使用此环境。即使标注了,如果没有对应的环境则都不激活,除了"default"
- 在启动时添加VM参数
-Dspring.profiles.active=test
,会使用此环境 - 不使用有参的AnnotationConfigApplicationContext创建ioc,使用无参构造器,在注册配置类之前设置激活环境。
- [SpringBoot]在类上使用注解@ActiveProfiles(“test”)激活
创建多种数据源环境匹配不同数据库
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
private String driverClass;
@Bean
public Yellow yellow(){
return new Yellow();
}
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
driverClass = valueResolver.resolveStringValue("${db.driverClass}");
}
}
测试
public class IOCTest_Profile {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
applicationContext.getEnvironment().setActiveProfiles("dev");
applicationContext.register(MainConfigOfProfile.class);
applicationContext.refresh();
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String string : namesForType) {
System.out.println(string);
}
Yellow bean = applicationContext.getBean(Yellow.class);
System.out.println(bean);
applicationContext.close();
}
}
输出
devDataSource
com.atguigu.bean.Yellow@1ab3a8c8
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)