在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
正如上图所示,UserDao和BookDao都需要开启和提交事务,而它们是必须的(通用的)且又和核心业务不相关,如何提高代码的复用性和开发效率,我们来看下图:
我们将与业务代码不相关(或某功能的代码)从类中剥离出来,把它们定义在另一个类中,在使用是可以利用AOP对需要增强的代码进行织入,这个就是AOP的目的。
AOP的实现是基于代理实现的,代理又有静态代理和动态代理,静态代理不能适应变化,动态代理利用反射机制可以适应变化。
Spring默认采取的动态代理机制实现AOP,当动态代理不可用时(代理类无接口)会使用CGlib机制。
Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。
静态代理根据OCP原则是在代理对象和目标对象实现共同的接口,并且代理对象持有目标对象的引用。
模拟实现:DAO接口:
- public interface IUserDao {
-
- void list();
-
- void add();
- }
DAO实现类:
- @Repository
- public class UserDao implements IUserDao {
- @Override
- public void list() {
- System.out.println("user.....list");
- }
-
- @Override
- public void add() {
- System.out.println("user....add");
- }
- }
增强功能类:
- public class LogInfo {
-
- public static void logInfo(String operation){
- System.out.println("执行了"+operation+"操作!");
- }
- }
静态代理类:代理DAO实现并实现增强功能(添加日志功能)
- @Component(value = "userProxyDao")
- public class UserProxyDao implements IUserDao {
-
- @Resource
- private IUserDao userDao;
-
- @Override
- public void list() {
- LogInfo.logInfo("list");
- this.userDao.list();
- }
-
- @Override
- public void add() {
- LogInfo.logInfo("add");
- this.userDao.add();
- }
- }
测试:
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration("/beans.xml")
- public class UserDaoTest {
-
- @Resource(name = "userProxyDao")
- private IUserDao userDao;
- @Test
- public void test(){
- this.userDao.add();
- this.userDao.list();
- }
- }
输出如下:
以上的静态代理的问题时,当有多个业务类时,每个类都要创建一个代理类,显然这是不切实际的。我们可以通过Java的java.lang.reflect.InvocationHandler接口实现动态代理。
基于JDK的动态代理类:
- package edu.song.aop.util;
-
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
-
- /**
- * 动态代理类
- */
- public class ProxyLog implements InvocationHandler {
-
- private ProxyLog(){}
- private Object target;
-
- /**
- * 生成代理对象的方法
- * @param object(要代理的对象)
- * @return (目标对象---》代理对象)
- */
- public static Object getInstance(Object object){
- ProxyLog proxyLog= new ProxyLog();
- proxyLog.target=object;
- /**
- * 创建代理实例:
- * 参数1:代理对象的类加载器
- * 参数2:代理对象的接口
- * 参数3:实现InvocationHandler的对象
- * 返回的就是代理对象
- */
- Object result= Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),proxyLog);
- return result;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- //要增加(强)的功能代码
- LogInfo.logInfo(method.getName()+"!");
- //回调
- Object invoke = method.invoke(target, args);
- return invoke;
- }
- }
DAO:
- @Repository
- public class UserDao implements IUserDao {
- @Override
- public void list() {
- System.out.println("user...dao..list");
- }
-
- @Override
- public void add() {
- System.out.println("user..dao..add");
- }
- }
Service:
- @Service
- public class UserService implements IUserService{
-
- @Resource//(name = "proxyLogDao")
- private IUserDao userDao;
-
- @Override
- public void list() {
- System.out.println("user service....list");
- this.userDao.list();
- }
-
- @Override
- public void add() {
- System.out.println("user service.....add");
- this.userDao.add();
- }
- }
注意以上业务类代码的注释部分
配置文件:
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
- 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
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd">
-
- <context:annotation-config/>
- <context:component-scan base-package="edu.song.aop"/>
-
-
- <bean id="proxyLogDao" class="edu.song.aop.util.ProxyLog" factory-method="getInstance">
- <constructor-arg ref="userDao"/>
- bean>
- beans>
测试:
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration("/beans.xml")
- public class UserDaoTest {
- @Resource
- private IUserService userService;
- @Test
- public void test(){
- userService.add();
- userService.list();
- }
- }
输出如下:
以上输出并没有使用代理类,我们将业务代码修改如下:
- @Service
- public class UserService implements IUserService{
-
- @Resource(name = "proxyLogDao")
- private IUserDao userDao;
我们还可以在代理中设置条件等来完成特有功能,修改代理类如下:
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- if(method.getName().equals("list")) {
- //只有list方法要增加(强)的功能代码
- LogInfo.logInfo(method.getName() + "!");
- }
- //回调
- Object invoke = method.invoke(target, args);
- return invoke;
- }
spring框架的AOP实现提供了两种方式:分别是xml的配置和annotation的配置。接下来我们自定义一个注解:
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface LogAnnotation {
- String value() default "";
- }
接口上添加自定义注解:
- public interface IUserDao {
-
- @LogAnnotation("#### 列表 #####")
- void list();
-
- void add();
- }
修改代理类,添加对注解的处理:
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- if(method.isAnnotationPresent(LogAnnotation.class)){
- LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
- LogInfo.logInfo(annotation.value());
- }
- //回调
- Object invoke = method.invoke(target, args);
- return invoke;
- }