/**
* @Author: kuromaru
* @Date: Created in 10:10 2019/3/13
* @Description:
* @modified:
*/
@Service
public class MyService {
public void sayHello() {
System.out.println("Say Hello");
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:10 2019/3/13
* @Description:
* @modified:
*/
@Repository
public class MyDao {
public void getString() {
System.out.println("get String");
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:11 2019/3/13
* @Description:
* @modified:
*/
@Configuration
@ComponentScan(basePackages = "configure.componentScan")
public class MyConfig {
}
/**
* @Author: kuromaru
* @Date: Created in 10:12 2019/3/13
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(MyConfig.class);
acac.refresh();
System.out.println(acac.getBean(MyDao.class));
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:10 2019/3/13
* @Description:
* @modified:
*/
@Service
public class MyService {
public void sayHello() {
System.out.println("Say Hello");
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:10 2019/3/13
* @Description:
* @modified:
*/
@Repository
public class MyDao {
public void getString() {
System.out.println("get String");
}
}
/**
* @Author: kuromaru
* @Date: Created in 10:11 2019/3/13
* @Description:
* @modified:
*/
@Configuration
@ComponentScan(basePackages = "configure.componentScan",
useDefaultFilters = false,
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = RestController.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class))
public class MyConfig {
// 需要把defaultFilters禁用,否则还是可以查询到包里的除被Repository注解的以外的bean
}
若将useDefaultFilters设置为false,实际上就是禁用了对@Component、@Repository、@Service、@Controller和@Configuration注释的自动扫描。
/**
* @Author: kuromaru
* @Date: Created in 10:12 2019/3/13
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(MyConfig.class);
acac.refresh();
System.out.println(acac.getBean(MyDao.class));
System.out.println(acac.getBean(MyService.class));
}
}
以上代码执行的结果都是找不到Bean实例。
@ComponentScan.Filter的type属性,可以传入以下值:
过滤器类型 | 描述 |
---|---|
Annotation(default) | 匹配指定注解类型 |
assignable | 目标组件可分配给(扩展或实现)的类(或接口) |
aspectJ | 匹配AspectJ表达式 |
regex | 匹配正则表达式 |
custom | org.springframework.core.type.TypeFilter接口的自定义实现 |
其中单独对assignable做下说明,代码如下:
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ParentAnnotation {
}
/**
* @Author: kuromaru
* @Date: Created in 10:56 2019/3/13
* @Description:
* @modified:
*/
@ParentAnnotation
public class MyBean {
}
/**
* @Author: kuromaru
* @Date: Created in 10:57 2019/3/13
* @Description:
* @modified:
*/
public class MySubBean extends MyBean {
}
/**
* @Author: kuromaru
* @Date: Created in 10:11 2019/3/13
* @Description:
* @modified:
*/
@Configuration
@ComponentScan(basePackages = "configure.componentScan",
useDefaultFilters = false,
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = ParentAnnotation.class))
public class MyConfig {
}
/**
* @Author: kuromaru
* @Date: Created in 10:12 2019/3/13
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(MyConfig.class);
acac.refresh();
System.out.println(acac.getBean(MyBean.class));
System.out.println(acac.getBean(MySubBean.class));
}
}
输出结果:
三月 13, 2019 11:05:44 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27fa135a: startup date [Wed Mar 13 11:05:44 CST 2019]; root of context hierarchy
configure.componentScan.MyBean@1cab0bfb
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘configure.componentScan.MySubBean‘ available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:353)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1092)
at configure.componentScan.Client.main(Client.java:20)
可以看到,如果单纯使用FilterType.ANNOTATION的情况下,子类是没有被装配的。对MyConfig类做如下改动:
/**
* @Author: kuromaru
* @Date: Created in 10:11 2019/3/13
* @Description:
* @modified:
*/
@Configuration
@ComponentScan(basePackages = "configure.componentScan",
useDefaultFilters = false,
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyBean.class))
public class MyConfig {
}
/**
* @Author: kuromaru
* @Date: Created in 10:12 2019/3/13
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(MyConfig.class);
acac.refresh();
System.out.println(acac.getBean(MySubBean.class));
}
}
输出结果:
三月 13, 2019 11:09:31 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27fa135a: startup date [Wed Mar 13 11:09:31 CST 2019]; root of context hierarchy
configure.componentScan.MySubBean@2473b9ce
可以看到,子类也被装配到容器中了。因此,assignable可以理解为,将子类也一同装配到spring IOC容器中。
上述配置也可以用xml来代替,写法如下:
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
@Inherited注解的语义是,被此注解标注的注解是可以被继承的。即自定义一个注解,并标注@inherited,在父类上使用此注解,新建子类继承父类,则子类也会被容器认为标注了该注解。但是,若注解标注在父类的抽象方法上,或者子类重写了父类标注了注解的方法,则注解不会被继承。
@Configuration
@Configuration注解可以用于将Bean装配至springIOC容器,其实现代码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
可以看到,@Configuration注解被标注了@Component,因此,本质上来讲,@Configuration也是一个@Component,被它注解的类可以被context:component-scan/或者@ComponentScan处理。
需要注意的是,在使用@Configuration是,有以下几点约束:
@Configuration有一个对应的ConfigurationClassPostProcessor,是一个BeanFactoryPostProcessor,回顾前面2019-03-11(扩展点).note中整理的内容,可知ConfigurationClassPostProcessor会在容器启动的时候执行一次,里面的逻辑就是获取BeanFactory中的所有被@Configuration注解的Bean,并逐一通过CGLIB做增强处理,在增强处理中注册了两个Interceptor--BeanMethodInterceptor和BeanFactoryAwareMethodInterceptor,在BeanMethodInterceptor的拦截处理中,会判断当前执行的方法是否是被@Bean注解的方法,如果是,则说明Bean需要被新建实例,如果不是,如在@Bean方法中引用另一个@Bean方法,则直接去容器中获取实例。因此,如下代码中的两个myBean()返回的是同一个实例:
@Configuration
public class MyConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean
public MyReference() {
return new MyReference(myBean());
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:21 2019/3/13
* @Description:
* @modified:
*/
public class MyBean {
}
/**
* @Author: kuromaru
* @Date: Created in 15:22 2019/3/13
* @Description:
* @modified:
*/
public class MyReference {
private MyBean myBean;
public MyReference(MyBean myBean) {
this.myBean = myBean;
}
public MyBean getMyBean() {
return myBean;
}
public void setMyBean(MyBean myBean) {
this.myBean = myBean;
}
}
/**
* @Author: kuromaru
* @Date: Created in 15:23 2019/3/13
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(MyConfig.class);
acac.refresh();
MyBean myBean = acac.getBean(MyBean.class);
MyReference myReference = acac.getBean(MyReference.class);
System.out.println(myBean == myReference.getMyBean());
}
}
输出结果:
三月 13, 2019 3:25:00 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27fa135a: startup date [Wed Mar 13 15:25:00 CST 2019]; root of context hierarchy
true
而@Component注解并没有去通过CGLIB来代理@Bean方法的调用,所以下面的代码会生成两个不同的myBean:
@Component
public class MyConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean
public MyReference() {
return new MyReference(myBean());
}
}
其他代码不做修改,执行结果如下:
三月 13, 2019 4:34:19 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27fa135a: startup date [Wed Mar 13 16:34:19 CST 2019]; root of context hierarchy
false
若想保证使用相同的实例,又不想使用代理,可以做如下改动:
@Component
public class MyConfig {
@Autowired
private MyBean myBean;
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean
public MyReference() {
return new MyReference(myBean);
}
}
执行结果如下:
三月 13, 2019 4:37:22 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27fa135a: startup date [Wed Mar 13 16:37:22 CST 2019]; root of context hierarchy
true
/**
* @Author: kuromaru
* @Date: Created in 16:40 2019/3/13
* @Description:
* @modified:
*/
public class MyBean {
private String name;
public MyBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
/**
* @Author: kuromaru
* @Date: Created in 16:53 2019/3/13
* @Description:
* @modified:
*/
@Component
public class MyComponent {
@Autowired
private MyBean myBean;
public void sayHello() {
System.out.println("Hello ~ " + myBean.getName());
}
}
/**
* @Author: kuromaru
* @Date: Created in 16:40 2019/3/13
* @Description:
* @modified:
*/
@Configuration
public class MyConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MyBean myBean(InjectionPoint injectionPoint) {
return new MyBean(injectionPoint.getMember().getName());
}
@Bean
public MyComponent myComponent() {
return new MyComponent();
}
}
/**
* @Author: kuromaru
* @Date: Created in 16:43 2019/3/13
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(MyConfig.class);
acac.refresh();
MyComponent myComponent = acac.getBean(MyComponent.class);
myComponent.sayHello();
}
}
输出结果:
三月 13, 2019 5:01:02 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27fa135a: startup date [Wed Mar 13 17:01:02 CST 2019]; root of context hierarchy
Hello ~ myBean
需要注意的是,InjectionPoint只对新建实例起作,而不适用于注入现有实例。如果将Scope设置为singleton,则会报错,如下:
/**
* @Author: kuromaru
* @Date: Created in 16:40 2019/3/13
* @Description:
* @modified:
*/
@Configuration
public class MyConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public MyBean myBean(InjectionPoint injectionPoint) {
return new MyBean(injectionPoint.getMember().getName());
}
@Bean
public MyComponent myComponent() {
return new MyComponent();
}
}
报错信息:
三月 13, 2019 5:07:10 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27fa135a: startup date [Wed Mar 13 17:07:10 CST 2019]; root of context hierarchy
Exception in thread "main" java.lang.IllegalStateException: No current InjectionPoint available for method ‘myBean‘ parameter 0
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:831)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:467)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1181)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1075)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at configure.InjectionPoint.Client.main(Client.java:16)
/**
* @Author: kuromaru
* @Date: Created in 17:11 2019/3/13
* @Description:
* @modified:
*/
public class MyBean {
private String name;
public MyBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* @Author: kuromaru
* @Date: Created in 17:11 2019/3/13
* @Description:
* @modified:
*/
public class MyReference {
private String name;
public MyReference(String name) {
System.out.println("reference bean name is " + name);
}
}
/**
* @Author: kuromaru
* @Date: Created in 17:12 2019/3/13
* @Description:
* @modified:
*/
@Configuration
public class MyConfig {
@Bean
public MyBean myBean() {
return new MyBean("pretty bean");
}
@Bean
public MyReference myReference(@Value("#{myBean.name}") String name) {
return new MyReference(name);
}
}
/**
* @Author: kuromaru
* @Date: Created in 17:15 2019/3/13
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(MyConfig.class);
acac.refresh();
}
}
输出结果:
三月 13, 2019 5:17:17 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27fa135a: startup date [Wed Mar 13 17:17:17 CST 2019]; root of context hierarchy
reference bean name is pretty bean
String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
Spring已经提供了两个实现类:AnnotationBeanNameGenerator和DefaultBeanNameGenerator
可以自己实现接口或者继承上面两个类中任意一个来自定义命名规则,代码如下:
/**
* @Author: kuromaru
* @Date: Created in 17:27 2019/3/13
* @Description:
* @modified:
*/
@Component
public class MyBean {
private String name;
public String getName() {
return name;
}
}
/**
* @Author: kuromaru
* @Date: Created in 17:27 2019/3/13
* @Description:
* @modified:
*/
@Component("myBean") // 与MyBean的名称一致
public class MyBean1 {
private String name;
// public MyBean1(MyBean myBean) {
// this.name = myBean.getName();
// }
public String getName() {
return name;
}
}
/**
* @Author: kuromaru
* @Date: Created in 17:27 2019/3/13
* @Description:
* @modified:
*/
public class MyComponent {
private boolean isSame;
public MyComponent(MyBean myBean, MyBean1 myBean1) {
this.isSame = myBean.equals(myBean1);
}
public boolean isSame() {
return isSame;
}
}
/**
* @Author: kuromaru
* @Date: Created in 17:27 2019/3/13
* @Description:
* @modified:
*/
@Component
@ComponentScan(basePackages = "configure.nameGenerator", nameGenerator = MyNameGenerator.class)
public class MyConfig {
@Autowired
private MyBean myBean;
@Autowired
private MyBean1 myBean1;
@Bean
private MyComponent myComponent() {
return new MyComponent(myBean, myBean1);
}
}
/**
* @Author: kuromaru
* @Date: Created in 17:22 2019/3/13
* @Description:
* @modified:
*/
public class MyNameGenerator extends AnnotationBeanNameGenerator {
private static final String PREFIX_NAME = "kaku.";
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return getBeanName(definition, registry);
}
private String getBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
String beanClassName = Introspector.decapitalize(definition.getBeanClassName());
String id = PREFIX_NAME + beanClassName;
if (registry.containsBeanDefinition(id)) {
int counter = -1;
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = id + counter;
}
}
return id;
}
}
/**
* @Author: kuromaru
* @Date: Created in 17:29 2019/3/13
* @Description:
* @modified:
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(MyConfig.class);
// 这里可以不设置,如果MyConfig里面配置了
// @ComponentScan(basePackages = "configure.nameGenerator", nameGenerator = MyNameGenerator.class)
// 这两个配置是重复的
acac.setBeanNameGenerator(new MyNameGenerator());
acac.refresh();
Arrays.stream(acac.getBeanFactory().getBeanDefinitionNames()).forEach(System.out::println);
}
}
输出结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
kaku.configure.nameGenerator.MyBean
kaku.configure.nameGenerator.MyBean1
myComponent
上面代码中的acac.setBeanNameGenerator和@ComponentScan(nameGenerator="xxx")任选一种就可以,两个设置是重复的,也可以在xml里做配置,如下:
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
也可以通过xml配置实现:
<beans>
<context:component-scan base-package="org.example"
scope-resolver="org.example.MyScopeResolver"
scopedProxy=ScopedProxyMode.INTERFACES/>
</beans>
@Bean(destoryMethod="")
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
${...}占位符可以设置默认值,如下
@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
上述代码的意思是,获取属性中的my.placeholder的值,如果没有,则以‘default/path‘为默认值
原文:https://www.cnblogs.com/kuromaru/p/12867218.html