个人感觉AOP动态代理像在最开始的类上创建了一个替身,以最初类的对象为原型,加了一些个人的方法,稍作修改使之变成了一个新的对象,用这个新的对象去做一些操作。甚至可以创建对多代理类,用哪个的时候加载即可。
有spring Aop的动态代理 和AspectJ两种动态代理方式
一、JDK动态代理
JDK动态代理必须实现接口
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
只要:
commons-logging-1.2.jar
spring-beans-4.3.6.RELEASE.jar
spring-context-4.3.6.RELEASE.jar
spring-core-4.3.6.RELEASE.jar
spring-expression-4.3.6.RELEASE.jar
public interface UserDao {
public void addUser();
public void deleteUser();
}
//需要增强的类
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("添加用户");
}
@Override
public void deleteUser() {
System.out.println("删除用户");
}
}
//切面类
public class MyAspect {
public void check_Permissions() {
System.out.println("模拟检查权限");
}
public void log() {
System.out.println("记录日志");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxy implements InvocationHandler {
private UserDao userDao;
public Object createProxy(UserDao userDao) {
this.userDao=userDao;
//类加载器
ClassLoader classLoader = JdkProxy.class.getClassLoader();
//返回userDao的所有接口
Class<?>[] clazz = userDao.getClass().getInterfaces();
/*
proxy.newProxyInstance(ClassLoader,Class<?>,InvocationHandler)
InvocationHandler这个参数应该是下方invoke
*/
return Proxy.newProxyInstance(classLoader, clazz, this);
}
@Override
/*
proxy:被代理后的对象,也就是那个小号
Method:将要被执行的方法
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MyAspect myAspect=new MyAspect();
myAspect.check_Permissions();
//调用userDao的所有方法,反射应用
Object obj=method.invoke(userDao, args);
myAspect.log();
return obj;
}
}
public class JdkProxyText {
public static void main(String[] args) throws ClassNotFoundException {
UserDao userDao=new UserDaoImpl();
JdkProxy jdkProxy=new JdkProxy();
//创造这个代理对象
UserDao userDao1=(UserDao)jdkProxy.createProxy(userDao);
userDao1.addUser();
userDao1.deleteUser();
}
}
二、CGLIB动态代理
CGLIB动态代理可以不需要实现接口。
CGLIB动态代理是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
所需jar包同上
//需要增强的类
public class UserService {
public void goSchool() {
System.out.println("我想去上学");
}
}
//切面类
public class MyAspect {
public void think() {
System.out.println("你想去学吗?");
}
public void fur() {
System.out.println("不,你不想。");
}
}
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
public Object createProxy(Object obj) {
// 创建一个动态类对象
Enhancer enhancer = new Enhancer();
// 设置enhancer的父类
enhancer.setSuperclass(obj.getClass());
// 添加enhancer的回调函数
enhancer.setCallback(this);
// 返回创建的代理类
return enhancer.create();
}
/*
* 这是一个拦截器
* arg0:CGLIB根据父类生成的代理对象
* arg1:拦截的方法
* arg3:方法的代理对象,用户执行父类的方法
*/
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
MyAspect myAspect = new MyAspect();
myAspect.think();
Object obj = arg3.invokeSuper(arg0, arg2);
myAspect.fur();
return obj;
}
}
public class CglibProxyTest {
public static void main(String[] args) {
CglibProxy cglibProxy=new CglibProxy();
UserService userService=new UserService();
UserService userService1=(UserService)cglibProxy.createProxy(userService);
userService1.goSchool();
}
}
三、ProxyFactoryBean
FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。
需要:
commons-logging-1.2.jar
spring-beans-4.3.6.RELEASE.jar
spring-context-4.3.6.RELEASE.jar
spring-core-4.3.6.RELEASE.jar
spring-expression-4.3.6.RELEASE.jar
新增:
spring-aop-4.3.6.RELEASE.jar
aopalliance-1.0.jar
public class CustomerDao {
public void buy() {
System.out.println("你想买点什么?");
}
}
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//注意此处MethodInterceptor导入的包与CGLIB导入的不同
public class MyAspect implements MethodInterceptor {
public void welcome() {
System.out.println("大爷来玩啊(猥琐表情.jpg)");
}
public void bye() {
System.out.println("大爷有空再来啊");
}
@Override
// 这里有点环绕式通知的
public Object invoke(MethodInvocation arg0) throws Throwable {
// 前置增强:在原来方法前
welcome();
// 执行最初类方法
Object obj = arg0.proceed();
// 后置增强:在原来方法前
bye();
return obj;
}
}
<?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">
<!-- 目标类 -->
<bean id="customer"
class="com.xingrenhaofa.ProxyFactoryBean.CustomerDao" />
<!-- 切面类 -->
<bean id="myAspect"
class="com.xingrenhaofa.ProxyFactoryBean.MyAspect" />
<!-- 定义一个代理对象 -->
<bean id="customerProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!--要是被增强的类有接口,下面这条就要写了-->
<!-- <property name="proxyInterfaces" value="com.xingrenhaofa.ProxyFactoryBean.CustomerDao"></property>-->
<!-- 指定目标对象 -->
<property name="target" ref="customer" />
<!-- 指定切面 -->
<property name="interceptorNames" value="myAspect" />
<!-- 指定代理方式 -->
<property name="proxyTargetClass" value="true" />
</bean>
</beans>
public class ProxyFactoryBeanTest {
public static void main(String[] args) {
String xmlPath = "com/xingrenhaofa/ProxyFactoryBean/file.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
CustomerDao customer = (CustomerDao) context.getBean("customerProxy");
customer.buy();
}
}
Spring有如下几种通知类型:
-
org.aopalliance.intercept.MethodIntercepter(环绕通知)
在目标方法执行前后实施增强,可以用于日志、事务管理等功能 -
org.springframework.aop.MethodBeforeAdvice(前置通知)
在目标方法执行前实施增强,可以用于权限管理等功能 -
org.springframework.aop.AfterReturningAdvice(后置通知)
在目标方法执行后实施增强,可以用于关闭流、上传文件、删除临时文件等功能 -
org.springframework.aop.ThrowsAdvice(异常抛出通知)
在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能 -
org.springframework.aop.IntroductionInterceptor(引介通知)
在目标类中添加一些新的方法和属性,可以应用于修改老版本程序
四、AspectJ代理
commons-logging-1.2.jar
spring-beans-4.3.6.RELEASE.jar
spring-context-4.3.6.RELEASE.jar
spring-core-4.3.6.RELEASE.jar
spring-expression-4.3.6.RELEASE.jar
spring-aop-4.3.6.RELEASE.jar
aopalliance-1.0.jar
新增:
spring-aspects-4.3.6.RELEASE.jar
aspectjweaver-1.8.10.jar
1.基于XML声明式AspectJ
public interface UserDao {
public void add();
public void delete();
}
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("添加用户");
}
public void delete() {
System.out.println("删除用户");
}
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
public void before(JoinPoint joinPoint) {
System.out.println("前置通知");
}
public void afterReturn(JoinPoint joinPoint) {
System.out.println("后置通知");
}
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始:");
Object obj=proceedingJoinPoint.proceed();
System.out.println("环绕通知结束");
return obj;
}
public void afterThrow(JoinPoint joinPoint,Throwable e) {
System.out.println("异常通知"+e.getMessage());
}
public void after() {
System.out.println("最终通知");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean id="userDao" class="com.xingrenhaofa.proxyXml.UserDaoImpl" />
<bean id="myAspect" class="com.xingrenhaofa.proxyXml.MyAspect" />
<aop:config>
<!--这里定义切面,也就是那个定义很多方法的类-->
<aop:aspect ref="myAspect">
<!--定义切入点,execution内格式为:修饰符(可不写) 返回类型 目标方法的类路径(可不写) 使用的方法 参数-->
<aop:pointcut id="myPointCut"
expression="execution(* com.xingrenhaofa.proxyXml.*.*(..))" />
<aop:before method="before" pointcut-ref="myPointCut" />
<aop:after-returning method="afterReturn"
pointcut-ref="myPointCut" returning="aa" />
<aop:around method="around" pointcut-ref="myPointCut"/>
<aop:after-throwing method="afterThrow"
pointcut-ref="myPointCut" throwing="e" />
<aop:after method="after" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
public class XmlTest {
public static void main(String[] args) {
String xmlPath="com/xingrenhaofa/proxyXml/NewFile.xml";
ApplicationContext context=new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao=(UserDao)context.getBean("userDao");
userDao.add();
userDao.delete();
}
}
2、基于注解的声明式AspectJ
代码和上面有点像,只贴出来不同地方
@Repository("userDao")
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("添加用户");
}
public void delete() {
System.out.println("删除用户");
}
}
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(* com.xingrenhaofa.annotation.*.*(..))")
private void myPointCut() {
}
@Before("myPointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知");
}
@AfterReturning("myPointCut()")
public void afterReturn(JoinPoint joinPoint) {
System.out.println("后置通知");
}
@Around("myPointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始:");
Object obj = proceedingJoinPoint.proceed();
System.out.println("环绕通知结束");
return obj;
}
@AfterThrowing(value = "myPointCut()", throwing = "e")
public void afterThrow(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知" + e.getMessage());
}
@After("myPointCut()")
public void after() {
System.out.println("最终通知");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 启动注解的声明式AspectJ支持 -->
<aop:aspectj-autoproxy/>
<!-- 指定需要扫描的包 -->
<context:component-scan base-package="com.xingrenhaofa.annotation"></context:component-scan>
</beans>
结果也与基于XML声明式AspectJ一样
有点长,,,,就到这里了。