Spring
Spring概述
什么是Spring?
Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和
AOP(Aspect Oriented Programming:面向切面编程)为内核。
提供了展现层 SpringMVC和持久层 Spring JDBCTemplate以及业务层事务管理等众多的企业级应用技术,还能整
合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架
Spring的优点
方便解耦,简化开发
AOP 编程的支持
声明式事务的支持
方便程序的测试
SPring的结构体系
Spring的快速入门
Spring的使用步骤
①导入 Spring 开发的基本包坐标
②编写 Dao 接口和实现类
③创建 Spring 核心配置文件
④在 Spring 配置文件中配置 UserDaoImpl
⑤使用 Spring 的 API 获得 Bean 实例
导入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
创建一个接口和接口的实现类
创建applicationContext.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">
<bean id="dao1" class="Dao.impl.dao1impl"></bean>
</beans>
然后在使用Spring的API获取bean的实例
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
dao1 d= (dao1) applicationContext.getBean("dao1");
d.save();
配置文件
基本属性
id:Bean实例在Spring容器中的唯一标识
class:Bean的全限定名称
scope:指对象的作用范围,取值如下:
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
1)当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2)当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
Bean生命周期配置:
init-method:指定类中的初始化方法名称(创建时执行)
destroy-method:指定类中销毁方法名称(销毁前执行)
Bean实例化三种方式
无参构造方法
参考前面入门时使用的方法(默认方法)
静态工厂方法
使用静态方法来返回bean的实例
public class StaticFactory {
public static dao1 getdao1(){
return new dao1impl();
}
}
<bean id="dao1" class="Factory.StaticFactory"
factory-method="getdao1" />
工厂示例方法
使用工厂 的非静态方法来实现
public class Factory {
public dao1 getdao(){
return new dao1impl();
}
}
<bean id="factoryBean" class="Factory.Factory"/>
<bean id="dao1" factory-bean="factoryBean" factory-method="getdao"/>
Bean的依赖注入
概念
依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。
IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取
构造方法实现
<bean id="dao1" class="Dao.impl.dao1impl"></bean>
<bean id="service" class="service.impl.service1impl">
<constructor-arg name="d" ref="dao1"></constructor-arg>
</bean>
public class service1impl implements service1 {
private dao1 d;
public service1impl(dao1 d) {
this.d = d;
}
@Override
public void save() {
d.save();
}
}
set方法实现
<bean id="dao1" class="Dao.impl.dao1impl"></bean>
<bean id="service" class="service.impl.service1impl">
<property name="d" ref="dao1"/>
</bean>
public class service1impl implements service1 {
private dao1 d;
public void setD(dao1 d) {
this.d = d;
}
@Override
public void save() {
d.save();
}
}
引入其他配置文件
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载
Spring 配置数据源
可以将DataSource的创建权交由Spring容器去完成
DataSource有无参构造方法,而Spring默认就是通过无参构造方法实例化对象的
DataSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/day17"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
@Test
public void test6() throws SQLException {
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource)
applicationContext.getBean("dataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
抽取配置文件的方式
applicationContext.xml加载jdbc.properties配置文件获得连接信息。
首先,需要引入context命名空间和约束路径:
命名空间:xmlns:context=“http://www.springframework.org/schema/context”
约束路径:http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<?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">
然后加载外部的properties文件
<context:property-placeholder location="classpath:jdbc.properties"/>
Spring的注解开发
原始注解
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
Spring原始注解主要是替代的配置
注解 | 说明 |
---|---|
@Component | 使用在类上用于实例化Bean |
@Controller | 使用在web层类上用于实例化Bean |
@Service | 使用在service层类上用于实例化Bean |
@Repository | 使用在dao层类上用于实例化Bean |
@Autowired | 使用在字段上用于根据类型依赖注入 |
@Qualifier | 结合@Autowired一起使用用于根据名称进行依赖注入 |
@Resource | 相当于@Autowired+@Qualifier,按照名称进行注入 |
@Value | 注入普通属性 |
@Scope | 标注Bean的作用范围 |
@PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 |
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
<!--注解的组件扫描-->
<context:component-scan base-package="Dao"></context:component-scan>
<context:component-scan base-package="service"></context:component-scan>
新注解
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
非自定义的Bean的配置
加载properties文件的配置
组件扫描的配置
引入其他文件
注解 | 说明 |
---|---|
@Configuration | 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解 |
@ComponentScan | 用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package=“com.itheima”/>一样 |
@Bean | 使用在方法上,标注将该方法的返回值存储到 Spring 容器中 |
@PropertySource | 用于加载.properties 文件中的配置 |
@Import | 用于导入其他配置类 |
原始注解和新注解的联合使用
dao:
package Dao.impl;
import Dao.dao1;
import org.springframework.stereotype.Repository;
//<bean id="dao1" class="Dao.impl.dao1impl"></bean>
//@Component("dao1") @Component此注解可以用在所有的地方实例化bean
@Repository("dao1") //@Repository 专属于dao层使用
public class dao1impl implements dao1 {
@Override
public void save() {
System.out.println("spring学习");
}
}
service:
package service.impl;
import Dao.dao1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import service.service1;
// <bean id="service" class="service.impl.service1impl">
//@Component("service")
@Service("service")//专属于service层
public class service1impl implements service1 {
//<property name="d" ref="dao1"></property>
@Autowired
@Qualifier("dao1")
private dao1 d;
public void setD(dao1 d) {
this.d = d;
}
@Override
public void save() {
d.save();
}
}
核心配置类:
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration//标志该类是Spring的核心配置类
//<context:component-scan base-package="Dao"></context:component-scan>
//<context:component-scan base-package="service"></context:component-scan>
@ComponentScan({"Dao","service"})
//<import resource=""/>
@Import({DataSourceConfiguration.class})//引入
public class SpringCofiguration {
}
数据源配置类:
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
@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") //Spring会将当前方法的返回值以指定名称存储到Spring容器中
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}
测试:
@Test
public void test8() throws SQLException {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringCofiguration.class);
service1 service = (service1) applicationContext.getBean("service");
service.save();
DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
AOP
什么是aop?
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
aop的优点
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少重复代码,提高开发效率,并且便于维护
aop的底层实现
实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
aop的动态代理技术
djk
基于接口实现的动态代理
cglib
基于父类实现的动态代理
aop的相关结束及其术语
Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
相关术语
-
Target(目标对象):代理的目标对象
-
Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
-
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
-
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
-
Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
-
Aspect(切面):是切入点和通知(引介)的结合
-
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
aop的开发
注意事项
需要编写的内容
-
编写核心业务代码(目标类的目标方法)
-
编写切面类,切面类中有通知(增强功能方法)
-
在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
AOP 技术实现的内容
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
AOP 底层使用哪种代理方式
在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
aop开发入门
①导入 AOP 相关坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
②创建目标接口和目标类(内部有切点)
③创建切面类(内部有增强方法)
④将目标类和切面类的对象创建权交给 spring
⑤在 applicationContext.xml 中配置织入关系
导入命名空间:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--目标对象-->
<bean id="target" class="Aop.Target"></bean>
<!--切面对象-->
<bean id="myAspect" class="Aop.MyAspect"></bean>
<!--配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置、后置...)-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut="execution(public void Aop.Target.save())"/>
</aop:aspect>
</aop:config>
⑥测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test1() {
target.save();
}
}