- spring 特性
非侵入、依赖注入是容器的一部分、容器、切面、组件化。
- 容器
1. 组件:一定功能的对象,一些有功能的一些类,万物皆是组件。
使用配置类替代配置文件:分类管理组件,也是容器中的一种组件。
2.容器(资源的控制):管理组件(创建、获取、保存、销毁)。
容器包含Ioc和DI:ioc控制反转(是一种思想)<—— DI依赖注入(ioc通过DI实现)。
DI:组件的依赖关系,如Controller依赖Service,通过setter、构造器、等自动的注入。
例如:个人信息注册到婚介网站中;当有合适的人过来的时候,婚介所就会推荐, 这样就是反转的方式。不用自己动手,婚介所相当与容器,存着着个人信息。
而我们要做的就是先把组件注册到容器中。
容器注册
spring提供了MVC分层注解,快速注册到容器中
@Component 非mvc其它任何地方
注意:分成组件能起作用的前提是组件必须在主程序所在包及其子包结构下。
解决方法:@ComponentScan(basePackages = "") 组件批量扫描,只扫spring相关注解注册到容器中的组件。
@Controller
public class UserController {}---------------------------------@Service
public class UserService {
}---------------------------------@Repository
public class BaseDao {
}
通过配置类注册组件
因为组件是框架的底层配置。
配置类替代配置文件:分类管理组件,也是容器中的一种组件。@Configuration注解进行标记配置类
@Configuration //告诉spring容器,这是配置类
public class PersonConfig {// 往容器注册组件,容器中方法名就是组件名@Bean("test1")public Person getPerson1() {Person person = new Person();person.setName("zs");person.setGender("nan");return person;}@Bean("test2")public Person getPerson2() {Person person = new Person();person.setName("ls");person.setGender("nv");}
}-----------------------------------------@Configuration
public class Dog {@Beanpublic Person getPerson3() {return new Person();}
}
注册第三方组件到容器
方式一:直接new对象
方式二:使用@Import(类名.class) //导入第三方组件
//组件扫描还是导入第三方等等推荐统一写在一个类里面,方便好管理。
@ComponentScan(basePackages = "") //组件批量扫描
@Import(类名.class) //导入第三方组件public class AppConfig {
}
从容器获取组件
从容器中获取组件 ——> 组件四大特性:名字、类型、对象、作用域
组件不存在:抛异常,NoSuchBeanDefinitionException.
组件不唯一:抛异常NoUniqueBeanDefinitionException。解决方法,按照组件名字精确获取指定对象
注意:组件名重复,只会给容器中最先声明的那个。
// 1.1 按照组件名字获取对象Person person =(Person) ioc.getBean("test1");System.out.println("对象"+person);//1.2按照组件类型获取这种类型所有对象Map<String, Person> beansOfType = ioc.getBeansOfType(Person.class);System.out.println("type"+beansOfType);//1.3按照组件类型+名字获取这种类型所有对象Person test2 = ioc.getBean("test2", Person.class);System.out.println("type"+test2);}
调整组件作用域
@Scope("prototype") 非单实例
容器启动的时候不会创建非单实例组件的对象,什么时候获取,什么时候创建。
@Scope("singleton")单实例 (默认)
容器启动的时创建单实例组件的对象,容器启动完成之前就会创建好。
懒加载
@Lazy:懒加载(前提单例模式)
容器启动完成之前不会创建懒加载组件对象,什么时候获取,什么时候创建。
FactoryBean工厂
FactoryBean是一个接口。
如果制造某些对象比较复杂,利用工厂方法进行创建。
conditional条件注解
@Conditional() 条件注解可标类或者方法上,满足条件就执行。
@Configuration
public class DogConfig {// 一下是conditional派生类
// @ConditionalOnMissingBean(name="userService") //容器中没有UserService这个名称,就执行
// @ConditionalOnBean(name = "bill") //有bill这个名称才执行
// @ConditionalOnResource(resources = "classpath:resource.test") //有这个资源才执行@Bean("dog")public Dog dog() {return new Dog();}
}
依赖注入
@Autowired 自动装配
@Controller
public class UserController {/*** 自动装配流程* 按照类型找组件* 找到一个,直接注入——>如果找到多个,按照名称去找(变量名就是名称)*/@Autowired //自动装配;原理:spring 调用容器的getBeanprivate UserService userService;@AutowiredList<Person> persons; //把这个类型的所有组件都拿到@AutowiredMap<String,Person> personMap;@AutowiredPerson test1; //和@Qualifier功能一致,按照名称找组件@Qualifier("test2") //如果容器中这种组件存在多个,则使用@Qualifier注解精确指定组件名称Person person;}
注意:@Resource和@Autowired都是自动注入
两者区别:Resource是Java标准规定的(所有具有容器功能的框架都支持),具有更强的通用性;Autowired是spring家族规定的(只有spring家族认识)。
构造器和setter方法注入
@Repository
public class UserDao {Dog dog;/*** spring推荐的方式:构造器注入* 因为哪一天脱离了spring环境,有人用UserDao都没有无参构造器* @param dog*///spring自动去容器中找到构造器需要的所有参数的组件值public UserDao(Dog dog) {System.out.println("userDao.....有参构造器"+dog);this.dog = dog;}//setter注入@Autowiredpublic void setDog(Dog dog) {System.out.println("setDog....."+dog);this.dog = dog;}
}
@Value给属性赋值
@Data
@Component
public class Dog {/*** @value("字面值"):直接赋值* @value("${}"),动态从配置文件中获取某一项值。* @value("#{SpEL}"):spring Expression Language;spring表达式语言(无限操作)*/@Value("${dog.name}") //取application.properties配置文件值private String name;private String color;@Value("#{T(java.util.UUID).randomUUID().toString()}") //每次随机生成idprivate String id;}
@PropertySource指定属性来源
@Data
@Component
//属性来源,把指定文件导入容器中,供使用。解决脱离application.properties也能够进行获取。
@PropertySource("classpath:person.properties")
public class Person {@Value("${person.name}")private String name;@Value("${person.age}")private int age;@Value("${person.gender}")private String gender;
}----------------------------------#person.properties配置文件person.name=ls
person.age=18
person.gender=nv
注意:①classpath:person.properties,从自己项目类路径下找。
②classpath*:Log4j...properties,从所有包的类路径下找。
扩展-资源工具类
// 资源工具类,获取文件File file = ResourceUtils.getFile("classpath:image.jpg");
// 使用流,读图;获取图片大小int available= new FileInputStream(file).available();System.out.println(available);
@Profile多环境
为什么使用多环境?
因为项目整个流程中,有开发环境,生产环境,测试环境等。
例如连数据库,这些环境配置不能一样,所以什么环境激活什么。
利用@Profile条件注解,只在某种环境下激活一个组件。
@Profile注解好处:代码一行不改,只需要修改配置文件的激活环境。
#application.properties文件#激活环境 test/dev/default
spring.profiles.active=test------------------------------@Data
public class DataSource {private String name;private String url;private String driver;
}-----------------------------
//往容器注册DataSource组件
@Configuration
public class DataSourceConfig {/*** Profile("环境标识"),当环境被激活时,才会加入已经标识的组件* 定义环境标识:* 告诉spring当前处于什么环境* 不说啥环境,就是default环境*/@Profile({"dev","default"})@Beanpublic DataSource dev() {DataSource dataSource = new DataSource();dataSource.setName("dev_user");dataSource.setUrl("jdbc:mysql://localhost:3306/dev_user");dataSource.setDriver("com.mysql.jdbc.Driver");return dataSource;}@Profile("test")@Beanpublic DataSource test() {DataSource dataSource = new DataSource();dataSource.setName("test_user");dataSource.setUrl("jdbc:mysql://localhost:3306/dev_user");dataSource.setDriver("com.mysql.jdbc.Driver");return dataSource;}
}
组件生命周期
希望在生命周期环节做一些自己定义的事情,也称为自定义回调。
~ spring基于这种思想,除了在管理组件生命周期的,还把各个关键环节暴露出来,
让可以定义自己方法,在核心的环节做自己的事情,这就是回调感知。
@Data
public class User {private String userName;private String userPassword;public User(){System.out.println("User 构造器...");}
// 初始化调用public void initUser(){System.out.println("@Bean 初始化");}
// 销毁调用public void destroyUser(){System.out.println("@Bean 销毁");}
}----------------------------------------@Configuration
public class UserConfig {//方式一:指定声明周期方法拦截@Bean(initMethod = "initUser",destroyMethod = "destroyUser")public User user() {return new User();}//方式2:可以使用InitializingBean和DisposableBean接口进行拦截//方式3:随便写两个方法进行拦截生命周期,进行标志注解@PostConstruct(构造器之后)和 // @PreDestroy(销毁之前)
}
BeanPostProcessor接口
类似Bean修改器:①发现对象在执行过程中,能改变对象内容。
②拦截所有Bean的后置处理器
@Component
public class MyTestPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 在最后运行之前给username赋值(修改)if (bean instanceof User){ //过滤为User组件的User user = (User) bean;user.setUserName("zsts");}return bean;}
}
@Autowired如何实现?
①有一个处理@Autowired注解的AutowiredAnnotationBeanPostProcessor——>②每个Bean创建后,调用BeanPostProcessor的方法——>③postProcessBeforeInitialization里面利用反射,得到当前Bean所有属性,利用反射,得到Bean属性上标注的所有注解,看有没有@Autowired。——>④去容器中找到这属性对应组件(按类型、按名字)。