Spring总结
Spring的概述
①Spring是一个开源框架
②Spring为简化企业级开发而生,使用Spring开发可以将Bean对象,Dao组件对象,Service组件对象等交给Spring容器来管理,这样使得很多复杂的代码在Spring中开发却变得非常的优雅和简洁,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。
③Spring是一个IOC(DI)和AOP容器框架。
④Spring的优良特性
[1]非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
[2]控制反转:IOC——Inversion of Control,指的是将对象的创建权交给Spring去创建。使用Spring之前,对象的创建都是由我们自己在代码中new创建。而使用Spring之后。对象的创建都是由给了Spring框架。
[3]依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用setXX方法去设置,而是通过配置赋值。
[4]面向切面编程:Aspect Oriented Programming——AOP
[5]容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
[6]组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
[7]一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。
自我理解,spring的三大内容:
1.ioc控制反转,依赖注入,通过配置xml信息或者设置注解生产bean对象,dao对象,service对象.
2.aop,面向切面,通过底层实现2个动态代理(java.lang.reflect动态代理,cjlib代理),实现面向方法编程,可以增强方法实现日志及其他功能,提高代码的复用性.
3.声明事事务:通过注解Transactional对方法进行注解,使其方法具有事务性,通过多种属性对不同的事务进行操作.
1. IOC控制反转 和 依赖注入
1.1 IOC 全称指的是 Inverse Of Control 控制反转。
使用Spring之前,我们对Bean对象的管制,都是自己手动的去new Xxx()。
而使用了Spring模型之后,我们把new的操作。交给Spring容器。
1.2 什么是DI
DI 指的是Dependency Injection 。是依赖注入的意思。原来在使用Spring之前。
Class BookService{
private BookDao bookDao ;
public void setBookDao( BookDao bookDao ){
this.bookDao = bookDao
}
Public BookService(){
bookDAO = new BookDAOImpl();
}
}
使用了Spring之后。我们只需要使用xml配置,或者注解配置。就可以直接注入。
1.3 搭建spring工程运行环境
1.核心jar包
4个及其日志打
印包2个–>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lk775SJY-1574917689982)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps1.jpg)]
**2、**创建log4j.properties日记配置文件
# Global logging configuration
log4j.rootLogger=INFO, stdout
# Console output…
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
**3、**创建Spring的配置文件
创建一个spring Bean config文件,sts软件自带
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FKsDMgMN-1574917689983)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps2.jpg)]
4、创建一个JavaBean对象
public class Person {
private int id;
private String name;
private String phone;
private int age;
**5、**到Spring配置文件中配置你的Bean对象
<!-- bean标签用于配置一个组件对象(javaBean,Dao。Service)
id 是给bean对象起一个唯一的标识
class bean对象的具体全类名
-->
<bean id=“p1” class=“com.atguigu.pojo.Person”>
<!-- property标签用来配置属性信息
name 是属性名
value 是属性值–>
<property name=“id” value=“1”/>
<property name=“name” value=“华仔”/>
<property name=“age” value=“18”/>
<property name=“phone” value=“18699998888”/>
1.4 从spring容器获取bean对象的多种方式
1.4.1通过id获取对象(重点)
// 创建一个Spring容器对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
“application.xml”);
// getBean从Spring容器中获取指定的id值的 bean对象返回
Person person = (Person) applicationContext.getBean(“p1”);
常见错误说明:如果调用getBean多次,会创建几个?
答:默认创建同一个
1.4.2 IOC通过类型获取对象(重点)
Person person = applicationContext.getBean(Person.class);
当在applicationContext.xml配置文件中。
有多个同Person.class类型实现的时候。
按照Class类型查找的时候,如果没有,也会报错误.
1.4.3 IOC 通过构造方法参数名注入
<bean id=“p3” class=“com.atguigu.pojo.Person”>
<constructor-arg name=“id” value=“3” />
1.4.4 IOC index属性指定参数的位置
<bean id=“p4” class=“com.atguigu.pojo.Person”>
<!–
constructor-arg 表示使用构造方法注入属性值
name 表示构造方法的参数名
index 表示构造方法的参数顺序从0开始
value 表示要传入的构造方法的参数值
-->
<constructor-arg value=“4” index=“0”/>
1.4.5 IOC根据参数类型注入
<bean id=“p5” class=“com.atguigu.pojo.Person”>
<constructor-arg value=“5” index=“0” type=“int”/>
<constructor-arg value=“phone” index=“1” type=“java.lang.String”/>
<constructor-arg value=“10” index=“2” type=“java.lang.String”/>
注意当有多种同参数类型时,必须标注其在类中的索引,预防串值
1.4.6 IOC之 P名称空间
先通过namespaces设置P属性标签
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fqaAZEtK-1574917689984)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps3.jpg)]
xml中的配置:
<bean id=“p6” class=“com.atguigu.pojo.Person” p:id=“60” p:name=“第六个” p:phone=“6电话” p:age=“66”/>
1.4.7 IOC之子对象的赋值测试(重点)
创建一个带有其他类为属性的类
public class Person {
private int id;
private String name;
private String phone;
private int age;
private Car car;
public class Car {
private int id;
private String name;
在xml中的配置如下:
<bean id=“car1” class=“com.atguigu.pojo.Car”>
<property name=“id” value=“1”/>
<property name=“name” value=“宝马”/>
<bean id=“p7” class=“com.atguigu.pojo.Person”>
<property name=“id” value=“7”/>
<property name=“car” ref=“car1” />
使用ref可以引用其他对象
1.4.8 IOC之内部Bean的使用
<bean id=“p8” class=“com.atguigu.pojo.Person”>
<property name=“id” value=“8”/>
<property name=“car”>
<bean id=“innerCar” class=“com.atguigu.pojo.Car” p:id=“22” p:name=“内部汽车”>
常见错误:内部的Bean不能被外部使用
1.4.9 IOC之List属性的赋值
public class Person {
private int id;
private List list;
xml中的配置:
<bean id=“p9” class=“com.atguigu.pojo.Person”>
<property name=“id” value=“8”/>
<property name=“list”>
list1
list2
list3
1.4.10 IOC之Map属性的赋值
public class Person {
private int id;
private Map<String, Object>map;
Xml中的配置
<bean id=“p10” class=“com.atguigu.pojo.Person”>
<property name=“name” value=“这是第10个实验” />
<property name=“map”>
<entry key=“key1” value=“value1” />
<entry key=“key2” value=“value2” />
<entry key=“key3” value=“value3” />
1.4.11 IOC之Properties属性的赋值
public class Person {
private int id;
private Properties prop;
xml中的配置如下:
<bean id=“p11” class=“com.atguigu.pojo.Person”>
<property name=“name” value=“这是第10个实验” />
<property name=“prop”>
<prop key=“jdbc.url”>jdbc:mysql://localhost:3306/test
<prop key=“jdbc.password”>root
1.4.12 IOC之util 名称空间
util名称空间,可以定义通过util名称空间创建集合类型的bean
先在namespaces去命名一个util名称空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3XHQVc3R-1574917689984)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps4.jpg)]
在xml中的配置:
<util:list id=“list1”>
list1
list2
list3
</util:list>
<bean id=“p12” class=“com.atguigu.pojo.Person”>
<property name=“name” value=“这是12个Person” />
<property name=“list” ref=“list1” />
1.4.13 IOC之级联属性赋值
在xml中的配置:
<bean id=“p13” class=“com.atguigu.pojo.Person”>
<property name=“name” value=“这是12个Person” />
<property name=“car” ref=“car1”>
<property name=“car.id” value=“2” />
<property name=“car.name” value=“级联属性赋值” />
常见错误:级联属性一定要先注入对象。再注入对象的属性
1.4.14 IOC之静态工厂方法创建Bean
1、创建一个工厂类
public class PersonFactory {
public static Person createPerson() {
Person person = new Person();
person.setName(“这是通过静态工厂方法创建的Person对象”);
return person;
}
2、到xml中去配置:
id 还是bean的唯一标识
class 属性定义工厂类全类名
factory-method 属性是调用哪个方法,的方法名
<bean id=“p14” class=“com.atguigu.util.PersonFactory” factory-method=“createPerson” />
1.4.15 IOC之工厂实例方法创建Bean
public Person createPerson2() {
Person person = new Person();
person.setName(“这是通过工厂实例对象创建的Person”);
return person;
}
Xml配置如下 先创建一个有实例方法的对象,通过对象调用方法获得返回对象
<bean id=“personFactory” class=“com.atguigu.util.PersonFactory” />
<!–
id是唯一的标识
factory-bean 引用哪个工厂对象的实例
factory-method 同时是调用哪个方法,的方法名
-->
<bean id=“p15” factory-bean=“personFactory” factory-method=“createPerson2” />
1.4.16 IOC之FactoryBean接口方式创建对象
实现FactoryBean接口的类
Public class PersonFactoryBean implements FactoryBean {
实现他的方法
返回创建的对象
@Override
public Person getObject() throws Exception {
Person person = new Person();
person.setName(“这是FactoryBean接口的方式创建的”);
return person;
* 返回创建的对象的类型
@Override
public Class<?> getObjectType() {
return Person.class;
* 是否是单例
@Override
public boolean isSingleton() {
return true;
在xml配置文件中的配置:
<bean id=“p16” class=“com.atguigu.util.PersonFactoryBean” />
1.4.17 IOC之继承Bean配置
通过继承实现bean配置信息的重用
<bean id=“p1” class=“com.atguigu.pojo.Person”>
<bean id=“p17” class=“com.atguigu.pojo.Person” parent=“p1”>
1.4.18 IOC之abstract抽象Bean
<bean id=“p1” class=“com.atguigu.pojo.Person” abstract=“true”>
如果配置的bean标签对象,设置了属性abstract=*“true”**,*那么此标签中的对象不能实例化
1.4.19 IOC之组件创建顺序
bean之间的依赖 depends-on 属性
depends-on=“b” 如果要创建a对象,就要有b对象也就是要先创建b再创建a
<bean id=“a” class=“com.atguigu.pojo.A” depends-on=“b”>
<bean id=“b” class=“com.atguigu.pojo.B”>
<bean id=“c” class=“com.atguigu.pojo.C”>
1.4.20 IOC之Bean的单例和多例(重点)
测试bean的作用域,分别创建单实例和多实例的bean★
scope 属性设置范围
singleton 默认情况 是singleton,表示Spring容器中只有一个单例
单例是在Spring容器创建的时候。初始化所有单例Bean对象
并且每次调用Bean对象的时候,原来原来Spring容器中的对象
prototype prototype是表示当前配置的Bean对象是多例。
在Spring容器被创建的时候,bean不会被创建出来。
并且每次调用getBean方法都会创建一个对象实例
request 表示一次请求内,不管调用几次getBean方法,返回的都是同一个bean对象
Object bean = request.getAttribute(xxx);
if (bean == null) {
bean = 创建一个
request.setAttribute(xxx,bean);
}
session 表示一个Session对象内,不管调用几次getBean方法,返回的都同一个Bean对象
Object bean = session.getAttribute(xxx);
if (bean == null) {
bean = 创建一个
session.setAttribute(xxx,bean);
}
-->
<bean id=“p21” class=“com.atguigu.pojo.Person” scope=“prototype”>
1.4.21 基于xml配置文件的自动注入
autowire 设置自动装配的方式
default 和 no 一样,都表示不装配, 如果对象不你手动设置,就没有值。 byName 表示Spring容器会自动按照子对象的属性名,当成是id来查找对象。找到就注入,找不到就为null
举例:private Car car;
就会以car做为id去spring容器中去查找对象。找到就把值注入给属性car。
byType 表示Spring容器会自动的按照子对象的类型去查找bean对象注入。
举例:private Car car;
Spring容器就会自动的按照Car.class类型去Spring容器中查找。
如果说,找到一个,就注入
如果没有找到,值就为null
如果说,找到多个,就报错。
constructor 表示Spring容器会按照子对象的类型去查找构造方法,中参数需要的类型去注入。先按照类型查询,如果找到一个就注入。如果找到多个,再按照构造方法中参数的变量名做为id来查找,如果找到就注入。如果没有找到,就为null如果没有找到,值也为null
Xml中的配置
<bean id=“p22” class=“com.atguigu.pojo.Person” autowire=“constructor”>
1.5 BeanFactory和ApplicationContext的关系
BeanFactory
BeanFactory是Spring容器的基础接口,提供了基础的容器访问能力。
BeanFactory提供懒加载方式,只有通过getBean方法调用获取Bean才会进行实例化。
ApplicationContext继承自BeanFactory接口,ApplicationContext包含了BeanFactory中所有的功能。
具有自己独特的特性:
Bean instantiation/wiriting
Bean实例化/串联
自动BeanPostProcessor注册
自动BeanFactoryPostProcessor注册
方便的MessageSource访问
ApplicationEvent发布
ApplicationContext采用的是预加载,每个Bean都在ApplicationContext启动后实例化。
1.6对象的生命周期
IOC之Bean的生命周期 创建带有生命周期方法的bean
给A类添加生命周期方法:
public class A {
public A() {
System.out.println(“这是A对象被创建了”);
}
public void initA() {
System.out.println(“这里是初始化A的方法”);
}
public void destroyA() {
System.out.println(“这里是销毁A的方法”);
}
}
在xml配置文件中配置如下:
<!–
init-method=“initA” 设置初始化方法
destroy-method=“destroyA” 设置销毁的方法
-->
<bean id=“a” class=“com.atguigu.pojo.A” init-method=“initA” destroy-method=“destroyA”>
2. Spring管理数据库连接池(重点)
2.1 搭建spring环境导入数据库jar包
需要导入数据库驱动包以及数据库连接池
mysql-connector-java-5.1.37-bin.jar
druid-1.1.10.jar
2.2 Spring配置管理数据库连接池对象(重点)
在Spring的配置文件中配置数据库连接池对象
<bean id="dataSource"class=“com.alibaba.druid.pool.DruidDataSource”>
<property name=“url” value=“jdbc:mysql://localhost:3306/atguigu” />
<property name=“username” value=“root” />
<property name=“password” value=“root” />
<property name=“driverClassName” value=“com.mysql.jdbc.Driver” />
2.3 使用context名称空间加载jdbc.properties配置文件(重点)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zAyKtoOM-1574917689984)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps5.jpg)]
<context:property-placeholder location=“classpath:jdbc.properties”/>
2.4 Spring引入单独的jdbc.properties配置文件(重点)
1、抽取四个jdbc连接属性到jdbc.properties属性配置文件中
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/atguigu
jdbc.driverClassName=com.mysql.jdbc.Driver
2、使用PropertyPlaceholderConfigurer 来加载jdbc.properties属性配置文件
<bean class=“org.springframework.beans.factory.config.PropertyPlaceholderConfigurer”>
<property name=“location” value=“classpath:jdbc.properties” />
3、使用加载后的jdbc.properties属性配置文件中的连接属性。
<bean id=“dataSource” class=“com.alibaba.druid.pool.DruidDataSource”>
<property name="*url " value="${jdbc.url}"* />
<property name=“username” value="${jdbc.user}" />
<property name=“password” value="${jdbc.password}" />
<property name=“driverClassName” value="${jdbc.driverClass}" />
3 Sring EL表达式(*了解内容*)
[SpEL测试I]在SpEL中使用字面量:使用格式:#{数值} #{“字符串” || ‘字符串’}
[SpEL测试II]在SpEL中引用其他bean使用格式:#{bean的id}
[SpEL测试III]在SpEL中引用其他bean的某个属性值使用格式: #{bean.属性名}
[SpEL测试IV]在SpEL中调用非静态方法使用格式: #{bean.方法名(参数)}
[SpEL测试V]在SpEL中调用静态方法使用格式:#{T(全名类).方法名(参数)}
[SpEL测试VI]在SpEL中使用运算符使用格式:#{表达式}
<bean id=“car” class=“com.atguigu.pojo.Car”>
<property name=“name” value=“宝马” />
<property name=“carNo” value=“京B123421” />
<bean id=“personEL” class=“com.atguigu.pojo.Person”>
-->
<property name=“car” value="#{car}"/>
<property name=“phone” value="#{car.name}" />
-->
<property name=“name” value="#{T(com.atguigu.pojo.Car).staticFun()}"
<property name=“salary” value="#{10*1024}"/>
4 Spring注解功能***(极其重要)
4.1注解配置Dao、Service、Controller组件
当我们使用Spring的注解功能的时候。需要把aop的jar包导入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xkvSI29H-1574917689985)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps6.jpg)]
通过注解分别创建Dao、Service、Controller★
Spring配置bean的常用注解有
@Controller 专门标注给web层的组件
@Service 专门给Service层的组件注解
@Repository 给Dao层组件标注
@Component 给Dao、Service、控制器Web层之外的组件进行标注。
@Scope 可以修改bean的Scope属性,默认不标注此注解表示单例.也可以通过注解修改为多例@Scope(value=“prototype”)
注解在类上的使用:
/**
* @Repository注解的功能相当于在Spring配置文件中做了如下的配置:
*
*/
@Scope(value=“prototype”)
@Repository(value=“bookDao”)
public class BookDao {
当我们在类上使用了注解之后。一定要在Spring配置文件中加上包扫描的配置才能生效
context:component-scan 表示包扫描base-package 指定要扫描哪些包下的类(包含子包)
<context:component-scanbase-package=“com.atguigu” />
4.2指定扫描包时的过滤内容(*重要*)
使用context:include-filter指定扫描包时要包含的类–使用context:exclude-filter指定扫描包时不包含的类
<context:include-filter /> 设置包含的内容
**注意:**通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,
<context:exclude-filter /> 设置排除的内容
类别 | 示例 | 说明 |
---|---|---|
annotation | com.atguigu.XxxAnnotation | 过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤 |
assignable | com.atguigu.BaseXxx | 过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。 |
aspectj | com.atguigu.*Service+ | 所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。 |
regex | com.atguigu.anno.* | 所有com.atguigu.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。 |
custom | com.atguigu.XxxTypeFilter | 使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口 |
applicationContext.xml 中配置的内容如下
<!-- use-default-filters="false" 设置取消默认包含规则 -->
<context:component-scan base-package=“com.atguigu” use-default-filters=“false”>
<context:include-filter type=“annotation” expression=“org.springframework.stereotype.Service”/>
<context:exclude-filter type=“assignable” expression=“com.atguigu.service.BookService”/>
</context:component-scan>
以上配置会包含所有@Service注解的类。排除com.atguigu.service.BookService类
4.3使用注解@Autowired自动装配
使用@Autowired注解实现根据类型实现自动装配★@Autowired 注解 会自动的根据标注的对象类型在Spring容器中查找相对应的类。如果找到,就自动装配。使用@Autowired注解,不需要get/set方法
@Repository
public class BookDao {
public BookDao() {
System.out.println(“BookDao也被初始化了”);
@Service(“bookService”)
public class BookService {
public BookService() {
System.out.println(“bookService被初始化了”);
* 实验35:使用@Autowired注解实现根据类型实现自动装配★
* 1、Spring容器会自动的根据对象的类型到Spring容器中去查找,然后注入值。
* 1、如果找到一个,就直接注入值
@Autowired
private BookDao bookDao;
@Override
public String toString() {
return “BookService [bookDao=” + bookDao + “]”;
多个同类型的bean如何自动装配如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配★
4.4使用**@Qualifier装配指定id的bean对象**
如果根据成员变量名作为id还是找不到bean,可以使用@Qualifier注解明确指定目标bean的id
@Repository(value=“aaa”)
public class BookDao {
public BookDao() {
System.out.println(“BookDao也被初始化了”);
@Repository(value=“bbb”)
public class BookDaoExt extends BookDao{
public BookDaoExt() {
System.out.println(“BookDaoExt也被初始化了”);
* @Qualifier 这个注解,可以给你标注的组件。按照给定的id去spring容器中查找,然后注入。
* 这个注解会改变默认规则,默认是按照变量名,做为id来查找。但是,当使用了。这个注解之后。就会按照给定的id来查找。
* 如果找到指定id的bean对象,就注入。如果找不到就报错
@Qualifier(“bbb”)
private BookDao bookDao;
@Autowired和@Qualifier在方法上的使用。
在方法的形参位置使用@Qualifier注解
private BookDao bookDaoExt;
* @Autowired 注解标注的方法和组件都是初始化的时候,就已经调用和注入了。
@Autowired
public void abc(@Qualifier(value = “aaa”) BookDao bookDao) {
System.out.println(“这是被标注了@Autowired注解的方法…………”);
System.out.println(bookDao);
this.bookDaoExt = bookDao;
4.5 @Autowired注解的required属性作用(因为会抑制报错,一般不推荐)
@Autowired注解中有一个属性,叫required是必须,必要的意思,默认值是true,
表示被标注的bean对象。必须要有值注入。如果找不到注入。就报错。
如果找不到,不希望它报错。就把此值改为false。则此对象的值可以为null。
@Autowired(required=false)
* @Qualifier 这个注解,可以给你标注的组件。按照给定的id去spring容器中查找,然后注入。
* 这个注解会改变默认规则,默认是按照变量名,做为id来查找。但是,当使用了。这个注解之后。就会按照给定的id来查找。
* 如果找到指定id的bean对象,就注入。如果找不到就报错
5泛型注入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyNssneJ-1574917689985)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps7.jpg)]
6 Spring的专有测试
先导入spring-test-4.0.0.RELEASE.jar包
@ContextConfiguration(locations = “classpath:application.xml”)
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunit {
@Autowired
private BookService bookService;
@Autowired
private UserService userService;
@Autowired
private UserDao userDao;
@Test
public void test1() {
}
在测试类上加上注解@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = “classpath:application.xml”)
这样测试,就不用再获取spring对象了,且不用再使用getBean,使用@Autowired就可以直接装配对象了
7、AOP切面编程***(极其重要)
7.1 AOP是面向切面编程**(面向方法)**
全称:Aspect Oriented Programming
面向切面编程指的是:程序是运行期间,动态地将某段代码插入到原来方法代码的某些位置中。这就叫面向切面编程。
7.2 Aop编程的底层实现(使用代理)
7.2.1 jdk动态代理
Java.lang.reflect.Proxy 创建对象 new Proxy(参数1(被代理对象的类加载器),参数2(被代理对象的父接口),参数3 自定义的Invoctionhandler的核心处理器) 生产代理对象
Invoctionhandler处理器需要传入需要被代理的对象,重写invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass().equals(Object.class)) {
return method.invoke(target, args);
}
LogUtil.log(method.getName(), (int) args[0], (int) args[1]);
Object result = null;
try {
result = method.invoke(target, args);
System.out.println(“后置日记”);
} catch (Exception e) {
System.out.println(“异常日记”);
} finally {
System.out.println(“finally日记”);
}
return result;
优点:这种方式已经解决我们前面所有代理方法加强需要的问题。非常的灵活。而且可以方便的在后期进行维护和升级。
缺点:当然使用jdk动态代理,需要有接口。如果没有接口。就无法使用jdk动态代理。
7.2.2 cglib****代理
导入jar包cglib
public class CGLibProxyFactory implements MethodInterceptor {
public static Object getCGLibProxy(Object target, Callback callback) {
// 创建一个CGLig生成器
Enhancer enhancer = new Enhancer();
// 设置父类。因为cglib是通过类,进行代码,不是通过接口
enhancer.setSuperclass(target.getClass());
// 设置拦截的代理方法
enhancer.setCallback(callback);
// create 方法创建一个代理对象并返回
return enhancer.create();
@Override
public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy)
throws Throwable {
LogUtil.log(method.getName(), (int) params[0], (int) params[1]);
// 调用实际的对象的方法
// 一定要使用methodProxy对象
// 第一个参数是proxy代码对象的父类方法
Object result = methodProxy.invokeSuper(proxy, params);
System.out.println(“这是后置代码”);
return result;
}
优点:在没有接口的情况下,同样可以实现代理的效果。
缺点:同样需要自己编码实现代理全部过程。
但是为了更好的整合Spring框架使用。所以我们需要学习一下Spring 的AOP 功能。也就是学习Spring提供的AOP功能。
7.3AOP编程的专业术语
通知(Advice)
通知就是增强的代码。比如前置增强的代码。后置增强的代码。异常增强代码。返回结果通知代码。这些就叫通知
切面(Aspect)
切面就是包含有通知代码的类叫切面。
横切关注点
横切关注点,就是我们可以添加增强代码的位置。比如前置位置,后置位置,异常位置。和返回值位置。这些都叫横切关注点。
目标(Target)
目标对象就是被关注的对象。或者被代理的对象。
代理(Proxy)
为了拦截目标对象方法,而被创建出来的那个对象,就叫做代理对象。
连接点(Joinpoint)
连接点指的是横切关注点和程序代码的连接,叫连接点。
切入点(pointcut)
切入点指的是用户真正处理的连接点,叫切入点。
在Spring中切入点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
7.4****图解AOP专业术语:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dLmbRgTs-1574917689985)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps8.jpg)]
7.5使用Spring实现AOP切面编程环境
需要导入工程的jar包
Spring的核心包
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
Spring的测试包
spring-test-4.0.0.RELEASE.jar
Spring日记相关包
commons-logging-1.1.3.jar
log4j-1.2.17.jar
Spring的AOP切面相关的包
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
7.5 切入点表达式
@PointCut切入点表达式语法格式是: execution(访问权限 返回值类型 方法全限定名(参数类型列表))
- 匹配某全类名下,任意或多个方法。
表示匹配com.atguigu.aop.Calculator下以a打头的任意方法。并且返回值和两个参数都是int类型。
execution(public int com.atguigu.aop.Calculator.a*(int, int))
表示匹配com.atguigu.aop.Calculator下的任意方法。并且返回值和两个参数都是int类型。
execution(public int com.atguigu.aop.Calculator.*(int, int))
- 在Spring中只有public权限能拦截到,访问权限可以省略(访问权限不能写*)。
// 权限省略,表示任意类型的访问权限 ,但Spring现在只支持public权限
execution(int com.atguigu.aop.Calculator.*(int, int))
- 匹配任意类型的返回值,可以使用 * 表示
// 表示任意类型的返回值
execution(* com.atguigu.aop.Calculator.*(int, int))
- 匹配任意子包。
// 表示匹配com的子包
execution(* com..aop.Calculator.(int, int))
- 任意类型参数
// 表示第二个参数是任意类型
execution(* com.atguigu.aop.Calculator.(int,))
…:可以匹配多层路径,或任意多个任意类型参数
// 表示com和aop之间可以有任意层级的包
execution(* com…aop.Calculator.*(int,int))
// 表示第一个参数是int。之后可以有任意个任意类型的参数
execution(* com.atguigu.aop.Calculator.*(int,…))
模糊匹配:
// 表示任意返回值,任意方法全限定符,任意参数
execution(* *(…))
// 表示任意返回值,任意包名+任意方法名,任意参数
execution(* .(…))
精确匹配:
// int 返回值,com.atguigu.aop.Calculator类的add方法,两个int参数
execution(public int com.atguigu.aop.Calculator.add(int, int))
切入点表达式连接:&& 、||
// 表示需要同时满足两个表达式
@Before(“execution(public int com.atguigu.aop.Calculator.add(int, int))”
+ " && "
+ “execution(public * com.atguigu.aop.Calculator.add(…))”)
// 表示两个条件只需要满足一个,就会被匹配到
@Before(“execution(public int com.atguigu.aop.Calculator.add(int, int))”
+ " || "
+ “execution(public * com.atguigu.aop.Calculator.a*(int))”)
7.6 Spring切面中的代理对象
在Spring中,可以对有接口的对象和无接口的对象分别进行代理。在使用上有些细微的差别。
-
如果被代理的对象实现了接口。在获取对象的时候,必须要以接口来接收返回的对象。(底层使用了jdk的动态代理)
-
被切面拦截的代理对象,如果没有实现接口。获取对象的时候使用对象类型本身
(底层使用了cglib代理)
7.7 Spring通知的执行顺序
Spring通知的执行顺序是:
正常情况:
前置通知====>>>>后置通知=====>>>>返回值之后
异常情况:
前置通知====>>>>后置通知=====>>>>抛异常通知
7.8获取连接点信息
JoinPoint 是连接点的信息。
只需要在通知方法的参数中,加入一个JoinPoint参数。就可以获取到拦截方法的信息。
使用这个类对象在形参列表中,可以获得拦截(增强)方法的方法名和参数
注意:是org.aspectj.lang.JoinPoint这个类。
@Aspect
@Component
public class LogUtil {
@Before(“execution(public int com.atguigu.aop.Calculator.add(int, int))” + " || "
+ “execution(public * com.atguigu.aop.Calculator.d*(…))”)
public static void logBefore(JoinPoint joinPoint) {
System.out.println(“前置 日记 :【” + joinPoint.getSignature().getName() + “】 方法调用前 。参数是:”
+ Arrays.asList(joinPoint.getArgs()));
}
前面测试的代码没有变。再次执行的结果。都能拿到拦截的方法名和参数
7.9获取拦截方法的返回值和抛的异常信息
获取方法返回的值分为两个步骤:
1、在返回值通知的方法中,追加一个参数 Object result
2、然后在@AfterReturning注解中添加参数returning=“参数名”
获取方法抛出的异常分为两个步骤:
1、在异常通知的方法中,追加一个参数Exception exception
2、然后在@AfterThrowing 注解中添加参数 throwing=“参数名”
修改LogUtil切面类的代码
@AfterReturning(任意切入点)
public static void logAfterReturn( Object result) {
System.out.println(返回值:" + result);
}
@AfterThrowing(任意切入点)
public static void logThrowException( Exception exception) {
System.out.println(异常对象:" + exception);
}
7.10 Spring的环绕通知
1、环绕通知使用@Around注解。
2、环绕通知如果和其他通知同时执行。环绕通知会优先于其他通知之前执行。
3、环绕通知一定要有返回值(环绕如果没有返回值。后面的其他通知就无法接收到目标方法执行的结果)。
4、在环绕通知中。如果拦截异常。一定要往外抛。否则其他的异常通知是无法捕获到异常的。
5 在环绕通知中ProceedingJoinPoint类对象可以用来调用方法和获取任意值
在LogUtil切面类中添加环绕通知
@Around(value = “execution(* *(…))”)
public static Object logAround(ProceedingJoinPoint proceedingJoinPoint) {
//获取请求参数
Object[] args = proceedingJoinPoint.getArgs();
Object resultObject = null;
try {
System.out.println(“环绕前置”);
//调用目标方法
resultObject = proceedingJoinPoint.proceed(args);
System.out.println(“环绕返回”);
} catch (Throwable e) {
System.out.println(“环绕异常:” + e);
throw new RuntimeException(e);
} finally {
System.out.println(“环绕后置”);
}
//返回返回值
return resultObject;
7.11切入点表达式的重用
切入点表达式的重点,需要分三个步骤:
1、在切面类中定义一个空的方法
public static void pointcut1() {}
2、在方法中使用@Pointcut定义一个切入表达式
@Pointcut(value=“execution(public int com.atguigu.aop.Calculator.add(int, int))” + " || "
+ “execution(public * com.atguigu.aop.Calculator.*(…))”)
public static void pointcut1() {}
3、其他的通知注解中使用方法名()的形式引用方法上定义的切入点表达式。
比如:@Before(“pointcut1()”)
7.12 多个通知的执行顺序
当我们有多个切面,多个通知的时候:
1、通知的执行顺序默认是由切面类的字母先后顺序决定。
2、在切面类上使用@Order注解决定通知执行的顺序(值越小,越先执行)
但是仅在第一次方法前置增强有效,因为是改变线程优先级
再添加另一个切面类
@Component
@Aspect
@Order(1)
另一个切面类
@Aspect
@Component
@Order(2)
7.13如何基于xml配置aop程序
aop:config
<aop:pointcut expression=“execution(* com.atguigu.aop.Calculator.*(…))” id=“pointcut1”/>
<aop:aspect order=“1” ref=“logUtil”>
<!-- 这是前置通知
method属性配置通知的方法
pointcut配置切入点表达式
-->
<aop:before method=“任一方法名” pointcut=“execution(* com.atguigu.aop.Calculator.*(…))”/>
<aop:after method=“任一方法r” pointcut=“execution(* com.atguigu.aop.Calculator.*(…))”/>
<aop:after-returning method=“任一方法名”
returning=“result” pointcut=“execution(* com.atguigu.aop.Calculator.*(…))”/>
<aop:after-throwing method=“任一方法名” throwing=“exception”
pointcut=“execution(* com.atguigu.aop.Calculator.*(…))”/>
</aop:aspect>
<aop:aspect order=“2” ref=“validation”>
<aop:before method=“before” pointcut-ref=“pointcut1”/>
引用上面的设置属性的横切注入点
<aop:after method=“after” pointcut-ref=“pointcut1”/>
<aop:after-returning method=“afterReturning”
returning=“result” pointcut-ref=“pointcut1”/>
</aop:aspect>
</aop:config>
8. Spring之JdbcTemplate使用
在Spring中提供了对jdbc的封装类叫JdbcTemplate。它可以很方便的帮我们执行sql语句,操作数据库。
JdbcTemplate的使用需要在applicationContext.xml中进行配置
先创建一个数据库连接池对象 <bean id=”d**ruidDataSource” 其他的内容省略>
<bean id=“jdbcTemplate” class=“org.springframework.jdbc.core.JdbcTemplate”>
<property name=“dataSource” ref=“druidDataSource**”/>引用上面创建的
1.增 删 改 使用方式同下 使用对象调用update方法 传入指定(增删改)sql语句和指定语句所需参数
@Test
public void test3() throws Exception {
String sql = “insert into employee(name
,salary
) values(?,?)”;
ArrayList<Object[]> params = new ArrayList<>();
params.add(new Object[] {“aaa”,100});
params.add(new Object[] {“bbb”,100});
params.add(new Object[] {“ccc”,100});
jdbcTemplate.batchUpdate(sql, params);
}
2.查一个 方法queryForObject 其他与增删改相同,但是必须创建一个对象new BeanPropertyRowMapper(传入指定类型)
public void test4() throws Exception {
// 查询id=5的数据库记录,封装为一个Java对象返回
String sql = “select id ,name ,salary from employee where id = ?”;
* 在queryRunner中使用的是ResultSetHandler
* 在Spring的jdbcTemplate中,使用RowMapper。
* BeanPropertyRowMapper 可以把一个结果集转成一个bean对象
Employee employee = jdbcTemplate.queryForObject(sql,
new BeanPropertyRowMapper(Employee.class), 5);
System.out.println(employee);
3.查询多个方法query 其他与增删改相同,但是必须创建一个对象new BeanPropertyRowMapper(传入指定类型)
jdbcTemplate.query(sql语句, new BeanPropertyRowMapper(Employee.class), 参数);
4.查询特殊值 方法queryForObject
jdbcTemplate.queryForObject(sql语句, 特殊值类型.class**,参数**);
9 声明式事务***(重点)
事务分为声明式和编程式两种:
声明式事务:声明式事务是指通过注解的形式对事务的各种特性进行控制和管理。
编码式(编程式)事务:指的是通过编码的方式实现事务的声明。
9.1声明式事务环境搭建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZGdPSUvl-1574917689986)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps9.jpg)]
配置Spring的事务需要的切面类DataSourceTransactionManager
<bean id=“transactionManager”
class=“org.springframework.jdbc.datasource.DataSourceTransactionManager”>
<property name=“dataSource” ref=“dataSource” /> 引入写好的jdbc对象
在Spring的配置文件中加入tx名称空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0C3FYG4Q-1574917689986)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps10.jpg)]
在Spring的配置文件中
下面设置需要使用开启的事务管理对象,写入上面xml中配好的
<tx:annotation-driven transaction-manager=“transactionManager”/>
9.2 声明式事务@Transactional注解的多种属性
9.2.1noRollbackFor和noRollbackForClassName测试不回滚的异常
noRollbackFor=ArithmeticException.class 表示当接收到数学异常之后。不回滚
* noRollbackFor=ArithmeticException.class
* noRollbackForClassName=“java.lang.ArithmeticException” 表示当接收到指定字符串表示的全类名的异常的时候,不回滚事务
9.2.2自定义设置回滚异常
rollbackFor和rollbackForClassName回滚的异常
Spring默认回滚的是RuntimeException,运行时异常或运行时异常的子异常
/**
* spring默认回去的是运行时异常RuntimeException和RuntimeException的子异常
* rollbackFor=FileNotFoundException.class 表示FileNotFoundException也会回滚
* rollbackForClassName=“java.io.FileNotFoundException” 表示当出现配置字符串所向定的全类名的异常的时候。也会回滚事务
9.2.3事务的只读属性
readOnly只读属性 一般用在查询上 增删改使用会报错
* readOnly 如果值为true。表示只支持查询操作。不支持写操作
*
如果设置为false,支持全部
@Transactional(readOnly=true)
9.2.4事务超时属性timeout(秒为单位)
下面这种情况会抛异常,且回滚数据
* timeout=3表示操作不能超过3秒
@Transactional(timeout=3)
public void updateTwoTable() throws FileNotFoundException {
userDao.updateUser();
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bookDao.updateBook();
9.3事务的传播特性****propagation
什么是事务的传播行为: 常用propagation.REQUIRED propagation.REQUIRED_NEW
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-grzwvFjj-1574917689986)(file:///C:\Users\尚硅谷\AppData\Local\Temp\ksohtml5496\wps11.jpg)]
9.4 xml配置式事务声明
<bean id=“transactionManager”
class=“org.springframework.jdbc.datasource.DataSourceTransactionManager”>
<property name=“dataSource” ref=“dataSource” />
<tx:advice id=“tx_advice” transaction-manager=“transactionManager”>
tx:attributes
<tx:method name=“multiUpdate” propagation=“REQUIRED”/>
<tx:method name=“updateBook” propagation=“REQUIRES_NEW” />
<tx:method name=“updateUser” propagation=“REQUIRED”/>
<tx:method name="*" read-only=“true”/>
</tx:attributes>
</tx:advice>
aop:config
<aop:advisor advice-ref=“tx_advice” pointcut=“execution(* com.atguigu.service.*Service.*(…))” />
</aop:config>
10 Spring整合Web(不推荐使用)
10.1 web环境搭建
在web工程中添加Spring的jar包。
Spring的核心包
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
aop包
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
JDBC-ORM包
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
Spring的web整合包
spring-web-4.0.0.RELEASE.jar
测试包
spring-test-4.0.0.RELEASE.jar
整合Spring和Web容器分两个步骤:
1、在web.xml配置文件中配置org.springframework.web.context.ContextLoaderListener监听器监听 ServletContext的初始化
2、在web.xml配置文件中配置contextConfigLocation上下文参数。配置Spring配置文件的位置,以用于初始化Spring容器
在web.xml中配置
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
org.springframework.web.context.ContextLoaderListener
listener>
10.2获取WebApplicationContext上下文对象的方法如下:
在创建的动态项目的servlet中获取webSpring容器,其他操作系统
方法一(推荐):
WebApplicationContextUtils.getWebApplicationContext(getServletContext())
方法二(不推荐):
getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
x:attributes>
<tx:method name=“multiUpdate” propagation=“REQUIRED”/>
<tx:method name=“updateBook” propagation=“REQUIRES_NEW” />
<tx:method name=“updateUser” propagation=“REQUIRED”/>
<tx:method name="*" read-only=“true”/>
</tx:attributes>
</tx:advice>
aop:config
<aop:advisor advice-ref=“tx_advice” pointcut=“execution(* com.atguigu.service.*Service.*(…))” />
</aop:config>
10 Spring整合Web(不推荐使用)
10.1 web环境搭建
在web工程中添加Spring的jar包。
Spring的核心包
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
aop包
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
JDBC-ORM包
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
Spring的web整合包
spring-web-4.0.0.RELEASE.jar
测试包
spring-test-4.0.0.RELEASE.jar
整合Spring和Web容器分两个步骤:
1、在web.xml配置文件中配置org.springframework.web.context.ContextLoaderListener监听器监听 ServletContext的初始化
2、在web.xml配置文件中配置contextConfigLocation上下文参数。配置Spring配置文件的位置,以用于初始化Spring容器
在web.xml中配置
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
org.springframework.web.context.ContextLoaderListener
listener>
10.2获取WebApplicationContext上下文对象的方法如下:
在创建的动态项目的servlet中获取webSpring容器,其他操作系统
方法一(推荐):
WebApplicationContextUtils.getWebApplicationContext(getServletContext())
方法二(不推荐):
getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);