Spring概述
「Spring介绍」
Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制,把创建对象过程交给Spring进行管理
) 和 AOP(Aspect Oriented Programming:面向切面编程,不修改源代码进行功能增强
) 为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。
如何在IDEA创建Spring项目可参考文章:juejin.cn/post/715988…
「Spring体系结构」
引入Spring IOC
「程序耦合」
我们在开发中,会写很多的类,而有些类之间不可避免的产生依赖关系,这种依赖关系称之为耦合。有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。如下示例代码:
// 客户的业务层实现类
public class CustomerServiceImpl implements ICustomerService {
private ICustomerDao customerDao = new CustomerDaoImpl();
}
上面的代码表示:业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种依赖关系就是我们可以通过优化代码解决的。
再比如下面的代码中,我们的类依赖了MySQL的具体驱动类,如果这时候更换了数据库品牌,我们需要改源码来修改数据库驱动。
public class JdbcDemo1 {
/**
* JDBC操作数据库的基本入门中存在什么问题?
* 导致驱动注册两次是个问题,但不是严重的。
* 严重的问题:是当前类和mysql的驱动类有很强的依赖关系.当我们没有驱动类的时候,连编译都不让。
*
* 我们在开发中,理想的状态应该是:编译时不依赖,运行时才依赖。
*/
public static void main(String[] args) throws Exception {
//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
...
}
}
「解决耦合的思路」
上面JDBC的代码,我们当时是通过反射来注册驱动的:
Class.forName("com.mysql.jdbc.Driver");
这时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包,依然可以编译。但是因为没有驱动类,所以不能运行。不过,此处也有个问题,就是我们反射类对象的全限定类名字符串是在java类中写死的,一旦要改还是要修改源码。解决这个问题也很简单,使用配置文件配置。
「工厂模式解耦」
在实际开发中我们可以把所有的dao和service和action对象使用配置文件配置起来,当启动服务器应用加载的时候,通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。
「IOC控制反转」
解耦思路的问题:
1)存去哪里
在应用加载时,创建一个Map,用于存放action,Service和dao对象。我们把这个map称之为容器。
2)什么是工厂
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。 原来我们在获取对象时,都是采用new的方式,是主动的。现在我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象,是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。它的作用只有一个:削减计算机程序的耦合。
「IOC解决程序耦合」
1、创建持久层接口和实现类
// 持久层接口
public interface CustomerDao {
void saveCustomer();
}
// 持久层实现类
public class CustomerDaoImpl implements CustomerDao {
@Override
public void saveCustomer() {
System.out.println("ICustomerDaoImpl--保存客户方法");
}
}
2、创建业务层接口和实现类
// 业务层接口
public interface CustomerService {
void saveCustomer();
}
// 业务层实现类
public class CustomerServiceImpl implements CustomerService {
// 此处有依赖关系
private CustomerDao customerDao = new CustomerDaoImpl();
@Override
public void saveCustomer() {
customerDao.saveCustomer();
}
}
3、配置 spring-config.xml
。把资源交给spring来管理,在配置文件中 配置service和dao
:
<?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">
<!--配置Customer相关资源-->
<bean id="customerDao" class="com.spring.customer.dao.CustomerDaoImpl" />
<bean id="customerService" class="com.spring.customer.service.CustomerServiceImpl" />
</beans>
4、测试配置。看下IOC是如何解决程序耦合问题的:
// 模拟表现层,调用
public class CustomerTest {
public static void main(String[] args) {
// 创建一个Spring的IOC容器对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 从IOC容器中获取Bean实例
CustomerService customerService = (CustomerService) context.getBean("customerService");
System.out.println(customerService);
customerService.saveCustomer();
// 从IOC容器中获取Bean实例
CustomerDao customerDao = (CustomerDao) context.getBean("customerDao");
System.out.println(customerDao);
customerDao.saveCustomer();
}
}
Spring基于XML的IOC细节
「工厂类结构图」
BeanFactory和ApplicationContext的区别:
- BeanFactory才是Spring容器中的顶层接口。ApplicationContext是它的子接口。
- BeanFactory和ApplicationContext还区别在创建对象的时间点不一样:
- ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。(立即加载思想)
- BeanFactory:什么时候使用(也就是调用)什么时候创建对象。(延迟加载思想)
ApplicationContext接口实现类:
- ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用这种)
- FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
// 创建一个Spring的IOC容器对象
ApplicationContext context = new
ClassPathXmlApplicationContext("spring-config.xml");
bean标签管理
bean管理指两部分:Spring创建对象、Spring注入属性
「bean标签」
bean标签的作用:用于配置对象让spring来创建的。默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。(最佳实践:类无参构造一定要写上)
【bean标签的属性】
- id:给对象在容器中提供一个唯一标识,用于获取对象
- class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
- scope:指定对象的作用范围
- singleton:默认值,单例
- prototype:多例的。(当Spring接管struts2的action创建,action必须配置此值)
- request:WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中。
- session:WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中。
- globalSession:WEB项目中,应用在Portlet环境。如果没有Portlet环境那么globalSession相当于session
- init-method:指定类中的初始化方法名称
- destroy-method:指定类中销毁方法名称
「Bean作用范围」
- 单例对象:scope="singleton"(默认为单例)
- 一个应用只有一个对象的实例。它的作用范围就是整个引用。
- 生命周期:
- 对象出生:当应用加载创建容器时(加载spring配置文件),对象就被创建了。
- 对象活着:只要容器在,对象一直活着。
- 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
- 多例对象:scope="prototype"
- 每次访问对象时(调用getBean方法),都会重新创建对象实例。
- 生命周期:
- 对象出生:当使用对象时,创建新的对象实例。
- 对象活着:只要对象在使用中,就一直活着。
- 对象死亡:当对象长时间不用时,被java的垃圾回收器回收了。
「实例化bean三种方式」
第一种:使用默认无参构造函数(使用最多)
在默认情况下,它会根据默认无参构造函数来创建类对象。如果bean中没有默认无参构造函数,将会创建失败。
<bean id="customerService" class="com.spring.customer.service.CustomerServiceImpl" />
第二种:spring管理静态工厂-使用静态工厂的方法创建对象
此种方式是:使用StaticFactory类中的静态方法createCustomerService创建对象,并存入spring容器
id属性:指定bean的id,用于从容器中获取
class属性:指定静态工厂的全限定类名
factory-method属性:指定生产对象的静态方法
// 模拟一个静态工厂,创建业务层实现类
public class StaticFactory {
public static CustomerService createCustomerService(){
return new CustomerServiceImpl();
}
}
<bean id="customerService" class="com.spring.customer.factory.StaticFactory" factory-method="createCustomerService" />
第三种:spring管理实例工厂-使用实例工厂的方法创建对象
此种方式是先把工厂的创建交给spring来管理。然后在使用工厂的bean来调用里面的方法。
factory-bean属性:用于指定实例工厂bean的id。
factory-method属性:用于指定实例工厂中创建对象的方法。
// 模拟一个实例工厂,创建业务层实现类
// 此工厂创建对象,必须现有工厂实例对象,再调用方法
public class InstanceFactory {
public CustomerService createCustomerService(){
return new CustomerServiceImpl();
}
}
<!--第3种实例化bean方式-->
<bean id="instanceFactory" class="com.spring.customer.factory.InstanceFactory"/>
<bean id="customerService"
factory-bean="instanceFactory"
factory-method="createCustomerService"/>
FactoryBean可以让定义的bean类型和返回类型不一样
public class MyBean implements FactoryBean<Course> {
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setName("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
「Bean生命周期」
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值和对其他bean引用(调用set方法)
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- bean可以使用了(对象获取到了)
- 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
Spring依赖注入
什么叫依赖注入:它是spring框架核心IOC的具体实现方式。简单的说,就是坐等框架把对象传入,而不用我们自己去获取(我们只管问框架要就可以了)
「构造函数注入」
顾名思义,就是使用类中的构造函数给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。
使用构造函数的方式,给service中的属性传值。要求:类中需要提供一个对应参数列表的构造函数。
constructor-arg属性:
- index:指定参数在构造函数参数列表的索引位置
- type:指定参数在构造函数中的数据类型
- name:指定参数在构造函数中的名称,用这个找给谁赋值
- value:它能赋的值是基本数据类型和String类型
- ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
public class CustomerServiceImpl2 implements CustomerService {
private String name;
private Integer age;
private Date birthday;
public CustomerServiceImpl2(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
@Override
public void saveCustomer() {
System.out.println("CustomerServiceImpl2{" + "name='" + name + ''' +
", age=" + age + ", birthday=" + birthday + '}');
}
}
<bean id="customerService2" class="com.spring.customer.service.CustomerServiceImpl2">
<constructor-arg name="name" value="测试依赖注入"/>
<constructor-arg name="age" value="2022"/>
<constructor-arg name="birthday" ref="now"/>
</bean>
<bean id="now" class="java.util.Date"/>
测试代码:
public class CustomerTest {
public static void main(String[] args) {
// 创建一个Spring的IOC容器对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 从IOC容器中获取Bean实例
CustomerService customerService = (CustomerService) context.getBean("customerService2");
System.out.println(customerService);
customerService.saveCustomer();
}
}
「★set()方法注入★」
顾名思义,就是在类中提供需要注入成员的set方法。通过配置文件给bean中的属性传值。
涉及的标签:
property标签属性
:
- name:找的是类中set方法后面的部分
- ref:给属性赋值是其他bean类型的
- value:给属性赋值是基本数据类型和string类型的
实际开发中,此种方式用的较多。
public class CustomerServiceImpl3 implements CustomerService{
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public void saveCustomer() {
System.out.println("CustomerServiceImpl2{" + "name='" + name + ''' +
", age=" + age + ", birthday=" + birthday + '}');
}
}
<bean id="customerService3" class="com.spring.customer.service.CustomerServiceImpl3">
<property name="name" value="测试依赖注入方式3"/>
<property name="age" value="2023"/>
<property name="birthday" ref="now"/>
</bean>
测试代码:
// 从IOC容器中获取Bean实例
CustomerService customerService = (CustomerService) context.getBean("customerService3");
System.out.println(customerService);
customerService.saveCustomer();
「使用p名称空间注入」
本质还是调用set()方法。了解即可
public class CustomerServiceImpl4 implements CustomerService {
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public void saveCustomer() {
System.out.println("CustomerServiceImpl2{" + "name='" + name + ''' +
", age=" + age + ", birthday=" + birthday + '}');
}
}
xmlns:p="http://www.springframework.org/schema/p"
<bean id="customerService4" class="com.spring.customer.service.CustomerServiceImpl4"
p:name="测试依赖注入方式4" p:age="2024" p:birthday-ref="now"/>
测试代码同上,bean获取customerService4
「注入其他类型」
null值
<property name="address">
<null />
</property>
属性值包含特殊符号
<property name="address">
<value><![CDATA[<<杭州>>]]></value>
</property>
「注入配置文件」
需要引入context名称空间
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/demo
jdbc.username=***
jdbc.password=***
在spring配置文件使用标签引入外部属性文件:
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
「注入集合属性」
给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。如下测试一下注入数组,List,Set,Map,Properties:
public class CustomerServiceImpl5 implements CustomerService {
private String[] myStr;
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
private Properties myProps;
public void setMyStr(String[] myStr) {
this.myStr = myStr;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
@Override
public void saveCustomer() {
System.out.println(Arrays.toString(myStr));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
System.out.println(myProps);
}
}
<bean id="customerService5" class="com.spring.customer.service.CustomerServiceImpl5">
<!-- 在注入集合数据时,只要结构相同,标签可以互换 -->
<!--给数组注入数据-->
<property name="myStr">
<array>
<value>myStr-AA</value>
<value>myStr-BB</value>
<value>myStr-CC</value>
</array>
</property>
<!--注入list集合数据-->
<property name="myList">
<list>
<value>myList-11</value>
<value>myList-22</value>
</list>
</property>
<!--注入set集合数据-->
<property name="mySet">
<set>
<value>mySet-aa</value>
<value>mySet-bb</value>
<value>mySet-cc</value>
</set>
</property>
<!--注入Map数据-->
<property name="myMap">
<map>
<entry key="map-A-Key" value="map-A-Value"/>
<entry key="map-B-Key">
<value>map-B-Value</value>
</entry>
</map>
</property>
<!--注入properties数据-->
<property name="myProps">
<props>
<prop key="myProps-xxx">myProps-xxx-XXX</prop>
<prop key="myProps-yyy">myProps-yyy-YYY</prop>
</props>
</property>
</bean>
测试代码同上,bean获取customerService5
测试结果:
com.spring.customer.service.CustomerServiceImpl5@7e2d773b
[myStr-AA, myStr-BB, myStr-CC]
[myList-11, myList-22]
[mySet-aa, mySet-bb, mySet-cc]
{map-A-Key=map-A-Value, map-B-Key=map-B-Value}
{myProps-yyy=myProps-yyy-YYY, myProps-xxx=myProps-xxx-XXX}
还可以使用util标签
提取出来集合然后使用:
<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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
...
<property name="bookList" ref="bookList"></property>
...
<util:list id="bookList">
<value>西游记</value>
<value>三国演义</value>
<value>水浒传</value>
<value>红楼梦</value>
</util:list>
「注入内部bean」
Emp类中有Dept属性,并有setDept()方法:
<bean id="emp" class="com.spr.emp.Emp">
<property name="ename" value="职员1"></property>
<property name="gender" value="female"></property>
<property name="dept">
<bean id="dept" class="com.spr.emp.Dept">
<property name="dname" value="技术中心"></property>
</bean>
</property>
</bean>
「级联赋值」
方法一:
<bean id="emp1" class="com.spr.emp.Emp">
<property name="ename" value="职员2"></property>
<property name="gender" value="male"></property>
<property name="dept" ref="dept1"></property>
</bean>
<bean id="dept1" class="com.spr.emp.Dept">
<property name="dname" value="测试部"></property>
</bean>
方法二:
private Dept dept;
public Dept getDept() { // 在Emp类中需要有Dept的get方法
return dept;
}
<bean id="dept1" class="com.spr.emp.Dept">
<property name="dname" value="财务部"></property>
</bean>
<bean id="emp2" class="com.spr.emp.Emp">
<property name="ename" value="职员3"></property>
<property name="gender" value="male"></property>
<property name="dept" ref="dept1"></property>
<property name="dept.dname" value="运营部"></property>
</bean>
最后输出的结果是运营部
「自动装配属性」
bean 标签属性autowire,配置自动装配
autowire 属性常用两个值:
- byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
- byType 根据属性类型注入
根据属性名称自动注入:
<bean id="emp" class="com.spring5.autowire.Emp" autowire="byName">
<!--<property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.spring5.autowire.Dept">
</bean>
根据属性类型自动注入:
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType">
<!--<property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.spring5.autowire.Dept">
</bean>
基于注解的IOC配置
第一步,导入约束(给配置文件导入约束)
<?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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
第二步,使用 @Component 配置管理的资源
// 业务层实现类
@Component(value = "customerService")
public class CustomerServiceImpl implements CustomerService {
@Override
public void saveCustomer() {
System.out.println("CustomerServiceImpl.saveCustomer()");
}
}
第三步,修改Spring配置文件,在spring的配置文件中开启spring对注解ioc的支持:(如果没有这一步,是无法执行的,显示“No bean named 'customerService'”)
多个包使用逗号隔开
<!--告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中-->
<context:component-scan base-package="com.spring.customer.service" />
「开启组件扫描细节配置」
只扫描带对应注解的类:
<!--示例1
use-default-filters="false" 表示现在不使用默filter,自己配置
filter context:include-filter 设置扫描哪些内容
-->
<context:component-scan base-package="com.code" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
不扫描带对应注解的类:
<!--示例2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
✍️常用注解✍️
「用于创建对象」
相当于:<bean id="" class="">
如: <bean id="customerService" class="com.spring.customer.service.CustomerServiceImpl"/>
|@Component|
作用:把资源让spring来管理。相当于在xml中配置一个bean。
属性:
value:指定bean的id
。如果不指定value属性,默认bean的id是当前类的类名,首字母小写。
@Component(value = "customerService")
public class CustomerServiceImpl implements CustomerService {}
|@Controller|
作用:一般用于表现层的注解
|@Service|
作用:一般用于业务层的注解
// 业务层实现类
@Service(value = "customerService")
public class CustomerServiceImpl implements CustomerService {
}
|@Repository|
作用:一般用于持久层的注解
// 持久层实现类
@Repository(value = "customerDao")
public class CustomerDaoImpl implements CustomerDao {
}
他们三个注解都是针对一个的衍生注解,作用及属性都是一模一样的,只不过是提供了更加明确的语义化。
细节:如果注解中有且只有一个属性要赋值时,且名称是value,value在赋值时可以不写。
「用于注入数据」
相当于:<property name="" ref=""> 和 <property name="" value="">
如:
<property name="age" value="24"></property>
<property name="birthday" ref="now"></property>
|@Autowired|
作用:自动按照类型注入
。当使用注解注入属性时,set方法可以省略。它只能注入其他bean类型。当有多个类型匹配时,使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功。找不到就报错。
@Autowired
private CustomerDao customerDao;
|@Qualifie|
作用:在自动按照类型注入的基础之上,再按照Bean的id注入
。它在给字段注入时不能独立使用,必须和@Autowire一起使用;但是给方法参数注入时,可以独立使用。
属性:value:指定bean的id。
@Autowired
@Qualifier(value = "customerDao")
private CustomerDao customerDao;
|@Resource|
作用:直接按照Bean的id注入
,它也只能注入其他bean类型。
可以根据类型注入,也可以根据名称注入
属性:name:指定bean的id。
@Resource // 根据类型进行注入
@Resource(name = "customerDao") // 根据名称进行注入
private CustomerDao customerDao;
|@Value|
作用:注入基本数据类型和String类型数据的。还可以读取文件配置
属性: value:用于指定值
@Value(value = "com.mysql.jdbc.Driver")
private String driver;
@Value(value = "19")
private int number;
「用于改变作用范围」
相当于:<bean id="" class="" scope="prototype|singleton|...">
|@Scope|
作用:指定bean的作用范围
属性:value:指定范围的值。取值:singleton、prototype、request、session、globalsession
@Scope(value = "singleton")
public class CustomerServiceImpl implements CustomerService {
}
「生命周期相关」
相当于:<bean id="" class="" init-method="" destroy-method=""/> (了解即可)
|@PostConstruct|
作用:用于指定初始化方法。
|@PreDestroy|
作用:用于指定销毁方法。
「注解&XML如何选择」
注解的优势:配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
XML的优势:修改时,不用改源码。不涉及重新编译和部署。
场景 | 基于XML配置 | 基于注解配置 |
---|---|---|
Bean定义 | <bean id="xxx" class="xxx" /> | @Component。衍生类:@Controller(表现层)@Service(业务层)@Repository(持久层) |
Bean名称 | 通过id或name指定 | @Component(value = "xxx") |
Bean注入 | <property name="xxx" value="xxx"/>,或者p命名空间 | @Autowired(按照类型)、@Qualifie(按照名称),或者 @Resource(Bean类型)、 @Value(基本类型) |
生命过程+Bean作用范围 | init-method、destory-method,范围scope属性 | @PostConstruct(初始化)、@PreDestroy(销毁)、@Scope(作用范围) |
适合场景 | Bean来自第三方 | Bean的实现类由自己开发 |
「常用注解实例」
Dao层接口和实现类:
public interface CustomerDao {
void saveCustomer();
}
@Repository(value = "customerDao")
public class CustomerDaoImpl implements CustomerDao{
@Override
public void saveCustomer() {
System.out.println("Dao层保存用户方法");
}
}
Service层接口和实现类:
public interface CustomerService {
void saveCustomer();
}
@Component(value = "customerService")
@Scope(value = "singleton")
public class CustomerServiceImpl implements CustomerService {
@Resource(name = "customerDao")
private CustomerDao customerDao = null;
@Value(value = "com.mysql.jdbc.Driver")
private String driver;
@Override
public void saveCustomer() {
System.out.println("这里是一些数据库信息:" + driver);
System.out.println("service层保存用户:");
customerDao.saveCustomer();
}
}
spring-config.xml配置文件:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.spdemo" />
</beans>
测试代码:
public class TestClient {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
CustomerService customerService = (CustomerService) context.getBean("customerService");
customerService.saveCustomer();
}
}
Spring纯注解配置
问题背景:
之所以我们现在离不开spring-config.xml配置文件,是因为我们有一句很关键的配置:
<context:component-scan base-package="com.spdemo" />
「注解配置扫描包」
@Configuration // 表明当前类是一个配置类
@ComponentScan(basePackages = "com.spdemo") //配置要扫描的包
public class SpringConfiguration {
}
测试类获取容器的方法也发生了变化:
public class TestClient {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
CustomerService customerService = (CustomerService) context.getBean("customerService");
customerService.saveCustomer();
}
}
「新注解说明」
|@Configuration|
作用:用于指定当前类是一个spring配置类
,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext
(有@Configuration注解的类.class)。
属性:value:用于指定配置类的字节码
|@ComponentScan|
作用:用于指定 spring在初始化容器时要扫描的包
。作用和在spring的xml配置文件中的:
<context:component-scan base-package="xxx"/>是一样的。
属性:basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。
@Configuration // 表明当前类是一个配置类
@ComponentScan(basePackages = "com.spdemo") //配置要扫描的包
public class SpringConfiguration {
}
|@PropertySource|
作用:用于加载.properties文件中的配置
。例如我们配置数据源时,可以把连接数据库的信息写到properties配置文件中,就可以使用此注解指定properties配置文件的位置。
属性:value[]:用于指定properties文件位置。如果是在类路径下,需要写上 classpath:xxx
jdbcConfig.properties配置:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/demo
jdbc.username=***
jdbc.password=***
配置加载文件:
@Configuration // 表明当前类是一个配置类
@ComponentScan(basePackages = "com.spdemo") //配置要扫描的包
@PropertySource(value = {"classpath:config/jdbcConfig.properties"})
public class SpringConfiguration {
}
备注:如果classpath路径错误,则会报错
|@Import|
作用:用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration注解。当然,写上也没问题。
属性:value[]:用于指定其他配置类的字节码。
@Configuration
@ComponentScan(basePackages = "xxx")
@Import({ Configuration_B.class})
public class Configuration_A {
}
@Configuration
@PropertySource("classpath:info.properties")
public class Configuration_B {
}
|@Bean|
作用:该注解只能写在方法上,表明使用此方法创建一个对象,并且放入spring容器。它就相当于我们之前在xml配置中的factory-bean和factory-method。
属性:name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。
@Bean(name = "datasource2")
public DataSource createDS() throws Exception {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("1234");
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql:///spring_ioc");
return comboPooledDataSource;
}
Spring整合Junit
【准备测试内容】
持久层接口和实现类:
// 持久层接口
public interface CustomerDao {
// 查询所有客户
List<Customer> findAllCustomer();
// 保存客户
void saveCustomer(Customer customer);
}
// 持久层实现类
public class CustomerDaoImpl implements CustomerDao{
@Override
public List<Customer> findAllCustomer() {
System.out.println("dao--查询所有用户成功");
return null;
}
@Override
public void saveCustomer(Customer customer) {
System.out.println("dao--保存用户成功");
}
}
业务层接口和实现类:
// 业务层接口
public interface CustomerService {
// 查询所有客户
List<Customer> findAllCustomer();
// 保存客户
void saveCustomer(Customer customer);
}
// 业务层实现类
public class CustomerServiceImpl implements CustomerService {
private CustomerDao customerDao;
public void setCustomerDao(CustomerDao customerDao) {
this.customerDao = customerDao;
}
@Override
public List<Customer> findAllCustomer() {
return customerDao.findAllCustomer();
}
@Override
public void saveCustomer(Customer customer) {
customerDao.saveCustomer(customer);
}
}
测试类:
public class CustomerTest {
private CustomerService customerService;
@Test
public void testFindAllCustomer(){
customerService.findAllCustomer();
}
@Test
public void testSaveCustomer(){
Customer customer = new Customer();
customer.setName("Spring-name");
customerService.saveCustomer(customer);
}
}
「使用XML配置」
1)xml文件中的配置
<?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">
<!-- 把资源交给spring来管理 -->
<bean id="customerDao" class="com.code.dao.CustomerDaoImpl"></bean>
<bean id="customerService" class="com.code.service.CustomerServiceImpl">
<property name="customerDao" ref="customerDao"></property>
</bean>
</beans>
2)使用@RunWith注解替换原有运行器
@RunWith(SpringJUnit4ClassRunner.class)
public class CustomerTest {
...
}
3)使用@ContextConfiguration指定spring配置文件的位置
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
public class CustomerTest {
...
}
4)使用@Autowired给测试类中的变量注入数据
@Autowired
private CustomerService customerService;
「使用纯注解配置」
1)把资源都用注解管理
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerDao customerDao;
...
}
@Repository("CustomerDao")
public class CustomerDaoImpl implements CustomerDao{
...
}
2)使用注解配置方式创建spring容器
@Configuration
@ComponentScan(basePackages = {"com.code"})
public class CustomerTest2 {
@Autowired
private CustomerService customerService;
...
}
3)使用RunWith注解和ContextConfiguration注解配置
@Configuration
@ComponentScan(basePackages = {"com.code"})
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {CustomerTest2.class})
public class CustomerTest2 {
...
}