Spring框架
文章目录
- Spring框架
- 1. 课程计划
- 3 ioc
- 4. Spring
- 5 Spring的初级工程
- 6 Spring的依赖注入
- 7 Spring整合Junit
- 8 动态代理:
- 9 Aop
- 10 Spring的jdbcTempalte(这有一部分在和mybatis整合的时候就没用了)
1. 课程计划
- 第一天:Spring的IOC(xml)
要求:
能够独立搭建ioc的开发环境
理解ioc的优势 - 第二天:Spring的IOC(注解)
要求:
能够独立搭建注解IOC的开发环境
能够使用xml和注解实现当日案例(XML班和注解版) - 第三天:Spring的AOP(xml和注解)
. 要求:
能够搭建AOP的开发环境
理解AOP的实现技术
能够使用spring的AOP实现案例(xml和注解) - 第四天: Spring的jdbcTemplate和生命式事务
要求:
会jdbcTemplate实现CRUD
会配置spring的生命式事务(xml和注解) - 第五天:SSH整合
整合:整三个版本
要求:三个版本都能整出来,我们学习阶段,后续项目中都能用第三个版本
#2 程序的依赖关系
2.1 程序的耦合性
调用程序的紧密型 高耦合就是紧密的联系
• 耦合性:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。
2.2 程序的内聚性
• 内聚性:又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。
2.3 程序的依赖关系
编译时不依赖,运行时才依赖
解决依赖关系:
使用反射创建类对象
使用反射创建励对象引发的新问题:
使用配置文件 通过读取配置文件来反射创建类对象
2.3.1 利用工厂模式创建对象
2.3.1.1 定义接口:
- 定义实体类的接口
-
定义实体类的实现
- 定义service层的接口
- 定义service的实现类
. - 通过抽取class文件来调用方法
- 测试类
2.3.2 如何从配置文件获取相应的数据
2.3.2.1 第一种:
1. 先创建一个properties对象
2. 调用inputStream 对象 然后 根据本类对象(应该是类名).class.getClassLoader().getResourceAsSream(“加载的文件名”);
3. Properities对象.load(inputStream对象)
4. Props.getProperty(‘文件里面的对象名 ’)
/* 1.定义一个properties对象 */
private static Properties props = null;
// 使用静态代码块给对象赋值
static {
// 只能在类目录下 不可跨目录 只能在内路径下
// InputStream
// in=BeanFactory.class.getClassLoader().getResourceAsStream("src/bean.properties");
// 也可以用绝对路径来操作
// web 工程在发布以后src路径就没有了
// 所以不能用
try {
props = new Properties();
InputStream in = BeanFactory.class.getClassLoader()
.getResourceAsStream("bean.properties");
// InputStream in =new FileInputStream("src/bean.properties");
props.load(in);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("加载配置文件出错");
e.printStackTrace();
public static void main(String[] args) {
System.out.println(props.getProperty("CUSTOMERSERVICE"
2.3.2.2 第二种:
注意:在将其他类创建实现类的接口的时候也应该用工厂模式 进行创建
ResourceBundle.getString(“类名” ) 相当于类加载器
Class.forName(ResourceBundle).newInstance 相当于创建一个实类.
注意 配置文件的格式和内容
控制反转 只能 解决解耦的问题
第一种是用对象创建对象是编译依赖
第二种用工厂模式类加载器创建类是运行时依赖 不会影响程序的运行 只是找不到相应的而已,对程序员的程序可以跨程序编译 意思就是没有实体类也可以进行运行,只是没有结果
3 ioc
3.1 ioc的叙述:
ioc将创建类的能力转交给了其他类 factory类 可以解耦 (仅有这一个功能)
减少类与类之间的耦合性 俗称控制翻转
3.2 ioc的实现 factory的最终实现步骤
3.2.1 线程安全:
类的方法体:类方法体独立进栈不会影响线程安全问题
类的成员体:在servlet中不要定义这种的会造成线程安全问题
3.2.2 再无线程安全下:
类的方法体中依旧不会影响线程安全问题
类的成员体也不会影响
3.2.3 工厂模式的具体步骤:
// 加载配置文件
private static ResourceBundle resourceBundle = ResourceBundle
.getBundle("bean");
// 给对象准备一个容器 Map可以放入多个对象
// Map集合也可以自动去掉重复的对象
private static Map<String, Object> beans = new HashMap<String, Object>();
// 使用静态代码块,初始化map
static {
try {
// 1.读取配置文件中的所有配置:key部分
Enumeration<String> keys = resourceBundle.getKeys();
while (keys.hasMoreElements()) {
// 3.取出一个key
String key = keys.nextElement();
// 4.根据key获取beanpath
String beanPath = resourceBundle.getString(key);
// 5.根据beanPath反射创建类对象
Object value = Class.forName(beanPath).newInstance();
//将key和value存入map中
beans.put(key, value);
}
3.2.3.1 创建一个map集合
Map集合可以存放多个对象
Map集合保证集合内部是惟一的
Map集合也保证存放的素的
3.2.3.2 具体:
- 读取配置文件key的部分
- 取出key
- 根据key的值 获取beanpath
- 根据beanpath反射创建类对象
- 将value和key的值存入map集合中
- 用的时候直接map.Get(“类名”)
4. Spring
4.1 Spring的概述:
Spring是分层的Java SE/EE应用 full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。
4.2 Spring 的发展历程
1997年IBM提出了EJB的思想
1998年,SUN制定开发标准规范EJB1.0
1999年,EJB1.1发布
2001年,EJB2.0发布
2003年,EJB2.1发布
2006年,EJB3.0发布
Rod Johnson(spring之父)
Expert One-to-One J2EE Design and Development(2002)
阐述了J2EE使用EJB开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了J2EE开发不使用EJB的解决方式(Spring雏形)
4.3 spring的优势
- 方便解耦,简化开发
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。 - AOP编程的支持
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。 - 声明式事务的支持
A. 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
方便程序的测试
B. 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。 - 方便集成各种优秀框架
A. Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
降低JavaEE API的使用难度
B. Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
Java源码是经典学习范例
C. Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。
4.4 spring的体系结构
4.5 程序的耦合性和解耦
4.5.1 什么是程序的耦合
我们在开发中,会写很多的类,而有些类之间不可避免的产生依赖关系,这种依赖关系称之为耦合。
有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。请看下面的示例代码:
/**
* 客户的业务层实现类
*/
public class CustomerServiceImpl implements ICustomerService {
private ICustomerDao customerDao = new CustomerDaoImpl();
}
上面的代码表示:业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种依赖关系就是我们可以通过优化代码解决的。
再比如:
下面的代码中,我们的类依赖了MySQL的具体驱动类,如果这时候更换了数据库品牌,我们需要改源码来修改数据库驱动。这显然不是我们想要的。
public class JdbcDemo1 {
/**
* JDBC操作数据库的基本入门中存在什么问题?
* 导致驱动注册两次是个问题,但不是严重的。
* 严重的问题:是当前类和mysql的驱动类有很强的依赖关系。
* 当我们没有驱动类的时候,连编译都不让。
* 那这种依赖关系,就叫做程序的耦合
*
* 我们在开发中,理想的状态应该是:
* 我们应该尽力达到的:编译时不依赖,运行时才依赖。
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//1.注册驱动
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
//3.获取预处理sql语句对象
//4.获取结果集
//5.遍历结果集
}
}
4.5.2 解决程序耦合的思路
当时我们讲解jdbc时,是通过反射来注册驱动的,代码如下:
Class.forName(“com.mysql.jdbc.Driver”);
这时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包,依然可以编译。但是因为没有驱动类,所以不能运行。
不过,此处也有个问题,就是我们反射类对象的全限定类名字符串是在java类中写死的,一旦要改还是要修改源码。
解决这个问题也很简单,使用配置文件配置。
4.5.3 工厂模式解耦
在实际开发中我们可以把所有的dao和service和action对象使用配置文件配置起来,当启动服务器应用加载的时候,通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了
4.5.4 控制反转-Inversion Of Control 解耦
上面解耦的思路有2个问题:
1、存哪去?
分析:由于我们是很多对象,肯定要找个集合来存。这时候有Map和List供选择。
到底选Map还是List就看我们有没有查找需求。有查找需求,选Map。
所以我们的答案就是
在应用加载时,创建一个Map,用于存放action,Service和dao对象。
我们把这个map称之为容器。
2、还是没解释什么是工厂?
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来:
我们在获取对象时,都是采用new的方式。是主动的。
现在:
我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。
它的作用只有一个:削减计算机程序的耦合。
5 Spring的初级工程
/* ClassPathXmlApplicationContext 只能加载类路径下的配置文件 用这个 fileApplicationContext 可以加载磁盘任何位置的配置文件 */5.1 创建java逻辑层和持久层
5.2 导入jar包
Spring 的必须包
辅助包
5.3 导入约束
该html里面的 所有
<?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 definitions here -->
</beans>
5.4 写bean.xml 配置文件
5.5 测试程序
public static void main(String[] args) {
//获取容器
ApplicationContext aC=new ClassPathXmlApplicationContext("bean.xml") ;
//根据bean的id对象获取对象
customerServer cS=(customerServer) aC.getBean("customerService");
System.out.println(cS);
5.6 Spring 中bean的两种创建规则
5.6.1 立即加载、
ApplicationContext:
提供的是一种立即加载思想来创建bean对象,只要已解析完配置文件,就立马创建
/* @SuppressWarnings("all")//为了让代码不显示错误
public static void main(String[] args) {
//获取容器
ApplicationContext aC=new ClassPathXmlApplicationContext("bean.xml") ;
//根据bean的id对象获取对象
customerServer cS=(customerServer) aC.getBean("customerService");
System.out.println(cS);
}*/
5.6.2 延迟加载
BeanFctory:
提供的是一种延迟加载思想来创建bean对象的
Bean对象是什么时候用 什么时候创建
@SuppressWarnings("all")//为了让代码不显示错误
public static void main(String[] args) {
//获取容器
Resource resource=new ClassPathResource("bean.xml");
BeanFactory factory=new XmlBeanFactory(resource);
//根据bean的id对象获取对象
customerServer cS=(customerServer) factory.getBean("customerService");
System.out.println(cS);
}
5.7 Spring 中bean的三种创建方式
- 第一种方式 :调用默认无参构造函数的创建(类似于上面的两种情况 即及时加载 和延迟加载)《你用的是最多的》
默认情况下,如果类中没有默认无参构造,则创建失败 会报异常 - 使用静态工厂的方法创建对象
静态工厂创建对象:(这里的方法必须是静态的 )
. 创建一个类专门创建实体类对象
注意 :必须有静态方法创建对象 - 配置xml文件的bean标签
factory-method 工厂方法 不用自己写只需要安Alt+/ (必须装插件可以)
是因为bean.xml里面有两个bean的配置文件
需要使用bean标签的fantory-method属性,指定静态工厂创建对象的方法
3. 使用实例工厂创建对象:(注意这里的方法不能是静态的)
①. 先定义一个类 创建 创建对象的方法
②. 配置xml文件《bean》
测试类先由《bean》的id找到创建类的bean 然后根据factory-bean里的名字找到实现创建bean的类的bean的配置《bean》然后进入类进行调用
5.7 Bean的作用范围
- 如果是多例模式的话,bean的使用范围就是使用的那一次
- 如果是单例模式的话,bean的适用范围应该是整个程序
Bean的作用范围是:
他可以通过配置的方式来调整作用范围的
配置的属性:bean标签的scope属性
属性的取值
== Singleton:单例的(默认值)==
== Prototype:多例的(当我们让spring接管struts2的action创建时)==
Request:作用范围是一次请求,当前请求的转发
Session:作用范围是一次绘画
Globalsession:作用范围是一次全局绘画 多台服务器的时候用
5.8 Bean的生命周期
设计bean标签的两个属性:
Init-method::创建容器
Destory-method:销毁容器
5.8.1 单例:
出生:容器创建,对象出生
活着:只要容器在,对象一直都存在
死亡:容器销毁,对象消亡
- 在实现类中先创建两个模拟创建和销毁对象的容器
- 修改xml
- 将容器创建的对象关闭
5.8.2 多例
出生:每次使用时创建对象
活着:只要对象在使用中就一直活着
死亡:的那个对象长时间不适用,硬切也没有别的的独享引用时,垃圾回收机制自动销毁
6 Spring的依赖注入
6.1 依赖注入:
使用配置文件使spring缺什么传什么
注入的方式有三种 :
第一种:使用构造函数注入
第二种:使用set方法注入
第三种:使用注解注入
注入的数据类型有三类
第一类:基本类型和string类型
第二类:其他bean类型*(必须是spring的配置文件中出现过的bean)
第三类:复杂类型(集合类型)
6.1.1 第一种:
Bean.xml文件:
Java文件:
6.1.2 Set注入:
Bean.xml:
Java程序:
6.1.3 复杂类型的注入
6.1.3.1 Java语句
6.1.3.2 Xml文件
<!-- 复杂类型的注入 结构一样的标签 标签可以互换 -->
<bean id="customerService3" class="com.tjk.daoserverImp.customerServerImp3">
<property name="myStrs">
<array>
<value>AAA</value>
<value>bbb</value>
</array>
</property>
<property name="myList">
<list>
<value>TTT</value>
<value>QQQ</value>
</list>
</property>
<property name="mySet">
<set>
<value>CCC</value>
<value>DDD</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="test1" value="DDD"></entry>
<entry key="test2" value="CCC">
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="ttttttt"></prop>
</props>
</property>
</bean>
</beans>
6.1.3.3 测试代码:
6.2 Dbutil
-什么是dbutils
它的出现就是封装了jdbc的代码,开发dao层代码的简单框架
框架的作用就是为你完成一些工作
dbutils是由apache公司提供的
为什么使用dbutils
在它出现之前在dao层中使用的技术是jdbc
(1)数据库链接对象、sql语句操作对象,封装结果集对象,这三大对象会重复定义
(2)封装数据的代码重复,而且操作复杂,代码量大
(3)释放资源的代码重复
结果:(1)程序员在开发的时候,有大量的重复劳动。
(2)开发的周期长,效率低
三:Dbutils三个核心类介绍:
1:DbUtils:连接数据库对象----jdbc辅助方法的集合类,线程安全
构造方法:DbUtils()
作用:控制连接,控制书屋,控制驱动加载额一个类。
2:QueryRunner:SQL语句的操作对象,可以设置查询结果集的封装策略,线程安全。
构造方法:
(1)QueryRunner():创建一个与数据库无关的QueryRunner对象,后期再操作数据库的会后,需要手动给一个Connection对象,它可以手动控制事务。Connection.setAutoCommit(false); 设置手动管理事务
Connection.commit(); 提交事务
(2)QueryRunner(DataSource ds):创建一个与数据库关联的queryRunner对象,后期再操作数据库的时候,不需要Connection对象,自动管理事务。
DataSource:数据库连接池对象。
构造函数与增删改查方法的组合:
QueryRunner()
update(Connection conn, String sql, Object... params)
query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)
QueryRunner(DataSource ds)
update(String sql, Object... params)
query(String sql, ResultSetHandler<T> rsh, Object... params)
(3)ResultSetHandle:封装数据的策略对象------将封装结果集中的数据,转换到另一个对象
策略:封装数据到对象的方式(示例:将数据库保存在User、保存到数组、保存到集合)
方法介绍:handle(ResultSet rs)
备注:详解参考ResultSetHandle实现类
四:Dbutils快速入门
使用Dbutils注意事项:
(1)需要导入的jar包:①MySql驱动 ②c3p0包 ③DbUtils包
(2)添加c3p0配置文件
(3)可以自行添加一个JDBCUtils工具类:用来获取c3p0连接池对象
连接池的jar包
6.2.1 连接池的应用
6.2.2 基本的结构写好
- 实现持久层的实现类
public class ICustomerdaoImpl implements IcustomerDao {
private QueryRunner queryRunner=new QueryRunner(new C3P0Utils().getDataSource());//传DateSource
@Override
public List<Customer> findAllCustomer() {
// TODO Auto-generated method stub
try {
return queryRunner.query("select *from cst_customer", new BeanListHandler<Customer>(Customer.class));
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
@Override
public void saveCustomer(Customer customer) {
// TODO Auto-generated method stub
try {
queryRunner.update(
"insert into cst_customer ( cust_name,cust_source,cust_industry,cust_level,cust_address,cust_phone )values(?
customer.getCust_name(), customer.getCust_source(), customer.getCust_industry(),
customer.getCust_level(), customer.getCust_address(), customer.getCust_phone());
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
@Override
public void updateCustomer(Customer customer) {
// TODO Auto-generated method stub
try {
queryRunner.update(
"update cst_customer set cust_name=?,cust_source=?,cust_industry=?,cust_level=?,cust_address=?,cust_phone=?
customer.getCust_name(), customer.getCust_source(), customer.getCust_industry(),
customer.getCust_level(), customer.getCust_address(), customer.getCust_phone(),
customer.getCust_id());
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
@Override
public void deleteCutomer(Long custId) {
// TODO Auto-generated method stub
try {
// new BeanHandler<Customer>(Customer.class) 是结果集的意思
queryRunner.update("delete from cst_customer where cust_id=? ", custId);
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
@Override
public Customer findCustomerById(Long custId) {
// TODO Auto-generated method stub
try {
return queryRunner.query("select *from cst_customer where cust_id=?",
new BeanHandler<Customer>(Customer.class), custId);
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
- 业务层:
- 业务层实现:
- c3p0-config.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/user</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>
Dbutils的实现类:
public class C3P0Utils {
private static ComboPooledDataSource ds = new ComboPooledDataSource();
/* 当我们使用配置文件时,并且配置文件按照文档要求,放到顶层构建路径中,就可以不用设置参数的值
* static{
try {
ds = new ComboPooledDataSource();
//ds = new ComboPooledDataSource("oracle");通过构造函数来指定读取配置文件中的哪段配置
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/ee_1122_day14");
ds.setUser("root");
ds.setPassword("1234");
} catch (Exception e) {
throw new ExceptionInInitializerError("初始化连接失败!");
}
}*/
//获取数据源
public static DataSource getDataSource(){
return ds;
}
//获取一个连接
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
}
测试类:
6.3 Dbutils和SpringIoc连用
注意set方法必须的有
6.3.1 逻辑层类:
6.3.2 持久层类:
6.3.3 Bean.xml:
<!-- 配置service -->
<bean id="customerService" class="com.tjk.service.impl.ICustomerServiceImpl">
<property name="customerDao" ref="customerDao"></property>
</bean>
<!-- 配置Dao -->
<bean id="customerDao" class="com.tjk.Dao.Impl.ICustomerdaoImpl">
<property name="queryRunner" ref="queryRunner"></property>
</bean>
<!-- 配置QueryRunner -->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
注意:这里的name必须是你创建对象时的对象名
Class必须是你映射的类的路径
<!-- 配置C3PO连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/user"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
6.4 注解:
6.4.1 分类:
- 用于创建bean对象
@Component
作用:相当于配置了一个bean标签
他能出现的位置:类上面
属性:value:含义是指定bean的id 当不写时,他有默认值 默认值是当前类的答案名 首字母小写
@Controller 一般用户表现的注解-
@Service 一般用于业务层
-
@Repository 一般用于持久层
-
他们和Component的作用及属性都是一样的
-
- 用于注入数据的
-
@Autowired
-
作用:自动按照类型注入
-
只要唯一类型匹配就可以注入成功
-
用注解的方式进行注入数据时 set方法就不是必须的了
-
如果注入的bean在容器中的类型不唯一时,他回去把变量名称作为bean的id,在容器中查找,找到后也能注入成功了
-
如果没有找到一致的id 则报错
-
@qualifier
-
作用:在自动按照类型注入的基础上,在按照bean的id注入,
-
他在给 类的成员注入数据时,不能独立使用,
-
但是在给方法的形参最数据时可以独立使用
-
属性:
-
value:用于指定bean的id
-
@resource
-
作用:直接按照bean的id注入
-
属性:
-
name:用于指定bean的id
-
@Value():
-
作用:用于注入基本类型和string类型的数据,他可以借助spring的el表达式读取properties文件中的配置
- 用于改变作用范围的
- @Scope :
- 作用:用于改变bean的作用范围
-
属性:
-
value:用于指定范围的取值
-
取值和xml的属性的取值是一样的
-
singleton
-
prototype
-
request
-
session
-
globalsession
- 和生命周期相关的
- spring的新注解
6.4.2 @Component 注解 创建对象
6.4.2.1 注解的类:
6.4.2.2 Bean.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"
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">
<!-- 告知spring 在创建容器时需要扫描的包,当配置了此标签之后,spring创建容器就会去指定的包及其子包下找对应的注解
标签是在context的名称空间里必须先导入context名称空间 40.2.8
--> <!-- bean definitions here -->
<context:component-scan base-package="com.tjk"></context:component-scan>
6.4.2.3 测试类:
public class Client {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
CustomerServer cs=(CustomerServer) ac.getBean("customerServerImp");
System.out.println(cs);
}
}
6.4.3 注入数据:
- 用户创建bean对象
@compnnent
相当于一个bean标签
属性:value:含义是指定bean的id 当不写时,他有默认值 默认值是当前类的答案名 首字母小写
- @Controller 一般用户表现的注解
@Service 一般用于业务层
@Repository 一般用于持久层
他们和Component的作用及属性都是一样的
类型
- 用于注入数据的
下面这三个是用于注入数据类型为bean类的 @Autowired @qualifier @resource
1.1. @Autowired
作用:自动按照类型注入
只要唯一类型匹配就可以注入成功
用注解的方式进行注入数据时 set方法就不是必须的了
如果注入的bean在容器中的类型不唯一时,他回去把变量名称作为bean的id,在容器中查找,找到后也能注入成功了
如果没有找到一致的id 则报错
1.2. @qualifier
作用:在自动按照类型注入的基础上,在按照bean的id注入,
他在给 类的成员注入数据时,不能独立使用,
但是在给方法的形参最数据时可以独立使用
属性:
value:用于指定bean的id
1.3. @resource
作用:直接按照bean的id注入
属性:
name:用于指定bean的id
Name不可以省略 value可以省略
6.4.3.1 用于注入基本类型和string类型的数据
@Value():
作用:用于注入基本类型和string类型的数据,他可以借助spring的el表达式读取properties文件中的配置
用于注入基本的数据类型的注解:
在下面直接可以引用tjk 相当于 tjk=“qrl”
6.4.4 用于改变作用范围的
@Scope :
作用:用于改变bean的作用范围
属性:
value:用于指定范围的取值
取值和xml的属性的取值是一样的
---------------------------------------------singleton
--------------------------------------------- prototype
---------------------------------------------request
---------------------------------------------session
---------------------------------------------globalsession
1、singleton 作用域
当一个bean的 作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把 一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都 将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中 只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时 候,spring的IOC容器中只会存在一个该bean。
配置实例:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>
或者
<bean id="role" class="spring.chapter2.maryGame.Role" singleton="true"/>
2、prototype
prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。 清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用 bean的后置处理器,该处理器持有要被清除的bean的引用。)
配置实例:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="prototype"/>
或者
<beanid="role" class="spring.chapter2.maryGame.Role" singleton="false"/>
3、request
request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,配置实例:
request、session、global session使用的时候首先要在初始化web的web.xml中做如下配置:
如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可:
<web-app>
...
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
...
</web-app>
如果是Servlet2.4以前的web容器,那么你要使用一个javax.servlet.Filter的实现:
<web-app>
..
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
接着既可以配置bean的作用域了:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="request"/>
4、session
session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效配置实例:
配置实例:
和request配置实例的前提一样,配置好web启动文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="session"/>
5、global session
global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。
配置实例:
和request配置实例的前提一样,配置好web启动文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="global session"/>
6.4.4.1 Bean.xml
6.4.4.2 持久层
@Repository("customerDao1")
public class CustomerDaoImp implements CustomerDao {
public void saveCustomer() {
// TODO Auto-generated method stub
System.out.println("调用了持久111111111111");
}
}
@Repository("customerDao2")
public class CustomerDaoImp2 implements CustomerDao {
public void saveCustomer() {
// TODO Auto-generated method stub
System.out.println("调用了持久2222222222222222");
}
6.4.4.3 逻辑层
@Service("customerServer")
public class CustomerServerImp implements CustomerServer {
/*@Autowired
@Qualifier("customerDao2")*/
@Resource(name="customerDao2")
private CustomerDao customerDao1 = null;
public void savecustomer() {
// TODO Auto-generated method stub
System.out.println("此项目经过逻辑层");
/* Customer.saveCustomer(); */
customerDao1.saveCustomer();
}
}
6.4.4.4 测试类
Xml和注解对的选择:
Xml:应该用于不停修改的配置中
注解:应该用于不经常修改并且随着时间的推移类越来越多 配置也越来越多
6.4.4.5 用class文件替换bean.xml文件
@ComponentScan("com.tjk") // 扫描com.tjk里面
// @Configuration 可以将xml配置文件的引用简化 不用写具体路径 但是必须是在类路径下
public class SpringConfiguation {
/*
* 这是spring的配置文件的类 相当于bean.xml
*/
@Bean(name = "queryRunner") // 将方法的返回值直接存入spring的容器中 该注解有一个属性 name用于指定id
// name不指定时 有默认值 为方法的名称
public QueryRunner creatQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean(name = "dataSource")
public DataSource creatDataource() {
ComboPooledDataSource ds = new ComboPooledDataSource();
//连接池里面的方法
try {
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/user");
ds.setUser("root");
ds.setPassword("root");
} catch (Exception e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
return ds;
}
}
6.4.4.6 用properties文件替换java类的文件
- Jdbc.java
public class JdbcConfig {
@Value("${jdbc.driver}")//spring的EL表达式
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String user;
@Value("${jdbc.password}")
private String password;
/*
* 这是spring的配置文件的类 相当于bean.xml
*/
@Bean(name = "queryRunner") // 将方法的返回值直接存入spring的容器中 该注解有一个属性 name用于指定id
// name不指定时 有默认值 为方法的名称
public QueryRunner creatQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean(name = "dataSource")
public DataSource creatDataource() {
System.out.println(driver);
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(user);
ds.setPassword(password);
} catch (Exception e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
return ds;
}
Properties文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/user
jdbc.username=root
jdbc.password=root
配置文件:
@ComponentScan("com.tjk") // 扫描com.tjk里面
// @Configuration
@Import({JdbcConfig.class}) //当你不需要类时 只需要类的返回值时 需要用此注解
@PropertySource("classpath:config/jdbcConfig.properties")//扫描配置文件
public class SpringConfiguation {
@Bean
public static PropertySourcesPlaceholderConfigurer creatpropertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();//解析器 解析配置文件
}
}
7 Spring整合Junit
7.1 具体步骤:
第一步:拷贝spring的提供整合的jar包
Spring-test-4.2.4.RELEASE.jar
第二步: 使用junit提供的一个注解,把原有的main哈数替换掉,换成spring提供的@RunWith
要更换类: SoringJunit4ClassRunner
第三部:使用spring提供的注解告知spring,配置文件或者注解类所在的位置
@ContextConfiguration
7.2 具体操作
配置文件有xml的
配置文件无xml的
@RunWith(SpringJUnit4ClassRunner.class)
//使用junit提供的一个注解,把原有的main哈数替换掉,换成spring提供的@RunWith
要更换类: SoringJunit4ClassRunner
@ContextConfiguration(classes={SpringConfiguation.class})
//:使用spring提供的注解告知spring,配置文件或者注解类所在的位置
@ContextConfiguration
public class CustomerServiceTest {
@Autowired
private ICustomerService cs;
public void testFindAllCustomer() {
//加载注解的类
List<Customer> list=cs.findAllCustomer();
for (Customer customer : list) {
System.out.println(customer);
}
}
8 动态代理:
8.1 基于接口的动态代理
8.1.1 基本思路:
- 现将接口和实体类 测试类 都写好
- 动态代理:
- 作用:不改变源码的基础上,对已有方法的增强(他是Aop思想的实现)
-
分类:
基于接口的动态代理
要求:被代理类最少实现实现一个接口
提供者:JDK
涉及的类:Proxy
创建代理对象的方法: Proxy.newProxyInstance(loader, interfaces, h)
参数的含义:loader:类加载器 和被代理对象使用相同的类加载器 代理谁用谁的类加载器
interfaces:Class【】:字节码数组,被代理类实现的接口(要求代理对象和被代理对象有相同的行为)
h: invocation handler 接口:他是一个接口,就是用于我们提供增强代码的,我们一般都是写一个实现类,
实现类可以是匿名内部类也可以不是
实现类可以是匿名内部类也可以不是 他的含义就是:如何代理: 此处的代码只能是谁用谁提供 策略模式: 要求有数据 要求目的明确 使用要求数据已经有了目的明确 达成目标的过程就是策略 在dbUils中的ResultSetHandler就是策略模式的具体应用`
-
- 代码实现:
package com.tjk.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Cliant {
* m模拟剧组进行找演员
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
final Actor actor=new Actor();//用final是为了保证内名内部类得到的数据都一致
//jdk8以后就不需要加fianl因为底层帮你加了
IActor ic= (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(),new InvocationHandler() {
/*
* 任何执行被代理对象的方法一定会经过下面的方法
* 此方法有过滤拦截的作用
* Object proxy:代理对象的 引用,不一定每次都执行
* Method method: 当前执行的方法
* Object[] args:当前执行方法所需的参数
* 返回值:
* 当前执行方法的返回值
*
*
* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object rtValue=null;
//1.取出代理对象的方法的参数
float money=(float) args[0];
//2.判断当前执行的是什么方法
if("basicaAct".equals(method.getName())){
//基本表演
if (money>5000) {
rtValue=method.invoke(actor, money);
}
}
//危险表演
if ("dangerAct".equals(method.getName())) {
if (money>10000) {
rtValue=method.invoke(actor, money);
}
}
return rtValue;
}
});
//用对象调用方法
ic.basicaAct(10000);
ic.dangerAct(20002);
}
}
8.2 基于子类的动态代理
8.2.1 基本实现思路:
动态代理:
* 作用:不改变远吗的基础上,对已有方法的增强(他是Aop思想的实现)
* 分类:
* 基于几口的动态代理
*
* 基于子类的动态代理:
* 要求:被代理类不能是最终类
* 提供者 :第三方CDLib
导入jar包
* 涉及的类:Enhancer
* 创建代理对象的方法:creat(Class,Callback);
* 参数的含义:
* class:被代理对象的字节码
* Callback:如何代理和incocationhandle的作用一样
* 也是一个接口 我们一般那会死一个该接口的子接口 MethodInterceptor
* 在使用时我们也是创建该接口的匿名内部类
8.2.2 实现的主要代码:
public static void main(String[] args) {
// TODO Auto-generated method stub
final Actor actor = new Actor();// 用final是为了保证内名内部类得到的数据都一致
// jdk8以后就不需要加fianl因为底层帮你加了
Actor actor2=(Actor) Enhancer.create(actor.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object proper, Method method, Object[] args, MethodProxy arg) throws Throwable {
// TODO Auto-generated method stub
Object rtValue = null;
// 1.取出代理对象的方法的参数
float money = (float) args[0];
// 2.判断当前执行的是什么方法
if ("basicaAct".equals(method.getName())) {
// 基本表演
if (money > 5000) {
rtValue = method.invoke(actor, money);
}
}
// 危险表演
if ("dangerAct".equals(method.getName())) {
if (money > 10000) {
rtValue = method.invoke(actor, money);
}
}
return rtValue;
}
});
actor2.basicaAct(10000);
actor2.dangerAct(20002);
}
8.3 自定义链接池的具体实现
8.3.1 定义一个dataSource类
public class MyDataSource {
/*
*
*
*/
//定义一个池 用于存放连接
//List线程不安全
private static List<Connection> pool =Collections.synchronizedList(new ArrayList<Connection>());
//将自定义连接池的线程变为线程同步安全的
static{
//使用静态代码块给尺子加入链接
for (int i = 0; i < 10; i++) {
Connection conn=JDBCUtil.getConnection();
pool.add(conn);
}
}
/*
* 获取一个链接
*/
public static Connection getConnection(){
Connection conn= pool.remove(0);
Connection connProxy=(Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
//动态代理的具体实现
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object reValue=null;
if ("close".equals(method.getName())) {
//不能直接关闭
pool.add(conn);//还回池中
}
else{
reValue=method.invoke(conn, args);
}
return reValue;
}
});
return connProxy;
}
public static int getConnectionsize() {
return pool.size();
}
}
8.3.2 创建一个创建数据库的类
public class JDBCUtil {
//使用ResourceBundle读取资源文件
private static ResourceBundle bundle = ResourceBundle.getBundle("dbconfig");
private static String driver;
private static String url;
private static String user;
private static String password;
//使用静态代码块进行赋值
static{
driver = bundle.getString("DRIVER");
url = bundle.getString("URL");
user = bundle.getString("USER");
password = bundle.getString("PASSWORD");
}
/**
* 获取连接
* @return
*/
public static Connection getConnection(){
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
return conn;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
8.3.3 用动态代理实现事务的操作
public static ICustomerService getCustomerService(){
final ICustomerService cs = new CustomerServiceImpl();
//创建业务层的代理对象
ICustomerService proxyCs = (ICustomerService) Proxy.newProxyInstance(cs.getClass().getClassLoader(), cs.getClass().getInterfaces(),new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("test".equals(method.getName())){
return method.invoke(cs, args);
}
try{
//开启事务
TransactionManager.beginTransaction();
//执行操作
Object rtValue = method.invoke(cs, args);
//提交事务
TransactionManager.commit();
//返回结果
return rtValue;
}catch(Exception e){
//回滚事务
TransactionManager.rollback();
//处理异常
throw new RuntimeException(e);
}finally{
//关闭资源
//由于我们把session绑定到线程上了,hibernate就帮我们关闭了
}
}
});
return proxyCs;
}
}
9 Aop
9.1 什么是Aop
Aop:是面向切面编程
简单的来说他就是把我们的程序重复的代码抽出来,在需要要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们已有的方法进行增强
9.1.1 Aop的作用及优势
作用:
爱程序运行期间,不修改源码对已有的方法进行增强
优势:
减少重复的代码 提高开发效率 维护方便
9.1.2 Aop的实现方式
使用动态代理技术
9.2 Spring的Aop
在spring中,框架很根据是否实现接口来确定那个采用哪种动态代理的方式
实现接口:proxy
不实现接口用Cjlib
可以通过配置的方式来告诉spring无论任何情况下都用Cjlib
9.2.1 Spring的Aop要了解:
- A. 在开发阶段(我们做的)
在填写核心业务代码(开发主线):大部分管理员未做,要求熟悉业务需求
把共用代码抽取出来,制作成通知,(在开发阶段最后再做),AOP编程人员做
在配置文件中,生命切入点与通知间的关系,即切面aop编程人员来做 - B. 运行阶段:(spring框架完成的)
Spring框架监控一切接入方法的执行,一旦监控切入点方法运行,使用代理机制,动态创建目标对象的代理对象,根据通知的类别,在代理对象的对应位置,将通知对应的功能投入,完成完整的代码逻辑运行 - Joinpoint(连接点): 业务层接口
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。 - Pointcut(切入点):被增强的是切入点 没被泽强的是连接点 切入点一定是连接点 但是连接点不一定是切入点
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。 - Advice(通知/增强):增强的代码在哪那就是通知 开启事务 提交事务 回滚事务
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
前置通知:写在切入点的方法的前面的就是前置通知
后置通知:写在切入点后面的就是后置通知
异常通知:写在catch中额代码
最终通知:写在finally中的代码
环绕通知上面的四种通知整合起来就是环绕通知 - Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。 - Target(目标对象):
代理的目标对象。 - Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。
spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。 - Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。 - Aspect(切面):
是切入点和通知(引介)的结合。
9.2.2 基于xml类的aop的配置步骤:
第一步:
配置service
第二步:
把通知类交给spring来管理
第三步:
导入aop名称空间 并且使用aop:config开始aop的配置 >
<使用aop:aspect配置子切面 id属性是aop:aspect的唯一标识 ref用于通知bean的id >
第四步:配置通知的类型,指定增强的方法合适执行 method用于指定增强的方法名称 pointcut用于指定切入点表达式 -->```
切入点表达式: execution( 包名.包名…类名.方法名((参数列表) )
<?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">
<!-- 配置service -->
<bean id="customerService" class="com.tjk.service.impl.CustomerServiceImpl"></bean>
<!-- 基于xml类的aop的配置步骤: -->
<!--第一步:把通知类交给spring来管理 -->
<bean id="logger" class="com.tjk.utils.Logger"></bean>
<!-- 第二步:导入aop名称空间 并且使用aop:config开始aop的配置 -->
<aop:config>
<!-- 使用aop:aspect配置子切面 id属性是aop:aspect的唯一标识 ref用于通知bean的id -->
<aop:aspect id="logAdvice" ref="logger">
<!-- 第四步:配置通知的类型,指定增强的方法合适执行 method用于指定增强的方法名称 pointcut用于指定切入点表达式 -->
<!-- 切入点表达式: execution( 包名.包名...类名.方法名((参数列表) ) -->
<aop:before method="printLog" pointcut="execution(public void
com.tjk.service.impl.CustomerServiceImpl.saveCustomer())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
切入点表达式:
全配符:
public void
com.tjk.service.impl.CustomerServiceImpl.saveCustomer())
访问类型可以省略
void
com.tjk.service.impl.CustomerServiceImpl.saveCustomer())
返回值类型可以用通配符*
表示任意返回值皆可以
*
com.tjk.service.impl.CustomerServiceImpl.saveCustomer())
包名可以用通配符 * 但是有几个包就就需要写几个*
execution( *
*.*.*.*.CustomerServiceImpl.saveCustomer())
包名下面可以用..表示该包及其子包
com..CustomerServiceImpl.saveCustomer())
类名和方法名都可以用通配符
execution( *
com..*.saveCustomer())
execution( *
com..*.*())
参数列表:可以使用具体类型来表示参数类型
基本类型直接写类型名名称 int
string: java.lang.juil
参数列表可以使用通配符 表示任意参数 但是必须有参数
execution( *
com..*.*(*))
参数列表可以使用.. 表示任意参数(有无参数均可)
execution( *
com..*.*(..)) --
实际开发中都是在业务层方法进行增强
*com.tjk.service.impl.*(..)
定义多个切入点:
此方法必须定义在aspect之前如果想要多个切面使用的话
全通配方式:*.*..*.*(..)
9.3 四种通知的方式
9.3.1 前置通知:会在切点之前执行
9.3.2 后置通知:再无异常的情况下,在切点之后执行
9.3.3 异常通知:再出现异常的情况下,会执行。然后后置通知不执行
9.3.4 最终异常:切点执行完 后置通知或者异常同志执行完后执行
9.3.5 环绕通知:
//环绕通知:
/*
* 问题:
* 当我们配置了环绕通知后,切入点方法没有执行,而环绕通知里的代码执行了
* 分析:
* 有动态代理克制,哈an绕通知指的是invoke方法,并且里面友明企鹅的切入点方法调用,而我们现在
* 环绕通知没有明确的切入点方法调用
* 解决:
* spring为我们提供了一个接口,proceedingJoinPoint,该接口可以作为环绕通知的方法参数来使用
* 程序运行时,spring框架为我们提供该接口的实现类,供我们使用
* 环绕执行的方式:是spring框架为我们提供的一种手动执行的什么时候执行增强代码的一种实现方式
*/
public Object aroundPrintLog(ProceedingJoinPoint proceedingJoinPoint){
Object value=null;
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
Logger logger= (Logger) applicationContext.getBean("logger");
try {
//前置
logger.beforPrintLog();
value= proceedingJoinPoint.proceed();
//后置
logger.afterRunnerPrintLog();
} catch (Throwable e) {
// TODO Auto-generated catch block
//异常
logger.afterthrowingPrintLog();
e.printStackTrace();
}finally {
//最终异常
logger.afterPrintLog();
}
System.out.println("aroundPrintLog方式开始执行了");
return null;
}
9.4 使用注解实现AOP
9.4.1 先将bean.xml中的注解扫描配置和Aop注解开关打开
9.4.2 再将需要增强的方法类注解好
9.4.3 再注解切面的
@Component(value="logger")
@Aspect//注解切面
public class Logger {
@Pointcut("execution(public void com.tjk.service.impl.CustomerServiceImpl.saveCustomer(..) )" )
private void pc1(){}
前置通知
@Before( "pc1()")
public void beforPrintLog(){
System.out.println("beforPrintLog方法开始记录日志");
* 后置通知
@AfterReturning("pc1()")
public void afterRunnerPrintLog(){
System.out.println("afterRunnerPrintLog方式开始结束日志记录");
}
//异常通知
@AfterThrowing("pc1()")
public void afterthrowingPrintLog(){
System.out.println("afterthrowingPrintLog方式开始结束日志记录");
}
//最终通知
@After("pc1()")
public void afterPrintLog(){
System.out.println("afterPrintLog方式开始结束日志记录");
}
//@Around("pc1()")
public Object aroundPrintLog(ProceedingJoinPoint proceedingJoinPoint){
}
出现的问题:
Spring开发人在写Aop注解的时候讲最终通知和后置通知写反了(只是出现的位置错了) 所以我们在写的时候尽量用环绕通知这样可以避免这个错误出现
9.4.4 全注解:
10 Spring的jdbcTempalte(这有一部分在和mybatis整合的时候就没用了)
10.1 什么是Spring的jdbcTempalte
它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类,入下图所示:
第一个jdbc jar包是spring jdbcTempalte的jar包
第二个是整合用到的Orm jar包
第三个是事务控制的jar包 这三个jar包一般一起使用
10.2 SpringTemplate链=连接数据库并进行操作
public static void main(String[] args) {
//获取数据源
DriverManagerDataSource dmds=new DriverManagerDataSource();
dmds.setDriverClassName("com.mysql.jdbc.Driver");
dmds.setUrl("jdbc:mysql://localhost:3306/springjdbctempalte");
dmds.setUsername("root");
dmds.setPassword("root");
//1.获取对象
//JdbcTemplate jT=new JdbcTemplate();
JdbcTemplate jt=new JdbcTemplate();
jt.setDataSource(dmds);
try {
//这里必须导入core spring的核心jar包要不然就会报错 处理异常也没有用
jt.execute("insert into account(name,money) values('111','222')");
} catch (DataAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
10.3 SpringTemplate入门基础
Bean.xml
Java.class
10.4 SpringTemplate的两种在dao层使用的方法
10.4.1 如果使用xml或者注解配置的话
public class CustomerImpl implements ICustomer {
使用xml配置文件给其初始化 注意: 要有set方法
也可以使用注解的方式给其赋值
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public account findAccuntById(int id) {
// TODO Auto-generated method stub
List<account> accounts=jdbcTemplate.query("select *from account where id=?", new BeanPropertyRowMapper<account>(account.class),id);
return accounts.isEmpty()?null:accounts.get(0);
}
<bean id="iCustomer" class="com.tjk.dao.impl.CustomerImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
10.4.2 使用xml的方法配置
public class CustomerImpl2 extends JdbcDaoSupport implements ICustomer {
@Override
public account findAccuntById(int id) {
// TODO Auto-generated method stub
List<account> accounts=getJdbcTemplate().query("select *from account where id=?", new BeanPropertyRowMapper<account>(account.class),id);
return accounts.isEmpty()?null:accounts.get(0);
}
<!-- spring支持的第二种配置方法 -->
<bean name="ICustomer2" class="com.tjk.dao.impl.CustomerImpl2">
<property name="dataSource" ref="dataSource"></property>
</bean>
第二种方法 继承JdbcDaoSupport类 这个类中有自动为JdbcTemplate().初始化 所以只需要给dataSource进行配置就可以
10.5 配置三种数据源的方法
10.5.1 Spring内置的数据源
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/springjdbctempalte"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
10.5.2 Dbcp数据源的配置
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/springjdbctempalte"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
10.5.3 C3p0数据源的配置
<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springjdbctempalte"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
10.6 Spring的事务控制:
10.6.1 明确
- 第一: JavaEE体系 进行分层开发 事务处理位于业务层,spring提供分层设计业务层的事务处理解决方案
- 第二:spring框架为我们提供了一组事务控制的接口,具体在后面 介绍,
- 第三:spring的事务控制都是基于Aop的,他可以使用编程的方式实现,也可以使用配置的方式实现,我们学习的重点是使用配置的方式实现
10.6.2 Spring中的 事务控制的API介绍
10.6.2.1 PlatformTransactionManager
此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法,如下图:
我们在开发中都是使用它的实现类,如下图:
== 真正管理事务的对象
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或iBatis 进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager 使用Hibernate版本进行持久化数据时使用==
10.6.2.2 TransactionDefinition
它是事务的定义信息对象,里面有如下方法:
10.6.2.3 事务的隔离级别
10.6.2.4 事务的传播行为
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
- MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
- REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,抛出异常
- NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
10.6.2.5 超时时间
默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
10.6.2.6 是否是只读事务
建议查询时设置为只读。
10.6.2.7 TransactionStatus
10.6.2.7 TransactionStatus
此接口提供的是事务具体的运行状态,方法介绍如下图:
脏读: 一个事务读到了另一个事物没有提交的数据
不可重复读:一个事务读到了另一个事务已经提交的update的数据
虚读:一个数据读到了另一个数据insert数据
不可重复读和虚读的区别:前后内容不一样 前后条数不同
增删改必须有事务
查询无事务 可以设置只读
10.6.3 Spring的声明事务的环境配置
10.6.3.1 准备环境:
10.6.3.2 spring 的基于xml的声明事务控制:
1. 第一步:配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
2. 第二步 配置事务的通知
<tx:advice id="txAdvice" transaction-manager="transactionManager">
第四步: 配置事务的属性
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true" /> <!-- 优先级高 -->
</tx:attributes>
</tx:advice>
3. 配置Aop
<!-- 配置切入点表达式 通知和且瑞安表达式的关联 -->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut expression="execution( * com.tjk.service.impl.*.*(..))" id="pt1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
配置事务的属性
- isolation=“DEFAULT” 配置事务的隔离级别 默认值是DEFAULT 使用数据库的默认隔离级别 mysql是REPEATABLE_read
propagation=“REQUIRED” 配置事务的传播行为 默认值是REQUIRED 一般的选择 (增删改方法) 当时查询方法时 选择Supports
timeout="-1" 指定事务的超市时间 -1是用不超时 秒为单位
read-only=“false” 配置是否是只读事务 默认是false 当指定为true 表示只读 只能用于查询方法 - rollback-for="" 用于指定一个异常 事务回滚 产生其他异常 事务不回滚 没有默认值 任何异常都回滚
- no-rollback-for="" 用于指定一个异常 当执行产生该异常时, 事务回滚 没有默认值 任何异常 回滚
回滚和提交交个spring了
10.6.3.3 Spring事务处理配置xml_annountion
因为是xml和annotation一起配合着用
注意加入命名空间的context的相关链接
加入扫描注解的包 <context;component-scan base—package=“”>
先将dao层的注解和service层的注解进行补全
这时候写事务管理器就比较简单了
配置事务管理器《bean》
配置spring开启事务管理器
<!-- 配置spring开启事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
在需要进行事务控制的类前加上@Transactional即可
在需要的地方使用注解 @transcational
该注解可以卸载接口上 类上 和方法上
写在接口上表示该接口的所有实现类都有事务
写在方法表示该方法有事务
写在类上表示该类有事务
优先级 就近原则
10.6.3.4 纯注解spring的事务管理器
- 配置数据源
- 配置事务管理器
- 配置spring的核心文件