Spring 注解学习
demo 地址: https://github.com/abinbao/spring-annotations
2019-01-01(打卡)
今天 spring 注解完成到 P10, 该完成 P11
总结:
Bean 的注入方式:
1)@Import
可以快速的将bean注入到容器中,不需要 @Controller 等注解或者包扫描, bean id 默认是包全名
@Configuration
@Import({ Color.class })
public class MainConfigure {
}
2)@ImportSelector
可以返回一组需要导入的组件数组
package com.beng.condition;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
// 自定义逻辑返回需要导入的组件
public class MyImportselector implements ImportSelector {
// AnnotationMetadata: 当前标注 @Import 注解类的所有注解信息
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 方法不要返回null值
return new String[] { "com.beng.model.Color", "com.beng.model.Red", "com.beng.model.Blue" };
}
}
@Configuration
@Import({ Color.class,MyImportselector.class })
public class MainConfigure {
}
3) @ImprtBeanDefinitionRegistrar
package com.beng.condition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import com.beng.model.RainBow;
public class MyImprtBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
// AnnotationMetadata 当前类的注解信息
// BeanDefinitionRegistry 注册类
// BeanDefinitionRegistry.registerBeanDefinition 手动注册
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean flag = registry.containsBeanDefinition("com.beng.model.Red");
if (flag) {
// 指定 bean 的定义信息
RootBeanDefinition rd = new RootBeanDefinition(RainBow.class);
registry.registerBeanDefinition("rainBow", rd);
}
}
}
@Configuration
@Import({ Color.class,MyImportselector.class,MyImprtBeanDefinitionRegistrar.class })
public class MainConfigure {
}
2019-01-02(打卡)
今天 spring 注解完成到 P15, 该完成 P16
总结:
补充上边 Bean 的注入方式:
@FactoryBean 使用工厂bean
默认获取工厂 bean getObject()创建的对象
要获取工厂本身 需在 bean 前边加前缀 &
@Configuration
public class MainConfigure {
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
}
import org.springframework.beans.factory.FactoryBean;
public class ColorFactoryBean implements FactoryBean<Color> {
/**
* 返回一个 Color 对象,注册到容器中
*/
public Color getObject() throws Exception {
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
/**
* 返回 true bean 单实例 false 多实例
*/
public boolean isSingleton() {
return false;
}
}
Bean 的生命周期:
/*
* bean 的生命周期:
* bean 的创建 - 初始化 - 销毁
* 容器管理 bean 的生命周期
* 自定义初始化和销毁方法,容器在执行bean生命周期时,调用初始化和销毁方法
*
* 构建对象:
* 单实例:容器启动时创建对象
* 多实例:每次调用的时候创建对象
* 初始化:
* 对象创建完成,并赋值完成,调用初始化方法
* 销毁:
* 单实例,容器关闭的时候进行销毁
* 多实例,容器不会管理这个bean,容器关闭不会调用销毁方法
*
* 1) 指定初始化和销毁方法
* initMethod destroyMethod
*
* 2) 通过 Bean 实现 InitailizingBean(定义初始化逻辑)
* DesposableBean(定义销毁逻辑)
*
* 3) 使用注解 @PostConstruct Bean 创建完成,并且赋值完成
* @PreDestroy Bean 销毁之前调用
*
* 4) BeanPostProcessor【interface】: bean 的后置处理器
* 在 bean 的初始化前后做一些处理工作
* postProcessBeforeInitialization : 在 bean 的初始化之前
* postProcessAfterInitailization : 在初始化之后
*/
@ComponentScan("com.beng.model")
@Configuration
public class MainConfigLifeCycle {
// @Scope("prototype")
@Bean(initMethod = "init", destroyMethod = "destroy")
public Car car() {
return new Car();
}
}
------------------------------------------------------------------------------------------------------
public class Car {
public Car() {
System.out.println("Car is 初始化...");
}
public void init() {
System.out.println("Car is initing...");
}
public void destroy() {
System.out.println("Car is destroyed...");
}
}
-------------------------------------------------------------------------------------------------------
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("Cat constructing...");
}
public void destroy() throws Exception {
System.out.println("Cat destroying...");
}
public void afterPropertiesSet() throws Exception {
System.out.println("Cat initing...");
}
}
---------------------------------------------------------------------------------------------------------
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
// 这种方式是在 java 1.5 是不行的
@Component
public class Dog {
public Dog() {
System.out.println("Dog constructing...");
}
@PreDestroy
public void destroy() {
System.out.println("Dog destroying...");
}
@PostConstruct
public void init() {
System.out.println("Dog initing...");
}
}
----------------------------------------------------------------------------------------------------------
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* 将后置处理器加到容器中
*
* @author apple
*/
@Component
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..." + beanName + " ===> " + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization" + beanName + " ===> " + bean);
return bean;
}
}
2019-01-03(打卡)
今天 spring 注解完成到 P19, 该完成 P20
总结:
今天主要学习了 BeanPostProcessor 的原理和其在Spring中的使用,这一部分需要自己再去深入学习!!!!!!
然后是 @Value 注解的学习,其主要有三种获取值得方式:
- 基本数值
- spel #{}
- ${} 取配置文件
示例代码:
Configure类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import com.beng.model.Person;
// 使用 @Propertysource 读取配置文件
@PropertySource(value = "classpath:/person.properties")
@Configuration
public class MainConfigPropertyValue {
@Bean
public Person person() {
return new Person();
}
}
model类:
public class Person {
// 使用 @Value 赋值
// 1. 基本数值
// 2. spel #{}
// 3. ${} 取配置文件
@Value(value = "张三")
private String username;
@Value("#{20-2}")
private int age;
@Value("${nickname}")
private String nickname;
···
配置文件:person.properties
nickname=zhangsan
单元测试类:
public class IocTest_PropertyValue {
@Test
public void test() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MainConfigPropertyValue.class);
System.out.println("容器创建完成...");
String[] beans = context.getBeanDefinitionNames();
ConfigurableEnvironment env = context.getEnvironment(); // 这里获取运行环境
String property = env.getProperty("nickname"); // 获取运行环境中的变量
System.out.println(property);
Person person = (Person) context.getBean("person");
System.out.println(person.toString());
for (String name : beans) {
System.out.println(name);
}
context.close();
}
}
2019-01-05(打卡)
今天 spring 注解完成到 P23, 该完成 P24
感受:
今天主要学习了注解的自动注入,原来在做三方项目的时候就接触到了这个问题,当时不是很理解,学习了这个之后加深了对其的理解,也发现自身的很多的不足之处,其实对于 spring 的了解还知识皮毛,从了解,到使用,到熟悉其中很多的使用方式,以及如何去查询api,看官方文档,读源码等 ,都需要自己去深入学习。
总结:
日常应用的体验:
- @Autowired 注解,这个 spring 环境中,十分的实用。在一些业务场景中,可能会用到工厂方法模式,抽象出一个接口,不同的场景实现不同的功能,这个时候就可以使用@Autowired注解来给不同的业务场景注入不同的bean
- ApplicationContextAware 这个接口的使用,这个十分常用。通过实现这个接口,可以获得spring 底层的上下文组将,通过其可以获取容器中的一些对象,比如更改了某个bean的名称,就可以通过实现这个接口来提供一个根据bean的名称获取bean的方法 getBean(String beanname)b
今天的主要内容就是:
1)@Autowired spring的注解
2)@Qualifier 指定bean的id
3)@Primary 优先注入
4)@Resource java规范的注解
5)@Inject
- 自定义组件实现 xxxAware 获得 spring 底层的组件
具体代码如下:
配置类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.beng.dao.BookDao;
import com.beng.model.Car;
import com.beng.model.Color;
/*
* 自动装配:
* Spring 利用依赖注入,完成对IOC容器中各个组件的关系赋值
* 1) @Autowired
* a. 默认有限按照类型从IOC容器中找对应的组件: context.getBean(BookDao.class)
* b. 如果找到多个相同类型的组件,再将属性的名称作为id去容器中查找
* c. @Qualifier("bookDao2"),指定需要装配的id,不适用属性名
* d. 自动装配默认一定要将属性赋值好,没有就报错
* @Autowired(required = false) 能装配就装配,不能就不装配
* e. @Primary, 让Spring进行自动装配的时候,默认首选的bean
* 也可以继续使用Qualifier指定需要装配的名字
* 2) @Resource @Inject 【Java规范的注解】
* @Resource :
* 可以和@Autowired一样实现自动装配功能,默认按照组件名称进行装配
* @Resource(name="bookDao2") 没有支持 @Primary 和 @Autowired(require=false/true) 等功能
* @Inject :
* 需要导入 javax.inject 和 @Autowired 一样都能进行自动装配,@Autowired 内部有参数
* 3) @Autowired : 构造器,参数,方法,属性,以上都是在属性位置标注
* a. 方法位置
* b. 构造器,如果组件只有一个有参构造器,参数构造器的 @Autowired 可以省略
* c. 参数位置
*
* 4) 自定义组件想要使用 spring 底层的一些组件(ApplicationContext、BeanFactory、xxx)
* 自定义组件 实现 xxxAware 接口
* 把Spring底层的一些组件注入到自定义的bean中
* @author apple
*/
@Configuration
@ComponentScan({ "com.beng.service", "com.beng.dao", "com.beng.controller", "com.beng.model" })
public class MainConfigAutowired {
@Primary
@Bean
public BookDao bookDao() {
return new BookDao();
}
/*
* @Bean 标注的方法创建对象的时候,方法参数的值从容器中获取
*/
@Bean
public Color color(Car car) {
Color color = new Color();
color.setCar(car);
return color;
}
}
model类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
// 默认加载IOC容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Component
public class Boss {
private Car car;
// @Autowired
public Boss(@Autowired Car car) {
this.car = car;
System.out.println("Boss 有参数构造器...");
}
public Car getCar() {
return car;
}
// @Autowired
// 标注在方法上,Spring 容器创建当前对象,就会调用该方法,进行赋值
// 方法使用的参数,自定义类型的值从IOC容器中获取
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Boss [car=" + car + "]";
}
}
---------------------------------------------------------------------------------------------------
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的IOC :" + applicationContext);
this.context = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println("当前bean的名字:" + name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String str = resolver.resolveStringValue("你好,${os.name} ,我是 #{20*18}");
System.out.println("解析的字符串:" + str);
}
}
---------------------------------------------------------------------------------------------------
测试类:
public class IocTest_Autowired {
@Test
public void test() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigAutowired.class);
BookService service = context.getBean(BookService.class);
System.out.println(service);
BookDao dao = context.getBean(BookDao.class);
System.out.println(dao);
// String[] beans = context.getBeanDefinitionNames();
// for (String name : beans) {
// System.out.println(name);
// }
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
Car car = context.getBean(Car.class);
System.out.println(car);
Color color = context.getBean(Color.class);
System.out.println(color);
}
}
2019-01-06(打卡)
今天 spring 注解完成到 P26, 该完成 P27
总结:
今天主要学习的@Profile注解的学习,同时还复习了一下多天对于值得获取的使用,例如@Value。平时工作中对于Profile的使用基本是在pom文件中的使用或者是在properties文件中的使用,今天所学到的是在java代码中的使用。
例子主要以数据源进行举例,使用的c3p0数据源,maven中心仓库地址:https://mvnrepository.com
配置类:
import java.beans.PropertyVetoException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import com.beng.model.Red;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/*
* Profile:
* Spring 为我们提供的介意根据当前环境,动态激活和切换组件的功能
* @Profile
* 指定组件在那个环境中生效
*
* 1) 加了环境标识的bean 只有环境被激活时,才会注册到容器中,默认是 default 环境
* 2) 写在配置类上,只有是指定环境的时候,所有的配置才能生效
* 3) 没有环境标志的bean,在任何环境下都会生效
*/
@PropertySource("classpath:/dbconfig.properties")
@Configuration
@Profile("test")
public class MainConfigProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver resolver;
private String driverClass;
// @Profile("test")
@Bean
public Red red() {
return new Red();
}
@Profile("test")
@Bean("testDatasource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(pwd);
comboPooledDataSource.setJdbcUrl("jdbc://mysql://localhost:3306/mydemo");
driverClass = resolver.resolveStringValue("${db.driverClass}");
comboPooledDataSource.setDriverClass(driverClass);
return comboPooledDataSource;
}
@Profile("prod")
@Bean("proDatasource")
public DataSource dataSourcePro() throws PropertyVetoException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("123456");
comboPooledDataSource.setJdbcUrl("jdbc://mysql://localhost:3306/mydemo");
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
return comboPooledDataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
}
测试类:
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.beng.configuration.MainConfigProfile;
public class IocTest_Profile {
/*
* 1) 使用命令行动态参数 -Dspring.profiles.active=test 2) 代码方式激活环境
*/
@Test
public void test() {
// AnnotationConfigApplicationContext context = new
// AnnotationConfigApplicationContext(MainConfigProfile.class);
// 1. 创建一个 ApplicationContext
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 2. 设置需要激活的环境
context.getEnvironment().setActiveProfiles("prod");
// 3. 注册主配置类
context.register(MainConfigProfile.class);
// 4. 启动刷新容器
context.refresh();
String[] beans = context.getBeanDefinitionNames();
for (String name : beans) {
System.out.println(name);
}
String[] dataSources = context.getBeanNamesForType(DataSource.class);
for (String name : dataSources) {
System.out.println(name);
}
}
}
pom.xml
<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.33</version>
</dependency>