与Ioc容器同样,AOP也是Spring的核心模块之一。AOP是Aspect-Oriented Programming的简称,如今一般称为面向切面编程。咱们学了OOP,面向对象编程,而AOP并不是是OOP的替代技术,它只是OOP的一个有益补充。
须要指出的是AOP的应用场合是受限的,它通常只适合于那些具备横切逻辑的应用场合:如性能监测、访问控制、事务管理以及日志记录,它并不适合处理具体的业务逻辑,分散处理业务逻辑会使得逻辑混乱、增长维护成本。
2、如何使用Spring AOP
下面以对用户操做类UserDao的AOP拦截演示Spring AOP的使用。
一、建立Java项目,添加Spring AOP依赖支持
aopalliance-1.0.jar
commons-logging-1.1.1.jar
spring-aop-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar
二、添加User及UserDao类
User类:
- public class User {
- private Integer id;
- private String name;
- }
UserDao类:
- public class UserDao {
- public void save(User user){
- System.out.println("save user....");
- }
-
- public void delete(int id){
- System.out.println("delete user....");
- }
-
- public void update(User user) {
- System.out.println("update user ....");
- }
-
- public User query(String name) {
- System.out.println("getUser ....");
- return new User();
- }
- }
三、添加AOP拦截处理
AOP前置通知:
- public class UserBeforeAdvice implements MethodBeforeAdvice {
- public void before(Method method, Object[] args, Object target) {
- System.out.println("调用方法:"+method.getName() + "()前拦截处理");
- }
- }
AOP后置通知:
- public class UserAfterAdvice implements AfterReturningAdvice {
- public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
- System.out.println("方法:"+method.getName() + "()返回后拦截处理");
- }
- }
AOP环绕通知:
- public class UserAroundAdvice implements MethodInterceptor {
- public Object invoke(MethodInvocation invocation) throws Throwable {
- System.out.println("调用方法:"+invocation.getMethod().getName() + "()前拦截处理");
- Object o = invocation.proceed();
- System.out.println("调用方法:"+invocation.getMethod().getName() + "()后拦截处理");
- return o;
- }
- }
四、添加Spring配置文件applicationContext.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"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
- <bean id="userDaoTarget" class="com.boya.spring.dao.UserDao" />
- <bean id="userBeforeAdvice" class="com.boya.spring.aop.UserBeforeAdvice" />
- <bean id="userAfterAdvice" class="com.boya.spring.aop.UserAfterAdvice" />
- <bean id="userAroundAdvice" class="com.boya.spring.aop.UserAroundAdvice" />
- <bean id="userDao" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="interceptorNames">
- <list><value>userAroundAdvice</value></list>
- </property>
- <property name="target" ref="userDaoTarget"></property>
- </bean>
- </beans>
五、测试AOP
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
- UserDao userDao = context.getBean("userDao", UserDao.class);
- userDao.save(new User());
- }
输出结果:
调用方法:save()前拦截处理
save user....
调用方法:save()后拦截处理
回过头来再看刚才的示例。
一、首先,原来的业务逻辑代码不变
再也不关心重复的系统逻辑代码
二、编写AOP切面处理逻辑
把原业务逻辑中的重复代码抽象出来,封装入切面代码中,如上面示例的三种Advice通知封装不一样的系统处理逻辑。
前置通知:实现MethodBeforeAdvice 接口,在调用业务方法前调用该接口
后置通知:实现AfterReturningAdvice 接口,在业务方法返回后调用该接口,在该接口中能够查看返回值(但不能修改返回值)
环绕通知:实现MethodInterceptor 接口,在该接口中invocation.proceed();这个方法会调用真实对象的方法
三、使用Spring配置文件将业务逻辑和AOP切面逻辑进行组装
AOP代理Bean类型须要设置为org.springframework.aop.framework.ProxyFactoryBean
必须设置代理目标(target属性设置)和通知类型(interceptorNames属性设置)
代理目标并不是必须实现接口,做为POJO被代理时,会对目标全部方法进行拦截
3、AOP实现原理
Spring AOP是基于Java反射和动态代理实现的。在讲解动态代理以前,咱们先回顾一下代理模式。
代理模式,就是为某一对象提供一个代理,经过代理操做该对象的方法。一般状况下,真实对象和代理对象须要实现相同的接口,在代理对象中保存真实对象的引用,以此来控制操做真实对象。
咱们以班长代理老师签到来演示代理模式。
建立签到接口:
- public interface SignInterface {
- public Object sign(String nameList);
- }
建立真实对象,Teacher类:
- public class Teacher implements SignInterface {
- public Object sign(String nameList) {
- System. out .println( "Teacher sign..." );
- return new Object();
- }
- }
建立代理对象,Leader类:
- public class Leader implements SignInterface {
- private Teacher teacher;
- public Object sign(String nameList) {
- if (teacher == null) {
- teacher = new Teacher();
- }
- Object o = teacher.sign(nameList);
- return o;
- }
- }
测试代理:
- public static void main(String[] args) {
- SignInterface s = new Leader();
- s.sign("names");
- }
以上就是一个代理模式的例子,代理类在编译时就已经被建立了,而动态代理是在运行时动态建立代理类来实现代理模式。以下代码:
- public class ProxyObject implements InvocationHandler {
- private Object proxy_obj;
- ProxyObject(Object obj) {
- this.proxy_obj = obj;
- }
- public static Object getProxy(Object obj) {
- Class cls = obj.getClass();
-
- return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new ProxyObject(obj));
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("调用方法:" + method + "()以前拦截处理");
- if (args != null) {
- System.out.println("方法有" + args.length + "个参数");
- for (int i = 0; i < args.length; i++) {
- System.out.println(args[i]);
- }
- }
-
- Object o = method.invoke(proxy_obj, args);
- System.out.println("调用方法:" + method + "()以后拦截处理");
- return o;
- }
-
- public static void main(String agr[]) {
- SignInterface si = (SignInterface) getProxy(new Teacher());
- si.sign("names");
- }
- }
以上就是使用JDK的Proxy实现的动态代理,不过JDK的动态代理实现只支持针对接口的动态代理实现。Spring AOP实现默认也是动态代理方式,不过,Spring AOP支持CGLib Proxy的实现方式,能够针对POJO进行动态代理,实现AOP拦截。
咱们来看一下CGLib实现的一个简单AOP拦截
建立业务POJO:
- public class CGLibTeacher {
- public Object sign(String nameList) {
- System.out.println("Teacher sign...");
- return new Object();
- }
- }
建立AOP拦截:
- public class CGLibAop implements MethodInterceptor {
- public Object intercept(Object arg0, Method arg1, Object[] arg2,
- MethodProxy arg3) throws Throwable {
- System.out.println("before...");
- Object o = arg3.invokeSuper(arg0, arg2);
- System.out.println("after...");
- return o;
- }
- }
CGLib代理对象建立及测试:
- public class CGLibProxy {
- public static CGLibTeacher create(CGLibAop aop){
- Enhancer en = new Enhancer();
-
- en.setSuperclass(CGLibTeacher.class);
- en.setCallback(aop);
-
- return (CGLibTeacher)en.create();
- }
-
- public static void main(String[] args) {
- CGLibTeacher t = CGLibProxy.create(new CGLibAop());
- t.sign("names");
- }
- }
从CGLib的代理对象建立中能够看到,代理对象须要设置代理目标以及AOP拦截实现,和Spring AOP的实现很是相似。