(jar包版本写的是我用的版本)
AOP的实现方式:
JDK
需要的Jar包
实现方法
使用JDK实现AOP(面向切面编程)要求被代理的类属于接口实现类
即类似
public interface TestDao(){
public void work();
}
public class TestDaoImpl implements TestDao{
@Override
public void work(){
System.out.println("Work start");
}
}
之后需要实现切面类(即包含通知方法的类)
import java.util.Date;
public class MyAspect {
public void check() {
System.out.println("权限控制");
}
public void except() {
System.out.println("异常处理");
}
public void log() {
System.out.println("日志记录");
}
public void monitor() {
System.out.println("性能监控");
}
public void gettime() {
System.out.println(new Date());
}
}
再之后创建代理类 注意代理类需要实现InvocationHandler接口
package dynamic.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import aspect.MyAspect;
public class JDKProxy implements InvocationHandler{
private TestDao dao;
public Object init(TestDao dao) {
this.dao=dao;
ClassLoader cl=MyAspect.class.getClassLoader();
Class[] clazz=dao.getClass().getInterfaces();
return Proxy.newProxyInstance(cl, clazz, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
MyAspect msp=new MyAspect();
msp.gettime();
Object obj=method.invoke(dao, args);
System.out.println("JDKProxy OK");
msp.log();
return obj;
}
}
其中的init函数用于增强TestDao类 主要函数是Proxy.newProxyInstance() 参数则分别为切面类的类加载器,被代理类实现的接口以及代理类本身
第二个函数是实现InvocationHandler接口的函数,method.invoke函数可以看作被包装类的方法的执行,前后的函数结果都会被打印出来
测试代码如下:
TestDao tdao=new TestDaoImpl();
JDKProxy jp=new JDKProxy();
TestDao newtdao=(TestDao) jp.init(tdao);
newtdao.save();
Fri Sep 18 17:06:55 CST 2020
Save Successed!
JDKProxy OK
这样就用JDK实现了AOP 但是这种方法必须要求类提供接口 而CGLIB的方法不需要类提供接口
CGLIB
需要的JAR包
在spring core包里面以及集成了所需的jar包 使用和jdk相同的jar包即可
实现方法
目标类可以直接写
public class Dao {
public void work() {
System.out.println("Im Working!");
}
public void run() {
System.out.println("Im running!");
}
}
之后依然是创建代理类,不同地方在于这里实现的接口是MethodInterceptor
代码如下:
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import aspect.MyAspect;
public class CGProxy implements MethodInterceptor{
public Object init(Object target) {
Enhancer enhancer =new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
// TODO Auto-generated method stub
MyAspect masp=new MyAspect();
masp.gettime();
Object obj=arg3.invokeSuper(arg0, arg2);
System.out.println("CGProxy OK");
return obj;
}
}
init依然是用来包装被代理类,这里使用了Object做参数,同时不同于JDK使用Proxy的函数进行强化,CGLIB是使用的Enhancer类,setsuperclass对被包装类进行强化,callback确定代理逻辑对象为当前对象(当前对象需要实现MethodInterceptor接口),返回enhancer.create();
测试类如下
Dao dao=new Dao();
CGProxy cgp=new CGProxy();
Dao newdao=(Dao)cgp.init(dao);
newdao.work();
结果如下:
Fri Sep 18 17:06:55 CST 2020
Im Working!
CGProxy OK
AspectJ
基于XML实现
需要的jar包:
spring-aspects-5.0.2.RELEASE.jar(之前JDK处下载spring-frame的时候已经下载)
aspectjweaver-1.8.13
首先创建切面类 注意在这里编写切面类时各个函数的参数
环绕通知的部分可以类比前面两种方式
public class MyAspect {
public void before(JoinPoint jp) {
System.out.println("前置通知:");
System.out.println("目标类:"+jp.getTarget()+",增强方法"+jp.getSignature().getName());
}
public void after(JoinPoint jp) {
System.out.println("后置通知:");
System.out.println("模拟删除");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕开始");
Object obj=pjp.proceed();
System.out.println("环绕结束");
return obj;
}
public void except(Throwable e) {
System.out.println("模拟异常处理"+e.getMessage());
}
public void finall() {
System.out.println("最终通知 释放资源");
}
}
各种通知的顺序如下:
无异常:
前置通知->环绕开始->目标方法(被代理类的函数)->最终通知(finall)->环绕结束->后置返回通知(after)
有异常:
前置通知->环绕开始->目标方法(被代理类的函数)->最终通知(finall)->异常
之后创建配置文件
(函数名我起的不是很好 最终通知的标签是after 之前提的后置通知应该叫做后置返回通知 标签是after-returning)
每个通知标签都标定了一个方法和一个切入点
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="testDao" class="dynamic.jdk.TestDaoImpl" /><!--被代理类加载-->
<bean id="myAspect" class="aspectj.xml.MyAspect"/><!--加载切面-->
<aop:config> <!--AOP配置-->
<aop:aspect ref="myAspect"><!--设置切面-->
<aop:pointcut expression="execution(* dynamic.jdk.*.*(..))" id="myPointCut"/><!--设置切入点-->
<aop:before method="before" pointcut-ref="myPointCut"/><!--前置-->
<aop:after-returning method="after" pointcut-ref="myPointCut"/><!--后置-->
<aop:around method="around" pointcut-ref="myPointCut"/><!--环绕-->
<aop:after-throwing method="except" pointcut-ref="myPointCut" throwing="e"/><!--异常-->
<aop:after method="finall" pointcut-ref="myPointCut" /><!--最终-->
</aop:aspect>
</aop:config>
</beans>
测试类(使用了之前编写的的被代理类)
package aspectj.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import dynamic.jdk.TestDao;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext appCon=new ClassPathXmlApplicationContext("/aspectj/xml/applicationContext.xml");
TestDao td=(TestDao) appCon.getBean("testDao");
td.save();
}
}
前置通知:
目标类:dynamic.jdk.TestDaoImpl@5dd6264,增强方法save
环绕开始
Save Successed!
最终通知 释放资源
环绕结束
后置通知:
模拟删除
基于注解的实现
注意前面的两个注解@Aspect(相当于前面的<aop:aspect ref=“myAspect”>)和@Component(用来在框架中作为组件使用)
切面类实现:
package aspectj.annotation;
import java.util.Date;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(* dynamic.jdk.*.* (..))")
private void pcut() {
}
@Before("pcut()")
public void before(JoinPoint jp) {
System.out.println("前置通知开始:\n"+new Date());
}
@AfterReturning("pcut()")
public void afterreturning(JoinPoint jp) {
System.out.println("后置返回 afterreturning");
}
@After("pcut()")
public void after(JoinPoint jp) {
System.out.println("后置最终");
}
@Around("pcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕开始");
Object obj=pjp.proceed();
System.out.println("环绕结束");
return obj;
}
@AfterThrowing(value="pcut()",throwing="e")
public void except(Throwable e) {
System.out.println("错误通知"+e.getMessage());
}
}
对目标类进行注解(在目标类上面把注解写进去)
@Repository("testDao")
最后配置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:aop="http://www.springframework.org/schema/aop"
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">
<!--确定扫描包 使配置文件生效-->
<context:component-scan base-package="aspectj.annotation"/>
<context:component-scan base-package="dynamic.jdk"/><!--被代理类的位置 自己编写的时候可以不一样-->
<aop:aspectj-autoproxy/>
</beans>
小结
以上就是AOP实现的不同方式,选择一种使用即可,最常用最方便的方法应该是最后的基于注解的AspectJ
代码一部分来自陈恒老师编写的JavaEE框架整合开发入门到实战