AOP,对业务的横向编程,这个思想确实不错。一个简单的实际应用:
在做项目的过程中,做了一个登陆的功能,项目做完这后,需要在用户登录完成后,进行日志管理,也就是将登录成功的用户信息存在数据库中。
这个时候就不要回过头来去源码中,在登录的那个功能里面进行写东西了,利用AOP的原理进行横向拦截大大扩展的程序的可伸缩性!
项目架构SpringMVC + Spring
这种架构项目熟悉的人都知道,Controller是由*-servlet.xml(SpringMVC)文件进行管理的,Service层则是由application.xml(Spring)文件进行管理的.
AOP有四种方式进行配置,常见的是2种,1.注解方式 2.配置文件的方式
我喜欢用配置文件的方式!
先说说对Service曾进行AOP编程吧!
Service层中:
接口:
package com.lgy.service;
public interface Sleepable {
void sleep();
int eat();
int play();
}
实现:
package com.lgy.service;
import org.springframework.stereotype.Service;
@Service
public class Human implements Sleepable {
@Override
public void sleep() {
System.out.println("人在睡觉");
}
@Override
public int eat() {
System.out.println("dog 在 eat");
return 1;
}
@Override
public int play() {
System.out.println("play baskball");
return 1;
}
}
说明:三个方法,其中有2个方法有返回值!
被插入的对象如下:
package com.lgy.aop;
import org.aspectj.lang.ProceedingJoinPoint;
public class SleepHelper {
public void afterSleep(){
System.out.println("睡醒了要穿衣服!");
}
//ProceedingJoinPoint only in around
public Object aroundEat(ProceedingJoinPoint joinpoint) throws Throwable {
System.out.println("执行之前");
Object result = joinpoint.proceed(); //目标对象执行
System.out.println("执行之后");
return result;
}
public void afterReturn() {
System.out.println("执行之后");
}
}
三个方法,对应切面的三个方法。
beans.xml(spring-aop核心配置文件):
<!-- 配置需要被切面操作的对象 -->
<bean id="sleepHelper" class="com.lgy.aop.SleepHelper"></bean>
<aop:config>
<aop:aspect ref="sleepHelper">
<!-- 之后进行插入 -->
<aop:after method="afterSleep" pointcut="execution(* com.lgy.service.*.sleep(..))"/>
<!-- around获取返回值 -->
<aop:around method="aroundEat" pointcut="execution(* com.lgy.service.*.eat(..))" />
<!-- 返回之后执行 -->
<aop:after-returning method="afterReturn" pointcut="execution(* com.lgy.service.*.play(..))" />
</aop:aspect>
</aop:config>
对于方法的插入顺序有如下:
<aop:after method=""/> 切面方法完成以后在进行插入
<aop:after-returning method=""/> 切面方法完成返回值以后在进行插入
<aop:after-throwing method=""/> 切面方法在抛出异常的时候进行插入
<aop:around method=""/> 切面方法在开始前后进行插入(实际是自定义)
<aop:before method=""/> 切面方式在开始之前进行插入
若用到环绕,则可以在方法参数添加 ProceedingJoinPoint joinpoint
通过该对象joinpoint.process()可以执行切面方法,进而得到返回值。
测试代码如下:
/**
* 建议:
* 若目标对象有无return:
* 根据需求
* 1.若方法有return
* 则用AOP的after-return或者around
* 2.若方法没有return
* 则用AOP的after或者before
*/
package aop;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.lgy.service.Sleepable;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/beans.xml"})
public class AopTest {
@Resource(name="human")
private Sleepable sleepable;
@Test
public void humAfter() {
sleepable.sleep();
}
@Test
public void humAround() {
int eat = sleepable.eat();
System.out.println(eat); //根据around的切面方法最终返回值
}
@Test
public void humafterReturn() {
int i = sleepable.play();
System.out.println(i); //根据around的切面方法最终返回值
}
}
以上就是对Service层代码进行切面编程-------------------------------------------------------------------------------------------------------------------
在Controller层中有一点不一样,那就是对Aop的配置位置。由于Contorller层是SpringMVC进行管理,Service层是Spring进行管理,进而生产的代理类不同,AOP生产的代理类有2种方式1.jdk的动态代理 2.cglib代理方式有 区别是前者只能对接口进行代理,后者可以对类进行代理,如果目标对象没有实现接口,则默认会采用CGLIB代理。
所以在bean.xml中配置AOP是不行的,需要在*-servlet.xml中进行配置AOP。
Controller层进行切面编程:
Controller层的代码:
同时也是切面对象
package com.lgy.controller;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.lgy.service.Sleepable;
@Controller
@RequestMapping("/aop")
public class AopController {
@Resource(name="human")
private Sleepable sleepable;
@RequestMapping("/hello")
public String hello() {
System.out.println("hello world");
return "hello";
}
}
被插入的对象如下:
package com.lgy.aop;
public class HelloHelp {
public void helloBefore() {
System.out.println("AOP hellp before...");
}
}
*.servlet.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<!-- 使用cglib代理
<aop:aspectj-autoproxy proxy-target-class="true" />-->
<!-- 自动扫描且只扫描@Controller -->
<context:component-scan base-package="com.lgy" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- 注解生效 json转换-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" >
<property name = "supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 文件上传的视图解析器,springmvc中对这个id名称写死 multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
<!-- 配置静态资源 -->
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- controller -->
<aop:config>
<aop:aspect ref="helloHelp">
<!-- 之后进行插入 -->
<aop:before method="helloBefore" pointcut="execution(* com.lgy.controller.*.hello(..))"/>
</aop:aspect>
</aop:config>
</beans>
ref中的helloHelp在bean.xml文件中进行配置即可。
执行localhost::8080/项目名/aop/hello
测试都通过了!