今日内容
l AOP的概述
l AOP 的底层实现
l Spring 的AOP
l 使用AspectJ实现AOP
l Spring JdbcTemplate使用
在软件业,AOP为Aspect OrientedProgramming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容.
AOP的出现为了解决OOP(面向对象编程)中的一些问题.
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
就是代理机制:
Spring的传统的AOP:
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
基于AspectJ的开发的AOP:(*****)
AspecJ是一个基于Java语言的AOP框架,Spring2.0开始,SpringAOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合.
底层实现就是代理机制:
* JDK动态代理 和 CGLIB代理.
原理:
* 生成了一个接口的实现类.
JDK动态代理的使用:
* Proxy:
static Object |
newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) |
* 三个参数:
* ClassLoader :类的加载器.
* Class[] :实现的所有接口.
* InvocationHandler :处理接口.
* Object invoke(Object proxy,Method method,Object[] args);
* Object:代理对象.
* Method:执行的方法.
* Object[]:执行这个方法的参数列表.
* 调用真实对象的方法的时候;
* UserDao(接口)---->UserDaoImpl(实现类)
* 对UserDaoImpl生成一个代理类.
* UserDao userDao = Proxy.newProxyInstance(..);
* userDao.add();
* userDao.update();
***** 都相当于调用了InvocationHandler中的invoke()方法.
JDK动态代理的代码实现:
public class JdkProxy implements InvocationHandler{ private UserDao userDao; public JdkProxy(UserDaouserDao) { this.userDao = userDao; } public UserDao createProxy(){ Object proxy =Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(), this); return (UserDao) proxy; } @Override public Object invoke(Objectproxy, Method method, Object[] args) throws Throwable { // 判断执行的方法 // 对add方法进行增强 if("add".equals(method.getName())){ // 增强 System.out.println("增强的代码..."); returnmethod.invoke(userDao, args); }else{ // 不增强 returnmethod.invoke(userDao, args); } } }
JDK的动态代理有局限性:类必须实现接口,才能生成代理.(一个类没有实现接口,JDK无法生成代理对象.)
CGLIB代理:可以对没有实现接口的类生成代理.
CGLIB(Code GenerationLibrary)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
* Hibernate :javassist
原理:
* 通过字节码技术,对这个类生成了一个子类对象.
* 使用CGLIB
* 步骤一:下载CGLIB相应的jar包.
* Spring的新的版本中,核心中引入cglib的jar包.
* 步骤二:编写一个类:(没有实现接口的类)
* 步骤三:编程生成代理的类:
public class CglibProxy implements MethodInterceptor{ private ProductDao productDao; public CglibProxy(ProductDaoproductDao) { this.productDao = productDao; } // 生成代理的方法: public ProductDaocreateProxy(){ // CGLIB核心对象. Enhancer enhancer = new Enhancer(); // 原理:对这个类生成一个子类对象. // 设置父类 enhancer.setSuperclass(productDao.getClass()); // 设置回调 enhancer.setCallback(this); // 创建代理对象 return (ProductDao) enhancer.create(); } @Override public Object intercept(Objectproxy, Method method, Object[] args, MethodProxy methodProxy)throws Throwable { if("add".equals(method.getName())){ System.out.println("方法前增强代码==============="); Object obj =methodProxy.invokeSuper(proxy, args); System.out.println("方法后增强代码==============="); return obj; }else{ returnmethodProxy.invokeSuper(proxy, args); } } }
***** Spring中使用这两种技术完成代理(AOP):
* 如果类实现了接口,使用JDK动态代理,为你生成代理对象.
* 如果类没有实现接口,使用CGLIB动态代理,生成代理对象.
总结:
Spring在运行期,生成动态代理对象,不需要特殊的编译器
Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术 为目标Bean执行横向织入
1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
* 程序中应优先对接口创建代理,便于程序解耦维护
3.标记为final的方法,不能被代理,因为无法进行覆盖
JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰
CGLib 是针对目标类生产子类,因此类或方法 不能使final的
4.Spring只支持方法连接点,不提供属性连接.
AOP:AOP联盟提出思想.AOP联盟为增强定义一些接口:org.aopalliance.aop.Interface.Advice
Advice就是增强:
前置通知(在方法之前增强) org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强
后置通知(在方法之后增强) org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强
环绕通知(在方法之前和之后增强) org.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强
异常抛出通知(在方法出现异常的时候) org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强
引介通知org.springframework.aop.IntroductionInterceptor(课程中不讲)
在目标类中添加一些新的方法和属性
切面:就是切入点和通知结合.
Advisor: 代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
* 一般切面:没有切入点,增强本身就是一个切面.(增强类上的所有的方法.)
PointcutAdvisor: 代表具有切点的切面,可以指定拦截目标类哪些方法
* 带有切入点切面:
IntroductionAdvisor: 代表引介切面,针对引介通知而使用切面(不要求掌握)
* 引入aop联盟规范包:com.springsource.org.aopalliance-1.0.0.jar
* 引入Spring对aop支持jar包:spring-aop-3.2.0.RELEASE.jar
在src下引入log4j.properties和applicationContext.xml
*cn.itcast.spring3.demo3
* OrderDao
* OrderDaoImpl
public classMyBeforeAdvice implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("前置增强================="); } }
<!-- 目标对象(被代理对象) --> <bean id="orderDao"class="cn.itcast.spring3.demo3.OrderDaoImpl"></bean> <!-- 前置增强代码 --> <bean id="beforeAdvice"class="cn.itcast.spring3.demo3.MyBeforeAdvice"></bean>
不用手动编写代码:
* Spring完成生成代理对象基于一个ProxyFactoryBean.
* 在ProxyFactoryBean中有一些属性:
target : 代理的目标对象
proxyInterfaces : 代理要实现的接口
如果多个接口可以使用以下格式赋值
<list>
<value></value>
....
</list>
proxyTargetClass : 是否对类代理而不是接口,设置为true时,使用CGLib代理
interceptorNames : 需要织入目标的Advice
singleton : 返回代理是否为单实例,默认为单例
optimize : 当设置为true时,强制使用CGLib
<!-- 生成代理--> <bean id="orderDaoProxy"class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 目标对象 --> <propertyname="target" ref="orderDao"/> <!-- 设置目标对象的接口 :value中编写接口的全路径--> <propertyname="proxyInterfaces" value="cn.itcast.spring3.demo3.OrderDao"/> <!-- 配置增强 : interceptorNames : 一定要写value属性.--> <propertyname="interceptorNames" value="beforeAdvice"/> </bean>
注入的时候一定要注入代理对象!!!!
使用普通Advice作为切面,将对目标类所有方法进行拦截,不够灵活,在实际开发中常采用 带有切点的切面.
PointcutAdvisor :带有切点的切面.
* DefaultPointcutAdvisor 最常用的切面类型,它可以通过任意Pointcut和Advice组合定义切面
* RegexpMethodPointcutAdvisor 构造正则表达式切点切面
* 引入aop联盟规范包:com.springsource.org.aopalliance-1.0.0.jar
* 引入Spring对aop支持jar包:spring-aop-3.2.0.RELEASE.jar
在src下引入log4j.properties和applicationContext.xml
*cn.itcast.spring3.demo4
* CustomerDao
public classMyAroundAdvice implements MethodInterceptor{ @Override public Object invoke(MethodInvocation methodInvocation) throwsThrowable { System.out.println("环绕前增强================="); Object result =methodInvocation.proceed(); System.out.println("环绕后增强================="); return result; } }
<!-- 目标对象--> <bean id="customerDao" class="cn.itcast.spring3.demo4.CustomerDao"></bean> <!-- 增强的对象 --> <bean id="aroundAdvice"class="cn.itcast.spring3.demo4.MyAroundAdvice"></bean>
<!-- 配置切面--> <bean id="advisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 正则表达式 .:代表任意字符, *:任意个 --> <propertyname="pattern" value=".*"/> <!-- 在方法上应用哪些增强 --> <propertyname="advice" ref="aroundAdvice"/> </bean>
<!-- 生成代理 --> <bean id="customerDaoProxy"class="org.springframework.aop.framework.ProxyFactoryBean"> <propertyname="target" ref="customerDao"/> <propertyname="proxyTargetClass" value="true"/> <propertyname="interceptorNames" value="advisor"/> </bean>
修改正则:
<!-- <propertyname="pattern" value=".*"/> --> <!-- <propertyname="pattern" value="cn\.itcast\.spring3\.demo4\.CustomerDao\.add.*"/>--> <propertyname="patterns" value=".*add.*,.*delete.*"/>
进行测试:
上面这两种代理的方式不好,需要为每一个类配置一个独立的ProxyFactoryBean.(如果需要代理的类特别的多,每个类都需要创建一个代理的类.)
* 根据切面Advisor的定义的信息,看到哪些类的哪些方法需要增强.(为这些需要增强的类生成代理.
* 自动代理:基于后处理BeanPostProcessor完成代理.
* 代理机制不同:
* 基于ProxyFactoryBean代理:先有被代理对象,将被代理对象传递给代理对象,为其生成代理.
* 自动代理:基于BeanPostProcessor代理.在生成类的过程中产生一个代理对象,返回的就是代理对象本身.
BeanNameAutoProxyCreator 根据Bean名称创建代理
DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理
* AnnotationAwareAspectJAutoProxyCreator基于Bean中的AspectJ 注解进行自动代理
<!-- 后处理Bean配置:不需要配置id --> <!-- 自动代理--> <beanclass="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!-- 配置Bean的名称 --> <property name="beanNames"value="*Dao"/> <propertyname="interceptorNames" value="beforeAdvice"/> </bean>
DefaultAdvisorAutoProxyCreator:
<!-- 定义切面(带有切点切面) --> <bean id="myAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="pattern"value="cn\.itcast\.spring3\.demo4\.CustomerDao\.add.*"/> <propertyname="advice" ref="aroundAdvice"/> </bean> <beanclass="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
区分:
* 代理机制不同:
* 基于ProxyFactoryBean代理:先有被代理对象,将被代理对象传递给代理对象,为其生成代理.
* 自动代理:基于BeanPostProcessor代理.在生成类的过程中产生一个代理对象,返回的就是代理对象本身.
Spring为了简化AOP的开发,引入AspectJ技术.
* AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
* Spring2.0以后新增了对AspectJ切点表达式支持
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)
配置哪些类的哪些方法需要使用增强:
* 类似于正则的方法切点切面.
语法:
* execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
* 例如:
* execution(* *(..));
* execution(* cn.itcast.spring3.demo1.UserDao.*(..))
* execution(* cn.itcast.spring3.demo1.*(..))
* execution(* cn.itcast.spring3.demo1..*(..))
* execution(* cn.itcast.dao.UserDAO+.*(..))
aspectJ的开发需要依赖aop的环境.
* 引入aspectJ的jar包:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* 引入spring整合aspectJ的jar包:spring-aspects-3.2.0.RELEASE.jar
Log4j和spring的配置文件
*cn.itcast.spring3.demo1
* UserDao
* UserDaoImpl
切面:就是切点和通知组合.
* 在哪些类的哪些方法上使用增强.
定义切面:
* @Aspect
定义增强:
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor(不要求掌握)
代码:
@Aspect public class MyAspectAnno { // 增强的代码:(前置增强) @Before("execution(*cn.itcast.spring3.demo1.UserDao.add(..))") public void before(){ System.out.println("前置增强============="); } }
引入aop的约束:
<!-- 基于AspectJ的自动代理.使用AnnotationAwareAspectJAutoProxyCreator --> <aop:aspectj-autoproxy/> <bean id="userDao"class="cn.itcast.spring3.demo1.UserDaoImpl"></bean> <bean id="myAspect"class="cn.itcast.spring3.demo1.MyAspectAnno"></bean>
@Before:前置通知:在目标方法之前执行.(不能拦截目标方法.)
* JoinPoint:连接点
@AfterReturing:后置通知:在目标方法执行之后执行.
* JoinPoint:连接点
* Object:返回值
// 后置增强: @AfterReturning(value="execution(*cn.itcast.spring3.demo1.UserDao.delete(..))",returning="result") public voidafterReturing(JoinPoint joinPoint,Object result){ System.out.println("后置增强=============="+result); }
@Around:环绕通知:
// 环绕通知:(阻止目标对象的方法执行)
@Around(value="execution(*cn.itcast.spring3.demo1.UserDao.find(..))") public Objectaround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("环绕前增强============="); Object obj = joinPoint.proceed(); System.out.println("环绕后增强============="); return obj; }
@AfterThrowing:抛出异常通知:
// 抛出异常通知:
@AfterThrowing(value="execution(*cn.itcast.spring3.demo1.UserDao.find(..))",throwing="e") public voidafterThrowing(Throwable e){ System.out.println("异常通知========="+e.getMessage()); }
@After:最终通知.类似finally代码块.
// 最终通知:
@After(value="execution(*cn.itcast.spring3.demo1.UserDao.find(..))") public void after(){ System.out.println("最终通知==========="); }
// 定义一个切点:
@Pointcut(value="execution(*cn.itcast.spring3.demo1.UserDao.find(..))")
private void mypointcut(){}
面试题:
* Advisor 和Aspect区别?
* Advisor:是Spring传统AOP开发中提供一个切面概念.一般情况下都一个切入点和一个通知组合.
* Aspect:是真正意义上的切面,可以有多个切入点和多个通知组合.
aspectJ的开发需要依赖aop的环境.
* 引入aspectJ的jar包:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* 引入spring整合aspectJ的jar包:spring-aspects-3.2.0.RELEASE.jar
Log4j和spring的配置文件
*cn.itcast.spring3.demo2
* OrderDao
public classMyAspectXml { public void before(){ System.out.println("前置增强============"); } }
<bean id="orderDao"class="cn.itcast.spring3.demo2.OrderDao"></bean> <bean id="myAspect"class="cn.itcast.spring3.demo2.MyAspectXml"></bean> <!-- aop的配置--> <aop:config> <!-- 定义切点 --> <aop:pointcutexpression="execution(* cn.itcast.spring3.demo2.OrderDao.add(..))"id="mypointcut"/> <!-- 定义切面 --> <aop:aspectref="myAspect"> <aop:before method="before"pointcut-ref="mypointcut"/> </aop:aspect> </aop:config>
<!-- aop的配置--> <aop:config> <!-- 定义切点 --> <aop:pointcutexpression="execution(* cn.itcast.spring3.demo2.OrderDao.add(..))"id="mypointcut1"/> <aop:pointcutexpression="execution(* cn.itcast.spring3.demo2.OrderDao.delete(..))"id="mypointcut2"/> <!-- 定义切面 --> <aop:aspectref="myAspect"> <!-- <aop:before method="before"pointcut-ref="mypointcut1"/> --> <!-- <aop:around method="around"pointcut-ref="mypointcut1"/> --> <aop:after method="after" pointcut-ref="mypointcut1"/> <aop:after-throwing method="afterThrowing"pointcut-ref="mypointcut1" throwing="e"/> <aop:after-returning method="afterReturing"pointcut-ref="mypointcut2" returning="result"/> </aop:aspect> </aop:config>
DbUtils工具类JdbcTemplate类似.
JDBC :org.springframework.jdbc.core.JdbcTemplate
Hibernate3.0 : org.springframework.orm.hibernate3.HibernateTemplate
IBatis(MyBatis) : org.springframework.orm.ibatis.SqlMapClientTemplate
JPA org.springframework.orm.jpa.JpaTemplate
步骤一:导入jar包.
* spring环境需要的jar包(6个)
* spring-jdbc-3.2.0.RELEASE.jar
* spring-tx-3.2.0.RELEASE.jar
* 引入数据库驱动包:
步骤二:入门案例:
* 需要连接池:
// 使用SPring的内置连接池: DriverManagerDataSourcedataSource = new DriverManagerDataSource(); // 设置基本参数: dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///spring3_day02"); dataSource.setUsername("root"); dataSource.setPassword("123"); // 创建模板: JdbcTemplate jdbcTemplate =new JdbcTemplate(dataSource); jdbcTemplate.execute("createtable user (id int primary key auto_increment,name varchar(20))");
<!-- Spring自带连接池配置 --> <bean id="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"value="com.mysql.jdbc.Driver"/> <propertyname="url" value="jdbc:mysql:///spring3_day02"/> <propertyname="username" value="root"/> <propertyname="password" value="123"/> </bean>
导入DBCP连接池jar包:
* 2个jar包:
* com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
* com.springsource.org.apache.commons.pool-1.5.3.jar
<!-- 配置DBCP连接池 --> <bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"> <propertyname="driverClassName" value="com.mysql.jdbc.Driver"/> <propertyname="url" value="jdbc:mysql:///spring3_day02"/> <propertyname="username" value="root"/> <propertyname="password" value="123"/> </bean>
导入C3P0连接池jar包 :
* 1个jar包:
* com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
<!-- 配置C3p0连接池 --> <bean id="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource"> <propertyname="driverClass" value="com.mysql.jdbc.Driver"/> <propertyname="jdbcUrl" value="jdbc:mysql:///spring3_day02"/> <propertyname="user" value="root"/> <propertyname="password" value="123"/> </bean>
在src下定义了jdbc.properties文件
jdbc.driver = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql:///spring3_day02 jdbc.user = root jdbc.password = 123
在Spring的配置文件中引入外部属性文件 :
* 两种引入方式:
*第一种: <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <propertyname="location"value="classpath:jdbc.properties"></property> </bean> *第二种: * 引入context约束: <!-- 引入外部文件的第二种方式: --> <context:property-placeholder location="classpath:jdbc.properties"/>
Spring对持久层技术提供的支持的类:
JDBC org.springframework.jdbc.core.support.JdbcDaoSupport
Hibernate3.0 org.springframework.orm.hibernate3.support.HibernateDaoSupport
iBatis org.springframework.orm.ibatis.support.SqlMapClientDaoSupport
使用JDBC模板完成CRUD操作:
* update(String sql,Object... args); ---增删改.
* 简单查询:
* 统计个数:
* select count(*) from user
* queryForInt(String sql);
* 查询3号用户的名称:
* select name from user where id = 3;
* queryForObject(String sql,Class clazz,Object...args);
* 复杂查询:
* 返回对象:
* select * from user where id = ?
* queryForObject(String sql,RowMapperrowMapper,Object... args);
* 返回集合:
* select * from user;
* query(String sql,RowMapper rowMapper,Object...args);
今天的内容总结:
SpringAOP:(思想)
* AOP:面向切面编程,是OOP升华.解决OOP中问题.
* AOP:底层实现:
* 代理机制.
* AOP:术语:
* 连接点:
* 切入点:
* 通知:
* 引介:
* 代理:
* 目标:
* 织入:
* 切面:
* JDK动态代理:
* CGLIB动态代理:
* Spring传统AOP写法:
* 不带切点切面 :Advisor.
* 带有切点的切面 :
* 通过配置生成代理对象:
* ProxyFactoryBean.
* 先有目标对象,将目标对象传递过来生成代理对象.
* 自动代理:
* 基于BeanPostProcessor类完成代理:
* 在类的生成过程中产生了一个代理,返回对象就是代理.
* 按名称自动代理:
* 按切点信息自动代理:
* Spring2.0之后简化编程引入AspectJ.(*****)
* 注解:
* 创建一个类:
UserDao:
* 创建一个切面类:
* @Aspect
* 定义增强:
* @Before
* @AfterReturing
* ...
* 在Spring配置文件中配置:
* <aop:aspectj-autoproxy/>
* XML:
* 创建一个类:
UserDao
* 创建一个切面类:
* 在SPring中配置:
* <aop:config>
<aop:pointcut id=”” expression=””/>
<aop:aspect ref=””>
<aop:before method=”” pointcut-ref=””/>
</aop:aspect>
</aop:config>
SpringJdbcTemplate
* 入门:
* 连接池配置:
* 默认:
* DBCP
* C3P0
* CRUD操作:
*