aop的概念(浅谈辄止):
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)(比如被拦截的方法)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
使用Spring进行面向切面(AOP)编程(以下是注解方式,也可以通过Xml方式):
1:首先我们要在spring的配置文件中引入aop命名空间
2:启动对@AspectJ注解的支持(蓝色部分),凡是没被拦截的方法则直接执行
3:编写切面类,下面已写的类LogPrint
4:通过配置<bean>把类交给spring管理
5:引入夹包:
核心包spring.jar输出日志信息包commons-logging.jar
如果使用了切面编程(AOP),还需要下列jar文件aspectjweaver.jar和aspectjrt.jarcglib-nodep-2.1_3.jar
配置文件:
配置文件:
<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-2.5.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<aop:aspectj-autoproxy/>// 注入解析aop中使用到的注解的处理器
//通过bean让类交给Spring管理
<bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/>
<bean id="log" class="cn.itcast.service.LogPrint"/> //
</beans>
@Aspect //切面注解
public class LogPrint {
@Pointcut(“execution(* cn.itcast.service..*.*(..))”)// 拦截切入点,(后面一句周亮总结)连接点(在spring指方法)的集合是切入点,
private void anyMethod() {}//声明一个切入点
//*任意返回类型,cn.itcast.service包名,..子包,*包中的任意类,.*类中的任意方法,(..)方法参数可以有也可没有
@Before(“anyMethod() && args(userName)”)//定义前置通知,只处理参数类型为String的方法
public void doAccessCheck(String userName) {
System.out.pirntln(“前置通知”);
}
//定义后置通知,只处理返回类型为String的方法,但是如果返回值是void的类型,也会处理,
@AfterReturning(pointcut=“anyMethod()”,returning=“revalue”)
public void doReturnCheck(String revalue) { // revalue存放拦截函数执行的返回值,如果返回类型是void,则revalue为null
System.out.pirntln(“后置通知”);
}
@AfterThrowing(pointcut=“anyMethod()”, throwing=“ex”)//定义例外通知,并接受处理例外ex
public void doExceptionAction(Exception ex) {
System.out.pirntln(“例外通知”);
}
@After("anyMethod()")//定义最终通知
public void doReleaseAction() {
System.out.pirntln(“最终通知”);
}
@Around("anyMethod()")//环绕通知
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // 参数类型不可变
//if()判断是否有权限
System.out.pirntln(“进入方法”);
Object result= pjp.proceed(); // 执行该方法才能执行目标对象的方法
System.out.println(“退出方法“);
Return result;
//注: pjp.proceed();会执行被拦截的业务方法,如果被拦截的页面方法还存在切面,则该方法会执行下一个切面的拦截方法再执行业务方法,如果不执行该方法,则下一个切面的方法和业务方法都不会执行。
}
}
测试:
ApplicationContext cxt = new ClassPathXmlApplicationContext("spring.xml");
OrderServiceBean service = cxt.getBean("orderservice");
service.save("xxx");
控制台输出结果:
前置通知
进入方法
后置通知
最终通知
退出方法
注:可以在环绕通知解决处理其它通知的事情,从而没必要写其它通知。
public class JDKProxy implements InvocationHandler {
private Object targetObject;//代理的目标对象
public Object createProxyInstance(Object targetObject){ // 创建代理对象,前提目标类必须实现接口,即被代理的方法都是接口方法,
this.targetObject = targetObject;
/*
* 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器
* 第二个参数设置代理类实现的接口
第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法
(即该方法的返回的代理对象的方法被调用时,会让JDKProxy对象的invoke方法来执行。)
*/
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
this.targetObject.getClass().getInterfaces(), this);// 代理类实现目标类的所有接口方法
}
// 回调方法,当代理类的相应接口被执行时便执行
// 参数:代理对象,代理对象被调用的方法(拦截到的方法),被调用方法传进的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if( (String)args[0].equal(“mmm”)
return method.invoke(this.targetObject, args);//把方法调用委派给目标对象
else
return null;
}
}
当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象。
public class CGLIBProxy implements MethodInterceptor {
private Object targetObject;//代理的目标对象
public Object createProxyInstance(Object targetObject){// 被代理对象可以不用继承接口
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();//该类用于生成代理对象
enhancer.setSuperclass(this.targetObject.getClass());//设置父类,对非final所有方法进行覆盖同时增加自身代码
enhancer.setCallback(this);//设置回调,用对象为本身
return enhancer.create();
}
// 代理对象,被拦截的方法,方法参数,方法代理对象
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable { // 环绕通知<pre code_snippet_id="358592" snippet_file_name="blog_20140522_6_5186680" name="code" class="java"><span style="white-space:pre"></span><pre code_snippet_id="358592" snippet_file_name="blog_20140522_6_5186680" name="code" class="java"><span style="white-space:pre"></span><pre code_snippet_id="358592" snippet_file_name="blog_20140522_6_5186680" name="code" class="java">Object result = null;
If( args…..)
{
// 前置通知 advice();
Try{
result methodProxy.invoke(this.targetObject, args);
//后置通知aferadvice();
}catch(Exception)
{
//例外通知exceptiondvice();
}finally{
//最终通知finallyadvice();
}
}
Return result;
}
}CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。