文章目录
1.Spring简介
Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制) 和 AOP(Aspect Oriented Programming:面向切面编程) 为内核。
Spring的优势:
- 方便解耦,简化开发,通过IOC容器可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度耦合
- 支持AOP,可通过它实现传统OOP不容易实现的功能
- 提供了众多企业级应用技术
- SpringMVC(展现层)
- Spring JDBCTemplate(持久层)
- 事务管理(业务层)
- 方便程序的测试
- 方便集成各种优秀的框架
- 降低JavaEE API的使用难度,Spring对其进行了封装
Spring体系结构
2.第一个Spring程序及其相关API
2.1 第一个Spring程序
1.导入spring坐标
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
创建一个Dao接口和实现类
//UserDao
public interface UserDao {
public void save();
}
//UserDaoImpl
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("UserDao is Running");
}
}
2.创建配置文件
<?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-3.0.xsd">
<!-- 配置UserDaoImpl-->
<bean id="userDao" class="com.kk.dao.impl.UserDaoImpl"/>
</beans>
3.测试
@Test
public void test(){
//这些类的具体含义下面会总结
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
测试结果
2.2 Spring相关API
- ApplicationContext:是一个接口,代表应用上下文,可通过其实例获得Beans容器中的Bean对象。它有三个实现类:
- ClassPathXmlApplicationContext:从类的根路径下加载配置文件
- FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
- AnnotationConfigApplicationContext:当使用注解配置容器对象时,需要使用此类来创建spring容器。它用来读取注解。
- applicationContext.getBean(name / Class< T>):当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转;当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。
3.配置文件详解_< bean/>
在配置文件中引入其他配置文件 : <import resource="xxx.xml/>"
3.1 bean标签
< bean id=" ",class=" ",{scope=" ", init_method=" ",destroy_method=" ",factory-bean=" ",factory-method=" "}>
- 将配置对象交由Spring创建,默认调用类中的无参构造,若没有无参构造则创建失败
- id=" ":Bean实例在Spring容器中的唯一标识,不能重复
- class=" ":Bean的全限定性名称
- scope=" ":指定对象的作用范围,取值如下:
- singleton:默认值,单例。加载配置文件时就会创建唯一一个实例;与容器共同创建、存活、销毁。
- prototype:多例。调用getBeam()方法时才会创建对象,因此可以创建多个实例;对象只要使用就一直存活、长时间不用会被GC回收
- 应用于WEB项目中 — request / session:Spring创建一个Bean对象,并将对象存入request / session域中;global session:应用在Portlet环境中(没有此环境则相当于session)
//测试scope:单例和多例
//首先,不更改配置文件,会发现两个对象地址一样,即为同一个对象;
//更改配置文件为:<bean id="userDao" class="com.kk.dao.impl.UserDaoImpl" scope="prototype"/>, 会发现两个对象的地址不一样
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
UserDao userDao1 = (UserDao) applicationContext.getBean("userDao");
System.out.println(userDao);
System.out.println(userDao1);
- 生命周期配置: init_method=" " 和 destroy_method=" " ,指定类中初始化方法和销毁方法的名称
在实现类中编写初始化和销毁方法
public class UserDaoImpl implements UserDao {
public UserDaoImpl(){
System.out.println("无参构造");
}
public void init() {
System.out.println("初始化");
}
public void destroy() {
System.out.println("销毁");
}
public void save() {
System.out.println("UserDao is Running");
}
}
在配置文件中指定名称
<bean id="userDao" class="com.kk.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"/>
<!--多例-->
<!-- <bean id="userDao" class="com.kk.dao.impl.UserDaoImpl" scope="prototype" init-method="init" destroy-method="destroy"/> -->
测试
@Test
public void test(){
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
((ClassPathXmlApplicationContext)applicationContext).close();
}
单例测试结果:与容器共同创建、存活、销毁
多例测试结果:对象只要使用就一直存活、长时间不用会被GC回收
- Bean实例化的三种方式:
- 无参构造实例化(默认方式):
< bean id="userDao" class="com.kk.dao.impl.UserDaoImpl">
- 工厂静态方法实例化:
<bean id="userDao" class="com.kk.dao.factory.StaticFactory" factory-method="getUserDao"/>
- 工厂实例方法实例化:
- 无参构造实例化(默认方式):
<bean id="factory" class="com.kk.dao.factory.DynamicFactory"/>
<bean id="userDao" factory-bean="factory" factory-method="getUserDao"/>
3.2 依赖注入
依赖注入(Dependency Injection): 是Spring框架核心IOC的具体实现。
通过控制反转,把对象的创建交给了Spring,即在进行业务层调用持久层方法时,Spring容器中会同时存在UserDao和UserService两个对象,而在业务层直接使用的只有UserService(没必要手动调用getBean()获得持久层实例),因此可以将UserDao实例注入到UserService实例中,只需要调用getBean()得到UserService实例即可。IOC解耦可以降低业务层和持久层之间的依赖关系。
注入的数据有三种类型:普通数据类型;引用;集合
3.2.1 引用注入
方式一:通过 set方法 和 P命名空间 注入
下面演示一个例子
业务层实现类
public class UserServiceImpl implements UserService {
private UserDao userDao;
//通过set方法注入依赖
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
//不注入依赖
// ApplicationContext applicationContext = new
// ClassPathXmlApplicationContext("applicationContext.xml");
// UserDao userDao = (UserDao) applicationContext.getBean("userDao");
// userDao.save();
//通过set方法注入依赖
userDao.save();
}
}
配置文件
<bean id="userDao" class="com.kk.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.kk.service.impl.UserServiceImpl">
<!--这里的name="setUserDao()方法中set后面的userDao(将首字母改为小写),并不是类中的属性名,而ref中的参数等于上面的id="userDao"-->
<property name="userDao" ref="userDao"/>
</bean>
<!-- 使用p命名空间,需要在此文件上方引入xmlns:p="http://www.springframework.org/schema/p"-->
<!-- <bean id="userService" class="com.kk.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>-->
测试
@Test
public void test(){
//测试依赖注入
//手动创建,此时会出现空指针,因为此实例并不是Spring容器创建的
// UserService userService = new UserServiceImpl();
// userService.save();
//使用bean标签让Spring创建
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.save();
}
方式二:构造方法注入
业务层实现类
public UserServiceImpl() {
}
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
配置文件
<bean id="userDao" class="com.kk.dao.impl.UserDaoImpl"/>
<!-- 构造方法注入-->
<bean id="userService" class="com.kk.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
</bean>
3.2.2 基本数据和集合注入
下面只使用set方法实现注入
在实现类中新增一些属性,并添加set方法和toString方法(这里不再粘贴)
public class UserDaoImpl implements UserDao {
private String username;
private List<String> strList;
private Map<String, User> userMap;
private Properties properties;
}
在配置文件中进行注入
<bean id="userDao" class="com.kk.dao.impl.UserDaoImpl">
<property name="username" value="kk"/>
<property name="strList">
<list>
<value>aaa</value>
<value>bbb</value>
</list>
</property>
<property name="userMap">
<map>
<!--ref代表引用-->
<entry key="1" value-ref="user1"/>
<entry key="2" value-ref="user2"/>
</map>
</property>
<property name="properties">
<props>
<prop key="1">p1</prop>
<prop key="2">p2</prop>
</props>
</property>
</bean>
<!-- 由于Map中的User为引用,所以仍需注入-->
<bean id="user1" class="com.kk.domain.User">
<property name="id" value="1"/>
</bean>
<bean id="user2" class="com.kk.domain.User">
<property name="id" value="2"/>
</bean>
测试结果
4.配置数据源
常见的数据源有(连接池):DBCP、CP30、BoneCP、Druid等。程序中会事先实例化数据源,初始化部分连接资源,要使用时从数据源中获取,使用完毕后将资源归还给数据源,以此来提高性能。下面来看看如何配置数据源:
1.导入数据源和数据库驱动的坐标
<!--数据源坐标-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!--数据库驱动坐标坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.24</version>
</dependency>
抽取数据库信息为.properties文件
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=ture&characterEncoding=UTF-8&serverTimezone=UTC
jdbc.username = root
jdbc.password = root
利用Spring容器注入数据库信息
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 注意头部信息必须要引入context命名空间和约束路径-->
<!-- 加载外部properties文件-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
测试
@Test
public void testC3P0() throws Exception {
//利用spring容器和注入创建
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
ComboPooledDataSource dataSource = (ComboPooledDataSource) applicationContext.getBean("dataSource");
Connection conn = dataSource.getConnection();
System.out.println(conn);
}
5.Spring注解
使用注解代替xml配置文件可以简化配置,提高开发效率
5.1 原始注解
原始注解主要用来代替< bean/>配置
- 用于在类上实例化Bean的注解:
- @Component:使用在类上
- @Controller:使用在web层
- @Service:使用在service层
- @Repository:使用在dao层
- 用于注入的注解
- @Autowired:使用在字段上,根据类型(从Spring容器中匹配Bean,若bean有多个则需要用下面的根据id的方式)进行依赖注入
- @Qualifier: 结合@Autowired使用,根据名称(id)进行依赖注入
- @Resource:相当于@Autowired+@Qualifier,根据名称(id)进行依赖注入
- @Value:注入普通属性
- @PostConstruct 和 @PreDestory,使用在方法上,标注该方法为Bean的初始化或销毁方法
首先,使用注解要先在配置文件中配置组件扫描,用来指定哪个包或子包下的Bean需要进行扫描
<!-- 注意头部信息必须要引入context命名空间和约束路径-->
<!-- 注解的组件扫描-->
<context:component-scan base-package="com.kk"/>
注解的使用
Dao层
//<bean id="userDao" class="com.UserDaoImpl">
//@Component("userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
//@Value("1")
@Value("${jdbc.driver}")
private String driver;
@Override
public void save() {
System.out.println(driver);
System.out.println("save running");
}
}
Service层
//@Component("userService")
@Repository("userService")
@Scope("singleton")
//@Scope("prototype")
public class UserServiceImpl implements UserService {
//相当于<property name="userDao" ref="userDao"/>
//使用注解进行依赖注入可以不用写set方法
//@Autowired
/*@Autowired
@Qualifier("userDao")*/
@Resource(name="userDao")
private UserDao userDao;
@Override
public void save() {
userDao.save();
}
}
5.2 新注解
- @Configuration:指定当前类是一个Spring的核心配置类。创建容器时会从该类上加载注解
- @ComponentScan:指定Spring在初始化容器时要扫描的包,等同于:< context:component-scan base-package=“com.kk”/>
- @Bean:使用在方法上,标注将该方法的返回值存到容器中
- @PropertySource:加载.properties文件中的配置
- @Import:导入其他配置类
使用新注解完全代替核心配置文件
//加载配置文件
//<context:property-placeholder location="classpath:db.properties"/>
@PropertySource("classpath:db.properties")
public class DbSourceConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")//会将该方法的返回值以此id(dataSource)存入到容器中
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}
//指定当前类是一个Spring的核心配置类
@Configuration
// <!-- 注解的组件扫描-->
// <context:component-scan base-package="com.kk"/>
@ComponentScan("com.kk")
//<import resource=""/>
@Import({DbSourceConfig.class})
public class SpringConfig {
}
测试
@Test
public void test(){
//注解代替配置文件,这里初始化使用AnnotationConfigApplicationContext类
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
}