现在的位置: 首页 > 综合 > 正文

AOP实现(二)—Spring框架中的AOP应用

2018年05月28日 ⁄ 综合 ⁄ 共 7089字 ⁄ 字号 评论关闭

一:传统方式(Spring1.0)

使用Spring中的AOP API中定义的接口来定义Adivce(通知,做什么事情),并设置代理对象(配置文件中使用org.springframework.aop.framework.ProxyFactoryBean)然后在程序中使用这个代理对象。

还有基于XML配置方式和注解方式。

1.方法前通知,实现接口MethodBeforeAdivce

复制代码
 1 package proxy;
2
3 import java.lang.reflect.Method;
4
5 import org.springframework.aop.MethodBeforeAdvice;
6
7 public class BeforeLogin implements MethodBeforeAdvice{
8
9 public void before(Method method, Object[] args, Object target)
10 throws Throwable {
11 // TODO Auto-generated method stub
12 System.out.println("执行前");

13 }
14 }
复制代码

2.被代理对象实现的接口

1 package proxy;
2
3 public interface ILogin {
4 public void login(String username,String password);
5 }

3.被代理对象

复制代码
 1 package proxy;
2
3 public class Login implements ILogin{
4 public void login(String username, String password) {
5 // TODO Auto-generated method stub
6 if("tazi".equals(username)&&"123".equals(password))

7 System.out.println("login success!");
8 else
9 System.out.println("login fail!");
10
11 }
12 }
复制代码

4.配置文件

复制代码
 1 <bean id='beforeLogin' class="proxy.BeforeLogin">
2 </bean>
3 <bean id='login' class="proxy.Login"></bean>
4 <bean id='my_proxy'
5 class="org.springframework.aop.framework.ProxyFactoryBean">

6 <property name="proxyInterfaces">
7 <value>proxy.ILogin</value>
8 </property>
9 <property name="target" ref='login'></property>
10 <property name="interceptorNames">
11 <list>
12 <value>beforeLogin</value>
13 </list>
14 </property>
15 </bean>
复制代码

配置文件中<property name="interceptorNames">下的<list>子标签也可以用

<idref local='interceptorLogin2'/>

5.测试

复制代码
1 public class Test {
2 public static void main(String[] args) {
3 ApplicationContext apc=new ClassPathXmlApplicationContext("applicationContext.xml");
4 ILogin login=(ILogin)apc.getBean("my_proxy");
5 login.login("tazi", "123");
6 }
7 }
复制代码

运行结果:

执行前
login success!

另外还有方法后通知,实现AfterReturingAdvice.它仅在方法正常返回后被调用,若方法执行中出现异常则不被调用。它可以看到方法的返回值,却不能改变它,由返回类型是void也可以知道。

 void    afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable

环绕型通知,实现org.aopalliance.intercept.MethodInterceptor(aop联盟定义的,便于在实现规范的aop中迁移)

Object invoke(MethodInvocation invocation) throws Throwable 

注意返回值是Object类型,如果希望不影响返回值,应当返回原方法的返回值,即

Object result=invocation.proceed();
//...
return result;

invocation参数中封装了原方法的信息,有getMethod(),getArguments()等方法。

异常通知ThrowsAdvice,此接口为标签接口,可以自定义方法名称,只要是如下形式

aferThrowing([Method],[args],[target],Throwable的子类)

二 XML文件的方式

1.引入AOP命名空间

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans
3 xmlns="http://www.springframework.org/schema/beans"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xmlns:p="http://www.springframework.org/schema/p"
6 xmlns:aop="http://www.springframework.org/schema/aop"
7 xmlns:tx="http://www.springframework.org/schema/tx"
8 xmlns:context="http://www.springframework.org/schema/context"
9 xsi:schemaLocation="http://www.springframework.org/schema/beans
10 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
11 http://www.springframework.org/schema/tx
12 http://www.springframework.org/schema/tx/spring-tx.xsd
13 http://www.springframework.org/schema/aop
14 http://www.springframework.org/schema/aop/spring-aop.xsd
15 http://www.springframework.org/schema/context
16 http://www.springframework.org/schema/context/spring-context-2.5.xsd
17 ">

18 <!-- 注解方式必须加上 <aop:aspectj-autoproxy/> -->
19 <!-- 目标对象 -->
20 <bean id="personService" class="com.tazi.service.impl.PersonServiceBean"></bean>
21 <!-- 横切面 -->
22 <bean id="myInterceptor" class="com.tazi.service.MyInterceptor"></bean>
23 <!-- 所有aop配置 -->
24 <aop:config>
25 <!-- 定义一个切面 -->
26 <aop:aspect id="asp" ref="myInterceptor">
27 <!-- 定义一个切入点,也可以作为<aop:config>的子标签 -->
28 <aop:pointcut id="mycut" expression="execution (* com.tazi.service.impl.PersonServiceBean.*(..))"/>
29 <!-- 前置通知,注意方法没有参数或者有一个JoinPoint类型的参数 -->
30 <aop:before pointcut-ref="mycut" method="doAccess"/>
31 <aop:after pointcut-ref="mycut" method="afterRet"/>
32
33 </aop:aspect>
34 </aop:config>
35 </beans>
复制代码

2.建立目标对象,自定义的切面对象(不需要实现任何接口)

复制代码
 1 package com.tazi.service;
2
3
4 import org.aspectj.lang.JoinPoint;
5 import org.aspectj.lang.ProceedingJoinPoint;
6 /*
7 * 切面,交给Spring容器管理
8 */

9
10 public class MyInterceptor {
11
12 /*
13 * 前置通知
14 */

15 public void doAccess(){
16 System.out.println("前置通知");
17 }
18
19 public void afterRet(JoinPoint jPoint){
20 System.out.println(jPoint.getSignature().getDeclaringTypeName());
21 System.out.println(jPoint.getSignature().getName());
22 System.out.println("后置通知");
23 }
24 public void after(){
25 System.out.println("最终通知");
26 }
27 public void expHandle(Exception e) {
28 System.out.println("抛出异常"+e);
29 }
30
31 public Object huan(ProceedingJoinPoint pjp)throws Throwable{
32 System.out.println("进入方法");
33 Object result=pjp.proceed();
34 System.out.println("退出方法");
35 return result;
36
37 }
38 }
复制代码

3.定义切入点,使用切入点表达式语言

4.配置xml文件

5.测试

三:使用注解的方式

在Spring的配置文件中引入aop命名空间

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation中加入

http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop.xsd

1.打开注解方式的配置项

注解本身不能干活,需要处理器

使用<aop:aspectj-autoproxy/> 提供对注解的解释功能。

2.定义切面、切入点(对业务Bean里面的哪些方法进行拦截),定义通知(拦截到方法以后所要做的工作)。

业务接口和业务方法如下:

复制代码
package com.tazi.service.impl;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.tazi.service.PersonService;

public class PersonServiceBean implements PersonService {

public void save(String name) {
// TODO Auto-generated method stub
System.out.println("save");

throw new RuntimeException("例外");
}

public void update() {
// TODO Auto-generated method stub

}
public static void main(String[] args) {
ApplicationContext apc=new ClassPathXmlApplicationContext("bean.xml");
PersonService pService=(PersonService)apc.getBean("personService");
pService.save("tazi");
}

public String getPersonName(Integer id) {
// TODO Auto-generated method stub
System.out.println("getName");

return "tazi";
}

}
复制代码
  • 定义切面,使用@Aspect注解,前提是把该切面交给Spring来管理,否则注解没有任何作用。
  • 定义切入点,使用切入点表达式,形如如下形式:

execution表示运行时,后面是[返回值]+空格+包名.类名(包名..类名则表示连同子包)+点号+方法+(..)

注意:方法(..)表示方法的参数任意,*可以表示任意

切入点的名称定义是以一个方法的形式。名称连括号也带上。

  • 定义通知
    根据所作的工作是方法的前后位置,有不同的通知。
复制代码
 1 package com.tazi.service;
2
3 import org.aspectj.lang.ProceedingJoinPoint;
4 import org.aspectj.lang.annotation.After;
5 import org.aspectj.lang.annotation.AfterReturning;
6 import org.aspectj.lang.annotation.AfterThrowing;
7 import org.aspectj.lang.annotation.Around;
8 import org.aspectj.lang.annotation.Aspect;
9 import org.aspectj.lang.annotation.Before;
10 import org.aspectj.lang.annotation.Pointcut;
11 /*
12 * 切面,交给Spring容器管理
13 */

14 @Aspect
15 public class MyInterceptor {
16 /*
17 * 定义一个切入点
18 */

19 @Pointcut ("execution (* com.tazi.service.impl.PersonServiceBean.*(..))")
20 private void anyMethod(){}
21
22 /*
23 * 前置通知
24 */

25 @Before("anyMethod() &&args(name)")//多个条件,与参数的名字相同,既然如此,也说明应用的方法必须有一个String类型的参数
26 public void doAccess(String name){
27 System.out.println("前置通知"+name);
28 }
29
30 @AfterReturning(pointcut="anyMethod()",returning="result")//必须是方法正常执行,若抛出异常则不可能被调用,这里加了返回值,也暗示了所拦截的方法必须带有一个String类型的返回值
31 public void afterRet(String result){
32 System.out.println("后置通知"+result);
33 }
34 @After("anyMethod()") //无论方法是否抛出异常,都会执行它,相当于在try catch的finally中执行
35 public void after(){
36 System.out.println("最终通知");
37 }
38 @AfterThrowing(pointcut="anyMethod()",throwing="e")//当抛出异常时
39 public void expHandle(Exception e) {
40 System.out.println("抛出异常"+e);
41 }
42
43 @Around("anyMethod()") //环绕方法前后,特别适合于权限管理,如果有权限,就调用pjp.proceed()否则不调用,环绕型的通知的方法签名必须是如下形式
44 public Object huan(ProceedingJoinPoint pjp)throws Throwable{
45 System.out.println("进入方法");
46 Object result=pjp.proceed();
47 System.out.println("退出方法");
48 return result;
49
50 }
51 }
复制代码

3.最后把切面和业务Bean都在xml配置文件中配置,交给Spring容器管理。

抱歉!评论已关闭.