Spring
春天
一、概述
1. 简介
- 1)spring出生于2002左右,解决企业开发的难度。
- spring做了什么:减轻对项目模块之间、类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。
- 官网:
spring.io
,( project — spring framework — learn ,可以查看spring的官方文档) - 欧陆词典,看官方文档必备
- 下载文档:
https://repo.spring.io/libs-release-local/org/springframework/spring/
- 2)spring全家桶:spring、springmvc、spring boot、spring cloud
- 3)spring核心技术:
ioc(控制反转)、aop(面向切面编程)
,能够实现模块、类之间解耦合。 - 4)依赖:类A中使用类B中的方法属性,叫做类A依赖类B
2. 优点
- 1)轻量
- spring使用的jar包很小,一般1M以下。核心功能所需jar包总共3M左右
- spring运行占用资源小,运行效率高,不依赖其他jar包
- 2)针对接口编程,解耦合
- Spring 提供loc控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。
- 3)AOP编程
- 4)方便继承各种优秀框架
3. 体系结构
二、IoC控制反转
1. 简介
- 1)IoC控制反转:是一个概念,一种思想、理论。描述的是把对象的创建、复制、管理工作都交给代码之外的容器实现。也就是说,对象的创建是由其他的外部资源完成的。
- 2)控制:创建对象,对象的属性赋值,对象之间的关系管理。
- 3)反转:把原来的开发人员管理对象的权限,转移给容器实现。由容器代替开发人员管理、创建对象。
- 正转:由开发人员在代码中使用new构造方法创建对象,开发人员主动管理对象。
- 4)容器:可以是一个服务器软件,也可以是一个框架(spring)
- 5)使用IoC的目的:减少对代码的改动,也能实现不同的功能。实现解耦合。
- 6)java中创建对象的方式
- 构造方法
- 反射
- 序列化
- 克隆
- 动态代理
- ioc
- 7)ioc的体现
- servlet
- 创建类,继承HttpServlet
- 在web.xml中注册servlet
- 我们没有创建Servlet对象。但是可以使用它的属性、方法
- Servlet是Tomcat服务器帮我们创建的 。Tomcat里存放着Servlet、Listener、Filter对象
- servlet
- 8)IoC的技术实现:DI
- DI:依赖注入,只需要在程序中提供要使用的对象的名称就可以了,至于对象如何在容器中创建、赋值、查找都由容器内部实现
- spring是使用DI实现了ioc的功能,spring底层是反射机制
2. 入门
-
1)ioc使用步骤
-
① 创建maven项目,导入依赖
-
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- spring依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.3</version> </dependency>
-
-
② 创建类(接口和实现类)
- 因为spring是反射机制创建对象,必须使用类
-
③ 创建spring需要的配置文件,spring把java对象称为bean
-
<!-- 告诉spring创建对象 1. 声明bean,就是告诉spring要创建某个类的对象 id:对象自定义名称,唯一值。spring通过id找到对象 class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类) 2. spring把创建好的对象放入map中,spring有个map存放对象 springMap.put(id值,对象); 例如:springMap("someService", "new SomeServiceImpl()"); 3. 一个bean标签,声明一个对象 --> <bean id="someService" class="cn.zhangm.service.impl.SomeServiceImpl" /> <bean id="someService2" class="cn.zhangm.service.impl.SomeServiceImpl" /> <!-- spring创建非自定义类的对象,创建一个存在的某个类的对象 --> <bean id="mydate" class="java.util.Date" />
-
-
④ 测试
-
/** * 使用框架创建对象 * spring容器创建时,会创建所有的bean对象 * spring容器创建对象时,使用无参构造方法 */ @Test public void test02(){ //1.指定spring配置文件的名称 String config = "beans.xml"; //2.创建表示spring容器的对象,ApplicationContext //ApplicationContext表示spring容器,通过该容器就可以获取对象了 //ApplicationContext实现类:ClassPathXmlApplicationContext:表示从类路径加载配置文件 ApplicationContext ac = new ClassPathXmlApplicationContext(config); //3.从容器获取对象 //getBean(“配置文件中bean的id值”) SomeService someService = (SomeService)ac.getBean("someService"); someService.doSome(); } /** * 获取spring容器中jvav对象的信息 *方法:getBeanDefinitionCount(),获取对象数量 * getBeanDefinitionNames(),获取对象id值 */ @Test public void test03(){ String config = "beans.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); //spring容器中对象的数量 int beanDefinitionCount = ac.getBeanDefinitionCount(); System.out.println("对象数量:" + beanDefinitionCount); //spring容器中对象的名称 String[] beanDefinitionNames = ac.getBeanDefinitionNames(); for(String name : beanDefinitionNames){ System.out.println(name); } }
-
-
-
2)注意:
- ① spring容器创建时,会创建所有的bean对象
- ② spring容器创建对象时,使用对象的无参构造方法
- ③ spring把创建好的对象放入map中,spring有个map存放对象
3. 基于XML的DI
3.1 注入分类
① set注入
-
1)set注入(设值注入):spring调用类的setXxx()方法
-
2)参数为简单类型(java基本数据类型 + string)
-
<!--语法: <bean id="xx" class="xx" > <property name="属性名字" value="属性值" /> 一个property只能给一个属性赋值 </bean> --> <bean id="mystu" class="cn.zhangm.di01.Student" > <property name="name" value="李四" /> <!--setName("李四")--> <property name="age" value="20" /> </bean>
-
-
3)参数为引用类型(java对象)
-
<!--语法: <bean id="xx" class="xx"> <property name="属性名" ref="bean的id(对象的名称)" /> </bean> --> <bean id="mystu" class="cn.zhangm.di02.Student" > <property name="name" value="李四" /> <property name="age" value="20" /> <!--引用类型赋值--> <property name="school" ref="myschool" /> </bean> <bean id="myschool" class="cn.zhangm.di02.School"> <property name="name" value="北京大学" /> <property name="address" value="北京" /> </bean>
-
② 构造注入
-
1)构造注入:spring调用类的构造方法
-
2)构造注入使用
<constructor-arg>
标签- name:表示构造方法形参名
- index:构造方法参数位置,从左往右 0,1,2…
- value:构造方法的形参类型是简单类型时,使用value
- ref:构造方法的形参类是引用类型时,使用ref
-
3)示例
-
<bean id="mystu" class="cn.zhangm.di03.Student" > <!--构造方法注入--> <constructor-arg name="name" value="小牛" /> <constructor-arg name="age" value="13" /> <constructor-arg name="school" ref="myschool" /> </bean> <bean id="myschool" class="cn.zhangm.di03.School"> <property name="name" value="北京大学" /> <property name="address" value="北京" /> </bean>
-
3.2 自动注入
- 自动注入:spring根据某些规则,可以给引用类型赋值**(自动注入只针对引用类型)**
- 使用规则:byName、byType
① byName方式自动注入
-
1)byName(按名称注入):java类中引用类型的属性名(被赋值)和spring中<bean>的id一样,且数据类型一致,这样的容器中的bean,spring能够赋值给引用类型
-
2)语法:
-
<bean id"xx" class="xx" autowire="byName"> 简单类型赋值 </bean>
-
-
3)示例
-
javabean
-
//在Student对象中声明引用数据类型 private School myschool;
-
-
xml配置文件
-
<!--byname, Student对象中的School类型的属性名为myschool与bean标签的id:myschool一样 --> <bean id="mystu" class="cn.zhangm.di04.Student" autowire="byName" > <property name="name" value="李四" /> <property name="age" value="20" /> </bean> <bean id="myschool" class="cn.zhangm.di04.School"> <property name="name" value="清华大学" /> <property name="address" value="北京" /> </bean>
-
-
② byType方式自动注入
-
1)byType(按类型注入):java类中引用类型的数据类型和spring容器中声明的某个<bean>的class属性是同源关系,这样的bean能够赋值给引用类型。
- 同源:一类的意思,三种同源关系:
- java类中引用类型的数据类型和bean的class值是一样的
- java类中引用类型的数据类型和bean的class值是父子类关系
- java类中引用类型的数据类型和bean的class值是接口和实现类关系
-
2)语法
-
<bean id"xx" class="xx" autowire="byType"> 简单类型赋值 </bean>
-
-
3)示例
-
//声明引用数据类型 private School school;
-
<!--bytype--> <bean id="mystu" class="cn.zhangm.di05.Student" autowire="byType" > <property name="name" value="李四" /> <property name="age" value="20" /> </bean> <bean id="myschool" class="cn.zhangm.di05.School"> <property name="name" value="郑州大学" /> <property name="address" value="郑州" /> </bean>
-
-
4)注意:注意:在byType中,xml配置文件中声明的bean只能有一个符合条件,否则报错
3.3 多个Spring配置文件
-
1)为什么使用多个spring配置文件
- 开发中创建的类比较多,写在一个文件中不便于管理。文件加载慢
-
2)优势
- 每个文件大小不是很大,效率高
- 避免多人竞争带来的冲突
-
3)多文件分配方式
- 按功能模块:一个模块一个配置文件
- 按类的功能:…
-
4)语法
-
<!-- total表示主配置文件,包含其他配置文件,主配置文件一般不定义对象 语法: <import resource="其他配置文件" /> 关键字:"classpath":表示类路径(class文件所在目录),在spring的配置文件中指定其他文件的位置, 需要使用classpath,告诉spring到哪儿去读取文件 --> <!--<import resource="classpath:di06/spring-school.xml" /> <import resource="classpath:di06/spring-student.xml" />--> <!-- 在包含关系的配置文件中,可以使用通配符(*:任意字符) 注意:主配置文件名称不能在通配符范围内 --> <import resource="classpath:di06/spring-*.xml" />
-
4. 基于注解的DI
-
1)使用步骤
-
① 加入依赖spring-context,在加入spring-context的同时,间接加入spring-aop。
- 使用注解,必须使用spring-aop依赖
-
② 在类中加入spring注解
-
/** * @ Component注解:创建对象的,等同于<bean>的功能 * 属性:value:对象的名称,就是bean的id,唯一。创建的对象在整个spring容器中就一个 * 位置:在类的上面 * @ Component等同于:<bean id="myStudent" class="cn.zhangm.anno01.Student" /> */ @Component(value = "myStudent") public class Student { //... }
-
-
③ 在spring配置文件中,加入一个组件扫描器的标签,说明注解在项目中的位置
-
<?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 https://www.springframework.org/schema/context/spring-context.xsd"> <!--声明组件扫描器 base-package:指定注解在项目中的包名 component-scan工作方式:扫描遍历base-package指定的包,扫描包中、子包中所有的类, 找到类中的注解,按照注解的功能创建对象,或给属性赋值 --> <context:component-scan base-package="cn.zhangm.anno01" /> </beans>
-
-
④ 测试对象是否创建
-
@Test public void test01(){ String config = "applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); Student myStudent = ac.getBean("myStudent", Student.class); System.out.println(myStudent); }
-
-
-
2)组件扫描器,指定多个包的三种方式
- ① 使用多次扫描器
- ② 使用分隔符,
;
、,
分割多个包名 - ③ 指定父包
4.1 创建对象的注解
① @Component
-
1)@Component注解:创建对象的,等同于<bean>的功能
- 属性:value:对象的名称,就是bean的id,唯一。创建的对象在整个spring容器中就一个
- 位置:在类的上面
- @ Component等同于:<bean id=“myStudent” class=“cn.zhangm.anno01.Student” />
-
2)使用方法
-
//@Component(value = "myStudent") @Component("myStudent") //省略value //@Component //不使用名称,有spring提供默认名称:类名首字母小写 public class Student { //... }
-
② @Repository、@Service、@Controller
- 1)
@Component
与@Repository、@Service、@Controller
注解的区别(★)@Repository(用在持久层)
:放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的@Service(用在业务层)
:放在service的实现类上面,service对象是做业务处理,有事物功能@Controller(用在控制器上面)
:放在控制器(处理器)上面,创建控制器对象,即servlet
- 2)注意:以上三个注解的使用语法,和@Component的语法一样。但是这三个注解有额外的功能
- @Repository、@Service、@Controller这三个注解是给项目对象分层的
4.2 赋值的注解
① @Value
-
1)
@Value
:给简单类型的属性赋值- 属性:
value="xx"
,是string类型的,表示简单类型的属性值。value一般可以省略直接写值 - 位置:
- 在属性定义的上面,无需set方法,推荐
- 在set方法上面
- 属性:
-
2)示例
-
@Component("myStudent") //省略value public class Student { @Value("张飞") private String name; @Value("20") private Integer age; public Student() { } }
-
② @Autowired
-
1)
@Autowired
:spring框架提供的注解,实现引用类型赋值。spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName、byType。 -
2)
@Autowired
默认使用的时byType
方式的自动注入。需要有同源对象(见基于XML的DI)。-
示例
-
@Autowired private School school;
-
-
3)
@Autowired
使用byName
方式,需要@Qualifier
注解配合-
示例
-
@Autowired @Qualifier("mySchool") private School school;
-
-
4)
@Autowired
的属性required
:是一个boolean类型,默认true- required=true:表示引用类型赋值失败,程序报错,并终止执行。一般使用该方式
- required=false:表示引用类型如果赋值失败,程序正常进行,引用类型赋值null
③ @Resource
-
1)
@Resource
:针对引用类型。来自jdk的注解,spring提供了对该注解的支持。使用的也是自动注入的原理,支持byType、byName。默认byName。 -
2)
@Resource
默认使用byName
的方式注入,如果byName
方式注入失败,会使用byType
方式-
示例
-
@Resource private School school;
-
-
3)
@Resource
只使用byName
方式-
使用属性:
name="bean的id值"
,示例 -
@Resource(name="myschool") private School school;
-
5.XML和注解的选择
- 1)经常改变的用XML
- 2)不经常改变的用注解
三、AOP面向切面编程
1. 动态代理
① 简介
- 1)动态代理能干什么?
- 能创建对象
- 在程序原有代码不改动的情况下,增强功能
- 2)**「★」**JDK动态代理
- 使用jdk中的Proxy、Method、InovcationHandler创建代理对象
- 要求目标类必须实现接口
- 3)**「★」**CGLIB动态代理
- 第三方的工具库,创建代理对象。原理是继承。通过继承目标类,创建子类。子类就是代理对象。
- 要求目标不能时final的,方法也不能时final的。
- 4)**「★」**动态代理的作用
- 在目标类源代码不改变的情况下,增加功能
- 减少代码重复
- 专注业务逻辑代码
- 解耦合,让业务功能和日志、事物、非业务功能分离
② JDK动态代理步骤
-
1)创建目标类,SomeServiceImpl实现类,给它的doSome(),doOther()增添输出时间,事物
-
public interface SomeService { public void doSome(); public void doOhter(); }
-
public class SomeServiceImpl implements SomeService { @Override public void doSome() { System.out.println("dosome..."); } @Override public void doOhter() { System.out.println("doother..."); } }
-
-
2)创建InvocationHandler接口的实现类,在这个类实现目标方法的增强功能
-
public class MyInvocationHandler implements InvocationHandler { //目标对象 private Object target; //SomeServiceImpl类 public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //通过代理对象执行方法时,会调用这个invoke方法 System.out.println("执行了MyInvocationHandler的invoke方法"); String methodName = method.getName(); System.out.println("方法名:" + methodName); Object res = null; if ("doSome".equals(methodName)){ System.out.println("方法执行时间:" + new Date()); //增强功能1 //执行目标类的方法,通过Method类实现 res = method.invoke(target ,args); //SomeServiceImpl类的doSome() System.out.println("方法执行完毕后,提交事物");//增强功能2 }else { res = method.invoke(target ,args); //SomeServiceImpl类的doOther() } //目标方法执行结果 return res; } }
-
-
3)使用jdk中类Proxy,创建代理对象。实现创建对象的能力。
-
public class MyApp { public static void main(String[] args) { //使用jdk的proxy创建代理对象 //创建目标对象 SomeService target = new SomeServiceImpl(); //创建invocationHandler InvocationHandler handler = new MyInvocationHandler(target); //使用proxy创建代理 SomeService proxy = (SomeService)Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); //通过代理执行方法,会调用handler中的invoke方法 proxy.doSome(); System.out.println("=================================================="); proxy.doOhter(); } }
-
2. AOP概述
- 1)AOP(Aspect Orient Programming):面前切面编程,是从动态角度考虑程序运行过程。
- AOP底层就是采用动态代理模式实现的。采用两种动态代理:
jdk的动态代理
、CGLIB的动态代理
- AOP就是动态代理规范化。把动态代理的实现步骤、方式都定义好了,让开发人员用统一的方式使用动态代理。
- AOP底层就是采用动态代理模式实现的。采用两种动态代理:
- 2)**「★」**如何理解面向切面编程?
- OOP:面向对象编程,就是将项目的各种功能封装为对象
- AOP:面向切面编程
- 需要在分析项目功能时,找出切面
- 合理安排切面的执行时间(在目标方法之前,还是在目标方法之后)
- 合理的安排切面执行的位置,在哪个类,在哪个方法增强功能
- 3)一个切面的三个要素
- 切面的功能代码,切面干什么
- 切面的执行位置,使用Pointcut表示切面执行位置
- 切面的执行时间,使用Advice表示时间在目标方法之前还是之后执行
3. AOP编程术语「掌握」
- 1)Aspect:切面,给目标类添加的功能,就是切面。
- 比如以上示例的日志、事物。统计信息、参数检查、权限验证。
- 切面特点:一般都是非业务方法,独立使用。
- 2)Orient:面向
- 3)Programming:编程
- 4)JoinPoint:连接点,连接业务方法和切面的位置。
- 就是某个类中的业务方法
- 5)Pointcut:切入点,指多个连接点方法的集合。多个方法
- 6)目标对象:给哪个类的方法增强功能,它就是目标对象
- 7)Advice:通知,表示切面的功能执行的时间
4. AspectJ对AOP的实现「掌握」
4.1 简介
-
aop是动态的一个规范,一个标准
-
aop的技术实现框架
-
spring:spring内部实现了aop规范,能做aop的工作。
- spring主要在处理事物时使用aop
- 我们项目开发中很少使用spring的aop实现。因为其比较笨重
-
aspectJ:一个开源的专业做aop的框架。Esclise的开源项目。spring中集成了aspectJ框架
- aspectJ框架实现aop有两种方式
- xml:配置全局事物
- 注解:「一般使用」,有5个注解
- aspectJ框架实现aop有两种方式
-
4.2 AspectJ的aop的要素
-
1)切面的执行时间,这个执行时间,在规范中叫做Advice(通知,增强)
@Before
@AfterReturning
@Around
@AfterThrowing
@After
-
2)切面的执行位置,使用的时切入点表达式
-
① 表达式原型
-
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
-
-
② 解释
- modifiers-pattern:访问权限类型
- ret-type-pattern:返回值类型,不可省略
- declaring-type-pattern:包名类名
- name-pattern(param-pattern):方法名(参数类型和参数个数),不可省略
- throws-pattern:抛出异常类型
- ?:表示可选部分
-
③ 总结:
execution([访问权限] 方法返回值 方法声明(参数) [异常类型])
,("[…]"表示可省略部分) -
④ 通配符:表达式中可以使用通配符
*
:0至多个任意字符..
:用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包路径+
:用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类
-
⑤ 举例
execution(public * *(..))
:指定切入点为:任意公共方法execution(* set*(..))
:指定切入点为:任何一个以"set"开始的方法execution(* cn.zhangm.service.*.*(..))
:指定切入点为:该包中的任意类的任意方法execution(* cn.zhangm.service..*.*(..))
:指定切入点为:该包以及子包中的任意类的任意方法execution(* *..service.*.*(..))
:指定切入点为:所有包含service包的service包的任意类的任意方法
-
4.3 使用步骤
-
1)创建maven项目
-
2)导入依赖
- spring依赖
- aspectJ框架依赖
- junit依赖
-
3)创建目标类:接口及其实现类
-
public interface SomeService { void doSome(String name, Integer age); }
-
//目标类 public class SomeServiceImpl implements SomeService { @Override public void doSome(String name, Integer age) { //给doSome方法增强一个功能,在doSome执行之前,输出方法执行时间 System.out.println("目标方法doSome..."); } }
-
-
4)创建切面类:普通类
-
在类的上面添加注解:
@Aspect
-
在类中定义方法,方法就是切面要执行的代码。在方法上面加入aspect中的通知注解,例如
@Before
。还需要指定切入点表达式 -
/** * @Aspect:是aspectj框架中的注解 * 作用:表示当前类时切面类 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码 * 位置:类上名 */ @Aspect public class MyAspect { /** * 定义方法,方法是实现切面功能的 * 方法定义的要求: * 1.公共方法 * 2.没有返回值 * 3.方法名称自定义 * 4.方法可以有参数,也可以没有。 * 如果有参数,参数不是自定义的。有几个参数类型可以使用 */ /** * @Before:前置通知注解 * 属性 * value,是切入点表达式,表示切面的功能执行的位置 * 位置:方法上 * 特点: * 1.在目标方法执行之前执行 * 2.不会改变目标方法的执行结果 * 3.不会影响目标方法执行 */ //切面表达式完整写法 @Before("execution(public void cn.zhangm.aop01.SomeServiceImpl.doSome(String,Integer))") public void myBefore(){ //切面执行功能代码 System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:" + new Date()); } }
-
-
5)创建spring配置文件:声明对象,把对象交给容器管理
-
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--把对象交给spring容器--> <!--声明目标对象--> <bean id="someService" class="cn.zhangm.aop01.SomeServiceImpl" /> <!--声明切面对象--> <bean id="myAspect" class="cn.zhangm.aop01.MyAspect" /> <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象 创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象 所以目标对象就是一个被修改后的代理对象 aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。 --> <aop:aspectj-autoproxy /> </beans>
-
-
6)创建测试类,从spring容器中获取目标对象(实际就是代理对象)。通过代理执行方法,实现方法增强
-
//测试aop之@before注解 @Test public void test01(){ String config = "applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); //从容器中获取目标对象 SomeService proxy = (SomeService) ac.getBean("someService"); //com.sun.proxy.$Proxy8 ---jdk的动态代理 System.out.println(proxy.getClass().getName()); //通过代理的对象执行方法,实现目标方法执行时,增强了功能 proxy.doSome("lisi", 20); }
-
4.4 注解详解
〇 JoinPoint参数
-
1)所有通知注解所注解的方法中都有
JoinPoint
参数- 作用:可以在通知方法中获取方法执行的信息,例如方法名、方法的实参
- JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
-
2)示例
-
@Before("execution(public void cn.zhangm.aop01.SomeServiceImpl.doSome(String,Integer))") public void myBefore(JoinPoint jp){ //获取方法完整定义 System.out.println("方法的签名(定义) = " + jp.getSignature()); System.out.println("方法的名称 = " + jp.getSignature().getName()); Object[] args = jp.getArgs(); for (Object arg:args){ System.out.println("参数:" + arg); } //切面执行功能代码 System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:" + new Date()); }
-
① @Before
- 1)
@Before:前置通知注解
- 前置通知的方法定义格式
- 公共方法
- 没有返回值
- 方法名称自定义
- 方法可以有参数,也可以没有。如果有参数,参数不是自定义的。有几个参数类型可以使用
- @Before的属性:
value
,是切入点表达式,表示切面的功能执行的位置 - 特点
- 在目标方法执行之前执行
- 不会改变目标方法的执行结果
- 不会影响目标方法执行
- 前置通知的方法定义格式
- 2)示例:见4.3代码示例
② @AfterReturning
-
1)
@AfterReturning:后置通知
-
后置通知的方法定义格式
- 公共方法
- 没有返回值
- 方法名称自定义
- 有参数,推荐使用Object,参数名自定义。该参数为目标方法的返回值
-
@AfterReturning的属性
value
:切入点表达式returning
:自定义变量,表示目标方法的返回值。(自定义变量名必须和通知方法的形参名一样)
-
特点
- 在目标方法之后执行
- 能够获取目标方法的返回值,可以根据返回值做不同的处理功能
- 可以修改返回值。(但无法修改目标方法的执行结果?)
-
后置通知的执行顺序
-
Object res = doOther(..) myAfterReturning(res);
-
-
-
2)示例
-
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))", returning = "res") public void myAfterReturning(Object res){ //Object res:是目标方法执行后的返回值 System.out.println("后置通知:在目标方法之后执行,获取的返回值是:" + res); //修改目标方法的返回值,看看是否会影响最后方法调用结果---不会 if (res != null) { res = "hello"; } }
-
③ @Around
-
1)
@Around
环绕通知,相当于jdk的动态代理。经常做事物,在目标方法之前开启事物,在目标方法之后提交事物- 环绕通知的方法定义格式
- public公共方法
- 必须有一个返回值,推荐Object。(影响目标方法的执行结果)
- 方法名自定义
- 方法有参数,是固定的参数:ProceedingJoinPoint,继承了JoinPoint
- @Around的属性:
value
,切入点表达式 - 特点
- 在目标方法的前、后都能增强功能
- 可以控制目标方法是否执行
- 会修改原来的目标方法的执行结果,影响最后的调用结果
- 方法的参数:
ProceedingJoinPoint
- 等同于jdk动态代理的 Method。ProceedingJoinPoint继承了JoinPoint
- 作用:执行目标方法、获取方法参数等
- 方法的返回值:目标方法执行结果返回值,可以被修改
- 环绕通知的方法定义格式
-
2)示例
-
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))") public Object myAround(ProceedingJoinPoint pjp) throws Throwable { String name = ""; //获取参数 Object[] args = pjp.getArgs(); if (args != null && args.length > 1) { Object arg = args[0]; name = (String)arg; } //环绕通知 Object result = null; System.out.println("环绕通知:在目标方法之前输出时间:" + new Date()); //新增功能1 //1.目标方法调用 if ("zhangsan".equals(name)){ //符合条件,调用目标方法 result = pjp.proceed(); //=method.invoke(); } System.out.println("环绕通知:在目标方法之后提交事物:");//新增功能2 //2.在目标方法的前或者后加功能 //3.修改目标方法的执行结果,影响方法最后的调用结果 if (result != null) { result = "hello aspect"; } //返回目标方法的执行结果 return result; }
-
④ @AfterThrowing
-
1)
@AfterThrowing
:异常通知- 异常通知方法定义格式
- public
- 没有返回值
- 方法名自定义
- 方法有一个参数
Exception
- @AfterThrowing的属性
- value:切入点表达式
- throwing:自定义的变量,表示目标方法抛出的异常对象。变量名必须和方法参数名一样
- 特点:
- 在目标方法抛出异常时,执行
- 可以做异常的监控程序,监控目标方法执行时是不是有异常,如果有,可以发送邮件、短信通知
- 异常通知方法定义格式
-
2)示例
-
AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))", throwing = "ex") public void myAfterThrowing(Exception ex){ System.out.println("异常通知:在方法发生异常时,执行。" + ex.getMessage()); //发送邮件、短信通知开发人员 }
-
⑤ @After
-
1)
@After
:最终通知- 方法定义格式
- public
- 没有返回值
- 方法名自定义
- 没有参数
- 属性:value
- 特点
- 总是会执行,相当于finaly{…}里面的代码
- 目标方法之后执行
- 方法定义格式
-
2)示例
-
@After(value = "execution(* *..SomeServiceImpl.doThird(..))") public void myAfter(){ System.out.println("执行最终通知,总是执行"); //一般做资源清除 }
-
⑥ @Pointcut
-
1)
@Pointcut
:定义和管理切入点,如果项目中有多个切入点表达式是重复的,复用的。使用- 属性:value
- 特点
- 当使用@Pointcut定义在一个方法上时,该方法的名称就是切入点表达式的别名。其他的通知中的value属性,就可以使用这个别名
-
2)示例
-
@After(value = "mypt()") public void myAfter(){ System.out.println("执行最终通知,总是执行"); //一般做资源清除 } @Before(value = "mypt()") public void myBefore(){ System.out.println("前置通知,在目标方法之前执行"); } @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))") private void mypt(){ //无需代码 }
-
5. 总结
-
1)通过以上案例我们可以发现aspectJ框架底层实现aop技术是使用的jdk的动态代理。
- 使用
jdk的动态代理
要求,必须有接口和其实现类
- 使用
-
2)若无接口,使用动态代理,使用的是
cglib的动态代理
- 如何使用?:在有接口及其实现类的基础上去掉接口即可。
-
3)如有接口,想用
cglib的动态代理
怎么做?-
在有接口及其实现类的基础上,在spring配置文件中添加:
-
<aop:aspectj-autoproxy proxy-target-class="true" />
-
四、srping集成mybatis
0. 简介
- 1)spring和mybatis的集成,使用的是ioc技术
- ioc能创建对象,可以把mybatis框架中的对象,交给spring统一创建,开发人员从spring中获取对象。开发人员就不用同时面对多个框架了,就面对spring。
- 2)我们需要spring帮我们创建哪些对象?
- 独立的连接池类的对象,Druid(srping内置连接池不好用,舍弃)
- SqlSessionFactory对象
- 创建dao对象
1. 创建项目
-
1)创建maven项目
-
2)导入依赖
-
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--spring ==ioc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.3</version> </dependency> <!--spring事物用的--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.3</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.1</version> </dependency> <!--mybatis集成spring--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <!--阿里数据库连接池druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency>
-
-
3)导入插件
-
<build> <resources> <resource> <directory>src/main/java</directory> <!--文件所在目录--> <!--包括目录下的properties和xml文件都会扫描到--> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <!--false不启用过滤,因为*.properties已经起到过滤作用了--> <filtering>false</filtering> </resource> </resources> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
-
2. 创建实体类
- 略
3. 创建Dao接口及mapper文件
-
1)dao接口
-
public interface StudentDao { int insertStudent(Student student); List<Student> selectStudents(); }
-
-
2)mapper文件
-
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.zhangm.dao.StudentDao"> <select id="selectStudents" resultType="cn.zhangm.domain.Student"> select * from student </select> <insert id="insertStudent"> insert into student values(#{id}, #{name}, #{email}, #{age}) </insert> </mapper>
-
4. mybatis核心配置
-
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--开启mybatis内置的日志功能--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!--启别名--> <typeAliases> <package name="cn.zhangm.domain"/> </typeAliases> <!--sql mapper(sql映射文件)的位置--> <mappers> <!--扫描该包下的所有xxx.xml文件--> <package name="cn.zhangm.dao" /> </mappers> </configuration>
-
因为我们不使用mybatis内置的连接池,所以省略了
<environments>
的配置
5. 创建service
-
该步骤意在模拟web开发中的业务层,调用dao方法执行数据库操作
-
//接口 public interface StudentService { int addStudent(Student student); List<Student> queryStudent(); }
-
//实现类 public class StudentServiceImpl implements StudentService { //引用类型 private StudentDao studentDao; //为了使用set注入,赋值 public void setStudentDao(StudentDao studentDao) { this.studentDao = studentDao; } @Override public int addStudent(Student student) { int nums = studentDao.insertStudent(student); return nums; } @Override public List<Student> queryStudent() { List<Student> students = studentDao.selectStudents(); return students; } }
6. spring配置文件
-
1)创建spring配置文件:声明mybatis对象交给spring创建
- ① 数据源:DataSource。在spring和mybatis集成中叫做:
com.alibaba.druid.pool.DruidDataSource
- druid的使用在github上有说明。
- 必要属性:
url、username、password、maxActive
- ② SqlSessionFactory。在spring和mybatis集成中叫做:
org.mybatis.spring.SqlSessionFactoryBean
dataSource
:指明数据源(值为连接池bean的id)configLocation
:指明mybatis配置文件
- ③ Dao对象。在spring和mybatis集成中叫做:
org.mybatis.spring.mapper.MapperScannerConfigurer
sqlSessionFactoryBeanName
:指明由哪个工厂创建basePackage
:指明dao接口所在包名。( 因为getMapper(XxxDao.class) )
- ④ 声明自定义的service
- ① 数据源:DataSource。在spring和mybatis集成中叫做:
-
2)示例
-
<!--把数据库信息放在一个单独文件中,方便管理 获取文件数据语法:${key} --> <context:property-placeholder location="classpath:jdbc.properties" /> <!--声明数据源DataSource,作用连接数据库的--> <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close" > <!--set注入,给DruidDataSource提供连接数据库的信息。4个重要属性以下:--> <property name="url" value="${jdbc.url}" /><!--setUrl()--> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="maxActive" value="${jdbc.maxActive}" /> </bean> <!--声明mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--set注入,把数据库连接池赋给了dataSource属性--> <property name="dataSource" ref="myDataSource" /> <!--mybatis主配置文件的位置 configLocation的类型是Resource(spring的资源,读取配置文件) 它的赋值,使用value,指定文件路径,使用classpath:xxx --> <property name="configLocation" value="classpath:mybatis.xml" /> </bean> <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class) MapperScannerConfigurer在内部调用getMapper()生成每个dao接口的代理对象 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--指定SqlSessionFactory的id--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <!--指定包名,dao接口所在包名 MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行一次 一个getMapper()方法,得到每个接口的dao对象。 创建好的对象放入到spring容器中,对象名称是接口的首字母小写 --> <property name="basePackage" value="cn.zhangm.dao" /> </bean> <!--声明service--> <bean id="studentService" class="cn.zhangm.service.impl.StudentServiceImpl"> <property name="studentDao" ref="studentDao" /> </bean>
-
7. 测试
-
//测试service的使用 @Test public void testService(){ String config = "applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(config); //获取spring容器中的dao对象 StudentService service = ac.getBean("studentService", StudentService.class); List<Student> students = service.queryStudent(); System.out.println("students = " + students); }
五、spring事物
1. 简介
-
1)事物:指一组sql语句的集合,这些语句要么同时成功,要么同时失败
-
2)何时使用事物:涉及多个表等需要sql语句同时成功才执行的地方。例如:转账
- 在java中,事物放在service类的方法上
-
3)如何处理事物
- JDBC:
Connection conn = ..; conn.commit(); conn.rollback();
- Mybatis:
sqlSession.commit(); sqlSession().rollback()
- JDBC:
-
4)(3)中处理事物的不足
- 不同数据库访问技术,处理事物的方法不同。需要了解不同技术的事物实现
- 需要掌握多种数据库事物的处理逻辑
- 处理数据库的多种方法
- 总结:多种数据库访问技术,有不同的事物处理机制、对象、方法
-
5)如何解决(3)中的不足
- spring提供一种处理事物的统一模型。能使用统一的方式,完成多种不同数据库访问技术的事物处理。
-
6)spring如何处理事物
-
① 事物内部提交、回滚事物,使用的是事务管理对象。
- 接口:
PlatformTransactionManager
,定义事物的提交和回滚
- 接口:
-
② 告诉spring使用哪种数据库访问技术
-
声明数据库访问技术对于事物管理器的实现类,例如:
-
<bean id="xxx" class="...DataSourceTransactionManger">
-
-
③ 说明方法需要的事物的类型
- 事物的隔离级别
- 事物的超时时间
- 事物传播行为
-
④ 事物提交、回滚的时机
- 当业务方法执行成功,没有异常抛出,spring在方法执行完毕后自动提交事物
- 当业务方法抛出运行时异常和ERROR时,spring回滚事物,调用事务管理器的rollback
- 当业务方法抛出非运行时异常,主要是受查异常时,提交事物
- 受查异常:写代码时,必须处理的异常,例如:IOException
-
2. 事物管理API
① 事物管理器接口(重点)
- 1)事物管理器是
PlatformTransactionManager
接口对象。用于事物提交、回滚、获取事物状态信息 - 2)接口中的方法
void commit(TransactionStatus status)
TransactionStatus getTransaction(TransactionDefiniton definition)
void rollback(TransactionStatus status)
- 3)常用的两个实现类
DataSourceTransactionManager
:使用JDBC或Mybatis操作数据库时使用HibernateTransactionManager
:使用Hibernate操作数据库时使用
- 4)Srping事物的默认回滚方式时:
发生运行时异常和error时回滚,发送受查(编译)异常时提交
② 事物定义接口
- 1)
TransactionDefinition
接口定义了事物描述相关的三类常量:事物隔离级别、事物传播行为、事物默认超时时限,及对其操作。 - 2)隔离级别
DEFAULT
READ_UNCOMMITTED
:读未提交,未解决任何并发问题READ_COMMITTED
:读已提交,解决脏读,存在不可重复读与幻读。(oracle默认)REPEATABLE_READ
:可重复读,解决脏读、不可重复读,存在幻读。(mysql默认)SERIALIZABLE
:串行化,不存在并发问题
- 3)传播行为:控制业务方法是否有事物,是什么样的事物。(掌握前三个)
PROPAGATION_REQUIRED
:有事物使用事物,没有事物创建事物,默认PROPAGATION_REQUIRED_NEW
:总创建一个新事物PROPAGATION_SUPPORTS
:支持事物,有没有事物都行PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
- 4)超时时限
TIMEOUT_DEFAULT
常量定义了事物底层默认的超时时限- 表示最长执行时间,超过时间,回滚。单位秒,
默认 -1
3. 注解管理事物
① 简介
- 1)注解管理事物:适用于中小项目使用
- 2)spring框架自己用aop实现给业务方法增加事物的功能,
@Transactional
注解管理事物。- 该注解放在public方法上,表示当前方法具有事物
- 可以给注解的属性赋值,表示具体的隔离级别、传播行为、异常信息等
- 3)
@Transactional
的属性propagation
:事物的传播属性,该属性类型为枚举,默认值Propagation.REQUIRED
isolation
:事物的隔离级别,枚举,默认值Isolation.DEFAULT
readOnly
:对数据库操作是否是只读的,默认值false
timeout
:操作与数据库连接超时时限,单位秒,默认值-1
即没有时限rollbackFor
:指定需要回滚的异常类。类型为Class[],默认值空数组
rollbackForClassName
:指定需要回滚的异常类类名,类型为String[],默认值空数组
noRollbackFor
:指定不需要回滚的异常类,类型为Class[],默认值空数组
noRollbackForClassName
:指定不需要回滚的异常类类名,类型为String[],默认值空数组
② 使用步骤
-
1)声明事物管理器对象
-
<bead id="xxx" class="DataSourceTransactionManager">
-
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--连接哪个数据库。指定数据源--> <property name="dataSource" ref="myDataSource" /> </bean>
-
-
2)开启事物注解驱动,告诉spring我要使用注解方式管理事物
-
spring使用aop机制,创建@Transactional所在类代理对象,给方法加入事物功能
-
spring如何给业务方法加入事物
- 在业务方法执行之前,先开启事物,在业务方法之后提交或回滚事物,使用aop环绕通知
-
<!-- 2.开启事物注解驱动,告诉spring使用注解开启事物,创建代理对象 transaction-manager:事物管理器对象的id --> <tx:annotation-driven transaction-manager="transactionManager" />
-
-
3)在方法上添加事物注解
-
/** * 测试spring事物管理 * * rollbackFor:表示发生指定的异常一定回滚 * 处理逻辑: * 1)spring首先会检查方法抛出的异常是不是在rollbackFro的属性值中 * 如果异常在该属性值中,不管是什么类型的异常,一定回滚 * 2)如果抛出的异常不自在rollbackFro列表中,spring会判断异常是不是RuntimeException, * 如果是,一定回滚 */ // @Transactional( // propagation = Propagation.REQUIRED, // isolation = Isolation.DEFAULT, // readOnly = false, // rollbackFor = { // NullPointerException.class, // NotEnoughException.class // } // ) //使用的都是默认值,默认抛出运行时异常,回滚事物 @Transactional @Override public void buy(Integer goodsId, Integer nums) {...}
-
4. AOP配置管理事物
① 简介
- 1)适合大型项目,有很多类、方法需要大量配置事物
- 2)使用aspectj框架功能,在spring配置文件中声明类、方法需要的事物。
② 使用步骤
-
1)加入aspectj的依赖
-
2)声明事物管理器对象
-
<!-- 1.声明事物管理器对象--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--连接哪个数据库。指定数据源--> <property name="dataSource" ref="myDataSource" /> </bean>
-
-
3)声明方法需要的事物的类型(配置方法的事物属性【隔离级别、传播行为、超时】)
-
<!--2.声明业务方法的业务属性(隔离级别、传播行为、超时时间) id:自定义名称,表示<tx:advice>和</tx:advice>之间的配置内容的 transaction-manager:事务管理器对象的id --> <tx:advice id="myAdvice" transaction-manager="transactionManager"> <!--配置事物的属性--> <tx:attributes> <!--给具体的方法配置属性 name:方法名称,1)完整的方法名,不带包和类。2)方法可以使用通配符,* = 任意字符 propagation:传播行为, isolation:隔离级别 rollback-for:指定的异常类名,全限定名。发送异常一定回滚 --> <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" rollback-for="cn.zhangm.excep.NotEnoughException,java.lang.NullPointerException"/> <!--使用通配符,指定多个方法--> <!--添加方法--> <tx:method name="add*" propagation="REQUIRES_NEW"/> <!--修改方法--> <tx:method name="modify*" /> <!--删除方法--> <tx:method name="remove*" /> <!--查询方法,query、search、find--> <tx:method name="*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice>
-
-
4)配置aop,指定哪些类要创建代理(即哪些包的哪些类的方法需要配置事物)
-
<!--配置aop--> <aop:config> <!--配置切入点表达式 id:切入点表达式的名称 expression:切入点表达式,指定哪些类要创建事物,aspectj会创建代理对象 --> <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))" /> <!--配置增强器:关联advice和pointcut dvice-ref:通知,上面tx:advice pointcut-ref:切入点表达式的id --> <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/> </aop:config>
-
六、spring与web
1. 简介
- 1)需求:web项目中容器对象只需要创建一次。
- 把容器对象放入到全局作用域ServletContext中
- 2)如何做?
- 使用监听器,当全局作用域对象被创建时,创建容器,存入ServletContext中
- 3)监听器的作用?
- 创建容器对象ApplicationContext
- 把容器对象放入到ServletContext
- 4)监听器可以自己创建,也可以使用框架中提供好的
ContextLoaderListener
2. 如何做
-
1)在web.xml中配置监听器:
ContextLoaderListener
-
<context-param> <!--contextConfigLocation表示配置文件路径--> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
-
监听器创建后会读取
/WEB-INF/applicationContext.xml
文件 -
为什么读取该文件:
- 因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
/WEB-INF/applicationContext.xml是监听器默认读取的spring配置文件路径
- 因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
-
可以修改默认路径,使用
<context-param>
标签
-
-
2)在servlet中获取容器
-
/* ======================================================================== * 创建spring容器对象,获取的容器对象并不是唯一的,每一次进入该servlet就会创建一个该对象, * 而创建容器对象时,所有交给spring创建的对象都会创建一次,造成内存浪费 * */ //String config = "applicationContext.xml"; //ApplicationContext ac = new ClassPathXmlApplicationContext(config); //======================================================================= WebApplicationContext ctx = null; //获取ServletContext中的容器 /* String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE; Object attribute = this.getServletContext().getAttribute(key); if (attribute != null) { ctx = (WebApplicationContext)attribute; } */ //使用工具类获取 ServletContext servletContext = this.getServletContext(); ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
-
七、总结
-
1)注入的注解形式需要声明
组件扫描器
-
<context:component-scan base-package="cn.zhangm.anno01" />
-
-
2)aspectj的aop需要声明
代理器
-
<aop:aspectj-autoproxy />
-
-
3)spring的配置文件中读取
.properties
格式文件的值,需要声明-
<context:property-placeholder location="classpath:jdbc.properties" />
-
-
4)spring事物管理需要声明
注解驱动
-
<tx:annotation-driven transaction-manager="事物管理器对象的id" />
-
pringframework.web.context.ContextLoaderListener
```
-
监听器创建后会读取
/WEB-INF/applicationContext.xml
文件 -
为什么读取该文件:
- 因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
/WEB-INF/applicationContext.xml是监听器默认读取的spring配置文件路径
- 因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
-
可以修改默认路径,使用
<context-param>
标签 -
2)在servlet中获取容器
-
/* ======================================================================== * 创建spring容器对象,获取的容器对象并不是唯一的,每一次进入该servlet就会创建一个该对象, * 而创建容器对象时,所有交给spring创建的对象都会创建一次,造成内存浪费 * */ //String config = "applicationContext.xml"; //ApplicationContext ac = new ClassPathXmlApplicationContext(config); //======================================================================= WebApplicationContext ctx = null; //获取ServletContext中的容器 /* String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE; Object attribute = this.getServletContext().getAttribute(key); if (attribute != null) { ctx = (WebApplicationContext)attribute; } */ //使用工具类获取 ServletContext servletContext = this.getServletContext(); ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
-
七、总结
-
1)注入的注解形式需要声明
组件扫描器
-
<context:component-scan base-package="cn.zhangm.anno01" />
-
-
2)aspectj的aop需要声明
代理器
-
<aop:aspectj-autoproxy />
-
-
3)spring的配置文件中读取
.properties
格式文件的值,需要声明-
<context:property-placeholder location="classpath:jdbc.properties" />
-
-
4)spring事物管理需要声明
注解驱动
-
<tx:annotation-driven transaction-manager="事物管理器对象的id" />
-