Sprin初学
引入
Spring容器,容器内有各种各样的组件。也可以自己添加组件,就要用到@Bean。在我目前看来,spring就是将各种代码封装起来,然后只需要使用注解@就可以实现各种操作。
我的idea目前spring项目运行代码是这个 不同版本可能不一样ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(SpringIocApplication.class)
SpringIocApplication是主类,context就是容器
容器and注入
- 获取容器中的组件
context.getBean(name = “”||class = “”)可以是名字获取 也可以是类型获取
- 定义
组件:框架的底层配置 通常用@Bean或者@Component来向容器内注册一个组件 每一个组件都叫Bean 把组件放进容器中 取出来方法就是getBean 相当于容器内的所有东西都是Bean 组件的四大 特性 名字 类型 对象 作用域
配置文件:指定的配置
配置类:分类管理组件的配置 配置类也是一个组件 通常用@Configuration来向容器注册一个配置类 @Configuration //告诉spring容器 这是一个配置类
创建组件对象的时机:容器启动过程中就会创建组件ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(SpringIocApplication.class) 在这个代码启动过程中就会创建对象
- 作用域 scope
单实例:所有组件默认都是单实例的 singleton 就是容器内只允许存在一个该组件 然后需要的时候直接从容器中取
多实例:prototype 可以存在多个实例
@Scope("prototype")
@Bean("zhangsan")
public Person zhangsan(){
Person zhangsan = new Person();
zhangsan.setName("zhangsan");
zhangsan.setAge(18);
zhangsan.setGender("man");
return zhangsan;
}
此处为注册了一个多实例的Bean 名字叫zhangsan 如果在主程序中获得该Bean并赋给一个人叫zhangsan1代码如下
Person zhangsan1 = context.getBean("zhangsan")
如果再创建一个叫zhangsan2的人
·Person zhangsan2 = context.getBean("zhangsan")
如果放在单实例里 if语句判断zhangsan1==zhangsan2 会显示true 因为单实例 这两个实例化的对象都是同一个zhangsan 而多实例的话 就会返回false 因为这是两个不同的对象 只是对象的值一样
注意:单实例 容器启动的时候就会创建单实例
而多实例 容器启动的时候不会创建 什么时候用到 什么时候创建多实例
但是在这里可以用到@Lazy 懒加载对单实例对象 即容器启动的时候不会创建对象 也是什么时候用到什么时候创建
- MVC分层注解
spring提供了快速的MVC分层注解
@Controller 作用于控制层的注解
@Service 作用于服务层的注解
@Repository 作用于持久层 也就是数据库 Dao包下
@Component 组件 简而言之 除去上面三个 都是Component 但是上面三个也属于Component 只是单独的分开了 便于读编码
- @Component 和 @Configuration的区别
@Component是一个组件 spring会自动扫描并创建该类的实例 通常都是服务类 工具类
@Configuration是一个配置类 配置类下有很多的Bean注解 通常于Bean搭配使用 会显示声明Bean 然后确保单例
*FactoryBean
如果创建对象比较复杂的时候 可以用工厂方法创建
public class BYDFactory implements FactoryBean<Car> {
//调用此方法给容器中制造对象
@Override
public Car getObject() throws Exception {
System.out.println("BYDFactory 正在制造car对象");
Car car = new Car();
return car;
}
FactoryBean在容器中放的组件的类型 是接口中泛型指定的类型 组件的名字是工厂自己的名字
- 条件注册 @Condition
场景 判断当前电脑操作系统是Windows还是mac
Windows系统 容器中有bill
mac系统 容器中有jobs
首先实现一个WindowsCondition类去实现Condition接口
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//判断环境变量中的OS 包含Windows 如果包含就是Windows系统
Environment environment = context.getEnvironment();
String property = environment.getProperty("OS");
return property != null && property.contains("Windows");
}
}
其中的context.getEvironment就是得到环境变量中的OS 因为OS下存放着系统区分 然后如果含有Windows就返回true
然后实现一个bill的Bean
@Conditional(WindowsCondition.class)
@Bean("bill")
public Person bill(){
Person person = new Person();
person.setName("比尔盖茨");
person.setAge(22);
person.setGender("Male");
return person;
}
去标明@Condition的实现以及依据是哪个类 当返回true的时候 才会扫描这个Bean 然后让spring把这个Bean放在容器中
除此之外Conditional还可以标在类上 如果满足 就扫描类中的语句 如果不满足 类中有什么都不管了
- 自动装配 @Autowired
@Autowired // 自动装配 原理:spring 调用容器的getBean
UserService userService;
@Autowired
Person bill;
@Autowired
List<Person> persons;
@Autowired
Map<String, Person> personMap;
自动装配流程 先按照类型 再按照名称
按照类型如果只找到一个 那么直接注入 如果找到多个 再按照名称 变量名就是名字
但是如果此时变量名也有多个 那么就要用到@Primary和@qualifier
@Primary 标注主要的的组件 之后遇到多个变量名会直接按照标有@Primary注解的注入
@qualifier 例如当遇到Person person的时候 spring扫描遇到有多个Person 也没有person变量名 因此在自动注入的时候加上@Qualifier("bill")注解 机会让spring去找名字叫bill的Bean对象 然后来注入
- 注意:
@Autowired @Resource两者的区别
都可以做自动注入
但是resource是Java 的标准规范库 autowired是spring的库
当切换框架时 autowired需要更改 但是resource就不需要更改
resource 有更强的通用性
同样的 也可以使用构造器来注入 但是这种方法使用的并不是很多
public UserDao(Dog dog){
System.out.println("UserDao构造器...." + dog);
this.dog = dog;
}
如代码所示 UserDao构造器会自动注入dog对象
- Aware 感知接口
可以将各种属性的值 通过spring调用 自动得到 只需要实现相关的接口即可
@Getter
@Service
public class HahaService implements EnvironmentAware, BeanNameAware {
private Environment environment;
private String beanName;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public String getOs(){
return environment.getProperty("OS");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
}
如代码所示 该Service实现了 EnvironmentAware和BeanNameAware 感知接口 实现了方法 setEnvironment 和setBeanName 就是将所感知到的Name和Envir赋给了service中的属性值 让service可以得到这些值从而返回 注意 该方法需要get方法来得到BeanName
- Value的强大功能
@Value("字面值") 直接赋值
@Value("${}") 从配置文件中取出某一项的值
@Value("#{SPEL}") Spring Expression Language Spring表达式
直接赋值
@Value("旺财")
private String name;
@Value("#{10+20}")
private String color;
下面都是SPEL表达式赋值
调用类的静态方法进行赋值
@Value("#{T(java.util.UUID).randomUUID().toString()}")
private String id;
调用类的非静态方法赋值
@Value("#{'Hello World'.substring(0,5)}")
private String msg;
三元运算符赋值
@Value("#{1 < 2? 'haha':'hehe'}")
private String msg2;
还可以直接定义进行赋值
@Value("#{new int[] {1,2,3}}")
private int[] array;
从配置文件中赋值就需要告诉spring容器是在哪个配置文件中扫描 因此就需要用到@PropertySource
@PropertySource("classpath:cat.properties")
@Data
@Component
public class Cat {
@Value("${cat.name:Tom1}") //:是取不到的默认值
private String name;
@Value("${cat.age:21}")
private int age;
}
Cat类的name和age就是从配置文件中取值 在cat.properties配置文件中有
cat.name = Tom
cat.age = 2
因此就可以拿到值
需要注意的是:classpath:cat.properties说明属性来源 把指定的文件导入到容器中 以此来使用从自己的类路径下找
classpath*:cat.properties 从所有包的类路径下找
classpath还可以直接获取文件
try {
File file = ResourceUtils.getFile("classpath:1.jpg");
System.out.println("file = " + file);
int available = new FileInputStream(file).available();
System.out.println("available = " + available);
} catch (IOException e) {
throw new RuntimeException(e);
}
这个就通过classpath获取了配置文件中jpg照片的大小
- 环境标识 @profile
@Profile("环境标识") 当这个环境标识被激活的时候 才会加入组件
场景:
分为三个环境 dev prod test 当不同的环境运行时 会链接不同的数据库
@Configuration
public class DataSourceConfig {
@Profile("dev")//
@Bean
public MyDataSource dev(){
MyDataSource myDataSource = new MyDataSource();
myDataSource.setUrl("dev");
myDataSource.setUsername("dev");
myDataSource.setPassword("dev");
return myDataSource;
}
@Profile({"test","default"})
@Bean
public MyDataSource test(){
MyDataSource myDataSource = new MyDataSource();
myDataSource.setUrl("test");
myDataSource.setUsername("test");
myDataSource.setPassword("test");
return myDataSource;
}
@Profile("prod")
@Bean
public MyDataSource prod(){
MyDataSource myDataSource = new MyDataSource();
myDataSource.setUrl("prod");
myDataSource.setUsername("prod");
myDataSource.setPassword("prod");
return myDataSource;
}
在该代码中 分别定义了三种不同的DataResource 并且用@profile进行三种不同的环境
定义环境标识dev test prod 激活环境标识 明确告诉spring当前环境 如果不说 就是默认环境 默认是default
现在只需要在主代码中定义具体环境即可
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("prod"); // ✅ 先设置 Profile
context.register(SpringIocApplication.class); // ✅ 再注册配置类
context.refresh(); // ✅ 最后刷新容器
DeliveryDao dao = context.getBean(DeliveryDao.class);
dao.saveDelivery();
此处需要先new一个容器 然后先设置好环境之后再注册配置类 这样容器中扫描组件的时候才会正确扫描
- 生命周期


- @Autowired如何实现的?


浙公网安备 33010602011771号