Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
可以把Spring看作是一个一站式的整合轻量级的开源框架,可以整合各层之间的其它框架.
◆JAVA EE应该更加容易使用。
◆面向接口编程,而不是针对类编程。Spring将使用接口的复杂度降低到零。(面向接口编程有哪些复杂度?)
◆代码应该易于测试。Spring框架会帮助你,使代码的测试更加简单。
◆JavaBean提供了应用程序配置的最好方法。
◆在Java中,已检查异常(Checked exception)被过度使用。框架不应该迫使你捕获不能恢复的异常。
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
IOC 就是把创建对象的控制权交给Spring来管理,我们只要向容器提出需求,容器就会按需求提供相应的对象,这就叫"控制反转"
DI(依赖注入) 多个对象之间会存在相应关系,我们把其它对象作为属性值传递给其它的对象做为其内部的一部分.(设置对象之间的关联关系)
对象之间产生的一种关联关系
强耦合:就是在编译器就产生了关联关系
硬编码
不利于维护和扩展
低耦合:就是在运行期间让对象之间产生关联
使用反射来创建对来
对象的完整限定名放到配置文件中
解决了硬编码问题,有利于程序后期的维护和扩展
Spring IOC 容器就能帮们解决以上的问题
下载地址:https://spring.io/
导入Spring jar包
配置Spring核心配置文件(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-4.3.xsd">
<!--给Spring容器配置Bean-->
<bean id="student" class="com.hwua.entity.Student"></bean>
</beans>
?
编写测试类
package com.hwua.test;
?
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hwua.entity.Student;
public class SpringTest {
使用反射去创建对象,默认会调用无参构造函数(重点)
<bean id="userService" class="com.hwua.service.impl.UserServiceImpl"></bean>
使用普通工厂来创建对象
package com.hwua.factory;
import com.hwua.dao.UserDao;
import com.hwua.dao.impl.UserDaoJDBCImpl;
/**
* 普通工厂类
*
* @author Administrator
*
*/
public class BeanFactroy1 {
public UserDao createUserDao() {
return new UserDaoJDBCImpl();
}
}
?
<bean id="factory1" class="com.hwua.factory.BeanFactroy1"></bean> <bean id="userDaoJDBC" factory-bean="factory1" factory-method="createUserDao"></bean>
?
- 使用静态工厂来创建bean对象
?
```java
package com.hwua.factory;
import com.hwua.dao.UserDao;
import com.hwua.dao.impl.UserDaoJDBCImpl;
import com.hwua.dao.impl.UserDaoMyBatisImpl;
/**
* 静态工厂类
*
* @author Administrator
*
*/
public class BeanFactroy2 {
public static UserDao createUserDao() {
return new UserDaoMyBatisImpl();
}
}
<bean id="userDaoMyBatis" class="com.hwua.factory.BeanFactroy2" factory-method="createUserDao"></bean>
构造器注入(会用)
<!--可以直接给value属性赋值,但必须值的顺序按照构造函数参数的顺序,否则课程存在出错 -->
<constructor-arg value="1"></constructor-arg>
<constructor-arg value="陈豪"></constructor-arg>
<constructor-arg value="20"></constructor-arg>
<!--可以指定index属性来控制参数赋值的顺序 -->
<constructor-arg value="陈豪" index="1"></constructor-arg>
<constructor-arg value="1" index="0"></constructor-arg>
<constructor-arg value="20" index="2"></constructor-arg>
?
<!--我们可以使用index,name,type来控制构造器注入参数的顺序-->
<constructor-arg value="20" type="java.lang.Integer" index="2"></constructor-arg>
<constructor-arg value="陈豪" type="java.lang.String"></constructor-arg>
<constructor-arg value="1" type="java.lang.Integer" name="age"></constructor-arg>
?
记住只要记住使用name,根据参数的名来来进行赋值
<constructor-arg value="1" name="age"></constructor-arg>
set注入方式(重点)
基本类型数据的注入使用value属性来注入,自定义的引用数据类型使用ref来给属性注入
set注入必须要有五参数构造函数,内部通过反射机制调用无参构造函数来创建对象的.
属性必须要有对象的get和set方法
<!--给Spring容器配置Bean -->
<bean id="student" class="com.hwua.entity.Student">
<property name="id" value="1"></property>
<property name="name" value="zhangsan"></property>
<property name="age" value="30"></property>
<property name="car" ref="car"></property>
</bean>
<bean id="car" class="com.hwua.entity.Car">
<property name="brand" value="奔驰"></property>
<property name="price" value="300000"></property>
</bean>
P命名空间注入方式(Spring2.5以后才有)
引入P命名空间
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
编写注入语法
<!--构造器注入--> <bean id="student" class="com.hwua.entity.Student" c:_0="1" c:_1="张三" c:_2="30" c:_3-ref="car"></bean> <bean id="car" class="com.hwua.entity.Car" c:_0="奔驰" c:_1="300000"></bean> <!--属性注入--> <bean id="car" class="com.hwua.entity.Car" p:brand="奔驰" p:price="300000"></bean> <bean id="student" class="com.hwua.entity.Student" p:id="1" p:name="张三" p:age="30" p:car-ref="car"></bean>
spel(Spring Expression Language)注入方式
<bean id="car" class="com.hwua.entity.Car"> <property name="brand" value="#{‘奔驰‘}"></property> <property name="price" value="#{300000}"></property> </bean> <bean id="student" class="com.hwua.entity.Student"> <property name="id" value="#{1+1}"></property> <property name="name" value="#{‘zhangsan‘}"></property> <property name="age" value="#{30}"></property> <property name="car" value="#{car}"></property> </bean>
复杂类型数据的注入
package com.hwua.entity; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class ComplexData { private List<String> list; private Set<String> set; private Map<String, String> map; private Properties properties; private Car[] arr; public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } public Set<String> getSet() { return set; } public void setSet(Set<String> set) { this.set = set; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } public Car[] getArr() { return arr; } public void setArr(Car[] arr) { this.arr = arr; } }
```java <?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" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <bean id="car1" class="com.hwua.entity.Car"> <property name="brand"> <!-- <value><![CDATA[1<2]]></value> --> <null/> </property> <property name="price" value="40000"></property> </bean> <bean id="car2" class="com.hwua.entity.Car"> <property name="brand" value="宝马"></property> <property name="price" value="30000"></property> </bean> <bean id="car3" class="com.hwua.entity.Car"> <property name="brand" value="奥迪"></property> <property name="price" value="25000"></property> </bean> <bean id="data" class="com.hwua.entity.ComplexData"> <!--List类型的属性注入 --> <property name="list"> <list> <value>chenhao1</value> <value>chenhao2</value> <value>chenhao3</value> <value>chenhao4</value> </list> </property> <!--Set类型的属性注入 --> <property name="set"> <set> <value>chenhao4</value> <value>chenhao5</value> <value>chenhao6</value> <value>chenhao7</value> </set> </property> <!--Map类型的属性注入 --> <property name="map"> <map> <entry key="jack" value="老王"></entry> <entry key="frank" value="老陈"></entry> <entry key="mary" value="老李"></entry> </map> </property> <!--Properties类型数据的属性注入 --> <property name="properties"> <props> <prop key="frank">老王</prop> <prop key="jack">老陈</prop> <prop key="mary">老李</prop> </props> </property> <!--数组类型数据的属性注入 --> <property name="arr"> <array> <ref bean="car1"/> <ref bean="car2"/> <ref bean="car3"/> </array> </property> </bean> </beans> ```
- ClassPathXmlApplicationContext :加载类路径下的spring配置文件,相对定位 - FileSystemXmlApplicationContext:加载文件系统路径下的Spring配置文件,绝对定位(不推荐) - AnnotationConfigApplicationContext:加载注解配置类,也就是说以后的配置信息写到一个配置类中,不用xml文件来作为配置文件(后续会讲)
- singleton 在容器中始终只产生一个对象 - prototype 在向容器获取多次bean的时候,会产生多个bean对象 - request 在request作用域中存入一个bean对象 - session 在session作用域中存入一个bean对象
单例时对象的生命周期
创建时机: 容器创建的时候对象创建
销毁时机: 容器关闭的时候对象就销毁
多例时对象的生命周期
创建时机: 当要从容器中获取指定对象的时候,就会由Spring来创建一个对象,延迟创建
销毁时机:当对象长时间不被使用,或对象的引用为null的时候,有JVM的垃圾回收器来执行回收
BeanFactory 是ApplicationContext接口的父接口,ApplicationContext功能更强大
ApplicationContext 容器 在创建的时候就会对配置文件的scope为singleton的对象进行统一创建,而BeanFactory容器在创建的时候不会对作用范围为singleton的对象进行统一创建,而是在获取对象的时候在创建.
@Component
@Service
@Controller
@Resposity
以上的功能完全一样,只是注解名字不同,在分层架构中可读性更好
@AutoWired 自动注入,默认是根据类型来注入的,也就是说它会从容器中找唯一相关类型的对象注入进来
@Primary 当有两个相同类型对象的时候,以@Primary修饰的对象来进行注入
@Qualifier 往往 配合@AutoWired 来使用,先以类型来注入,当类型相同的时候再按@Qualifier指定的名字来注入,不能单独使用的
@Autowired @Qualifier("userDaoMyBatis")
@Resource 等同于@Autowired 和 @Qualifier的组合,假设不给name属性,默认按类型来注入,给name属性值,那就按名字来注入
以上几个注解都只能注入bean数据类型的数据,基础类型的数据(8大基本数据类型和String),是不能用上述注解来注入的
@Scope("prototype") 注解 来声明对象在容器中作用范围,主要值sigleton和prototype
@PostConstruct 修饰的方法就是对象创建时要执行的方法
@PreDestroy 修饰的方法是在对象销毁的时候要执行的方法
@Value 注解给属性赋值基本类型的数据
Spring 整合 DBUtils 和 C3P0
<?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-4.3.xsd"> <!--用来读取属性文件 --> <context:property-placeholder location="classpath:db.properties"/> <!--配置业务层对象 --> <bean id="userService" class="com.hwua.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <!--配置一个DAO对象 --> <bean id="userDao" class="com.hwua.dao.impl.UserDaoImpl"> <property name="qr" ref="runner"></property> </bean> <!--配置QueryRunner对象 --> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> <!--配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans>
Spring 整合 JDBCTemplate 和 DRUID
<?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-4.3.xsd"> <!--用来读取属性文件 --> <context:property-placeholder location="classpath:db.properties"/> <!--配置业务层对象 --> <bean id="userService" class="com.hwua.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <!--配置一个DAO对象 --> <bean id="userDao" class="com.hwua.dao.impl.UserDaoImpl"> <property name="jTemplate" ref="jdbcTemplate"></property> </bean> <!--配置jdbcTemplate对象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置DRUID数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans>
Spring 整合junit
我们一般通过main方法来运行程序
单元测试类其实它底层继承了一个main方法,当运行的时候会调用运行那些有@Test修饰的方法
我们可以使用Spring整合Junit来简化单元测试的复杂度
1. 导入Spring-test jar包
package com.hwua.test; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.hwua.config.SpringConfig; import com.hwua.pojo.Student; import com.hwua.service.StudentService; import com.hwua.service.impl.StudentServiceImpl; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes= {SpringConfig.class}) public class UserTest { @Autowired private StudentService stuService = null; @Test public void testSaveUser() throws Exception { // 创建SpringIOC容器,context 就是IOC容器的引用 Student stu = new Student(); stu.setSname("hello"); stu.setSsex("男"); stu.setSno("no110"); stuService.saveStudent(stu); } @Test public void testQueryUserById() throws Exception { // 创建SpringIOC容器,context 就是IOC容器的引用 Student stu = stuService.findStudentById(1); System.out.println(stu); } @Test public void testQueryAllUsers() throws Exception { List<Student> stuList = stuService.findAllStudents(); System.out.println(stuList); } @Test public void testUpdateStudentById() throws Exception { // 创建SpringIOC容器,context 就是IOC容器的引用 Student stu = stuService.findStudentById(1); stu.setSname("玛丽"); stu.setSsex("女"); stuService.UpdateStudent(stu); } @Test public void testDeleteStudentById() throws Exception { // 创建SpringIOC容器,context 就是IOC容器的引用 stuService.deleteStudentById(9); } }
Spring 整合 MyBatis 和 DRUID
导入MyBatis的jar包,mybatis-spring整合包,spring-jdbc 事务处理包
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hwua</groupId> <artifactId>Spring_Day3_Spring_MyBatis</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.5.RELEASE</version> </dependency> </dependencies> </project>
applicationContext配置文件
第一种配置方式,有spring配置文件和mybatis配置文件 <?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-4.3.xsd"> <!--读取类路径下的配置db.properties文件 --> <context:property-placeholder location="classpath:db.properties"/> <context:component-scan base-package="com.hwua.service"></context:component-scan> <bean id="userService" class="com.hwua.service.impl.UserServiceImpl"></bean> <!--Spring整合MyBatis --> <bean id="sqlSessionFatory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property> </bean> <!--配置扫描Mapper接口类型的数据,把接口类型的对象放入Spring容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--配置包扫描 --> <property name="basePackage" value="com.hwua.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFatory"></property> </bean> <!--配置DRUID数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans> MyBatis配置文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--配置一个别名包扫描 --> <typeAliases> <package name="com.hwua.pojo"/> </typeAliases> <mappers> <package name="com.hwua.mapper"/> </mappers> </configuration> 第二种:省略MyBatis配置文件的方式 <?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-4.3.xsd"> <!--读取类路径下的配置db.properties文件 --> <context:property-placeholder location="classpath:db.properties"/> <bean id="userService" class="com.hwua.service.impl.UserServiceImpl"></bean> <!--Spring整合MyBatis --> <bean id="sqlSessionFatory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="typeAliasesPackage" value="com.hwua.pojo"></property> <property name="mapperLocations" value="classpath:com/hwua/mapper/*.xml"></property> </bean> <!--配置扫描Mapper接口类型的数据,把接口类型的对象放入Spring容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--配置包扫描 --> <property name="basePackage" value="com.hwua.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFatory"></property> </bean> <!--配置DRUID数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans>
@Configuration: 表明这个类是配置类
@ComponentScan:组件包扫描
@Bean:修饰方法,会执行工厂方法,并把返回的对象放到IOC容器中
@PropertySource 读取属性文件的注解
@Import 导入其它的配置类
总结:实际开发中是xml和注解混合配置,自定义的bean对象推荐使用注解,因为简单方便,而第三方或框架自带的类推荐使用xml来进行配置
主配置类 package com.hwua.config; import java.beans.PropertyVetoException; import javax.annotation.Resource; import javax.sql.DataSource; import org.apache.commons.dbutils.QueryRunner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * 配置类的作用就等同于配置文件 * @author Administrator * */ @Configuration @ComponentScan(basePackages= {"com.hwua.service","com.hwua.dao"}) @PropertySource("classpath:db.properties") @Import(JDBCConfiguration.class)//导入其它配置类 public class SpringConfiguration { //使用工厂方法去创建一个QueryRunner对象,工厂方法中的参数会自动从IOC容器中找到相应的对象进行自动注入 @Bean("runner") public QueryRunner createQueryRunner(@Autowired DataSource ds) { return new QueryRunner(ds); } } 从配置类 package com.hwua.config; import java.beans.PropertyVetoException; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.mchange.v2.c3p0.ComboPooledDataSource; @Configuration public class JDBCConfiguration { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String jdbcUrl; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean("dataSource") public DataSource createDataSource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass(driver); dataSource.setJdbcUrl(jdbcUrl); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } }
实现转账案例
发现案例中的问题:
一个业务方法中的多个功能都是在自己的连接对象上来单独开启事务的,所以多个独立的dao不能使用同一个事务来进行处理,导致转账数据不一致.
解决案例中的问题
ThreadLocal :用来给当前线程上绑定连接对象,一个请求用的就是一个线程,那么只要多个dao从线程上获取绑定的连接对象就能进行统一的事务处理.
动态代理 : 作用:在类上没有修改其源码的情况下,通过代理对象来对其功能进行增强.
jdk动态代理:对实现接口的对象来创建代理类对象
cglib动态代理:对没有实现接口的对象,通过对其创建子类对象来实现代理
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
日志记录,性能统计,安全控制,事务处理,异常处理等等。
作用: 在运行期间,在不修改源码的前提下对已有方法进行功能增强
优势:代码的复用,提高开发效率维护方便
joinPoint(连接点):目标对象,所有可以被增强的方法。
Advice(通知/增强):具体增强功能的类
PointCut(切入点):目标对象的方法,将要被增强的具体的方法。
Introduction(引入):声明某个方法或字段。(不用)
Target(目标对象):被代理的对象,也就是要被增强功能的哪个对象
AOP 代理(AOp Proxy) Spring 帮我们创建的代理类对象
Weaving(织入):将通知应用到切入点的过程。
Aspect(切面):切入点+通知的组合。
前置通知[Before advice] 在业务方法执行之前执行的增强功能
后置通知[After returning advice]:在业务方法执行之后执行的增强功能
异常通知[After throwing advice]:在业务方法报错时要执行的增强功能
最终通知[After (finally) advice]:在业务方法执行后,不管有没有异常,最终都要执行的增强功能
环绕通知[Around advice]:环绕通知围绕在切入点前后,比如一个方法调用的前后。这是最强大的通知类型,可以手动通过代码来控制通知的类型.
把通知类对象放到Spring容器中
使用aop:config标签来开始aop的配置
使用aop:aspect标签来配置切面
id 属性随意给个名字
ref: 引用的是通知类对象的id
使用aop:xxx标签来配置通知类型
method 指的是通知类对象中具体执行的增强方法
pointcut 指定具体的切入点,要编写切入点表达式,使用切入点表达式,必须要导入AspectJ的jar包
语法: [访问修饰符] 返回类型 包名.包名...类名.方法名(参数1,参数2) - 返回类型可以使用通配符*,代表返回任意类型 - 包名可以使用通配符*, 包名随意*.*.* *.*.. ..代表任意的子包 - 类名可以使用通配符*,代表任意类 - 方法名可以使用通配符,代表任意方法 - 参数可以使用.. 代表任意多个参数(0到多) void com.hwua.service.impl.StudentServiceImpl.saveStudent() 常规写法: * com.hwua.service.impl.StudentServiceImpl.*(..) <!--开始配置aop --> <aop:config> <!--配置全局切入点,这个切入点可以被能当前所有切面中的通知来使用 --> <aop:pointcut expression="execution( * com.hwua.service.impl.*.*(..))" id="p1" /> <!--配置一个切面 --> <aop:aspect id="logAdvise" ref="log"> <aop:before method="logBefore" pointcut-ref="p1" /> <aop:after-returning method="logAfterReturning" pointcut-ref="p1" /> <aop:after-throwing method="logAfterThrowing" pointcut-ref="p1" /> <aop:after method="logAfter" pointcut-ref="p1" /> <!--配置局部切入点,这个切入点只能给当前切面中的通知来使用 --> <!-- <aop:pointcut expression="execution( * com.hwua.service.impl.*.*(..))" id="p1"/> --> </aop:aspect> </aop:config>
aop:before 配置前置通知
aop:after-returning 配置后置通知
aop:after-throwing 配置异常通知
aop:after 配置最终通知
aop:around 配置环绕通知:通过手动编写代码来实现业务的功能加强.就是可以手动来进行设置
配置了环绕通知后,在执行的时候默认不执行业务方法,Spring框架为我们提供了一个接口:ProceedingJoinPoint,当通知中的方法被执行的时候,Spring会把传过来ProceedingJoinPoint类型的一个对象,里面包含了业务方法的相关信息
public Object around(ProceedingJoinPoint pjp) { Object res = null; Object[] args = pjp.getArgs();// 获取业务方法中的参数列表 try { logBefore(); res = pjp.proceed(args);// 手动的去指定业务方法 logAfterReturning(); } catch (Throwable e) { logAfterThrowing(); e.printStackTrace(); } finally { logAfter(); } // 执行业务方法 return res; }
package com.hwua.utils; import java.sql.SQLException; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 事务管理器 简单的说就要要对业务方法增强的事务功能 * * @author Administrator * */ @Component("txManager") @Aspect//切面类 public class TXManager { @Pointcut("execution(* com.hwua.service.impl.AccountServiceImpl.*(..))") private void p1() { } @Autowired private ConnectionUtils connectionUtils = null; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } //开启事务 @Before("p1()") public void beginTransAction() { System.out.println("前置通知"); try { connectionUtils.getConnection().setAutoCommit(false);//开启事务 } catch (SQLException e) { e.printStackTrace(); } } //提交事务 @AfterReturning("p1()") public void commit() { System.out.println("后置通知"); try { connectionUtils.getConnection().commit();//提交事务 } catch (SQLException e) { e.printStackTrace(); } } //回滚事务 @AfterThrowing("p1()") public void rollback() { System.out.println("异常通知"); try { connectionUtils.getConnection().rollback();//回滚事务 } catch (SQLException e) { e.printStackTrace(); } } //释放连接 @After("p1()") public void release() { System.out.println("最终通知"); try { connectionUtils.getConnection().close();//关闭连接 connectionUtils.release();//把连接对象从当前线程解绑 } catch (SQLException e) { e.printStackTrace(); } } }
注意点:使用注解配置AOP会出现执行顺序的问题,先执行前置通知,最终通知,(后置通知或异常通知)
使用注解配置AOP我们推荐使用配置环绕通知.
<!--开启AOP注解配置的支持 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.hwua.utils; import java.sql.SQLException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 事务管理器 简单的说就要要对业务方法增强的事务功能 * * @author Administrator * */ @Component("txManager") @Aspect//切面类 public class TXManager { @Pointcut("execution(* com.hwua.service.impl.AccountServiceImpl.*(..))") private void p1() { } @Autowired private ConnectionUtils connectionUtils = null; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } //开启事务 /*@Before("p1()")*/ public void beginTransAction() { System.out.println("前置通知"); try { connectionUtils.getConnection().setAutoCommit(false);//开启事务 } catch (SQLException e) { e.printStackTrace(); } } //提交事务 /*@AfterReturning("p1()")*/ public void commit() { System.out.println("后置通知"); try { connectionUtils.getConnection().commit();//提交事务 } catch (SQLException e) { e.printStackTrace(); } } //回滚事务 /*@AfterThrowing("p1()")*/ public void rollback() { System.out.println("异常通知"); try { connectionUtils.getConnection().rollback();//回滚事务 } catch (SQLException e) { e.printStackTrace(); } } //释放连接 /*@After("p1()")*/ public void release() { System.out.println("最终通知"); try { connectionUtils.getConnection().close();;//关闭连接 connectionUtils.release();//把连接对象从当前线程解绑 } catch (SQLException e) { e.printStackTrace(); } } @Around("p1()") public Object around(ProceedingJoinPoint pjp) { Object obj=null; Object[] args = pjp.getArgs();//获取业务方法的参数 try { beginTransAction(); obj=pjp.proceed(args);//业务方法 commit(); } catch (Throwable e) { rollback(); e.printStackTrace(); }finally { release(); } return obj; } }
原子性:强调事务操作的单元是一个不可分割的整体.
一致性:事务操作前后数据要保持一致
隔离性:一个事务在执行的过程中,不应该受其它事务的影响
持久性:事务一旦提交,数据就永久持久化到数据库中,不能回滚
脏读:一个事务读取到另一个事务未提交的数据
不可重复读:一个事务读取到另一个已提交事务的update数据,导致多次读取数据产生不一致
幻读:一个事务读取到另一个已提交事务的insert数据,导致多次读取数据产生不一致.
读未提交(Read Uncommited):脏读,不可重复读,幻读,都避免不了
读已提交(Read committed):解决脏读,(不可重复读,幻读解决不了)
可重复读(Repeatable read):解决脏读,不可重复读,(幻读解决不了)
串行化(serializable):解决上述三个问题,效率比较低,会导致一个失去就等待另一个事务处理完毕.
1.查看数据库的隔离级别: select @@transaction_isolation; 2.设置数据库隔离级别: set session transaction isolation level 隔离级别
Spring 内部已经帮我们设计好了一个事务管理对象,我们只要配置这个事务管理器对象就能帮业务方法实现实现事务的管理.
导入jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring-vesion}</version> </dependency>
配置spring自带的事务管理器对象
<!--配置事务管理器对象 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
配置一个事务通知
<tx:advice id="txAdvise" transaction-manager="transactionManager"> </tx:advice>
配置切面
<aop:config> <!--配置切入点 --> <aop:pointcut expression="execution(* com.hwua.service.impl.AccountServiceImpl.*(..))" id="p1"/> <!--把通知切入到指定的切入点 --> <aop:advisor advice-ref="txAdvise" pointcut-ref="p1"/> </aop:config>
对事务通知进行属性的设置
isolation: 设置事务隔离级别 propagation: 事务的传播行为 - REQUIRED 必须要有事务,针对增删改的操作 - SUPPORTS 如果当前有事务则加入,如果没有则不用事务。 read-only="true" 代表查询 rollback-for: 指定碰到相应的异常就回滚事务,不配默认就是碰到异常就回滚 no-rollback-for: 指定碰到相应的异常不回滚事务,不配默认就是碰到异常就回滚 timeout:不设置就代表默认-1永不超时,设置数字单位是秒 <!--配置一个事务通知--> <tx:advice id="txAdvise" transaction-manager="transactionManager"> <!--配置事务的属性 --> <tx:attributes> <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" /> <tx:method name="query*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice>
开启事务的注解支持
<!--开启事务注解的支持 --> <tx:annotation-driven/>
在指定方法上设置事务处理@Transactional
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
课堂练习: 修改银行转账代码:
Spring 整合 jdbcTemplate实现声明式事务事务处理
Spring 整合 MyBatis来实现声明式事务处理
下载spring整合web应用程序的jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.5.RELEASE</version> </dependency>
我们去注册一个监听器去监听Servlet应用加载,只要监听到加载,我们就应该加载配置文件来创建容器
<!--配置全局参数 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
Servlet对象时在Servlet容器中,而我们创建的业务层,数据访问层对象都放在了IOC容器中,也就是不同容器间不能直接访问.我们可以使用一个工具类来得到IOC容器的引用
ApplicationContext context=null; @Override public void init() throws ServletException { //创建了IOC容器的引用 context=WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); } //取IOC容器中bean对象 UserService us = (UserSerice)context.getBean("userService");
原文:https://www.cnblogs.com/pinghu-gary/p/11257923.html