目录
[TOC]
Spring IOC
什么是ioc(控制反转)
首先IOC是Inversion of Controller即控制反转 它并不是一种新的技术而是一种新的编设计思想。原来创建对象的话需要使用关键字new,需要程序主动去寻找能够让程序正常运行的类 ,从而造成了类与类之间有很强的依赖关系,代码不够灵活,修改一个类可能会涉及很多类的修改 ioc的出现把创建对象的主动权交给了第三方即ioc容器,程序需要什么对象,ioc就给你什么对象,程序从原来的主动寻找对象到现在被提供对象,原来的依赖关系就没有了,现在类与类之间都依赖于ioc容器,靠ioc容器来建立它们之间的关系
DI(依赖注入)
DI即Dependency Injection DI其实就是在程序运行期间动态地向某个对象提供它所需要的其他对象,而Spring正是使用反射来实现注入的
虽然通过反射创建对象的方式的效率没有使用new关键字直接创建对象高,但是降低了程序之间的耦合性,即解耦,它使得 程序之间松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活
IOCxml约束文档
1 |
|
创建工厂模式解耦
bean.properties文件
1 | accountService = cn.qingweico.service.impl.AccountServiceImpl |
bean.xml文件
1 | public class BeanFactoryImpl { |
根据bean的id获取想要的类
1 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); |
ApplicationContext的三个实现类
1 | ClassPathXmlApplicationContext //加载类路径下的配置文件 |
ioc核心容器的两大接口 ApplicationContext BeanFactory
1 | ApplicationContext在创建ioc核心容器时采用立即加载的方式,即读取完配置文件之后就会立即创建对象 |
三种创建bean对象的方式
使用默认构造函数创建
1
2
3<!--配置文件中除了id和class标签和属性外没有其他属性和标签外 默认使用无参构造函数创建bean对象.如果没有则无法创建对象-->
<bean id="accountService"class="cn.qingweico.service.impl.AccountServiceImpl"/>
<bean id="accountDao" class="cn.qingweico.dao.impl.AccountDaoImpl"/>使用普通工厂中的方法创建对象(使用类中的方法创建对象 并存入spring容器中)
1
2
3
4
5// 工厂类
public class Factory {
public AccountService getAccountService(){
return new AccountServiceImpl();
}1
2<bean id ="factory" class = "cn.qingweico.factory.Factory"/>
<bean id="accountService" factory-bean="factory" factory-method="getAccountService"/>使用普通工厂中的静态方法创建对象
1
2
3
4// 工厂类
public static AccountService getAccountService(){
return new AccountServiceImpl();
}1
<bean id="accountService" class = "cn.qingweico.factory.Factory" factory-method="getAccountService"/>
bean作用的范围
- singleton 单例(默认)
- prototype 多例对象
- request 作用于web请求范围
- session 作用于web的会话范围
- global-session 作用域集群环境的会话范围即全局会话
bean的生命周期
单例对象随着ioc容器的创建而创建随着容器的销毁而销毁(需要手动关闭ioc容器(使用close方法),销毁方法才会执行)
1
2
3
4
5ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
AccountService accountService = (AccountService)applicationContext.getBean("accountService");
accountService.saveAccount();
/*关闭后执行销毁方法*/
applicationContext.close();多例对象则是ioc容器创建时不会创建对象,当什么时候用到时才会创建对象,而当对象没有引用时则被gc回收
spring依赖注入
构造函数注入
1
2
3
4//构造函数
public AccountServiceImpl(String name,Date date){
System.out.println(name + "----" + date);
}1
2
3
4
5
6
7
8<bean id="accountService" class="cn.qingweico.service.impl.AccountServiceImpl">
<!-- 引用类型注入-->
<constructor-arg name="date" ref="date"/>
<!-- String类型注入-->
<constructor-arg name="name" value="姓名"/>
</bean>
<bean id="date" class = "java.util.Date"/>
</beans>set方法注入(需要有默认的无参构造函数)
1
2
3
4
5
6
7
8
9private String name;
private Date date;
public AccountServiceImpl(){}
public void setName(String name) {
this.name = name;
}
public void setData(Date date) {
this.date = date;
}1
2
3
4
5
6
7<bean id="accountService" class="cn.qingweico.service.impl.AccountServiceImpl">
<!-- String类型 -->
<property name="name" value="姓名"/> /
<!-- 引用类型 -->
<property name="data" ref = "date"/> //
</bean>
<bean id="date" class = "java.util.Date"/>复杂数据类型的注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25private String[] array;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void setArray(String[] arr) {
this.array = arr;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<!--具有相同结构的数据类型标签可以互换-->
<bean id="accountService" class="cn.qingweico.service.impl.AccountServiceImpl">
<property name="array">
<array>
<value>数组</value>
</array>
</property>
<property name="list">
<list>
<value>列表</value>
</list>
</property>
<property name="set">
<set>
<value>set集合</value>
</set>
</property>
<property name="map">
<map>
<entry key="001" value="map">
</entry>
</map>
</property>
<property name="properties">
<props>
<prop key="001">properties</prop>
</props>
</property>
</bean>
</beans>
IOC注解
spring注解约束文档
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
1 | <!--开启注解扫描--> |
@Component 用于把当前类注入Spring容器中(衍生以下三个注解,功能和作用一摸一样)
- @Controller 一般用在表现层
- @Service 一般用在业务层
- @Repository 一般用在持久层
1
2
3/*指定ioc容器bean的唯一id 默认是类名首字母小写 value可省略*/
public class AccountDaoImpl{}1
<beans id="accountDao" class="cn.qingweico.service.impl.AccountDaoImpl">
@Autowried 按照ioc容器中的bean数据类型注入 若数据类型相同则按照变量名称注入@Autowried不可以为静态成员变量注入
1
2
3
4
5
6
7
public class AccountDaoImpl implements AccountDao {
public void saveAccount() {
System.out.println("保存了账户111");
}
}1
2
3
4
5
6
7
public class AccountDaoImpl2 implements AccountDao {
public void saveAccount() {
System.out.println("保存了账户222");
}
}1
2
3
4
5
6
7
8
9
10
11
public class AccountServiceImpl implements AccountService {
// 若使用变量名称accountDao1 会输出 "保存了账户111"
// 若使用变量名称accountDao2 会输出 "保存的账户222"
private AccountDao accountDao;
public void saveAccount() {
accountDao.saveAccount();
}
}当使用
@Autowried
注入数据时,但此时容器中存在两个数据类型都为AccountDao的Bean,此时会报错,当数据类型相时,@Autowried
会根据变量的名称来匹配相应的Bean,匹配成功则注入@Qualifier(value = “”) 指定注入bean的id 不能单独使用,必须要与@Autowired一起使用
@Resource(name = “”) 直接注入bean的id 可以单独使用(name不能省略)
```properties
以上三种注解只能注入bean类型的数据 而基本类型的数据和String等则不能使用且集合类型的数据只能通过xml来配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
- @Scope( value = "") 改变scope的作用范围(可以为prototype singleton)
- @PostConstruct 用于指定初始化方法
- @PreDestroy 用于指定销毁方法
```xml
<!--部分注解所需要的依赖-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>1
2
3
4
public void init(){
System.out.println("初始化方法执行了。。。");
}1
2
3
4
public void destroy(){
System.out.println("销毁方法执行了。。。");
}1
<bean id="accountService" class="cn.qingweico.service.impl.AccountServiceImpl" init-method="init" destroy-method="destroy"/>
ioc自定义注解类
public class SpringConfiguration {}
1
2
3
4
5
- @Configuration 指定当前类是一个配置类 当该配置类作为AnnotationConfigApplicationContext对象创建的参数时 可以省略该注解
```java
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);@PropertySource(“classpath:JDBCResource.properties”) 指定properties文件的位置
/*可以使用注解注入数据源 spring的el表达 #{}*/ @Value("${driverClass}") private String driverClass; @Value("${url}") private String url; @Value("${user}") @Value("${url}") private String url; @Value("${password}") private String password;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- ```java
@Bean(name = "jdbcTemplate")
@Scope("prototype")
public JdbcTemplate createTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean(name = "dataSource")
public DataSource createDataSource() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl(url);
dataSource.setDriverClass(driverClass);
} catch (Exception e) {
throw new RuntimeException(e);
}
return dataSource;
}```properties
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/sys?useSSL=false&serverTimezone=UTC
user=root
password=9907121
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- @Import(JDBCConfiguration.class) 用于导入其他的配置类 有import的配置类是父类 而其他类都是子配置类
- @ComponentScan(basePackages = "cn.qingweico") basePackages和value的作用都是指定创建容器时所要扫描的包
- @Bean(name = "") 将当前方法的返回值存入到ioc容器中 name 表示bean的id 默认为方法名
### Spring整合junit
```xml
<!-- 导入依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
1 | //开启junit对spring的支持 |
1 | //基于注解的junit整合 |
1 | //基于xml的junit整合 |
Spring AOP
事务控制
自定义工具类ConnectionUtils TransactionManager实现事务控制
1 | // 连接的工具类 实现从当前数据源中获取一个连接 并实现和当前线程的绑定 |
1 | /*和事务管理相关的工具类 它实现了开始事务 提交事务 回滚事务 释放连接 */ |
动态代理
特点:字节码随用随创建,随用随加载
作用:在不修改源码的基础上对已有方法进行增强
基于接口的动态代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35/*
被代理类至少实现一个接口
使用Proxy类中的newIProxyInstance方法
newProxyInstance方法的三个参数是
ClassLoader 类加载器
Class[] 字节码数组
InvocationHandle 用于提供增强的代码
*/
public class Producer implements IProducer {
public static void main(final String[] args) {
final Producer producer = new Producer();
IProducer iProducer =
(IProducer)Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(),
//匿名内部类访问外部变量时 其要被final关键字修饰
new InvocationHandler() {
/**
* 作用 :执行被代理对象的任何接口方法都会经过该方法
* @param o 代理对象的引用
* @param method 当前执行的方法
* @param objects 当前执行方法所需要的参数
* @return 和被代理对象有相同的返回值
* @throws Throwable .
*/
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Object returnValue = null;
Float money = (Float)objects[0];
if("saleProduct".equals(method.getName())){
returnValue = method.invoke(producer,money*0.8f);
}
return returnValue;
}
});//返回Object类型
iProducer.saleProduct(10000f); //对Iproducer接口中saleProduct方法进行增强
}
}基于子类的动态代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/*
被代理类不能是最终类
使用Enhancer类中的create方法
create方法的参数
Class: 用于指定被代理对象的字节码
CallBack: 用于提供增强的代码
使用该接口的子接口实现类 MethodInterceptor
*/
public class Producer { //基于子类的动态代理不用实现接口
public static void main(String[] args) {
final Producer producer = new Producer();
Producer cglibProduce = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
Float money = (Float)objects[0];
if("saleProduct".equals(method.getName())){
returnValue = method.invoke(producer,money*0.8f);
}
return returnValue;
}
});
cglibProduce.saleProduct(12000f); //对Produce类中saleProduct方法进行增强
}
基于动态代理实现事务控制
1 | //用于创建Service的代理对象的工厂 |
AOP (Aspect Oriented Programmer 面向切面编程)
作用: 将程序中重复的代码抽取出来,在需要执行的时候使用动态代理技术,对在不修改源码的基础上对已有方法进行增强
优势:
- 减少重复代码
- 提高开发效率
- 维护方便
1 | AOP相关术语 |
AOP约束(包括xml和注解以及aop)
1 |
|
Spring中基于xml的AOP配置实现事务控制
把通知Bean交给Spring来管理
使用aop:congfig标签来表明配置的开始
1
<aop:config></aop:config>
使用aop:aspect标签表明配置切面
- id 给切面提供一个唯一标志
- ref 指定通知类bean的id
在aop:aspect标签内部使用对应的标签来配置通知的类型
- aop:before 表明配置前置通知
- aop:after-returning 表明配置后置通知
- aop:after-throwing 表明是异常通知
- aop:after 表明最终通知
- method 用于指定类中的那个方法是前置通知
- pointcut 用于指定切入点表达式 该表达式的含义是对业务层的哪些方法进行增强
切入表达式的写法
execution(表达式)
访问修饰符 返回值 包名.包名…类名.方法名(参数列表)
1 | 通配符 |
1 | <!--通知类--> |
spring中的环绕通知
1 | <!--导入所需要的依赖--> |
环绕通知spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式
1 | public Object aroundLogger(ProceedingJoinPoint proceedingJoinPoint){ |
Spring中基于注解AOP配置
1 | <!--开启注解扫描--> |
@Aspect 表明当前类是一个切面类
/*配置切入点表达式*/ @Pointcut("execution(* cn.qingweico.service.impl.*.*(..))") private void pointcut(){ }
1
2
3
- ```java
//配置前置通知@AfterReturning("pointcut()") //配置后置通知
1
2
3
- ```java
//配置异常通知@After("pointcut()") //配置最终通知
1
2
3
- ```java
//配置环绕通知基于注解的四个通知其最终通知会在后置通知之前执行 而使用环绕通知则不会出现这样的问题
```java
/使用注解时当使用四个通知时则实现不了事务的控制 此时应该使用环绕通知才能实现事务的控制/
@Around(“pointcut()”)
public Object around(ProceedingJoinPoint proceedingJoinPoint){
Object returnValue = null;
try {
Object[] args = proceedingJoinPoint.getArgs();
this.begin();
returnValue = proceedingJoinPoint.proceed();
this.commit();
}catch(Throwable t){ //使用Throwable来拦截异常
this.rollback();
throw new RuntimeException(t);
}finally{
this.release();
}
return returnValue;
}1
2
3
4
5
6
7
8
9
10
### jdbcTemplate
```xml
<!--导入jdbc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
jdbcTemplate的crud语句
1 | <!--配置jdbcTemplatebean文件时应该将数据源作为函数参数传入--> |
1 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("template.xml"); |
jdbcTemplate继承父类jdbcDaoSupport实现Dao层减少重复代码 只能在基于XML配置时使用
1 | public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { |
Spring中基于xml的声明式事务控制(Sping自带的事务控制)
声明式事务控制的约束
1 |
|
导入依赖
1 | <dependency> |
1 | <!--配置事务管理器 spring提供--> |
1 | <!--配置事务的通知--> |
1 | <!--配置aop中的切入点表达式--> |
配置事务的属性
- 在事务通知aop:advice标签的内部
- isolation: 用于指定事务的隔离级别 默认是default 式数据库默认的隔离级别
- propagation: 用于指定事务的传播行为 默认是required 表示一定有事务的增删改 如果查询的话使用supports
- read-only; 用于指定事务是否只读 只有查询方法才能设置为true 默认值为false
- timeout: 用于事务的超时时间 默认是-1 表示永不超时 如果指定了单位 则以秒为单位
- rollback-for: 用于指定一个异常 当产生该异常时 事务回滚 产生其他异常时 事务不会回滚 如果没有默认值 表示任何异常都回滚
- no-rollback-for: 与rollback-for相反 如果没有默认值 表示任何异常都回滚
Spring中基于注解的声明式事务控制(Sping自带的事务控制)
1 | <!-- 开启注解扫描 --> |
@Transactional 对需要事务支持的类使用该注解(该注解也可以标注在类上)
1 | // 可以对单个查询方法进行配置 |
1 | <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> |
Spring中使用纯注解的声明式事务控制
1 | // 开启spring对注解的支持 |
1 | // 用于创建事务管理器对象并加入ioc容器中 |