6 认识AOP中的一些基本概念,然后在一个一个的例子中,不断的加强对这些概念的理解,同时要能自己表述出每个概念的含义
AOP 面向切面编程
aspect 切面/切面类,要给代理类添加的操作
joinPoint 连接点,被代理的方法
在spring的aop中只有 类中的方法 可以做连接点,每一个方法都可以是一个连接点.
pointCut 切入点
一组连接点的集合
advice 通知/拦截器 拦截器携带着切面类织入到切入点上
用来控制切面类将来到底是织入到切入点的前面、后面或者是抛异常的时候。
adivsor 增强器
用来筛选类中的哪些方法是我们的连接点(哪些方法需要被拦截).
target 目标对象
proxy 代理对象
wave 织入
前置通知(Before advice):
在某连接点(join point)之前执行的通知
返回后通知(After returning advice):
在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
抛出异常后通知(After throwing advice):
在方法抛出异常退出时执行的通知。
后通知(After (finally) advice):
当某连接点退出的时候执行的通知
环绕通知(Around Advice):
包围一个连接点(join point)的通知,例如事务的处理,就需要这样的通知,因为事务需要在方法前开启,在方法后提交
7 在Spring中,Advice是由spring中的几个接口来指定,主要有以下几种:
1) Before Advice
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
例如:
//有一下几个类或者接口:
Account.java
private int id;
private String name;
private double balance;//余额
get/set
...
AccountDao.java
//取款 账号减去多少钱
void withdraw(Account acc,double amt);
//存款 账号加上多少钱
void deposit(Account acc,double amt);
AccountDaoImpl.java
//简单的实现接口中的抽象方式
IAccountService.java
//银行账号的一个操作:例如转账
void bankAction();
AccountServiceImpl.java
private AccountDao accountDao;
private Account account;
//转账
public void bankAction(){
accountDao.withdraw(account, 100);
accountDao.deposit(account, 100);
}
get/set
...
//切面类
public class MyLogger {
public void log(String msg){
System.out.println("log:"+msg);
}
}
/*我们要做的事情:在转账方法(bankAction)执行之前进行一个日志输出*/
//前置通知
public class BeforeAdvice implements MethodBeforeAdvice {
//切面类
private MyLogger logger;
// 参数1 将来我们需要调用的目标对象中的方法镜像
// 参数2 将来调用方法的时候所传过来的参数
// 参数3 目标对象
//将来在调用目标对象方法之前,会先执行这个before方法
public void before(Method m, Object[] args, Object target) throws Throwable {
logger.log(m.getName() + " is invoked..");
/*
* 注意:这里一定不要自己手动的用反射去 调用这个目标对象中的方法,
* 因为spring 会帮我们去调用的,如果我们这个再去调用这个方法,
* 那么这这个方法会被调用俩次.
*
* m.invoke(target,args);
*
*/
}
get/set
}
配置xml文件: 注意ProxyFactoryBean的配置,htmlsingle中搜索即可
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置一个测试账户 -->
<bean name="account" class="com.briup.aop.pojo.Account">
<property name="id" value="1"></property>
<property name="name" value="tom"></property>
<property name="balance" value="1000"></property>
</bean>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
<property name="account" ref="account"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>beforeAdvice</value>
</list>
</property>
</bean>
2) After Returning advice
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
}
例如:
public class AfterAdvice implements AfterReturningAdvice {
private MyLogger logger;
//参数1 目标对象中的方法执行完返回值
//参数2 所执行方法的镜像对象
//参数3 执行方法时候所传的参数
//参数4 目标对象
//将来调用目标对象的方法之后会执行这个afterReturning方法
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
logger.log("after returning " + " target=" + target
+ " method Name=" + method.getName() + " args are:" + args
+ " returnValue=" + returnValue);
}
get/set
}
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="afterAdvice"
class="com.briup.aop.after.AfterAdvice">
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl" />
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>afterAdvice</value>
</list>
</property>
</bean>
3) 环绕Advice:
import org.aopalliance.intercept.MethodInterceptor;
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
例如:
public class AroundAdvice implements MethodInterceptor {
private MyLogger logger;
public Object invoke(MethodInvocation mi) throws Throwable {
// mi.getMethod()获得将来要调用的方法的镜像
//在目标方法执行之前做日志
logger.log(mi.getMethod().getName() + " is start...");
// 这个方法就是用来调用目标对象中的方法的
Object returnValue = mi.proceed();
//在目标方法执行之后做日志
logger.log(mi.getMethod().getName() + " is end...");
return returnValue;
}
get/set
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="aroundAdvice"
class="com.briup.aop.around.AroundAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean name="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>aroundAdvice</value>
</list>
</property>
</bean>
4) Throws Advice
//ThrowsAdvice 是一个空接口,起标识作用
public interface ThrowsAdvice extends Advice {
}
例如:
public class ThrowingAdvice implements ThrowsAdvice {
private MyLogger logger;
public MyLogger getLogger() {
return logger;
}
public void setLogger(MyLogger logger) {
this.logger = logger;
}
//这里这个方法的名字一定要叫afterThrowing
//参数可以是1个也可以是四个
//1个参数的时候只能是一个异常类型的参数
//如果是4个参数的话,参数的顺序也一定要是下面的顺序
public void afterThrowing(Method method, Object[] args, Object target,Exception e) {
logger.log(e.getMessage());
}
//下面这样写也可以
/*
public void afterThrowing(Exception e) {
logger.log(e.getMessage());
}
*/
get/set
}
配置xml文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="throwAdvice" class="com.briup.aop.throwException.ThrowingAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao层对象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多个 -->
<property name="interceptorNames">
<list>
<value>throwAdvice</value>
</list>
</property>
</bean>
8 advisor 增强器
作用:筛选目标对象中要代理的方法,之前的advice是把目标对象中的所有方法全部都进行代理
指定为advisor的接口为:
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
public interface Advisor {
Advice getAdvice();
boolean isPerInstance();
}
spring中已经给我们提供了实现类RegexpMethodPointcutAdvisor,在xml中直接配使用就可以了
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor 增强器-->
<!-- 作用:筛选要拦截(要代理)的方法 -->
<bean name="advisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice 表示增强器要在哪一个advice起作用-->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法(连接点) -->
<property name="patterns">
<list>
<value>.*bankAction</value>
</list>
</property>
</bean>
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="target"></property>
<!-- 注入目标对象所实现的接口 可以有多个接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice/advisor 可以有多个 -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
注意:另外spring还提供了一个增强器:NameMatchMethodPointcutAdvisor
这个增强器的配置方式和上面的类似,不同之处在于可以利用setMappedNames方法之间注意要处理的方法名字
9 AutoProxy 自动代理:DefaultAdvisorAutoProxyCreator类的使用
使用原因:在配置文件中我们往往需要给很多个目标对象设置代理对象,那么上面例子的方式就需要每个目标对象的代理对象都需要配置一套类似的标签
自动代理:可以用很少的配置为xml文件中的目标对象自动的生成对应的代理对象
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor -->
<!-- 作用:筛选要拦截的方法 -->
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice -->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法 -->
<property name="patterns">
<list>
<value>.*bankAction</value>
</list>
</property>
</bean>
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target2"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target3"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用自动代理的方式 autoproxy -->
<!-- 注意:这不是一个工厂类,所以不能用过proxy来拿代理对象 -->
<bean name="proxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean>
<!--
使用自动代理的时候需要注意的方面:
1.当前的配置里面"一定要有"一个advisor的配置
2.不需要向自动代理类中注入任何信息
3.不管目标对象是否实现了一个或多接口,自动代理的方式
都能够为它产生代理对象(CGLib的方式).
4.从spring容器中拿代理对象的时候,需要通过目标对象的
名字来拿。
5.spring如何确定配置文件中哪个bean是作为目标对象:
通过advisor中筛选的方法,如果这个bean中含有advisor中所配置的方法,则这个bean将来称为我们的目标对象进行代理
-->
10 AutoProxyByName 通过名字进行自动代理:BeanNameAutoProxyCreator类的使用
使用原因:虽然自动代理可以很方便的给xml文件中的目标对象设置对应的代理对象,但是并不是xml文件中的所有对象都是我们的目标对象,我们更想希望可以进一步筛选出某几个对象为我们的目标对象
名字进行自动代理:解决了上面的问题,给我们提供了筛选目标对象的配置方式
xml配置文件:
<!-- 配置切面类 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面类对象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor -->
<!-- 作用:筛选要拦截的方法 -->
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice -->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被拦截的目标对象中的方法 -->
<property name="patterns">
<list>
<value>.*bankAction</value>
</list>
</property>
</bean>
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目标对象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target2"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target3"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理对象 -->
<!-- 这里使用自动代理的方式 autoproxybyname -->
<bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 注入需要被代理的对象名字 -->
<property name="beanNames">
<list>
<value>target</value>
<value>target2</value>
<value>dao</value>
<value>service*</value>
</list>
</property>
<!-- 注入advice或者advisor -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
<!--
使用byName自动代理的时候需要注意的方面:
1.当前的配置里面"有没有"advisor的配置"都没关系"
2.需要向自动代理类中注入被代理目标对象的名字已经advice或者advisor
3.不管目标对象是否实现了一个或多接口,自动代理的方式
都能够为它产生代理对象.
4.从spring容器中拿代理对象的时候,需要通过目标对象的
名字来拿。
-->