零、这个知识重要吗?有什么用?
Spring是一切的基础。
一、Spring概述
(1.1)Spring是什么?
是一个轻量级开源的 JavaEE 框架,用来解决企业应用开发的复杂性。
- 轻量级:需要依赖的文件少,小,可以单独使用。
- 开源:源代码开放。
- 框架:让开发更加方便、简洁。
(1.2)Spring 两个核心:IOC 以及 AOP
- IOC(控制翻转):将创建对象的过程交给 Spring 进行管理。
- AOP(面向切面):可以不修改源代码的情况下,对代码功能进行增强。
(1.3)Spring 入门框架
(1)添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 2.Spring dao依赖-->
<!-- spring-jdbc包括了一些如jdbcTemplate的工具类-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 3.Spring web依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 4.Spring test依赖:方便做单元测试和集成测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- junit 依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
(2)创建对象,创建 spring-config.xml
文件,在配置文件中创建 bean 标签,将对象类加载到配置文件中。
(3)创建测试类,通过ClassPathXmlApplicationContext获取context对象,再获取对象。
public class UserTest {
@Test
public void test() {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user = context.getBean("user", User.class);
user.doThing();
}
}
二、IOC
(2.1)什么是IOC
IOC(控制反转)是一种设计原则,在对象创建时,通过一个调控系统将对象的引用传递给它,目的是减少耦合。
(2.2)IOC底层原理
使用到了 XML解析、工厂模式、反射。
IOC实现解耦合:XML配置文件配置对象,创建工厂类,通过反射创建对象。
(2.3)BeanFactory 接口
1、IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
2、Spring 提供 IOC 容器实现两种方式:(两个接口)
-
BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
特点:加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象 -
ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人
员进行使用
特点:加载配置文件时候就会把在配置文件对象进行创建
ApplicationContext 实现类
// 类路径加载
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
// 磁盘路径加载
FileSystemXmlApplicationContext context1 = new FileSystemXmlApplicationContext();
(2.4)Bean 作用域
Bean 作用域指的就是在 IOC 容器中,Bean 是单例还是多例的。
单例(默认):所有请求由一个对象处理。
多例:多个请求由多个对象进行处理。
在<bean>
标签中,修改 scope = "protorype"
可以将 Bean 修改成多例的,此时不再是加载配置文件时进行加载对象,而是在调用 getBean()
时,创建一个新的对象。
(2.5)FactoryBean
Spring 中有两种类型的 Bean ,普通Bean 以及 工厂Bean
-
普通Bean
- 定义的类型与实际返回的类型相同
-
工厂Bean
- 配置文件中定义的类型和实际返回的类型可以不同,目的是从Spring的角度实现创建不同的Bean对象。
- (1)实现
FactoryBean
接口 - (2)覆写方法
public class UserFactory implements FactoryBean<User> {
/**
* 如果是单例实例,那么创建的 Bean 会放入Spring容器的单实例缓存池中
* @return FactoryBean 创建的 Bean
* @throws Exception
*/
@Override
public User getObject() throws Exception {
return new User();
}
/**
* @return FactoryBean 创建 Bean 的类型
*/
@Override
public Class<?> getObjectType() {
return User.class;
}
/**
* 确定 FactoryBean 创建的 Bean 实例是否为单例的
* @return 是否为单例对象
*/
@Override
public boolean isSingleton() {
return false;
}
}
@Test
public void test() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
User bean = context.getBean("userFactory", User.class);
bean.user();
}
(2.6)Bean 的生命周期
(2.6.1)默认五步生命周期
- (1)无参构造创建
- (2)注入属性(set方法)
- (3)初始化(需要配置方法)
- (4)使用
- (5)销毁(手动,需要配置方法)
(2.6.2)加入后置处理器以后
- (1)无参构造创建
- (2) 注入属性(set方法)
- (3)后置处理器
- (4)初始化(需要配置方法)
- (5)后置处理器
- (6)使用
- (7)销毁(手动,需要配置方法)
(2.6.3)配置初始化、销毁方法
- 在
<bean>
标签中有 init-method 以及 destroy-method - 将Bean类中方法名赋值给它们就可以完成初始化、销毁方法的定义
- 注意的是销毁需要使用手动调用销毁 context.close()
(2.6.4)实现后置处理器
- 创建类实现
BeanPostProcessor
- 重新方法
before after
,添加后置处理器逻辑 - 在配置文件中进行配置,当配置文件配置了后置处理器后,会为全部的 Bean 对象添加后置处理器。
(2.7)Bean 管理
Bean管理指的是两个操作,Spring 创建对象,Spring 注入属性。
(2.7.1) 基于 XML 文件进行 Bean 管理
(2.7.1.1) 创建对象
在 Spring 配置文件中,通过 <Bean>
标签进行创建以及配置属性,创建时使用无参构造函数进行创建。
id
:唯一标识name
:唯一标识,相比于 ID ,可以添加特殊符号class
:对象所在的包类路径
(2.7.1.2)注入属性(DI依赖注入)
核心是 set 方法
在<Bean>
标签中,添加<property>
标签,底层会将属性名首字母大写,添加前缀set
调用对应的方法。
name
:属性名value
:属性值
P标签注入
本质还是使用 set 方法进行注入,使用命名空间简化了属性,在<Bean>
标签中,添加p:属性名=属性值
有参构造注入
使用<comstructor-arg>
标签调用对象的构造方法进行注入,要求拥有对应的构造方法。
name
:属性名value
:属性值
注入的属性为 null 或者含有特殊符号
null
<property name="args">
<null/>
</property>
特殊符号
<property name="args">
<value> <![CDATA[<<属性值>>]]> </value>
</property>
外部 Bean 的引用注入
—个Bean标签 A 引用了另外—个Bean标签 B,B是在A外进行注册的,那么A就称为引入外部Bean B
<property name="B" ref="对象B在Spring配置文件中的唯一标识">
内部Bean注入以及级联属性
—个Bean标签A引用了另外一个Bean标签B,B是在A内部进行注册的,那么A就称为引入内部 Bean
使用<property>
标签,在<property>
标签内进行<bean>
的注册,同时这个bean也能配置属性,就称为级联属性
集合注入,以及集合添加元素
XML 自动装配
自动装配指的是根据装配规则,Spring 自动将属性进行匹配注入,在需要自动装配的 Bean 中添加属性 autowire
,底层是调用了对应的 Set 方法。
ByName
:根据属性名匹配对应的 BeanByType
:根据属性类型匹配对应的 Bean
— XML外部属性文件
需要引入 Context 命名空间,使用<context:property-placeholder>
标签,它是一个单标签,引入外部文件,使用 ${key}
进行取值。
xmlns:context="http://www.springframework.org/schema/context"
location
:声明文件地址
<context:property-placeholder location="classpath:database.properties" />
<bean name="user" class="com.wuqiyong.pojo.User">
<property name="" value="${jdbc.username}"/>
</bean>
(2.7.2) 基于注解进行 Bean 管理
(2.7.2.1)创建对象
1、在对象上进行Bean的声明,告诉 Spring 哪些类需要进行创建,提供了多个注解帮助对象的创建,是规范。
@Component
普通 Bean@Service
Service 层@Controller
Controller 层@Repository
Dao 层
2、开启组件扫描
XML形式:
<context:component-scan base-package="com.wuqiyong"></context:component-scan>
配置扫描范围
设置扫描的范围
<context:component-scan base-package="com.wuqiyong" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
排除扫描的范围
<context:component-scan base-package="com.wuqiyong">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
(2.7.2.2)注入属性
@Autowired
用于注释属性,根据属性联系进行自动装配,内部封装了Set
方法。@Qualifier
配合@Autowired
使用,当注入属性有多个实现类时,使用@Qualifier(value = "类名")
指定实现类进行注入。@Resource
根据类型或者名称进行注入,当使用name
属性时,就根据名称进行注入,否则根据联系,是JavaX
提供的注解。@Value
注入普通联系的属性,使用value
属性进行赋值,会将它转换为注入属性的联系,如果不能转换,将抛出异常。
(2.8)注解类,获取外部文件,开启扫描
@Configuration
表示当前类为注解类@PropertySource
引入外部文件,使用${key}
进行取值@ComponentScan
开启组件扫描,使用basePackages
声明扫描位置
@PropertySource(value = "classpath:database.properties")
@Configuration
@ComponentScan(basePackages = {"com.wuqiyong"})
public class MyConfig {
使用 key 来获取外部文件的值
}
使用 AnnotationConfigApplicationContext(ConfigClass.class)
加载配置文件
三、AOP
(3.1)什么是AOP,起到什么作用,怎么实现的?
- AOP是面向切面编程,利用AOP可以使业务逻辑各个部分进行分离,使得耦合性降低。
- 将不属于业务逻辑的代码从业务中划分出来,即在不修改源代码的情况下,在主干功能里面添加新的功能。
- 是通过创建一个代理对象,在代理对象中添加逻辑代码从而起到增强的作用。
(3.2)AOP术语
连接点
在一个类中,哪些方法可以被增强,那么这个方法就称为连接点。切入点
在一个类中,实际被增强的方法称为切入点通知(增强)
对主体代码进行补充的逻辑代码部分称为通知(增强)
切面
指的是一个动作,把通知应用到切入点的过程称为切面
(3.3)AOP 动态代理的两种情况
当有接口时,使用 JDK 动态代理
创建接口实现类的代理对象,来进行增强方法。没有接口时,使用 CGLIB 动态代理
创建当前类的子类,通过子类的代理对象,来进行增强方法。
(3.4)使用 JDK 代理实现 AOP 操作
Proxy.newProxyInstance(arg0, arg1, arg2)
得到一个代理对象
方法参数解释:- 类加载器
- 需要增强方法所在类实现的接口
- 代理对象类,需要实现
InvocationHandler
里面写增强方法
public interface User {
void user();
}
class UserImpl implements User {
@Override
public void user() {
System.out.println("userImpl");
}
}
class UserPlus implements InvocationHandler {
private Object object;
public UserPlus(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object invoke = method.invoke(object, args);
System.out.println("after");
return invoke;
}
}
class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = new Class[]{User.class};
UserImpl user = new UserImpl();
User userPlus = (User) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserPlus(user));
userPlus.user();
}
}
(3.4)使用 Spring & AspectJ 实现 AOP操作
Spring 框架一般都是基于 AspectJ 实现 AOP 操作
AspectJ 是独立的AOP框架,将 spring 和 AspectJ 一起实现AOP操作
(3.4.1)切入表达式
切入表达式就是声明类中哪个方法需要进行增强
语法结构 execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]))
- 权限修饰符:
*
代表全部权限 - 返回联系:可以省略
- 类全路径 . 方法名:代表增强当前方法,
*
代表全部方法 - 参数列表:可以使用
..
省略
(3.4.2)实现 AOP 操作
1、添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2、代码实现
添加到配置文件中,开启 aspectj
代理
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
public class AspectJ {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Emp emp = context.getBean("emp", Emp.class);
emp.emp();
}
}
@Component
class Emp {
public void emp() {
System.out.println("Emp !!!");
}
}
@Component
@Aspect
class EmpPlus {
// 提取相同的切入点
@Pointcut(value = "execution(* com.wuqiyong.Review.Emp.emp(..))")
public void pointCut() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointCut()")
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "pointCut()")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "pointCut()")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "pointCut()")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws
Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
3、使用注解类实现
public class AspectJ {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectJConfig.class);
Emp emp = context.getBean("emp", Emp.class);
emp.emp();
}
}
@Component
class Emp {
public void emp() {
System.out.println("Emp !!!");
}
}
@ComponentScan(basePackages = {"com.wuqiyong.Review"})
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
class AspectJConfig {
}
@Component
@Aspect
class EmpPlus {
// 提取相同的切入点
@Pointcut(value = "execution(* com.wuqiyong.Review.Emp.emp(..))")
public void pointCut() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointCut()")
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "pointCut()")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "pointCut()")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "pointCut()")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws
Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
四、JDBCtemplate
JDBCemplate 是 Spring 框架对 JDBC 进行的封装
1、导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
2、配置数据库连接池
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"/>
</bean>
3、编写代码
update
增删改queryForObject
查询一个对象,包括特殊值查询,使用BeanPropertyRowMapper
封装自定义类。query
查询多个对象,使用BeanPropertyRowMapper
封装自定义类。
五、事务管理
事务是添加到 Service 层的,在 Spring 中有编程型事务(通过编程)以及声明型事务(通过配置文件),声明型事务又可以通过注解以及 XML 文件实现,底层使用了 AOP。
1、创建事务管理器
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
2、开启事务注解
- 引入名称空间 tx
- 开启事务注解
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager">
</tx:annotation-driven>
3、在 Service 类或方法上添加 @Transactional
-
@Transactional
添加到类上,该类所有方法开启事务,添加到方法上,当前方法开启事务。在
@Transactional
中有多个重要属性-
propagation
事务传播行为,即当多个方法进行调用时,有普通方法以及事务方法,那么应该怎么处理。REQUIRED
如果有事务在运行,那么当前方法就在这个事务内运行,否则,就创建一个新的事务,并在自己的事务内运行。
方法 A 调用 方法 B 当 A 有事务时,B 方法使用 A 方法中的事务 当 A 没有事务时,创建一个新的事务 A、B任意⼀个⽅法异常(默认是RuntimeException和Error)都会导致A、B的操作被回滚。
REQUIRED_NEW
当前方法必须启动新事务,并在自己的事务中内运行,如果有事务正在运行,应该将它挂起。
方法 A 调用 方法 B 如果 A 存在一个事务,那么会将 A 的事务挂起 新建一个属于 B 的事务,等 B 的事务执行完成以后才会执行 A 的事务 如果 A 发生异常,只会回滚 A 不会回滚 B 如果 B 发生异常 B 一定会进行回滚,抛出异常给 A ,如果 A catch 住了,那么 A 不会回滚,否则 A 也会回滚。
-
ioslation
事务隔离级别
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
timeout
超时时间
- 设置事务需要在一定的时间内进行提交,否则进行回滚,默认值为
-1
单位是秒。
readOnly
是否只读
false
增删查改true
只能查询
rollbackFor noRollbackFor
当出现哪些异常时回滚/不回滚
使用注解类实现银行转账功能
public class Transaction {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
MyService myService = context.getBean("myService", MyService.class);
myService.transfer();
}
}
@Service
@Transactional
class MyService {
@Autowired
Dao dao;
public void transfer() {
dao.transfer1();
System.out.println(1 / 0);
dao.transfer2();
}
}
@Repository
class Dao {
@Autowired
JdbcTemplate jdbcTemplate;
public void transfer1() {
String sql = "update user_table set balance = balance - 100 where user = ?";
jdbcTemplate.update(sql, "AA");
}
public void transfer2() {
String sql = "update user_table set balance = balance + 100 where user = ?";
jdbcTemplate.update(sql, "BB");
}
}
@Configuration
@ComponentScan(basePackages = {"com.wuqiyong"})
@EnableTransactionManagement
@PropertySource(value = "classpath:database.properties")
class TxConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriverClassName(driver);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
六、整合 log4j
1、引入依赖
2、创建 log4j2.xml
指导复习的问题
- 使用 JDK 代理实现 AOP 操作
- 使用 AspectJ 实现 AOP 操作
- 使用 JDBCtemplate 对数据库进行增删查(一个对象,多个对象,记录条数)改
- 使用事务管理实现转账功能