spring Basic 
Spring 的核心:AOP,IOC
带来了以下的优势
为什么说Spring解耦了? 
 
原始javaEE的开发时,内部所有对象都是自己手动声明的,因此类和类的耦合也重,Spring带来的第一个好处就是类的解耦,Spring将所有程序中需要生成的对象由他自己控制,每个对象(bean)的创建到死亡都由Spring来管理
这样带来的好处是每个对象想要使用其他对象时,只需要问Spring要就行了,类和类之间不在有直接的耦合关系
 
AOP 的好处? 
 
面向切面编程,提供了一个统一管理接口的方法
 
事务开发? 
 
原来的事务开发是真正的编程式的,在开启一个事务前你得写一些控制代码,结束了事务也得写,Spring带来了配置方式,只要配置好那些方法需要事务即可,不需要像原来那样每个方法写事务控制代码
 
方便测试? 
 
spring带来的junit4等工具可以方便快捷的编写测试用例
 
spring还集成了许多其他框架如 Structs,Hibernate等 
 
 
封装了很多工具的API如JDBC,JDBC原始的代码是很麻烦的,Spring封装填写了很多参数 
 
 
 
Spring IOC内容 配置bean 
上面说了,以后所有的对象都不再由我们手动new出来了,都交给spring来生成,我们拿过来用就行了,那么spring怎么知道哪些对象(Bean)要生成出来呢,我们需要提供一个xml配置文件告诉它
不过首先要知道怎么定位一个类文件,在java开发中,每个类文件属于一个包,我们可以通过包名+文件名找到类,假如一个文件名为test.java处在com.tr.example包下,那么我们可以通过com.tr.example.test定位
光说无法体会,以下以一个例子说明
 
搭建spring环境 
先用maven导入spring的包 
创建对象 
配置文件内配置对象 
通过spring获取对象 
 
 
新建maven工程,打开idea,选择maven,随便选择启动器都行,反正pom文件自己配置就行了
在pom文件中添加spring的核心依赖:spring-context 这个依赖包含了5个核心包,写好后点击同步等待包下载即可
 
1 2 3 4 5 6 7 <dependencies >        <dependency >             <groupId > org.springframework</groupId >             <artifactId > spring-context</artifactId >             <version > 5.3.18</version >         </dependency >     </dependencies >  
配置好依赖后,创建interface接口和对应的实现,众所周知interface接口可以对应很多个实现,具体对应哪个我们可以xml中配置,先创建interface代码如下
 
1 2 3 4 5 package  com.tr.example;public  interface  UserDao      public  void  save ()  } 
具体实现
 
1 2 3 4 5 6 7 8 9 10 11 package  com.tr.example.impl;import  com.tr.example.UserDao;public  class  UserDaoImpl  implements  UserDao      @Override      public  void  save ()           System.out.println("hello from user dao" );     } } 
编写配置bean 文件名就叫ApplicationConfig.xml放在resources目录下
 
1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8"?> <beans  xmlns ="http://www.springframework.org/schema/beans"         xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >     <bean  id ="userDao"  class ="com.tr.example.impl.UserDaoImpl" />  </beans > 
这步bean的配置定义基本完成了,现在其他类可以轻松的从spring中获取这些bean了
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package  com.tr.example.controller;import  com.tr.example.UserDao;import  org.springframework.context.ApplicationContext;import  org.springframework.context.support.ClassPathXmlApplicationContext;public  class  UserDaoDemo      public  static  void  main (String[] args)           ApplicationContext context = new  ClassPathXmlApplicationContext("ApplicationConfig.xml" );         UserDao userDao = (UserDao) context.getBean("userDao" );         userDao.save();     } } 
执行代码即可看到效果
这里可以看到使用类的所有对象都不是自己生成的,都是从spring获取的
 
Spring的配置文件 Bean标签的基本配置 
所有对象交由Spring创建,默认情况下Spring创建是调用无参构造函数的类似new A()
id :唯一性标识用来标志类,不可以重复
class:类的全路径标识
 
Bean的标签范围配置 
scope:对象的作用范围
singleton:默认的,单例  在加载配置文件的时候,实例就已经被创建了,当应用停止时才销毁 
prototype:多例的  只有bean被获取时才创建一个新的返回,无指针引用就被gc回收了 
request:Spring创建后会将对象存入request中 
session:创建后存入session中 
global session:创建后存入Portle环境,若无Portle环境则存入session 
 
测试scope很简单,只要修改例子中的xml配置即可,可以在代码内打印bean的地址查看<bean id="userDao" class="com.tr.example.impl.UserDaoImpl" scope="prototype"/>
 
Bean的声明周期 
可以在xml中给类声明创建和销毁时调用的方法
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  class  UserDaoImpl  implements  UserDao      public  UserDaoImpl ()           System.out.println("user dao impl created" );     }     public  void  init ()          System.out.println("init user dao" );     }     public  void  destroy ()          System.out.println("destroy user dao" );     }     @Override      public  void  save ()           System.out.println("hello from user dao" );     } } 
1 <bean  id ="userDao"  class ="com.tr.example.impl.UserDaoImpl"  init-method ="init"  destroy-method ="destroy" /> 
Bean实例化的三种方式 
无参构造很简单,提供构造函数即可,下面研究工厂方法,首先创建静态工厂类
 
1 2 3 4 5 6 7 8 9 10 11 package  com.tr.example.factory;import  com.tr.example.UserDao;import  com.tr.example.impl.UserDaoImpl;public  class  StaticFactory      public  static  UserDao getUserDao ()          return  new  UserDaoImpl();     } } 
然后告诉spring,创建这个对象使用工厂来创建
 
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8"?> <beans  xmlns ="http://www.springframework.org/schema/beans"         xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >         <bean  id ="userDao"  class ="com.tr.example.factory.StaticFactory"  factory-method ="getUserDao" />  </beans > 
这就是静态工厂的方式了
再来看看动态工厂,创建工厂
 
1 2 3 4 5 6 7 8 9 10 package  com.tr.example.factory;import  com.tr.example.UserDao;import  com.tr.example.impl.UserDaoImpl;public  class  DynamicFactory      public   UserDao getUserDao ()          return  new  UserDaoImpl();     } } 
配置xml,首先要声明一个Factory的bean,我们目标bean需要和Factory的bean关联
 
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="UTF-8"?> <beans  xmlns ="http://www.springframework.org/schema/beans"         xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >               <bean  id ="myFactory"  class ="com.tr.example.factory.DynamicFactory" />      <bean  id ="userDao"  factory-bean ="myFactory"  factory-method ="getUserDao" />  </beans > 
为什么要用工厂? 不觉得麻烦吗?其实是因为很多对象创建的时候不是单单一个new那么简单,创建的对象可能引用了很多其他对象,需要再new更多对象作为参数传入,使用工厂就相当于有人帮我们完成了复杂的创建逻辑
 
Bean的依赖注入 
先配置好三层架构:controller,dao,service,在xml中配置好service和dao层的bean
 
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8"?> <beans  xmlns ="http://www.springframework.org/schema/beans"         xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >                         <bean  id ="userDao"  class ="com.tr.example.dao.impl.UserDaoImpl" />      <bean  id ="userService"  class ="com.tr.example.service.impl.UserServiceImpl" />  </beans > 
在controller层中新建方法使用service的bean
 
1 2 3 4 5 6 7 8 9 10 11 12 13 package  com.tr.example.controller;import  com.tr.example.service.UserService;import  org.springframework.context.ApplicationContext;import  org.springframework.context.support.ClassPathXmlApplicationContext;public  class  UserController      public  static  void  main (String[] args)           ApplicationContext context = new  ClassPathXmlApplicationContext("ApplicationConfig.xml" );         UserService userService = (UserService) context.getBean("userService" );         userService.save();     } } 
这里其实有问题,我们代码中从spring那里获取了service的bean,调用了save方法,执行到save方法的时候又去spring里获取了dao的bean,然后调用save方法,可以看出service层的这个类是引用了dao层的类的,他们存在耦合关系,但是当前做法是从外部将dao层的bean注入进去的:即手动获取调用
但是这样不好,我们更希望不要自己手动去spring里面获取bean,让spring把需要的bean送过来,我只需要声明一下private 类 a 像这样 spring自动把对应的bean注入到a中,spring提供了在内部结合的方式DI(Dependency injection),通过xml配置和set的方式注入
先配置下我们需要被注入的类,写上set方法给spring调用
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  UserServiceImpl  implements  UserService      private  UserDao userDao;     public  void  setUserDao (UserDao userDao)           this .userDao = userDao;     }     @Override      public  void  save ()           System.out.println("do service" );         userDao.save();     } } 
我们需要通知spring,把UserDao的bean调用set方法传递进来
 
1 2 3 4 <bean  id ="userDao"  class ="com.tr.example.dao.impl.UserDaoImpl" /> <bean  id ="userService"  class ="com.tr.example.service.impl.UserServiceImpl" >     <property  name ="userDao"  ref ="userDao" > </property >  </bean > 
这样就完成了bean注入,直接运行controller的方法可以看到没问题,不过这么写xml有点啰嗦,Spring还提供了命名空间p可以精简xml书写
首先添加命名空间:xmlns:p="http://www.springframework.org/schema/p"
 
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8"?> <beans  xmlns ="http://www.springframework.org/schema/beans"         xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"         xmlns:p ="http://www.springframework.org/schema/p"         xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >     <bean  id ="userDao"  class ="com.tr.example.dao.impl.UserDaoImpl" />      <bean  id ="userService"  class ="com.tr.example.service.impl.UserServiceImpl"  p:userDao-ref ="userDao" />  </beans > 
Bean还提供了另一种注入方式,构造方式注入,先给个构造函数
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  class  UserServiceImpl  implements  UserService      private  UserDao userDao;     public  UserServiceImpl (UserDao userDao)           this .userDao = userDao;     }     public  UserServiceImpl ()       }     @Override      public  void  save ()           System.out.println("do service" );         userDao.save();     } } 
惯例,配置xml让spring调用构造函数的时候把bean注入进来
 
1 2 3 4 <bean  id ="userDao"  class ="com.tr.example.dao.impl.UserDaoImpl" /> <bean  id ="userService"  class ="com.tr.example.service.impl.UserServiceImpl" >     <constructor-arg  name ="userDao"  ref ="userDao" > </constructor-arg >  </bean > 
测试即可
要注意的是,set的时候 name属性指的是set方法后面的小写开头的名字,构造函数模式下name指的是参数接口的名字
 
Bean依赖注入的数据类型 
Bean可以注入哪些数据类型?
除了对象,普通数据和集合都可以,这里以dao层的类为例,给dao层的类注入普通数据
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package  com.tr.example.dao.impl;import  com.tr.example.dao.UserDao;public  class  UserDaoImpl  implements  UserDao      private  String name;     private  int  age;     public  void  setName (String name)           this .name = name;     }     public  void  setAge (int  age)           this .age = age;     }     public  UserDaoImpl ()           System.out.println("user dao impl created" );     }     public  void  init ()          System.out.println("init user dao" );     }     public  void  destroy ()          System.out.println("destroy user dao" );     }     @Override      public  void  save ()           System.out.println("hello from user dao" );         System.out.println(name+age);     } } 
配置xml 唯一不同的是 不用ref 改用 value
 
1 2 3 4 5 6 7 <bean  id ="userDao"  class ="com.tr.example.dao.impl.UserDaoImpl" >         <property  name ="name"  value ="tr" > </property >          <property  name ="age"  value ="24" > </property >  </bean > <bean  id ="userService"  class ="com.tr.example.service.impl.UserServiceImpl" >     <constructor-arg  name ="userDaoBkk"  ref ="userDao" > </constructor-arg >  </bean > 
然后是集合的注入,dao层的方法中新增以下三个,并且写好set方法
 
1 2 3 private  List<String> strList;private  Map<String, User> userMap;private  Properties properties;
新建一个User类,这里用了Lombok插件的@Data少写点get set方法
 
1 2 3 4 5 6 7 8 9 package  com.tr.example.domain;import  lombok.Data;@Data public  class  User      private  String name;     private  int  age; } 
然后配置下xml,把dao里面用到的都注入进去
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <bean  id ="userDao"  class ="com.tr.example.dao.impl.UserDaoImpl" >     <property  name ="strList" >          <list >              <value > aaa</value >              <value > bbb</value >              <value > ccc</value >          </list >      </property >      <property  name ="userMap" >          <map >              <entry  key ="u1"  value-ref ="user1" > </entry >              <entry  key ="u2"  value-ref ="user2" > </entry >          </map >      </property >      <property  name ="properties" >          <props >              <prop  key ="p1" > p1</prop >              <prop  key ="p2" > p2</prop >          </props >      </property >  </bean > <bean  id ="user1"  class ="com.tr.example.domain.User" >     <property  name ="age"  value ="24" />      <property  name ="name"  value ="tr" />  </bean > <bean  id ="user2"  class ="com.tr.example.domain.User" >     <property  name ="age"  value ="24" />      <property  name ="name"  value ="tzq" />  </bean > <bean  id ="userService"  class ="com.tr.example.service.impl.UserServiceImpl" >     <constructor-arg  name ="userDaoBkk"  ref ="userDao" > </constructor-arg >  </bean > 
引入其他配置文件(分模块) 
xml配置随便写写就容易过大,必须要分,不然很影响阅读
这里新增一个user的xml,然后主配置文件导入即可
 
1 <import  resource ="ApplicationConfig-user.xml" > /import>
Spring的api 
applicationContext :接口类型,代表应用上下文,可以用来获取容器的bean,他有三个实现类分别是
ClassPathXmlApplicationContext:从根路径(resources目录)加载xml配置 
FileSystemXmlApplicationContext:从磁盘路径读取配置 
AnnotationConfigApplicationContext:使用注解配置对象时候用的,用来读取注解 
 
 
getBean() 
getBean有两种方式使用,直接传递bean的id,另一种是通过传递字节码找context.getBean(UserService.class)这样的好处是不用强制转换但是要注意,如果配置了两个都是UserService的bean,只是id不同,那么这种情况只能用传id的方式
不过最新版本还可以用context.getBean(id,UserService.class)传id和类型,也不用强转
 
数据源 手动读取配置生成数据源对象 
数据源即连接池的作用:提高性能,能提前初始化资源
常见的数据源:DBCP,DRUID,C3P0
 
开发步骤
导入依赖 
创建对象 
设置参数,驱动,数据库地址,用户名密码等 
 
 
导入依赖
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <dependency >     <groupId > mysql</groupId >      <artifactId > mysql-connector-java</artifactId >      <version > 8.0.15</version >  </dependency > <dependency >     <groupId > c3p0</groupId >      <artifactId > c3p0</artifactId >      <version > 0.9.1.2</version >  </dependency > <dependency >     <groupId > com.alibaba</groupId >      <artifactId > druid</artifactId >      <version > 1.1.10</version >  </dependency > 
创建对象使用
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package  com.tr;import  com.alibaba.druid.pool.DruidDataSource;import  com.mchange.v2.c3p0.ComboPooledDataSource;import  org.junit.Test;import  java.sql.Connection;public  class  DatasourceTest          @Test      public  void  test0 () throws  Exception          DruidDataSource dataSource = new  DruidDataSource();         dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver" );         dataSource.setUrl("jdbc:mysql://192.168.10.85:3306/user?useSSL=false" );         dataSource.setUsername("root" );         dataSource.setPassword("0800" );         Connection connection = dataSource.getConnection();         System.out.println(connection);         connection.close();     }          @Test      public  void  test1 ()  throws  Exception         ComboPooledDataSource dataSource = new  ComboPooledDataSource();         dataSource.setDriverClass("com.mysql.cj.jdbc.Driver" );         dataSource.setJdbcUrl("jdbc:mysql://192.168.10.85:3306/user?useSSL=false" );         dataSource.setUser("root" );         dataSource.setPassword("0800" );         Connection connection = dataSource.getConnection();         System.out.println(connection);         connection.close();     } } 
在这两个测试用例中我们可以看到,数据库的信息配置都是手动配置上去的,后期如果修改,必须要找到相关代码配置,可以利用上节的bean注入里面的注入技术把配置信息注入
 
这里不建议xml存放配置,用peoperties文件,简单易懂
在resources目录建立 jdbc.properties
 
1 2 3 4 jdbc.driver =com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://192.168.10.85:3306/user?useSSL=false jdbc.username =root jdbc.password =0800 
代码里使用我们的配置
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public  void  test2 ()  throws  Exception         ResourceBundle bundle = ResourceBundle.getBundle("jdbc" );     String driver = bundle.getString("jdbc.driver" );     String url = bundle.getString("jdbc.url" );     String username = bundle.getString("jdbc.username" );     String password = bundle.getString("jdbc.password" );     ComboPooledDataSource dataSource = new  ComboPooledDataSource();     dataSource.setDriverClass(driver);     dataSource.setJdbcUrl(url);     dataSource.setUser(username);     dataSource.setPassword(password);     Connection connection = dataSource.getConnection();     System.out.println(connection);     connection.close(); } 
用spring配置数据源 
可以发现不论是druid还是c3p0都是set什么东西,这就可以利用spring帮我们导入这些配置
 
1 2 3 4 5 6 <bean  id ="dataSource"  class ="com.mchange.v2.c3p0.ComboPooledDataSource" >     <property  name ="driverClass"  value ="com.mysql.cj.jdbc.Driver" > </property >      <property  name ="jdbcUrl"  value ="jdbc:mysql://192.168.10.85:3306/user?useSSL=false" />      <property  name ="user"  value ="root" />      <property  name ="password"  value ="0800" />  </bean > 
来测试下这种方式获取bean链接
 
1 2 3 4 5 6 7 8 @Test public  void  test3 ()  throws  Exception    ApplicationContext context = new  ClassPathXmlApplicationContext("applicationConfig.xml" );     ComboPooledDataSource dataSource = context.getBean("dataSource" , ComboPooledDataSource.class);     Connection connection = dataSource.getConnection();     System.out.println(connection);     connection.close(); } 
Spring读取properties配置数据源 
上节的方式是在xml内配置数据源,对每个bean写死了配置,一般是通过读取properties文件的方式动态配置每个bean
 
引入命名空间,只需要改xml就够了
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0" encoding="UTF-8"?> <beans  xmlns ="http://www.springframework.org/schema/beans"  xmlns:context ="http://www.springframework.org/schema/context" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" ><context:property-placeholder  location ="classpath:jdbc.properties" /> <bean  id ="dataSource"  class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property  name ="driverClass"  value ="${jdbc.driver}" > </property > <property  name ="jdbcUrl"  value ="${jdbc.url}" /> <property  name ="user"  value ="${jdbc.username}" /> <property  name ="password"  value ="${jdbc.password}" /> </bean > </beans > 
Spring 注解 
刚刚的那一系列操作已经可以看到xml配置文件写起来真的非常麻烦,注解是代替xml配置的一种方式
 
Spring早期版本注解 
 原始注解,主要用来替代bean配置
 
@Component 使用在类上,用于实例化Bean 
@Controller 用在web层类 
@Service 用在service层 
@Repository 用在dao层 
@Autowired 用于引用注入 
@Qualified 接口@Autowired用于根据名称注入 
@Resource 相当于@Autowired+@Qualified 
@Value 注入普通属性 
@Scope 标记bean范围(单例多例) 
@PostConstruct bean的初始化方式指明 
@PreDestroy 指明销毁方法 
 
 
还是构建三层架构 controller,service,dao,分别写上方法,如同上节的bean学习,写好UserDao,UserService等方法。这不过这次不再使用xml配置文件了
 
1 2 3 4 5 6 7 8 9 10 11 12 13 package  com.tr.dao.impl;import  com.tr.dao.UserDao;import  org.springframework.stereotype.Component;@Component("userDao") public  class  UserDaoImpl  implements  UserDao      @Override      public  void  save ()           System.out.println("save running" );     } } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package  com.tr.service.impl;import  com.tr.dao.UserDao;import  com.tr.service.UserService;import  lombok.Data;import  org.springframework.beans.factory.annotation.Autowired;import  org.springframework.beans.factory.annotation.Qualifier;import  org.springframework.stereotype.Component;@Component("userService") public  class  UserServiceImpl  implements  UserService      @Autowired      @Qualifier("userDao")      private  UserDao userDao;     @Override      public  void  save ()           userDao.save();     } } 
还需要配置组件扫描