Spring-基本注解
基础环境
基本组件(Spring-Context
)
aop
beans
context
core
expression
jcl
基本调用
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");
}
}
初始化:构造器>处理器>注解>接口>方法指定
constructor
postProcessBeforeInitialization
PostConstruct
InitializingBean
init
postProcessAfterInitialization
销毁:注解>接口>方法指定
PreDestroy
DisposableBean
destroy2
- 处理器只负责初始化化
- 销毁只负责
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
没个传参,只能这样了。