一、介绍
AOP(Aspect Oriented Programming)。
区别:OOP(Object Oriented Programming)和AOP(Aspect Oriented Programming)的区别:面向目标的区别,OOP面向名词领域,AOP面向动词领域。思想结构的区别,OOP是纵向结构,AOP是横向结构。注重方面的区别,OOP注重业务逻辑单元的划分,AOP偏重业务处理过程的某个步骤或阶段。
AOP功能:AOP面向切面编程是对OOP面向对象编程的一种补充。对业务逻辑各个部分进行隔离,降低功能之间的耦合度,提高代码复用率。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理,权限管理、缓存管理、资源池管理等。
AOP代理:AOP实现的关键是代理模式,AOP代理分为静态代理和动态代理。静态代理指AspectJ,在编译阶段生成AOP代理类,也称编译时增强。动态代理在运行时用JDK动态代理和CGLIB在内存中临时生成AOP代理类,也称为运行时增强。
两种动态代理:Spring会使用JDK动态代理只支持实现类(实现了某个接口,委托类)的代理。否则,会使用CGLib来实现动态代理。CGLib动态代理是通过字节码底层继承被代理类(不能被final关键字所修饰)来实现。
CGLIB(Code Generator Library):是一个强大的、高性能的代码生成库。它可以在运行期扩展Java类与实现Java接口。CGLIB以ASM为基础, 对ASM的功能进行了扩展和封装,提供了更友好的API。在AOP框架中,用来提供方法拦截操作。在Hibernate中,用来实现PO(Persistent Object 持久化对象)字节码的动态生成。
AspectJ:Spring AOP的切入点类型只能是方法,AspectJ可以是多种类型(如构造方法)。AspectJ也实现了AOP的功能,且其实现简捷,使用方便,功能健全,支持注解式开发。所以,Spring将AspectJ基础到框架中,与Spring AOP相互独立。
Aop的专业术语:
Aspect(切面):在Aspect中会包含着一些Pointcut以及相应的Advice。
Pointcut(切入点):定义了相应Advice要发生的地方。
Advice(通知):Advice定义了在Pointcut(切入点)具体要做的操作。
Weaving(织入):将Aspect和其他对象连接起来。
Advice(通知)的5中类型:
before advice, 前置。
after return advice, 后置(出错不执行)。
after throwing advice, 后置(出错才执行)。
after(final) advice, 后置(怎么都执行)。
around advice, 前后都执行。
二、代码
1、项目目录结构。
2、pom.xml。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zxj</groupId>
<artifactId>zxj-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<name>zxj-spring</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring.version>4.3.18.RELEASE</spring.version>
</properties>
<dependencies>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
</plugins>
</build>
</project>
3、主流程代码(在单元测试中)。
package com.zxj;
import com.zxj.dao.entity.User;
import com.zxj.dao.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
//junit测试单元要使用下面两个注解,否则@Autowwire为null
@RunWith(SpringJUnit4ClassRunner.class)//可能会报junit版本过低的错误,将其变高就可以了
@ContextConfiguration("classpath:spring.xml")
public class AopTest {
@Autowired
@Qualifier(value = "userServiceProxy")
private IUserService userServiceProxy;
@Autowired
@Qualifier(value = "childServiceProxy")
private IUserService childServiceProxy;
@Test
public void aopTest1() {
System.out.println();
User user=new User();
user.setName("小明");
user.setAge(12);
userServiceProxy.printInfo(user);
System.out.println();
User child=new User();
child.setName("小红");
child.setAge(18);
childServiceProxy.printInfo(child);
System.out.println();
}
}
4、Spring.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--要扫描包-->
<context:component-scan base-package="com.zxj"/>
<!--AOP 经典的基于代理-->
<!--定义切入点的常用的两种方式:1、使用正则表达式。 2、使用AspectJ表达式(https://www.iteye.com/blog/jinnianshilongnian-1415606)。-->
<!--Pointcut(切入点):定义了相应Advice要发生的地方。-->
<bean id="userPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*printInfo"/>
</bean>
<!--Aspect(切面,拦截器):在Aspect中会包含着一些Pointcut以及相应的Advice。-->
<bean id="userAdvice" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="userAdviceHandle"/>
<property name="pointcut" ref="userPointcut"/>
</bean>
<!--Weaving(织入):将Aspect和其他对象连接起来。-->
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userService"/><!--被代理类-->
<property name="interceptorNames" value="userAdvice"/><!--Aspect(切面,拦截器)-->
<property name="proxyInterfaces" value="com.zxj.dao.service.IUserService"/><!--被代理类的实现接口-->
</bean>
<bean id="childServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="childService"/><!--被代理类-->
<property name="interceptorNames" value="userAdvice"/><!--Aspect(切面,拦截器)-->
<property name="proxyInterfaces" value="com.zxj.dao.service.IUserService"/><!--被代理类的实现接口-->
</bean>
</beans>
5、 Advice(通知):Advice定义了在Pointcut(切入点)具体要做的操作。
package com.zxj.test.aop;
import com.zxj.dao.entity.User;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* AOP 经典的基于代理
* <p>
* 切入点在AOP中有多种类型,但在Spring中只有方法类型。
* <p>
* AOP中的Joinpoint可以有多种类型,但是Spring只支持方法执行类型的切入点。
* <p>
* before advice, 前置。
* after return advice, 后置(出错不执行)。
* after throwing advice, 后置(出错才执行)。
* after(final) advice, 后置(怎么都执行)。
* around advice, 前后都执行。
*/
@Component(value = "userAdviceHandle")
public class UserAdviceHandle implements MethodBeforeAdvice, AfterReturningAdvice/*, MethodInterceptor*/ {
private final int adultAge = 16;
/**
* 前置处理
*/
@Override
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println(String.format("%s %s before", target.getClass().getName(), method.getName()));
User user = (User) objects[0];
if (user.getAge() > adultAge) {
System.out.println(String.format("%s已成年", user.getName()));
} else {
System.out.println(String.format("%s未成年", user.getName()));
}
}
/**
* 后置处理
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object target) throws Throwable {
System.out.println(String.format("%s %s afterReturning", target.getClass().getName(), method.getName()));
User user = (User) objects[0];
int ageDiff = Math.abs(user.getAge() - adultAge);
if (user.getAge() > adultAge) {
System.out.println(String.format("%s%d年前就成年了", user.getName(), ageDiff));
} else {
System.out.println(String.format("%s还有%d年才成年", user.getName(), ageDiff));
}
}
// /**
// * 使用动态代理的方式,直接替代
// */
// @Override
// public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// Method method = methodInvocation.getMethod(); //方法
// Object[] objs = methodInvocation.getArguments(); //参数
// Object target = methodInvocation.getThis(); //操作对象
// System.out.println("UserAdviceHandle invoke");
// return method.invoke(target,objs);
// }
}
6、User.java、IUserService.java、UserServiceImpl.java、ChildServiceImpl.java。
package com.zxj.dao.entity;
public class User {
private String name;
private int age;
public User(){
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.zxj.dao.service;
import com.zxj.dao.entity.User;
public interface IUserService {
void printInfo(User user);
}
package com.zxj.dao.service.impl;
import com.zxj.dao.entity.User;
import com.zxj.dao.service.IUserService;
import org.springframework.stereotype.Service;
@Service(value = "userService")
public class UserServiceImpl implements IUserService {
@Override
public void printInfo(User user) {
System.out.println(String.format("用户%s已经%d岁了", user.getName(), user.getAge()));
}
}
package com.zxj.dao.service.impl;
import com.zxj.dao.entity.User;
import com.zxj.dao.service.IUserService;
import org.springframework.stereotype.Service;
@Service(value = "childService")
public class ChildServiceImpl implements IUserService {
@Override
public void printInfo(User user) {
System.out.println(String.format("小孩:%s已经%d岁了", user.getName(), user.getAge()));
}
}