使用jdk:1.8、maven:3.3.3
spring获取Bean的方式
pom.xml文件内容:
<?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>cn.ac.iie</groupId>
<artifactId>spring-course</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
</dependencies>
</project>
配置类MyConfig.java:
package com.edu.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置类
*/
@Configuration
public class MyConfig {
// 配置一个bean
@Bean
public MyBean createMyBean(){
return new MyBean();
}
}
MyBean.java
package com.edu.spring;
public class MyBean {
}
主函数:App.java
package com.edu.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// 从容器中获取bean---从类型获取
System.out.println(context.getBean(MyBean.class));
// 从容器中获取bean---从名字获取,默认名字是方法名
System.out.println(context.getBean("createMyBean"));
context.close();
}
}
输出结果如下:
com.edu.spring.MyBean@1445d7f
com.edu.spring.MyBean@1445d7f
如果需要指定bean名字,需要修改MyConfig.java:
@Configuration
public class MyConfig {
// 配置一个bean
@Bean(name = "myBean")
public MyBean createMyBean(){
return new MyBean();
}
}
然后在App.java指定Bean的名字:这样就无法根据方法名获取Bean了。
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// 从容器中获取bean---从类型获取
System.out.println(context.getBean(MyBean.class));
// 从容器中获取bean---从名字获取,默认名字是方法名
// System.out.println(context.getBean("createMyBean"));
// 从容器中获取bean---从指定名字获取
System.out.println(context.getBean("myBean"));
context.close();
}
}
Bean默认是单例
我们可以看到,Bean是单例的,两次打印出的对象是一样的。
如果我们想修改Bean的单例为多例,修改MyConfig.java如下(添加scope(prototype)):
@Configuration
public class MyConfig {
// 配置一个bean
@Bean(name = "myBean")
@Scope("prototype")
public MyBean createMyBean(){
return new MyBean();
}
}
打印如下:
com.edu.spring.MyBean@10b48321
com.edu.spring.MyBean@6b67034
FactoryBean
新建方法JeepFactoryBean.java
package com.edu.spring;
import org.springframework.beans.factory.FactoryBean;
public class JeepFactoryBean implements FactoryBean<Jeep> {
/**
* 创建的实例对象
* @return
* @throws Exception
*/
@Override
public Jeep getObject() throws Exception {
return new Jeep();
}
/**
*
* @return
*/
@Override
public Class<?> getObjectType() {
return Jeep.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
在MyConfig.java中添加配置:
@Bean
public JeepFactoryBean createJeepFactoryBean(){
return new JeepFactoryBean();
}
可以在App.java中得到Jeep.class。
System.out.println(context.getBean(Jeep.class)); System.out.println(context.getBean("createJeepFactoryBean"));
如果要获取JeepFactoryBean本身,而不是工厂生产出的类,可以通过下面的两种方式获取:
System.out.println(context.getBean(JeepFactoryBean.class)); System.out.println(context.getBean("&createJeepFactoryBean"));
目前有两种方式进行Bean的装配,一种是使用FactoryBean,一种是原始方式
使用第三种装配
新建CarFactory.java
public class CarFactory {
public Car create(){
return new Car();
}
}
新建Car.java
配置MyConfig.java
@Bean
public Car createJeep(CarFactory carFactory){
return carFactory.create();
}
@Bean
public CarFactory createCarFactory(){
return new CarFactory();
}
获取对象:
System.out.println(context.getBean(Car.class));
因为,在Bean的装配过程中,需要参数的时候,spring会默认从当前容器中获取到对应的参数,然后注入。
Bean初始化,在Bean初始化时,进行一些操作。
方式一:
创建Cat.java
package com.edu.spring;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Cat implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("====afterPropertiesSet====");
}
@Override
public void destroy() throws Exception {
System.out.println("====destroy====");
}
}
配置MyConfig.java
@Bean
public Cat createCat(){
return new Cat();
}
获得cat
System.out.println(context.getBean(Cat.class));
控制台打印:
====afterPropertiesSet====
com.edu.spring.MyBean@7fe8ea47
com.edu.spring.MyBean@226a82c4
com.edu.spring.JeepFactoryBean@731f8236
com.edu.spring.JeepFactoryBean@731f8236
com.edu.spring.Jeep@255b53dc
com.edu.spring.Jeep@255b53dc
com.edu.spring.Car@1dd92fe2
com.edu.spring.Cat@6b53e23f
====destroy====
方法二:
创建Dog.java
package com.edu.spring;
public class Dog {
public void myInit(){
System.out.println("init=====");
}
public void myDestory(){
System.out.println("destory===");
}
}
在配置MyConfig.java时指定初始化时执行和销毁时执行的方法
@Bean(initMethod = "myInit", destroyMethod = "myDestory")
public Dog createDog(){
return new Dog();
}
方式三:
新建Fish.java
package com.edu.spring;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class Fish {
@PostConstruct
public void initial(){
System.out.println("fish init");
}
@PreDestroy
public void close(){
System.out.println("fish close");
}
}
配置MyConfig.java
@Bean
public Fish createFish(){
return new Fish();
}
Bean装配
新建User.java
package com.edu.spring;
import org.springframework.stereotype.Component;
@Component
public class User {
}
修改App.java,将User.class添加到容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, User.class);
System.out.println(context.getBean(User.class));
但是使用@Component,则无法使用@Bean(initMethod = "myInit", destroyMethod = "myDestory")设置初始化和销毁的方法
此外,默认的名字是类名。可以指定名字:
@Component("myUser")
如果此时同样在MyConfig.java中,配置一个Bean,
@Bean
public User createUser(){
return new User();
}
则会报错:
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.edu.spring.User' available: expected single matching bean but found 2: myUser,createUser
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)
at com.edu.spring.App.main(App.java:24)
也就是说通过类型获取User时找到了两个 一个是myUser一个是createUser。如果我们通过名字来获取就没有问题了。
System.out.println(context.getBean("myUser"));
如果想获取,User类型的所有对象,可以使用getBeansOfType方法。
除了使用Component,还可以使用Repository来装配类,一般用在数据访问层,而Component一般用于没有明确角色的时候。
新建UserDao.java
@Repository
public class UserDao {
}
添加到AnnotationConfigApplicationContext中
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, User.class, UserDao.class);
System.out.println(context.getBean(UserDao.class));
还可以使用Service来装配,一般用在业务逻辑层
新建UserService.java,
// 业务逻辑层
@Service
public class UserService {
}
添加到AnnotationConfigApplicationContext:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, User.class, UserDao.class, UserService.class);
System.out.println(context.getBean(UserService.class));
使用Controller来装配,一般用于展示层,
package com.edu.spring;
import org.springframework.stereotype.Controller;
// 用在展示层
@Controller
public class UserController {
}
依赖注入
在User中,依赖UserService.java
User.java
package com.edu.spring;
import org.springframework.stereotype.Component;
@Component("myUser")
public class User {
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public String toString() {
return "User{" +
"userService=" + userService +
'}';
}
}
在App.java中调用show方法
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, User.class, UserDao.class, UserService.class, UserController.class);
User user = context.getBean("myUser", User.class);
user.show();
context.close();
输出打印:
null
说明UserService没有注入进去,使用@AutoWired修饰UserService,便可以注入进去了,同时也不需要set get方法了。
package com.edu.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("myUser")
public class User {
@Autowired
private UserService userService;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
打印输出:
User{userService=UserService{userDao=com.edu.spring.UserDao@3bbc39f8}}
说明成功注入进去了。
如果继续在MyConfig.java中装配一个Bean:
@Bean
public UserDao createUserDao(){
return new UserDao();
}
那么这时,就会有两个UserDao存在,就不知道用哪个对象,出现报错:
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.edu.spring.UserDao' available: expected single matching bean but found 2: userDao,createUserDao
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)
at com.edu.spring.App.main(App.java:26)
这时可以用两个方法解决问题。
方法一:使用Qualifier注释
@Service
public class UserService {
@Autowired
@Qualifier("createUserDao")
private UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
这样就知道用哪一个对象了。(注意要将其他所有用到UserDao的地方,都需要使用Qualifier指明到底用哪一个userdao)
方法二:使用Primary注释Bean
在MyConfig.java中:
@Bean
@Primary
public UserDao createUserDao(){
return new UserDao();
}
Primary用于有多个对象存在时,首先寻找标有Primary的对象,进行注入。
如果要注入其他类,比如Car.java,需要使用Resource注释来注入。
package com.edu.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component("myUser")
public class User {
@Autowired
private UserService userService;
// 使用JSR 250的注解
@Resource
private Car car;
@Override
public String toString() {
return "User{" +
"userService=" + userService +
", car=" + car +
'}';
}
}
输出:User{userService=UserService{userDao=com.edu.spring.UserDao@1b083826}, car=com.edu.spring.Car@55a1c291}
说明Car.java 也注入进来了。
此外还可以使用 JSR 330 的注解方式进行注入,但是这种方式需要添加依赖:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
尝试将Cat.java注入进去User。使用Inject注释。
package com.edu.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.inject.Inject;
@Component("myUser")
public class User {
// Spring提供的注解
@Autowired
private UserService userService;
// 使用JSR-250的注解
@Resource
private Car car;
// 使用JSR-330的注解
@Inject
private Cat cat;
@Override
public String toString() {
return "User{" +
"userService=" + userService +
", car=" + car +
", cat=" + cat +
'}';
}
}
控制台显示输出:信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring 说明JSR330 已经起作用了。
User{userService=UserService{userDao=com.edu.spring.UserDao@55ca8de8}, car=com.edu.spring.Car@5d740a0f, cat=com.edu.spring.Cat@214b199c}
说明cat也成功注入进去了。
我们发现AnnotationConfigApplicationContext很麻烦,每次都需要手动去添加新的类。
我们可以使用包扫描的方式进行类装载,更加方便。
package com.edu.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AnnotationClient {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.edu.spring");
System.out.println(context.getBean(Jeep.class));
System.out.println(context.getBean("createJeepFactoryBean"));
System.out.println(context.getBean(Car.class));
System.out.println(context.getBean(Cat.class));
System.out.println(context.getBean(Dog.class));
System.out.println(context.getBean(Fish.class));
System.out.println(context.getBean("myUser"));
context.close();
}
}
如果我们想排除某一些类,这些类不想被扫描到。方法如下:
新建AnnotationScan.java
package com.edu.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("com.edu.spring")
@Configuration
public class AnnotationScan {
}
新建AnnotationClient2.java
package com.edu.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AnnotationClient2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationScan.class);
context.close();
}
}
这种方法同样可以达到扫描包的效果。如果我们想排除Dog类,需要将MyConfig.java中的配置Dog的地方删除,新建DogConfig.java
package com.edu.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DogConfig {
@Bean(initMethod = "myInit", destroyMethod = "myDestory")
public Dog createDog(){
return new Dog();
}
}
修改AnnotationScan.java
package com.edu.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@ComponentScan(basePackages = "com.edu.spring", excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = DogConfig.class))
@Configuration
public class AnnotationScan {
}
这样就将Dog类排除了,然后执行AnnotationClient2.java 就会报错:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.edu.spring.Dog' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:348)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)
at com.edu.spring.AnnotationClient2.main(AnnotationClient2.java:13)
说明Dog已经排除成功了。同样可以排除UserController.java
因为UserController上有注释,可以直接排除。
package com.edu.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@ComponentScan(basePackages = "com.edu.spring", excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {DogConfig.class, UserController.class}))
@Configuration
public class AnnotationScan {
}
第二课
Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他们都可代表Spring容器,Spring容器是生成Bean实例的工厂,并且管理容器中的Bean。
BeanFactory负责配置、创建、管理Bean,他有一个子接口:ApplicationContext,因此也称之为Spring上下文。Spring容器负责管理Bean与Bean之间的依赖关系。
BeanFactory接口包含以下几个基本方法:
- Boolean containBean(String name):判断Spring容器是否包含id为name的Bean实例。
- <T> getBean(Class<T> requiredTypr):获取Spring容器中属于requiredType类型的唯一的Bean实例。
- Object getBean(String name):返回Sprin容器中id为name的Bean实例。
- <T> T getBean(String name,Class requiredType):返回容器中id为name,并且类型为requiredType的Bean
- Class <?> getType(String name):返回容器中指定Bean实例的类型。
调用者只需使用getBean()方法即可获得指定Bean的引用,无须关心Bean的实例化过程。即Bean实例的创建过程完全透明。
让Bean获取Spring容器
在Spring中我们可以使用Spring容器中getBean()方法来获取Spring容器中的Bean实例。在这样的访问模式下,程序中总是持有Spring容器的引用。但是在实际的应用中,Spring容器通常是采用声明式方式配置产生:即开发者只要在web.xml文件中配置一个Listener,该Listener将会负责初始化Spring容器。在这种情况下,容器中Bean处于容器管理下,无须主动访问容器,只需要接受容器的注入管理即可。同时Bean实例的依赖关系通常也是由容器自动注入,无须Bean实例主动请求。
如何注入ApplicationContext?
新建myConfig.java
package com.edu.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public User createUser(){
return new User();
}
}
新建User.java
package com.edu.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
public class User {
@Autowired
private ApplicationContext applicationContext;
public void show(){
System.out.println("user:" + applicationContext.getClass());
}
}
新建App.java
package com.edu.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.edu.spring");
System.out.println(context.getBean(User.class));
System.out.println(context.getBean("createUser"));
context.getBean(User.class).show();
context.close();
}
}
方法一:
在Use.java中使用AutoWired成功注入ApplicationContext。也可以使用JSR 250 和JSR330 方式注入
输出如下:
com.edu.spring.User@6ee12bac
com.edu.spring.User@6ee12bac
user:class org.springframework.context.annotation.AnnotationConfigApplicationContext
方法二:
新建Book.java
package com.edu.spring;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class Book implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void show(){
System.out.println("book:" + applicationContext.getClass());
}
}
方法三:
新键Bank.java
package com.edu.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class Bank {
private ApplicationContext applicationContext;
public Bank (ApplicationContext applicationContext){
this.applicationContext = applicationContext;
}
public void show(){
System.out.println("bank:" + applicationContext.getClass());
}
}
这种方法同样可以注入ApplicationContext。但是这个方法,构造函数只能有一个,如果有多个的话,就必须有一个无参的构造函数,此时,spring会调用无参的构造函数。构造函数的参数,必须都要在Spring容器中。
BeanPostProcessor
BeanPostProcessor会在每个bean初始化的时候,调用一次
新建EchoBeanPostProcessor.java
package com.edu.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
/**
* BeanPostProcessor会在每个bean初始化的时候,调用一次
*/
@Component
public class EchoBeanPostProcessor implements BeanPostProcessor {
// 在bean依赖装配(属性设置完)完成之后触发
@Nullable
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("====postProcessBeforeInitialization======" + bean.getClass());
return bean;
}
// 在bean init方法执行之后触发
@Nullable
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("====postProcessAfterInitialization======" + bean.getClass());
return bean;
}
}
打印输出如下:
====postProcessBeforeInitialization======class org.springframework.context.event.EventListenerMethodProcessor
====postProcessAfterInitialization======class org.springframework.context.event.EventListenerMethodProcessor
====postProcessBeforeInitialization======class org.springframework.context.event.DefaultEventListenerFactory
====postProcessAfterInitialization======class org.springframework.context.event.DefaultEventListenerFactory
====postProcessBeforeInitialization======class com.edu.spring.Bank
====postProcessAfterInitialization======class com.edu.spring.Bank
====postProcessBeforeInitialization======class com.edu.spring.Book
====postProcessAfterInitialization======class com.edu.spring.Book
====postProcessBeforeInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$3a1c64d1
====postProcessAfterInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$3a1c64d1
====postProcessBeforeInitialization======class com.edu.spring.User
====postProcessAfterInitialization======class com.edu.spring.User
com.edu.spring.User@482cd91f
com.edu.spring.User@482cd91f
user:class org.springframework.context.annotation.AnnotationConfigApplicationContext
book:class org.springframework.context.annotation.AnnotationConfigApplicationContext
bank:class org.springframework.context.annotation.AnnotationConfigApplicationContext
在每个bean初始化之前和之后执行的方法。
如果我们给User添加一个初始化方法init
user.java
package com.edu.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
public class User {
@Autowired
private ApplicationContext applicationContext;
public void init(){
System.out.println("user init");
}
public void show(){
System.out.println("user:" + applicationContext.getClass());
}
}
在MyConfig.java中指定初始化方法:
package com.edu.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean(initMethod = "init")
public User createUser(){
return new User();
}
}
则输出结果如下:
====postProcessBeforeInitialization======class org.springframework.context.event.EventListenerMethodProcessor
====postProcessAfterInitialization======class org.springframework.context.event.EventListenerMethodProcessor
====postProcessBeforeInitialization======class org.springframework.context.event.DefaultEventListenerFactory
====postProcessAfterInitialization======class org.springframework.context.event.DefaultEventListenerFactory
====postProcessBeforeInitialization======class com.edu.spring.Bank
====postProcessAfterInitialization======class com.edu.spring.Bank
====postProcessBeforeInitialization======class com.edu.spring.Book
====postProcessAfterInitialization======class com.edu.spring.Book
====postProcessBeforeInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$3a1c64d1
====postProcessAfterInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$3a1c64d1
====postProcessBeforeInitialization======class com.edu.spring.User
user init
====postProcessAfterInitialization======class com.edu.spring.User
com.edu.spring.User@482cd91f
com.edu.spring.User@482cd91f
user:class org.springframework.context.annotation.AnnotationConfigApplicationContext
book:class org.springframework.context.annotation.AnnotationConfigApplicationContext
bank:class org.springframework.context.annotation.AnnotationConfigApplicationContext
说明postProcessBeforeInitialization方法是在bean init方法之前执行,postProcessAfterInitialization方法是在bean init方法之后执行。
修改User.java
package com.edu.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
public class User {
private ApplicationContext applicationContext;
public void init(){
System.out.println("user init");
}
public void show(){
System.out.println("user:" + applicationContext.getClass());
}
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
System.out.println("applicationContext set");
this.applicationContext = applicationContext;
}
}
Autowired既可以用在变量上,也可以用在方法上。
输出结果如下:
====postProcessBeforeInitialization======class org.springframework.context.event.EventListenerMethodProcessor
====postProcessAfterInitialization======class org.springframework.context.event.EventListenerMethodProcessor
====postProcessBeforeInitialization======class org.springframework.context.event.DefaultEventListenerFactory
====postProcessAfterInitialization======class org.springframework.context.event.DefaultEventListenerFactory
====postProcessBeforeInitialization======class com.edu.spring.Bank
====postProcessAfterInitialization======class com.edu.spring.Bank
====postProcessBeforeInitialization======class com.edu.spring.Book
====postProcessAfterInitialization======class com.edu.spring.Book
====postProcessBeforeInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$50df4f2b
====postProcessAfterInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$50df4f2b
applicationContext set
====postProcessBeforeInitialization======class com.edu.spring.User
user init
====postProcessAfterInitialization======class com.edu.spring.User
com.edu.spring.User@5b0abc94
com.edu.spring.User@5b0abc94
user:class org.springframework.context.annotation.AnnotationConfigApplicationContext
book:class org.springframework.context.annotation.AnnotationConfigApplicationContext
bank:class org.springframework.context.annotation.AnnotationConfigApplicationContext
说明依赖都装配完成之后出发postProcessBeforeInitialization
代理对象
新建LogUser.java
package com.edu.spring;
public class LogUser extends User {
@Override
public void show() {
System.out.println("log start ...");
super.show();
System.out.println("log end ...");
}
}
修改Use.java
public void show(){
System.out.println("user:" + applicationContext);
}
修改EchoBeanPostProcessor.java
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("====postProcessBeforeInitialization======" + bean.getClass());
if(bean instanceof User){
return new LogUser();
}
return bean;
}
输出结果如下:
====postProcessBeforeInitialization======class org.springframework.context.event.EventListenerMethodProcessor
====postProcessAfterInitialization======class org.springframework.context.event.EventListenerMethodProcessor
====postProcessBeforeInitialization======class org.springframework.context.event.DefaultEventListenerFactory
====postProcessAfterInitialization======class org.springframework.context.event.DefaultEventListenerFactory
====postProcessBeforeInitialization======class com.edu.spring.Bank
====postProcessAfterInitialization======class com.edu.spring.Bank
====postProcessBeforeInitialization======class com.edu.spring.Book
====postProcessAfterInitialization======class com.edu.spring.Book
====postProcessBeforeInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$50df4f2b
====postProcessAfterInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$50df4f2b
applicationContext set
====postProcessBeforeInitialization======class com.edu.spring.User
user init
====postProcessAfterInitialization======class com.edu.spring.LogUser
com.edu.spring.LogUser@75c072cb
com.edu.spring.LogUser@75c072cb
log start ...
user:null
log end ...
book:class org.springframework.context.annotation.AnnotationConfigApplicationContext
bank:class org.springframework.context.annotation.AnnotationConfigApplicationContext
可以对指定的Bean做一些处理,比如返回对象的代理对象。
spring扩展二
目前我们知道BeanPostProcessor是在某个bean初始化的时候,进行回调的,我们可以控制bean的初始化和其他的操作。如果我们想要对某个容器进行初始化回调,如何做?spring同样留了接口, BeanFactoryPostProcessor,是在spring容器初始化之后进行的
新建App.java
package com.edu.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.edu.spring");
context.close();
}
}
新建MyBeanFactoryPostProcessor.java
package com.edu.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* BeanFactoryPostProcessor在spring容器的初始化之后出发,而且只会触发一次
* 触发的时机比BeanPostProcessor早
*/
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("============" + configurableListableBeanFactory.getBeanDefinitionCount());
}
}
运行App.java 输出结果如下:
============7
说明即使我们什么都不做,spring容器还是有很多个bean的。
新建User.java
public class User {
public void init(){
System.out.println("user init");
}
}
新建MyConfig.java
package com.edu.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean(initMethod = "init")
public User createUser(){
return new User();
}
@Bean
public User createUser2(){
return new User();
}
}
运行App.java,输出结果如下:
============10
user init
新建MyBeanPostProcessor.java,对比BeanPostProcessor和BeanFactoryPostProcessor执行顺序是什么
package com.edu.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("====postProcessBeforeInitialization=====" + bean.getClass());
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("====postProcessAfterInitialization=====" + bean.getClass());
return bean;
}
}
运行输出结果:
============11
四月 22, 2019 3:47:16 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
====postProcessBeforeInitialization=====class org.springframework.context.event.EventListenerMethodProcessor
====postProcessAfterInitialization=====class org.springframework.context.event.EventListenerMethodProcessor
====postProcessBeforeInitialization=====class org.springframework.context.event.DefaultEventListenerFactory
====postProcessAfterInitialization=====class org.springframework.context.event.DefaultEventListenerFactory
====postProcessBeforeInitialization=====class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$a601222d
====postProcessAfterInitialization=====class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$a601222d
====postProcessBeforeInitialization=====class com.edu.spring.User
user init
====postProcessAfterInitialization=====class com.edu.spring.User
====postProcessBeforeInitialization=====class com.edu.spring.User
====postProcessAfterInitialization=====class com.edu.spring.User
说明BeanFactoryPostProcessor最先执行,但只会执行一次,因为容器执行完成一次。
BeanDefinitionRegistry
我们使用Component注释来注册一个bean,但这是静态的注册,我们可以通过BeanDefinitionRegistry来动态注册一个Bean。
新建一个要注入的类Person.java
package com.edu.spring;
public class Person {
private String name;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
新建MyBeanDefinitionRegistryPostProcessor.java,注入十个Person类,并给他们的属性赋值。
package com.edu.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
/**
* BeanDefinitionRegistryPostProcessor可以拿到BeanDefinitionRegistry,ConfigurableListableBeanFactory两个对象
* BeanDefinitionRegistry可以动态注入bean
*/
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
for( int i = 0; i < 10; i++){
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Person.class);
// 往person内注入属性,可以注入引用
beanDefinitionBuilder.addPropertyValue("name", "admin" + i);
beanDefinitionRegistry.registerBeanDefinition("person" + i, beanDefinitionBuilder.getBeanDefinition());
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
修改App.java
package com.edu.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.edu.spring");
context.getBeansOfType(Person.class).values().forEach(person -> {
System.out.println(person);
});
context.close();
}
}
输出结果:
============22
四月 22, 2019 4:38:56 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
====postProcessBeforeInitialization=====class org.springframework.context.event.EventListenerMethodProcessor
====postProcessAfterInitialization=====class org.springframework.context.event.EventListenerMethodProcessor
====postProcessBeforeInitialization=====class org.springframework.context.event.DefaultEventListenerFactory
====postProcessAfterInitialization=====class org.springframework.context.event.DefaultEventListenerFactory
====postProcessBeforeInitialization=====class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$bc5fcd78
====postProcessAfterInitialization=====class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$bc5fcd78
====postProcessBeforeInitialization=====class com.edu.spring.User
user init
====postProcessAfterInitialization=====class com.edu.spring.User
====postProcessBeforeInitialization=====class com.edu.spring.User
====postProcessAfterInitialization=====class com.edu.spring.User
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
====postProcessBeforeInitialization=====class com.edu.spring.Person
====postProcessAfterInitialization=====class com.edu.spring.Person
Person{name='admin0'}
Person{name='admin1'}
Person{name='admin2'}
Person{name='admin3'}
Person{name='admin4'}
Person{name='admin5'}
Person{name='admin6'}
Person{name='admin7'}
Person{name='admin8'}
Person{name='admin9'}
说明Person自动注入进去了。
除了这个方法,也可以使用
AnnotationConfigApplicationContext context.registerBeanDefinition(beanName, beanDefinition);
springboot-1
新建pom.xml
<?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>cn.ac.iie</groupId>
<artifactId>spring-course</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
新建App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class App {
@Bean
public Runnable createRunable(){
return () -> {
System.out.println("spring boot is running");
};
}
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
context.getBean(Runnable.class).run();
}
}
运行App.java 可以得到输出结果如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.4.RELEASE)
2019-04-22 22:52:03.868 INFO 11372 --- [ main] com.edu.spring.App : Starting App on duandingyangdeMacBook-Pro.local with PID 11372 (/Users/duandingyang/git-project/springcourse/target/classes started by duandingyang in /Users/duandingyang/git-project/springcourse)
2019-04-22 22:52:03.876 INFO 11372 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default
2019-04-22 22:52:04.651 INFO 11372 --- [ main] com.edu.spring.App : Started App in 1.432 seconds (JVM running for 2.166)
spring boot is running
这是一个最简单的SpringBoot应用。
跟之前的spring使用方法差不多,只是main的入口变了。
在pom.xml文件中,如果我们不想使用parent依赖,应该怎么做?
新的pom.xml如下
<?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>cn.ac.iie</groupId>
<artifactId>spring-course</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
在App.java中,点击查看@SpringBootApplication注释,有三个重要的注释,分别是@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
目前的情况,我们可以直接使用@ComponentScan注释就行
App.java如下:
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class App {
@Bean
public Runnable createRunable(){
return () -> {
System.out.println("spring boot is running");
};
}
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
context.getBean(Runnable.class).run();
}
}
运行结果跟之前是一样的。
其实SpringApplication.run(App.class, args)中App.class一般就是入口,也就是配置类,里面可以配置各种Bean。
删掉@ComponentScan 修改入口配置
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
public class App {
@Bean
public Runnable createRunable(){
return () -> {
System.out.println("spring boot is running");
};
}
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
context.getBean(Runnable.class).run();
}
}
新建App2.java
package com.edu.spring;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class App2 {
}
这样输出结果如下:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.Runnable' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:343)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1123)
at com.edu.spring.App.main(App.java:21)
因为找不到bean,如果将App.java中的生命bean 的代码剪切到App2.java中,那么程序正常运行。
也可以通过这个方式加载配置类:
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.HashSet;
import java.util.Set;
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App2.class);
ConfigurableApplicationContext context = application.run(args);
context.getBean(Runnable.class).run();
}
}
新建MyConfig.java
package com.edu.spring;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import java.util.ArrayList;
import java.util.List;
@SpringBootConfiguration
public class MyConfig {
@Bean
public List<String> createList(){
ArrayList arrayList = new ArrayList();
arrayList.add("a");
return arrayList;
}
}
使用SpringBootConfiguration同样可以装配Bean
App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.List;
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App2.class);
ConfigurableApplicationContext context = application.run(args);
context.getBean(Runnable.class).run();
System.out.println(context.getBean(List.class));
}
}
Springboot配置文件
springboot默认的配置文件是application.properties.
新建application.properties,内容如下:
local.ip=192.168.1.1
local.port=8080
name=springboot
app.name=this is ${name}
方法一 使用context.getEnvironment().getProperty("local.ip")
新建App.java,
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootConfiguration
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getEnvironment().getProperty("local.ip"));
context.close();
}
}
输出:
192.168.1.1
方法二:
新建UserConfig.java
package com.edu.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class UserConfig {
@Autowired
private Environment environment;
public void show(){
System.out.println("local.ip=" + environment.getProperty("local.ip"));
}
}
修改App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
context.getBean(UserConfig.class).show();
context.close();
}
}
同样可以输出local.ip=192.168.1.1
方法三:
修改UserConfig.java, 使用@Value("${local.port}")注释,来获取
package com.edu.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class UserConfig {
@Value("${local.port}")
private String localPort;
/**
* @Value 默认要有配置项,配置项可以为空,如果没有配置项,则可以给默认值
*/
@Value("${tomcat.port:9090}")
private String tomcatPort;
@Autowired
private Environment environment;
public void show(){
System.out.println("local.ip=" + environment.getProperty("local.ip"));
System.out.println("local.port=" + localPort);
System.out.println("name=" + environment.getProperty("name"));
System.out.println("app.name=" + environment.getProperty("app.name"));
System.out.println("tomcat.port=" + tomcatPort);
}
}
使用配置文件引用其他变量
public void show(){
System.out.println("local.ip=" + environment.getProperty("local.ip"));
System.out.println("local.port=" + localPort);
System.out.println("name=" + environment.getProperty("name"));
System.out.println("app.name=" + environment.getProperty("app.name"));
}
application.properties的位置是resources目录下(即classpath根目录)或者是resources/config目录(即classpath:/config目录)下。
如果要改application.properties的名字和目录如何做?
可以在intellij工具 Program argument添加参数 --spring.config.name=文件名 可以省略文件扩展名
如果修改目录 添加参数 --spring.config.location=classpath:conf/app.properties, 必须指定扩展名。还可以指定多个路径,用逗号隔开。之间的指定方式有两种1.classpath: 2.file:
如果要加载其他配置文件,如何做?
新建jdbc.properties
url=jdbc:mysql:///springboot
driverClassName=com.mysql.jdbc.Driver
新建FileConfig.java
package com.edu.spring;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:jdbc.properties")
public class FileConfig {
}
新建JdbcConfig.java
package com.edu.spring;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class JdbcConfig {
@Value("${url}")
private String url;
@Value("${driverClassName}")
private String driverClassName;
public void show(){
System.out.println("url=" + url);
System.out.println("driverClassName=" + driverClassName);
}
}
在App.java中
context.getBean(JdbcConfig.class).show();
可以正常输出。
如果是多个路径下的文件,修改FileConfig.java
package com.edu.spring;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:jdbc.properties")
@PropertySource("file:/e/tmp/jdbc.properties")
public class FileConfig {
}
或者使用
package com.edu.spring;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
@Configuration
@PropertySources({@PropertySource("classpath:jdbc.properties"),@PropertySource("file:/e/tmp/jdbc.properties")})
public class FileConfig {
}
新建DataSourceProperties.java
package com.edu.spring;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "ds")
@PropertySource("classpath:/ds.properties")
public class DataSourceProperties {
private String url;
private String driverClassName;
private String username;
private String password;
public void show(){
System.out.println("url:" + url);
System.out.println("driverClassName:" + driverClassName);
System.out.println("username:" + username);
System.out.println("password:" + password);
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
新建ds.properties
ds.url=jdbc:mysql:///springboot
ds.driverClassName=com.mysql.jdbc.Driver
ds.username=root
ds.password=123456
springboot 也可以使用application.yml作为配置文件,是使用缩进方式书写。
从配置文件中,注入集合与数组
新建TomcatProperties.java
package com.edu.spring;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Component
@ConfigurationProperties("ds")
public class TomcatProperties {
private List<String> hosts = new ArrayList<>();
private String[] ports;
@Override
public String toString() {
return "TomcatProperties{" +
"hosts=" + hosts +
", ports=" + Arrays.toString(ports) +
'}';
}
public String[] getPorts() {
return ports;
}
public void setPorts(String[] ports) {
this.ports = ports;
}
public List<String> getHosts() {
return hosts;
}
public void setHosts(List<String> hosts) {
this.hosts = hosts;
}
}
applicatioin.properties
ds.hosts[0]=192.168.1.100
ds.hosts[1]=192.168.1.101
ds.hosts[2]=192.168.1.102
ds.ports[0]=8080
ds.ports[1]=8081
ds.ports[2]=8082
App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(TomcatProperties.class));
context.close();
}
}
注入正常。输出结果:
TomcatProperties{hosts=[192.168.1.100, 192.168.1.101, 192.168.1.102], ports=[8080, 8081, 8082]}
动态引入配置文件,使用EnvironmentPostProcessor接口
新建MyEnvironmentPostProcessor.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.stereotype.Component;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
@Component
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
try(InputStream inputStream = new FileInputStream("F:/test/springboot.properties")){
Properties properties = new Properties();
properties.load(inputStream);
PropertiesPropertySource propertySource = new PropertiesPropertySource("my", properties);
environment.getPropertySources().addLast(propertySource);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试输出
System.out.println(context.getEnvironment().getProperty("springboot.name"));
输出结果为Null
说明没有正确注入,在resources目录下新建META-INF/spring.factories,内容如下:
org.springframework.boot.env.EnvironmentPostProcessor=com.edu.spring.MyEnvironmentPostProcessor
执行输出结果正常。原因是EnvironmentPostProcessor接口不属于spring的,而是属于Springboot的,因此需要配置到spring.factories。
有了这一功能我们就可以随意的增加一些配置了,任意读取配置,即配置文件中心化。
profile,开发阶段和上线测试阶段是不同的,如何在不同阶段载入不同配置
新建application-dev.properties,内容如下
jdbc.url=mysql:jdbc://127.0.0.1/db_springboot_dev
新建application-test.properties,内容如下:
jdbc.url=mysql:jdbc://127.0.0.1/db_springboot_test
App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
//application.setAdditionalProfiles("dev");
application.setAdditionalProfiles("test");
ConfigurableApplicationContext context = application.run(args);
System.out.println(context.getBean(TomcatProperties.class));
System.out.println(context.getEnvironment().getProperty("springboot.name"));
System.out.println(context.getEnvironment().getProperty("jdbc.url"));
context.close();
}
}
这样就可以实现切换。默认的application.properties也会加进去。
通过启动参数来控制生效的profile,--spring.profiles.active=test,dev,这样test和dev便同时启用了。
新建MyConfig.java
package com.edu.spring;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
@SpringBootConfiguration
public class MyConfig {
@Bean
public Runnable createRunable(){
System.out.println("===1==");
return () -> {};
}
/**
* 当application-test.properties激活之后才装配这个Bean
* @return
*/
@Bean
@Profile("test")
public Runnable createRunable2(){
System.out.println("===2==");
return () -> {};
}
/**
* 当application-dev.properties激活之后才装配这个Bean
* @return
*/
@Bean
@Profile("dev")
public Runnable createRunable3(){
System.out.println("===3==");
return () -> {};
}
}
可以当具体properties激活时,装配Bean。profile注释也可以用在类上,表示当某个profile生效时,使用这个类。
spring boot自动配置
新建接口EncodingConvert.java
package com.edu.spring;
public interface EncodingConvert {
}
新建实现类GBKEncodingConvert.java
public class GBKEncodingConvert implements EncodingConvert {
}
新建实现类UTF8EncodingConvert.java
package com.edu.spring;
public class UTF8EncodingConvert implements EncodingConvert {
}
新建EncodingConvertConfig.java
package com.edu.spring;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
@SpringBootConfiguration
public class EncodingConvertConfig {
@Bean
public EncodingConvert createUTF8EncodingConvert(){
return new UTF8EncodingConvert();
}
@Bean
public EncodingConvert createGBKEncodingConvert(){
return new GBKEncodingConvert();
}
}
新建App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(System.getProperty("file.encoding"));
System.out.println(context.getBeansOfType(EncodingConvert.class));
context.close();
}
}
运行App.java,打印输出结果如下:
{createUTF8EncodingConvert=com.edu.spring.UTF8EncodingConvert@c9d0d6, createGBKEncodingConvert=com.edu.spring.GBKEncodingConvert@6ccdb29f}
两个Bean都装配成功。
新建GBKCondition.java
package com.edu.spring;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class GBKCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String encoding = System.getProperty("file.encoding");
if(encoding != null){
return "gbk".equals(encoding.toLowerCase());
}
return false;
}
}
新建UTF8Condition.java
package com.edu.spring;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class UTF8Condition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String encoding = System.getProperty("file.encoding");
if(encoding != null){
return "utf-8".equals(encoding.toLowerCase());
}
return false;
}
}
在intellij 的VMoption参数添加 -Dfile.encoding=GBK
打印输出:
{createGBKEncodingConvert=com.edu.spring.GBKEncodingConvert@512baff6}
@Conditional 是基于条件的自动配置,一般配合Condition接口一起使用,只有接口(一个或多个)实现类都返回true才装配,否则不装配。
它可以用在方法上,则只对该方法起作用,还可以用在类上,则对该类的所有方法起作用。
此外,@Conditional({UTF8Condition.class, GBKCondition.class}),表示两个条件都返回true才生效。
@ConditionalOnProperty,表示某个属性等于某个值时。
新建UserConfiguration.java
package com.edu.spring;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
@SpringBootConfiguration
public class UserConfiguration {
@Bean
@ConditionalOnProperty(name = "runnable.enabled", havingValue = "true")
public Runnable createRunnable(){
return ()->{};
}
}
当application.properties中的属性runnable.enabled=true时,这个Bean才能够装配。否则装配不成功。matchIfMissing表示当这个配置不存在的时候,也为true。
修改UserConfiguration.java
package com.edu.spring;
import com.google.gson.Gson;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
@SpringBootConfiguration
public class UserConfiguration {
@Bean
@ConditionalOnProperty(name = "runnable.enabled", havingValue = "true", matchIfMissing = true)
public Runnable createRunnable() {
return () -> {
};
}
@Bean
@ConditionalOnClass(name = "com.google.gson.Gson")
public Runnable createGsonRunnable() {
return () -> {
};
}
}
修改pom.xml,添加gson依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.1</version>
</dependency>
@ConditionalOnClass表示当存在这个“com.google.gson.Gson”类的时候,就装配这个Bean。
@ConditionalOnMissingClass表示当不存在这个“com.google.gson.Gson”类的时候,就装配这个Bean。
打印输出:
{createRunnable=com.edu.spring.UserConfiguration$$Lambda$137/800735172@335b5620, createGsonRunnable=com.edu.spring.UserConfiguration$$Lambda$138/478489615@29a0cdb}
去掉pom.xml中的依赖是,就不会装配这个bean。
@ConditionalOnBean 根据容器中存在某个bean来进行装配
修改UserConfiguration.java
@Bean
@ConditionalOnBean(name="user")
public Runnable createBeanRunnable() {
return () -> {
};
}
运行时输出:
{createRunnable=com.edu.spring.UserConfiguration$$Lambda$137/698741991@22356acd, createGsonRunnable=com.edu.spring.UserConfiguration$$Lambda$138/669284403@386f0da3}
没有输出createBeanRunnable这个Bean,添加User.java
package com.edu.spring;
import org.springframework.stereotype.Component;
@Component
public class User {
}
然后运行输出:
{createRunnable=com.edu.spring.UserConfiguration$$Lambda$138/1934932165@27d4a09, createGsonRunnable=com.edu.spring.UserConfiguration$$Lambda$139/1508038883@7e4204e2, createBeanRunnable=com.edu.spring.UserConfiguration$$Lambda$140/728943498@b7c4869}
说明成功配置了createBeanRunnable这个bean了。
@ConditionalOnMissiongBean 根据容器中不存在某个bean来进行装配
Spring Boot @Enable*注解工作原理
新建TomcatProperties.java
package com.edu.spring;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "tomcat")
public class TomcatProperties {
private String host;
private String port;
@Override
public String toString() {
return "TomcatProperties{" +
"host='" + host + '\'' +
", port='" + port + '\'' +
'}';
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
}
application.properties
tomcat.host=192.168.1.100
tomcat.port=8080
新建App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(TomcatProperties.class));
context.close();
}
}
运行输出结果:
TomcatProperties{host='192.168.1.100', port='8080'}
当我们修改App.java的注解时,
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
/**
* @EnableConfiguratinProperties是用来启用一个特性的,这个特性可以把配置文件的属性注入到bean里面去
*/
@EnableConfigurationProperties
@ComponentScan
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(TomcatProperties.class));
context.close();
}
}
程序也可以正常运行,但是当我们将注解@EnableConfiguratinProperties删掉后,程序就获取不到properties的内容了。说明是@EnableConfiguratinProperties起了作用。
如何在Springboot中启动异步?
新建Jeep.java
package com.edu.spring;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class Jeep implements Runnable {
@Override
public void run() {
try {
for(int i = 1; i<= 10; i++){
System.out.println("============" + i);
TimeUnit.SECONDS.sleep(1);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
修改App.java
context.getBean(Runnable.class).run();
System.out.println("----end-----");
输出结果如下:
TomcatProperties{host='192.168.1.100', port='8080'}
============1
============2
============3
============4
============5
============6
============7
============8
============9
============10
----end-----
说明当run方法执行完之后,才输出end。如何实现异步?
在App.java上启动异步,添加注释:
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* @EnableConfiguratinProperties是用来启用一个特性的,这个特性可以把配置文件的属性注入到bean里面去,一般是和@ConfigurationProperties一起使用
* @EnableAsync 启用异步,一般是和@Async一起使用
*/
@EnableConfigurationProperties
@EnableAsync
@ComponentScan
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(TomcatProperties.class));
context.getBean(Runnable.class).run();
System.out.println("----end-----");
context.close();
}
}
在Jeep.java的run方法上添加@Async注释
package com.edu.spring;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class Jeep implements Runnable {
@Async
@Override
public void run() {
try {
for(int i = 1; i<= 10; i++){
System.out.println("============" + i);
TimeUnit.SECONDS.sleep(1);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
运行输出结果如下:
----end-----
============1
============2
============3
============4
============5
============6
============7
============8
============9
============10
说明已经成功异步了。
新建User.java
package com.edu.spring;
public class User {
}
新建Role.java
package com.edu.spring;
public class Role {
}
新建App2.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class App2 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
context.close();
}
}
此时App2.java中无法获取User的Bean和Role的Bean,想获取的方式是:有很多,之前有讲过,例如:@Component。今天讲另一种方式:
修改App2.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
@ComponentScan
@Import(User.class)
public class App2 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
System.out.println(context.getBean(User.class));
context.close();
}
}
添加Import注释,这样就可以使用User的Bean了。
@Import注释也可以导入一个数组,例如:@Import({User.class, Role.class})
此外,@Import还可以到如配置类
新建MyConfiguration.java
package com.edu.spring;
import org.springframework.context.annotation.Bean;
public class MyConfiguration {
@Bean
public Runnable createRunnable(){
return () -> {};
}
@Bean
public Runnable createRunnable2(){
return () -> {};
}
}
修改App2.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
/**
* @Import 用来导入一个或多个类(Bean会被Spring容器托管),或者配置类(配置类里面的bean会被Spring容器托管)
*/
@ComponentScan
@Import({User.class, Role.class, MyConfiguration.class})
public class App2 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
System.out.println(context.getBean(User.class));
System.out.println(context.getBeansOfType(Runnable.class));
context.close();
}
}
使用Import导入MyConfiguration,输出结果:
com.edu.spring.User@6f27a732
{jeep=com.edu.spring.Jeep@6c779568, createRunnable=com.edu.spring.MyConfiguration$$Lambda$93/1730704097@f381794, createRunnable2=com.edu.spring.MyConfiguration$$Lambda$94/726379593@2cdd0d4b}
说明成功导入了配置类。
ImportSelector是什么?
新建MyImportSelector.java
package com.edu.spring;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* ImportSelector的方法的返回值,必须是一个class(全称),该class会被spring容器托管起来。
*/
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.edu.spring.User",Role.class.getName(), MyConfiguration.class.getName()};
}
}
修改App2.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
/**
* @Import 用来导入一个或多个类(Bean会被Spring容器托管),或者配置类(配置类里面的bean会被Spring容器托管)
*/
@ComponentScan
//@Import({User.class, Role.class, MyConfiguration.class})
@Import(MyImportSelector.class)
public class App2 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
System.out.println(context.getBean(User.class));
System.out.println(context.getBean(Role.class));
System.out.println(context.getBeansOfType(Runnable.class));
context.close();
}
}
同样可以输出User的Bean,Role的Bean以及Runnable的Bean,说明装配成功。
新建EnableLog.java
package com.edu.spring;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyImportSelector.class)
public @interface EnableLog {
String name();
}
在MyImportSelector.java中修改:
package com.edu.spring;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* ImportSelector的方法的返回值,必须是一个class(全称),该class会被spring容器托管起来。
*/
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
/**
* 这里可以获取到注解的详细信息。然后根据信息去动态的返回需要被spring容器管理的bean
*/
return new String[]{"com.edu.spring.User",Role.class.getName(), MyConfiguration.class.getName()};
}
}
可以输出注释的信息。
修改APP2.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
/**
* @Import 用来导入一个或多个类(Bean会被Spring容器托管),或者配置类(配置类里面的bean会被Spring容器托管)
*/
@ComponentScan
//@Import({User.class, Role.class, MyConfiguration.class})
@Import(MyImportSelector.class)
@EnableLog(name="my springboot")
public class App2 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
System.out.println(context.getBean(User.class));
System.out.println(context.getBean(Role.class));
System.out.println(context.getBeansOfType(Runnable.class));
context.close();
}
}
输出结果可以输出{name=[my springboot]},可以获取到注解的详细信息
@EnableAutoConfiguration深入分析
新建App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(Runnable.class));
context.close();
}
}
运行报错,因为没有Runnable这个bean。
新建项目:corebean
pom.xml文件:
<?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.edu.core</groupId>
<artifactId>core-bean</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
</dependencies>
</project>
新建RunnableConfiguration.java
package com.edu.core.bean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RunnableConfiguration {
@Bean
public Runnable createRunnable(){
return () -> {};
}
}
在springcourse项目的pom.xml中添加corebean项目依赖:
<?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>cn.ac.iie</groupId>
<artifactId>spring-course</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.edu.core</groupId>
<artifactId>core-bean</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
在次运行App.java
同样显示没有找到Runnable的bean类。
如果spring只支持当前项目中加载配置,那么他的扩展性太不好了,如何解决这个第三方jar的配置?
使用EnableAutoConfiguration注解
修改App.java,我们只用@EnableAutoConfiguration和@ComponentScan注释
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
/**
* EnableAutoConfiguration 作用:从classpath中搜索所有META-INF/spring.factories配置文件,
* 然后,将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration key对应的配置项加载到spring容器中
*/
@EnableAutoConfiguration
@ComponentScan
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(Runnable.class));
context.close();
}
}
在resources目录下创建META-INF/spring.factories,内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.edu.core.bean.RunnableConfiguration
运行App.java,输出如下:
com.edu.core.bean.RunnableConfiguration$$Lambda$134/549293029@398dada8
成功注入进去了Runnable的Bean。
如何配置多个?
在core-bean中新建User.java和Role.java以及一个配置类UserConfiguration.java内容分别如下:
package com.edu.core.bean;
public class User {
}
package com.edu.core.bean;
public class Role {
}
package com.edu.core.bean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfiguration {
@Bean
public User createUser(){
return new User();
}
}
然后在springcourse的spring.factories中内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.edu.core.bean.RunnableConfiguration,com.edu.core.bean.UserConfiguration,com.edu.core.bean.Role
同样成功注入进去了。这样就实现了添加多个配置类。
只有在application.properties 文件中,spring.boot.enableautoconfiguration为true(默认为true)时,才启用自动配置。默认不用配置时为true
其内部实现的关键点有:
1. ImportSelector 该接口的方法的额返回值都会被纳入到spring容器管理中
2. SpringFactoriesLoader 该类可以从classpath中搜索所有META-INF/spring.factories配置文件,并读取配置
如果想要排除某些类,应该如何做?
方式一:通过class来排除
使用exclude
使用@EnableAutoConfiguration(exclude = UserConfiguration.class) 就将User的bean排除在外了。
方式二:通过类名来排除
使用@EnableAutoConfiguration(excludeName = "com.edu.core.bean.Role")
Springboot 事件监听
事件流程:
1. 自定义事件,一般是继承ApplicationEvent抽象类
2. 定义时间监听器,一般是实现ApplicationListener接口
3. 启动的时候,需要把监听器加入到spring容器中
4. 发布事件,使用ApplicationContext的publishEvent发布事件
5. 配置监听器
a. SpringApplication.addListeners 添加监听器
b. 把监听器纳入到spring容器中管理 @Component
c. 可以使用配置项,在application.properties中配置context.listener.classes=com.edu.spring.MyApplicationListener 详细内容参数:DelegatingApplicationListener
定义事件,新建MyApplicationEvent.java
package com.edu.spring;
import org.springframework.context.ApplicationEvent;
/**
* 定义事件
*/
public class MyApplicationEvent extends ApplicationEvent {
public MyApplicationEvent(Object source) {
super(source);
}
}
定义监听器,MyApplicationListener.java
package com.edu.spring;
import org.springframework.context.ApplicationListener;
public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
@Override
public void onApplicationEvent(MyApplicationEvent event) {
System.out.println("接收到事件:" + event.getClass());
}
}
新建App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@EnableAutoConfiguration(excludeName = "com.edu.core.bean.Role")
@ComponentScan
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
application.addListeners(new MyApplicationListener());
ConfigurableApplicationContext context = application.run(args);
//发布事件
context.publishEvent(new MyApplicationEvent(new Object()));
context.close();
}
}
运行结果:
接收到事件:class com.edu.spring.MyApplicationEvent
说明监听成功。
除了使用application.addListeners(new MyApplicationListener());这种方式添加监听器,还有什么方式?
可以使用@Component注释给MyApplicationListener.java。这样也可以。
另一种方式配置监听器:新建MyEventHandler.java
package com.edu.spring;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventHandle {
/**
* 参数一定要是ApplicationEvent,或者其子类
* @param event
*/
@EventListener
public void event(MyApplicationEvent event){
System.out.println("接受到事件" + event.getClass());
}
}
使用注解@EventListener也可以配置监听器,且该类需要纳入到spring容器重管理(详细内容参照EventListenerFactory和EventListenerMethodProcessor),不用其他的配置了。如果参数设置成public void event(Object event) 那么所有的事件都能够接收到。
spring或者Springboot内部有哪些已经定义好的事件?
修改MyEventHandle.java
package com.edu.spring;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventHandle {
/**
* 参数一定要是ApplicationEvent,或者其子类
* @param event
*/
@EventListener
public void event(MyApplicationEvent event){
System.out.println("接受到事件" + event.getClass());
}
@EventListener
public void event2(ContextStoppedEvent event){
System.out.println("应用停止事件:" + event);
}
}
在App.java中修改:
context.publishEvent(new MyApplicationEvent(new Object()));
输出结果:
接受到事件class com.edu.spring.MyApplicationEvent
应用停止事件:org.springframework.context.event.ContextStoppedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@314c508a, started on Thu Apr 25 17:07:00 CST 2019]
监听器起作用了。
spring boot扩展分析
新建MyApplicationContextInitializer.java
package com.edu.spring;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("bean count: " + applicationContext.getBeanDefinitionCount());
}
}
新建MyApplicationContextInitializer2.java
package com.edu.spring;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class MyApplicationContextInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("app name: " + applicationContext.getDisplayName());
}
}
新建App.java
package com.edu.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
application.addInitializers(new MyApplicationContextInitializer());
ConfigurableApplicationContext context = application.run(args);
context.stop();
context.close();
}
}
在application.properties中内容如下:
context.initializer.classes=com.edu.spring.MyApplicationContextInitializer, com.edu.spring.MyApplicationContextInitializer2
运行App.java
输出结果如下:
bean count: 5
app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@1a052a00
ApplicationContextInitializer 接口是在spring容器执行refreshed之前的一个回调
使用步骤:
1. 写一个类,实现ApplicationContextInitializer接口
2. 注册ApplicationContextInitializer注册方法:
1. SpringApplication.addInitializers()
2. 通过application.properties 配置context.initializer.classes=com.edu.spring.MyApplicationContextInitializer来进行,可以指定多个,多个用逗号隔开
3. 通过spring.factories机制,(注册listener监听器也可以使用这种方式)
下面详细说明如何通过spring.factories实现。
新建项目initializer。
其中pom.xml内容如下:
<?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.edu.spring</groupId>
<artifactId>initializer</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
新建EchoApplicationContextInitializer.java
package com.edu.spring.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class EchoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("===EchoApplicationContextInitializer====");
}
}
在resources下新建META-INF/spring.factories,内容如下:
org.springframework.context.ApplicationContextInitializer=com.edu.spring.initializer.EchoApplicationContextInitializer
然后在springcourse项目的pom中导入Initializer项目
<dependency>
<groupId>com.edu.spring</groupId>
<artifactId>initializer</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
运行App.java,执行结果如下:
bean count: 5
app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@ca263c2
===EchoApplicationContextInitializer====
说明成功注入。
CommandLineRunner接口回调
在springboot项目中,新建ServerSuccessReport.java
package com.edu.spring;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class ServerSuccessReport implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("=====应用已经成功启动=====");
}
}
运行结果如下:
bean count: 5
app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@27c6e487
===EchoApplicationContextInitializer====
2019-04-26 15:33:13.441 INFO 1396 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 1396 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse)
2019-04-26 15:33:13.443 INFO 1396 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default
2019-04-26 15:33:14.150 INFO 1396 --- [ main] com.edu.spring.App : Started App in 1.013 seconds (JVM running for 1.577)
=====应用已经成功启动=====
从输出结果中可以看到,在Started App启动之后,输出CommandLineRunner接口方法。
CommandLineRunner ApplicationRunner接口是在容器启动成功后的最后一步的回调。类似开机自启动
使用步骤:
1. 写一个类,实现CommandLineRunner接口
2. 把该类纳入到Spring容器中
3. 可以通过@Order注解或者Ordered接口来控制执行顺序
如果有多个类实现了CommandLineRunner接口,如何保证执行顺序?
新建ServerStartedReport.java
package com.edu.spring;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class ServerStartedReport implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("=========应用启动后的时间是:" + LocalDateTime.now().toString());
}
}
运行App.java,结果如下:
bean count: 5
app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@7ff2a664
===EchoApplicationContextInitializer====
2019-04-26 15:44:03.081 INFO 2656 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 2656 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse)
2019-04-26 15:44:03.084 INFO 2656 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default
2019-04-26 15:44:03.775 INFO 2656 --- [ main] com.edu.spring.App : Started App in 1.0 seconds (JVM running for 1.426)
=========应用启动后的时间是:2019-04-26T15:44:03.784
=====应用已经成功启动=====
如果我们想要ServerSuccessReport类先执行,可以使用@Order注释。
@Order(3)
@Component
public class ServerStartedReport implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("=========应用启动后的时间是:" + LocalDateTime.now().toString());
}
}
@Order(2)
@Component
public class ServerSuccessReport implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("=====应用已经成功启动=====");
}
}
只要Order括号内的数字越小,则越先执行。
新建StartedApplicationRunner.java
package com.edu.spring;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class StartedApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("应用已经启动,参数为:" + Arrays.deepHashCode(args.getSourceArgs()));
}
}
修改ServerSuccessReport.java的输出
@Override
public void run(String... args) throws Exception {
System.out.println("=====应用已经成功启动=====" + Arrays.asList(args));
}
App.java添加参数:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
//application.addInitializers(new MyApplicationContextInitializer());
ConfigurableApplicationContext context = application.run("aa", "bb");
context.stop();
context.close();
}
}
运行App.java,结果如下:
bean count: 5
app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@78a2da20
===EchoApplicationContextInitializer====
2019-04-26 16:04:07.136 INFO 1684 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 1684 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse)
2019-04-26 16:04:07.138 INFO 1684 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default
2019-04-26 16:04:07.832 INFO 1684 --- [ main] com.edu.spring.App : Started App in 0.994 seconds (JVM running for 1.458)
=====应用已经成功启动=====[aa, bb]
=========应用启动后的时间是:2019-04-26T16:04:07.844
应用已经启动,参数为:[aa, bb]
CommandLineRunner ApplicationRunner区别:
区别在于方法的参数不一样,CommandLineRunner的参数为最原始参数,没有做任何处理,ApplicationRunner的参数是ApplicationArguments,是对原始参数做了进一步的封装。
进一步说明ApplicationArguments的作用。
修改App.java
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
//application.addInitializers(new MyApplicationContextInitializer());
ConfigurableApplicationContext context = application.run(args);
ApplicationArguments arguments = context.getBean(ApplicationArguments.class);
System.out.println(arguments.getSourceArgs().length);
System.out.println(arguments.getOptionNames());
System.out.println(arguments.getOptionValues("myname"));
context.stop();
context.close();
}
}
修改Intellij 的运行参数,Program arguments 为 --myname admin,运行输出结果如下:
bean count: 5
app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@6a79c292
===EchoApplicationContextInitializer====
2019-04-26 16:19:12.343 INFO 8216 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 8216 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse)
2019-04-26 16:19:12.346 INFO 8216 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default
2019-04-26 16:19:13.001 INFO 8216 --- [ main] com.edu.spring.App : Started App in 0.959 seconds (JVM running for 1.401)
=====应用已经成功启动=====[--myname, admin]
=========应用启动后的时间是:2019-04-26T16:19:13.010
应用已经启动,参数为:[--myname, admin]
2
[myname]
[]
修改Intellij 的运行参数,Program arguments 为 --myname=admin,运行输出结果如下:
bean count: 5
app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@78a2da20
===EchoApplicationContextInitializer====
2019-04-26 16:20:08.312 INFO 11652 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 11652 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse)
2019-04-26 16:20:08.315 INFO 11652 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default
2019-04-26 16:20:08.956 INFO 11652 --- [ main] com.edu.spring.App : Started App in 0.939 seconds (JVM running for 1.469)
=====应用已经成功启动=====[--myname=admin]
=========应用启动后的时间是:2019-04-26T16:20:08.961
应用已经启动,参数为:[--myname=admin]
1
[myname]
[admin]
ApplicationArguments是对参数(main方法),做了进一步处理
可以解析 --name=value的,我们就可以通过name来获取value。如果我们用原始的方法进行获取参数时,还得需要对参数进行分割。
Springboot补充讲解
我们知道@SpringBootApplication注解有三个注解所组成的,分别是:@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
在com.edu.spring.springboot包下面新建App.java
package com.edu.spring.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
ConfigurableApplicationContext context = application.run(args);
System.out.println(context.getBean(Runnable.class));
context.close();
}
}
在com.edu.spring.bean包下新建RunnableConfiguration.java
package com.edu.spring.bean;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
@SpringBootConfiguration
public class RunnableConfiguration {
@Bean
public Runnable createRunnable(){
return ()->{};
}
}
运行App.java输出结果如下:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.Runnable' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:343)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1123)
at com.edu.spring.springboot.App.main(App.java:13)
说明没有成功注入。原因是@SpringBootApplication扫描的是当前包和子包下面的所有类。但是同一级的包是无法扫描到的。可以通过basePackage来指定。修改App.java如下:
package com.edu.spring.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication(scanBasePackages = "com.edu.spring")
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
ConfigurableApplicationContext context = application.run(args);
System.out.println(context.getBean(Runnable.class));
context.close();
}
}
这样就成功注入了。
排除某些类,方法如下:
修改pom.xml,添加gson依赖。
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
因为gson依赖,已经在springboot父类中定义,所以不需要指定version。
修改RunnableConfiguration.java
@Bean
public Gson createGson(){
return new Gson();
}
修改App.java
System.out.println(context.getBean(Gson.class));
输出结果正常。说明成功注入进去。
排除Gson的bean。修改App.java
@SpringBootApplication(scanBasePackages = "com.edu.spring", exclude = GsonAutoConfiguration.class)
没有排除掉,目前这有问题。
排除指定类、配置类,exclude:根据class来排除,excludeName:根据class name来排除。
自定义Banner方法
我们每次输出的时候都会输出spring的Banner:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
如何控制不输出呢?
修改App.java如下:
SpringApplication application = new SpringApplication(App.class);
application.setBannerMode(Banner.Mode.OFF);
这样就不输出Spring的Banner了。
还可以自定义Banner,在resources目录下新建banner.txt
然后就可以执行成功了。注意不要把Banner.Mode.OFF开启。
第二种方式自定义Banner的方法是,在application.properties文件中写文件路径。支持图片的Banner,图片的格式支持jpg,png,gif
spring.banner.location=banner2.txt spring.banner.image.location=banner2.jpg
读取application.properties时如果没有key则获取默认的value,方法如下:
方法一:在@value注释上使用默认的value
@Value(("${server.host:localhost}"))
private String serverHosts;
方法二:使用getProperty方法:
System.out.println(context.getEnvironment().getProperty("server.host2","localhost2"));
spring boot 运行流程分析
spingboot的执行入口有两个分别是:
// 实例化SpringApplication对象,然后调用run方法
SpringApplication application = new SpringApplication(App.class);
ConfigurableApplicationContext context = application.run(args);
// 直接调用静态run方法(内部转换成第一种调用方式)
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
运行流程
1. 判断是否是web环境
2. 加载所有classpath下面的META-INF/spring.factories, ApplicationContextInitializerr
3. 加载所有classpath下面的META-INF/spring.factories, ApplicationListener
4. 推断main方法所在的类
5. 开始执行run方法
6. 设置java.awt.headless系统变量
7. 加载所有classpath下面的META-INF/spring.factories SpringApplicationRunListener
8. 执行所有SpringApplicationRunListener的started方法
9. 实例化ApplicationArguments对象
10. 创建Environment
11. 配置Environment,主要是把run方法的参数配置到Environment
12. 执行所有SpringApplicationRunListener的environment.prepared方法
13. 如果不是web环境,但是是web的Environment,则把web的Environment转换成标准的Environment
14. 输出Banner
15. 初始化applicationContext,如果是web环境,则实例化AnnotationConfigEmbeddedWebApplicationContext对象,否则实例化AnnotationConfigApplicationContext对象
16. 如果beanNameGenerator不为空,就把beanNameGenerator对象注入Context里面去
17. 回调所有的ApplicationContextInitializer方法
18. 执行所有SpringApplicationRunListener的contextPrepared方法
19. 依次往Spring容器中注入:ApplicationArguments, Banner
20. 加载所有的源到context里面去。
21. 执行所有SpringApplicationRunListener的contextLoaded方法
22. 执行context的refresh方法,并且调用context的registerShutdownHook方法
23. 回调,获取容器中所有的ApplicationRunner, CommandLineRunner接口,然后排序,依次调用
24. 执行所有SpringApplicationRunListener的finished的finished方法