目录html
随着Spring的流行,咱们经历过基于XML-Based 的配置,随着SpringBoot的流行,咱们逐渐使用基于注解的配置替换掉了基于XML-Based的配置,那么你知道基于注解的配置的基础组件都是什么吗?都包括哪些要素?那么本节就来探讨一下。注:本篇文章更多的是讨论Spring基于注解的配置一览,具体的技术可能没有那么深,请各位大佬见谅。java
探讨主题:mysql
Spring中新的概念是支持@Bean注解 和 @Configuration 注解的类。@Bean 注解用来代表一个方法实例化,配置而且经过IOC容器初始化并管理一个新的对象。@Bean注解就等同于XML-Based中的<beans/>
标签,而且扮演了相同的做用。你可使用基于注解的配置@Bean 和 @Component,然而他们都用在@Configuration配置类中。web
使用@Configuration 注解的主要做用是做为bean定义的类,进一步来讲,@Configuration注解的类容许经过调用同类中的其余@Bean标注的方法来定义bean之间依赖关系。 以下所示:spring
新建一个maven项目(我通常都直接建立SpringBoot项目,比较省事),建立AppConfig
,MyService
,MyServiceImpl
类,代码以下:sql
@Configuration public class AppConfig { @Bean public MyService myService(){ return new MyServiceImpl(); } } public interface MyService {} public class MyServiceImpl implements MyService {}
上述的依赖关系等同于XML-Based:数据库
<beans> <bean id="myService",class="com.spring.annotation.service.impl.MyServiceImpl"/> </beans>
AnnotationConfigApplicationContext 基于注解的上下文是Spring3.0 新添加的注解,它是ApplicationContext
的一个具体实现,它能够接收@Configuration
注解的类做为输入参数,还能接收使用JSR-330元注解的普通@Component类。数组
当提供了@Configuration 类做为输入参数时,@Configuration类就会注册做为bean的定义信息而且全部声明@Bean的方法也都会做为bean的定义信息。websocket
当提供@Component和JSR-330 声明的类时,他们都会注册做为bean的定义信息,而且假设在必要时在这些类中使用诸如@Autowired或@Inject之类的注解session
在某些基于XML-Based的配置,咱们想获取上下文容器使用ClassPathXmlApplicationContext
,如今你可以使用@Configuration 类来实例化AnnotationConfigApplicationContext。
在MyService
中添加一个printMessage()
方法,实现类实现对应的方法。新建测试类进行测试
public class ApplicationTests { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyService service = context.getBean(MyService.class); // printMessage() 输出something... service.printMessage(); } }
如前所述,AnnotationConfigApplicationContext不只限于使用@Configuration类。 任何@Component或JSR-330带注释的类均可以做为输入提供给构造函数,以下例所示
public class ApplicationTests { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(MyServiceImpl.class,Dependency1.class,Dependency2.class); MyService myService = context.getBean(MyService.class); myService.printMessage(); } }
你能够实例化AnnotationConfigApplicationContext
经过使用无参数的构造器而且使用register
方法进行注册,它和AnnotationConfigApplicationContext
带参数的构造器起到的效果相同。
public class ApplicationTests { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(AppConfig.class, OtherConfig.class); ctx.register(AdditionalConfig.class); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); System.out.println(ctx.getBean(OtherConfig.class)); System.out.println(ctx.getBean(AdditionalConfig.class)); myService.printMessage(); } }
OtherConfig.class 和 AdditionalConfig.class 是使用@Component 标注的类。
为了容许组件进行扫描,须要在@Configuration配置类添加@ComponentScan()
注解,改造以前的AdditionalConfig
类,以下:
@Configuration @ComponentScan(basePackages = "com.spring.annotation.config") public class AdditionalConfig {}
@ComponentScan指定了基础扫描包位于com.spring.annotation.config下,全部位于该包范围内的bean都会被注册进来,交由Spring管理。它就等同于基于XML-Based的注解:
<beans> <context:component-scan base-package="com.spring.annotation.config/> </beans>
AnnotationConfigApplicationContext中的scan()方法以容许相同的组件扫描功能,如如下示例所示:
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("com.spring.annotation"); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); }
为何说@Configuration用法和@Component都可以标注配置类?由于@Configuration的元注解就是@Component。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { String value() default ""; }
AnnotationConfigApplicationContext的一个WebApplicationContext的变化是使用AnnotationConfigWebApplicationContext
。配置Spring ContextLoaderListener的servlet监听器,Spring MVC的DispatcherServlet等时,可使用此实现。如下web.xml代码段配置典型的Spring MVC Web应用程序(请注意context-param和init-param的使用)
<web-app> <!-- 配置web上下文监听器使用 AnnotationConfigWebApplicationContext 而不是默认的 XmlWebApplicationContext --> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </context-param> <!-- 配置位置必须包含一个或多个以逗号或空格分隔的彻底限定的@Configuration类。 也能够为组件扫描指定彻底 限定的包--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.spring.annotation.config.AdditionalConfig</param-value> </context-param> <!--使用ContextLoaderListener像往常同样引导根应用程序上下文--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 定义一个SpringMVC 核心控制器 DispatcherServlet--> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置web上下文监听器使用 AnnotationConfigWebApplicationContext 而不是默认的 XmlWebApplicationContext --> <init-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </init-param> <!-- 配置位置必须包含一个或多个以逗号或空格分隔的彻底限定的@Configuration类。 也能够为组件扫描指定 彻底限定的包--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>com.spring.annotation.config.MvcConfig</param-value> </init-param> </servlet> <!-- 将/app/* 的全部请求映射到调度程序servlet --> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping> </web-app>
@Bean 注解是一个方法级别的注解,可以替换XML-Based中的
init-method
,
destroy-method
,
autowiring
。
与基础概念中Bean的定义相同,读者能够参考基础概念部分进行了解,咱们不在此再进行探讨。
@Bean 注解能够有任意数量的参数来构建其依赖项,例如
public class MyService { private final MyRepository myRepository; public MyService(MyRepository myRepository) { this.myRepository = myRepository; } public String generateSomeString() { return myRepository.findString() + "-from-MyService"; } } @Configuration class MyConfiguration { @Bean public MyService myService() { return new MyService(myRepository()); } @Bean public MyRepository myRepository() { return new MyRepository(); } } public class MyRepository { public String findString() { return "some-string"; } }
任何使用@Bean的注解都支持生命周期的回调,使用JSR-220提供的@PostConstruct
和@PreDestory
注解来实现。若是bean实现了InitializingBean
,DisposableBean
或者Lifecycle
接口,他们的方法会由IOC容器回调。一些以Aware的实现接口(像是BeanFactoryAware,BeanNameAware, MessageSourceAware, ApplicationContextAware等)也支持回调。
@Bean注解支持特定的初始化和销毁方法,就像XML-Based中的init-method
和 destory-method
中的bean属性,下面这个例子证明了这一点
AppConfig.java
@Configuration public class AppConfig { @Bean(initMethod = "init") public BeanOne beanOne(){ return new BeanOne(); } @Bean(destroyMethod = "cleanup") public BeanTwo beanTwo(){ return new BeanTwo(); } } class BeanOne { public void init(){} } class BeanTwo { public void cleanup(){} }
对于上面的例子,也能够手动调用init()方法,与上面的initMethod 方法等效
@Bean public BeanOne beanOne(){ BeanOne beanOne = new BeanOne(); beanOne.init(); return beanOne; }
当你直接使用Java开发时,你可使用对象执行任何操做,而且没必要老是依赖于容器生命周期。
Spring包括@Scope注解可以让你指定Bean的做用范围,Bean的Scope默认是单例的,也就是说@Bean标注的对象在IOC的容器中只有一个。你能够重写@Scope的做用范围,下面的例子说明了这一点,修改OtherConfig以下
OtherConfig.java
@Configuration public class OtherConfig { @Bean @Scope("prototype") public Dependency1 dependency1(){ return new Dependency1(); } }
每次尝试获取dependency1这个对象的时候都会从新生成一个新的对象实例。下面是Scope的做用范围和解释:
Scope | Descriptionn |
---|---|
singleton | 默认单例的bean定义信息,对于每一个IOC容器来讲都是单例对象 |
prototype | bean对象的定义为任意数量的对象实例 |
request | bean对象的定义为一次HTTP请求的生命周期,也就是说,每一个HTTP请求都有本身的bean实例,它是在单个bean定义的后面建立的。仅仅在web-aware的上下文中有效 |
session | bean对象的定义为一次HTTP会话的生命周期。仅仅在web-aware的上下文中有效 |
application | bean对象的定义范围在ServletContext生命周期内。仅仅在web-aware的上下文中有效 |
websocket | bean对象的定义为WebSocket的生命周期内。仅仅在web-aware的上下文中有效 |
Spring提供了一种经过scoped proxies与scoped依赖一块儿做用的方式。最简单的在XML环境中建立代理的方式是经过<aop:scoped-proxy/>
标签。使用@Scope
注解为在Java中配置bean提供了与proxyMode属性相同的功能。默认是不须要代理的(ScopedProxyMode.NO),可是你须要指定ScopedProxyMode.TARGET_CLASS
或者ScopedProxyMode.INTERFACES
。
默认的状况下,配置类经过@Bean配置的默认名称(方法名第一个字母小写)进行注册和使用,可是你能够更换@Bean的name为你想指定的名称。修改AdditionalConfig 类
AdditionalConfig.java
@Configuration //@ComponentScan(basePackages = "com.spring.annotation.config") public class AdditionalConfig { @Bean(name = "default") public Dependency2 dependency2(){ return new Dependency2(); } }
有时候须要为单例的bean提供多个名称,也叫作Bean的别名。Bean注解的name属性接收一个Array数组。下面这个例子证明了这一点:
OtherConfig.java
@Configuration public class OtherConfig { // @Bean // @Scope("prototype") // public Dependency1 dependency1(){ // return new Dependency1(); // } @Bean({"dataSource", "dataSourceA", "dataSourceB"}) public DataSource dataSource(){ return null; } }
有时,提供更详细的bean描述信息会颇有帮助(可是开发不多使用到)。为了增长一个对@Bean的描述,你须要使用到@Description注解
OtherConfig.java
@Configuration public class OtherConfig { // @Bean // @Scope("prototype") // public Dependency1 dependency1(){ // return new Dependency1(); // } // @Bean({"dataSource", "dataSourceA", "dataSourceB"}) // public DataSource dataSource(){ // return null; // } @Bean @Description("此方法的bean名称为dependency1") public Dependency1 dependency1(){ return new Dependency1(); } }
更多关于@Configuration 的详细说明,请你参考https://mp.weixin.qq.com/s/FLJTsT2bAru-w7cF4CG8kQ
已经把@Configuration的注解说明的比较详细了。
Spring基于注解的配置可以容许你自定义注解,同时可以下降配置的复杂性。
就像在Spring XML文件中使用
@Configuration public class ConfigA { @Bean public A a(){ return new A(); } } @Configuration @Import(ConfigA.class) public class ConfigB { @Bean public B b(){ return new B(); } }
如今,在实例化上下文时,不须要同时指定ConfigA.class 和 ConfigB.class ,只须要显示提供ConfigB
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class); A a = ctx.getBean(A.class); B b = ctx.getBean(B.class); }
这种方法简化了容器实例化,由于只须要处理一个类,而不是要求你在构造期间记住可能大量的@Configuration类
选择性的容许或者禁止@Configuration注解的类和@Bean注解的方法是颇有用的,基于一些任意系统状态。一个常见的例子是只有在Spring环境中启用了特定的配置文件时才使用@Profile注释激活bean。
@Profile注解也实现了更灵活的注解@Conditional,@Conditional 注解代表在注册@Bean 以前应参考特定的Condition实现。
实现Condition接口就会提供一个matched方法返回true或者false
更多关于@Conditional 的示例,请参考
https://www.cnblogs.com/cxuanBlog/p/10960575.html
Spring @Configuration类可以100%替换XML配置,但一些工具(如XML命名空间)仍旧是配置容器的首选方法,在这种背景下,使用XML使很方便的并且使刚需了。你有两个选择:使用以XML配置实例化容器为中心,例如:ClassPathXmlApplicationContext
导入XML或者实例化以Java配置为中心的AnnotationConfigApplicationContext
并提供ImportResource
注解导入须要的XML配置。
请记住,@Configuration类存放的是容器中的bean定义信息,下面的例子中,咱们将会建立一个@Configuration类而且加载了外部xml配置。下面展现了一个普通的Java配置类
@Configuration public class AppConfig { @Autowired private DataSource dataSource; @Bean public AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource); } @Bean public TransferService transferService() { return new TransferService(accountRepository()); } }
下面是system-test-config.xml
配置类的一部分
<beans> <!--容许开启 @Autowired 或者 @Configuration--> <context:annotation-config/> <!-- 读取外部属性文件 --> <!-- 更多关于属性读取的资料,参考 https://www.cnblogs.com/cxuanBlog/p/10927819.html --> <context:property-placeholder location="classpath:/com/spring/annotation/jdbc.properties"/> <bean class="com.spring.annotation.config.AppConfig"/> <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
引入jdbc.properties创建数据库链接
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/sys jdbc.username=root jdbc.password=123456
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/spring/annotation/system-test-config.xml"); TransferService transferService = ctx.getBean(TransferService.class); // ... }
在
system-test-config.xml
中,AppConfig 对应的标签没有声明id属性,虽然这样作是能够接受的,可是没有必要,由于没有其余bean引用它,而且不太可能经过名称从容器中获取它。一样的,DataSource bean只是按类型自动装配,所以不严格要求显式的bean id。
由于@Configuration的原注解是@Component,因此@Configuration注解的类也能用于组件扫描,使用与前一个示例中描述的相同的方案,咱们能够从新定义system-test-config.xml以利用组件扫描。 请注意,在这种状况下,咱们不须要显式声明<context:annotation-config />
,由于<context:component-scan />
启用相同的功能。
<beans> <context:component-scan base-package="com.spring.annotation"/> <context:property-placeholder location="classpath:/com/spring/annotation/jdbc.properties"/> <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
在基于Java注解的配置类中,仍然可使用少许的@ImportResource导入外部配置,最好的方式就是二者结合,下面展现了一下Java注解结合XML配置的示例
@Configuration @ImportResource("classpath:/com/spring/annotation/properties-config.xml") public class AppConfig { @Value("${jdbc.driverClassName}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource() { return new DriverManagerDataSource(url, username, password); } }
Properties-config.xml
<beans> <context:property-placeholder location="classpath:/com/spring/annotation/jdbc.properties"/> </beans>
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/sys jdbc.username=root jdbc.password=123456
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); TransferService transferService = ctx.getBean(TransferService.class); // ... }
相关阅读:
PropertyPlaceholderConfigurer 基本用法
谈谈 ServletConfig 和 ServletContext
@Configuration所有配置一览https://mp.weixin.qq.com/s/FLJTsT2bAru-w7cF4CG8kQ