先看一下之前我们怎么使用spring来做项目开发的
1)新建一个java工程
2)引入spring核心容器环境依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
3)配置文件
<?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"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<bean id="person" class="com.bjc.pojo.Person">
<property name="name" value="张三 "></property>
<property name="age" value="18 "></property>
</bean>
</beans>
4)测试类
package com.bjc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bjc.pojo.Person;
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Person p = applicationContext.getBean("person", Person.class);
System.out.println(p);
}
}
5)输出结果
那么,我们现在使用注解开发,就可以不需要编写xml配置文件了。
1. 组件注册
1.1 配置类——@Configuration + @Bean给容器中注册组件
我们可以新建一个java类来代替之前的xml文件,用@Configuration标记该类,表示该类是一个配置类(beans标签),同时,在该类中可以定义一个一个的方法来代替xml中的bean标签,在方法上使用@Bean来表示。
例如:将上面xml改成如下的配置类
package com.bjc.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.bjc.pojo.Person;
// 配置类 == 配置文件
@Configuration // 告诉spring这是一个配置类
public class MainConfig {
// 给容器注册一个Bean,类型为返回值类型,id默认是方法名
@Bean
public Person person() {
return new Person("李四",25);
}
}
那么,怎么使用了?可以使用AnnotationConfigApplicationContext来代替ClassPathXmlApplicationContext,例如:
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
// 通过类型获取
// Person person = context.getBean(Person.class);
// 通过id获取:因为在配置类中Person定义的方法名为person,所以,id = person
Person person = context.getBean("person", Person.class);
System.out.println(person);
}
@Bean注解
如果我们想改变id的值,除了修改配置类中方法名之外,还可以给@Bean注解注入属性的方式来实现。
通过方法getBeanNamesForType来获取指定类型的name属性值,例如:
1.2 @ComponentScan-自动扫描组件
我们在xml模式的开发中,通常会配置如下的配置来实现包扫描
<context:component-scan base-package="erp.web.controller"></context:component-scan>
那么,现在我们是不是也可以在配置类中通过注解的方式来实现该功能了,答案是肯定的。我们只需要在配置类上加上注解@ComponentScan并指定包路径即可。例如:
怎么验证该配置是否有效了,我们可以定义controller与service,然后通过方法getBeanDefinitionNames来查看,如图:
1.2.1 指定扫描规则——excludeFilters
在xml中我们可以指定扫描某些包也可以指定排除某些包,在配置类中同样可以,通过属性includeFilters与excludeFilters来实现扫描指定的组件,例如:
// 配置类 == 配置文件
@Configuration // 告诉spring这是一个配置类
@ComponentScan(value="com.bjc",excludeFilters = {
// 以注解排除,排除掉Controller注解
@Filter(type=FilterType.ANNOTATION,classes = Controller.class)
})
public class MainConfig {
// 给容器注册一个Bean,类型为返回值类型,id默认是方法名
@Bean("personId")
public Person person() {
return new Person("李四",25);
}
}
注意:@Filter注解的classes是一个数组,所以我们也可以用数组的形式表示排除多个
例如:
1.2.2 指定扫描规则——includeFilters
指定扫描的时候,只包含哪些组件
但是,需要注意,要让该配置生效,需要禁用掉默认规则生效。
例如:只扫描controller
// 配置类 == 配置文件
@Configuration // 告诉spring这是一个配置类
@ComponentScan(value="com.bjc",includeFilters = {
// 以注解排除,排除掉Controller注解
@Filter(type=FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
public class MainConfig {
// 给容器注册一个Bean,类型为返回值类型,id默认是方法名
@Bean("personId")
public Person person() {
return new Person("李四",25);
}
}
1.2.3 扫描规则介绍
在FilterType中给定了很多规则
1)ANNOTATION:按照注解
2)ASSIGNABLE_TYPE:按照给定的类型
例如:
3)ASPECTJ:使用ASPECTJ表达式(基本上不会用)
4)REGEX:使用正则表达式
5)CUSTOM:自定义规则
1.2.4 自定义规则——CUSTOM
1)定义自定义规则类
package com.bjc.config;
import java.io.IOException;
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;
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:读取到当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的工厂
* */
@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);
return false;
}
}
2)使用自定义规则
package com.bjc.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import com.bjc.pojo.Person;
import com.bjc.service.BookService;
// 配置类 == 配置文件
@Configuration // 告诉spring这是一个配置类
@ComponentScan(value="com.bjc",includeFilters= {
// 以注解排除,排除掉Controller注解
//@Filter(type=FilterType.ANNOTATION,classes = {Controller.class}),
// 排除指定类型
//@Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),
// 由我们自定义的规则MyTypeFilter返回true或者false代表匹配或者不匹配
@Filter(type=FilterType.CUSTOM,classes=MyTypeFilter.class)
},useDefaultFilters = false)
public class MainConfig {
// 给容器注册一个Bean,类型为返回值类型,id默认是方法名
@Bean("personId")
public Person person() {
return new Person("李四",25);
}
}
运行结果如图:
因为我们现在自定义规则返回的是false,所以,全部给过滤掉了,只有mainConfig与personId注入到了spring容器中。接下来写一下我们自己的规则。
做一个简单的匹配规则,只要类名包含Service即匹配,例如:
运行结果如图,bookService匹配成功
1.3 @Scope——设置组件作用域
@Scope有4个取值:
1)singleton:单例,默认
注意:在单例模式下,在AOC容器被启动的时候,就会创建对象放到IOC容器中,以后每次获取就是直接从容器中拿
2)prototype:多例
注意:在多例模式中,IOC容器启动并不会创建对象,对象的创建是在每次获取对象的时候调用方法创建对象
3)request:同一个请求创建一个实例
4)session:同一个session创建一个实例
1.4 @Lazy——懒加载
所谓的懒加载就是在容器启动的时候,不要创建对象,等第一次使用(获取)Bean的时候,创建对象并初始化。
注意:懒加载是针对单例模式的。
例如:
测试类:
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("容器创建完成");
// 通过类型获取
//Person person = context.getBean(Person.class);
}
运行结果如下,“给容器注入person”并没有打印
然后,取消调用注释,再次运行,如图:
1.5 @Conditional——按照条件注册
该注解的作用是按照一定条件进行判断,满足条件给容器中注册bean
例如:如果当前系统环境是Windows就注入window的bean,如果是Linux的就注入Linux的bean
1)定义条件
Windows条件
package com.bjc.conditation;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/** 条件
* @author Administrator
*
*/
public class WindowsCondication implements Condition{
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取系统环境变量
Environment env = context.getEnvironment();
String os = env.getProperty("os.name");
if(os.contains("Windows"))
return true;
return false;
}
}
Linux条件
package com.bjc.conditation;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/** 条件
* @author Administrator
*
*/
public class LinuxCondication implements Condition{
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
String os = env.getProperty("os.name");
if(os.contains("Linux"))
return true;
return false;
}
}
2)使用注解
3)测试
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] names = context.getBeanDefinitionNames();
for(String name : names) {
System.out.println(name);
}
}
输出结果:
通过输出结果可以知道,只有Windows的bean注入到容器中了,Linux的没有。
注意:@Conditional可以放在配置类上,也可以放在方法上
1.6 @Import
通过前面的学习我们知道将一个类注册进spring容器有几种方法:
1)注解+包扫描
2)@Bean注册,可以将第三方的bean注册进容器中
接下来,我们再来看看另一种方式,通过@Import给容器中快速的导入一个组件,既然有了@Bean为什么还需要@Import了,这是因为@Bean注册的时候,如果对于很简单的业务,比如仅仅是new一个对象,使用@Bean就显得比较麻烦了,这时候我们就可以使用户@Import来注册组件到容器中。
查看该注解的源码,可以发现,该注册的使用有三种方式,如图:
1.6.1 给容器中快速导入一个组件
语法:@Import(要导入的类的Class)
使用起来很简单,如图:
测试:
注意:
1. @Import也可以导入多个
2. 使用import注册组件,容器中就会自动注册这个组件,id默认是类的全名称。
1.6.2 ImportSelector方式
我们除了可以通过@Import快速导入组件之外,还可以实现ImportSelector接口,返回需要导入组件的全类名数组
例如;
1)实现ImportSelector接口
package com.bjc.conditation;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
// 自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
// 返回值就是要导入到容器中的组件全类名
// AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {"com.bjc.pojo.Blue","com.bjc.pojo.Yellow"};
}
}
2)将该类注册进spring
3)测试运行
1.6.3 ImportBeanDefinitionRegistrar
该方式是手动的将需要的类注册进spring容器中,使用起来也很简单,只需要实现ImportBeanDefinitionRegistrar接口并写入业务逻辑即可。
1)实现ImportBeanDefinitionRegistrar
package com.bjc.conditation;
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;
import com.bjc.pojo.Red;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata: 当前类的注解信息
* BeanDefinitionRegistry: BeanDefinition注册类
把所有需要注册进容器中的bean,都可以通过BeanDefinitionRegistry的registerBeanDefinition方法手动注册
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean b = registry.containsBeanDefinition("com.bjc.pojo.Blue");
if(b) {
// 注册一个bean并指定名字
registry.registerBeanDefinition("red", new RootBeanDefinition(Red.class));
}
}
}
2)在@Import中引入该类
3)测试
1.7 FactoryBean
默认获取到的是工厂bean调用getObject创建的对象
要获取工厂Bean本身,我们需要给id前面加上&即可,例如:&colorFactoryBean,因为在FactoryBean的接口中有定义前缀为&
1)定义Factory实现FactoryBean
package com.bjc.conditation;
import org.springframework.beans.factory.FactoryBean;
import com.bjc.pojo.Colors;
public class ColorFactoryBean implements FactoryBean<Colors> {
@Override
public Colors getObject() throws Exception {
return Colors.class.newInstance();
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Colors.class;
}
/**
* 返回值true表示单例,false表示多例
*/
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return true;
}
}
2)在配置类中配置factory
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
3)测试
3.1 测试是否为单例
3.2 测试默认获取到的类是哪个
如图:显然是FactoryBean中返回的Color对象
3.3 测试获取FactoryBean本身