public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext
("classpath:spring/applicationContext-mybatis.xml");
DruidDataSource dataSource = (DruidDataSource) context.getBean("dataSource");
System.out.println(dataSource.getUrl());
}
<!--扫描位置,扫描com.test包中所有的-->
<context:component-scan base-package="com.test" >
<!--排除掉注解, 为Controller类型的-->
<context:exclude-filter
type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
创建配置类,该类使用@Configuration修饰,
配置类中创建方法,方法被@Bean注解修饰,(如果@Bean修饰的方法有形参,形参默认在容器中获取并赋值)
方法有返回值,返回的数据就是注入到Spring容器中的数据
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfiguration {
private String url="bbb";
private String name="ccc";
private String password = "ccc";
//@Bean注解,修饰方法,将方法返回的对象注入到Spring容器中
//默认方法名为对象名,有点像xml方式配置是的id="方法名"
//也可以通过@Bean("指定")来指定
@Bean
public DruidDataSource configMethod(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(url);
druidDataSource.setName(name);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
public static void main(String[] args) {
ApplicationContext context = new
AnnotationConfigApplicationContext (MyConfiguration.class);
DruidDataSource dataSource =
(DruidDataSource) context.getBean(DruidDataSource.class);
System.out.println(dataSource.getUrl());
System.out.println(dataSource.getPassword());
}
@Configuration
/*value 指定扫描那个包
* excludeFilters 指定排除哪些,后面是个{}括号,数组存在的,如果想排除多个,逗号分隔即可
* type 是排除类型,可以根据注解,类,按照正则表达式排除,自定义排除等
* 此处的示例是扫描com.ssm.service包,排除该包下的注解,为Controller类型的
* 如果把excludeFilters 换成 includeFilters 则就是扫描 com.ssm.service 中
* 注解是Controller类型的注入到Spring容器中, 但是还需要添加设置@ComponentScan
* 中的一个 useDefaultFilters = false 默认规则是扫描所有,禁用默认规则,才能生效*/
@ComponentScan(value = "com.ssm.service",
excludeFilters = {
@ComponentScan.Filter
(type = FilterType.ANNOTATION,
classes = {Controller.class})
})
public class MyConfiguration {
}
//向容器中注入String,与Persion,多个使用逗号隔开
@Import({String.class , Persion.class})
public class MyConfiguration {
}
创建返回批量需要注入的的组件的类,此时就可以向@Import({MyImportSelector .class})注解中传递,在注入时自动调用MyImportSelector 中的selectImports()方法,将方法返回的数组中的类注入到容器中,注意数组可以为0,但是不能为null,会报空指针
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
//实现ImportSelector 接口,重写selectImports()方法
public class MyImportSelector implements ImportSelector {
/**
* 该方法的返回值就是要导入到容器中的组件,全类名
* @param annotationMetadata 当前使用@Import注解修饰的类的所有信息
* @return
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.ssm.configaution.Persion"};
}
}
创建 实现ImportBeanDefinitionRegistrar 接口,并重写接口中的抽象方法类
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;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param annotationMetadata 通过这个变量可以拿到使用@Import修饰的的类的所有信息
* @param beanDefinitionRegistry 通过该变量可以在容器启动时,手动的想容器中注册,删除注册,判断容器中是否存在已注册的某个bean
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//通过beanDefinitionRegistry查询容器中是存在已注册的"persion",也可以传递全类名
boolean b = beanDefinitionRegistry.containsBeanDefinition("persion");
//如果已注册,手动向容器中注册一个persion2,注意注册的bean需要使用RootBeanDefinition包裹
//如果没有注册,手动向容器中注册一个"persion3"
if(b){
RootBeanDefinition beanDefinition = new RootBeanDefinition(Persion.class);
//registerBeanDefinition()向容器中注册bea
beanDefinitionRegistry.registerBeanDefinition("persion2", beanDefinition);
}else{
RootBeanDefinition beanDefinition = new RootBeanDefinition(Persion.class);
beanDefinitionRegistry.registerBeanDefinition("persion3", beanDefinition);
}
}
}
此时在使用@Import注解时就可以向注解中传递MyImportBeanDefinitionRegistrar.class实现对容器中的bean的一些手动操作
//向容器中注入String,与Persion,多个使用逗号隔开
@Import({MyImportBeanDefinitionRegistrar.class})
public class MyConfiguration {
}
创建一个实现了 FactoryBean 接口的类, 泛型T就是想容器中注入的bean的类型,重写接口中的抽象方法,定义向容器中注入的bean的一些信息
在使用@Bean注解修饰方法向容器中注入bean时,设置方法返回的数据是实现了 FactoryBean 接口的类型数据,此时就将实现该接口时重写 getObject() 方法返回的bean注入到了容器中
import org.springframework.beans.factory.FactoryBean;
public class MyFactoryBean implements FactoryBean<Persion> {
//该方法返回的Bean对象就是我们想要注入到容器中的bean
@Override
public Persion getObject() throws Exception {
return new Persion();
}
//设置注入到容器中的bean的类型
@Override
public Class<?> getObjectType() {
return Persion.class;
}
//设置注入到容器中的bean是否是单例模式,true 是单例,
@Override
public boolean isSingleton() {
return true;
}
}
@Configuration
public class MyConfiguration {
@Bean
public MyFactoryBean configMethod1(){
return new MyFactoryBean();
}
}
3.调用测试
public class Test {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
//注意点,虽然,在使用@Bean修饰方法,有方法层面看返回注入
//到容器中的bean是自定义的MyFactoryBean类型,但是实际是通过MyFactoryBean调用
//里面重写的getObject()方法注入的bean
Object obj = context.getBean("configMethod1");
//输出class com.ssm.configaution.Persion
System.out.println(obj.getClass());
//如果我们就是想要获取到注入的 MyFactoryBean
//在通过id获取时在id前面添加"&"
Object obj2 = context.getBean("&configMethod1");
System.out.println(obj2.getClass());
}
}
在Spring中默认注入到容器中的实例都是单例的,通过scopt可以进行修改设置
<!--singleton: 默认,单例的
prototype: 多例
session: 同一个session创建创建一个实例
request: 同一次请求创建一个实例-->
<bean id="hh" class="com.ssm.entity.HelpCategory" scope="singleton">
<property name="name" value="aaaa"/>
<property name="url" value="bbbb"/>
</bean>
@Configuration
public class MyConfiguration {
@Scope(value = "singleton")
@Bean
public DruidDataSource configMethod(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("aaa");
druidDataSource.setName("bbb");
druidDataSource.setPassword("ccc");
return druidDataSource;
}
}
在单例模式时,有懒加载与非懒加载的区别, Spring创建容器,就会创建实例,将实例放入容器中,默认非懒加载,但是有些对象实例可能是重量级的,在程序运行时也不一定会用到,为了节省资源则可以设置为懒加载
@Lazy设置懒加载
@Configuration
public class MyConfiguration {
private String url="bbb";
private String name="ccc";
private String password = "ccc";
//@Lazy 单例模式,懒加载,容器创建不会创建实例,获取实例时才会
@Lazy
@Bean
public DruidDataSource configMethod(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(url);
druidDataSource.setName(name);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
使用 @Conditional()注解, 可以根据条件向ioc中注入实例,对实例进行判断,满足则向容器中添加,不满足则不添加,该注解需要一个,解需要一个 Condition 类型的参数, Condition 是一个接口,需要创建该接口的实现类,重写接口中的matches()抽象方法,在方法中编写条件,该注解可以修饰方法,也可以修饰类
案例
有一个persion对象,当项目运行环境是windows系统时,将这个persion对象注入到Spring容器中
public class Persion {
private String name;
public Persion(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class MyCondition implements Condition {
/**
*
* @param conditionContext 上下文环境
* @param annotatedTypeMetadata 注释信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//==========查看ConditionContext中可以获取什么数据 =============
//1.使用上下文环境对象可以获取到当前ioc使用的创建实例,进行装配的bean工厂
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2.可以获取到类加载器
ClassLoader loader = conditionContext.getClassLoader();
//3.可以获取到当前环境信息,运行时的一下信息,虚拟机信息,环境变量等等
Environment environment = (Environment) conditionContext.getEnvironment();
//4.可以获取到,Spring运行时的注册类,
//运行时所有bean通过这个类注册,可以通过这个类注册一个bean
//移除一个bea,查看bean是否存在等
BeanDefinitionRegistry reader = (BeanDefinitionRegistry) conditionContext.getRegistry();
//============查看ConditionContext中可以获取什么数据 =============
//此处通过第三步拿到的运行上下文环境,去获取数据
//获取运行项目的系统名称
String property = environment.getProperty("os.name");
//判读如果运行环境系统名称包含"Windows",则将使用该类的bean注入到容器中
if(property.contains("Windows")){
return true;
}else {
return false;
}
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
//如果使用该注解修饰类,则这个配置类中所有向容器中注册bean的方法都会被监管
//@Conditional({MyCondition.class})
@Configuration
public class MyConfiguration {
private String url="bbb";
private String name="ccc";
private String password = "ccc";
/*@Conditional()设置向容器中注入实例的条件,
该注解需要一个 Condition 类型的参数, Condition 是一个接口
创建该接口的实现类,重写接口中的matches()抽象方法*/
@Conditional({MyCondition.class})
@Bean("persion")
public Persion configMethod1(){
return new Persion("联想电脑");
}
}
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
Map<String,Persion> persionMap = context.getBeansOfType(Persion.class);
for(Map.Entry<String,Persion> entry: persionMap.entrySet()){
System.out.println(entry.getValue().getName());
}
}
通过了解bean的几种不同注入方式我们了解到