Spring-注解驱动
组件注册:
1.定义配置类(相当于配置文件)
@Configuration//告诉Spring这是配置类(配置文件)
public class MainConfig {
// 将组件注册到容器中
@Bean()//组件类型就是方法返回值类型,组件id默认就是方法名
public Person person() {
return new Person("zh", 21);
}
}
2.包扫描
/**
* @ComponentScans(value={
* @ComponentScan(value = "com.zh",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class, Repository.class})},
* useDefaultFilters = false)
* })
* 自动包扫描:
* @ComponentScan value:指定要扫描的包
* excludeFilters =Filter[]:配置过滤规则
* includeFilters=Filter[]:配置只包含规则
* useDefaultFilters = false取消默认规则(扫描所有)
*FilterType.ANNOTATION:按照注解
*FilterType.ASSIGNABLE_TYPE:按照给定类型
*FilterType.ASPECTJ:使用ASPECTJ表达式
*FilterType.REGEX:使用正则指定
*FilterType.CUSTOM:使用自定义规则
*/
@ComponentScan(value = "com.zh", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class, Repository.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)},
useDefaultFilters = false)
@Configuration
public class MainConfig {
@Bean()
public Person person() {
return new Person("zh", 21);
}
}
3.组件作用域、懒加载
@Configuration
public class MainConfig2 {
/**
* @Scope:设置组件作用域
* singleton:单实例,容器启动时创建对象
* 以后每次获取都是直接从容器中获取(t同一个对象)
* prototype:多实例,从容器中获取时,创建对象
* 每次获取的时候才会调用方法创建
* request:同一个请求创建一个实例
* session:同一个session创建一个实例
*
* 懒加载:
* 单实例bean:默认在容器启动时创建对象
* 懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
*/
@Scope()
@Lazy
@Bean("person")
public Person person(){
System.out.println("person组件创建成功");
return new Person("zh2",12);
}
}
4.@Conditional
LinuxCondition.class
public class LinuxConditional implements Condition {
/**
* ConditionContext:判断条件能使用的上下文环境
* AnnotatedTypeMetadata:注释信息
*/
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//1.获取刀ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2.获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//3.获取bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//可以判断容器中bean的注册情况,也可以给容器中注册bean
boolean person = registry.containsBeanDefinition("Person");
//4.获取当前环境信息
Environment environment = conditionContext.getEnvironment();
if (environment.getProperty("os.name").contains("linux")) {
return true;
}
return false;
}
}
WindowConditional.class
public class WindowsConditional implements Condition {
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
if (environment.getProperty("os.name").contains("Windows")){
return true;
}
return false;
}
}
1.加到类上时,满足条件,容器中的所有组件才会注册
@Conditional({WindowsConditional.class})
@Configuration
public class MainConfig3 {
@Bean("person")
public Person person01() {
return new Person("zh", 12);
}
}
2.加到方法上时,根据条件判断,该组件是否能注册到容器中
@Configuration
public class MainConfig3 {
@Bean("person")
public Person person01() {
return new Person("zh", 12);
}
@Conditional({WindowsConditional.class})
@Bean("bill")
public Person person02() {
return new Person("bill", 64);
}
@Conditional(LinuxConditional.class)
@Bean("linux")
public Person person03() {
return new Person("linux", 54);
}
}
3.测试
@Test
public void testConditional(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
//动态获取环境变量的值:Windows 10
String property = context.getEnvironment().getProperty("os.name");
System.out.println(property);
String[] beanNamesForType = context.getBeanNamesForType(Person.class);
for (String name : beanNamesForType) {
System.out.println(name);
}
Map<String, Person> beansOfType = context.getBeansOfType(Person.class);
System.out.println(beansOfType);
}
5.@Import(快速注册组件)
/**
*容器中注册组件:
* 1.包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)
* (只适用于自己写的类)
* 2.@Bean(导入第三方报的组件)
* 3.@Import[快速给容器导入一个组件]
* 1.@Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名
* 2.ImportSeletor:返回需要导入的组件的全类名数组
* 3.ImportBeanDefinitionRegistrar:手动注册bean到容器中
*/
1.@Import
@Import({Red.class, Blue.class, MyImportSeletor.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig3 {
@Bean("person")
public Person person01() {
return new Person("zh", 12);
}
/**
* @Conditional:按照一定的条件进行判断,满足条件给容器中注册bean
* 如果系统是windows
*/
@Conditional({WindowsConditional.class})
@Bean("bill")
public Person person02() {
return new Person("bill", 64);
}
@Conditional(LinuxConditional.class)
@Bean("linux")
public Person person03() {
return new Person("linux", 54);
}
}
2.ImportSeletor
//自定义逻辑需要返回的组件
public class MyImportSeletor implements ImportSelector {
// AnnotationMetadata:当前标注@Import注解的类的所有注解信息
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.zh.pojo.Purple","com.zh.pojo.Pink"};
}
}
3.ImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类注解信息
* BeanDefinitionRegistry:BeanDefinition注册类
* 把所有需要添加到容器中的bean:调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
boolean definition = beanDefinitionRegistry.containsBeanDefinition("com.zh.pojo.Red");
boolean definition2 = beanDefinitionRegistry.containsBeanDefinition("com.zh.pojo.Blue");
if (definition && definition2) {
//指定bean定义信息(Bean的类型)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RawBall.class);
//注册一个bean,指定bean名
beanDefinitionRegistry.registerBeanDefinition("rawBall", rootBeanDefinition);
}
}
}
6.使用Spring提供的FactoryBean(工厂bean)
1.默认获取到的是工厂bean调用getObject创建的对象
2.要获取工厂bean本身,给id前面加&
context.getBean("&colorFactoryBean")
public class ColorFactoryBean implements FactoryBean<Color> {
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean....getObject");
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
//是否为单例
public boolean isSingleton() {
return false;
}
}
Bean的生命周期:
bean创建==初始化=销毁的过程
容器管理bean的生命周期:
1.自定义初始化和销毁方法
容器在bean进行到当前生命周期的时候来调用自定义初始化和销毁方法
指定初始化和销毁方法
@Bean(initMthod="init",destroyMethod="destroy")
单实例:
1.创建:在容器启动时创建对象
2.初始化:对象创建完成,并赋值好,调用初始化方法
3.销毁:容器关闭时
多实例:
1.创建:在每次获取时创建对象
2.初始化:对象创建完成,并赋值好,调用初始化方法
3.销毁:容器不会管理这个bean,容器不会调用销毁方法
2.通过让bean实现InitializingBean(定义初始化逻辑)
DisposableBean(定义销毁逻辑)
3.可以使用JSR250:
@PostConstruct:在bean创建完成并且属性赋值完成:来执行初始化方法
@PreDestroy:在容器销毁bean之前通知我们进行清理工作
4.BeanPostProcessor[interface]:bean的后置处理器
在bean初始化前后进行一些处理工作
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
调用构造方法=》初始化之前(后置处理器)》初始化=》初始化之后(后置处理器)==》销毁
/**
* 后置处理器:初始化前后进行处理工作
* 写好后加在容器中
*/
@Component
public class myBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization...."+beanName+"==>"+bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization...."+beanName+"==>"+bean);
return bean;
}
}
组件赋值:
@Value
/**
* 使用@Value赋值
* 1.基本数值
* 2.SPEL表达式:#{}
* 3.引用外部配置文件(properties)中的值(在运行环境变量里面的值)
*/
public class Person {
@Value("ZH")
private String name;
@Value("#{19+2}")
private Integer age;
@Value("${person.nickName}")
private String nick;
}
@Autowired
自动装配:
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值@AutoWired:自动注入
1.默认优先按照类型去容器中查找相应的组件:context.getBean(BookDao.class)
2.如果找到多个相同类型的组件,再将属性名作为组件id在容器中查找
3.@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件id
4.自动装配默认一定要将属性赋值,找不到组件将报错
可以使用@AutoWired(required=false)
5.@Primary:让Spring进行自动装配时,默认使用首选的bean
也可以继续使用@Qualifier指定需要装配的bean的名字
MainAutoWiredConfig:
@Configuration
@ComponentScan({"com.zh.controller","com.zh.service","com.zh.dao","com.zh.pojo"})
public class MainAutoWired {
@Bean("bookDao2")
@Primary
public BookDao bookDao(){
BookDao bookDao = new BookDao("3");
bookDao.setLable("3");
// return new BookDao("3");
return bookDao;
}
pojo.BookDao
//名字默认为小写
@Repository
public class BookDao {
private String lable="1";
public String getLable() {
return lable;
}
public BookDao() {
}
public void setLable(String lable) {
this.lable = lable;
}
public BookDao(String lable) {
this.lable = lable;
}
@Override
public String toString() {
return "BookDao{" +
"lable='" + lable + '\'' +
'}';
}
}
BookService:
@Service
public class BookService {
@Autowired
@Qualifier("bookDao")
// @Resource
//@Inject
private BookDao bookDao;
public void getBookDao(){
System.out.println(bookDao);
}
}
@Resource
/**@Resource:
* 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装
*配的
*不能支持@Primary和@Autowired(required=false)
*/
@Service
public class BookService {
@Qualifier("bookDao")
@Resource
private BookDao bookDao;
public void getBookDao(){
System.out.println(bookDao);
}
}
@Inject
/**@Inject:
* 需要导入javax.inject的包,和Autowired的功能一样,可以使用*@Primary,不支持required=false
*
*/
@Service
public class BookService {
@Autowired
@Qualifier("bookDao")
// @Resource
//@Inject
private BookDao bookDao;
public void getBookDao(){
System.out.println(bookDao);
}
}
@ProperSource
@PropertySource/@ProperSources(引入外部配置文件)
/**
*使用@PropertySouece读取外部配置文件中的k/v保存到运行的环境变量中,加载完外部配置文件后使用${}取出配置文件中的值
*/
@PropertySource(value = "person.properties")
@Configuration
public class MainConfigOfProperty {
@Bean
public Person person(){
return new Person();
}
}
public class Person {
//获取外部文件的值
@Value("${person.nickName}")
private String nick;
}
@Profile
@Profile:Spring提供的可以根据当前环境,动态的激活和切换一系列组件的功能
开发环境、测试环境、生产环境
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
1.加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境
2.写在配置类上,只有是指定的环境的时候,整个将配置类里的所有组件才会生效
3.没有标注环境局标识的bean,任何环境下都是加载的
1.代码方式
MainConfigOfProfile
@Profile("dev")
@Configuration
@PropertySource("db.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Bean
@Profile("test")
public Color color(){
return new Color();
}
@Value("${data.userName}")
private String userName;
private String driver;
@Profile("test")
@Bean("test")
public DataSource dataSourceTest(@Value("${data.pwd}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(userName);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/goods");
dataSource.setDriverClass(driver);
return dataSource;
}
@Profile("dev")
@Bean("dev")
public DataSource dataSourceDev(@Value("${data.pwd}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(userName);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/goods");
dataSource.setDriverClass(driver);
return dataSource;
}
@Profile(("pro"))
@Bean("pro")
public DataSource dataSourcePro(@Value("${data.pwd}") String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(userName);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/goods");
dataSource.setDriverClass(driver);
return dataSource;
}
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
String s = stringValueResolver.resolveStringValue("${data.driver}");
this.driver = s;
}
}
test
/**
*代码方式激活某种环境
* public AnnotationConfigApplicationContext(Class<?>... *annotationClasses){
* this();
* this.register(annotatedClasses);
* this.refresh();
*}
*/
//1.创建一个applicationContext
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext();
//2.设置需要激活的环境
context.getEnvironment().setActiveProfiles("pro","dev")
//3.注册配置类
context.register(MainConfigOfProfile.class);
//4.启动刷新容器
context.refresh();
//5.获取特定环境中的bean
String[] names=context.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
2.命令行参数方式
在虚拟机参数位置加装-Dspring.profiles.active=test
组件注入
@Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
1.【标注在方法位置】@Bean+方法参数:参数从容器中获取,默认不写@Autowired
2.【放在构造器上】如果组件只有一个有参构造器,容器创建对象只能通过有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件可以自动装配
3,放在参数位置
//默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Component
public class Boss {
// @Autowired
private Car car;
public Car getCar() {
return car;
}
//构造器要用组件,都从容器中获取
// @Autowired
public Boss(Car car) {
this.car = car;
System.out.println("调用有参构造器");
}
// @Autowired
//标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值
//方法使用的参数,自定义类型的值从ioc容器中获取
public void setCar(Car car) {
this.car = car;
}
}
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFacrory,xxx)
自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware
把Spring底层一些组件注入到自定义的Bean中
xxxAware:功能使用xxxProcessor:
. ApplicationContextAware====>ApplicationContextAwareProcessor