Spring
一、引言
Spring概念: 是轻量级的javaEE解决方案,众多优秀设计模式的组合
代码侵入性小——轻量级的JavaEE解决方案
设计模式概念: 特定的代码解决特定的问题
二、工厂模式
- 好处: 解耦和
- 原理 : 读配置文件,获得类的全限命名,通过反射创建对象.
- Spring 默认创建的对象为单列。开发中创建一次对象的情况居多。
2.1 Set注入
- 原理: 底层通过调用该类中的set方法 进行属性赋值
- 注意: 使用set注入时,需要给该类提供对应的set方法
2.1.1 基本数据类型和Sring 的注入
<bean id="user" class="com.baizhi.entity.User" scope="prototype">
//默认为单例(简单对象) singleton scope 属性
<property name="id">
<value>1</value>
</property>
<property name="name">
<value>张三</value>
</property>
<property name="password">
<value>123456</value>
</property>
<!--数组-->
<property name="aarey">
<array>
<value>张三</value>
<value>李四</value>
<value>老王</value>
</array>
</property>
<!--list集合-->
<property name="list">
<list>
<value>oooo</value>
<value>ppppppp</value>
</list>
</property>
<property name="set">
<set>
<value>aaaaaaaaaa</value>
<value>bbbbbbbb</value>
</set>
</property>
<!--MAp集合-->
<property name="hashMap">
<map>
<entry key="1">
<value>sssadad</value>
</entry>
<entry key="2">
<value>adaddafffa</value>
</entry>
</map>
</property>
<!--properces-->
<property name="properties">
<props>
<prop key="a">aaaaaaa</prop>
<prop key="b">bbbbbbbbbbbbbb</prop>
</props>
</property>
</bean>
public class User {
private int id;
private String name;
private String password;
private String[] aarey;
private List<String> list;
private Set<String> set;
private Map<Integer,String> hashMap;
private Properties properties;
public User() {
}
public User(int id, String name, String password, String[] aarey, List<String> list, Set<String> set, Map<Integer, String> hashMap, Properties properties) {
this.id = id;
this.name = name;
this.password = password;
this.aarey = aarey;
this.list = list;
this.set = set;
this.hashMap = hashMap;
this.properties = properties;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String[] getAarey() {
return aarey;
}
public void setAarey(String[] aarey) {
this.aarey = aarey;
}
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<Integer, String> getHashMap() {
return hashMap;
}
public void setHashMap(Map<Integer, String> hashMap) {
this.hashMap = hashMap;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", aarey=" + Arrays.toString(aarey) +
", list=" + list +
", set=" + set +
", hashMap=" + hashMap +
", properties=" + properties +
'}';
}
public class test_all {
private ClassPathXmlApplicationContext AC = new ClassPathXmlApplicationContext("/applicationContext.xml");
@Test
public void test2(){
User user =(User) AC.getBean("user");
System.out.println("----------"+user);//默认为单例(简单对象)
}
}
//-User{id=1, name='张三', password='123456', aarey=[张三, 李四, 老王], list=[oooo, ppppppp], set=[aaaaaaaaaa, bbbbbbbb], hashMap={1=sssadad, 2=adaddafffa}, properties={b=bbbbbbbbbbbbbb, a=aaaaaaa}}
2.1.2 Spring 自定义类型进行注入
2.2 构造注入
- 原理: 底层通过调用有参构造 为对应的属性进行赋值操作
- 注意:提供有参构造
<bean id="acount" class="com.baizhi.entity.Acount">
<constructor-arg>
<value>6</value>
</constructor-arg>
<constructor-arg>
<value>zhangsan</value>
</constructor-arg>
<constructor-arg>
<value>123</value>
</constructor-arg>
<constructor-arg>
<value>123</value>
</constructor-arg>
</bean>
//获取工厂
private ClassPathXmlApplicationContext AC = new ClassPathXmlApplicationContext("/applicationContext.xml");
@Test
public void test5(){
//构造注入
Acount acount =(Acount) AC.getBean("acount");
System.out.println("-------"+acount);
}
//Acount{id=6, name='zhangsan', password='123', balance=123.0}
2.3 自动注入
- 应用:只应用于自定义类型的注入 autowire=“byType” 根据属性类型进行注入
public class Student {
private int code;
private String name;
private int age;
....set get toString +构造;
}
// 注入book
private Book books;
<!--自定义对象注入的三种方式 autowire 自动扫描注入已有的类型 可bytype-->
<bean id="student" class="com.baizhi.entity.Student" autowire="byName">
<property name="code">
<value>22</value>
</property>
<property name="name">
<value>qwqw</value>
</property>
<property name="age">
<value>23</value>
</property>
<!--
<property name="books" ref="books">
</property>-->
</bean>
Student student = (Student)AC.getBean("student");
System.out.println("-------"+student);
System.out.println("-------"+student.getBooks());
Student{code=22, name='qwqw', age=23, books=Book{id=1, name='qqqq', price='2232'}}
Book{id=1, name='qqqq', price='2232'}
2.4 IOC 反转控制(Inversion Of Control)
- 控制:给对象中属性进行赋值
- 反转:把给对象属性赋值的权利 由代码反转到配置文件中
2.5 DI 依赖注入 (Dependency Injection)
- 当一个类型需要另一个类型时,就意味这两个类型存在依赖关系。我们就可以把一个类型作为另一个类型的属性进行注入
三、 FactoryBean技术
- 作用: 创建复杂对象
- 复杂对象: 不能通过new的方式创建的对象
- 获取的对象不是该类的对象,而是该类创建的复杂对象。
- Connection connection1 = (Connection)AC.getBean("&connection"); 此写法可以获取到该类的对象。
3.1 数据库连接对象的创建
<?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-3.2.xsd">
<!--名字空间-->
<context:property-placeholder location="classpath:/jdbc.properties"/>
<!--复杂対象的创建 连接对象-->
<bean id="connection" class="com.baizhi.factoryBean.MyConnection">
<property name="driver">
<value>${driverClassName}</value>
</property>
<property name="url">
<value>${url}</value>
</property>
<property name="username">
<value>${name}</value>
</property>
<property name="password">
<value>${password}</value>
</property>
</bean>
</beans>
driverClassName=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:xe
name=XX
password=XX
public class MyConnection implements FactoryBean<Connection> {
private String driver;
private String url;
private String username;
private String password;
//<editor-fold desc="set/get">
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
//</editor-fold>
//书写 创建复杂对象的步骤
@Override
public Connection getObject() throws Exception {
//创建Connection对象的步骤
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
// 返回创建的复杂对象的类型
@Override
public Class<?> getObjectType() {
return Connection.class;
}
//控制复杂对象的创建次数,根据创建复杂对象的特点
// 设置返回值 连接对象返回false 每一创建一个 比较安全
@Override
public boolean isSingleton() {
/*
* false: 不单例 每次都会创建新的对象
* true: 单例 应用只会创建一个对象
* */
return false;
}
3.2 SqlSessionFactory 的创建
- src\main\resources\mybatis-config.xml
<?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>
<!-- 读取配置文件 -->
<properties resource="jdbc.properties" />
<!--数据库连接相关的参数 -->
<environments default="oracle">
<environment id="oracle">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<!-- mapper 文件的注册 -->
<mappers>
</mappers>
</configuration>
public class MySqlSessionFactory implements FactoryBean<SqlSessionFactory> {
//书写创建SqlSessionFactory的步骤
@Override
public SqlSessionFactory getObject() throws Exception {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
return sqlSessionFactory;
}
//返回复杂对象的类型
@Override
public Class<?> getObjectType() {
return SqlSessionFactory.class;
}
// 创建复杂的对象的次数
@Override
public boolean isSingleton() {
return true;
}
}
//test
SqlSessionFactory sqlSessionFactory =(SqlSessionFactory) AC.getBean("sqlSessionFactory");
System.out.println("---------"+sqlSessionFactory);
// SqlSession sqlSession = sqlSessionFactory.openSession();
// sqlSession.rollback();
// sqlSession.commit();
// sqlSession.close();
3.3 Spring创建对象的生命周期
- 什么时候被创建?
- 什么时候被销毁?
Spring工厂被创建则对象被创建 这种创建对象的方式属于饿汉创建 效率高
Spring工厂被销毁 则对象被销毁
private ClassPathXmlApplicationContext AC = new ClassPathXmlApplicationContext("/applicationContext.xml");
AC.close();
3.4 配置信息参数化
- 把spring配置文件中经常需要修改的内容抽取到小配置文件中
- 1.准备小配置文件 jdbc.properties
driverClassName=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:xe
name=hr
password=hr
- 2.把小配置文件读取到spring的配置文件中
四、Aop(Aspect Oriented Programming ) 面向切面编程
- 名词
原始类: 负责核心功能的类
原始方法: 原始类中的方法
代理类: 给原始类的原始方法添加额外功能的类
静态代理本质: 把额外功能拆分到代理类中,原始类只负责核心功能,有利于代码的维护
缺点: 1. 类过载
2. 额外功能代码冗余
4.1 .静态代理设计模式(static Proxy)
4.1.1 书写原始类
public interface UserService {
//注册
public int register(String username);
}
//原始类
public class UserServiceImpl implements UserService {
@Override
public int register(String username) {
System.out.println("我是带有一个参数的注册方法");
return 666;
}
}
4.1.2 在Spring的配置文件中,配置的Bean相关信息
<?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: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="userService" class="com.baizhi.fiels.dynamicProxy.UserServiceImpl">
</bean>
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pc" expression="execution(* register(String))"></aop:pointcut>
<!--配置类切点-->
<!--<aop:pointcut id="pc" expression="execution(* *..UserServiceImpl.register(String,..))"></aop:pointcut>-->
<!--组装切面 :把那一个 额外功能加给那一个 方法-->
<!-- <aop:pointcut id="pc" expression="execution(* com.baizhi.dynimicproxy..UserServiceImpl.register(String,..))"></aop:pointcut>-->
<aop:advisor advice-ref="around" pointcut-ref="pc"></aop:advisor>
</aop:config>
</beans>
4.1.3 书写额外功能
public class Around implements MethodInterceptor {
/*
* 方法的作用: 额外功能运行在原始方法之前后执行
* 参数的作用:mi 代表添加的原始方法对应的对象的封装
* Object 代表 原始方法执行过后的返回值
* */
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("我是在原始方法之前执行的额外功能");
//执行原始方法
Object ret = mi.proceed();
System.out.println("我是在原始方法之后执行的额外功能");
System.out.println(ret);
return ret;
}
}
4.2 动态代理设计模式 (Dynamic Proxy)
4.2.1 JDK方式创建代理对象(基于接口) (默认)
public interface UserService {
//注册
public int register(String username);
}
public class UserServiceImpl implements UserService {
@Override
public int register(String username) {
System.out.println("有一个参数的注册方法");
return 666;
}
}
public class testMy_JDKProxy {
@Test
public void test1() {
final UserService us = new UserServiceImpl();
//现实InvocationHandler 接口
InvocationHandler hi = new InvocationHandler() {
/*
* method:原始方法
* args:原始的方法参数
* 返回值:原始方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---------before -------");
//原始方法运行
//userService.login("suns", "123456")
//userService.register(user);
Object ret = method.invoke(us, args);
System.out.println("---------after -------");
return ret;
}
};
UserService usp = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), us.getClass().getInterfaces(), hi);
usp.register("lasa");
}
}
//---------before -------
//带有一个参数的注册方法
//---------after -------
4.2.2 Cglib方式创建代理对象 (基于父子类继承关系)
public class test_CglibProxy {
@Test
public void test_cgLib(){
//创建原始类的对象
final UserService us = new UserServiceImpl();
//准备额外功能
InvocationHandler invocationHandler = new InvocationHandler() {
/*
* 方法的作用:给原始类添加额外功能
* 参数的作用: o 代表代理对象 method代表 原始方法对象 objects代表 原始方法传入的实际参数
* 返回值:代表 原始方法执行过后的返回值
* */
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("我是执行在原始方法之前执行的额外功能");
//调用原始方法 原始对象 原始方法传入的实际参数
Object ret = method.invoke(us, objects);
System.out.println("我是执行在原始方法之后执行的额外功能");
return ret;
}
};
Enhancer enhancer = new Enhancer();
//类加载器
//设置类加载器
enhancer.setClassLoader(test_CglibProxy.class.getClassLoader());
//设置父类 原因 通过继承关系 保证代理类与原始类都有相同的方法
enhancer.setSuperclass(us.getClass());
//给原始类添加额外功能
enhancer.setCallback(invocationHandler);
//构建代理对象
UserServiceImpl o=(UserServiceImpl) enhancer.create();
o.register("ss");
}
//我是执行在原始方法之前执行的额外功能
//带有一个参数的注册方法
//我是执行在原始方法之后执行的额外功能
五、Spring+MyBatis整合
建表
写实体
定义DAO接口
Mapper文件实现DAO接口
Mapper文件注册
测试
六、Spring中的事务控制
6.1 事物概念
保证一组DAO操作的完整性 (一起成功 一起失败)
主要应用在Service层.
如何控制事务?
JDBC中:
connection.setAutoCommited(false)
connection.commit();
connection.rollback();
Mybatis:
sqlSession.commit();
sqlSession.rollback();
6.2 Spring中控制事务的开发
- 在Spring中,把事务划分为额外功能,需要通过Spring的动态代理给对应的方法添加事务
- 步骤
1.书写原始类
2.在Spring的配置文件中配置原始类的相关信息
3.书写额外功能类
4.配置额外功能类的信息
5.配置切入点
6.组装切面
<!--引入事务额外功能-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--控制事务需要创建连接 创建连接需要连接数据库的相关参数-->
<property name="dataSource" ref="dataSource">
</property>
</bean>
<!--对于事务的相关描述-->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<!--给添加到哪一个方法上的事务进行相关表述 支持通配符-->
<tx:method name="modify*"/>
</tx:attributes>
</tx:advice>
<!--配置切入点-->
<aop:config>
<aop:pointcut id="p1" expression="execution(* *..AcountServiceImpl.*(..))"></aop:pointcut>
<!--组装切面,将额外功能,添加到哪些方法添加额外功能-->
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="p1"></aop:advisor>
</aop:config>
七 Spring中的事务描述
7.1 隔离属性 isolation
脏读: 一个事务读取了另一个事务尚未提交的数据
isolation="READ_COMMITTED" 解决脏读问题(默认值) (列锁)
不可重复读: 一个事务中多次查询得到的结果不一致
isolation="REPEATABLE_READ" 解决不可重复读 (行锁)
幻影读: 一个事务多次统计的结果不一致
isolation="SERIALIZABLE" 解决幻影读问题 (表锁))
7.2 传播属性 propagation
作用:解决事务嵌套问题(一个事务嵌套了另外一个事务)
(增删改)默认
propagation="REQUIRED" 外部有事务则融合到外部事务中,外部没有事务 则开启新的事务
(查询)
propagation="SUPPORTS" 外部有事务则融合到外部事务中,外部没有事务也不开启新的事务
7.3 只读属性 readOnly
默认 read-Only=false 增删改
read-Only=true 查询 提高查询效率
7.4 超时属性 timeout
默认值: -1
作用:指定等待时长(一个事务占用对应的数据,需要进行等待,才能操作)
-1 代表根据数据库的特性,指定对应的等待时长
7.5 异常属性
默认:
对于RuntimeException以及子类做回滚操作
对于Exception进行提交操作
rollback-for="" 指定那些异常遇到了做回滚操作
no-rollback-for="" 指定那些异常做遇到了提交操作
7.6 属性的配置
总结:
增删改
isolation="DEFAULT"
propagation="REQUIRED"
read-Only=false
timeout=-1 全用默认值
查询
isolation="DEFAULT"
propagation="SUPPORTS"
read-Only=true
timeout=-1
- 注意