一、AOP实现机制
1、JDK动态代理
(1)接口 LoginService
package com.qf.chapter08.demo01;
public interface LoginService {
public void login();
}
(2)接口实现类 LoginServiceImpl
package com.qf.chapter08.demo01;
public class LoginServiceImpl implements LoginService {
public void login() {
System.out.println("执行login()方法");
}
}
(3)PerformHandler.java
package com.qf.chapter08.demo01;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PerformHandler implements InvocationHandler {
// 目标对象
private Object target;
public PerformHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强的方法
System.out.println("方法开始执行");
// 执行被代理类的原方法
Object invoke = method.invoke(target, args);
// 增强的方法
System.out.println("方法执行完毕");
return invoke;
}
}
(4)测试类 TestPerformHandler
package com.qf.chapter08.demo01;
import java.lang.reflect.Proxy;
public class TestPerformHandler {
public static void main(String[] args) {
LoginService loginService = new LoginServiceImpl();
PerformHandler performHandler = new PerformHandler(loginService);
// 创建代理对象
loginService = (LoginService) Proxy.newProxyInstance(loginService.getClass().getClassLoader(),
loginService.getClass().getInterfaces(), performHandler);
loginService.login();
}
}
2、CGLib动态代理
(1)CglibProxy.java
package com.qf.chapter08.demo01;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.*;
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
// 生成代理对象的方法
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
// 回调方法
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLig代理之前");
Object invoke = methodProxy.invokeSuper(obj, objects);
System.out.println("CGLig代理之后");
return invoke;
}
}
(2)测试类
package com.qf.chapter08.demo01;
public class TestCGLlib {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
// 创建代理对象
LoginServiceImpl userService = (LoginServiceImpl) cglibProxy.getProxy(LoginServiceImpl.class);
userService.login();
}
}
二、Spring AOP的开发方法
1、基于XML开发Spring AOP
(1)修改 applicationContext.xml ,引入 AOP 命名空间
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
</beans>
(2)创建接口
package com.qf.chapter08.demo02;
public interface UserService {
void insert();
void delete();
void update();
void select();
}
(3)创建接口实现类
package com.qf.chapter08.demo02;
public class UserServiceImpl implements UserService {
public void insert() {
System.out.println("添加用户信息");
}
public void delete() {
System.out.println("删除用户信息");
}
public void update() {
System.out.println("更新用户信息");
}
public void select() {
System.out.println("查询用户信息");
}
}
(4)创建通知类
package com.qf.chapter08.demo02;
import org.aspectj.lang.ProceedingJoinPoint;
public class XmlAdvice {
// 前置通知
public void before() {
System.out.println("这是前置通知");
}
// 后置通知
public void afterReturning() {
System.out.println("这是后置通知(方法不出现异常时调用)");
}
// 环绕通知
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("这是环绕通知之前的部分!!");
// 调用目标方法
Object object = point.proceed();
System.out.println("这是环绕通知之后的部分!!");
return object;
}
// 异常通知
public void afterException() {
System.out.println("异常通知!!");
}
// 后置通知
public void after() {
System.out.println("这是后置通知!!");
}
}
(5)修改 applicationContext.xml ,添加 AOP 配置信息
<!-- 注册Bean -->
<bean name="userService" class="com.qf.chapter08.demo02.UserServiceImpl" />
<bean name="xmlAdvice" class="com.qf.chapter08.demo02.XmlAdvice" />
<!-- 配置Spring AOP -->
<aop:config>
<!-- 指定切点 -->
<aop:pointcut id="pointcut" expression="execution(* com.qf.chapter08.demo02.UserServiceImpl.*(..))"/>
<!-- 指定切面 -->
<aop:aspect ref="xmlAdvice">
<!-- 指定前置通知 -->
<aop:before method="before" pointcut-ref="pointcut"/>
<!-- 指定返回通知 -->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
<!-- 指定环绕方式 -->
<aop:around method="around" pointcut-ref="pointcut" />
<!-- 指定异常通知 -->
<aop:after-throwing method="afterException" pointcut-ref="pointcut" />
<!-- 指定后置通知 -->
<aop:after method="after" pointcut-ref="pointcut" />
</aop:aspect>
</aop:config>
(6)测试类 TestXml
package com.qf.chapter08.demo02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestXml {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.delete();
}
}
2、基于注解开发Spring AOP
(1)创建类 AnnoAdvice.java
package com.qf.chapter08.demo02;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class AnnoAdvice {
// 切点
@Pointcut("execution(* com.qf.chapter08.demo02.UserServiceImpl.*(..))")
public void pointcut() {
}
// 前置通知
@Before("pointcut()")
public void before() {
System.out.println("这是前置通知");
}
// 后置通知
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("这是后置通知(方法不出现异常时调用)");
}
// 环绕通知
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("这是环绕通知之前的部分!!");
// 调用目标方法
Object object = point.proceed();
System.out.println("这是环绕通知之后的部分!!");
return object;
}
// 异常通知
@AfterThrowing("pointcut()")
public void afterException() {
System.out.println("异常通知!!");
}
// 后置通知
@After("pointcut()")
public void after() {
System.out.println("这是后置通知!!");
}
}
(2)修改 applicationContext.xml ,beans 标签内容替换为
<!-- 注册Bean -->
<bean name="userService" class="com.qf.chapter08.demo02.UserServiceImpl" />
<bean name="annoAdvice" class="com.qf.chapter08.demo02.AnnoAdvice" />
<!-- 开启@aspectj的自动代理支持 -->
<aop:aspectj-autoproxy />
(4)测试类 TestAnnotation
package com.qf.chapter08.demo02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAnnotation {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.insert();
}
}
三、Spring AOP的应用
1、性能监控
(1)Services01.java
package com.qf.chapter08.demo03;
public class Service01 {
public void service() throws Exception {
System.out.println("执行service()方法");
Thread.sleep(1000);
}
}
(2)Record.java
package com.qf.chapter08.demo03;
import java.util.Date;
public class Record {
private String className; // 类名称
private String methodName; // 方法名称
private Date recordTime; // 记录时间
private Long expendTime; // 程序执行耗费的时间
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Date getRecordTime() {
return recordTime;
}
public void setRecordTime(Date recordTime) {
this.recordTime = recordTime;
}
public Long getExpendTime() {
return expendTime;
}
public void setExpendTime(Long expendTime) {
this.expendTime = expendTime;
}
@Override
public String toString() {
return "Record [className=" + className + ", methodName=" + methodName + ", recordTime=" + recordTime
+ ", expendTime=" + expendTime + "]";
}
}
(3)RecordAspect.java
package com.qf.chapter08.demo03;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class RecordAspect {
@Pointcut("execution(* com.qf.chapter08.demo03.Service*.*(..))")
public void record() {
}
@Around("record()")
public Object recordTimer(ProceedingJoinPoint thisJoinPoint) throws Throwable {
// 获取目标对象的类名称
String clazzName = thisJoinPoint.getTarget().getClass().getName();
// 获取目标对象的方法名称
String methodName = thisJoinPoint.getSignature().getName();
// 计算目标对象对应方法执行所耗时间
long startTime = System.currentTimeMillis();
Object result = thisJoinPoint.proceed();
long time = System.currentTimeMillis() - startTime;
Record record = new Record();
record.setExpendTime(time); // 记录执行所耗时间
record.setClassName(clazzName); // 记录类名称
record.setMethodName(methodName); // 记录对应方法名称
record.setRecordTime(new Date()); // 记录时间
System.out.println(record.toString());
return result;
}
}
(4)applicationContext_record.xml
resources目录下新建chapter08目录,在chapter08目录下新建
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean name="service01" class="com.qf.chapter08.demo03.Service01" />
<bean name="recordAspect" class="com.qf.chapter08.demo03.RecordAspect" />
<aop:aspectj-autoproxy />
</beans>
(5)测试类 TestRecord.java
package com.qf.chapter08.demo03;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.qf.chapter08.demo03.Service01;
public class TestRecord {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("chapter08/applicationContext_record.xml");
Service01 service01 = context.getBean("service01", Service01.class);
service01.service();
}
}
2、异常监控
(1)自定义异常类
package com.qf.chapter08.demo03;
public class MyException extends Exception {
private static final long serialVersionUID = 1L;
private String msg;
public MyException(String msg) {
super();
this.msg = msg;
}
public String getMsg() {
return msg;
}
}
(2)Services02.java
package com.qf.chapter08.demo03;
public class Service02 {
public void service() throws Exception {
System.out.println("执行service()方法");
int num = 105;
if (num > 100 || num < 0) {
throw new MyException("您输入的不正确,请输入0--100之间的数字");
} else {
System.out.println("您输入的数字是" + num);
}
}
}
(3)Message.java
package com.qf.chapter08.demo03;
import java.util.Date;
public class Message {
private String className;
private String methodName;
private Date recordTime; // 异常记录时间
private String exceptionMsg; // 异常信息
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Date getRecordTime() {
return recordTime;
}
public void setRecordTime(Date recordTime) {
this.recordTime = recordTime;
}
public String getExceptionMsg() {
return exceptionMsg;
}
public void setExceptionMsg(String exceptionMsg) {
this.exceptionMsg = exceptionMsg;
}
@Override
public String toString() {
return "Message [className=" + className + ", " + "methodName=" + methodName + ", recordTime=" + recordTime
+ ", exceptionMsg=" + exceptionMsg + "]";
}
}
(4)MessageAspect.java
package com.qf.chapter08.demo03;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class MessageAspect {
@Pointcut("execution(* com.qf.chapter08.demo03.Service*.*(..))")
public void exceptionMsg() {
}
@Around("exceptionMsg()")
public Object msgMethod(ProceedingJoinPoint thisJoinPoint) throws Throwable {
// 获取目标对象的类名称
String clazzName = thisJoinPoint.getTarget().getClass().getName();
// 获取目标对象的方法名称
String methodName = thisJoinPoint.getSignature().getName();
try {
return thisJoinPoint.proceed();
} catch (MyException e) {
Message msg = new Message();
// 封装异常信息
msg.setClassName(thisJoinPoint.getTarget().getClass().getName());
msg.setMethodName(thisJoinPoint.getSignature().getName());
msg.setRecordTime(new Date());
msg.setExceptionMsg(e.getMsg());
System.out.println(msg.toString());
return null;
}
}
}
(5)applicationContext_msg.xml
在chapter08目录下新建
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean name="service01" class="com.qf.chapter08.demo03.Service01" />
<bean name="recordAspect" class="com.qf.chapter08.demo03.RecordAspect" />
<aop:aspectj-autoproxy />
<bean name="service02" class="com.qf.chapter08.demo03.Service02"/>
<bean name="messageAspect" class="com.qf.chapter08.demo03.MessageAspect"/>
<aop:aspectj-autoproxy/>
</beans>
(5)测试类 TestMessage.java
package com.qf.chapter08.demo03;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMessage {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("chapter08/applicationContext_msg.xml");
Service02 service02 = context.getBean("service02", Service02.class);
service02.service();
}
}