基础环境
基本组件(Spring-Context)
aopbeanscontextcoreexpressionjcl
基本调用
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Person person = context.getBean(Person.class);
System.out.println(person);
}
}
通过注解配置获取上下文,开启工作。
配置
@Configuration
@Configuration
public class Config {
@Bean(value = "person")
public Person person(){
return new Person("seconder");
}
}
通过@Configuration进行配置。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class, Person.class);
获取配置信息,支持多个配置类传入。
@ComponentScan
@ComponentScan("com.seconder")
@Configuration
public class Config {
}
指定额外扫描包。
Filter
type:导入类型指定
ANNOTATION:通过注解判断ASSIGNABLE_TYPE:通过类型判断ASPECTJ:ASPECTJ表达式REGEX:正则表达式CUSTOM:自定义规则classes:指定指定类自定义规则
public class Filter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return false; } }需要实现指定接口
TypeFilter
metadataReader:当前扫描的类的信息metadataReaderFactory:可获取其他扫描的类的信息@ComponentScan(value="com.godme",useDefaultFilters = false, excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = {com.godme.filter.Filter.class}) })这样就可以直接使用了。
字符判断
public class Filter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = metadataReader.getClassMetadata().getClassName(); if(className.contains("seconder")){ return true; } return false; } }父子不同在
public class Filter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { ClassMetadata metadata = metadataReader.getClassMetadata(); if(!metadata.hasSuperClass()){ return true; } if(metadataReaderFactory.getMetadataReader(metadata.getSuperClassName()) == null){ return true; } return false; } }
excludeFilters
排除符合特征的导入项
@ComponentScan(value="com.seconder", excludeFilters = {
@Filter(type=FilterType.CUSTOM, classes={
Person.class
})
})
可以指定多个@Filter过滤器,每个过滤器还可以指定多个过滤类。
includeFilters
同excludeFilters,不过含义相反,不是排除,而是只包含。
@ComponentScan(useDefaultFilters = false,includeFilters = {
@Filter(type=FilterType.ANNOTATION, classes={
Person.class
})
})
默认的包含规则useDefaultFilters为true,也就是全包含,不过滤。
excludeFilters可以生效,但是includeFilters需要把它设置为false。
否则includeFilters没有包含,却被默认的过滤器给直接导入了。
ComponentScans@ComponentScans(value = { @ComponentScan(value="com.godme",useDefaultFilters = false, excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = {com.godme.filter.Filter.class}) }) })可以一次性指定多个导入项。
导入和注册
- 单组件:注册
ComponentScan:单包扫描导入,多个组件,多过滤规则ComponentScans:多包导入,ComponentScan集合总结下来:
单组件主动注册和多组件类型过滤
对象
@Bean
@Configuration
public class Config {
@Bean
public Person person(){
return new Person("seconder");
}
}
配置下,@Bean标记方法
- 返回值:类型
- 方法名:对象名
Person如果没有无参构造会报错,不知道是不是必须要有,为什么。
value
@Configuration
public class Config {
@Bean(value = "person")
public Person person(){
return new Person("seconder");
}
}
value可指定变量名,而不采用方法名,不指定默认采用方法名。
@Scope
-
SCOPE_SINGLETON(singleton):单实例,每次获取都是同一个对象,默认 -
SCOPE_PROTOTYPE(prototype):多实例,每次获取都重新创建对象 -
SCOPE_REQUEST(request):同请求创建单一对象 -
SCOPE_SESSION(session):同会话创建单一对象
@Lazy
-
singleton:自动填充容器,后续自动获取 -
prototype:获取时创建对象,不预先创建对象到容器
@Configuration
public class Config {
@Lazy
@Bean
public Person person(){
return new Person();
}
}
懒加载,调用时慢慢创建对象,不获取不作为。
@Conditional
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if(context.getRegistry().containsBeanDefinition("seconder")){
return false;
}
return true;
}
}
实现Condition接口。
@Configuration
public class Config {
@Conditional(MyCondition.class)
@Bean
public Person person(){
return new Person();
}
}
通过@Conditional进行条件调用,不存在seconder的变量名就导入。
@Conditional(MyCondition.class)
@Configuration
public class Config {
@Bean
public Person person(){
return new Person();
}
}
对类使用,会自动映射到全部方法。
导入
@Import
组件导入
-
@Resource/@Service/@Component:类主动注册 -
@Bean:对象手动注册
@Configuration
@Import(Person.class)
public class Config {
}
- 自己编写的组件,可以直接类注册,组件名为类名小写。
- 无论是否第三方,可以手动注册对象,组件名可自定义,默认为方法名。
-
@Import可以对无源码第三方类进行类注册,组件名为全类名。
ImportSelector
public class MyImportSeletcot implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{Person.class.getName()};
}
}
实现ImportSelector接口,返回需要导入的类名数组。
@Configuration
@Import(MyImportSeletcot.class)
public class Config {
}
当导入该类的时候,会直接导入类名数组中的全部类组件。
相当于channelInitializer,会导入内部添加的组件,本身类似于一种集合器。
- 必须为全类名
- 不可指定实例名称
ImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if(!registry.containsBeanDefinition("person")){
RootBeanDefinition person = new RootBeanDefinition(Person.class);
registry.registerBeanDefinition("person", person);
}
}
}
实现接口,手动注册。
@Import(MyImportBeanDefinitionRegistrar.class)
FactoryBean
public class PersonFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
return new Person();
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
- 对象
- 类型
- 数量
@Configuration
public class Config {
@Bean
public PersonFactoryBean personFactoryBean(){
return new PersonFactoryBean();
}
}
差异
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); System.out.println(context.getBean("&personFactoryBean").getClass().getName()); System.out.println(context.getBean("personFactoryBean").getClass().getName()); } }名称直接获取会创建目标对象,并非直接导入的
FactoryBean对象。想获取
FactoryBean对象,需要添加上&符号。
| way |
|---|
@Import |
ImportSelector |
ImportBeanDefinitionRegistrar |
FactoryBean |
生命
@Bean
@Configuration
public class Config {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Person person(){
return new Person();
}
}
- 外部指定类内部方法
-
initMethod指定初始化方法,destroyMethod指定销毁方法。 -
init在constructor之后进行执行 -
singleton在context.close之后会destroy,prototypy不会。
InitializingBean, DisposableBean
class Person implements InitializingBean, DisposableBean{
public Person(){
System.out.println("constructor");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("init");
}
@Override
public void destroy() throws Exception {
System.out.println("destroy");
}
}
- 类内部实现接口
-
InitializingBean-afterPropertiesSet定义初始化方法 -
DisposableBean-destroy定义销毁方法 -
afterPropertiesSet在constructor之后执行 -
prototype在context.close之后不执行
@PostConstruct,@PreDestroy
class Person{
public Person(){
System.out.println("constructor");
}
@PostConstruct
public void afterPropertiesSet() throws Exception {
System.out.println("init");
}
@PreDestroy
public void destroy() throws Exception {
System.out.println("destroy");
}
}
- 类内部标记方法
-
@PostConstructor指定初始化方法,@PreDestory指定销毁方法。 -
prototype不自动销毁
BeanPostProcessor
public class Person implements BeanPostProcessor {
public Person(){
System.out.println("constructor");
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("after");
return null;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before");
return null;
}
}
- 类内部接口实现
- 有参,可设置
- 两者
constructor后执行,无销毁 -
prototype有效,singleton无效
顺序
@Component
public class Processor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization");
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization");
return bean;
}
}
public class Person implements InitializingBean,DisposableBean {
public Person(){
System.out.println("constructor");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("BeanPostProcessor");
}
@PostConstruct
public void postConstruct(){
System.out.println("PostConstruct");
}
@PreDestroy
public void preDestroy(){
System.out.println("PreDestroy");
}
public void init(){
System.out.println("init");
}
public void destroy2(){
System.out.println("destroy2");
}
}
初始化:构造器>处理器>注解>接口>方法指定
constructorpostProcessBeforeInitializationPostConstructInitializingBeaninitpostProcessAfterInitialization
销毁:注解>接口>方法指定
PreDestroyDisposableBeandestroy2
- 处理器只负责初始化化
- 销毁只负责
singleton处理器>注解>接口>方法指定
配置
PropertySource
@PropertySource(value = {"classpath:person.properties"})
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
String name = context.getEnvironment().getProperty("person.name");
System.out.println(name);
context.close();
}
}
通过enviroment就可以获取环境变量了,配置的变量也会导入哦。
最好标记在
@Configuration配置类上,然后其他地方引入,避免扫描未执行。
属性(@Value)
赋值
public class Person {
@Value("seconder")
private String name;
}
SpEL
public class Person {
@Value("#{6 + 3 + 3 + 4}")
private int age;
}
#{}是SpEL表达式,是可以直接计算的。
取配置
public class Person {
@Value("${person.name}")
private int age;
}
取配置需要配合
@PropertySource先导入了配置,或者直接导入环境变量。总的来说,都是
""字符串,只是特殊处理之间的区别。
@ConfigurationProperties是spring-boot中直接进行对应名称的直接区配置注入。
注入
@Configuration
public class Config {
@Bean
public Person father() {
return new Person("father");
}
@Bean
public Person mother(){
return new Person("mother");
}
@Bean
public Family family(){
return new Family();
}
}
public class Family {
@Autowired
public Person person;
}
@Autowired
@Autowired连线注入会是哪一个呢,结果是报错。
public class Family {
@Autowired(required = false)
public Person person;
}
默认情况下,属性都是非空的,也就是require=true,找不到注入的数据,就会报错。
如果不是必要的,那就设置require = false。
@Qualifier
public class Family {
@Qualifier("father")
@Autowired
public Person person;
}
和@Bean一样
| @Bean | wire |
|---|---|
default:默认为方法名 |
default:默认为变量名 |
@Bean("instanceName"):指定别名 |
@Qualifier("instance"):指定对象 |
默认情况下,属性变量和实例对象之间的关联,会经过
- 类型判断
- 名称比较
所以,当变量名和实例名不一致的时候,是不能够自动关联上的。
需要使用@Qualifier进行指定对象的关联。
@Primary
@Primary
@Bean
public Person mother(){
return new Person("mother");
}
默认情况
- 类型一致
- 名称一致
手动指定
- 必须得知并写死对象名
两者都有些极端和弊病。
@Primary,定义优先级
- 类型匹配
- 指定对象
- 名称匹配
- 是否优先
刨除前两者的影响,当是在无法装配时,仅按照类型判断,优先装配标记@Primary的对象。
@Resource和@inject
public class Family {
@Resource(name = "father")
public Person person;
}
@Resource(JSR250),按照名称装配
- 无默认装配功能
- 无
@Primary选择功能- 无
require=false,必须装配
public class Family {
@Inject
public Person person;
}
@Injet(JSR30),可默认装配
- 需导入
javax.inject- 无
required属性
@Autowired位置
- 属性
public class Family {
@Aurowired
public Person person;
}
- 方法
public class Family {
public Person person;
@Autowired
public void setPerson(Person person){
this.person = person;
}
}
- 参数
public class Family {
public Person person;
public void setPerson(@Autowired Person person){
this.person = person;
}
}
- 构造器
public class Family {
public Person person;
@Autowired
public Family(Person person){
this.person = person;
}
}
public class Family {
public Person person;
public Family(@Autowired Person person){
this.person = person;
}
}
如果只有一个有参构造器,而且是扫描注入的话
public class Family {
public Person person;
public Family(Person person){
this.person = person;
}
}
不用标注,也会自动从容器中获取并完成注入。
@Bean public Family family(Person person){ return new Family(person); }
@Bean标注的方法,也是类似的原理。当需要但是没有入参的时候,会自动从容器中进行获取并注入。
当然,也可以手动标记
@Autowired入参。@Bean public Family family(@Value("family") String name){ return new Family(name); }同样的,没有回自动的从容器中获取,但是简单的类型的话,也可以手动进行指定赋值。
逆向注入
我们总是把需要的组件,向
IOC中进行注入,为的是便捷的使用spring。但是,更好的使用,是能够更方便的直接操作
spring的各种组件。
ApplicationContextAware
public class Person implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = context;
}
}
谁说一定得在启动类中才能获取context呢,这样一来,获取spring内部组件,更方便实现一些操作了。
EmbeddedValueResolverAware
public class Person implements EmbeddedValueResolverAware {
private StringValueResolver resolver;
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
String result = this.resolver.resolveStringValue("#{1+1}");
}
}
怎么解析乱七八糟的表达式?这不就获取解析器了么。
BeanNameAware
public class Person implements BeanNameAware {
private String name;
@Override
public void setBeanName(String name) {
this.name = name;
}
}
组件注册名称?轻松获取。
EnvironmentAware
public class Person implements EnvironmentAware {
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
环境变量,轻松获取。
更多
多种口味任你选择。spring不仅好管理你注册的组件,其中也有很多你想要的组件。
顺序
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
你只需要知道入口是refresh
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
调用栈里面还能找到getBean,也就是说,在进行注册的时候,也就是在你的操作开始前。
对象创建时,就会自动注入spring组件到你的对象中了,简直舒服。
@Profile
指定profile
- 虚拟机参数
-Dspring.profiles.active=dev
- 代码指定
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("dev");
context.register(Config.class);
context.refresh();
}
对比一下直接创建的
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
就是提前注入了一些参数,然后才开始的注册和刷新,机智。
选用
@Configuration
public class Config {
@Profile("test")
@Bean
public Person father() {
return new Person();
}
@Profile("default")
@Bean
public Person mother(){
return new Person();
}
}
-
others:名称随便定义,和设置丁profile配置参数一致即可 -
default:固定名称,当没有其他配置被选用,默认使用,如果有配置被选用,则不生效
未进行
@Profile标记的方法,无论如何设置,都会进行注入。
类标记
@Profile("dev")
@Configuration
public class Config {
@Bean
public Person father() {
return new Person();
}
@Bean
public Person mother(){
return new Person();
}
}
类标记的,相当于标记给了全部方法,如果只有个别对象需要切换,不推荐如此做法。
手写判断
public class DevProfile implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String[] profiles = context.getEnvironment().getActiveProfiles();
if(profiles == null || profiles.length == 0){
return false;
}
for(String profile:profiles){
if("dev".equals(profile)){
return true;
}
}
return false;
}
}
Coditional没个传参,只能这样了。