Spring 框架第二天 AOP切面编程
今天重点内容:
1、 什么是AOP ? AOP实现原理是怎样的? AOP相关术语
2、 AOP底层实现 (了解) ----- JDK动态代理、 Cglib动态代理
3、 传统Spring AOP 编程 (了解实现,掌握使用 )
4、 基于AspectJ 框架,实现spring2.0之后 AOP 编程(重点)
5、 Spring JdbcTemplate 模板工具类 使用 (ORM 解决方案 )--- 类似Apache DbUtils
1. AOP面向切面编程概述
1.1. 什么是AOP ?
l AOP Aspect Oriented Programing 面向切面编程
1) 一种程序设计思想
2)
3) AOP是OOP (Object Oriented Programing )延伸
l 思想:AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
AOP底层就是代理的增强 !
l 面试题:在项目哪些功能使用AOP ? --- AOP使用场景
1、性能监视 (方法运行时间)
2、事务管理
3、安全检查 (权限 )
4、缓存优化 (第一次访问,查询数据,放入缓存, 第二次以后访问 从缓存返回 )
5、记录日志
l 学习AOP 分为两个部分
1、 学习传统Spring AOP编程 (Spring1.2年代 )
2、 学习AspectJ 框架的AOP编程 (Spring2.0以后年代 )
1.2. AOP开发术语
切面由切入点和通知组成!
2. AOP的底层实现(了解)
2.1. JDK 动态代理机制
1、必须针对接口进行代理
2、生成代理对象,通过Proxy类
进行代理 ,传入目标对象类加载器、 目标对象接口 、 处理类
3、自己实现InvocationHandler 接口
对目标对象方法调用时,都会执行invoke方法,起到拦截作用
第一步: 编写业务接口和实现类
第二步: 编写传统调用,编写基于代理调用
// 生成代理工具类
public class JdkProxyFactory implements InvocationHandler{
// 目标对象
private Object target;
public JdkProxyFactory(Object target) {
this.target = target;
}
// 创建代理对象
public Object createJdkProxy(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 记录日志
System.out.println("日志记录:" + method.getName() + "执行了...");
return method.invoke(target, args);
}
}
第三步: 测试代码
2.2. Cglib动态代理机制
JDK只能对接口进行代理,如果目标对象没有接口,无法使用JDK动态代理 ---- 使用cglib
什么是cglib ?
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
Cglib可以对接口或者类进行代理 !
使用Spring最新版本开发,无需单独下载cglib (spring core 已经集成cglib )
Cglib开发依赖 asm的jar (spring core 已经集成 asm )
1、 编写目标业务类
2、编写Cglib 生成代理工厂类
// 生成cglib代理工厂
public class CglibProxyFactory implements MethodInterceptor {
// 目标对象
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
// 生成代理对象方法
public Object createCglibProxy() {
// 通过Enhancer 生成代理
Enhancer enhancer = new Enhancer();
// 设置超类
// 生成UserDAO的子类
enhancer.setSuperclass(target.getClass());
// 设置回调处理类
enhancer.setCallback(this);
// 生成代理返回
return enhancer.create();
}
@Override
/**
* proxy 代理对象
* method 方法反射对象
* args 参数
* methodProxy 方法的代理对象,用于执行父类的方法
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 日志: 调用了" + method.getName() + "方法...");
// return method.invoke(target, args);
return methodProxy.invokeSuper(proxy, args);// 调用真实对象方法
}
}
3、编写测试类
小结:
Spring AOP 就是基于JDKProxy 和 CglibProxy
1、如果目标对象有接口,优先使用JDK Proxy
2、如果目标对象没有接口, 使用CglibProxy
3. 传统Spring AOP 切面编程
传统 : Spring1.2 开始支持AOP编程 (这种编程,现在自己写的很少,但是我们会用到 )
Spring AOP编程,必须学习切入点和通知
1) 切入点,通过配置完成的
2) 通知,通过编程完成的
3.1. 传统AOP编程,通知类型(5种)
不同Advice 执行增强代码的时间点不同 !
3.2. 编写一个Advice
1、 导入jar包
传统Spring AOP开发
- com.springsource.org.aopalliance-1.0.0.jar AOP联盟定义接口规范
- spring-aop-3.2.0.RELEASE.jar Spring对AOP扩展支持
整合AspectJ AOP开发
- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar 第三方AspectJ 包
- spring-aspects-3.2.0.RELEASE.jar Spring 对AspectJ 扩展支持
2、 编写Advice 实现 MethodInterceptor 环绕增强
3.3. AspectJ 切入点语法
为什么要学习AspectJ切入点语法?
传统AOP切入点 ,使用正则表达式语法,现在基本上不去用
Spring没有支持所有AspectJ语法,所以我们学习Spring AOP 只能spring文档 !
AspectJ切入点,是通过函数进行配置
l execution 执行
n 语法:execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
execution(* *(..)) 第一个* 任意返回类型 , 第二个* 任意方法名 , .. 任意参数
execution(* cn.itcast.service.UserService.*(..)) 匹配UserService所有方法
execution(* cn.itcast.service.UserService+.*(..)) 匹配UserService子类所有方法
execution(* cn.itcast.service..*.*(..)) 第一个.. 任意子包 *.*任何类的任何方法
l within 根据包匹配
n 语法:within(包名..*)
within(cn.itcast.service..*) 拦截service下所有类的方法
l this根据目标类型匹配
n 语法:this(类名)
this(cn.itcast.service.UserService) 拦截 UserService所有方法 (包括代理对象)
l target 根据目标类型匹配
n 语法 :target(类名)
target(cn.itcast.service.UserService) 拦截UserService所有方法 (不包括代理对象 )
l args 根据参数匹配
args(java.lang.String) 拦截所有参数为String类的方法
l bean 根据bean name匹配
bean(userService) 拦截bean id/name为userService对象所有方法
3.4. 配置传统Spring AOP切面
1、 引入aop的命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
</beans>
2、 配置切面
3、编写测试
图解:
注意: 需要对有接口实现类直接代理,不对接口代理 !
4. AspectJ AOP切面编程(XML配置)
Spring1.2 开始支持AOP编程,主要代理对象创建通过ProxyFactoryBean构造 !
后来人们发现spring1.2 AOP开发太麻烦了,spring2.0 引入对AspectJ(第三方AOP框架)支持,AOP开发就被简化了。
4.1. AspectJ支持通知类型 (6种)
相比传统AOP编程,多了一个After通知(最终通知, 效果finally )
4.2. Before前置通知使用
分析前置通知应用场景: 权限控制、 记录访问日志
第一步: 编写Advice
第二步: 配置切入点和切面
面试题: advisor和 aspect的区别 ?
Advisor是传统Spring AOP 切面,只能有一个通知和一个切入点
Aspect 是AspectJ 提供切面,可以包括多个通知和多个切入点
面试题: aspect和aspectJ区别 ?
aspect 切面, 由切入点和通知组成
aspectJ 框架,进行AOP开发
===================== 扩展 struts2 拦截器机制是AOP思想吗?
1、 访问Action 进行代理 ActionProxy
2、 提供Interceptor 类似Advice 增强 ,通过配置使不同Action访问前执行不同拦截器
4.3. AfterReturing 后置通知使用
在目标类的方法运行之后,进行代码增强
特性:操作目标方法返回值
使用AspectJ 框架开发,可以将多个增强的方法,写在同一个类里面 !
第一步: 编写增强方法
第二步: 配置applicationContext.xml
4.4. Around 环绕通知使用
在目标类方法 执行前和执行后,都可以增强
场景: 1) 运行方法时间监控 2) 事务管理 3) 权限控制 4) 缓存 5) 日志
第一步: 编写Advice增强方法
第二步: 配置切入点和切面
4.5. AfterThrowing 抛出通知使用
在目标发生异常时,执行增强代码
场景: 目标方法异常,日志记录异常,将异常信息发送邮件给管理员
第一步: 编写抛出增强方法
第二步: 配置切入点和切面
当发生异常时, 目标方法后置通知不会执行的!!
4.6. After最终通知使用
无论目标方法是否出现异常,都将执行增强代码
场景: 释放资源
try {
开启事务…
执行代码
} catch {
事务回滚
} finally {
事务提交
}
第一步: 编写增强方法
第二步: 配置切入点和切面
小结: Around环绕通知什么都可以
try{
前置通知…
执行目标方法
后置通知…
}catch {
异常通知…
}finally {
最终通知…
}
5. AspectJ AOP 切面编程 (注解配置 )
新建 spring3_day3_annotation web 项目, 导入spring AOP开发jar包
5.1. 编写目标业务类
配置applicationContext.xml bean的扫描
5.2. 编写Advice增强
配置applicationContext.xml
使用注解修饰Advice类
@Aspect 这是一个切面
@Before 前置通知
@AfterReturning 后置通知
@Around 环绕通知
@AfterThrowing 抛出通知
@After最终通知
@Pointcut 切入点
小结:
1、 注册Bean <context:component-scan> (@Service、@Controller、 @Repository 、@Component )
2、 声明具有增强方法类是一个切面 @Aspect
3、 在对应方法上,声明各种通知 @Before @AfterReturning @Around @AfterThrowing @After
4、 根据切面注解自动生成代理 <aop:aspectj-autoproxy />
5.3. 使用@Pointcut 定义切入点
将公共切入点表达式,抽取出来,便于后期维护
Spring规定,将@Pointcut注解加到 私有、无返回值、无参数的方法上 (该方法的名字 就是切入点的名字 !)
如果引用切入点,类似方法调用
能否在一个通知上, 应用多个切入点 可以 && 或者 || 引入多个
pointcut1() && pointcut2() , 必须同时满足 pointcut1和pointcut2 切入点表达式
pointcut1()||pointcut2() 只需要满足 pointcut1 或者 pointcut2 当中一个切入点表达式
6. Spring JdbcTemplate
学习spring 重点三--- 六章
第三章:核心技术: IoC和AOP
第四章:数据访问: (JDBC数据访问 JdbcTemplate 、 事务管理 、 整合ORM框架 )
第五章:Web应用 :SpringMVC --- 后续课程
第六章:整合
6.1. JdbcTemplate 快速入门
JdbcTemplate 用来简化JDBC 编程开发,使用效果类似 DbUtils 框架
第一步: 新建web项目 spring3_day2_jdbctemplate ,导入jar包
4个核心
2个日志
1个web集成
1个junit集成
1个inject
4个AOP
导入 jdbc和tx 2个jar包
导入 oracle驱动
第二步: 编写无配置文件Jdbc程序
-- Create the user
create user SH1026
identified by ""
default tablespace SYSTEM
temporary tablespace TEMP
profile DEFAULT;
-- Grant/Revoke role privileges
grant connect to SH1026;
grant resource to SH1026;
-- Grant/Revoke system privileges
grant unlimited tablespace to SH1026;
6.2. 使用spring配置管理JdbcTemplate
在applicationContext.xml 配置数据库连接池和JdbcTemplate
6.2.1. DrvierManagerDataSource 配置
测试
6.2.2. DBCP 连接池BasicDataSource 配置
导入 dbcp 和pool的jar包
配置
6.2.3. C3P0连接池 CombopooledDataSource 使用
导入c3p0的jar包
配置
6.2.4. JNDI数据库配置使用
什么是JNDI ?
JNDI(Java Naming and Directory Interface,Java命名和目录接口)
在JNDI容器进行配置,将对象交给容器创建和管理,为对象进行命名 ,在JNDI容器其它程序通过名字去访问这个对象。
第一步: 在tomcat配置数据库连接池
需要在<Context>元素下配置 <Resouce> 元素
配置位置 : server.xml 、context.xml (所有项目生效)、 项目目录 WebRoot/META-INF (只对当前项目有效 )
在项目WebRoot/META-INF 新建context.xml
将数据库驱动包,复制tomcat/lib下
第二步: spring访问 配置JNDI
配置applicationContext.xml
编写Servlet ,从Spring获取对象
6.3. 外部属性文件的配置使用
什么是外部属性文件?
属性文件 --- properties文件
XML中很多内容,格式比properties 复杂, 将经常需要改动属性数据,配置properties文件, 便于后期维护!
在applicationContext.xml 引入属性文件,通过${} 引入属性值
6.4. 编写DAO程序,实现数据表CRUD操作
6.4.1. 建立数据表,编写实体类
建立customers数据表
-- Create table
create table customers
(
id number,
name varchar2(50),
city varchar2(100),
age number
)
;
-- Create/Recreate primary, unique and foreign key constraints
alter table customers
add constraint customers_pk primary key (ID);
实体类
6.4.2. 在DAO中注入JdbcTemplate
配置
6.4.3. 通过update方法,实现增加、修改、删除
JdbcTemplate提供
6.4.4. 通过queryXxx方法实现查询
l 返回简单类型数据
返回int : queryForInt
返回long: queryForLong
返回String : queryForObject(sql, requiredType, args ) requiredType就是返回类型
l 返回复杂类型数据:
需要使用RowMapper对象
什么是RowMapper ? 将每行数据封装为Customer对象
问题: 如果表中列非常多, 自定义RowMapper 非常复杂
类属性名和表列名,代码能否优化 !
Spring JdbcTemplate 提供ParameterizedBeanPropertyRowMapper ,自动完成同名列和属性 自动对应封装 !
今天总结:
1、 IoC和DI的概念和区别? IoC是如何实现的 ?
2、 什么AOP? 如何实现的? 在项目哪些功能中使用到AOP ?
3、 BeanFactory和FactoryBean的区别 ?
4、 singleton和prototype的区别 ? 能否写出单例设计模式代码实现 ?
5、 BeanPostProcessor 后处理的作用 ?
6、 如何用xml和注解 定义Bean? 如何完成属性注入 ?
7、 Spring AOP 使用什么技术实现的 ? 底层技术 JDK和Cglib动态代理
8、 传统Spring AOP 开发和配置
9、 重点掌握 AspectJ AOP 开发和配置 (XML和注解,重点掌握一种 )
10、 advisor、advice、aspect 区别
11、 SpringAOP 应用: 运行时间、 权限控制、 缓存、事务管理 …
12、 数据源配置 JNDI 和 C3p0 重点