深浅 第三章全注解下的IOC

最为简单的配置bean


public class User {
    private Long id;
    private String userName;
    private String note;
    }

@Configuration //加上配置注解
public class AppConfig {
    @Bean(name = "user")  //指定bean
    public User initUser() {
        User user = new User();
        user.setId(1L);
        user.setUserName("用户名");
        user.setNote("节点");
        return user;
    }
}
    private static Logger log = Logger.getLogger(ToTest.class);
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        User user = ctx.getBean(User.class);
        log.info(user.getId());
        log.info(user.getUserName());
    }

getBean 每次拿到的都是同一个对象,使用时要注意,直接改对象内容,其他地方也会改变

Spring Ioc 容器是一个管理的bean的容器
所有的IoC容器都需要实现BeanFactory,它是一个顶级容器接口
BeanFactory有根据类型,和根据名字获取bean的,判断是否包含,是否单例,是否原型
默认情况下都是单例的, isPrototype 是否原型,与是否单例相反。是否原型为true,说明不是单例,每次都会创建一个新的bean
BeanFactory不够强大,设计了更高级的接口ApplicationContext 是beanFactory的子接口
现实中大部分容器都是ApplicationContext的实现类

基于注解的IOC容器 AnnotationConfigApplicationContext (springBoot常用)

Java简单对象 Plain Ordinary Java Object POJO

plain 
英 [pleɪn]  美 [plen]
adj. 平的;简单的;朴素的;清晰的
n. 平原;无格式;朴实无华的东西
adv. 清楚地;平易地
ordinary 
英 ['ɔːdɪn(ə)rɪ; -d(ə)n-]  美 ['ɔrdnɛri]
adj. 普通的;平凡的;平常的
n. 普通;平常的人(或事)
复数 ordinaries

装配你的 Bean

一个一个的配置bean会很麻烦,所以有了装配bean
就是扫描装配 注解:@Component @ComponentScan
@Componet 标明那个类被扫描进入Spring Ioc容器
@ComponetScan 标明采用何种的策略,扫描装配

component 
英 [kəm'pəʊnənt]  美 [kəm'ponənt]
adj. 组成的,构成的
n. 成分;组件;[电子] 元件
@Component("student")
public class Student {
    @Value("1")
    private Long id;
    @Value("用户名")
    private String userName;
    @Value("note")
    private String note;
    }
@Configuration
@ComponentScan
public class AppConfig {
}
使用依然不变:
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
getBean
依然是单例的:可以BeanUtils.copyProperties(bean,bean2);  不改动元对象

ComponentScan

basePackages 定义扫描的包
includeFilters excludeFilters 定义满足条件,和不满足条件 才去扫描,
条件用 @Filter去定义,里有一个type类型,classes定义注解类,pater定义正则式类

@ComponentScan("com.hua.huademo1.*")
@ComponentScan(basePackages = {"com.hua.huademo1.*"})
@ComponentScan(basePackageClasses = {Student.class})
@ComponentScan(basePackages = {"com.hua.huademo1.*"},excludeFilters = {@ComponentScan.Filter(classes = {User.class})})

自定义第三方bean

		
			org.apache.commons
			commons-dbcp2
		
		
			mysql
			mysql-connector-java
		
在配置类加入
@Configuration
@ComponentScan(basePackages = "com.springboot.chapter3.*")

	@Bean(name = "dataSource", destroyMethod = "close")
	@Profile("dev")
	public DataSource getDevDataSource() {
		Properties props = new Properties();
		props.setProperty("driver", "com.mysql.jdbc.Driver");
		props.setProperty("url", "jdbc:mysql://localhost:3306/dev_spring_boot");
		props.setProperty("username", "root");
		props.setProperty("password", "123456");
		DataSource dataSource = null;
		try {
			dataSource = BasicDataSourceFactory.createDataSource(props);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return dataSource;
	}

依赖注入最简单的实现

@Configuration
@ComponentScan(basePackages = "com.hua.huademo2.*")
public class AppConfig {
}
@Component  //定义为springBean
public class Dog implements Animal {
	@Override
	public void use() {
		System.out.println("狗【" + Dog.class.getSimpleName()+"】是看门用的。");
	}
}

@Component
public class BussinessPerson implements Person {

    @Autowired  //注入接口,实际注入的是实现类
    private Animal animal = null;

    @Override
    public void service() {
        this.animal.use();
    }

    @Override
    public void setAnimal(Animal animal) {
        this.animal = animal;
    }
}

		ApplicationContext ctx= new AnnotationConfigApplicationContext(AppConfig.class);
        Person bean = ctx.getBean(BussinessPerson.class);
        bean.service();

当一个接口有连个实现类时候,解决办法
@Autowired
private Animal cat = null; //注入的是 cat的实现类
使用注解,注解的权限高与上面的变量

@Component
@Primary  或者:    @Qualifier("dog")  //指定注入那个bean
public class Dog implements Animal {

在方法上注入

    private Animal cat = null;
    @Override
    @Autowired
    @Qualifier("dog")  //指定注入那个bean
    public void setAnimal(Animal animal) {
        this.cat = animal;
    }

   @Autowired @Qualifier("dog") //还可以这样,直接写两个注解
    public void setAnimal(Animal animal) {
        this.cat = animal;
    }

    或者: 但是不能把 Autowired写在方法上(没用)
    @Override
    @Autowired
    public void setAnimal(@Qualifier("dog")  Animal animal) {
        this.cat = animal;
    }
但可以写在构造上:
    public BussinessPerson(@Autowired @Qualifier("dog") Animal cat) {
        this.cat = cat;
    }

并不一定非要存在,可能为null
@Autowired(required = false)

bean的流程,bean的生命周期

  1. 定义
  2. 初始化
  3. 生存期
  4. 销毁

往细了说就是:资源定位,Bean定义,发布Bean,实例化,依赖注入

默认发布bean定义之后,就开始创建对象,(调用构造)

懒加载

可以用懒加载 在使用的时候,才调用对象

@Configuration
@ComponentScan(basePackages = "com.hua.huademo2.*",lazyInit = true)
public class AppConfig {
}

测试生命周期

implements Person, BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean 
  1. 初始化——
  2. 依赖注入——
  3. setBeanNmae——
  4. setBeanFactory——
  5. setApplicationContext——
  6. postProcessBeforeInit——
  7. postConstruct(自定义初始化)——
  8. afterPropertiesSet——
  9. postProcessAfterInit——
  10. 生存期
  11. preDestroy标注方法,自定义销毁方法
  12. destroy方法

后置初始化bean

//后置初始化bean对所有的bean生效
@Component
public class BeanPostProcessorExample implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		//6,postProcessBeforeInitialization
		System.out.println("BeanPostProcessor调用" + "postProcessBeforeInitialization方法,参数【"
				+ bean.getClass().getSimpleName() + "】【" + beanName + "】 ");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		//9,postProcessAfterInitialization
		System.out.println("BeanPostProcessor调用" + "postProcessAfterInitialization方法,参数【"
				+ bean.getClass().getSimpleName() + "】【" + beanName + "】 ");
		return bean;
	}
}
@Component
public class BussinessPerson
		implements Person, BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

	private Animal animal = null;


	@Override
	public void service() {
		this.animal.use();
	}

	@Override
	@Autowired
	@Qualifier("dog")
	public void setAnimal(Animal animal) {
		System.out.println("延迟依赖注入");
		this.animal = animal;
	}

	@Override
	public void setBeanName(String beanName) {
		//1初始化
		//2依赖注入
		//这里是3,setBeanName
		System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanNameAware的setBeanName");
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		//4,setBeanFactory
		System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanFactoryAware的setBeanFactory");
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		//5,setApplication
		System.out.println("【" + this.getClass().getSimpleName() + "】调用ApplicationContextAware的setApplicationContext");

	}
	//6,在BeanPostProcessor中 postProcessBeforeInitialization


	@PostConstruct
	public void init() {
		//7,自定义初始化 PostConstruct
		System.out.println("【" + this.getClass().getSimpleName() + "】注解@PostConstruct定义的自定义初始化方法");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		//8,afterPropertiesSet
		System.out.println("【" + this.getClass().getSimpleName() + "】调用InitializingBean的afterPropertiesSet方法");
	}

	//9,在BeanPostProcessor中 postProcessAfterInitialization

	//10,生存期
	@PreDestroy
	public void destroy1() {
		//11,自定义销毁方法 PreDestroy
		System.out.println("【" + this.getClass().getSimpleName() + "】注解@PreDestroy定义的自定义销毁方法");
	}

	@Override
	public void destroy() throws Exception {
		//12,销毁的方法
		System.out.println("【" + this.getClass().getSimpleName() + "】 DisposableBean方法");

	}
}
1,初始化
延迟依赖注入  2,依赖注入
【BussinessPerson 3】调用BeanNameAware的setBeanName  
【BussinessPerson 4】调用BeanFactoryAware的setBeanFactory
【BussinessPerson 5】调用ApplicationContextAware的setApplicationContext
【BeanPostProcessor 6】调用postProcessBeforeInitialization方法,参数【BussinessPerson】【bussinessPerson】 
【BussinessPerson 7】注解@PostConstruct定义的自定义初始化方法
【BussinessPerson 8】调用InitializingBean的afterPropertiesSet方法
【BeanPostProcessor 9】调用postProcessAfterInitialization方法,参数【BussinessPerson】【bussinessPerson】 
狗【Dog】是看门用的。 10,生存期
【BussinessPerson 11】注解@PreDestroy定义的自定义销毁方法
【BussinessPerson 12】 DisposableBean方法

最常用的:

    @PostConstruct
    @PreDestroy

使用bean指定方法:

	@Bean(name = "dataSource", destroyMethod = "close")  //,initMethod = 
	@Profile("test")
	public DataSource getTestDataSource() {

@ComponentScan 注意点

@Configuration
@ComponentScan(basePackages = "com.example.*", //是要扫描的上层目录 User在com.example.demobenaware下
        excludeFilters = {@ComponentScan.Filter(classes={UserService.class})})
public class AppConfig {
}

如果要扫User可以:@ComponentScan(basePackages = “com.example.demobenaware”)

使用属性文件

1,PropertySource读取配置文件

@Data
@Component
@PropertySource("classpath:application.properties")
public class DataBaseProperties {
	@Value("${book}")
	private String url = null;
}

2,自动配置加在依赖属性

//可以不用引入
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-configuration-processorartifactId>
			<optional>trueoptional>
		dependency>
processor 
英 ['prəʊsesə]  美 ['prɑsɛsɚ]
n. [计] 处理器;处理程序;加工者

2.1 必须整个项目启动,springboot

database.driver-name=com.mysql.jdbc.Driver
database.password=123456
database.username=root
database.url=jdbc:mysql://localhost:3306/chapter3
@Component
public class DataBaseProperties {
	@Value("${database.driver-name}") //配置这样database.driverName=com.mysql.jdbc.Driver 也是可以的,相同最好
	private String driverName = null;
	@Value("${database.url}")
	private String url = null;

也可以放在set方法上
	@Value("${database.username}")
	public void setUsername(String username) {
		this.username = username;
	}

2.2 使用 ConfigurationProperties

@Component
@ConfigurationProperties( "database")
public class DataBaseProperties {
	private String driverName = null;  //这里等于driver-name , 	@Value("${database.driver-name}") ,反过来不行
	private String url = null;
	}
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/chapter3
@Data
@Component
@ConfigurationProperties("spring.datasource")
public class DataBaseProperties {
	private String driverClassName;
	private String url;
	private String username;
	private String password;
	public void setUsername(String username) {
		System.out.println(username);
		this.username = username;
	}
	public void setPassword(String password) {
		System.out.println(password);
		this.password = password;
	}
}
  • 也是需要启动项目的: SpringApplication.run(DemoBenAwareApplication.class, args);

2.3 指定其他的加载

默认只加载application.properties
想要加载其他的配置,

@SpringBootApplication (里面也有扫描包)
或者:扫描包下
@ComponentScan(basePackages = "com.hua.huademo2.*") //,lazyInit = true
@PropertySource(value={"classpath:jdbc.properties"}, ignoreResourceNotFound=true)
@Data
@Component
@ConfigurationProperties("spring.datasource")//加在下面这两个文件里,这个开头的
@PropertySource(value = {"classpath:jdbc.properties","classpath:application.properties"})
public class DataBaseProperties {
	private String book;
	public void setBook(String book) {
		System.out.println(book);
		this.book = book;
	}
}
spring.datasource.book=dddd
@SpringBootApplication(scanBasePackages = "com.*")
@PropertySource(value={"classpath:jdbc.properties"}, ignoreResourceNotFound=true)
@ImportResource(value = {"classpath:spring-other.xml"})

3.6 条件装配bean

  • @Conditional注解,
  • 需要Condition 接口配合
	@Bean(name = "dataSource", destroyMethod = "close")
	@Conditional(DatabaseConditional.class)  //满足这个条件,才装配bean
	public DataSource getDataSource(
			@Value("${database.driverName}") String driver,    //@Value 也可以在参数上写
			@Value("${database.url}") String url,
			@Value("${database.username}") String username,
			@Value("${database.password}") String password
			) {
		Properties props = new Properties();
		props.setProperty("driver", driver);
		props.setProperty("url", url);
		props.setProperty("username", username);
		props.setProperty("password", password);
		DataSource dataSource = null;
		try {
			dataSource = BasicDataSourceFactory.createDataSource(props);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return dataSource;
	}



public class DatabaseConditional implements Condition {
	
	/*
	 * @param context 条件上下文
	 * @param matadata 注释类型的元数据
	 * return true 装配bean 否则不装配
	 */
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		Environment env = context.getEnvironment();
		return env.containsProperty("database.driverName") && env.containsProperty("database.url") 
				&& env.containsProperty("database.username") && env.containsProperty("database.password");
	}
}


bean的作用域

  • 最顶级的接口BeanFactory的时候,有 isSingleton和 isPrototype 两个方法

  • 一般容器:有单例 singleton和原型 prototype

  • javaEE容器,存在 page ,请求 request ,会话 session 和 应用 application

  • page是 jsp当前页面的作用域

作用域类型 使用范围 描述
single spring应用 单例
prototype spring应用 原型,每次都会创建一个bean
session web应用 HTTP会话
application web应用 web工程的生命周期
request web应用 单次请求
globalSession web应用 一个全局的http Session中,一个Bean定义对应一个实例,实践中基本不使用

默认作用域是单例的,改变

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //默认是单例的
public class ScopeBean {
}
@Configuration
@ComponentScan
public class AppConfig {
}
        AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(AppConfig.class);
        ScopeBean bean = ctx.getBean(ScopeBean.class);
        ScopeBean bean2 = ctx.getBean(ScopeBean.class);
        System.out.println(bean==bean2);//默认是true

/@Scope(WebApplicationContext.SCOPE_REQUEST)/ 我也不会用

更改日志的级别

logging.level.root=DEBUG
logging.level.org.springframework=DEBUG

3.8 @Profile(“test”) 注解的使用:

  • 开发环境
  • 测试环境
  • 准生产环境
  • 生产环境
@Configuration
@ComponentScan(basePackages = "com.example.demo1.*")
public class AppConfig {
    @Bean(name = "dataSource", destroyMethod = "close")
    @Profile("dev")
    public DataSource getDevDataSource() {
        Properties props = new Properties();
        props.setProperty("driver", "com.mysql.jdbc.Driver");
        props.setProperty("url", "jdbc:mysql://localhost:3306/dev_spring_boot");
        props.setProperty("username", "root");
        props.setProperty("password", "15517");
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }

    @Bean(name = "dataSource", destroyMethod = "close")
    @Profile("test")
    public DataSource getTestDataSource() {
        return dataSource;
    }
}
  • 1.使用注解
#spring.profiles.active=test
spring.profiles.default=dev
  • 2.使用VM 参数
    • 参数配置

java启动项目中配置 未测试

JAVA_OPTS="-Dspring.profiles.active=dev"

或者: idea vm options 配置 -Dspring.profiles.active=dev

idea参数

Active Profiles : test
    1. java -D参数=值 -jar
java -Dspring.profiles.active=dev -jar demo1-0.0.1-SNAPSHOT.jar

引入XML配置bean

public class Squirrel implements Animal {
	@Override
	public void use() {
		System.out.println("松鼠可以采集松果");
	}
}

@Configuration
@ComponentScan
@ImportResource(value = {"classpath:spring-other.xml"})  //springboot 在 resource 下 和 
application.properties同目录
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">
       <bean id="squirrel" class="com.hua.demo3.Squirrel"/>
beans>

SpringEL的使用

@Data
@ToString
@Component
@ConfigurationProperties( "database")
public class DataBaseProperties {
    private String driverName = null;
    private String url = null;

    @Value("${database.driverName}") //为了适应这个,上面请加入:@PropertySource("classpath:application.properties")
    private String driverName2 = null;
    
    @Value("#{T(System).currentTimeMillis()}") //T 代表引入类,System long.*包下。使用的是静态的方法。
    private Long initTime=null;
    
    @Value("#{12345}")
    private Long initTime2=null;  //直接赋值

    @Value("#{9.3E3}") //科学计算赋值
    private Long initTime2=null;
    
    @Value("#{3.12}") //浮点小数赋值
    private float f;

    @Value("#{squirrel.name?.toUpperCase()}")  //取Spring Ioc容器bean的名称,这个bean还能在读取配置文件
    private String use; //?代表判断这个属性是否为空,如果不为空才去执行toUpperCase方法

    @Value("#{1+2}")
    private int run;

    @Value("#{squirrel.pi == 3.14f}")
    private boolean piFlag;

    @Value("#{squirrel.name +'连接的字符串'}")
    private String strApp;

    @Value("#{squirrel.pi > 1000 ? '大于' : '小于'}")
    private String resultDesc;
    
    @Value("#{squirrel.name eq 'com.mysql.jdbc.Driver'}")
    private String strFlag;
}

#{} 启用spring表达式 有运算的功能
T() 引入类
System是 java.lang.*的包,默认加载的包,不用全路径,currentTimeMillis是静态方法


@Data
@Component
public class Squirrel {
    @Value("#{3.12}") //浮点小数赋值
    private float pi;

    @Value("为何要连接???")
    private String name;

}

你可能感兴趣的:(深浅,第三章,spring注解注解)