一、切面的说明
切面就是允许在某个方法执行之前、执行之后、return后、抛异常后等等…执行一些操作,例如日志记录等等。。用下面的一段代码说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Demo { public int add(int i, int y) { System.out.println("---执行add()前干的事----"); int sum = i+y; System.out.println("----执行add()后干的事----"); return sum; } }
|
二、切面的实现方式
首先模拟一个dao类
接口类
UserDao.java1 2 3
| public interface UserDao { public boolean login(String username, String password); }
|
实现类
UserDaoImpl.java1 2 3 4 5 6 7 8 9 10 11 12
| public class UserDaoImpl implements UserDao { @Override public boolean login(String username, String password) { if ("crazykid".equals(username) && "123456".equals(password)) { System.out.println("登录成功! AQA"); return true; } System.out.println("登录失败! QAQ"); return false; } }
|
2.1 通过 jdk 动态代理 (jdkProxy) 实现
编写代理类
UserDaoProxy.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class jdkProxy implements InvocationHandler { private Object target;
public Object createProxy(Object target) { this.target = target; Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return proxy; }
public Object invoke(Object proxy, Method method, Object[] args) throws Exception { System.out.println(target.getClass().getName() + " 的 " + method.getName() + " 将被调用..."); Object result = method.invoke(target, args); System.out.println(target.getClass().getName() + " 的 " + method.getName() + " 调用完毕,目标方法返回值是:" + result); return result; } }
|
编写测试方法
1 2 3 4 5 6
| @Test public void test01 () { UserDao userDao = (UserDao) new jdkProxy().createProxy(new UserDaoImpl()); userDao.login("crazykid","123456"); System.out.println("登录dao执行完了.."); }
|
执行结果
net.crazykid.bean.aop.dao.UserDaoImpl 的 login 将被调用…
登录成功
net.crazykid.bean.aop.dao.UserDaoImpl 的 login 调用完毕,目标方法返回值是:true
登录dao执行完了..
2.2 通过cglib实现
导入cglib的包
pom.xml1 2 3 4 5 6
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.10</version> </dependency>
|
编写代理类
CglibProxy.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor { private Enhancer enhancer = new Enhancer();
public Object createProxy(Class clazz) { enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); }
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println(method.getName() + "执行方法之前做一些内容..."); Object result = proxy.invokeSuper(obj, args); System.out.println("执行方法返回的结果:" + result); System.out.println(method.getName() + "执行方法之后做一些内容..."); return result; } }
|
编写测试方法
1 2 3 4 5 6
| @Test public void test02 () { UserDao userDao = (UserDao) new CglibProxy().createProxy(UserDaoImpl.class); userDao.login("crazykid","123456"); System.out.println("登录dao执行完了.."); }
|
执行结果:
login执行方法之前做一些内容…
登录成功
执行方法返回的结果:true
login执行方法之后做一些内容…
登录dao执行完了..
2.3 通过 spring 自带的 aoc 实现
导入相关包
pom.xml1 2 3 4 5 6
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.2.RELEASE</version> </dependency>
|
2.3.1 非注解方式实现
假设一个业务类
Hello.java1 2 3 4 5 6 7
| public class Hello { private String msg; public void say(){ System.out.println("-----------"+msg); } }
|
Spring配置文件配置切面
aop_config.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" xmlns:aop="http://www.springframework.org/schema/aop">
<aop:aspectj-autoproxy/>
<bean id="myAspect" class="net.crazykid.bean.aop.springaop.MyAspect"></bean>
<aop:config> <aop:pointcut id="myPointcut" expression="execution(* net.crazykid.bean..*(..))"/>
<aop:aspect id="aspect" ref="myAspect"> <aop:before method="before" pointcut-ref="myPointcut"/> <aop:after method="after" pointcut="execution(* net.crazykid.bean..*(..))"/> <aop:around method="Around" pointcut-ref="myPointcut"/> <aop:after-returning method="AfterReturning" pointcut-ref="myPointcut" returning="result"/> <aop:after-throwing method="AfterThrowing" pointcut-ref="myPointcut" throwing="e"/> </aop:aspect> </aop:config>
<bean id="hello" class="net.crazykid.bean.Hello"> <property name="msg" value="自定义信息..."/> </bean> </beans>
|
编写切面类
MyAspect.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect { public void before(JoinPoint joinPoint) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法将被执行 ----"); } public void after(JoinPoint joinPoint) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法执行结束 ----"); }
public void Around(ProceedingJoinPoint joinPoint) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法调用前被拦截了 ----");
try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } }
public void AfterReturning(JoinPoint joinPoint, String result) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法执行结束,返回值是 "+result+" ----"); }
public void AfterThrowing(JoinPoint joinPoint, Exception e) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法执行结束,抛出异常 "+e.getMessage()+" ----"); } }
|
编写测试方法
1 2 3 4 5 6
| @Test public void test03 () { ApplicationContext context = new ClassPathXmlApplicationContext("aop_config.xml"); Hello hello = (Hello) context.getBean("hello"); hello.say(); }
|
运行结果
—- net.crazykid.bean.Hello 的 say 方法调用前被拦截了 —-
—- net.crazykid.bean.Hello 的 say 方法将被执行 —-
———–自定义信息…
—- net.crazykid.bean.Hello 的 say 方法执行结束 —-
2.3.2 通过注解方式实现
因为使用了注解的方式,我们在刚刚假设的业务类上加上注解
Hello.java1 2 3 4
| @Component public class Hello { .. }
|
Spring 配置文件
aop_zhujie_config.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?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/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" xmlns:aop="http://www.springframework.org/schema/aop">
<context:component-scan base-package="net.crazykid.bean"/>
<aop:aspectj-autoproxy/> </beans>
|
切面类
MyAspect_zhujie.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component;
@Component @Aspect public class MyAspect_Zhujie {
@Pointcut("execution(* net.crazykid..*(..))") public void myMethod(){ }
@Before("myMethod()") public void before(JoinPoint joinPoint) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法将被执行 ----"); }
@After("myMethod()") public void after(JoinPoint joinPoint) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法执行结束 ----"); }
@Around("myMethod()") public void Around(ProceedingJoinPoint joinPoint) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法调用前被拦截了 ----");
try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } }
@AfterReturning(pointcut = "myMethod()", returning = "result") public void AfterReturning(JoinPoint joinPoint, String result) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法执行结束,返回值是 "+result+" ----"); }
@AfterThrowing(pointcut = "myMethod()", throwing = "e") public void AfterThrowing(JoinPoint joinPoint, Exception e) { String name = joinPoint.getTarget().getClass().getName(); String method = joinPoint.getSignature().getName(); System.out.println("---- "+name+" 的 "+method+" 方法执行结束,抛出异常 "+e.getMessage()+" ----"); } }
|
编写测试方法
测试方法跟上面一样,这里就不写了。。。