spring学习
1.概述
2.核心知识
3.优点
4.注解开发
4.1组件注册
1.新建maven项目,导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
2.创建一个类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
}
3.创建一个配置类
//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
public class MainConfig {
//给容器中注册一个Bean,类型为返回值的类型,id默认是是使用方法名作为id
@Bean
public User user(){
return new User("吴杰",20);
}
}
4.2@ComponentScan自动扫描组件和指定扫描规则
//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
@ComponentScan(
value = "com.wxit",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,
classes = {Controller.class})})
//ComponentScan value 指定要扫描的包
//excludeFilter= Filter[],指定扫描的时候按照什么规则排除那些组件
//includeFilters=Filter[] 指定扫描的时候只需要包含那些组件
public class MainConfig {
//给容器中注册一个Bean,类型为返回值的类型,id默认是是使用方法名作为id
@Bean
public User user(){
return new User("吴杰",20);
}
}
4.3自定义TypeFilter指定过滤规则
public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader 读取到的当前正在扫描的类的信息
* @param metadataReaderFactory 可以获取到其他任何类的信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类的注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类的资源 (类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("----" + className);
if (className.contains("er")){
return true;
}
return false;
}
}
测试
public class IocTest {
@Test
@SuppressWarnings("resource")
public void test(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] names = applicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
4.4@Scope设置组件作用域
@Configuration
public class MainConfig2 {
/**
*prototype : 多实例的,IOC容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象
*singleton:单实例,IOC容器启动会调用方法创建对象到IOC容器中,以后每次获取就是直接从容器(map.get())中拿
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*
*/
@Scope("prototype")
@Bean("user")
public User user(){
System.out.println("给容器中添加user");
return new User("吴昊",20);
}
}
测试
@Test
public void test02(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
// String[] definitionName = applicationContext.getBeanDefinitionNames();
// for (String name : definitionName) {
// System.out.println(name);
// }
System.out.println("ioc 容器创建完成");
Object bean1 = applicationContext.getBean("user");
Object bean2 = applicationContext.getBean("user");
System.out.println(bean1 == bean2);
}
4.5@Lazy懒加载
@Configuration
public class MainConfig3 {
/**
*
* 懒加载:
* 单实例bean:默认在容器启动的时创建对象
* 懒加载:容器启动不创建对象,第一次使用(获取)bean,创建对象,并初始化
*/
@Bean("user")
@Lazy
public User user(){
System.out.println("给容器中添加user对象");
return new User("李婷",23);
}
}
测试代码
@Test
public void test03(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
Object bean1 = applicationContext.getBean("user");
System.out.println("ioc容器创建完成");
}
4.6@Conditional按照条件注册bean
@Configuration
public class MainConfig3 {
/**
*
* 懒加载:
* 单实例bean:默认在容器启动的时创建对象
* 懒加载:容器启动不创建对象,第一次使用(获取)bean,创建对象,并初始化
*
* @Condition:按照一定的条件进行判断,满足条件给容器中注册bean
*
*
*/
@Bean("user")
@Lazy
public User user(){
System.out.println("给容器中添加user对象");
return new User("李婷",23);
}
@Bean("bili")
@Conditional({WindowsCondition.class})
public User user1(){
return new User("Bill Gates",62);
}
@Bean("linus")
@Conditional({LinuxCondition.class})
public User user2(){
return new User("linus",51);
}
}
筛选条件为计算机系统
public class WindowsCondition implements Condition {
/**
*
* @param context 判断条件,能使用的上下文
* @param annotatedTypeMetadata 注释信息
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
//1.能获取到ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2.获取类加载器
ClassLoader classLoader = context.getClassLoader();
//3.获取当前环境信息
Environment environment = context.getEnvironment();
//4.获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
if (property.contains("Windows")){
return true;
}
return false;
}
}
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Linux")){
return true;
}
return false;
}
}
测试代码
@Test
public void test04(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
String[] namesForType = applicationContext.getBeanNamesForType(User.class);
//动态获取环境变量的值
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
for (String name : namesForType) {
System.out.println(name);
}
Map<String, User> ofType = applicationContext.getBeansOfType(User.class);
System.out.println(ofType);
}
4.7使用FactoryBean注册组件
待补充
5.生命周期
bean的生命周期:
bean创建 -----初始化-------销毁的过程
容器管理bean的生命周期:
我们可以自定义初始化初始化和销毁方法,容器bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
5.1指定初始化和销毁方法
构造(对象创建)
单实例 : 在容器启动的时候创建对象
多实例: 在每次获取的时候创建对象
初始化:
对象创建完成,并赋值好,调用初始化方法
销毁:
单实例:容器关闭的时候
多实例: 容器不会管理这个bean,容器不会调用销毁方法
代码示例
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod = "init",destroyMethod = "destroy")
@Scope("prototype")
public Car car(){
return new Car();
}
}
public class Car {
public Car(){
System.out.println("car -----");
}
public void init(){
System.out.println("car init");
}
public void destroy(){
System.out.println("car destroy");
}
}
//测试代码
public class IocTestLifeCycle {
@Test
public void test01(){
//1创建ioc容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
Object bean = applicationContext.getBean("car");
applicationContext.close();
}
}
5.2让bean实现InitializingBean(定义初始化逻辑)DisposableBean(定义销毁)
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat(){
System.out.println("cat 创建了");
}
@Override
public void destroy() throws Exception {
System.out.println("我会在容器关闭的时候调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("我会在bean创建完成,并且属性都赋好之后调用");
}
}
@Configuration
@ComponentScan("com.wxit.bean")
public class MainConfigOfLifeCycle {
@Bean(initMethod = "init",destroyMethod = "destroy")
@Scope("prototype")
public Car car(){
return new Car();
}
}
5.3@PostConstruct和@PreDestroy
@PostConstruct:在bean创建完成并且属性赋值完成,来执行初始化方法
@PreDestroy:在容器销毁bean之前通知我们进行清理工作
代码示例
@Component
public class Dog {
public Dog(){
System.out.println("dog构造器");
}
//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("Dog-----@PostConstruct");
}
//容器移除对象之前
@PreDestroy
public void detory(){
System.out.println("dog-----@PreDestroy");
}
}
5.4BeanPostProcessor后置处理器
BeanPostProcessor:为接口,bean的后置处理器
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
示例代码
@Component
public class MyBeanPostProcessor 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;
}
}
5.5BeanPostProcessor原理
遍历得到容器中所有的BeanPostProcessor,挨个执行BeforeInitialization,一旦返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessBeforeInitialization
5.6BeanPostProcessor在spring底层的使用
bean赋值: 注入其他组价,@Autowired,生命周期注解功能,@Async,BeanPostProcessor
代码示例
@Component
public class Dog implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Dog(){
System.out.println("dog构造器");
}
//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("Dog-----@PostConstruct");
}
//容器移除对象之前
@PreDestroy
public void detory(){
System.out.println("dog-----@PreDestroy");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
6.属性赋值
使用@Value赋值
1.基本数值
2.可以写SpEl,#{}
3.可以写${},取出配置文件中的值(在运行环境变量里面的值)
public class User {
@Value("吴杰")
private String name;
@Value("#{22-2}")
private int age;
}
4.加载外部配置文件
@Configuration
@PropertySource(value = {"classpath:/user.properties"},encoding = "gbk")
public class MainConfigOfPropertyValues {
@Bean
public User user(){
return new User();
}
}
public class User {
@Value("吴杰")
private String name;
@Value("#{22-2}")
private int age;
@Value("${user.nickName}")
private String nickName;
}
7.自动装配
7.1自动装配:
spring利用依赖注入,完成对ioc容器中各个组件的依赖关系赋值
@Autowired:自动注入
1.默认优先按照类型去容器中找对应的组件,
2.如果找到多个相同类型的组件,再将属性的名称作为组件id去容器中查找
3.@Qualifier:指定需要装配的组件id,而不是属性名
4.自动装配默认一定要将属性赋值好,没有就会报错
可以使用@Autowired(required = false)
5.@Primar让spring自动进行自动装配的时候,默认使用首选的bean,
@Configuration
@ComponentScan({"com.wxit.service","com.wxit.dao","com.wxit.controller"})
public class MainConfigOfAutowired {
@Bean("bookDao2")
@Primary
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
};
}
public class TestAutowired {
@Test
public void test01(){
//创建ioc容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
System.out.println("容器创建完成");
BookService bean = applicationContext.getBean(BookService.class);
System.out.println(bean);
// BookDao bookDao = applicationContext.getBean(BookDao.class);
// System.out.println(bookDao);
applicationContext.close();
}
}
7.2方法,构造器位置的自动装配
@Autowired:位置:构造器,方法,属性,都是从容器中获取参数组件的值
1.标在方法位置
2.标在构造器上:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还可以自动从容器中获取
3.放在参数位置
代码示例
@Component
public class Boss {
private Car car;
@Override
public String toString() {
return "Boss{" +
"car=" + car +
'}';
}
@Autowired
public Boss(Car car){
this.car = car;
System.out.println("Bose有参构造器");
}
public Car getCar() {
return car;
}
//@Autowired
//标注在方法,spring容器创建当前对象,就会调用方法,完成赋值
//方法使用的参数,自定义类型的值从ioc容器中获取
public void setCar(Car car) {
this.car = car;
}
}
7.3@Profile环境搭建
profile:spring为我们提供的可以根据当前环境,动态,的激活和切换一系列组件的功能
首先导入maven依赖
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
编写配置文件dbconfig.properties
db.user = root
db.password = root
db.driverClass = com.mysql.jdbc.Driver
配置类代码
@Configuration
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
private String driverClass;
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssmbuild");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Bean("proDataSource")
public DataSource dataSourcePro(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/wujie");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
driverClass = valueResolver.resolveStringValue("${db.driverClass}");
}
}
测试代码
public class TestProfile {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String name : namesForType) {
System.out.println(name);
}
applicationContext.close();
}
7.4@Profile根据环境注册bean
@Profile:指定组件在哪个环境情况下才能被注册到容器中,不指定,任何环境都能注册到这个组件
加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境
写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能生效
没有标注环境标识的bean,在任何时候都是加载的
方法一:使用命令行动态参数:这个方法略过
方法二:使用代码来实现
1.创建一个applicationContext
2.设置需要激活的环境,可以设置多个
3.注册主配置类
4.启动刷新容器
代码演示
@Configuration
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
private String driverClass;
@Bean("testDataSource")
@Profile("test")
public DataSource dataSourceTest(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Bean("devDataSource")
@Profile("dev")
public DataSource dataSourceDev(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssmbuild");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Bean("proDataSource")
@Profile("pro")
public DataSource dataSourcePro(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/wujie");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
driverClass = valueResolver.resolveStringValue("${db.driverClass}");
}
}
//测试代码
public class TestProfile {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new
AnnotationConfigApplicationContext();
//1.创建一个applicationContext
//2.设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("test","dev");
//3.注册主配置类
applicationContext.register(MainConfigOfProfile.class);
//4.启动刷新容器
applicationContext.refresh();
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String name : namesForType) {
System.out.println(name);
}
applicationContext.close();
}
}
8.ioc总结
1.组件添加的注解
2.组件赋值
3.组件注入
9.AOP面向切面编程(重中之重)
9.1AOP功能测试
AOP:动态代理
指在程序运行期间动态的将某段代码切入到指定的方法指定位置进行运行的编程方式
1.导入依赖
2.定义一个业务逻辑类,在业务逻辑运行的时候将日志进行打印
3.定义一个日志切面类,切面类里的方法需要动态感知MathCalculator.div
通知方法:
前置通知:@Before 在目标方法运行之前
后置通知:@After 在目标方法运行结束之后运行
返回通知:@AfterReturning 在目标方法正常返回之后运行
异常通知:@AfterThrowing 在目标方法出现异常以后运行
环绕通知:动态代理,手动推动目标方法运行()
4.给切面类的目标方法标注何时运行(通知注解)
5.将切面类和业务逻辑类(目标方法所在类)都加入到容器中
6.必须告诉Spring,哪个类是切面类(给类上面加一个注解:@Aspect)
7.给配置类中加@EnableAspectJAutoProxy【开启注解的Aop模式】
总结:
三步:
1.将业务逻辑组件和切面类都加入到容器中,告诉Spring哪个是切面类
2.在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3.开始基于注解的aop模式
代码示例
//配置类
@Configuration
@EnableAspectJAutoProxy
public class MainConfigOfAOP {
//业务逻辑类加入到容器中
@Bean
public MathCalculator mathCalculator(){
return new MathCalculator();
}
//切面类加入到容器中
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
public class MathCalculator {
public int div(int i,int j){
System.out.println("开始计算------");
return i / j;
}
}
@Aspect
public class LogAspects {
//抽取公共的切入点表达式
//1.本类引用
//2.其他切面引用
@Pointcut("execution(public int com.wxit.aop.MathCalculator.*(..))")
public void pointCut(){};
@Before("pointCut()")
//JoinPoint一定要出现在参数的第一位
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println("" + joinPoint.getSignature().getName() + "运行-----参数列表:{" + Arrays.asList(args) +"}");
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println("" + joinPoint.getSignature().getName() + "结束----");
}
@AfterReturning(value = "pointCut()",returning = "result")
public void logReturn(Object result){
System.out.println("除法正常返回---运行结果" + result);
}
@AfterThrowing("pointCut()")
public void logException(){
System.out.println("除法异常---异常信息---");
}
}
//测试代码
public class TestAop {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
mathCalculator.div(1,1);
applicationContext.close();
}
}
9.2AOP原理
流程:
1.传入配置类,创建ioc容器
2.注册配置列,调用refresh()刷新容器
3.registerBeanPostProcessors(beanFactory),注册bean的后置处理器来方便拦截bean的创建
1.先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
2.给容器中加别的BeanPostProcessor
3.优先注册实现了PriorityOrder接口的BeanPostProcessor
4.再给容器中注册实现了Orderd接口的BeanPostProcessor
5.注册没有实现优先级接口的BeanPostProcessor
6.注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中
9.3创建AOP代理
…
10.声明式事务
1.环境搭建
导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
2.配置数据源,JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据库