Spring注解(@Bean、@ComponentScan、自定
环境搭建
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.spring.test</groupId>
<artifactId>spring-annotation</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
</dependencies>
</project>
bean
package com.ming.beans;
public class Person {
private String name;
private int age;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
编写配置类
给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
@Configuration
public class MainConfig {
/**
* 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
* @return
*
* 修改bean的名字
* 1.修改方法名
* 2.在@Bean 注解中指定名字 @Bean("person02")
*/
@Bean("person02")
public Person person(){
return new Person("10", 20);
}
}
Spring 使用xml 注入bean
<?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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<bean id="person" class="com.spring.annotation.bean.Person">
<property name="name" value="zhangsan"></property>
<property name="age" value="20"></property>
</bean>
</beans>
测试代码
public class MainTest {
public static void main(String[] args){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
/**
* 按照类型获取bean的名字(默认注入bean的方法名字)
*/
String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
}
}
运行结果
Person [name=10, age=20, nickName=null]
person02
Process finished with exit code 0
而在实际开发中会经常使用包扫描,只要标注了@Controller、@Service、@Repository,@Component 注解的类会自动加入到容器中
Spring xml 包扫描
<context:component-scan base-package="com.ming" use-default-filters="false"></context:component-scan>
Spring使用注解 包扫描 @ComponentScan
@ComponentScan("com.spring.annotation")
@Configuration
public class MainConfig {
/**
* 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
* @return
*
* 修改bean的名字
* 1.修改方法名
* 2.在@Bean 注解中指定名字 @Bean("person02")
*/
@Bean("person02")
public Person person(){
return new Person("10", 20);
}
}
1.添加bean
@Controller
public class BookController {
}
@Repository
public class BookDao {
}
@Service
public class BookService {
}
2.测试
@Test
public void test(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
/**
* 获取容器中所有bean定义的名字
*/
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
3.测试结果
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
mainConfig
bookController
bookDao
bookService
person02
Process finished with exit code 0
注意:mainConfig 配置类也是一个组件 因为@Configuration 注解中标有@Component
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
@ComponentScan 包含过滤和排除过滤
ComponentScan.Filter[] includeFilters() default {}; 按照某些规则排除组件
ComponentScan.Filter[] excludeFilters() default {}; 指定扫描的时候只需要包含哪些组件
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default "**/*.class";
/**/
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
FilterType 指定不同的包含/排除 规则
package org.springframework.context.annotation;
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM;
private FilterType() {
}
}
例如:我们按照注解类型排除 Controller.class, Service.class, Repository.class
@ComponentScan(value = "com.spring.annotation", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})
@Configuration
public class MainConfig {
/**
* 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
* @return
*
* 修改bean的名字
* 1.修改方法名
* 2.在@Bean 注解中指定名字 @Bean("person02")
*/
@Bean("person02")
public Person person(){
return new Person("10", 20);
}
}
测试结果(容器中已经没有 BookController BookService BookServie这三个bean)
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
mainConfig
person02
Process finished with exit code 0
包含过滤 includeFilters 如果想要只包含 Controller 注解的bean,如下配置
注意:需要添加 useDefaultFilters = false
@ComponentScan(value = "com.spring.annotation", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,
classes = {Controller.class})},useDefaultFilters = false)
@Configuration
public class MainConfig {
/**
* 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
* @return
*
* 修改bean的名字
* 1.修改方法名
* 2.在@Bean 注解中指定名字 @Bean("person02")
*/
@Bean("person02")
public Person person(){
return new Person("10", 20);
}
}
对比Spring xml 配置
<context:component-scan base-package="com.test.ming" use-default-filters="false"></context:componentscan>
单元测试
@Test
public void test(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
/**
* 获取容器中所有bean定义的名字
*/
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
结果如下(可以看到只有Controller注解的bean在容器中)
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
mainConfig
bookController
person02
Process finished with exit code 0
我们还可以使用@ComponentScans 来指定 扫描策略
ComponentScans 注解结构 如下
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {
ComponentScan[] value();
}
可以看到其内部是一个ComponentScan[] 数组,所以我们可以在其中指定多个ComponentScan
@ComponentScans(value = {
@ComponentScan(value = "com.spring.annotation", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,
classes = {Controller.class})},useDefaultFilters = false)
})
@Configuration
public class MainConfig {
/**
* 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
* @return
*
* 修改bean的名字
* 1.修改方法名
* 2.在@Bean 注解中指定名字 @Bean("person02")
*/
@Bean("person02")
public Person person(){
return new Person("10", 20);
}
}
FilterType.ASSIGNABLE_TYPE 指定不同的类型
例如,包含Controller 注解的bean 和 BookService类型的bean
@ComponentScans(value = {
@ComponentScan(value = "com.spring.annotation", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
},useDefaultFilters = false)
})
@Configuration
public class MainConfig {
/**
* 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
* @return
*
* 修改bean的名字
* 1.修改方法名
* 2.在@Bean 注解中指定名字 @Bean("person02")
*/
@Bean("person02")
public Person person(){
return new Person("10", 20);
}
}
FilterType.CUSTOM:使用自定义规则
1.编写MyTypeFilter 并实现 TypeFilter 接口
2.match方法中 实现自定义规则
/**
* 自定义过滤规则
*/
public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader
* @param metadataReaderFactory
* @return
* @throws IOException
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*
*
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// TODO Auto-generated method stub
//获取当前类注解的信息
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;
}
}
3.使用实例(当前扫描到的类,类名中包含er,就会注入到容器中)
@ComponentScans(value = {
@ComponentScan(value = "com.spring.annotation", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
},useDefaultFilters = false)
})
@Configuration
public class MainConfig {
/**
* 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
* @return
*
* 修改bean的名字
* 1.修改方法名
* 2.在@Bean 注解中指定名字 @Bean("person02")
*/
@Bean("person02")
public Person person(){
return new Person("10", 20);
}
}
小总结:
@ComponentScan value:指定要扫描的包
excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则